第八节:面向对象高级
记录现阶段感觉有用的知识点,查缺补漏。
继承
-
关键字 extends。
-
Java 里的继承是 单继承。 一个子类 只能继承自 一个父类。
-
子类的内存结构:
类 都是在 堆 上分配内存空间的。 (创建子类对象时,都要先去看看有没有父类)
只不过子类里有个 super( ) 的关键字,指向了父类的内存地址。
也就是说,实现继承的手段,就是在子类的内存空间里,保存了父类的信息(内存地址)。
疑问
this super 代表的是 类? 还是对象?
有构造函数的写法,也有 调用属性名的写法。
代表的对象, 哪构造函数的写法?
重写(Override)
-
里氏替换原则:
通俗点说:子类不能改变父类原有的功能,但是可以扩展父类的功能。
1987年提出来的:
Inheritance should ensure that
any property proved about supertype objects also holds for subtype objects.
-
重写需要满足:(可以用 @Override 注解让 编译器来检查)
- 子类方法 的访问权限 不能小于 父类方法 的访问权限。(可以访问子类方法,就一定能访问父类方法)
- 子类方法 的返回值类型 是 父类方法 的返回类值类型或其子类型。
- 子类方法 抛出的异常 是 父类方法抛出的异常或其子异常。
-
方法的调用顺序:
先去 当前类看有没有相应方法,再去父类看有没有继承过来的相应方法。
如果还不行,就对参数进行类型转换,然后还是先看当前类,再看父类。
public class OverrideTest2 { public static void main(String[] args) { A a = new A(); B b = new B(); C c = new C(); D d = new D(); // 当前就有的方法。 a.show(a); // A.show(A) a.show(c); // A.show(C) // b的父类A中有相应方法。 b.show(c); // A.show(C) // 将b进行类型转换为其父类A。 a.show(b); // A.show(A) // 将d进行类型转换为其父类C。 // b的父类A中有相应的方法。 b.show(d); // A.show(C) // 引用的还是 B对象, 和B对象调用的情况是一样的。 A ba = new B(); ba.show(c); // A.show(C) ba.show(d); // A.show(C) ba.show(a); // B.show(A) } } // 继承关系: A <--- B <--- C <--- D class A{ public void show(A obj) { System.out.println("A.show(A)"); } public void show(C obj) { System.out.println("A.show(C)"); } } class B extends A { @Override // 重写了 A 类的方法。 public void show(A obj) { System.out.println("B.show(A)"); } } class C extends B { } class D extends C { }
-
存在继承的情况下,代码块的初始化顺序:
父类(静态变量、静态代码块)
子类(静态变量、静态代码块)
父类(实例变量、构造代码块)
父类(构造函数)
子类(实例变量、构造代码块)
子类(构造函数)
虽然重写(Override)和重载(Overload)听着很像,但是搞明白他们代表什么,还是很好区分的。。。
final 关键字
-
被 final 修饰过的变量,就是常量。
对于基本类型, final 使其数值不能改变。
对于引用类型, final 使其引用不能改变。但是引用指向的 对象里的数据是可以改变的。
-
public static final :
就是他的名称一样: 全局的(不同包下可访问),静态的(类名可访问),常量(不可变)。
-
final 修饰的方法: 不能被子类重写。
-
final 修饰的类: 不能被继承。
抽象类
有时候我们描述事物的时候,不是把他定死的,也是一个很抽象的事物。
我们就可以用 抽象类 来表示这个事物。
-
包含 抽象方法的类,都是抽象类。 用 abstract 关键字 进行声明。
-
抽象方法没有实现体,只有声明,等着子类继承从而实现他。
子类要实现 父类的所有抽象方法,否则子类仍然是 抽象类。
-
可以有构造方法,但我们不能进行 new 实例化。继承的子类实例化时,JVM会用他的父类抽象类的构造方法
-
抽象类因为要有 子类继承,才有存在意义。所以权限修饰符只能用 public(默认)、protected。
接口
接口是一种规范,是一种更加纯粹的抽象。接口里面只能有 全局常量、和抽象方法。
-
Java8 之前,接口不用有任何方法的实现; Java8 开始,接口里可以有默认方法的实现。
-
用接口, 需要用 implements 关键字来实现。 可以有多个实现。
实现接口的类,仍然要实现接口里所有没有实现的抽象方法。否则是抽象类。
-
接口的字段,默认的就是 public static final;
接口的方法,默认的就是 public abstract。
-
接口之间是允许 多继承的。(继承的还是接口,不要去继承类去了。。。)
就算继承的多个接口中相同的抽象方法也没关系,最后在 “该接口” 中,对该方法只有一种实现方案。
多态
可以简单的理解为 : 父类引用 指向 子类对象。 (向上转型)。
当然还有向下转型的,就是强转,不过这时候需要保证类型所属是正确的,否则就会抛 ClassCast异常。
比如
Animal a = new Bird();
Bird b = (Bird)a; // 这是可以的,类型是正确的。
Animal a2 = new Person();
Bird b2 = (Bird) a2; // 这样就不行了, 人类 不是 鸟类。 会抛 ClassCast 异常。
- instanceof 关键字: 判断某个对象 是不是属于某种类型(或其父类型)。
重写 和 重载 也是属于多态的一种体现。(多态: 多种表现形态)
重写: 字父类中,方法的多态体现。
重载: 一个类中,方法的多态体现。
Object 类
是所有类的 根基类。相当于 对象中 “万物” 的概念。
多态表现:可以接收 任意数据类型的引用。
-
toString()
默认返回是 类名@4554617c 形式。 @后面的数值是 散列码 的 无符号的十六进制表示形式。
推荐都要重写, 返回的是人们可读的 字符串表示形式。
-
equals( )
引用类型用 == 判断的是地址是否相等;
基本数据类型用 == 判断的是 数值是否相等。如果要对 引用类型 用人们常理解的相等的方式来判断相不相等,就要重写 equals() 方法。
equals( ) 方法需要满足5个特性:
-
自反性。
x.equals(x) 始终为true。
-
对称性。
x.equals(y) 和 y.equals(x) 始终相等。
-
传递性。
如果有 x.equals(y), y.equals(z) 都为 true
则有 x.equals(z) 为 true。
-
一致性。
在不改变 x,y的情况下, 多次调用 x.equals(y) 始终一致。
-
非空性。
任何非空 x, 都有 x.equals(null) 始终为 false。
-
转载:https://blog.csdn.net/weixin_43201538/article/details/105850308