抽象类
前面提到,要想使用多态,就必须得有继承和方法重写,但目前无法强制要求子类是否重写方法,子类到底重写方法与否,重写哪些方法,全凭自愿。若需要对子类的重写方法做出一些强制性的要求,就会用到抽象类。
1、定义
所谓抽象类其实就是比普通类多了一些抽象方法而已,是普通类的超集(普通类有的,抽象类都有)。
2、抽象类和抽象方法的定义:abstract关键字。抽象方法所在的类一定是抽象类,可以包含任何抽象方法;抽象方法只有方法声明,没有方法体。
注意:在Java中,没有方法体的方法不一定是抽象方法!!还有一类方法没有方法体,使用native关键字进行方法声明,这种方法称为本地方法。本地方法是指Java调用C++实现的代码,具体的方法实现在C++代码中,Java只是调用。
3、抽象类必须有子类,抽象类不能自己实例化对象,要通过子类对象向上转型为其实例化。
4、子类继承抽象类,若子类是普通类,必须覆写抽象类中所有的抽象方法,更好的保证多态正确运行。
若子类也是抽象类,可以选择性的覆写方法,一个抽象方法都不覆写也可以。
5、abstract和final不能同时出现;abstract和private也不能同时出现。(abstract修饰的方法必须被覆写,但final和private修饰的方法不能覆写)
6、抽象类只是比普通类多了一些抽象方法而已,抽象类的内部仍然可以存在构造方法和普通方法。
虽然抽象类无法直接实例化对象,子类在产生对象时,任然满足继承的原则,先调用父类的构造方法,实例化父类然后再调用子类构造。
接口
在Java中,在出现能使用抽象类也能使用接口的场景下,优先考虑使用接口实现。
1、接口的定义
使用关键字interface关键字来定义,接口中只存在全局常量和抽象方法(JDK8之前)。
接口的命名规范:使用大写的I开头。
2、接口的子类称为实现接口。
子类要实现接口,使用implements关键字来表示子类实现接口,子类必须覆写接口中的所有抽象方法(前提:子类是普通类)
子类的命名:以父类接口名称开头,以impl结尾。
3、接口中只能定义全局常量和抽象方法,不能存在构造方法,成员变量等等都不行,因此接口是一个更加"纯粹"的抽象类,接口无法直接实例化对象,需要通过子类向上转型为其实例化。
4、接口的使用原则
a、由于接口中只存在抽象方法和全局常量,因此在定义接口时,以下关键字通通都可以省略,且在阿里编码规约中明确表示,以下关键字不要出现在接口的定义中。
public
:描述属性
static、final
:描述常量
abstract
:描述抽象方法
如何证明getNews确实是public权限?通过子类实现接口,子类覆写抽象方法时必须要遵循子类权限大于等于父类的权限,子类不写的话就是包访问权限。
如何证明MSG属性时static和final共同修饰的?
b、子类可以同时实现多个父类接口(打破抽象类的单继承局限)。
若子类同时实现了多个父类接口,则必须覆写这些接口中所有的抽象方法(前提:子类是普通类)
c、当子类同时需要实现多个父接口,以及一个抽象类时,先使用extends继承一个类,而后使用implements实现多个接口。
d、接口不能使用extends继承类,但是接口可以使用extends来继承父接口。
5、接口的应用场景
a、表示标准或规范。例如,无论USB接口的子类有哪些,这些子类都需要实现这两个方法。
b、表示一种能力
IRun接口的三个子类Cat、Dog、Duck之间并没有太大的联系,只是都具备跑这个能力。
6、JDK中常用的接口
a、java.lang.Comparable接口。实现这个接口,必须实现CompareTo方法,就是比较大小的标准。
基本数据类型,变量之间的大小关系时确定的,根据其保存的数值大小进行比较。
对于自定义类型来说,不存在明确的大小关系,因此,自定义的类实现Comparable接口,覆写ComparaTo方法,就是表示实现大小比较的标准。
Comparable接口定义的ComparaTo方法:
以Student类为例,覆写之后:
b、java.lang.Cloneable
此时,Animal的对象在JVM中运行时就具备了可以复制/拷贝的能力。
凡是实现了这种标记接口的子类,在JVM中运行的时候,JVM就会识别出这些带标记的接口子类。
在Java中一个类要实现克隆的能力,需要两步走:
(1)在类的定义之上实现Cloneable接口:打标记,JVM只会将带Cloneable接口的子类赋予其可以拷贝的能力。
(2)在类中覆写Object类的clone方法:具体实现的克隆方法。
Object类中的clone方法:
覆写clone方法:
克隆对象的属性值完全赋值原对象的值:
如何证明克隆对象确实是新产生的对象?
深浅拷贝
1、浅拷贝
当前类中若包含了其他类的引用,拷贝对象只是复制引用的地址,克隆后的新对象不会产生新的内部对象,浅拷贝只是将原对象中的属性值复制一份。
2、深拷贝
产生的拷贝对象内部,包含的其他类对象也会复制一个新的对象产生。
以下两种方法可以实现深拷贝:
(1)递归的clone方法调用;
(2)通过序列化的方式:现代所谓的json序列化和反序列化就是在进行深浅拷贝。
对比抽象类和接口
区别 | 抽象类 | 接口 |
---|---|---|
结构组成 | 普通类中的所有方法+抽象方法 | 全局常量+抽象方法 |
权限 | 各种权限都有(public…private) | 只有public权限 |
关于子类 | 使用extends继承抽象类,单继承 | 使用implements实现接口,多实现 |
彼此关系 | 抽象类可以使用implements实现接口 | 接口不能继承抽象类,但可以使用extends继承多个父接口 |
包装类
1、定义:将基本数据类型值封装在类中,这种类称为包装类。
JDK中的包装类:
2、将基本类型保存到对应的包装类中称为装箱,将包装类对象还原为具体的基本类型称为拆箱。当包装类参与数学运算时需要频繁的进行装箱和拆箱操作,增加了代码的复杂度。JDK1.5之后引入了自动拆装箱机制:
自动拆装箱也是一种java“语法糖”,只存在于源代码阶段,编译结束由javac搞定拆装箱过程。
3、何时使用包装类?何时使用基本类型?
阿里编码规约要求:
在类中定义的属性,推荐使用包装类;在方法中定义的局部变量,可以使用基本类型。
4、关于包装类对象的相等比较问题!!!!!!!!!!!(巨坑)
整型包装类有缓存,默认缓存-128~127,当第一此包装类对象的数值在这个区间内,产生新的包装类对象;当第二次以及以后又出现该数值,直接复用已有的对象。而在缓存区间外的数值,不会复用已有的对象,每次自动装箱都会产生新对象!
为了避免这个坑,凡是引用类型的比较,千万别使用"==",一定要使用equals方法来进行对象的属性相等比较。JDK中常用的类,包装类,String类都覆写了equals方法进行属性值的比较,而非地址。