Java基础学习(进阶版)

发布时间:2024年01月06日

继承

继承:泛化,实现子类共享父类属性的方法的机制。在已有类的基础上,扩展出新的类,在新的类中加入特殊的属性和方法。

java 的类是单继承

//语法格式:
class 子类 entends 父类{} 
  • 是否可以继承和访问父类私有的属性和方法?

    理解一:子类会继承父类的所有属性和方法,是否能访问,是因为访问权限的问题。

    java官方文档的解释:子类不能继承父类的私有属性和方法,但是如果子类中公有的方法影响到了父类私有,那私有属性是可以被子类使用的。

    总结:父类私有的属性在子类中不可以直接使用,必须通过getter或setter方式进行访问

  • 是否可以继承和访问父类默认的属性和方法?

    可以继承,在同包下可以访问,在不同包下不可以访问。

  • 是否可以继承和访问父类受保护的属性和方法?

    可以继承,在同包下可以访问,在不同包下也可以访问。

  • 是否可以继承和访问父类公有的属性和方法?

    可以继承,在同包下可以访问,在不同包下也可以访问。

    总结:私有的不可以访问,默认的在同包下可以访问,受保护的和公有的,可以在不同包访问,相当于在子类中声明一样使用。

子类实例化过程:

  1. 父类的构造方法,不能被子继承

  2. 在子类的构造方法中,调用了父类的构造方法

  3. 如果父类有无参的构造方法,子类super()可以省略

  4. 如果父类中没有无参的构造方法,子类super(参数)不可以省略

  5. 如果使用super()显式地调用父类的构造方法,要求必须写在子类构造方法中的第一行

  6. 子类的构造方法中,不能同时出现 super()和 this()

在子类创建对象时,是否也创建了父类的对象。答:不会创建父类对象。只是创建了父类空间,并进行了初始化操作。

方法重写

当一个子类继承一个父类的时候,可以重写覆盖原来父类里面的方法,当然这个方法和父类的方法名称一定要相同,参数也相同。

方法重写的要求:(private ,default ,protected ,public)

1、方法名必须相同

2、参数列表必须相同

3、访问修饰符 大于等于父类方法的访问修饰符(访问权限不能比父类中被重写的方法的访问权限更低 例如:如果父类方法被public修饰,则子类中重写该方 法就不能声明为 protected)

4、父类方法不能是私有的(父类被 static, private 修饰的方法、构造方法都不能被重写

5、返回值类型 小于等于父类方法的返回值类型

  • 重载: “两同一不同” ,同一个类,相同的方法名,参数列表不同。

    重写:继承以后,子类覆盖父类中同名同参数的方法。

抽象类与抽象方法

抽象方法:使用 abstract 关键字修饰,只有方法的声明(方法头),没有方法的实现(方法体)。不是静态方法(static)【没有方法体的方法】

抽象类:使用 abstract 关键字修饰,可以有抽象方法,也可以有普通方法,也可以有构造方法,也可以没有抽象方法。如果一个类中有抽象方法,则这个类必须是抽象类。抽象类不能实例化。【包含抽象方法的类】

方法重写的时候,必须要重写的方法是 abstract 修饰的抽象方法。如果子类不重写父类的抽象方法,子类必须是抽象类。

//定义方法:
修饰符	abstract 返回值类型	方法名(参数列表);
    eg:	public abstract void run();

如果一个类包含抽象方法,那么这个类一定是抽象类

抽象类不一定有抽象方法,但是有抽象方法的类一定是抽象类

抽象类可以写抽象方法,但是不能创建对象

class Person{
    public String info = "TGU";
}
class Student extends Person{
    public String info = "CSC";
    public void print(){
	System.out.println("父类中的属性:" + super.info);
    System.out.println("子类中的属性:" + this.info);
    }
}
public class Demo{
    public static void main(String[] args){
        new Student().print();
    }
}


接口

接口中没有成员变量,只有公有静态常量(public static final)

默认情况下接口变量:public static final 数据类型 常量名 = 常量值

概念:Java接口就是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)

(JDK1.8以前:接口是解决 Java 无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。或者接口理解成100%的抽象类,既接口中的方法必须全部是抽象方法)

特点:

