『JavaSE』面向對象三大特徵

Java面向對象的三大特徵:封裝、繼承、多態。

封裝

Java中的封裝是指一個類把自己內部的實現細節進行隱藏,只暴露對外的接口(setter和getter方法)。封裝又分爲屬性的封裝和方法的封裝。把屬性定義爲私有的,它們通過setter和getter方法來對屬性的值進行設定和獲取。

封裝的思想保證了類內部數據結構的完整性,使用戶無法輕易直接操作類的內部數據,這樣降低了對內部數據的影響,提高了程序的安全性和可維護性。
封裝的目的:
只能通過規定的方法訪問數據
隱藏類的實現細節
方便加入控制語句
方便實現修改

public class Person {
    private int id;
    private String name;
    private Person person;
 
    public int getId() {
        return id;
    }
 
    public String getName() {
        return name;
    }
 
    public Person getPerson() {
        return person;
    }
 
    public void setId(int id) {
        this.id = id;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public void setPerson(Person person) {
        this.person = person;
    }
}

private實現封裝
private/ public 這兩個關鍵字表示 “訪問權限控制” .

被 public 修飾的成員變量或者成員方法, 可以直接被類的調用者使用.
被 private 修飾的成員變量或者成員方法, 不能被類的調用者使用.

換句話說, 類的使用者根本不需要知道, 也不需要關注一個類都有哪些 private 的成員. 從而讓類調用者以更低的成本來使用類.

直接使用 public

class Person {
	public String name = "張三";
	public int age = 18;
}
class Test {
	public static void main(String[] args) {
		Person person = new Person();
		System.out.println("我叫" + person.name + ", 今年" + person.age + "歲");
	}
}
// 執行結果
我叫張三, 今年18

這樣的代碼導致類的使用者(main方法的代碼)必須要瞭解 Person 類內部的實現, 才能夠使用這個類. 學習成本較高
一旦類的實現者修改了代碼(例如把 name 改成 myName), 那麼類的使用者就需要大規模的修改自己的代碼, 維護成本較高

使用 private 封裝屬性, 並提供 public 方法供類的調用者使用

class Person {
	private String name = "張三";
	private int age = 18;
	public void show() {
		System.out.println("我叫" + name + ", 今年" + age + "歲");
	}
}
class Test {
	public static void main(String[] args) {
		Person person = new Person();
		person.show();
	}
}
// 執行結果
我叫張三, 今年18

此時字段已經使用 private 來修飾. 類的調用者(main方法中)不能直接使用. 而需要藉助 show 方法. 此時類的使用者就不必瞭解 Person 類的實現細節.
同時如果類的實現者修改了字段的名字, 類的調用者不需要做出任何修改(類的調用者根本訪問不到 name, age這樣的字段).

注意事項:
①private 不光能修飾字段, 也能修飾方法
②通常情況下我們會把字段設爲 private 屬性, 但是方法是否需要設爲 public, 就需要視具體情形而定. 一般我們希望一個類只提供 “必要的” public 方法, 而不應該是把所有的方法都無腦設爲 public.

getter和setter方法

當我們使用 private 來修飾字段的時候, 就無法直接使用這個字段了.
代碼示例:

class Person {
	private String name = "張三";
	private int age = 18;
	public void show() {
		System.out.println("我叫" + name + ", 今年" + age + "歲");
	}
}
class Test {
	public static void main(String[] args) {
		Person person = new Person();
		person.age = 20;
		person.show();
	}
}
// 編譯出錯
Test.java:13: 錯誤: age可以在Person中訪問private
		person.age = 20;
			^
1 個錯誤

此時如果需要獲取或者修改這個 private 屬性, 就需要使用 getter / setter 方法.
代碼示例:

class Person {
	private String name;//實例成員變量
	private int age;
	public void setName(String name){
		//name = name;//不能這樣寫
		this.name = name;//this引用,表示調用該方法的對象
	}
	public String getName(){
		return name;
	}
	public void show(){
		System.out.println("name: "+name+" age: "+age);
	}
}

public static void main(String[] args) {
	Person person = new Person();
	person.setName("caocao");
	String name = person.getName();
	System.out.println(name);
	person.show();
}
// 運行結果
caocao
name: caocao age: 0

注意事項

<1> getName 即爲 getter 方法, 表示獲取這個成員的值.
<2> setName 即爲 setter 方法, 表示設置這個成員的值.
<3> 當set方法的形參名字和類中的成員屬性的名字一樣的時候,如果不使用this, 相當於自賦值. this 表示當前實例的引用.
<4> 不是所有的字段都一定要提供 setter / getter 方法, 而是要根據實際情況決定提供哪種方法.
<5> 在 IDEA 中可以使用 alt + insert (或者 alt + F12) 快速生成 setter / getter 方法.在 VSCode 中可以使用鼠標右鍵菜單 -> 源代碼操作 中自動生成 setter / getter 方法.

字段 vs 屬性
我們通常將類的數據成員稱爲 “字段(field)” , 如果該字段同時提供了 getter / setter 方法, 那麼我們也可以將它稱爲 “屬性(property )”.

繼承

繼承就是子類繼承父類的特徵和行爲,使得子類對象具有父類的信息,同時可以擴展。基本語法:

class 子類 extends 父類 {
}

a.使用 extends 指定父類.
b.Java 中一個子類只能繼承一個父類 (而C++/Python等語言支持多繼承).單繼承多實現。
c.子類會繼承父類的所有 public 的字段和方法.
d.對於父類的 private 的字段和方法, 子類中是無法訪問的.
e.子類的實例中, 也包含着父類的實例. 可以使用 super 關鍵字得到父類實例的引用
f. 子類可以擁有屬於自己的屬性和方法

子類訪問父類成員:
父類無參的構造方法:super();
父類有參的構造方法:super(name);
訪問父類屬性:super.name;
訪問父類方法:super.方法名();

子類不能被繼承的父類成員:
構造方法
private成員
子類與父類不在同包,使用默認訪問權限的成員

class Animal {
	public String name;
	public Animal(String name) {
		this.name = name;
	}
	public void eat(String food) {
		System.out.println(this.name + "正在喫" + food);
	}
}
class Cat extends Animal {
	public Cat(String name) {
		// 使用 super 調用父類的構造方法.
		super(name);
	}
}
class Bird extends Animal {
	public Bird(String name) {
		super(name);
	}
	public void fly() {
		System.out.println(this.name + "正在飛 ︿( ̄︶ ̄)︿");
	}
}
public class Test {
	public static void main(String[] args) {
		Cat cat = new Cat("小黑");
		cat.eat("貓糧");
		Bird bird = new Bird("圓圓");
		bird.fly();
	}
}

protected 關鍵字
如果把字段設爲 private, 子類不能訪問. 但是設成 public, 又違背了我們 “封裝” 的初衷.兩全其美的辦法就是 protected 關鍵字.對於類的調用者來說, protected 修飾的字段和方法是不能訪問的.對於類的子類和同一個包的其他類來說, protected 修飾的字段和方法是可以訪問的.

// Animal.java
public class Animal {
	protected String name;
	public Animal(String name) {
		this.name = name;
	}
	public void eat(String food) {
		System.out.println(this.name + "正在喫" + food);
	}
}
// Bird.java
public class Bird extends Animal {
	public Bird(String name) {
		super(name);
	}
	public void fly() {
		// 對於父類的 protected 字段, 子類可以正確訪問
		System.out.println(this.name + "正在飛 ︿( ̄︶ ̄)︿");
	}
}
// Test.java 和 Animal.java 不在同一個包中了.
public class Test {
	public static void main(String[] args) {
		Animal animal = new Animal("小動物");
		System.out.println(animal.name); // 此時編譯出錯, 無法訪問 name
	}
}

小結: Java 中對於字段和方法共有四種訪問權限
private: 類內部能訪問, 類外部不能訪問
默認(也叫包訪問權限): 類內部能訪問, 同一個包中的類可以訪問, 其他類不能訪問.
protected: 類內部能訪問, 子類和同一個包中的類可以訪問, 其他類不能訪問.
public : 類內部和類的調用者都能訪問

在這裏插入圖片描述
final 關鍵字

final 關鍵字, 修飾一個變量或者字段的時候, 表示常量 (不能修改)

final int a = 10;
a = 20; // 編譯出錯

final 關鍵字也能修飾類, 此時表示被修飾的類就不能被繼承.

final public class Animal {
	...
}
public class Bird extends Animal {
	...
}
// 編譯出錯
Error:(3, 27) java: 無法從最終com.bit.Animal進行繼承

final 關鍵字的功能是 限制 類被繼承
“限制” 這件事情意味着 “不靈活”. 在編程中, 靈活往往不見得是一件好事. 靈活可能意味着更容易出錯.
是用 final 修飾的類被繼承的時候, 就會編譯報錯, 此時就可以提示我們這樣的繼承是有悖這個類設計的初衷的.
我們平時是用的 String 字符串類, 就是用 final 修飾的, 不能被繼承.

super關鍵字

代碼中由於使用了重寫機制, 調用到的是子類的方法. 如果需要在子類內部調用父類方法可以使用super 關鍵字。

super只能出現在子類的方法和構造方法中;
super調用構造方法時,只能是第一句;
super不能訪問法父類的private成員;

注意 super 和 this 功能有些相似, 但是還是要注意其中的區別

在這裏插入圖片描述

多態

多態就是統一接口,使用不同的實現,而執行不同的操作。
多態的實現方式: 靜態多態(重載)、動態多態(重寫)
重寫和重載的區別:
重寫:把在子類中把父類本身有的方法重新寫一遍。
重載:在一個類中,同名的方法如果有不同的參數列表(參數類型不同、參數個數不同甚至是參數順序不同)則視爲重載。

在這裏插入圖片描述

向上轉型和向下轉型:

向上轉型: 父類引用指向子類對象
父類 = new 子類();
向下轉型: 子類引用指向父類對象
父類 對象1 = new 子類();
子類 對象2 = (子類)對象1;

向上轉型:父類引用指向子類對象,將會丟失子類和父類中不一致的方法,並且父類引用調用的變量只能是父類中的。

class Person{
    private String name = "Person";
    int age=0;
    public void function() {
		System.out.println("this is person");
	}
}
public class Child extends Person{
	
    public String grade;
    int age=10;
    public void function() {
		System.out.println("this is child");
	}
    public void b1(){
    	System.out.println("子類新方法");
    } //B類定義了自己的新方法
    @Test
    public  void test(){
        Person p = new Child();
        p.function();
        System.out.println(p.age);
    	
    }
}
//輸出:this is child”  
0

並且我們是不能使用p.b1()的,編譯錯誤,因爲,此時該對象p已經失去了該方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章