8. 面向对象编程(中级)
8.1 包和访问修饰符
8.1.1 包的基本语法
包的本质,实际上就是创建不同的文件夹/目录来保存类文件。
例子:
package com.forever.win.anomaly
import java.util.Arrays;
8.1.1.1 包的命名
8.1.2 访问修饰符
8.2 面向对象编程的三大特性(重点)
基本介绍
面向对象编程的三大特性:封装、继承和多态。
8.2.1 封装
封装(encapssulation)就是把抽象出的数据**[属性]**和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作[方法],才能对数据进行操作。
8.2.1.1 封装的实现步骤
隐藏实现细节,仅对外暴露公共的访问方式。
8.2.2 继承
继承的基本语法
class 子类 extends 父类{
}
(1) 子类就会自动拥有父类定义的属性和方法
(2) 父类又叫超类,基类。
(3) 父类又叫派生类。
继承给编程带来的便利
- 代码的复用性提高了
- 代码的扩展性和维护性提高了
继承的细节(重点)
- 子类继承了父类所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,如果子类和父类不在同一个包下面的话,默认的访问修饰符也不能直接访问,要通过父类提供公共的方法去访问。
- 子类必须调用父类的构造器,完成父类的初始化。
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作。否则,编译不会通过。
- 如果希望指定去调用父类的某个构造器,则显式的调用一下:super(参数列表)
- super构造器在使用时,必须放在构造器的第一行
- super()和this()都只能放在构造器的第一行,因此这两个方法不能共存在一个构造器。
- java所有类都是Object类的子类,Object类是所有类的基类。
- 父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类)
- 子类最多只能继承一个父类(指直接继承),即java中是单继承机制。
- 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系,is-a,即什么是一个什么,比如说猫是动物,即猫类可以继承动物类。
super关键字
- 访问父类的属性,但是不能访问父类的private属性,如果不在同一个包下,父类的默认属性也不能访问,super.属性名
- 访问父类的方法,不能访问父类的private方法
- 访问父类的构造器
- super(参数列表);只能放在构造器的第一句,只能出现一句。
super和this 的比较
方法重写@Override
使用细节
- 子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样
- 子类的返回类型和父类方法的返回类型一样,或者是父类返回类型的子类,比如父类返回类型是Object,子类方法返回类型可以是String
- 子类方法不能缩小父类方法的访问权限
- public > protected > 默认 > private
- 子类重写父类的方法时,所抛出的异常要么和父类抛出的异常一致,要么为父类抛出异常类型的子类型
8.2.3 多态
- 一个对象的编译类型可以和运行类型不一致。
- 编译类型在定义对象时,就确定了,不能改变。
- 运行类型是可以改变的。
- 编译类型看定义时 = 号的左边,运行类型看 = 的右边。
多态的向上转型
- 多态的本质就是父类的引用指向了子类的对象。
- 语法 父类类型 引用名 = new 子类类型()。
- 特点:编译类型看左边,运行类型看右边。
- 可以调用父类中的所有成员(需遵守访问权限)
- 不能调用子类中的特有成员。
- 最终运行效果看子类的具体实现。
多态的向下转型
-
语法:子类类型 引用名 = (子类类型) 父类引用。
-
只能强转父类的引用,不能强转父类的对象
-
要求父类的引用必须指向的是当前目标类型的对象。
-
Animal animal = new Cat(); Cat cat = (Cat) animal;//正确 Dog dog = (Dog) animal;//错误
-
-
当向下转型后,可以调用子类类型中的所有的成员。
重点:
public class Properties {
public int a = 1;
public Properties(int a){
this.a = a;
}
public static void main(String[] args) {
Properties properties = new C(45);
//属性没有重写之说!属性的值看编译类型,即properties
//所以输出1,但是可以用构造器来初始化父类属性的参数,所以输出45
//如果该编译类型没有该属性的话,就会报错
System.out.println(properties.a);
}
}
class C extends Properties{
public int a = 2;
public C(int a){
super(a);
}
}
class D extends Properties{
public int a = 3;
public D(int a){
super(a);
}
}
instanceOf
instanceOf 比较操作符,用于判断对象的运行类型(看右边)是否为XX类型或者是XX类型的子类型
动态绑定机制(运行时绑定)
-
当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定。
-
当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。即该属性的值是看编译类型
package dynamicBinding; /** * @Description TODO * @Author zhaojiawei * @Since 2021-08-15 */ public class DynamicBinding { public static void main(String[] args) { A a = new B(); //10,会调用编译类型的属性,因为属性没有动态绑定机制,如果删除了A对象属性n,则会报错,但是也可以强制调用其子类B对象的属性,如:((B) a).n System.out.println(a.n); //30 因为方法有动态绑定机制,所以会调用B的getN方法 System.out.println(a.get()); } } class A { int n = 10; public int say(){ return n; } public int get(){ return n + getN(); } public int getN(){ return n; } } class B extends A{ int n = 20; @Override public int say(){ return n; } // public int get(){ // return n + getN(); // } @Override public int getN(){ return n; } }
==和equals详解
- ==:既可以判断基本类型,也可以判断引用类型
- ==:如果判断基本类型,判断的是值是否相等。
- ==:如果判断引用类型,判断的是地址是否相等,即判断是不是同一个对象。
HashCode
- 提高具有哈希结构的容器的效率
- 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的。
- 两个引用,如果指向的是不同的对象,则哈希值肯定是不一样的
- hashCode一般是通过对象的内部地址经过hash算法转换成一个整数来实现的,因为java是跑在虚拟机上的,找不到对象的内部地址(实际地址)。
toString()
基本介绍
默认返回:全类名+@+哈希值的十六进制,子类往往重写toString方法,用于返回对象的属性信息。
当直接输出一个对象是,toString方法默认会被调用
finallize()
- 当垃圾回收器确定该对象没有指向它的引用时,由对象的垃圾回收器调用此方法。
- 什么时候回收:当某个对象没有任何引用的时候,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法。我们可以重写Object里面的finilize方法来写自己的业务逻辑(比如释放资源等)
- 垃圾回收机制的调用,是由系统来决定的(即有自己的GC算法),也可以通过**System.gc()**主动触发垃圾回收机制。