天天看點

Java學習之繼承中的執行順序詳解

代碼塊(了解)

(1)用{}括起來的代碼。

(2)分類:

A:局部代碼塊

用于限定變量的生命周期,及早釋放,提高記憶體使用率。

B:構造代碼塊

把多個構造方法中相同的代碼可以放到這裡,每個構造方法執行前,首先執行構造代碼塊。

C:靜态代碼塊

static{}對類的資料進行初始化,僅僅隻執行一次。

(3)靜态代碼塊,構造代碼塊,構造方法的順序問題?

靜态代碼塊 > 構造代碼塊 > 構造方法

class Student {
	static {
		System.out.println("Student 靜态代碼塊");
	}
	
	{
		System.out.println("Student 構造代碼塊");
	}
	
	public Student() {
		System.out.println("Student 構造方法");
	}
}

class StudentDemo {
	static {
		System.out.println("studentDemo靜态代碼塊");
	}
	
	public static void main(String[] args) {
		System.out.println("我是main方法");
		Student s1 = new Student();
		Student s2 = new Student();
	}
}
           

運作結果如下:

/*
	寫程式的執行結果。
	<span style="font-family: Arial, Helvetica, sans-serif;">studentDemo靜态代碼塊</span>
	我是main方法
	Student 靜态代碼塊
	Student 構造代碼塊
	Student 構造方法
	Student 構造代碼塊
	Student 構造方法
*/
           

繼承(掌握)

(1)把多個類中相同的成員給提取出來定義到一個獨立的類中。然後讓這多個類和該獨立的類産生一個關系,

  這多個類就具備了這些内容。這個關系叫繼承。

(2)Java中如何表示繼承呢?格式是什麼呢?

A:用關鍵字extends表示

B:格式:

class 子類名 extends 父類名 {}

(3)繼承的好處:

A:提高了代碼的複用性

B:提高了代碼的維護性

C:讓類與類産生了一個關系,是多态的前提

(4)繼承的弊端:

A:讓類的耦合性增強。這樣某個類的改變,就會影響其他和該類相關的類。

原則:低耦合,高内聚。

耦合:類與類的關系

内聚:自己完成某件事情的能力

B:打破了封裝性

(5)Java中繼承的特點

A:Java中類隻支援單繼承

B:Java中可以多層(重)繼承(繼承體系)

(6)繼承的注意事項:

A:子類不能繼承父類的私有成員

B:子類不能繼承父類的構造方法,但是可以通過super去通路

C:不要為了部分功能而去繼承

(7)什麼時候使用繼承呢?

A:繼承展現的是:is a的關系。

B:采用假設法

(8)Java繼承中的成員關系

A:成員變量

a:子類的成員變量名稱和父類中的成員變量名稱不一樣,直接通路

b:子類的成員變量名稱和父類中的成員變量名稱一樣,這個怎麼通路呢?

子類的方法通路變量的查找順序:就近原則

在子類方法的局部範圍找,有就使用。

在子類的成員範圍找,有就使用。

在父類的成員範圍找,有就使用。

找不到,就報錯。

B:構造方法

a:子類的構造方法預設會去通路 父類的無參構造方法

1:子類中所有的構造方法預設都會通路父類中空參數的構造方法

2:為什麼呢?

因為子類會繼承父類中的資料,可能還會使用父類的資料。

是以,子類初始化之前,一定要先完成父類資料的初始化。父類的初始化是調用方法區中的構造方法進行初始化,不會建立父類對象,對象是要new關鍵字來建立的(

new關鍵字有兩個作用。一是配置設定記憶體,

建立對象。二是調用構造方法,完成對象的初始化工作。完成這兩步之後,才算建立了一個完整的Java對象。

是以new子類的時候,調用父類的構造方法不是建立了一個父類對象,而是隻對它的資料進行初始化,那麼父類這些資料存儲在哪裡呢? 通俗說 子類對象記憶體區域中會劃一部分區域給父類的資料的存儲,即子類對象記憶體中封裝了父類的初始化資料,建立子類對象時, 父類的資料就是子類的對象的一部分,不存在獨立的父類的對象,所有的東西在一起才是一個完整的子類的對象 ) 注意:子類每一個構造方法的第一條語句預設都是:super();

class Son extends Father {
	public Son() {
		//super();
		System.out.println("Son的無參構造方法");
	}
	
	public Son(String name) {
		//super();
		System.out.println("Son的帶參構造方法");
	}
}	

class ExtendsDemo6 {
	public static void main(String[] args) {
		//建立對象
		Son s = new Son();
		System.out.println("------------");
		Son s2 = new Son("林青霞");
	}
}
           

運作結果:

Father的無參構造方法
Son的無參構造方法
------------
Father的無參構造方法
Son的帶參構造方法
           

b:父類中如果沒有無參構造方法,怎麼辦?

子類通過super去明确調用帶參構造(子類用super();調用父類構造方法隻能在構造方法中的第一行調用)

