JavaSE學習筆記06面向物件程式設計02
阿新 • • 發佈:2020-10-21
面向物件程式設計02
1.封裝
我們程式設計要求“高內聚,低耦合”。高內聚就是類的內部資料操作細節自己完成,不允許外部干涉;低耦合:僅暴露少量的方法給外部使用
封裝即:屬性私有,get/set
package com.oop.demo04; //類 public class Student { //private:私有 //名字 private String name; //學號 private int id; //性別 private char sex; //年齡 private int age; //提供一些可以操作這個屬性的方法 //提供一些public的get、set方法 //get 獲得這個資料 public String getName(){ return this.name; } //set 給這個資料設定值 public void setName(String name){ this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int a) {//不合法 if (a> 120 || a < 0){ System.out.println("輸入年齡不合法!"); }else { this.age = a; } } /*快捷鍵: 1.Alt + insert彈出小視窗 2.選擇Getter and Setter 3.選擇要私有化的屬性,點OK即可 */ //注意:set和get方法不加static }
package com.oop.demo04; /* 作用: 1.提高程式的安全性,保護資料 2.隱藏程式碼的實現細節 3.統一介面 4.系統可維護性增加了 */ public class Application { public static void main(String[] args) { Student s1 = new Student(); Student s2 = new Student(); //s1.name = "XX";屬性私有化後不能這麼寫,會報錯 s1.setName("阿波"); System.out.println(s1.getName());//阿波 //不合法的,需要在set方法中設立關卡來保證資料的安全性 s1.setAge(999); System.out.println(s1.getAge()); s2.setAge(120); System.out.println(s2.getAge()); } }
2.繼承
- 繼承是類和類之間的一種關係。除此之外,類和類之間的關係還有依賴、組合、聚合等。
- 繼承關係的兩個類,一個為子類,一個為父類。子類繼承父類,使用關鍵字extends來表示。
- extends的意思是“擴充套件”。子類是父類的擴充套件。
- 子類和父類之間,從意義上講應該具有“is a”的關係。
- Java中類只有單繼承,沒有多繼承!一個兒子只能有一個爸爸,但是一個爸爸可以有多個兒子。
package com.oop.demo05; //在java中所有的類,都預設直接或者間接繼承Object類 /* Java中的類沒有顯示繼承任何類。則預設繼承object類,object類是java語言提供的根類(老祖宗類),也就是說,一個物件與生俱來就有object型別中的所有特徵 */ //人: 父類 public class Person /*extends Object*/ { private int money = 10_0000_0000; public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } public void say(){ System.out.println("說了一句話"); } }
package com.oop.demo05;
//學生 is 人 : 子類
//Ctrl + H 可以顯示繼承的結構圖
public class Student extends Person{}
package com.oop.demo05;
//老師 is 人 :子類
public class Teacher extends Person{}
package com.oop.demo05;
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
s1.say();//說了一句話
System.out.println(s1.getMoney());//1000000000
}
}
Super
語法:“super.”,“super()”
注意點:
- super呼叫父類的構造方法,必須在構造方法的第一行
- super必須只能出現在子類的方法或者構造方法中
- super和this不能同時呼叫構造方法!
- 當構造方法沒有this()也沒有super(),預設會有一個super()
super與this
-
代表的物件不同:
this:本身呼叫者這個物件
super:代表父類物件的引用
-
前提:
this:沒有繼承也可以使用
super:只能在繼承條件下才可以使用
-
構造方法:
this():本類的構造!
super():父類的構造!
Public class Test{
Public static void main(String[] args){
//建立子類物件
new C();
}
}
Class A{
Public A(){
System.out.println(“1”);
}
}
Class B extends A{
Public B(){
System.out.println(“2”);
}
Public B(String name){
//super(name);沒寫會預設自動建立super()
System.out.println(“3”);
}
}
Class C extends B{
Public C(){
this(“張三”);
system.out.println(“4”);
}
Public C(String name){
this(name,20);
System.out.println(“5”);
}
Public C(String name,int age){
Super(name);
System.out.println(“6”);
}
}//執行結果:1 3 6 5 4
方法重寫(方法覆蓋)override
子類繼承父類之後,當繼承過來的方法無法滿足當前子類的業務需求時,子類有權力對這個方法進行重新編寫,有必要進行方法的覆蓋
Pubic class Test{
Public static void main(String[] args){
//建立物件
ChinaPeople p1 = new ChinaPeople();
P1.setName(“張三”);
P1.speak();//張三正在說漢語
AmericPeople p2 = new AmericPeople();
P2.setName(“Jack”);
P2.speak();//jack speak English
}
}
Class People{//父類,人類
Private String name;
//構造方法
Public People(){}
Public People(String name){
this.name = name;
}
Public void setName(String name){
this.name = name;
}
Public String getName(){
return name;
}
//方法
Public void speak(){
System.out.println(name + “……”);
}
}
Class ChinaPeople extends People{//子類
Public void speak(){
System.out.println(this.getName() + “正在說漢語”);
}
}
Class AmericPeople extends People{//子類
Public void speak(){
System.out.println(this.getName() + “speak English”);
}
}
注意:
-
方法覆蓋只針對方法,和屬性無關
-
私有方法無法覆蓋,靜態方法不談覆蓋
-
構造方法不能被繼承,所以也不能被覆蓋
快捷鍵:Alt + Insert 選擇 Override Methods...
3.多型
什麼是多型?
-
多種形態,多種狀態,編譯和執行有兩個不同的狀態
-
編譯期叫做靜態繫結
-
執行期叫做動態繫結
即同一方法可以根據傳送物件的不同而採用多種不同的行為方式。一個物件的實際型別是確定的,但可以指向物件的引用的型別有很多
多型存在的條件:
- 有繼承關係
- 子類重寫父類的方法
- 父類引用指向子類物件
基礎語法:
-
向上轉型:子------>父(upcasting) Animal a = new Cat();
-
向下轉型:父------>子(downcasting) Cat c = (Cat)a;
注意:java中允許向上與向下轉型,無論是哪種,必須有繼承關係,沒有繼承關係編譯器會報錯。
package com.oop.demo06;
public class Person {
public void run(){
System.out.println("run");
}
}
package com.oop.demo06;
public class Student extends Person {
public void run(){
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
package com.oop.demo06;
public class Application {
public static void main(String[] args) {
/* 一個物件的實際型別是確定的
new Student();
new Person();
*/
//可以指向的引用型別就不確定了:父類的引用指向子類
//Student 能呼叫的方法都是自己的或者繼承父類的
Student s1 = new Student();
//Person父型別,可以指向子類,但是不能呼叫子類獨有的方法
Person s2 = new Student();
Object s3 = new Student();
//物件能執行哪些方法,主要看物件左邊的型別,和右邊關係不大
s1.run();//son
s2.run();//son
s1.eat();//eat
//s2.eat();報錯
//子類重寫了父類的方法,執行子類的方法,由於父類中沒有eat方法,這是子類獨有的方法,所以報錯
//強制轉型便不會報錯
((Student) s2).eat();//eat
}
}
再舉個例子
Public calss Test01{
Public static void main(String[] args){
Animal a1 = new Animal();
a1.move(); //動物在移動!
Cat c1 = new Cat();
c1.move(); //貓在走貓步!
Bird b1 = new Bird();
b1.move(); //鳥兒在飛翔!
Animal a2 = new Cat();
Animal a3 = new Bird();
/*
分析:Animal是父類,Cat和Bird是子類,Cat和Bird is a Animal,這句話能不能說通?能。a2和a3就是父類的引用,new Cat()和new Bird()是一個子型別的物件,經過測試得知:父型別的引用允許指向子型別的物件。
*/
a2.move(); //貓在走貓步!(向上轉型)
a3.move();
/*
分析:編譯階段:編譯器只知道a2的型別是Animal,所以編譯器在檢查語法的時候會去Animal.Class位元組碼檔案中找move()方法,找到繫結上move()方法,編譯通過,靜態繫結成功。(編譯階段屬於靜態繫結)
執行階段:實際上在堆記憶體中建立的java物件是cat物件,所以move的時候,真正參與move的物件是一隻貓,於是執行的階段會動態執行cat物件的move()方法,這個過程屬於執行階段繫結。(執行階段屬於動態繫結)
*/
Animal a2 = new Cat(); //底層物件是cat
A5.catchMouse(); //錯誤,找不到符號
/*
Why?因為編譯器只知道a5的型別是Animal,去Animal.class檔案中找catchMouse()方法,沒找到,所以靜態繫結失敗,編譯報錯,無法執行。
*/
//這個時候必須使用向下轉型
Cat x = (Cat) a5;
x.catchMouse(); //貓正在抓老鼠!
//因為Animal和Cat存在繼承關係,所以Animal轉成Cat沒報錯
}
}
Class Animal{ //父類,動物類
Public void move(){
System.out.println(“動物在移動!”);
}
}
Class Cat extends Animal{ //貓類,子類
Public void move(){
System.out.println(“貓在走貓步!”);
}
Public void catchMouse(){ //貓自己特有的行為
System.out.println(“貓正在抓老鼠!”);
}
}
Class Bird extends Aniaml{
Public void move(){
System.out.println(“鳥兒在飛翔!”);
}
}
/*
向下轉型的風險:Animal a6 = new Bird();表面上是Aniaml,執行的時候是Bird。
Cat y = (Cat) a6;
y.catchMouse();
分析:編譯器檢測到a6是Animal型別,而Animal和Cat之間存在繼承關係,所以編譯沒毛病,執行階段,堆記憶體實際建立的物件是Bird,Bird物件轉換成Cat物件就不行了,因為Bird和Cat之間沒有繼承關係。
執行出現異常:java,lang.ClassCastException(型別轉換異常)
*/
4.instanceof和型別轉換
-
instanceof可以在執行階段動態判斷引用指向的物件的型別
-
語法:(引用 instanceof 型別)
-
instanceof運算子的運算結果只能是true或false
package com.oop.demo06;
public class Application {
public static void main(String[] args) {
//Object > Person > Student
Object object = new Student();
System.out.println(object instanceof Student);//true
System.out.println(object instanceof Person);//true
System.out.println(object instanceof Object);//true
System.out.println(object instanceof Teachet);//false
System.out.println(object instanceof String);//false
System.out.println("================================");
Person person = new Student();
System.out.println(person instanceof Student);//true
System.out.println(person instanceof Person);//true
System.out.println(person instanceof Object);//true
System.out.println(person instanceof Teachet);//false
//System.out.println(person instanceof String);//編譯報錯
System.out.println("================================");
Student student = new Student();
System.out.println(student instanceof Student);//true
System.out.println(student instanceof Person);//true
System.out.println(student instanceof Object);//true
//System.out.println(student instanceof Teachet);//編譯報錯
//System.out.println(student instanceof String);//編譯報錯
System.out.println("================================");
//型別之間的轉換
Person p = new Student();
//p.go();報錯
//將這個物件轉換為Student型別,我們就可以使用Student型別的方法了!
Student s = (Student)p;
s.go();//go
//簡化後如下
((Student) p).go();//go
/*
子類轉換為父類,可能丟失自己本來的一些方法
*/
}
}
package com.oop.demo06;
public class Person {
public void run(){
System.out.println("run");
}
}
package com.oop.demo06;
public class Student extends Person {
public void go(){
System.out.println("go");
}
}