就像一个类一样,一个接口也能够拥有方法和属性,但是在接口中声明的方法默认是抽象的。(即只有方法标识符,而没有方法体)。

  • 接口指明了一个类必须要做什么和不能做什么,相当于类的蓝图。

  • 一个接口就是描述一种能力,比如“运动员”也可以作为一个接口,并且任何实现“运动员”接口的类都必须有能力实现奔跑这个动作(或者implement move()方法),所以接口的作用就是告诉类,你要实现我这种接口代表的功能,你就必须实现某些方法,我才能承认你确实拥有该接口代表的某种能力。

  • 如果一个类实现了一个接口中要求的所有的方法,然而没有提供方法体而仅仅只有方法标识,那么这个类一定是一个抽象类。(必须记住:抽象方法只能存在于抽象类或者接口中,但抽象类中却能存在非抽象方法,即有方法体的方法。接口是百分之百的抽象类)

  • 一个JAVA库中接口的例子是:Comparator 接口,这个接口代表了“能够进行比较”这种能力,任何类只要实现了这个Comparator接口的话,这个类也具备了“比较”这种能力,那么就可以用来进行排序操作了。

为什么要用接口
  • 接口被用来描述一种现象

  • Java不像 C++ 一样支持多继承,所以 Java 可以通过实现接口来弥补这个局限。

  • 接口也被用来实现解耦

  • 接口被用来实现抽象,而抽象类也被用来实现抽象,为什么要用接口?接口和抽象类之间区别是什么呢? ( 抽象类内部可能包含非 final 的变量,但是在接口中存在的变量一定是final )

interface exam1{
    final int a = 10;
    void display();
}
//必须实现接口的所有功能才能使用接口
class test implements exam1{
    public void display(){
        System.out.println("Geek");
    }
}
  • 接口的定义:[访问修饰符] interface 接口名{}

  • 接口中的数据成员,全部是公有的静态常量

  • 类和接口的关系 :class 类名 implements 接口名

  • 在jdk1.8之前,接口中的方法,全部是公有的抽象方法

  • 接口不是类,没有构造方法,不能实例化

  • 接口可以实现多继承

  • 一个接口,可以被多个类实现

  • 一个类可以实现多个接口: class 类名 implements 接口1[,接口2...]{}

  • 一个类可以在继承一个父类的同时,实现一个或多个接口: class 类名 extends 父类 implements接口1[,接口2...]{}

final关键字

final关键字不能和abstract关键字放在一起使用。

  • final定义常量:

    修饰引用类型: 无法更改引用所指向的对象地址。引用的对象属性可以修改。修饰基本数据类型:值不能被修改

    编译期常量、非编译期常量

    不是所有的final修饰的字段都是编译期常量,非编译期常量,在被初始化之后无法修改

    static final:

    一个既是static,又是final的字段,只占据一段不能改变的存储空间。

  • final定义终结类:

    当一个类定义为final 时,就表明不能继承这个类,表示不能产生子类。

    final类里面的所有方法,都是隐式的final,不会产生子类,也不会覆盖方法,所以不需要在final类中

  • 为方法加final关键字。

  • final定义终结方法:

    不能被子类重写。所有private方法都是隐式地final方法,在private方法中,不需要使用final关键

    字修饰。

    final方法可以被重载。

public final void test(){
    
}

public final void test(int x){
    
}
  • final初始化:

    类的普通成员变量,在定义时,可以直接初始化,也可以先不作初始化,在构造方法或构造代码块中,进行初始化。

    final int x;
    /*
    {
    x = 100;
    }
    */
    
    public test (int x){
        this.x = x;
    }

    类的静态成员变量,在定义时,可以直接初始化,也可以先不做初始化,在静态代码块中,进行初始化。

    static final int x ;
    static{
        x = 10;
    }

多态

多态是方法或对象具有多种形态,是面向对象的第三大特征。多态的前提是两个对象(类)存在继承关系,多态是建立在封装和继承基础之上的。

具有多种表现形态,三个必要条件

  • 要有继承

  • 要有重写

  • 父类引用指向子类对象

优点:

  • 简化代码

  • 面向抽象编程,不面向具体编程。(依赖倒转原则)

  • 易于扩展,增强代码可读性

里氏代换原则:
  1. 父类出现的地方,子类一定出现

  2. 子类出现的地方,父类不一定可以出现

规则:
  • 一个对象的编译类型与运行类型可以不一致

  • 编译类型在定义对象时,就确定了,不能改变,而运行类型是可以变化。

  • 编译类型看 定义对象 = 号的左边,运行类型 = 的右边

