Java基础9--继承--抽象类--接口(一)

2014-11-24 07:48:18 · 作者: · 浏览: 0

9-1,子父类的构造函数-子类的实例化过程

1,在子类构造对象的时候,发现访问子类构造函数时,父类的构造函数也运行了,这时为什么呢?

原因是:在自类的构造函数中第一行有一个默认的隐式语句,就是super()。

super()调用的就是父类中空参的构造函数。

示例:

class Fu{
	Fu() {
		super();//Fu继承自Object,也有一个super(),访问的是Object的空参构造函数
		System.out.println("Fu run ...");
	}
}
class Zi extends Fu {
	Zi() {
		super();//这里访问的是Fu的空参构造函数
		System.out.println("Zi run ...");
	}
}
class ExtendsDemo {
	public static void main(String[] args) {
		new Zi();
	}
}

运行结果是:

Fu run ...

Zi run ...

在本例中,如果Fu没有空参构造函数,如只有一个Fu(int x),则编译出错,在子类中必须显示的调用父类构造函数,并且也必须在Zi构造函数的第一行,比如super(1),即可解决。

继承时,构造函数不能被继承过来,也不能被覆盖。

2,子类的实例化过程

子类中所有的构造函数默认都会访问父类空参的构造函数。

示例:

class Fu {
	Fu() {
		super();
		System.out.println("A Fu run ...");
	}
	Fu(int x) {
		super();
		System.out.println("B Fu run ..." + x);
	}
}
class Zi extends Fu {
	Zi() {
		super();
		System.out.println("C Zi run ...");
	}
	Zi(int x) {
		super();
		System.out.println("D Zi run ..." + x);
	}
}
class ExtendsDemo {
	public static void main(String[] args) {
		new Zi(6);
	}
}

程序结果:

A Fu run ...

D Zi run ...6

实例化过程为:

创建Zi对象,调用Zi的构造函数Zi(int x),在Zi(int x)中有默认的额super(),再调用Fu中的Fu(),Fu()中有super(),调用Object的Object(),这个函数啥都没做,直接return,返回到Fu()中,执行下一句打印,输出A Fu run ...,再返回Zi(int x)输出D Zi run ...6。

若在Zi(int x)中加入super(6),则先调用Fu中的Fu(int x),再输出Zi(intx)中的语句。

9-2,子父类中的构造函数-子类实例化过程细节

1,为什么子类实例化的时候要访问父类中的构造函数呢?

因为子类继承了父类,就取到了父类中的内容(属性),所以在使用父类的内容之前,要先看父类是如何对自己的内容进行初始化的。所以子类在构造对象时,必须访问父类中的构造函数,为了完成这个必须的动作,就在子类的构造函数的第一句中加入了super()语句,这是个默认的隐式语句,不写也会调用。

如果父类中没有定义空参的构造函数,那么子类的构造函数必须用super明确要调用父类中的那个构造函数。

注意:super语句必须定义在子类构造函数的第一行,因为父类的初始化动作要先完成。

子类构造函数中,如果使用this调用了本类的构造函数,那么super这个隐式语句就没有了,因为super和this都只能定义在第一行,所以二者只能存在一个。但是可以保证的是,子类中肯定会有其他的构造函数访问父类的构造函数。

2,示例:

class Demo extends Object {//java中所有的类都继承自Object
	/* 这是类中自带的默认的构造函数,自带有super
	Demo() {
		super();
		return;
	}
	*/
}

从这个例子中可以看出,我们写一个Demo类,在这个类什么都没写的时候,就已经具备注释中的内容了。

9-3,子父类构造函数-子类实例化过程-内存图解

1,一个对象的实例化过程:

以Person p = new Person();为例。

(1)JVM会读取指定路径(classpath)下的Person.class文件,并加载进内存,并会先加载Person的父类(如果有直接父类的情况下)。

(2)在堆内存中开辟空间,分配地址。

(3)并在对象空间中,对对象的属性进行默认的初始化。

(4)调用对应的函数进行初始化。

(5)在构造函数中,第一行会先调用父类中构造函数进行初始化。

(6)父类初始化完毕后,再对子类的属性进行显示初始化。

(7)再进行子类构造函数的特定初始化。

(8)初始化完毕后,将地址值赋给引用变量。

2,示例:

class Fu {
	Fu() {
		super();
		show();
		return;
	}
	void show() {
		System.out.println("fu show");
	}
}
class Zi extends Fu {
	int num = 8;
	Zi() {
		super();
		return;
	}
	void show() { //覆盖了Fu中的show
		System.out.println("zi show ..." + num);
	}
}
class ExtendsDemo{
	public static void main(String[] args) {
		Zi z = new Zi();
		z.show();
	}
}

运行结果:

zi show ... 0

zi show ... 8

\

步骤:

(1)ExtendsDemo类的方法加载进方法区。

(2)main方法加载进静态方法区。

(3)main进栈。

(4)Zi z = new Zi();开始加载Zi类,由于Zi类继承了Fu类,所以Fu类先加载进方法区,然后Zi类加载。

(5)z对象进栈。

(6)实例化z对象,再堆中开辟空间,分配地址,num默认初始化为0。

(7)Zi()构造方法进栈,自带this引用,并指向0X0045。

(8)Zi()中super()引用Fu的构造函数Fu(),Fu()构造方法进栈,自带this引用,指向0X0045,执行show方法,由于Zi类中重写了Fu类中的show方法,把Fu类中的show覆盖,Fu()指向Zi类对象z,调用z中的show方法,且这时num的值为0,故此时输出zi show...0。

(9)Fu()运行结束,弹栈。

(10)回到Zi()构造方法中,继续显示初始化num为8.

(11)Zi()运行结束,弹栈。

(12)实例化完毕,把0X0045赋给z,Zi指向堆内存。

(13)show方法进栈,自带this引用指向0X0045,输出zi show ...8。

(14)show运行完毕,弹栈。

9-4,final关键字

1,继承的弊端:打破了封装性。

2,final关键字:

(1)final是一个修饰符,可以修饰类、方法、变量。

(2)final修饰的类不