继承的基本思想是可以基于已有的类创建新的类;
反射是指在程序运行期间更多的了解类及其属性的能力;
5.1 类,超类和子类
is-a
关系是继承的一个明显特征;- 关键字
extends
表示继承;
public class Manager extends Employee
{
added methods and fields
}
- Java中所有的继承都是公共继承;
- 可以使用
super
关键字调用超类方法; - 由于子类不能访问超类的私有字段,但可以通过特殊语法
super()
来调用构造器初始化这些私有字段,super()
调用构造器语句必须是子类构造器的第一条语句。 - 子类没有显示调用超类构造器,将自动调用超类的无参构造器,子类没有显式调用又没有无参构造器JAVA编译器就会会报错;
- 一个对象变量可以指示多种实际类型的现象称为多态;
- 在运行时能够自动的选择适当的方法,称为动态绑定;
- 由一个公共超类派生出来的所有类的集合称为继承层次;继承层次中从某个特定的类到其祖先的路径称为该类的继承链;
is-a
规则的另一种表述是替换原则,他指出程序中出现超类对象的任何地方都可以使用子类对象替换;- 不能将超类的应用赋值给子类对象;“不是所有的员工都是经理”;
- java中子类引用的数组可以转换成超类引用的数组,而不需要使用强制类型转换;
- 再覆盖一个方法的时候,子类方法不能低于超类方法的可见性;
- 不允许扩展的类称为
final
类;final
类中的方法自动称为final
,不包括字段。 - 类中的方法被声明为
final
,子类就不能覆盖这个方法; - 进行强制类型转换的唯一原因是:要在暂时忽视对象的实际类型之后使用对象的全部功能;
- 一个良好的程序设计习惯:在进行强制类型转换之前先查看是否能够成功的转换;
if (staff[1]) instanof Manger)
{
boss =(Manger) staff[1];
...
}
- 只能在继承层次内进行强制转换;
- 在将超类强制转换成子类之前,应该使用
instanceof
进行检查; - 一般境况下最好尽量少用强制类型转换和
instanceof
;
5.1.9 抽象类
- 包含一个或多个抽象方法的类本身必须被声明为抽象的
abstract
; - 抽象方法充当着占位方法的角色,他们在子类中具体实现;
- 扩展抽象类可以有两种选择,一种是在子类中保留抽象类中部分或所有方法仍未定义,这样就必须将子类也标记为抽象类;另一种是定义全部方法,这样子类就不是抽象类了。
- 抽象类不能实例化;
- 可以定义一个抽象类的实例变量,但只能引用非抽象子类的对象;
- 如果省略超类中的抽象方法,只在子类中定义方法,就不能使用超类变量调用子类方法;
5.1.10 受保护访问
- 最好将类中的字段标记为
private
,而方法标记为public
; - 限制超类中的某个方法只允许子类访问,或者希望允许子类的方法访问超类的某个字段,可以声明为
protected
; - 保护字段只能由同一个包中的类访问;
Object:所有类的超类
- 如果没有明确的指出超类,object就被认为是这个类的超类;
- 可以使用object类型的变量引用任何类型的对象;但是要想对对象进行具体的操作,还需要清楚对象的原始类型,并进行相应的强制类型转换;
- 在java中只有基本类型不是对象;
- object类中的
equals
方法通过确定两个对象引用是否相等来检测一个对象是否等于另一个对象; equals
方法要求具有以下特性:1.自反性;2.对称性;3.传递性;4.一致性;5.对于任何非空引用x,xequals(null)
应该返回false
;- 如果隐式和显示的参数不属于同一个类,equals方法将如何处理呢?
- 如果子类可以有自己的相等性概念,则对称性需求将强制使用
getclass
检测; - 如果由超类决定相等性需求。那么就可以使用
instanceof
检测,这样可以哦在不同子类的对象之间进行相等性比较。
- 编写一个完美的
equals
方法的建议;- 显示参数命名为
otherObject
,稍后需将他强制转换为另一个名为other
的变量; - 检测
this
和otherObject
是否相等:if (this==otherObject) return true;
- 检测
otherObject
是否为null
,如果为null
,返回false
。这项检测是很有必要的:if (otherObject==null) return false ;
- 比较
this
与otherObject
的类。如果equals语义可以在子类中改变,就使用getClass
检测;如果所有子类都有i相同的相等性语义,可以使用instanceof检测; - 将
otherObject
强制转换为相印类型的变量;ClassName other=(Classname)otherObject
- 现在根据相等性概念要求来比较字段,基本类型用
==
,对象字段用object.equals
,如果在子类重新定义equals
,就要在其中包含一个super.equals(other)
调用;
- 显示参数命名为
- hashCode方法: **散列码(hash Code)**是有对象导出的一个整型值,不同对象基本上不会相同;
equals
和hashCode
定义必须相容:如果x.equals(y)
返回true
,那么x.hashcode()
与y.hashcode()
返回相同的值;- toString方法:返回表示对象值的一个字符串,设计子类的程序员应该定义自己的
toString
方法,并加入子类的字段 ;
5.3 泛型数组列表
- java允许运行时确定数组大小;
ArrayList
是一个有类型参数的泛型类,为了指定数组列表保存的元素对象的类型,需要用一对尖括号将类名括起来追加到ArrayList
后面;- 在java 10中可以用
var
关键字以避免重复写类名; - 如果没有使用
var
关键字,可以省去右边的类型参数,这称为菱形语法; - 使用
add
方法可以将元素添加到数组列表中;如果调用add
方法而内部数组已经满了,数组列表会自动创建一个更大的数组,并将所有对象拷贝到较大数组中; - 如果已经知道或能够估计出数组可能储存的元素数量,就可以在填充数组之前调用
ensureCapacity
方法,节省开销;这与分配一个新数组不同; size
方法将返回数组列表中包含的实际元素个数;- 一旦能确定数组列表大小保持恒定不再变化,就可以调用
trimToSize
方法,将储存块的容量调整为当前所需储存空间,垃圾回收器将回收多余存储空间;确认不变时在调用,以免增大开销; ArrayList
是一个实用工具类,访问和改变数组元素需用get
和set
方法;
5.4对象包装器和自动装箱
- 所有的基本类型都有一个与之对应的类,通常这些类被称为包装器;
- 包装器类是不可可变的,包装器还是
final
,因此不能派生他们的子类; - 尖括号中的参数不允许是基本类型;
- 由于每个值分别包装在对象中,所以
ArraryList<>
效率远低于[]
; - 装箱和拆箱是编译器要做的工作;
5.5参数数量可变的方法
- 可以提供参数数量可变的方法,有时称为“变参”方法;
- 省略号
...
是javaj代码的一部分,它表明和这个方法可以接受任意数量的对象,对于printf
的实现者来说,Object...
参数类型和Object[]
完全一样;
5.6枚举类
5.7 反射
- 能够分析类能力的程序称为反射;
- java运行时系统始终为所有对象维护一个运行时类型标识,这个信息会跟踪每个对象所属的类,虚拟机利用运行时类型信息选择要执行的正确的方法;可以使用一个特殊的类访问这些信息Class类;
Object
类中的getClass()
方法将返回一个Class
类型的实例;- 一个
Class
对象实际上表示的是一个类型,可这能是类,也可能不是类。例如,int
不是类,但int.class
是一个Class
类型的对象。 - 虚拟机为每一个类型管理一个唯一的
Class
对象,因此,可以利用==
运算符实现两个类对象的比较; - 还可以使用静态方法
forName
获得类名对应的Class
对象:
String className=" java.util.Random";
Class cl=Class.forName(className);
- 如果有一个
Class
类型的对象,可以用他来构造类的实例。调用getConstructor
方法将得到一个Constructor
类型的对象,然后使用一个newInstance
方法来构造一个实例;如果这个类没有无参数构造器,getConstructor
方法会抛出一个异常。
var className ="java.util.Random";
Class cl=Class.forName(className);
Object obj=cl.getConstructor().newInstance();
5.7.2声明异常入门
- 异常有两种类型:非检查型异常和检查型异常;
- 如果一个方法包含一条可能抛出检查型异常的语句,则在方法名前增加一个
throws
子句; - 只要你调用了一个可能抛出检查型异常的方法而没有提供相应的异常处理器,编译器就会报错;
5.7.4利用反射分析类的能力
- 在
java.lang.reflect
包中有三个类Field
,Method
和Constructor
分别用于描述类的字段,方法和构造器。 - 详情见代码
ReflectionTest.java
;
5.7.5使用反射在运行时分析对象
- 利用反射机制可以查看在编译时还不知道的对象字段;利用
Field
类中的get
方法 - 只能对可以访问的字段使用
get
和set
方法; - 反射机制的默认行为受限于Java的访问控制,不过可以调用
Field
,Method
,Constructor
,对象的setAaccessible
方法覆盖java的访问控制;f.setAccessible(true)
;
5.7.6使用反射编写泛型数组代码
5.7.7调用任意方法和构造器
- 建议仅在绝对必要的时候才在你自己的程序中使用
Method
对象。
5.8 继承的设计技巧
- 将公共操作和字段放在超类中;
- 不要使用受保护的字段;
- 使用继承实现
is-a
关系; - 除非所有继承的方法都有意义,否则不要使用继承;
- 在覆盖方法时不要改变预期的行为;
- 使用多态而不用使用类型信息;
- 不要滥用反射;