java面向對象的三大特性是:封裝、繼承與多態,是面向對象編程的核心。
一、封裝
簡單說封裝就是將同一類事物的特性與功能包裝在一起,對外暴露調用的接口。
封裝:封裝也稱信息隱藏,是指利用抽象數據類型把數據和基於數據的操作封裝起來,使其成為一個不可分割的整體,數據隱藏在抽象數據內部,盡可能的隱藏數據細節,只保留一些接口使其與外界發生聯系。也就是說用戶無需知道內部的數據和方法的具體實現細節,只需根據留在外部的接口進行操作就行。
封裝的好處:
1) 實現了專業的分工
2) 良好的封裝能夠減少耦合
3) 類內部的結構能夠自由修改
4) 可以對成員進行更精確的控制
5) 隱藏信息,實現細節
1.1、為什么需要封裝
定義一個學生類,在類中定義學生身高。
Student類:
package com.zhangguo.c41; /**學生類*/ public class Student { /**身高*/ public int height; /**展示*/ public void show(){ System.out.println("身高:"+this.height+"cm"); } }
School類型:
package com.zhangguo.c41; public class School { public static void main(String[] args) { Student tom=new Student(); tom.height=189; tom.show(); Student rose=new Student(); rose.height=-398; rose.show(); } }
運行結果:

從示例中可以看出rose這個學生的對象的身高達-398cm這明顯不科學,將屬性封裝起來,對外提供訪問接口。
1.2、封裝屬性
如果要保護height這個屬性,可以將它的訪問修飾符修改為私有的,如下所示:
/**身高*/ private int height;
報錯了:

