第6章 面向对象基础-中

通过extends关键字,可以声明一个子类继承另外一个父类

6.2.3 继承的特点

1.子类会继承父类所有的成员
从类的定义来看,类是一类具有相同特性的事物的抽象描述。父类是所有子类共同特征的抽象描述。而实例变量和实例方法就是事物的特征,那么父类中声明的实例变量和实例方法代表子类事物也有这个特征。

  • 当子类对象被创建时,在堆中给对象申请内存时,就要看子类和父类都声明了什么实例变量,这些实例变量都要分配内存。
  • 当子类对象调用方法时,编译器会先在子类模板中看该类是否有这个方法,如果没找到,会看它的父类甚至父类的父类是否声明了这个方法,遵循从下往上找的顺序,找到了就停止,一直到根父类都没有找到,就会报编译错误。

所以继承意味着子类的对象除了看子类的类模板还要看父类的类模板。

2.Java只支持单继承,不支持多重继承

3.Java支持多层继承

4.一个父类可以同时拥有多个子类

6.3 方法重写

IDEA 重写方法快捷键:CTRL + O

6.4 多态

多态是继封装,继承之后,面向对象的第三大特性。

6.3.2 多态的形式和体现

1.多态引用
Java规定父类类型的变量可以接收子类类型的对象

父类类型 变量名 = 子类对象

父类类型:指子类继承的父类类型,或者实现的父类接口类型。所以说继承是多态的前提。
2.多态引用的表现
表现:编译时类型与运行时类型不一致,编译时看父类,运行时看子类
3.多态引用的好处和弊端
弊端:编译时,只能调用父类声明的方法,不能调用子类扩展的方法
好处:运行时,看子类,如果子类重写了方法,一定是执行子类重写的方法体,变量引用的子类对象不同,执行的方法就不同,实现动态绑定。代码编写更灵活,功能更强大,可维护性和扩展性更好了。
4.多态演示

6.3.3 应用多态解决问题

1.声明变量是父类类型,变量赋值子类对象

  • 方法的形参是父类类型,调用方法的实参是子类对象
  • 实例变量声明父类类型,实际存储的是子类对象

2.数组元素是父类类型,元素对象是子类对象

3.方法返回值类型声明为父类类型,实际返回的是子类对象

instanceof关键字
为了避免ClassCastException的发生,Java提供了instanceof关键字,给引用变量做类型的校验,只要用instanceof判断返回true的,那么强转为该类型就i一定是安全的,不会报ClassCastException

变量/匿名对象 instanceof 数据类型

6.3.5 虚方法

在Java中虚方法是指在编译阶段和类加载阶段都不能确定方法的调用入口地址,在运行阶段才能确定的方法,即可能被重写的方法。

当我们通过“对象xx.方法”的形式调用一个虚方法时,要如何确定它具体执行哪个方法呢?

(1)静态分派:先看这个对象xx的编译时类型,在这个对象的编译时类型中找到能匹配的方法

匹配的原则:看实参的编译时类型与方法形参的类型的匹配程度
A:找最匹配    实参的编译时类型 = 方法形参的类型
B:找兼容      实参的编译时类型 < 方法形参的类型

(2)动态绑定:再看这个对象xx的运行时类型,如果这个对象xx的运行时类重写了刚刚找到的那个匹配的方法,那么执行重写的,否则仍然执行刚才编译时类型中的那个匹配的方法

6.4 实例初始化

1、继承时构造器如何处理

  • 子类继承父类时,不会继承父类的构造器。只能通过super()或super(实参列表)的方式调用父类的构造器。
  • super();:子类构造器中一定会调用父类的构造器,默认调用父类的无参构造,super();可以省略。
  • super(实参列表);:如果父类没有无参构造或者有无参构造但是子类就是想要调用父类的有参构造,则必须使用super(实参列表);的语句。
  • super()和super(实参列表)都只能出现在子类构造器的首行

6.4.2 非静态代码块(了解)

1、非静态代码块的作用

和构造器一样,也是用于实例变量的初始化等操作。

2、非静态代码块的意义

如果多个重载的构造器有公共代码,并且这些代码都是先于构造器其他代码执行的,那么可以将这部分代码抽取到非静态代码块中,减少冗余代码。

3、非静态代码块的执行特点

所有非静态代码块中代码都是在new对象时自动执行,并且一定是先于构造器的代码执行。

4、非静态代码块的语法格式
【修饰符】 class{
    {
        非静态代码块
    }
    【修饰符】 构造器名(){
    	// 实例初始化代码
    }
    【修饰符】 构造器名(参数列表){
        // 实例初始化代码
    }
}

6.4.3 实例初始化过程(了解)

1、实例初始化的目的

实例初始化的过程其实就是在new对象的过程中为实例变量赋有效初始值的过程

2、实例初始化相关代码

在new对象的过程中给实例变量赋初始值可以通过以下3个部分的代码完成:

(1)实例变量直接初始化

(2)非静态代码块

(3)构造器

当然,如果没有编写上面3个部分的任何代码,那么实例变量也有默认值。

3、实例初始化方法

实际上我们编写的代码在编译时,会自动处理代码,整理出一个或多个的(…)实例初始化方法。一个类有几个实例初始化方法,由这个类就有几个构造器决定。

实例初始化方法的方法体,由4部分构成:

(1)super()或super(实参列表)

  • 这里选择哪个,看原来构造器首行是super()还是super(实参列表)
  • 如果原来构造器首行是this()或this(实参列表),那么就取对应构造器首行的super()或super(实参列表)
  • 如果原来构造器首行既没写this()或this(实参列表),也没写super()或super(实参列表) ,默认就是super()