子類通過this調用本身的其他構造,但是一定會有一個去通路了父類的構造

習慣:最好每次都讓父類提供無參構造

class Father {
	
	/*public Father() {
		System.out.println("Father的無參構造方法");
	}
	*/
	
	public Father(String name) {
		System.out.println("Father的帶參構造方法");
	}
}

class Son extends Father {
	public Son() {
		super("随便給");
		System.out.println("Son的無參構造方法");
		//super("随便給");
	}
	
	public Son(String name) {
		//super("随便給");
		this();
		System.out.println("Son的帶參構造方法");
	}
}

class ExtendsDemo7 {
	public static void main(String[] args) {
		Son s = new Son();
		System.out.println("----------------");
		Son ss = new Son("林青霞");
	}
}
           

運作結果:

Father的帶參構造方法
Son的無參構造方法
----------------
Father的帶參構造方法
Son的無參構造方法
Son的帶參構造方法
           

C:成員方法

a:子類的成員方法和父類中的成員方法名稱不一樣,直接通路

b:子類的成員方法和父類中的成員方法名稱一樣,這個怎麼通路呢?

通過子類對象通路一個方法的查找順序:就近原則

在子類中找,有就使用

在父類中找,有就使用

找不到,就報錯

(9)兩個面試題:

A:Override和Overload的差別?Overload是否可以改變傳回值類型?可以

B:this和super的差別和各自的作用?

this代表本類對應的引用。

super代表父類存儲空間的辨別(可以了解為父類引用,可以操作父類的成員)

怎麼用呢?

A:調用成員變量

this.成員變量 調用本類的成員變量

super.成員變量 調用父類的成員變量

B:調用構造方法

this(..參數.) 調用本類的構造方法

super(..參數.) 調用父類的構造方法

C:調用成員方法

this.成員方法 調用本類的成員方法

super.成員方法 調用父類的成員方法

(10)資料初始化的面試題

A:一個類的初始化過程

/*
	看程式寫結果:
		A:成員變量	就近原則
		B:this和super的問題
			this通路本類的成員
			super通路父類的成員
		C:子類構造方法執行前預設先執行父類的無參構造方法
		D:一個類的初始化過程
			成員變量進行初始化
				預設初始化
				顯示初始化
				構造方法初始化
				
	結果:
		fu
		zi
		30
		20
		10
*/
class Fu{
	public int num = 10;
	public Fu(){
		System.out.println("fu");
	}
}
class Zi extends Fu{
	public int num = 20;
	public Zi(){
		System.out.println("zi");
	}
	public void show(){
		int num = 30;
		System.out.println(num); //30
		System.out.println(this.num); //20
		System.out.println(super.num); //10
	}
}
class ExtendsTest {
	public static void main(String[] args) {
		Zi z = new Zi();
		z.show();
	}
}
           

B:子父類的構造執行過程

/*
	看程式寫結果:
		A:一個類的靜态代碼塊,構造代碼塊,構造方法的執行流程
			靜态代碼塊 > 構造代碼塊 > 構造方法
		B:靜态的内容是随着類的加載而加載
			靜态代碼塊的内容會優先執行
		C:子類初始化之前先會進行父類的初始化
		
	結果是:
		靜态代碼塊Fu
		靜态代碼塊Zi
		構造代碼塊Fu
		構造方法Fu
		構造代碼塊Zi
		構造方法Zi
*/
class Fu {
	static {
		System.out.println("靜态代碼塊Fu");
	}

	{
		System.out.println("構造代碼塊Fu");
	}

	public Fu() {
		System.out.println("構造方法Fu");
	}
}

class Zi extends Fu {
	static {
		System.out.println("靜态代碼塊Zi");
	}

	{
		System.out.println("構造代碼塊Zi");
	}

	public Zi() {
		System.out.println("構造方法Zi");
	}
}

class ExtendsTest2 {
	public static void main(String[] args) {
		Zi z = new Zi();
	}
}
           

C:分層初始化

/*
	看程式寫結果:
		A:成員變量的問題
			int x = 10; //成員變量是基本類型
			Student s = new Student(); //成員變量是引用類型
		B:一個類的初始化過程
			成員變量的初始化
				預設初始化(給預設的值)
				顯示初始化(給我們給變量賦的值)
				構造方法初始化
		C:子父類的初始化(分層初始化)
			先進行父類初始化,然後進行子類初始化。
			
	結果:
		YXYZ
		
	問題:
		雖然子類中構造方法預設有一個super()
		初始化的時候,不是按照那個順序進行的。
		而是按照分層初始化進行的。
		它僅僅表示要先初始化父類資料,再初始化子類資料。
*/
class X {
	Y b = new Y();
	X() {
		System.out.print("X");
	}
}

class Y {
	Y() {
		System.out.print("Y");
	}
}

public class Z extends X {
	Y y = new Y();
	Z() {
		//super
		System.out.print("Z");
	}
	public static void main(String[] args) {
		new Z(); 
	}
}