继承、多态、接口、抽象类

一、继承:

  • 表示is-a的关系。将多个类的相同功能部分抽出放到父类,extends父类。
  • 继承是is关系,组合是has关系(组合:通过持有实例)。
    1
    2
    3
    4
    5
    6
    7
    public class BaseMessage{
    //...
    }
    public class TextMessage extends BaseMessage{
    //...
    //在子类中想调用父类哪个方法就调用哪个
    }

一个类有且仅有一个父类(若没有明确写extends的类,编译器会自动加上 extends Object)。Object特殊,它没有父类。

子类不会继承父类的任何构造方法。

1、protected

子类无法访问父类的private方法或private字段。父类的字段需要用protected或public修饰。

2、super

  • 子类引用父类的字段时,可以用super.fieldName
  • 子类调用父类的方法,可以通过super来调用。
  • super()。任何子类定义的构造方法,第一行语句必须是调用父类的构造方法。如果没有明确调用父类的构造方法,编译器会自动在第一句加上super()。(此时父类必须有无参数构造方法,否则编译失败)。
  • 若是父类没有无参数的构造方法(若是已经自定义构造方法,编译器不再自动创建默认构造方法)。此时,子类必须显示调用super()并带参数以便让编译器定位到父类合适的构造方法。

3、向上转型、向下转型

  • 向上转型就是把一个子类型安全地变为更加抽象的父类型。
  • 可以强制向下转型,最好借助instanceOf判断。
1
2
3
4
5
6
//instanceOf实际上判断一个变量所指向的实例是否是指定类型,或者是这个类型的子类。
Person s = new Student(); //Student类继承Person类,向上转型
System.out.println(s instanceof Person); // true
System.out.println(s instanceof Student); // true

//Java14后,使用instanceOf可直接指定类型变量(须打开编译器开关 --source 14 和 --enable-preview)

4、方法重写/覆盖/覆写(Override):

返回值类型、方法名、参数类型(个数、顺序)都一致。

  • 加上@Override可以让编译器帮助检查是否进行了正确的覆写,但并非必需。

方法重载和方法重写的区别:

  • 重写(覆盖):子类对父类的方法重新定义(方法名、返回类型、参数个数类型顺序均相同)。如需调用父类该原有方法,可super关键字。
  • 重载:一个类中,多个方法的方法名相同,但参数的个数、类型或顺序不相同。方法重载的目的是,功能类似的方法使用同一个名字,更容易记住和被调用。如String的indexOf方法。

注意:方法重载的返回值类型应该相同。

5、final

  • final修饰的方法可以阻止被覆写;
  • final修饰的class可以阻止被继承;
  • final修饰的field必须在创建对象时初始化(可以设置固定值,或者在构造方法中初始化final字段),随后不可修改。

6、覆写Object方法

所有的class最终都继承自ObjectObject定义了几个重要的方法:

  • toString()。
  • hashCode():计算一个instance哈希值
  • equals():判断两个instance是否逻辑相等。(==)

== 比较两个基本数据类型是否相等,或者比较两个引用是否指向同一个对象;equals 用于比较两个对象是否是同一个对象

二、多态(Polymorphic)

Java 的实例方法调用是基于运行时的实际类型的动态调用,而非变量的声明类型。

多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。

  • 允许添加更多类型的子类实现功能扩展,却不需要修改基于父类的代码。

三、抽象类

  • 把一个方法声明为abstract。如果父类的方法本身不需要实现任何功能,仅仅是定义方法签名让子类去覆盖它,可以把这个方法声明为抽象方法;
  • 包含抽象方法的类必须被声明成abstract。抽象方法无法被执行,所以包含抽象方法的这个类无法被实例化。
  • 抽象类必须被子类继承,抽象方法必须被子类重写,如果子类不实现抽象方法,子类仍是一个抽象类。

四、接口:

  • 如果一个抽象类没有字段,所有方法全部都是抽象方法。就可以使用interface声明成接口。
  • 接口定义的方法默认都是public abstract的。
  • 一个类implements某个接口,必须实现接口中定义的所有方法。
  • 接口中定义的成员变量,默认都是public static final的常量(静态字段)。

表示can-do的能力。多个类的处理目标一致,方式不同,implements这个接口(对功能的描述),各自实现自己的处理方法。

1、接口继承

1
2
3
4
5
6
7
// 一个接口可以继承另一接口:
public interface BeanFactory {

Object getBean(String beanID);
}
public interface ApplicationContext extends BeanFactory {
}

2、接口中定义default方法

在接口中,可以定义default方法。实现类不必覆写default方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {
public static void main(String[] args) {
Person p = new Student("Xiao Ming");
p.run();
}
}
interface Person {
String getName();
String s="Hello!"; // 默认都是public static final的常量,编译器会自动加上public static final
default void run() {
System.out.println(s+getName() + " run");
}
}
class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
  • 当给接口增加一个方法时,会涉及到修改全部子类。若是新增的default方法,那么子类不用全部修改,只需要在需要覆写的地方去覆写新增方法。

3、接口和抽象类的区别

1、相同点:都不能被实例化;接口的实现类或继承抽象类的子类只有实现了接口或抽象类的方法后才能实例化。

2、不同点:
(1)接口只能有定义不能有方法的实现。而抽象类可以有定义和方法的实现,方法可在抽象类中实现。

(2)一个类能implements多个接口,但一个类只能extends一个抽象类。使用接口可以间接地实现多重继承。

(3)接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的;接口不能定义实例字段。

抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。

(4)接口被用于常用的功能,便于日后维护和添加删除,而抽象类更倾向于充当公共类的角色,不适用于日后重新对里面的代码修改。功能需要累积时用抽象类,不需要累积时用接口。

五、静态字段和静态方法

1、用static修饰的字段,称为静态字段

  • 所有实例共享一个静态字段,静态字段实际上属于class的字段(非实例字段)。

  • 推荐用类名来访问静态字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main {
public static void main(String[] args) {
Person ming = new Person("Xiao Ming");
Person hong = new Person("Xiao Hong");
ming.number = 88;// 不推荐用实例变量来访问静态字段。在Java对象中,实例对象没有静态字段
System.out.println(hong.number);// 输出88。编译器根据实例类型自动转换成类型来访问静态字段。
Person.number = 99;
System.out.println(Person.number);
}
}
class Person {
public String name;
public static int number;
public Person(String name) {
this.name = name;
}
}

2、用static修饰的方法称为静态方法

  • 推荐通过类名访问静态方法。
  • 静态方法不属于实例,因此,静态方法内部不能访问this变量,也无法访问实例字段,只能访问静态字段。
  • 静态方法常用于工具类(如Math.random())或辅助方法(如main())。

    3、接口的静态字段

interface是一个纯抽象类,不能定义实例字段。但是可以有静态字段,且必须为final类型。

接口的字段只能是public static final类型。