(2)非静态实例变量的显示赋值语句

(3)非静态代码块

(4)对应构造器中剩下的的代码

特别说明:其中(2)和(3)是按顺序合并的,(1)一定在最前面(4)一定在最后面

4、实例初始化执行特点
  • 创建对象时,才会执行
  • 每new一个对象,都会完成该对象的实例初始化
  • 调用哪个构造器,就是执行它对应的实例初始化方法
  • 子类super()还是super(实参列表)实例初始化方法中的super()或super(实参列表) 不仅仅代表父类的构造器代码了,而是代表父类构造器对应的实例初始化方法。

6.5 关键字和API

6.5.1 this和super关键字

1.this和super的意义

this:当前对象

  • 在构造器和非静态代码块中,表示正在new的对象
  • 在实例方法中,表示调用当前方法的对象

super:引用父类声明的成员

无论是this和super都是和对象有关的。

2.this和super的使用格式
  • this
    • this.成员变量:表示当前对象的某个成员变量,而不是局部变量
    • this.成员方法:表示当前对象的某个成员方法,完全可以省略this.
    • this()或this(实参列表):调用另一个构造器协助当前对象的实例化,只能在构造器首行,只会找本类的构造器,找不到就报错
  • super
    • super.成员变量:表示当前对象的某个成员变量,该成员变量在父类中声明的
    • super.成员方法:表示当前对象的某个成员方法,该成员方法在父类中声明的
    • super()或super(实参列表):调用父类的构造器协助当前对象的实例化,只能在构造器首行,只会找直接父类的对应构造器,找不到就报错
3.避免子类和父类声明重名的成员变量

特别说明:应该避免子类声明和父类重名的成员变量

因为,子类会继承父类所有的成员变量,所以:

  • 如果重名的成员变量表示相同的意义,就无需重复声明
  • 如果重名的成员变量表示不同的意义,会引起歧义
4.解决成员变量重名问题
  • 如果实例变量与局部变量重名,可以在实例变量前面加this.进行区别

  • 如果子类实例变量和父类实例变量重名,并且父类的该实例变量在子类仍然可见,在子类中要访问父类声明的实例变量需要在父类实例变量前加super.,否则默认访问的是子类自己声明的实例变量

  • 如果父子类实例变量没有重名,只要权限修饰符允许,在子类中完全可以直接访问父类中声明的实例变量,也可以用this.实例访问,也可以用super.实例变量访问

  • 变量前面没有super.和this.

    • 在构造器、代码块、方法中如果出现使用某个变量,先查看是否是当前块声明的局部变量
    • 如果不是局部变量,先从当前执行代码的本类去找成员变量
    • 如果从当前执行代码的本类中没有找到,会往上找父类声明的成员变量(权限修饰符允许在子类中访问的)
  • 变量前面有this.

    • 通过this找成员变量时,先从当前执行代码的本类去找成员变量
    • 如果从当前执行代码的本类中没有找到,会往上找==父类声明的成员变量(==权限修饰符允许在子类中访问的)
  • 变量前面super.

    • 通过super找成员变量,直接从当前执行代码的直接父类去找成员变量(权限修饰符允许在子类中访问的)
    • 如果直接父类没有,就去父类的父类中找(权限修饰符允许在子类中访问的)
5.解决成员方法重写后调用问题
  • 如果子类没有重写父类的方法,只有权限修饰符运行,在子类中完全可以直接调用父类的方法;
  • 如果子类重写了父类的方法,在子类中需要通过super.才能调用父类被重写的方法,否则默认调用的子类重写的方法

总结:

  • 方法前面没有super.和this.
    • 先从子类找匹配方法,如果没有,再从直接父类找,再没有,继续往上追溯
  • 方法前面有this.
    • 先从子类找匹配方法,如果没有,再从直接父类找,再没有,继续往上追溯
  • 方法前面有super.
    • 从当前子类的直接父类找,如果没有,继续往上追溯

6.5.2 native 关键字

1.native的意义
native : 本地的,原生的
2.native的语法
native只能修饰方法,表示这个方法的方法体代码不是用Java语言实现的,而是由C/C++语言编写的。但是对于Java程序员来说,可以当做Java的方法一样去正常调用它,或者子类重写它。

区域名称作用程序计数器程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址本地方法栈当程序中调用了native的本地方法时,本地方法执行期间的内存区域方法区存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。堆内存存储对象(包括数组对象),new来创建的,都存储在堆内存。虚拟机栈用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型、对象引用,方法执行完,自动释放。

6.5.3 final关键字

final 修饰类 表示这个类不能被继承,没有子类
final 修饰方法 表示这个方法不能被子类重写
final 修饰某个变量 表示它的值就不能被修改,即常量,常量命名建议使用大写字母

面试题:对finalize()的理解?

  • 当对象被GC确定为要被回收的垃圾,在回收之前由GC帮你调用这个方法,不是由程序员手动调用。
  • 这个方法与C语言的析构函数不同,C语言的析构函数被调用,那么对象一定被销毁,内存被回收,而finalize方法的调用不一定会销毁当前对象,因为可能在finalize()中出现了让当前对象“复活”的代码
  • 每一个对象的finalize方法只会被调用一次。
  • 子类可以选择重写,一般用于彻底释放一些资源对象,而且这些资源对象往往时通过C/C++等代码申请的资源内存
(6)重写toString、equals和hashCode方法(Alt+Insert)

建议使用IDEA中的Alt + Insert快捷键,而不是Ctrl + O快捷键

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值