面向對象的三大特徵:繼承、封裝、多態java
面向對象編程語言是對客觀世界的模擬,客觀世界裏成員變量都是隱藏在對象內部的,外界沒法直接操做和修改。而後封裝能夠被認爲是一個保護屏障,防止該類的代碼和數據被其餘類隨意訪問。要訪問該類的數據,必須經過指定的方式。適當的封裝可讓代碼更容易理解與維護,也增強了代碼的安全性。其原則就是將屬性隱藏起來,若須要訪問某個屬性,提供公共方法對其訪問。程序員
private的含義編程
private是一個權限修飾符,表明最小權限。安全
private能夠修飾成員變量和成員方法。編程語言
被private修飾後的成員變量和成員方法,只在本類中才能訪問。ide
private的使用格式
private 數據類型 變量名;
使用 private 修飾成員變量,代碼以下:函數
public class Student { private String name; private int age; }
提供 getXxx 方法 / setXxx 方法,能夠訪問成員變量,代碼以下:學習
public class Student { private String name; private int age; public void setName(String n) { name = n; } public String getName() { return name; } public void setAge(int a) { age = a; } public int getAge() { return age; } }
this的含義
this表明所在類的當前對象的引用(地址值),即對象本身的引用。記住 :方法被哪一個對象調用,方法中的this就表明那個對象。即誰在調用,this就表明誰。測試
this使用格式優化
this.成員變量名;
使用 this 修飾方法中的變量,解決成員變量被隱藏的問題,代碼以下:
public class Student { private String name; private int age; public void setName(String name) { //name = name; this.name = name; } public String getName() { return name; } public void setAge(int age) { //age = age; this.age = age; } public int getAge() { return age; } }
小貼士:方法中只有一個變量名時,默認也是使用 this 修飾,能夠省略不寫。
當一個對象被建立時候,構造方法用來初始化該對象,給對象的成員變量賦初始值。
不管你與否自定義構造方法,全部的類都有構造方法,由於Java自動提供了一個無參數構造方法,一旦本身定義了構造方法,Java自動提供的默認無參數構造方法就會失效。
構造方法的定義格式:
修飾符 構造方法名(參數列表){ // 方法體 }
構造方法的寫法上,方法名與它所在的類名相同。它沒有返回值,因此不須要返回值類型,甚至不須要void。使用構造方法後,代碼以下:
public class Student { private String name; private int age; // 無參數構造方法 public Student() {} // 有參數構造方法 public Student(String name,int age) { this.name = name; this.age = age; } }
注意事項
若是你不提供構造方法,系統會給出無參數構造方法。
若是你提供了構造方法,系統將再也不提供無參數構造方法。
構造方法是能夠重載的,既能夠定義參數,也能夠不定義參數。
標準代碼——JavaBean
JavaBean 是 Java語言編寫類的一種標準規範。符合 JavaBean 的類,要求類必須是具體的和公共的,而且具備無參數的構造方法,提供用來操做成員變量的 set 和 get 方法。
public class ClassName{ //成員變量 //構造方法 //無參構造方法【必須】 //有參構造方法【建議】 //成員方法 //getXxx() //setXxx() }
編寫符合 JavaBean 規範的類,以學生類爲例,標準代碼以下:
public class Student { //成員變量 private String name; private int age; //構造方法 public Student() {} public Student(String name,int age) { this.name = name; this.age = age; } //成員方法 publicvoid setName(String name) { this.name = name; } public String getName() { return name; } publicvoid setAge(int age) { this.age = age; } publicint getAge() { return age; } } 測試類,代碼以下: public class TestStudent { public static void main(String[] args) { //無參構造使用 Student s= new Student(); s.setName("小米"); s.setAge(18); System.out.println(s.getName()+"‐‐‐"+s.getAge()); //帶參構造使用 Student s2= new Student("小明",18); System.out.println(s2.getName()+"‐‐‐"+s2.getAge()); } }
封裝是很是有必要的,由於:
現實生活中的繼承,孩子繼承父輩的財產,孩子能夠直接拿父輩的財產來使用,Java中的繼承是指在現有類的基礎上定義一個新的類,現有類稱爲父類,新的類稱爲子類,子類會自動擁有父類的可繼承的內容多個類中存在相同屬性和行爲時,將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行爲,只要繼承那一個類便可。內容
繼承:
就是子類繼承父類的屬性和行爲,使得子類對象具備與父類相同的屬性、相同的行爲。子類能夠直接訪問父類中的非私有的屬性和行爲。
好處
經過extends關鍵字,能夠聲明一個子類繼承另一個父類,定義格式以下:
class 父類 { ... } class 子類 extends 父類 { ... }
舉個例子,代碼以下:
// 1.定義父類 class Person { String name; int age; public void eat() { System.out.println(name + " 吃飯"); } public void sleep() { System.out.println(name + " 睡覺"); } } // 2.再寫子類 class Student extends Person { } class Teacher extends Person { } // 3.定義測試類 public class Demo01 { public static void main(String[] args) { Student stu = new Student(); stu.name = "小王"; stu.age = 18; stu.eat(); stu.sleep(); Teacher tea = new Teacher(); tea.name = "馬老師"; tea.age = 30; tea.eat(); tea.sleep(); Person p = new Person(); p.name = "張三"; p.age = 20; p.eat(); p.sleep(); } }
請使用繼承定義如下類:
1.程序員(Coder)
成員變量: 姓名,年齡
成員方法: 吃飯,睡覺,敲代碼
2.老師(Teacher)
成員變量: 姓名,年齡
成員方法: 吃飯,睡覺,上課
1. 父類Person類 public class Person { String name; int age; public void eat() { System.out.println(name + "吃飯"); } public void sleep() { System.out.println(name + "睡覺"); } } 2. 子類Coder類 public class Coder extends Person { // 敲代碼 public void coding() { System.out.println(name + "敲代碼"); } } 3. 子類Teacher類 public class Teacher extends Person { public void teach() { System.out.println(name + "上課"); } } 4. 測試類 public class Demo02 { public static void main(String[] args) { Coder c = new Coder(); c.name = "馬化騰"; c.age = 45; c.eat(); c.sleep(); c.coding(); System.out.println("‐‐‐‐‐‐‐‐‐‐‐"); Teacher t = new Teacher(); t.name = "馬雲"; t.age = 50; t.eat(); t.sleep(); t.teach(); } }
並非父類的全部內容均可以給子類繼承的,如下2個內容不能被子類繼承:
public class Demo03 { public static void main(String[] args) { Zi z = new Zi(); System.out.println(z.num1); // System.out.println(z.num2); // 私有的子類沒法使用 // 經過getter/setter方法訪問父類的private成員變量 System.out.println(z.getNum2()); z.show1(); // z.show2(); // 私有的子類沒法使用 } } class Fu { public Fu() { } public int num1 = 10; private int num2 = 20; public void show1() { System.out.println("show1"); } private void show2() { System.out.println("show2"); } public int getNum2() { return num2; } public void setNum2(int num2) { this.num2 = num2; } } class Zi extends Fu { /* 2. 構造方法不能繼承,由於構造方法和類名相同,父類和子類的名稱確定不相同,沒法繼承 */ // public Fu() { // } }
final class A {}
成員變量不重名
若是子類父類中出現不重名的成員變量,這時的訪問是沒有影響的。
成員變量重名
若是子類父類中出現重名的成員變量,這時的訪問是有影響的。子父類中出現了同名的成員變量時,在子類中須要訪問父類中非私有成員變量時,須要使用 super 關鍵字,修飾父類成員變量,相似於以前學過的 this 。
使用格式:super.父類成員變量名
注意: 父類中的成員變量是非私有的,子類中能夠直接訪問。若父類中的成員變量私有了,子類是不能直接訪問的。一般編碼時,咱們遵循封裝的原則,使用private修飾成員變量,那麼如何訪問父類的私有成員變量呢,能夠在父類中提供公共的getXxx方法和setXxx方法。
成員方法不重名
若是子類父類中出現不重名的成員方法,這時的調用是沒有影響的。對象調用方法時,會先在子類中查找有沒有對應的方法,若子類中存在就會執行子類中的方法,若子類中不存在就會執行父類中相應的方法。
成員方法重名——重寫(Override)
若是子類父類中出現重名的成員方法,這時的訪問是一種特殊狀況,叫作 方法重寫 (Override) 。
方法重寫 :子類中出現與父類如出一轍的方法時(返回值類型,方法名和參數列表都相同),會出現覆蓋效果,也稱爲重寫或者複寫。聲明不變,從新實現。
注意事項
1.子類方法覆蓋父類方法,必需要保證權限大於等於父類權限。
2.子類方法覆蓋父類方法,返回值類型、函數名和參數列表都要如出一轍。
當類之間產生了關係,其中各種中的構造方法,又產生了哪些影響?
構造方法的名字是與類名一致的。因此子類是沒法繼承父類構造方法的。
構造方法的做用是初始化成員變量的。因此子類的初始化過程當中,必須先執行父類的初始化動做。子類的構造方法中默認有一個 super() ,表示調用父類的構造方法,父類成員變量初始化後,才能夠給子類使用。
繼承後子類構造方法特色:子類全部構造方法都會調用父類的無參構造
生活中,好比跑的動做,小貓、小狗和大象,跑起來是不同的。再好比飛的動做,昆蟲、鳥類和飛機,飛起來也是不同的。可見,同一行爲,經過不一樣的事物,能夠體現出來的不一樣的形態。多態,描述的就是這樣的狀態。
多態: 是指同一行爲,具備多個不一樣表現形式。
多態指的是同一個方法調用,因爲對象不一樣可能會有不一樣的行爲。現實生活中,同一個方法,具體實現會徹底不一樣。 好比:一樣是調用人的「休息」方法,張三是睡覺,李四是旅遊,程序員是敲代碼,數學教授是作數學題; 一樣是調用人「吃飯」的方法,中國人用筷子吃飯,英國人用刀叉吃飯,印度人用手吃飯。
多態的要點:
多態是方法的多態,不是屬性的多態(多態與屬性無關)。
多態的存在要有3個必要條件:繼承,方法重寫,父類引用指向子類對象。
父類引用指向子類對象後,用該父類引用調用子類重寫的方法,此時多態就出現了。
class Animal { public void shout() { System.out.println("叫了一聲!"); } } class Dog extends Animal { public void shout() { System.out.println("汪汪汪!"); } public void seeDoor() { System.out.println("看門中...."); } } class Cat extends Animal { public void shout() { System.out.println("喵喵喵喵!"); } } public class TestPolym { public static void main(String[] args) { Animal a1 = new Cat(); // 向上能夠自動轉型 //傳的具體是哪個類就調用哪個類的方法。大大提升了程序的可擴展性。 animalCry(a1); Animal a2 = new Dog(); animalCry(a2);//a2爲編譯類型,Dog對象纔是運行時類型。 //編寫程序時,若是想調用運行時類型的方法,只能進行強制類型轉換。 // 不然通不過編譯器的檢查。 Dog dog = (Dog)a2;//向下須要強制類型轉換 dog.seeDoor(); } // 有了多態,只須要讓增長的這個類繼承Animal類就能夠了。 static void animalCry(Animal a) { a.shout(); } /* 若是沒有多態,咱們這裏須要寫不少重載的方法。 * 每增長一種動物,就須要重載一種動物的喊叫方法。很是麻煩。 static void animalCry(Dog d) { d.shout(); } static void animalCry(Cat c) { c.shout(); }*/ }
父類引用指向子類對象,咱們稱這個過程爲向上轉型,屬於自動類型轉換。
向上轉型後的父類引用變量只能調用它編譯類型的方法,不能調用它運行時類型的方法。這時,咱們就須要進行類型的強制轉換,咱們稱之爲向下轉型!
示例 對象的轉型
public class TestCasting { public static void main(String[] args) { Object obj = new String("new"); // 向上能夠自動轉型 // obj.charAt(0) 沒法調用。編譯器認爲obj是Object類型而不是String類型 /* 編寫程序時,若是想調用運行時類型的方法,只能進行強制類型轉換。 * 否則通不過編譯器的檢查。 */ String str = (String) obj; // 向下轉型 System.out.println(str.charAt(0)); // 位於0索引位置的字符 System.out.println(obj == str); // true.他們倆運行時是同一個對象 } }
在向下轉型過程當中,必須將引用變量轉成真實的子類類型(運行時類型)不然會出現類型轉換異常ClassCastException。
類型轉換異常
public class TestCasting2 { public static void main(String[] args) { Object obj = new String("news"); //真實的子類類型是String,可是此處向下轉型爲StringBuffer StringBuffer str = (StringBuffer) obj; System.out.println(str.charAt(0)); } }
爲了不出現這種異常,咱們可使用學過的instanceof運算符進行判斷,以下所示:
向下轉型中使用instanceof
public class TestCasting3 { public static void main(String[] args) { Object obj = new String("news"); if(obj instanceof String){ String str = (String)obj; System.out.println(str.charAt(0)); }else if(obj instanceof StringBuffer){ StringBuffer str = (StringBuffer) obj; System.out.println(str.charAt(0)); } } }