1. 程式人生 > >黑馬程式設計師——Java面向物件(二)之封裝、繼承、多型、介面等

黑馬程式設計師——Java面向物件(二)之封裝、繼承、多型、介面等

-----------android培訓java培訓、java學習型技術部落格、期待與您交流!------------

五、面向物件的特徵

 面向物件主要有三大特徵:

1.特徵一 —— 封裝

 1)定義:是指隱藏物件的屬性和實現細節,僅對外提供公共訪問方式。

2)好處:

  a)將變化隔離。

  b)便於使用。

  c)提高重用性。

  d)提高安全性。

 3)封裝原則:

  a)將不需要對外提供的內容都隱藏起來。

  b)把屬性都隱藏,提供公共方法對其訪問。

 4)封裝表現形式(private):

  什麼是private?

  private(私有)是一種許可權修飾符,用於修飾類中的成員(成員變數,成員函式、建構函式)。

 注:私有隻在本類中有效,僅僅只是封裝的一種表現形式。

  封裝的作用:

  將成員變數私有化,對外提供對應的set,get方法對其進行訪問,提高對資料訪問的安全性。如果對建構函式進行私有,則不能對該類建立物件。

 5)程式碼例項:

//建立一個Person類,用來描述人的事物
class Person
{
	//將age私有化
	private int age;
	//將name私有化
	private String name;

	//設定年齡
	public void setAge(int age)
	{
		if(age>0&&age<120)
		{
			this.age=age;
		}
		else
			System.out.println("非法年齡");
	}
	//設定姓名
	public void setName(String name)
	{
		this.name=name;
	}

	//獲取年齡
	public int getAge()
	{
		return age;
	}
	//獲取姓名
	public String getName()
	{
		return name;
	}

}
class PersonDemo
{
	public static void main(String[] args) 
	{
		//建立person物件
		Person p=new Person();

		p.setAge(40);//設定年齡
		p.setName("張三");//設定姓名
		
		System.out.println("age="+p.getAge());//獲取年齡並列印
		System.out.println("name="+p.getName());//獲取姓名並列印
		
	}
}
 在該例項中,將Person類中age和name通過私有進行隱藏,而提供setAge()、setName()和getAge()、getName()的公共訪問方式分別進行設定和獲取,因此很好體現了封裝的特性。執行結果如下圖:



 2.特徵二 —— 繼承

1)定義:當多個類中存在相同屬性和行為時,將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行為,只需通過一種方式去獲取這些共性內容即可,而這種方式就稱為繼承。

 2)作用:

  a)提高了程式碼的複用性。

  b)讓類與類之間產生了關係,有了這個關係,才有了多型。

 注:千萬不要為了獲取其他類中的功能,簡化程式碼而繼承。必須是類與類之間有所屬關係才可以繼承。

3)特點:

  a)java中類與類之間只支援單繼承,不支援多繼承,即只能繼承一個類,但有一種情況例外,就是介面可以實現多繼承。

  b)當子類和父類存在一模一樣的函式(包括返回值型別)時,子類的函式會覆蓋父類的相同函式。

  c)子類訪問本類中的引用,用this,子類訪問父類中的引用,用super。如果存在多級繼承,那麼super會先訪問直接父類,沒有再找上一級,即逐級訪問。

  d)子類要成功覆蓋父類,那麼子類的許可權必須大於或等於父類的許可權,靜態只能覆蓋靜態。

  e)子類繼承父類,只延襲父類非私有內容。

 4)子父類建構函式:

  a)子類建構函式預設第一行會存在super()語句,用來訪問父類建構函式,如果父類的建構函式為自定義建構函式,且非預設形式,則子類需指定訪問父類的建構函式。

  b)父類建構函式優先子類建構函式初始化。

  c)子類所有的建構函式,預設都會訪問父類中空引數的建構函式,因為每一個建構函式內的第一行都有一句隱式super(),子類的建構函式第一行也可以手動指定this語句來訪問本類中的建構函式,但子類至少有一個建構函式會訪問父類的建構函式。

 5)問題思考:

a)為什麼類與類之間只支援單繼承?

  因為如果一個類可以同時繼承多個類,多個類中可能出現相同函式,但功能不同,那麼在子類中就可能存在多個相同函式,但一個類中是不允許存在多個相同函式,只能通過覆蓋,但java無法確定多個父類中的同名函式的覆蓋順序,所以是不允許多繼承。

  b)為什麼this和super不能同時在建構函式當中?

  因為this和super語句必須位於建構函式的第一行,所以不可能同時存在建構函式當中。

  c)為什麼this和super語句必須位於第一行?

  因為this和super語句是用來進行初始化,初始化動作要先執行。

 6)程式碼例項:

//定義一個Person類
class Person
{
	//定義人的屬性,姓名和年齡
	String name;
	int age;

	//初始化物件的屬性
	Person(String name,int age)
	{
		this.name=name;
		this.age=age;
		System.out.println(name+"----"+age);
	}
	
	//人都具有吃飯的功能
	public void eat()
	{
		System.out.println("吃飯");
	}

}

//定義一個Student類,並繼承Person類
class Student extends Person
{
	Student(String name,int age)
	{
		super(name,age);//由於父類已經有name和age的屬性,因此只需呼叫父類建構函式進行初始化即可
	}

	//學生具有特有的聽課功能
	public void listen()
	{
		System.out.println("聽課");	
	}

}

class ExtendsDemo 
{
	public static void main(String[] args) 
	{
		//建立一個學生物件,並初始化
		Student stu= new Student("lisi",20);

		//呼叫繼承父類的吃飯功能
		stu.eat();

		//呼叫學生的特有的聽課功能
		stu.listen();
	}
}
 執行結果如下圖:

 在該例項中,由於學生屬於人中的一員,因此可通過繼承人來獲取共有的方法和屬性,提高了程式碼的複用性,但學生自身也有其特有的功能,因此將這些特有的功能定義在學生類中。

 3.抽象類

 1)什麼是抽象類?

  抽象類是指不能夠直接建立例項物件的類,用關鍵字abstract進行修飾。通常抽象類都會存在無法定義功能主體的方法。

 2)特點:

  a)抽象方法指只能定義在抽象類中,只有功能定義,沒有功能主體(即沒有大括號及內容),同一個類中抽象方法可以被非抽象方法呼叫。

  b)抽象只能修飾方法和類,且抽象方法和抽象類都必須被abstract關鍵字修飾。

  c)抽象類不可以用new建立物件。

  d)抽象類中的方法要被使用,必須由子類複寫其所有抽象方法後,才能建立子類物件呼叫,如只覆蓋了部分方法,那麼子類還是一個抽象類。

 3)問題思考

  抽象類與一般類有什麼區別?

  抽象類既可以定義抽象方法,也可以定義非抽象方法,而一般類只能定義非抽象方法,但抽象類不能被例項化,而一般類可以被例項化。

 4)程式碼示例:

//定義一個抽象類Animal
abstract class Animal
{
	//定義抽象方法eat()
	public abstract void eat();
}

//定義一個Cat類,並繼承Animal
class Cat extends Animal
{
	//複寫父類的eat()方法
	public void eat()
	{
		System.out.println("吃魚");
	}
}

//定義一個Dog類,並繼承Animal
class Dog extends Animal
{
	//複寫父類的eat()方法
	public void eat()
	{
		System.out.println("吃骨頭");
	
	}
}
class AbstractDemo 
{
	public static void main(String[] args) 
	{
		//建立一個Cat物件,並呼叫物件的eat()方法
		Cat c=new Cat();
		c.eat();
		
		//建立一個Dog物件,並呼叫物件的eat()方法
		Dog d=new Dog();
		d.eat();
	}
}
 執行結果如下圖:


 在上面的例項中可以看到,Animal就是一個抽象類,因為Cat類和Dog類都有各自的吃的方法,因此對吃的方法進行向上抽取,就得到了一個吃的抽象方法,而吃的抽象方法就必須存在抽象類中,子類要建立例項物件就必須複寫Animal中的eat()方法,因此抽象類還具有強制繼承的子類去做一些事情的功能。

 4.介面

 1)概念:當抽象類中的方法都是抽象的,那麼把這樣的類的形式就描述為介面。介面的定義用關鍵字interface,子類實現父類用關鍵字implements。

 2)格式:

     interface 介面名

    {

      定義常量:public static final 返回值型別 常量名稱

      定義方法:public abstract 返回值型別 方法名稱(形參1,形參2,....) { }

    }

 注:上面常量和方法的格式也可部分或全部省略,java會預設自動補上,但不可省略返回值型別和名稱。

