1. 多态
什么是多态?是不是被面试官这样问道过。
用书中的话来讲,一个对象变量可以只是多种实际类型的现象称之为多态。
说人话就是,一个变量 bb
他可能既可以是Person
类型,也可能是Student
类型,只要这两个类型有着继承关系即可,根据实际的对象来表现出对应的行为。
话不多说,看代码。
public class Demo1 {
public static void main(String[] args) {
Person person = new Person();
//虽然左边的类型是Person 但最终的对象取决于右边
Person student = new Student();
person.say();
student.say();
//I'm person
//I'm student
}
}
class Person {
public void say() {
System.out.println("I'm person");
}
}
class Student extends Person {
@Override
public void say() {
System.out.println("I'm student");
}
}
可以看到。Student重写了父类的方法,虽然实际类型是父类,但编译器在运行的时候会去找该对象的实际类型是什么再调用对应的方法,这个过程就被称之为动态绑定
。
相对的,一些private,static,final
修饰符,不涉及到父子类的重写,在编译期就可以直接确定了,这被称之为静态绑定
。
2. 子类强转与抽象创建
首先说第一点,子类强转,如果得到的类是父类,但我们需要调用的方法只有子类才有,那么必须要强转。
public class Demo5 {
public static void main(String[] args) {
Dad[] dads = new Dad[3];
dads[0] = new Dad();
dads[1] = new Dad();
dads[2] = new Son();
//因为play方法只有Son类才有 所以只能强转,父类是调用不了的
Son son = (Son) dads[2];
son.play();
}
}
class Son extends Dad {
public void play() {
System.out.println("一起来玩啊");
}
}
class Dad {
}
对于抽象类,要稍微提一嘴,有时候我们可以看到下面示例代码,通过抽象类去接收真实的对象,这并没有任何问题,但如果你需要一个真实的抽象对象,就得像代码第三行一样创建了。
public class Demo5 {
public static void main(String[] args) {
//可以看到 前面的类型可以通过Fish来接收
Fish fish1 = new BlackFish();
Fish fish2 = new RedFish();
Fish fish = new Fish() {
//除非创建匿抽象对象 如果有抽象方法那也需要实现
};
}
}
abstract class Fish {
}
class BlackFish extends Fish{
}
class RedFish extends Fish{
}
3. equals原则
篇幅有点长,这里贴下链接好了
4. 反射编写泛型数组
之前可能也有同学碰到过,如果说我的数组是Object[]
类型的,即便内部装的是int类型,也是无法通过(int[])进行强转的。
所以书中给出了一个方法,可以让我们拓展任意类型的数组,代码也很简单,看下注释就能明白了,如下所示。
public class Demo3 {
public static void main(String[] args) {
int[] arr = {1, 3, 4, 5, 6};
Object arrObj = goodCopyOf(arr, 10);
System.out.println(Arrays.toString((int[]) arrObj));
//[1, 3, 4, 5, 6, 0, 0, 0, 0, 0]
}
/**
* 可以扩展任意类型的数组
*
* @param a 数组
* @param newLength 要扩展的数组长度
* @return 数组对象 可通过强转获取数组
*/
public static Object goodCopyOf(Object a, int newLength) {
//首先获取数组的类型
Class<?> cl = a.getClass();
//传入的必须是一个数组 但用对象类接收 如果不是数组 则直接返回null
if (!cl.isArray()) {
return null;
}
//返回数组的元素类型
Class<?> componentType = cl.getComponentType();
//获取传入数组的长度
int length = Array.getLength(a);
//实例化一个长度为newLength的新数组
Object instance = Array.newInstance(componentType, newLength);
//将原有的数据进行拷贝
System.arraycopy(a, 0, instance, 0, Math.min(length, newLength));
return instance;
}
}
5. 继承的设计技巧
最后说下书中给的建议,如何更好的使用继承。
1.将公共操作和域放在超类,这个很好理解,既然子类都要用到这些字段,就不必一个个的去创建了。
2.不要使用受保护的域,因为你不知道这个类会被接下里多少类所继承,可能在不经意间破坏了其封装性,除非迫不得已,一般都设置为private即可。
3.使用继承实现“ is-a” 关系,书中给出了钟点工和员工不是继承关系,个人感觉这个说明不是很好,但里面提到了一点,员工有工资,小时工是计时工资,这样钟点工类中就含有了两种工资,容易造成混淆,所以最好不要用继承关系,而是新开一个类。
4.除非所有继承的方法都有意义,否则不要使用继承。
5.在覆盖方法时,不要改变预期的行为。
6.使用多态, 而非类型信息,这里的类型信息指的是,无需通过if,else来判断是哪个类,再调用特定的方法,通过多态,编译器自己就能找到对应的行为。
7.不要过多地使用反射。其实反射在编写应用程序时用不太到,我觉得这点可以忽略了,个人认为在架构设计时,反射很有用。