构造方法可以调用重载的构造方法或它父类的构造方法。如果他们都没有被显示的调用,编译器就会自动的将super()作为构造方法的第一条语句!
如:
public MyConstruct() {
}
public MyConstruct() {
super();
}
以上两个构造方法是完全等价的,因为 当没有显示调用的时候,编译器会自动认为super()为构造函数的第一条语句。
public MyConstruct(int i) {
}
public MyConstruct(int i) {
super();
}
这两个构造方法也是等价的!
在任何情况下,构造一个类的实例时,将会调用沿着继承链的所有父类的构造方法。通俗的说就是在构造一个子类的对象时,子类构造方法会在完成自己的任务之前先调用父类的构造方法,如果父类又继承自其他类,那么父类在完成自己的任务之前也会先调用他的父类的构造方法,一直持续,直到最后一个类的构造方法被调用,这就是构造方法链。
下面通过一个实例来说明:
class Person{
public Person() {
System.out.println("Person");
}
}
class Employee extends Person{
public Employee() {
this("Employee1");
System.out.println("Employee2");
}
public Employee(String str) {
System.out.println(str);
}
}
public class Faculty extends Employee{
public Faculty() {
System.out.println("Faculty");
}
public static void main(String[] args) {
new Faculty();
}
}
程序的输出结果:
下面我们来解释一下为什么会如此这般的输出:
1:程序执行main()方法中的new Faculty();根据构造方法链原理,程序会去调用它的父类的构造方法,所以调用了public Employee(){},在执行之前又调用了Employee父类的构造函数即public Person();此时已经到了终点,所以第一条被打印的语句应该是Person类中的“Person”。
2:打印完“Person”之后,程序并没有结束,而是往回倒,回到了Employee中执行Employee中的逻辑代码,在Employee中我们可以发现第一句有个this关键字,这是显示调用本类中带参的构造函数的意思,所以程序直接跳到了public Employee(String s){XXX};这个构造函数中,所以打印Employee1,打印完之后再执行的public Employee(){}无参构造函数中的"Employee2"。
3:打印完Employee中的字符串之后,又回到了Faculty类,所以最后打印的是“Faculty”
动态绑定:
先看一个问题:
Object o = new Car();
System.out.println(o.toString());
程序是调用Object中的toString()方法呢?还是调用Car中的toString()方法呢?
我们先来看两个概念:声明类型和引用类型。
一个变量必须被声明为某种类型,变量的这个类型被称为声明类型,上述代码中的o的声明类型是Object;实际类型是被变量引用的对象的实际类,上述代码中o的实际类型为Car。
动态绑定的工作机制:假设对象o是类C1,C2,C3...Cn的实例,其中C1是C2的子类,C2是C3的子类...以此类推。如果对象o调用一个方法fun();那么java 虚拟机会依次在C1,C2,...Cn中查找方法fun();的实现,知道找到为止,一旦找到一个实现,就会停止查找然后调用这个第一次找到的实现。
下面通过一个示例来展示:
class People extends Object{
@Override
public String toString() {
return "People";
}
}
class Student extends People{
@Override
public String toString() {
return "Student";
}
}
class GraduateStudent extends Student{
//这个方法中不对toString()方法进行重写
}
public class DynamicBindTest {
public static void main(String[] args) {
fun(new GraduateStudent());
fun(new Student());
fun(new People());
fun(new Object());
}
/**
* 这个方法的类型是Object,所以通用类型都可以传进来
* @param obj
*/
public static void fun(Object obj){
System.out.println(obj.toString());
}
}
程序的输出结果为:
如此这般输出的原因:
1:fun(new GraduateStudent())执行时会调用fun()方法中的obj.toString()方法,GraduateStudent继承Student,Student继承People,People又继承Object,根据动态绑定的原理,会依次查询GraduateStudent->Student->People->Object中的toString()方法,一旦找到就立调用并停止查询,GraduateStudent类中没有实现toString()方法,所以到了Student类中,发现这个类中实现了toString()方法,程序马上调用,然后停止,即第一个输出的是Student类中的"Student"
2:执行fun(new Student())时同样的道理,依次寻找toString()方法,然后发现Student类中就实现了toString()方法,调用然后停止,即输出第二个也是Student类中的"Studnet"。
3:执行fun(new People())时也是同样的道理,输出的是people类中的"People"。
4:执行fun(new Object())时,输出的是Object类中的toString()方法!
到这里,这两个知识点就讲完了,切记不要混淆两个概念,构造方法链是一直往上找,找到最后的类进行执行,而动态绑定也是往上找,只不过一旦找到就立即调用并马上停止!