|第5章 继承
5.1 类、超类和子类
p156
- 名称对应: 超类 - 子类; 基类 - 派生类(derived class); 父类 - 孩子类
- “超”和“子”来源于超集、子集的概念
p158
super
和this
并不类似。super不是一个对象的引用,不可赋值给对象变量,只是一个指示编译器调用超类方法的特殊关键字。- 使用
super
调用超类构造器的语句必须是子类构造器的第一条语句 this
的两个含义:1.隐式参数的引用 2.调用该类的其他构造器
super
的两个含义:1.调用超类方法 2.调用超类的构造器
p159
- 数组保持类型相同的元素,但可以保持超/子类混合的元素。 比如`<超类>[]{<超类实例>,<子类实例>...}
注意:往子类数组里存储超类元素将引发ArrayStoreException异常。
遍历时可以:1
for (<超类> e : 数组) {e.方法;}
当e为超类实例时,调用超类方法;e为子类实例时,调用子类方法。一个对象变量可以指示多种实际类型的现象即为多态;在运行时能够判断对象的实际类型、自动选择适当方法称为动态绑定(dynamic binding)
- 数组保持类型相同的元素,但可以保持超/子类混合的元素。 比如`<超类>[]{<超类实例>,<子类实例>...}
p162
- 继承层次(inheritance hierarchy):由一个公共超类派生出来的所有类的集合
is-a
原则=替换原则(substitution principle):超类任何时候都可以用子类替换
p163
- 重载解析(overriding resolution):方法调用时编译器从若干候补方法中选定签名完全匹配的方法的过程。由于过程耗时,实际会在预先计算好的方法表中搜索
- 静态绑定(static binding):编译器处理调用private/static/final方法时使用的绑定
p165
- 覆盖方法时子类方法的可见性不得低于超类方法
p167
- 在继承链由上至下强制转换时,应使用
instanceof
检查可行性。但应尽量少进行强制转换,而应考虑重新设计超类
- 在继承链由上至下强制转换时,应使用
5.2 Object: 所有类的超类
p176
- equals使用规范:1.自反性 2. 对称性 3.传递性 4.一致性(如果x,y引用的对象不变,反复比较x,y结果也应不变)5.对于任意非空引用x,x.equals(null)为false
p178
- equals必须正确覆盖
Object
类的equals
方法, 应该用@Override检测
- equals必须正确覆盖
p179
- 散列码(哈希值):字符串的散列码由内容导出(容易相同),Object默认的散列码由对象的储存地址导出(基本不会相同)。
Objects.hashCode
方法是null安全的。
p181
toString
大多遵循类名[字段1=字段值,字段2=字段值...]
的格式- 使用“+”连接字符串与对象会自动调用对象的
toString
方法
5.3 泛型数组列表
p188
- 数组列表
ArrayList
可以自动调节容量,也可以手动调用ensureCapacity(int capacity)
提前确保容量,还可以在初始化时指定容量
- 数组列表
p190
- 快速访问数组列表元素技巧:
1 2
var a = new X[list.size]; //list is an ArrayList list.toArray(a);//copy list elements to the Array
- 快速访问数组列表元素技巧:
5.4 对象包装器与自动装箱
p192-193
- 包装器(wrapper):
Integer
,Boolean
... 皆为不可变,不可继承(final),可为null - 自动装箱/拆箱:(编译器)
1 2 3 4 5 6 7 8 9 10 11 12
//boxing var list = new ArrayList<Integer>(); list.add(3); // the same as list.add(Integer.valueOf(3)); //unboxing int n = list.get(i); // the same as int n = list.get(i).intValue; //unboxing then boxing Integer n = 3; n++
注意:Integer数组列表的效率远低于int[]数组,慎用。
- 同一性: 用
==
比较包装器对象通常会失败。注意:介于-128~127的short或int会被包装到固定的对象中。
- 包装器(wrapper):
5.5 参数数量可变的方法
5.6 枚举类
p196
- 枚举类定义的是一个类,每个项是一个实例,不能构造新对象。
- 比较枚举类用
==
不用equals
p197
- 枚举可以拥有私有构造器,枚举类是
Enum
类的子类 - 实用方法:
toString
(Enum to string),Enum.valueOf(EnumClass, String)
(String to Enum),values()
(Enum to array);
- 枚举可以拥有私有构造器,枚举类是
5.7 反射
p198
- 反射:能够分析类能力的程序
p199
- 运行时类型标识:java运行时为所有对象维护的标识信息,通过“Class”类访问
- Class(类)对象:描述一个特定类的属性,有3种获取方法:
实例.getClass()
,Class.forName(className)
,java类型.class
p200
- 每个类型仅有唯一的Class对象,可用“==”比较同一性
p201-p204
- 利用类对象构造实例:
getConstructor( ).newInstance(params)
- 利用类对象加载资源:
URL getResource(String name)
- 检查类结构:
1 2 3 4 5 6 7
Class cl = Class.forName("className"); Class supercl = cl.getSuperclass(); String modifiers = Modifier.toString(cl.getModifiers()); Constructor[] constructors = cl.getDeclaredConstructors(); Method[] methods = cl.getDeclaredMethods(); Field[] fields = cl.getDeclaredFields(); ...
- 利用类对象构造实例:
p208-p210
- 在运行时分析对象:
1 2 3 4 5
var obj = new SomeClass(); Class cl = obj.getClass(); Field f = cl.getDaclaredField("fieldName"); f.setAccessible(true); //若无权限则抛出异常 Object v = f.get(obj); //实例当前"fieldName"字段的值
p210
- 将来可能用 可变句柄(variable handles) 而非反射来读写字段
p214
- 利用反射编写泛型数组:
Object newArray = Arrray.newInstance(cl.getComponentType(), newLength);
- 利用反射编写泛型数组:
p216-p219
- 调用任意方法和构造器
- 可以完成C语言函数指针的所有操作
1 2 3 4 5 6
Method m = cl.getMethod("methodName", double.class); //p2: 参数型 double n = (Double) m.invoke(obj, args);// 返回原返回类型的包装器类型 //静态方法第一个参数为null Constructor c = cl.getConstructor(double.class); // p1: 参数型 Object obj2 = c.newInstance(1.5);