3)特點:

  a)介面不可以建立物件,如果被子類實現,需要覆蓋介面的全部抽象方法後,子類才可以例項化,否則子類是一個抽象類。

  b)介面中只能存在成員常量和抽象的成員方法,兩者可以同時存在或只存在一個,或都不存在。

  c)類對介面可以多實現,介面對介面可以多繼承。

  d)介面是對外暴露的規則。

  e)介面是程式的功能擴充套件。

 4)程式碼示例:

//定義一個介面Animal
interface Animal
{
	//定義抽象方法eat()
	public abstract void eat();
}

//定義一個Cat類,並實現Animal
class Cat implements Animal
{
	//複寫父類的eat()方法
	public void eat()
	{
		System.out.println("吃魚");
	}
}


class InterfaceDemo 
{
	public static void main(String[] args) 
	{
		//建立一個Cat物件,並呼叫物件的eat()方法
		Cat c=new Cat();
		c.eat();
		
	}
}
 程式執行的結果如下圖:



 5.特徵三 —— 多型

 1)定義:某類事物具有的多種表現形態。

 2)前提:

  a)必須是類與類之間必須有關係,要麼繼承,要麼實現。

  b)通常情況還應存在覆蓋。

 3)體現:

  a)父類的引用指向子類物件;

  b)父類的引用也可以接受自己的子類物件。

 4)利弊:

  好處:多型的出現大大提高了程式的擴充套件性,降低了程式的耦合性。

  弊端:雖然提高了程式的擴充套件性,但是隻能使用父類的引用訪問父類中的成員。

 5)特點:

  a)非靜態成員函式:

  編譯時期:如果父類的引用指向子類物件,那麼該引用呼叫的方法必須父類要有,否則編譯不通過。

  執行時期:父類的引用先查詢子類的方法,如沒有則執行父類的方法,其實執行的都是子類物件的方法。

  b)成員變數:無論編譯還是執行,都看左邊,即引用所屬的類如果所屬父類就呼叫父類的成員變數,即使子類有覆蓋也一樣。

  c)靜態成員方法:無論編譯還是執行,都參考左邊。也就是說引用所屬的類為父類就呼叫父類的方法,否則呼叫子類的方法。

 6)程式碼例項:

//定義一個抽象類Animal
abstract class Animal
{
	//動物都具有吃的功能
	public abstract void eat();
}

//定義一個類Cat,並繼承Animal
class Cat extends Animal
{
	//複寫父類的eat()方法
	public void eat()
	{
		System.out.println("吃魚");
	}
	//定義特有的方法
	public void catchMouse()
	{
		System.out.println("抓老鼠");
	}

}

//定義一個類Dog,並繼承Animal
class Dog extends Animal
{
	//複寫父類的eat()方法
	public void eat()
	{
		System.out.println("吃骨頭");
	}
	//定義特有的方法
	public void kanJia()
	{
		System.out.println("看家");
	}

}
class  DuoTaiDemo
{
	public static void main(String[] args) 
	{
		Animal a= new Dog();//型別提升,多型的體現
		function(a);

		Cat c=new Cat();
		function(c);
	}

	//父類引用只能訪問父類的方法,如果訪問的方法子類已經覆蓋或實現,則執行子類的方法,
	//如果要訪問子類的特有方法,那麼需要把父類引用強轉成子類。該方法體現了多型的性質。
	public static void function(Animal a)
	{
		a.eat();
		//判斷動物a是否為cat
		if(a instanceof Cat)
		{
			
			/*強制將父類的引用,轉成子類型別,向下轉型.
			  強制轉換的是父類的引用而不是父類的物件,只有指向子類的
		      父類的引用才可以進行強制轉換,多型自始至終都是子類物件
			  在做著變化。
			 */
			Cat c= (Cat)a;
			c.catchMouse();
		}
	}
	
	
}