1. 程式人生 > >Java學習個人備忘錄之繼承

Java學習個人備忘錄之繼承

創建 多態 overload 多層 erl 運行 str 隱式 class

繼承的好處
1. 提高了代碼的復用性。
2. 讓類與類之間產生了關系,給第三個特征多態提供了前提。

java中支持單繼承,不直接支持多繼承,但對C++中的多繼承機制進行改良。
java支持多層繼承。

C繼承B,B繼承A,就會出現繼承體系。

當要使用一個繼承體系時:
1. 查看該體系中的頂層類,了解該體系的基本功能。
2. 創建體系中的最子類對象,完成共能的使用。

什麽時候定義繼承呢?
當類與類之間存在著所屬關系的時候,就定義繼承,xxx是yyy中的一種, xxx extends yyy。

class Person
{
	String name;
	int age;
}

class Student extends Person   //繼承
{
	// String name;
	// int age;
	void study()
	{
		System.out.println(name+"...student study...."+age);
	}
}

class Worker extends Person
{
	// String name;
	// int age;
	void work()
	{
		System.out.println("worker work");
	}
}

class ExtendsDemo
{
	public static void main(String[] args)
	{
		Student s = new Student();
		s.name = "zhangsan";
		s.age = 20;
		s.study();
	}
}



在子父類中,成員的特點體現
1.成員變量
2.成員函數
3.構造函數


1. 成員變量
當本類的成員和局部變量同名時,用this區分;
當子父類中的成員變量同名時,用super區分父類。

this和super的用法很相似。

this:代表一個本類對象的引用。
super:代表一個父類的空間,並不代表父類對象。

class Fu
{
	int num = 4;
}

class Zi extends Fu
{
	int num = 5;
	void show()
	{
		System.out.println(super.num+"..."+num);
	}
}

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



2. 成員函數
當子父類中出現成員函數一模一樣的情況,會運行子類的函數。
這種現象,稱為覆蓋操作,這時函數在子父類中的特性。
函數兩個特性:
1.重載. 同一個類中,overload
2.覆蓋. 子類中,覆蓋也稱為重寫,override


覆蓋註意事項:
1.子類方法覆蓋父類方法時,子類權限必須要大於等於父類的權限。
2.靜態只能覆蓋靜態,或被靜態覆蓋。

class Fu
{
	void show()
	{
		System.out.println("fu show run");
	}
}

class Zi extends Fu
{
	void show()
	{
		System.out.println("zi show run");
	}
}

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



什麽時候使用覆蓋?


當對一個類進行子類的擴展時,子類需要保留父類的功能聲明。
但是要定義子類中該功能的特有內容時,就是用覆蓋操作完成。

class Phone
{
	void call()
	{}
	void show()
	{
		// System.out.println("pic");
		// System.out.println("name");
		System.out.println("number");
	}
}


應用場景
現在手機升級了,加了顯示姓名和圖片的功能。
但是如果我直接在源代碼上修改會增加修改成本,不好。
這時候我們就可以用繼承來解決。

class NewPhone extends Phone
{
	void show()
	{
		System.out.println("pic");
		System.out.println("name");
		// System.out.println("number");
		super.show();
	}
}

class PhoneDemo
{
	public static void main(String[] args)
	{
		// Phone p = new Phone();
		// p.show();
		NewPhone p = new NewPhone();
		p.show();
	}
}



子父類中構造函數的繼承

在子類構造對象時,發現訪問子類構造函數時,父類也運行了。
為什麽呢?
原因是:在子類的構造函數中第一行有一個默認的隱式語句---super();


子類的實例化過程:子類中所有的構造函數默認都會訪問父類中的空參數的構造函數。

class Fu
{
	Fu()
	{
		System.out.println("fu run");
	}
}
class Zi extends Fu
{
	Zi()
	{
		//super();  //默認的隱式語句.  調用的就是父類中的空參數的構造函數.
		System.out.println("zi run");
	}
}

class ExtendsDemo4
{
	public static void main(String[] args)
	{
		new Zi();
		/*
		結果為:
		fu run
		zi run
		*/
	}
}


還有一種情況

class Fu
{
	Fu()
	{
		System.out.println("fu run");
	}
	Fu(int x)
	{
		System.out.println("fu run...."+x);
	}
}
class Zi extends Fu
{
	Zi()
	{
		//super();  //默認的隱式語句.  調用的就是父類中的空參數的構造函數.
		System.out.println("zi run...");
	}
	Zi(int x)
	{
		System.out.println("zi run...."+x);
	}
}

class ExtendsDemo4
{
	public static void main(String[] args)
	{
		new Zi(4);
		/*
		結果為:
		fu run
		zi run....4
		*/
	}
}



為什麽子類實例化的時候要訪問父類中的構造函數呢?
那是因為子類繼承了父類,獲取到了父類中的內容(屬性),所以在使用父類內容之前,要先看父類是如何對自己的內容進行初始化的。

所以子類在構造對象時,必須訪問父類中的構造函數。
為什麽完成這個必須的動作,就子啊子類的構造函數中加了super(). 語句。

如果父類中沒有定義空參數構造函數,那麽子類的構造函數必須用super明確要調用父類中哪個構造函數。

同時子類構造函數中如果使用this調用了本類構造函數時,
那麽super就沒有了,因為super和this都只能定義第一行.所以只能有一個。
但是可以保證的是,子類中肯定會有其他的構造函數訪問父類的構造函數。

註意:super語句必須要定義在子類構造函數的第一行,因為父類的初始化動作要先完成。

class Fu
{
	Fu()
	{
		super();
		show();
		return;
	}
	void show()
	{
		System.out.println("fu show");
	}
}
class Zi extends Fu
{
	int num = 8;
	Zi()
	{
		//super();  //默認的隱式語句.  調用的就是父類中的空參數的構造函數.
		//-->通過super初始化父類內容時,子類的成員變量並未顯示初始化.
		//等super()父類初始化完畢後,才進行子類的成員變量顯示初始化.
		//也就是說要先忙完父類那邊的事,然後才能忙自己的事.
		System.out.println("zi cons run..."+num);
	}
	void show()
	{
		System.out.println("zi show ..."+num);
	}
}

class ExtendsDemo5
{
	public static void main(String[] args)
	{
		Zi z = new Zi();
		z.show();
		/*
		結果為:
		zi show...0
		zi cons run...8
		zi show...8
		*/
	}
}



一個對象實例化的過程:
Person p = new Person();
1. jvm會去讀取指定路徑下的Person.class文件,並加載進內存。
並會先加載Person的父類(如果有直接的父類的情況下)。
2. 在堆內存中的開辟空間,分配地址。
3. 並在對象空間中,對對象中的屬性進行默認初始化。
4. 調用對應的構造函數進行初始化。
5. 在構造函數中,第一行會先調用父類中的構造函數進行初始化。
6. 父類初始化完畢後,再對子類的屬性進行顯示初始化。
7. 在進行子類構造函數的特定初始化。
8. 初始化完畢後,將地址賦值給引用變量。

Java學習個人備忘錄之繼承