因為是私有的,只有本類可以正常訪問,外部訪問不了,可以定義屬性達到訪問控制與封裝的功能,如下所示:
Student類
package com.zhangguo.c41; /** 學生類 */ public class Student { /** 身高 */ private int height; /** 返回身高屬性 */ public int getHeight() { return this.height; } /** * 設置身高屬性 * * @throws Exception */ public void setHeight(int height) { // 如果參數height大於260或小於0 if (height < 0 || height > 260) { // 輸出錯誤信息 System.out.println("身高只能是0-260之間"); } else { this.height = height; } } /** 展示 */ public void show() { System.out.println("身高:" + this.height + "cm"); } }
學生類:
package com.zhangguo.c41; public class School { public static void main(String[] args) { Student tom=new Student(); tom.setHeight(189); tom.show(); Student rose=new Student(); rose.setHeight(-398); rose.show(); } }
運行結果:

getXXX()獲得,setXX(參數)設置
可以調試查看。
1.3、自動封裝屬性
如果在一個類中定義了非常多的屬性,手動封裝是很麻煩的,1個屬性對應2個方法,1個get,1個set。Eclipse中有自動封裝屬性的功能,如下所示:
1.3.1、創建單個
將鼠標懸停在屬性上,點擊“create getter and setter for xxx”


1.3.2、批量創建


生成的結果:
package com.zhangguo.c41; /**狗*/ public class Dog { /**名稱*/ private String name; /**價格*/ private float price; /**顏色*/ private String color; //可讀可寫 public float getPrice() { return price; } public void setPrice(float price) { this.price = price; } //只讀 public String getName() { return name; } //只寫 public void setColor(String color) { this.color = color; } }
二、繼承
2.1、 Java繼承概要
Java繼承是面向對象的最顯著的一個特征。繼承是從已有的類中派生出新的類,新的類能吸收已有類的數據屬性和行為,並能擴展新的能力。JAVA不支持多繼承,單繼承使JAVA的繼承關系很簡單,一個類只能有一個父類,易於管理程序,父類是子類的一般化,子類是父類的特殊化(具體化)
父類(基類):人 子類(派生):學生
學生繼承人,人派生出學生
繼承所表達的就是一種對象類之間的相交關系,它使得某類對象可以繼承另外一類對象的數據成員和成員方法。若類B繼承類A,則屬於B的對象便具有類A的全部或部分性質(數據屬性)和功能(操作),我們稱被繼承的類A為基類、父類或超類,而稱繼承類B為A的派生類或子類。
繼承避免了對一般類和特殊類之間共同特征進行的重復描述。同時,通過繼承可以清晰地表達每一項共同特征所適應的概念范圍——在一般類中定義的屬性和操作適應於這個類本身以及它以下的每一層特殊類的全部對象。運用繼承原則使得系統模型比較簡練也比較清晰。
java使用extends關鍵字標識繼承。
2.2、為什么需要繼承
Dog狗
package com.zhangguo.c42; /**狗*/ public class Dog { /**名稱*/ public String name; /**顏色*/ public String color; /**價格*/ public double price; /**顯示信息*/ public void show(){ System.out.println("名稱:"+name+",顏色:"+color); } }
Cat貓
package com.zhangguo.c42; /**貓*/ public class Cat { /**名稱*/ public String name; /**顏色*/ public String color; /**重量*/ public double weight; /**顯示信息*/ public void show(){ System.out.println("名稱:"+name+",顏色:"+color); } }
Zoo動物園
package com.zhangguo.c42; /**動物園*/ public class Zoo { public static void main(String[] args) { Dog dog=new Dog(); dog.name="吉娃娃狗"; dog.color="綠色"; dog.price=19800.7; dog.show(); Cat cat=new Cat(); cat.name="波斯貓"; cat.color="紅色"; cat.weight=18.5; cat.show(); } }
運行結果:

上面的代碼實現了基本功能,但有問題,主要是:name,color,show重復,如果系統中的動物類再增加將不停的重復,重復就會帶來不便修改,不便維護的問題。
2.3、實現繼承
要解決上面的問題可以使用繼承,達到代碼復用的目的。
Animal動物:
package com.zhangguo.c43; /**動物*/ public class Animal { /**名稱*/ public String name; /**顏色*/ public String color; /**顯示信息*/ public void show(){ System.out.println("名稱:"+name+",顏色:"+color); } }
Dog狗:
package com.zhangguo.c43; /**狗繼承自動物,子類 is a 父類*/ public class Dog extends Animal { /**價格*/ public double price; }
Cat貓:
package com.zhangguo.c43; /**貓*/ public class Cat extends Animal { /**重量*/ public double weight; }
Zoo動物園:
package com.zhangguo.c43; /**動物園*/ public class Zoo { public static void main(String[] args) { Dog dog=new Dog(); dog.name="吉娃娃狗"; dog.color="綠色"; dog.price=19800.7; dog.show(); Cat cat=new Cat(); cat.name="波斯貓"; cat.color="紅色"; cat.weight=18.5; cat.show(); } }
運行結果:

從示例中可見dog並沒有定義color屬性,但在使用中可以調用,是因為dog繼承了父類Animal,父類的非私有成員將被子類繼承。如果再定義其它的動物類則無須再反復定義name,color與show方法。
2.2. Java繼承的特征
2.2.1、傳遞性
若類C繼承類B,類B繼承類A(多繼承),則類C既有從類B那里繼承下來的屬性與方法,也有從類A那里繼承下來的屬性與方法,還可以有自己新定義的屬性和方法。繼承來的屬性和方法盡管是隱式的,但仍是類C的屬性和方法。
2.2.2、單根性
若類B繼承類A,那么建立類B時只需要再描述與基類(類A)不同的少量特征(數據成員和成員方法)即可。這種做法能減小代碼和數據的冗余度,大大增加程序的重用性。
2.2.3、子類調用父類成員
1、使用super關鍵字調用父類成員
2、子類默認會先調用父類的無參構造方法,如果父沒有則報錯,可以手動指定,但必須在第一行
動物:
package com.zhangguo.c43; /**動物*/ public class Animal { /**名稱*/ public String name; /**顏色*/ public String color; public Animal() { System.out.println("這是動物類的空構造方法"); } public Animal(String name, String color) { this.name = name; this.color = color; } /**顯示信息*/ public void show(){ System.out.println("名稱:"+name+",顏色:"+color); } }
狗:
package com.zhangguo.c43; /**狗繼承自動物,子類 is a 父類*/ public class Dog extends Animal { public Dog(String name, String color,double price) { super(name,color); //調用父類構造方法 this.price=price; //調用當前對象的成員 } /**價格*/ public double price; /**重寫父類方法*/ public void show(){ /**子類調用父類成員*/ super.show(); System.out.println("價格:"+this.price); } }
貓:
package com.zhangguo.c43; /**貓*/ public class Cat extends Animal { /**重量*/ public double weight; }
動物園:
package com.zhangguo.c43; public class Zoo { public static void main(String[] args) { Dog dog = new Dog("中華田園犬","藍色",123.56); dog.show(); Cat cat = new Cat(); cat.name = "波斯貓"; cat.color = "紅色"; cat.weight = 18.5; cat.show(); A a = new A(); a.y = 100; B b = new B(); b.y = 200; // com.nf.c401.Hello h=new com.nf.c401.Hello(); C c = new C(); c.y = 200; c.z = 200; } } class A { /** * private 私有的,只允許本類訪問 * public 公有的,允許所有類訪問 * protected 受權保護的,只允許子類訪問,同包訪問 * default 允許同一個包訪問 package,不寫訪問修飾符 **/ private int x; protected int y; } class B extends A { public int z; } class C extends B { } //class D extends A,B,C{} 單根性,只能繼承一個父類,可實現多個接口
運行結果:

三、構造方法
3.1、構造方法概要
a)、構造方法是創建對象時調用的方法,(實例化,new),析構方法
b)、構造方法名與類名相同(如Book類的構造方法名稱一定Book)
c)、構造方法沒有返回類型() public Book(){}
d)、一個類如果不定義構造方法會默認有一個無參構造方法,如果用戶自定義了構造方法則不再默認定義無參構造方法
package com.zhangguo.c44; /** 車 */ public class Car { /**車速*/ private int speed; //構造方法 public Car() { System.out.println("安裝輪胎"); System.out.println("安裝方向盤"); this.speed=230; } }
測試
package com.zhangguo.c44; public class CarClient { public static void main(String[] args) { Car c1=new Car(); Car c2=new Car(); } }
結果:

3.2、帶參數的構造方法
a)、構造方法的參數與普通方法相同
b)、構造方法允許重載(同名方法不同參數個數或類型)
c)、在創建子類時會默認調用父類的構造方法,一般是無參構造
d)、使用super可以調用父類指定的構造方法,子類中調用父類的構造方法時必須在第一行
e)、使用super可以調用父類中的任意公開成員
package com.zhangguo.c44; /** 車 */ public class Car { /**車速*/ public int speed; //構造方法 public Car(int _speed) { this.speed=_speed; } //無參構造方法 public Car(){ this.speed=100; } /**啟動*/ public void start() { System.out.println("車速:"+this.speed); } }
測試:
package com.zhangguo.c44; public class CarClient { public static void main(String[] args) { Car c1=new Car(99); c1.speed=100; c1.start(); Car c2=new Car(350); c2.start(); Car c3=new Car(); c3.speed=198; c3.start(); } }
結果:

package com.zhangguo.c44; class A{ public A() { System.out.println("A"); } } class B extends A{ public B() { System.out.println("B"); } } public class Student { public static void main(String[] args) { B b=new B(); } }
運行結果:
A
B
動物:
package com.zhangguo.c47; /**動物*/ public class Animal { public Animal() { } public Animal(String name){ setName(name); } /**名稱屬性*/ private String name; /** * 獲得名稱 */ public String getName() { return name; } /** * 設置名稱 */ public void setName(String name) { this.name = name; } /**展示*/ public void show(){ System.out.println("這是"+this.name); } }
狗:
package com.zhangguo.c47; /**狗*/ public class Dog extends Animal { public Dog() { } public Dog(String name,String color) { super(name); setColor(color); } /**顏色*/ private String color; public String getColor() { return color; } public void setColor(String color) { this.color = color; } /**重寫父類的show方法,因為父類中也有一樣的show方法*/ public void show(){ super.show(); System.out.println(" 顏色:"+color); } //注解 @Override public String toString() { return "名稱:"+getName()+",顏色:"+getColor(); } }
動物園:
package com.zhangguo.c47; public class Zoo { public static void main(String[] args) { Dog dog1=new Dog("吉娃娃","紅色"); System.out.println(dog1.toString()); Dog dog2=new Dog("大娃娃","藍色"); System.out.println(dog2.toString()); } }
結果:

四、多態
面向對象的多態性主要體現在:重寫與重載兩方面,前面的課程中已經詳細講解過重載。
4.0、重載
同名方法,不同參數(類型或個數),與返回值無關,適用所有方法(構造,靜態,實例)。
構造方法重載:
package com.nf.c401; /** 學生 類 */ public class Student { /** 構造方法 */ public Student(int height, String name, String hobby) { // super(); /**調用父類構造方法,寫在第一行,默認就調用*/ this.height = height; this.name = name; this.hobby = hobby; } public Student(int height, String name) { this.height = height; this.name = name; } public Student(String name) { this.name = name; } public Student() { } /** 身高 字段,成員變量,所有成員變量都有默認值 */ private int height; /** 姓名 */ private String name; /** 愛好 */ private String hobby; /** 身高屬性,寫 */ public void setHeight(int height) { if (height > 0 && height < 300) { this.height = height; } else { System.out.println("身高不正確"); } } /** 身高屬性 讀 */ public int getHeight() { return this.height; } /** 顯示 方法 */ public void show() { System.out.println("身高:" + height + ",姓名:" + this.name + ",愛好:" + hobby); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby = hobby; } }
測試類:
package com.nf.c401; public class School { public static void main(String[] args) { Student tom=new Student(171,"張果","代碼"); tom.show(); Student rose=new Student(); rose.show(); Student mark=new Student("馬克"); mark.setHeight(158); mark.setHobby("籃球"); mark.show(); } }
結果:

自動封裝:

普通方法重載:
系統重載方法:
/**java內置的方法,有很多重載*/ System.out.println("重載"); System.out.println(98.5); System.out.println(true);
自定義重載方法:
package com.nf.c401; public class Math { public void add(int a,int b){ System.out.println(a+"+"+b+"="+(a+b)); } /**參數個數不同*/ public int add(int a,int b,int c){ System.out.println(a+"+"+b+"+"+c+"="+(a+b+c)); return a+b; } /**參數類型不同*/ public void add(double a,double b){ System.out.println(a+"+"+b+"="+(a+b)); } }
測試:
package com.nf.c401; public class MathTest { public static void main(String[] args) { Math math=new Math(); math.add(10, 15); math.add(1.0, 1.5); math.add(100, 200,10); } }
輸出結果:

4.1、LSP(里氏代換原則)
LSP全稱Liskov Substitution Principle,中文名意思是里氏代換原則。LSP講的是基類和子類的關系。只有當這種關系存在時,里氏代換關系才存在。
子類一定可以替換父類。
子類的信息量一定大於父類的。老鼠的兒子會打洞,老鼠的兒子一定可以替代老鼠的父親
package com.zhangguo.c45; class A{ } class B extends A{ } public class Student { public static void main(String[] args) { A a1=new B(); Object a2=new A(); task(new B()); } public static void task(A a){ } }
在代碼中需要父類對象的地方都可以使用子類對象替換。
4.2、重寫(override)
在Java中,子類可繼承父類中的方法,而不需要重新編寫相同的方法。但有時子類並不想原封不動地繼承父類的方法,而是想作一定的修改,這就需要采用方法的重寫。方法重寫又稱方法覆蓋。
package com.zhangguo.c46; class Car{ public void start() { System.out.println("車在跑"); } } class SuperCar extends Car{ public void start() { System.out.println("車在飛"); } } public class Student { public static void main(String[] args) { Car car=new SuperCar(); car.start(); } }
運行結果:
車在飛
4.3、多態實現
同一個類的不同子類對象對同一個方法的調用產生不同的結果叫多態。
演員
廚師
理發師
他都是Person人的子類,但當聽到Cut(切)時他們的表現不一樣。
package com.zhangguo.c47; /**人*/ public class Person { public void cut(){ } public static void main(String[] args) { Person player=new Player(); Person cooker=new Cooker(); Person cuter=new Cuter(); player.cut(); cooker.cut(); cuter.cut(); } } /**演員*/ class Player extends Person { /**重寫*/ public void cut(){ System.out.println("停止演戲"); } } /**廚師*/ class Cooker extends Person { /**重寫*/ public void cut(){ System.out.println("開始切菜"); } } /**廚師*/ class Cuter extends Person { /**重寫*/ public void cut(){ System.out.println("開始剪頭發"); } }
運行結果:

程序中定義的引用變量所指向的具體類型和通過該引用變量發出的方法調用在編程時並不確定,而是在程序運行期間才確定,即一個引用變量倒底會指向哪個類的實例對象,該引用變量發出的方法調用到底是哪個類中實現的方法,必須在由程序運行期間才能決定。因為在程序運行時才確定具體的類,這樣,不用修改源程序代碼,就可以讓引用變量綁定到各種不同的類實現上,從而導致該引用調用的具體方法隨之改變,即不修改程序代碼就可以改變程序運行時所綁定的具體代碼,讓程序可以選擇多個運行狀態,這就是多態性。多態性增強了軟件的靈活性和擴展性。小李喜歡聽小鳥唱歌{麻雀,杜鵑,鸚鵡}
小李:窗外的鳥兒,給我唱首歌。
1.(鳥 bird = new 麻雀 )?
2.(鳥 bird = new 杜鵑 )?
3.(鳥 bird = new 鸚鵡 )?
鳥兒:bird.sing()~~~~~
小李:鳥兒唱的不錯,你是哪種鳥?
鳥兒: bird.shape()
小李:(---如果上面藍字定義的是3,是鸚鵡)哈哈!原來你是鸚鵡!
所以,多態的過程實質是一個抽象指令,讓一組具有相同行為單具有不同內容的個體協同工作的這樣的一個過程。
方法的重寫、重載與動態連接構成多態性;
Java之所以引入多態的概念,原因之一是它在類的繼承問題上和C++不同,后者允許多繼承,這確實給其帶來的非常強大的功能,但是復雜的繼承關系也給C++開發者帶來了更大的麻煩,為了規避風險,Java只允許單繼承,派生類與基類間有IS-A的關系(即“貓”is a “動物”)。這樣做雖然保證了繼承關系的簡單明了,但是勢必在功能上有很大的限制,所以,Java引入了多態性的概念以彌補這點的不足,此外,抽象類和接口也是解決單繼承規定限制的重要手段。同時,多態也是面向對象編程的精髓所在。
要理解多態性,首先要知道什么是“向上轉型”。
我定義了一個子類Cat,它繼承了Animal類,那么后者就是前者的父類。我可以通過
Cat c = new Cat(); 例化一個Cat的對象,這個不難理解。
但當我這樣定義時: Animal a = new Cat();
這代表什么意思呢?
很簡單,它表示我定義了一個Animal類型的引用,指向新建的Cat類型的對象。由於Cat是繼承自它的父類Animal,所以Animal類型的引用是可以指向Cat類型的對象的。那么這樣做有什么意義呢?因為子類是對父類的一個改進和擴充,所以一般子類在功能上較父類更強大,屬性較父類更獨特,定義一個父類類型的引用指向一個子類的對象既可以使用子類強大的功能,又可以抽取父類的共性。所以,
父類引用只能調用父類中存在的方法和屬性,不能調用子類的擴展部分;因為父類引用指向的是堆中子類對象繼承的父類;(但是如果強制把超類轉換成子類的話,就可以調用子類中新添加而超類沒有的方法了。)
同時,父類中的一個方法只有在父類中定義而在子類中沒有重寫的情況下,才可以被父類類型的引用調用;
對於父類中定義的方法,如果子類中重寫了該方法,那么父類類型的引用將會調用子類中的這個方法,這就是動態連接。
五、示例與視頻下載
六、面試題
1、定義包使用什么關鍵字?
package com.zhangguo.projectA
2、導入包使用什么關鍵字?
import com.zhangguo.projectA.類 import com.zhangguo.projectA.*
3、如果想導入com.zhangguo.project下的所有類應該怎樣聲明?
import com.zhangguo.project.*
4、靜態方法是否可以直接調用同一個類中其它非靜態方法?
不行,通過對象調用
5、非靜態方法是否可以直接調用同一個類中的其它靜態方法?
同一個類:直接調用
不同的類:使用類名調用,如類名.成員名
package com.gdnf.java.d3; public class Student { public static void main(String[] args) { //靜態的直接調用靜態的 int i=add(1,2); System.out.println(add(add(i,100),7)); //靜態的實例調用非靜態的 *** Student stu=new Student(); System.out.println(stu.calc(1,2,add(9,91))); } public int calc(int i,int j,int k){ //非靜態的直接調用靜態的 return add(add(i,j),k); } /** * 加法操作 * @param x 第一個參數 * @param y 第二個參數 * @return 相加的結果 */ public static int add(int x,int y){ return x+y; } }
運行結果:

6、請定義一個類車(Car),在車中定義屬性車速(Speed)與車名(Name),封裝Speed與Name,要求車速在50-300之間;定義Start方法輸出車名與車速。測試定義好的類TestCar;
7、請定義父類人(Person),屬性姓名(Name)與性別(Sex),方法Hello方法輸出姓名與性別;子類學生Student與老師Teacher分別繼承人,
Student擁有屬性班級(ClassNO),老師擁有屬性教齡(TeachLen);測試定義好的類TestSchool;
8、java面向對象的三大特性是:封裝、繼承與多態
9、有一個類的定義是這樣的:public class Cat extends Animal,其中Cat是貓,Animal是動物,請問基類是、子類是、基類又稱為什么?子類又稱為什么?
基類Animal、子類Cat 、基類又稱為父類,子類又稱為派生類
10、已知如下類(Phone:Product,Book:Product),請完成另一個類(Book,添加屬性Author)的繼承,請實現對Price的封裝
Product.java
package com.zhangguo.c44; /** 產品 */ public class Product { /** 價格 */ private double price; /** 獲得價格 */ public double getPrice() { return price; } /** 設置價格 */ public void setPrice(double price) { if (price >= 0) { this.price = price; } else { System.out.println("價格必須>=0"); } } /** 展示 */ public void show() { System.out.println("商品的價格是:" + price); } }
Book.java
package com.zhangguo.c44; /** 圖書 繼承 產品 */ public class Book extends Product { /** 作者 屬性 */ private String author; public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } }
Phone.java
package com.zhangguo.c44; /**電話*/ public class Phone extends Product { /**品牌*/ public String brand; } class Vivo extends Phone { }
Store.java
package com.zhangguo.c44; public class Store { public static void main(String[] args) { //請定義圖書對象,設置價格為-100,顯示價格 Book book = new Book(); book.setPrice(-100); book.show(); Vivo vivo=new Vivo(); } }