class Animal{
    public void say(){
        System.out.println("动物会发出各种各样的叫声:");
    }
}

class Dog extends Animal{
    public void say(){
        System.out.println("汪汪汪");
    }
}

class Cat extends Animal{
    public void say(){
        System.out.println("喵喵瞄");
    }
}

public static class Demo{
    public static void main(String[] args){
        Animal cat = new Cat();
        Animal dog = new Dog();
        
        cat.say();		//喵喵喵
        dog.say();		//汪汪汪
    }
}
多态的转型
向上转型:
  • 本质:父类引用指向子类的对象

  • 特点:

    1. 编译类型看左边,运行类型看右边

    2. 可以调用父类的所有成员(需遵守访问权限)

    3. 不能调用子类的特有成员(当向上转型之后,父类的引用变量可以访问子类中属于父类的属性和方法,但是不能访问子类独有的属性和方法

    4. 运行效果看子类的具体实现

  • 语法:

    
    父类类型 引用名 = new 子类类型();
    //右侧创建一个子类对象,把它当做父类看待使用
向下转型:
  • 本质:一个已经向上转型的子类对象,将父类引用转为子类引用

  • 特点:

    1. 只能强制转换父类的引用,不能强制转换父类的对象

    2. 要求父类的引用必须指向的是当前目标类型的对象

    3. 当向下转型后,可以调用子类类型中的所有成员

  • 语法

    子类类型 引用名 = (子类类型) 父类引用;

instanceof 运算符:

判断一个对象是否属于某个类(或其父类)返回boolean的值,如果属于返回true,否则false。

public void ride(Horse horse){
    if(horse instanceof whitehorse){
        System.out.println("为马洗澡");
    }
    System.out.println("人骑马");
    horse.run();
}

子类可以写一个和父类相同的静态方法,当使用多态机制,将子类声明成父类类型时,调用的静态方法,还是父类的静态方法,不能实现方法的覆盖。

必须要重写的方法:abstract修饰的抽象方法

不能重写的方法:构造方法、private、final、static

异常

异常就是异于常态,和正常情况不一样,有错误出错。在 Java 中,阻止当前方法或作用域的情况,称之为异常。

  • Java 中的所有不正常类都继承于Throwable。Throwable主要包括两个大类,一个是 Error 类,一个是 Exception类;

  • Error 类包含虚拟机错误,线程死锁,一旦 Error 出现了,程序就彻底挂了,被称为程序终结者。

  • Exception 类,也就是 “异常” 。主要指编码、环境、用户操作输入出现问题, Exception 主要包括两大类,非检查异常 (RunningException)和检查异常(其他的一些异常)。

  • RuntimeException异常主要包括以下四种异常(其实还有很多其他异常,这里不一一列出):空指针异常、数组下标越界异常、类型转换异常、算术异常。RuntimeException异常会由java虚拟机自动抛出并自动捕获(就算我们没写异常捕获语句运行时也会抛出错误!!),此类异常的出现绝大数情况是代码本身有问题应该从逻辑上去解决并改进代码。

  • 检查异常,引起该异常的原因多种多样,比如说文件不存在、或者是连接错误等等。跟它的“兄弟”RuntimeException运行异常不同,该异常我们必须手动在代码里添加捕获语句来处理该异常,这也是我们学习java异常语句中主要处理的异常对象。

try - catch - finally 语句

(1)try块:负责捕获异常,一旦try中发现异常,程序的控制权将被移交给catch块中的异常处理程序。

  【try语句块不可以独立存在,必须与 catch 或者 finally 块同存】

(2)catch块:如何处理?比如发出警告:提示、检查配置、网络连接,记录错误等。执行完catch块之后程序跳出catch块,继续执行后面的代码。

  【编写catch块的注意事项:多个catch块处理的异常类,要按照先catch子类后catch父类的处理方式,因为会【就近处理】异常(由上自下)。

(3)finally:最终执行的代码,用于关闭和释放资源。

try{
    //一些会抛出异常
}catch(Exception e){
    //第一个异常
	//处理该异常的代码块
}catch(Exception e){
    //第二个catch,可以有多个catch
    //处理该异常的代码块
}finally{
    //最终要执行的代码
}
    当异常出现时,程序将终止执行,交由异常处理程序(抛出提醒或记录日志等),异常代码块外代码正常执行。 try会抛出很多种类型的异常,由多个catch块捕获多钟错误。

多重异常处理代码块顺序问题:先子类再父类(顺序不对编译器会提醒错误),finally语句块处理最终将要执行的代码。

Example 1 :

import java.util*;
public class End1{
    public static void main(String[] args){
        Scanner num = new Scanner(System.in);
    	try{
            System.out.println("请输入分数:");
        	int score = num.nextInt();
            if(score < 0 || score > 100){
                throw new Exception("分数必须在0~100之间");
            }
            System.out.println("分数为:" + score);
        }catch(Excption e){
            System.out.println(e.getMessage());
        }
    }
}    

总结:

  • 不管有没有出现异常或者try和catch中有返回值return,finally块中代码都会执行;

  • finally中最好不要包含return,否则程序会提前退出,返回会覆盖try或catch中保存的返回值。

  • e.printStackTrace()可以输出异常信息。

  • return值为-1为抛出异常的习惯写法。

  • 如果方法中try,catch,finally中没有返回语句,则会调用这三个语句块之外的return结果。

  • finally 在try中的return之后 在返回主调函数之前执行。

throw 和 throws

Java 中的异常的抛出通常使用 throw 和 throws 关键字来实现

throw ——将产生的异常抛出,是抛出异常的一个动作

一般会用于程序出现某种逻辑时程序员主动抛出某种特定类型的异常

public static void main(String[] args) { 
    String s = "abc"; 
    if(s.equals("abc")) { 
      throw new NumberFormatException(); 
    } else { 
      System.out.println(s); 
    } 
    //function(); 
} 

throws ——声明将要抛出何种类型的异常(声明)

public void 方法名(参数列表)
   throws 异常列表{
//调用会抛出异常的方法或者:
throw new Exception();
}

throw 与 throws的比较 1、throws出现在方法函数头;而 throw出现在函数体。 2、throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。 3、两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。

void doA(int a) throws (Exception1,Exception2,Exception3){
      try{
         ......
 
      }catch(Exception1 e){
       throw e;
      }catch(Exception2 e){
       System.out.println("出错了!");
      }
      if(a!=b)
       throw new Exception3("自定义异常");
}
//throws e1,e2,e3只是告诉程序这个方法可能会抛出这些异常,方法的调用者可能要处理这些异常,而这些异常e1,e2,e3可能是该函数体产生的。
//throw则是明确了这个地方要抛出这个异常。
/*
分析:
1.代码块中可能会产生3个异常,(Exception1,Exception2,Exception3)。
2.如果产生Exception1异常,则捕获之后再抛出,由该方法的调用者去处理。
3.如果产生Exception2异常,则该方法自己处理了(即System.out.println("出错了!");)。所以该方法就不会再向外抛出Exception2异常了,void doA() throws Exception1,Exception3 里面的Exception2也就不用写了。因为已经用try-catch语句捕获并处理了。
4.Exception3异常是该方法的某段逻辑出错,程序员自己做了处理,在该段逻辑错误的情况下抛出异常Exception3,则该方法的调用者也要处理此异常。
*/

使用throw和throws关键字需要注意以下几点:

1.throws的异常列表可以是抛出一条异常,也可以是抛出多条异常,每个类型的异常中间用逗号隔开

2.方法体中调用会抛出异常的方法或者是先抛出一个异常:用 throw new Exception() throw写在方法体里,表示“抛出异常”这个动作。

3.如果某个方法调用了抛出异常的方法,那么必须添加 try catch语句去尝试捕获这种异常, 或者添加声明,将异常抛出给更上一层的调用者进行处理

Exanple 2 :

设计一个 Triangle 类,包含方法 isTriangle(int a, int b ,int c),用于判断三个参数是否能构成一个三角形。如果不能则抛出异常 illegalArgumentException ,显示异常信息“ a,b,c 不能构成三角形”,如果可以构成则显示三角形三个边长,在主方法中得到命令行输入的三个整数,调用此方法,并捕获异常。

import java.util*
    
public class Triangle{
    public static void main(String[] args){
        
    } 
}

private static isTriangle(int a, int b,int c){
    if(a < 0 || b < 0 || c < 0 ){
        throw new illegalArgumentException("三条边不能是负数");
    }
    if(a + b > c && a + c > b && b + c > a){
        System.out.println("a:" + a + " " + "b:" + b + " " + "c:" + c + " ");
    }
    else
        throw new illegalArgumentException("不能构成三角形");
}

文章来源:https://blog.csdn.net/m0_74070993/article/details/135355742
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。