学习Java语言特性时的笔记,基本没什么参考价值,只是知识点是初学时容易忽略的,整理把它丢在这吧。
继承相关
继承中super与this不是类似的概念,super不是一个对象的引用,不能将super赋给另一个对象变量,它只是一个指示编译器调用超类方法的特殊关键字。super在构造器中的应用有不同的含义。即super具有两个作用:
- 调用超类的方法
- 调用超类的构造器
this的两个用途:
- 引用隐式参数
- 调用该类其他的构造器
上述两个关键字使用方法很相似,调用构造器的语句只能作为另一个构造器的第一条语句出现。构造参数既可以传给本类(this)的其他构造器,也可以传给超类(super)的构造器。
阻止继承:final类和final方法
编写一个完美的equals方法:
+ 显示参数命名为otherObject,稍后需要将它转换为另一个叫做other的变量。
+ 检测this与otherObject是否引用同一个对象
if(this == otherObject) return true;
+ 检测otherObject是否为null,是则返回false
if(otherObject == null) return false;
+ 比较this与otherObject是否属于同一个类
if(getClass() != otherObject.getClass()) return false;
如果所有子类具有统一语义,可使用instanceof检测:
if(!(otherObject instanceof ClassName)) return false;
+ 将otherObject转换为相应的类类型变量,因为otherObject是Object类型的变量:
ClassName other = (ClassName) otherObject;
+ 对需要进行比较的域进行比较;
return field1 == other.field1
&& Objects.equals(field2,other.field2)
&& ...
继承设计的技巧
- 将公共操作和域放在超类
- 不要使用受保护的域
- 使用继承实现“is-a”关系
- 除非所有的继承都有意义,否则不要轻易使用继承
- 在覆盖方法的时候不要改变预期的行为
- 使用多态而非类型信息
对于下面这种形式的代码:
1 | if(x is of type1) action1(x); |
都应该考虑动态多态性。
action1和action2是相同的概念吗?如果是,就应该为这个概念定义一个公共的方法,将其放在两个类的超类或接口中,然后调用x.action()
- 不要过多的使用反射
final关键字
final关键字:final通常指“这是无法改变的”.不想做改变的理由有两个:设计或效率。
- final数据
- 一个永远不改变的编译时常量
- 一个在运行时被初始化的值,而你不希望它被改变。
当对对象引用而不是基本数据类型时,其含义会有一点点令人迷惑。对基本类型,final使得数值恒定不变;对于引用对象,final是引用恒定不变对数组来说具有同样意义。
- 访问权限修饰符、static与final结合
- 定义为public,则可以被用于包外;
- 定义为static,则强调只有一份;
- 定义为final,则说明它是一个常量。当然带恒定初始值的final static基本类型全用大写字母命名,并且词与词之间用下划线隔开。
- 空白final:java允许生成“空白final”,所谓空白final是指被声明为final但又为给定初始值的域。无论什么情况,编译器都确保空白final在使用前必须被初始化。如此一来,一个final域可以根据对象而有所不同,却又保持其恒定不变的特性,提供了更大的灵活性。
- final参数:java允许在参数列表中以声明的方式将参数指明为final.这就意味着你无法在方法中更改参数引用所指向的对象。可读不可改。
- final方法,使用final方法的原因:
- 把方法锁定。
- 效率。
- final和private关键字:类中所有的private方法都隐式地指定为final,由于无法取用private方法,所以也就无法覆盖它。对private添加final并不会增加额外的意义。
- final类:final类禁止继承,所以final类中的所有方法隐式地指定为final,因为无法覆盖它们。
构造器内部的多态方法的行为
如果构造器的内部再调用正在构造的对象的某个动态绑定方法时,那会发生什么情况呢?我们来看一个例子:
1 | class Glyph { |
输出结果:
1 | Glyph() before draw() |
综上分析,你可以发现初始化的过程其实是这样的:
(1)在其他任何事物发生之前,将分配给对象的存储空间初始化为二进制零;
(2)调用基类构造器。此时,调用被覆盖后的draw()方法(要在调用RoundGlyph构造器之前调用),由于步骤一的缘故,我们会发现radiu的值为0.
(3)按照声明的顺序调用成员的初始化方法。
(4)调用导出类的构造器主体。
编写构造器有一条有效的准则:
用尽量可能简单的方法使对象进入正常状态;如果可以的话,尽量避免调用其它的方法。
instanceof与Class的等价性
== 和 equals() 来检查Class对象是否相等;
instanceof和isInstance()生成结果一样,equals()和 == 也一样;
instanceof保持了类型的概念:你是这个类吗,或者你是这个类的派生类吗?而用 == 比较实际的对象,就没有考虑继承。
RTTI与反射之间的区别
RTTI:编译器在编译时打开和检查.class文件
反射:.class文件在编译时是不可获取的,所以是在运行时打开和检查.class文件的。