1. 程式人生 > 實用技巧 >JavaSE學習筆記06面向物件程式設計02

JavaSE學習筆記06面向物件程式設計02

面向物件程式設計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.繼承

  1. 繼承是類和類之間的一種關係。除此之外,類和類之間的關係還有依賴、組合、聚合等。
  2. 繼承關係的兩個類,一個為子類,一個為父類。子類繼承父類,使用關鍵字extends來表示。
  3. extends的意思是“擴充套件”。子類是父類的擴充套件。
  4. 子類和父類之間,從意義上講應該具有“is a”的關係。
  5. 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()”

注意點:

  1. super呼叫父類的構造方法,必須在構造方法的第一行
  2. super必須只能出現在子類的方法或者構造方法中
  3. super和this不能同時呼叫構造方法!
  4. 當構造方法沒有this()也沒有super(),預設會有一個super()

super與this

  1. 代表的物件不同:

    this:本身呼叫者這個物件

    super:代表父類物件的引用

  2. 前提:

    this:沒有繼承也可以使用

    super:只能在繼承條件下才可以使用

  3. 構造方法:

    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”);
	}
}

注意:

  1. 方法覆蓋只針對方法,和屬性無關

  2. 私有方法無法覆蓋,靜態方法不談覆蓋

  3. 構造方法不能被繼承,所以也不能被覆蓋

快捷鍵:Alt + Insert 選擇 Override Methods...

3.多型

什麼是多型?

  1. 多種形態,多種狀態,編譯和執行有兩個不同的狀態

  2. 編譯期叫做靜態繫結

  3. 執行期叫做動態繫結

即同一方法可以根據傳送物件的不同而採用多種不同的行為方式。一個物件的實際型別是確定的,但可以指向物件的引用的型別有很多

多型存在的條件:

  1. 有繼承關係
  2. 子類重寫父類的方法
  3. 父類引用指向子類物件

基礎語法:

  1. 向上轉型:子------>父(upcasting) Animal a = new Cat();

  2. 向下轉型:父------>子(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和型別轉換

  1. instanceof可以在執行階段動態判斷引用指向的物件的型別

  2. 語法:(引用 instanceof 型別)

  3. 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");
    }
}