1?? 類和對象
面向?qū)ο缶幊?Object-Oriented Programming, OOP)是一種廣泛應(yīng)用于軟件開發(fā)的編程范式。Java是一種面向?qū)ο蟮木幊陶Z言,它提供了豐富的工具和特性來支持面向?qū)ο缶幊?。本文將詳?xì)介紹Java面向?qū)ο蟮娜恐R。
我們可以把一個Java類想象成是一個藍(lán)圖或者模具,用來創(chuàng)建對象。就像制造同樣的零食用品,我們需要設(shè)計和使用模具來制作各種各樣的巧克力、糖果或餅干。
對象則是基于這個類創(chuàng)建出來的具體實例。我們可以把一個對象比作是一塊巧克力,它擁有自己的形狀、重量和口味。每次創(chuàng)建新的對象,都會有一份獨特的巧克力從模具中取出。
類定義了對象可以有的屬性(變量)和行為(方法)。就像巧克力可能有不同的顏色、形狀和味道一樣,類定義了對象可能具有的各種特征。
當(dāng)我們創(chuàng)建一個新的對象時,我們實際上是在生產(chǎn)一塊新的巧克力。通過給對象的屬性賦值,我們可以自定義每一塊巧克力的特性。例如,我們可以設(shè)置巧克力的顏色為紅色、形狀為方形、味道為草莓。
此外,對象還可以執(zhí)行不同的操作,也就是調(diào)用類中定義的方法。類中的方法可以讓我們對對象進(jìn)行各種操作,就像我們能夠吃掉巧克力、分享給他人或?qū)⑵溆米髋淞现谱髌渌瘘c一樣。
因此,類就像是制造巧克力的模具,對象則是由該模具創(chuàng)建出的巧克力。通過設(shè)置屬性和執(zhí)行方法,我們可以個性化每一個對象并進(jìn)行各種操作。
根據(jù)上邊的描述,那么可以寫出下面這個簡單的示例代碼,主要展示如何創(chuàng)建一個Chocolate
類來代表巧克力,并為其定義一些屬性和方法:
public class Chocolate {
// 定義巧克力的屬性(變量)
private String color;
private String shape;
private String flavor;
// 構(gòu)造函數(shù)用于創(chuàng)建巧克力對象,并初始化屬性
public Chocolate(String color, String shape, String flavor) {
this.color = color;
this.shape = shape;
this.flavor = flavor;
}
// 定義獲取屬性的方法(getter)
public String getColor() {
return color;
}
public String getShape() {
return shape;
}
public String getFlavor() {
return flavor;
}
// 定義一些操作方法
public void eat() {
System.out.println("吃掉這個" + color + "色的巧克力,味道是" + flavor);
}
public void share() {
System.out.println("分享這塊" + shape + "形狀的巧克力給他人");
}
public void useAsIngredient() {
System.out.println("將這個巧克力用作配料制作其他甜點");
}
}
使用以上代碼,可以在其他類中創(chuàng)建Chocolate
對象并對其進(jìn)行個性化和操作。例如:
public class Main {
public static void main(String[] args) {
// 創(chuàng)建一個紅色、方形、草莓味的巧克力對象
Chocolate chocolate = new Chocolate("紅色", "方形", "草莓");
// 調(diào)用對象的方法進(jìn)行操作
chocolate.eat();
chocolate.share();
chocolate.useAsIngredient();
}
}
輸出結(jié)果:
吃掉這個紅色的巧克力,味道是草莓
分享這塊方形形狀的巧克力給他人
將這個巧克力用作配料制作其他甜點
以上示例展示了類Chocolate
的使用方式。通過在類中定義屬性和方法,可以根據(jù)需要創(chuàng)建對象的實例,并進(jìn)行個性化配置和操作。
所以在Java中,類(Class)是創(chuàng)建對象(Object)的藍(lán)圖。一個類可以有多個對象實例化,每個對象都具有相同的屬性和方法。類定義了一個對象應(yīng)該包含的數(shù)據(jù)和能夠執(zhí)行的操作。
2?? 三大特性
2.1 封裝(Encapsulation)
封裝是指將數(shù)據(jù)和行為(方法)封裝在一個類中,并隱藏其實現(xiàn)細(xì)節(jié)。這樣做可以保護(hù)數(shù)據(jù)不受外部訪問和修改。
Java的封裝性可以用這樣一個場景來形象描述:
假設(shè)你是一名飯店的顧客,進(jìn)入餐廳后,你將看到一些餐桌、椅子、服務(wù)員和廚師,以及從廚房傳來的美味香氣。然而,你并不需要了解餐廳內(nèi)部的具體細(xì)節(jié),比如餐桌是怎么制作的、椅子使用的木材種類,或者是廚師如何調(diào)制菜肴等等。
這里,餐廳就像是一個Java類,它有自己的屬性和方法。而對于你作為顧客來說,這些屬性和方法都被封裝起來了。封裝就好比是餐廳將其內(nèi)部的細(xì)節(jié)隱藏起來,只向外提供必要的接口。在這個例子中,服務(wù)員就是餐廳對外界提供的接口,她會詢問你的座位需求、點餐、送上美食。
通過封裝,Java類將數(shù)據(jù)(例如屬性)和操作(例如方法)包裝在一起,形成一個不可分割的實體,對外界隱藏了實現(xiàn)的細(xì)節(jié)。這樣做的好處在于,封裝性可以確保類的使用者不需要了解類內(nèi)部的復(fù)雜實現(xiàn),只需要調(diào)用提供的接口即可完成需要的功能。這種套路類似于你在餐廳點菜,你并不需要知道廚師是怎樣做出這么美味的菜肴的,只需告訴服務(wù)員你的需求即可。
總結(jié)一下,Java的封裝性就像是一家飯店,通過隱藏內(nèi)部實現(xiàn)細(xì)節(jié),提供干凈整潔的接口給用戶使用。這種方式讓程序的使用更加簡單、易懂,并且使代碼更安全可靠,因為類的用戶無法直接訪問和修改其內(nèi)部細(xì)節(jié),防止了不必要的錯誤和風(fēng)險。
下面是一個簡單的Java類,用來展示面向?qū)ο蟮姆庋b性:
public class Restaurant {
private String name;
private int numberOfTables;
private Waiter waiter;
public Restaurant(String name, int numberOfTables) {
this.name = name;
this.numberOfTables = numberOfTables;
this.waiter = new Waiter();
}
public void enter() {
System.out.println("You entered the restaurant.");
System.out.println("You see " + numberOfTables + " tables and a pleasant aroma from the kitchen.");
}
public void orderFood(String foodName) {
waiter.takeOrder(foodName);
}
// Inner class representing the waiter
private class Waiter {
public void takeOrder(String foodName) {
System.out.println("Waiter: Your order for " + foodName + " has been taken.");
serveFood(foodName);
}
private void serveFood(String foodName) {
System.out.println("Waiter: Here is your delicious " + foodName + ". Enjoy your meal!");
}
}
}
上述代碼中,Restaurant
類代表了餐廳,具有 name
、numberOfTables
和 waiter
三個私有屬性。通過將這些數(shù)據(jù)成員設(shè)為私有,封裝了類的內(nèi)部細(xì)節(jié),外部用戶無法直接訪問和修改這些屬性。
構(gòu)造函數(shù) Restaurant
用于初始化餐廳的名稱和桌子數(shù)量,并創(chuàng)建一個 Waiter
對象。enter
方法展示了進(jìn)入餐廳后的動作,而 orderFood
方法調(diào)用服務(wù)員提供的接口來點餐。
內(nèi)部類 Waiter
代表服務(wù)員,它包含了 takeOrder
和 serveFood
兩個私有方法。takeOrder
接受食物的名稱,并給用戶提供了一個接口來點餐。serveFood
方法用于將美食送到客人面前。
通過封裝性,外部用戶只需要調(diào)用提供的接口(例如 enter
和 orderFood
),而無需關(guān)心具體的實現(xiàn)細(xì)節(jié)。這增加了代碼的可讀性和安全性,同時使使用者更容易理解和使用程序。如果想進(jìn)一步了解和操作餐廳的內(nèi)部細(xì)節(jié),可以通過添加額外的公共方法來提供更多的功能。
2.2 繼承(Inheritance)
繼承是一種通過從已有類派生新類來共享代碼和行為的機(jī)制。子類可以繼承父類的屬性和方法,并添加自己的特定功能。
同樣的,也可以用一個有趣的比喻來形象描述Java的繼承:
假設(shè)你是一個新手廚師,而Java的繼承就像你擁有了一本神奇的食譜。這本食譜不僅包含了很多經(jīng)典菜肴的制作方法,還提供了一些基礎(chǔ)菜肴的基本步驟。
在這個比喻中,你就是新建的類,而這本食譜就是父類。通過繼承,你可以輕松地獲得這本食譜上的所有菜肴制作方法和基本步驟,而無需重新創(chuàng)建。你只需要專注于添加自己特殊的調(diào)料和烹飪方法以制作出獨特的菜品。
但繼續(xù)深入思考,你可能會發(fā)現(xiàn)這本食譜也是其他人創(chuàng)造的,在這里它就是父類的子類。所以如果你想要對這本食譜進(jìn)行修改或者加入全新的菜肴,你可以創(chuàng)建另外一本新的食譜。這本新的食譜同樣也可以繼承原來那本食譜上的菜肴,然后再加入你自己的烹飪技巧。
通過繼承機(jī)制,Java讓我們可以更加靈活方便地構(gòu)建代碼。就像使用這本食譜,你可以避免重復(fù)勞動,只需專注于自己的菜肴創(chuàng)作,并且隨時可以自由地修改現(xiàn)有的食譜或創(chuàng)建屬于自己的新食譜。這樣,你在Java世界中就可以成為一名獨具特色的大廚了!
以下是一個簡單的Java代碼示例,展示了面向?qū)ο蟮睦^承性:
// 父類 - 食譜
class Recipe {
public void prepareIngredients() {
System.out.println("準(zhǔn)備食材");
}
public void cook() {
System.out.println("烹飪菜肴");
}
}
// 子類 - 新建的類,擁有父類的菜肴制作方法和基本步驟
class Chef extends Recipe {
public void addSpecialSeasoning() {
System.out.println("添加特殊調(diào)料");
}
public void customizeCookingMethod() {
System.out.println("個性化的烹飪方法");
}
// 可以重寫父類的方法
@Override
public void cook() {
System.out.println("根據(jù)自己的菜譜烹飪菜肴");
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
Chef chef = new Chef();
chef.prepareIngredients(); // 調(diào)用從父類繼承的方法
chef.cook(); // 調(diào)用子類重寫過的方法
chef.addSpecialSeasoning(); // 子類新增的方法
chef.customizeCookingMethod(); // 子類新增的方法
}
}
運(yùn)行結(jié)果:
準(zhǔn)備食材
根據(jù)自己的菜譜烹飪菜肴
添加特殊調(diào)料
個性化的烹飪方法
在這個示例中,Recipe
類表示食譜,具有prepareIngredients
和cook
兩種方法。Chef
類是新建的類,它通過繼承Recipe
類,獲得了父類的方法。此外,Chef
類還定義了自己特有的方法addSpecialSeasoning
和customizeCookingMethod
。
通過創(chuàng)建Chef
類的實例,我們可以調(diào)用繼承自父類的方法prepareIngredients
和cook
,以及調(diào)用子類自己新增的方法addSpecialSeasoning
和customizeCookingMethod
。這樣,在Java中應(yīng)用繼承機(jī)制,我們可以更加靈活地構(gòu)建代碼,并擴(kuò)展已有的功能,使之符合不同的需求。
2.3 多態(tài)(Polymorphism)
多態(tài)允許不同類型的對象對同一消息作出響應(yīng)。這意味著使用一個基類類型的引用可以指向其派生類的對象。
我們繼續(xù)通過一個比喻來理解Java的多態(tài):
假設(shè)有一個家族,家族的成員都有一個共同的特點,那就是能夠發(fā)出聲音。在這個家族中,有各種不同的人和動物,比如爸爸、媽媽、狗狗、貓咪等等。
當(dāng)我們要讓這些家族成員發(fā)出聲音時,我們只需要用同一個命令:“發(fā)出聲音”。而不需要區(qū)分不同的成員是爸爸還是狗狗。鑒于家族成員的不同特點,每一個成員都會根據(jù)自己的獨特方式發(fā)出聲音。比如爸爸會說話,狗狗會汪汪叫,貓咪會喵喵叫。
在這個例子中,家族成員即為對象,發(fā)出聲音就是一個共同的方法。Java中的多態(tài)則允許我們使用一個通用的方法名來調(diào)用不同類的同名方法,而不用關(guān)心實際對象的類型。這是因為每個對象都具備相同的方法,但不同的對象可以選擇自己特定的實現(xiàn)方式。
多態(tài)使得代碼更加靈活、可擴(kuò)展。當(dāng)我們需要新增一種家族成員時,只需要繼承于基類并實現(xiàn)自己的特定方法,而不需要修改原有的代碼。這種靈活性和可擴(kuò)展性使得Java多態(tài)成為面向?qū)ο缶幊痰闹匾匦灾弧?/p>
下面是一個使用Java實現(xiàn)多態(tài)性的案例代碼:
// 父類:家族成員
class FamilyMember {
public void makeSound() {
System.out.println("發(fā)出聲音");
}
}
// 子類:爸爸
class Dad extends FamilyMember {
@Override
public void makeSound() {
System.out.println("爸爸說話");
}
}
// 子類:狗狗
class Dog extends FamilyMember {
@Override
public void makeSound() {
System.out.println("汪汪叫");
}
}
// 子類:貓咪
class Cat extends FamilyMember {
@Override
public void makeSound() {
System.out.println("喵喵叫");
}
}
// 主程序
public class Main {
public static void main(String[] args) {
FamilyMember dad = new Dad();
dad.makeSound(); // 調(diào)用爸爸對象的makeSound方法,輸出:"爸爸說話"
FamilyMember dog = new Dog();
dog.makeSound(); // 調(diào)用狗狗對象的makeSound方法,輸出:"汪汪叫"
FamilyMember cat = new Cat();
cat.makeSound(); // 調(diào)用貓咪對象的makeSound方法,輸出:"喵喵叫"
}
}
運(yùn)行結(jié)果:
爸爸說話
汪汪叫
喵喵叫
在上述代碼中,FamilyMember
是一個抽象的基類,定義了所有家族成員共同的方法 makeSound()
。Dad
、Dog
和 Cat
分別是繼承自 FamilyMember
的子類,重寫了 makeSound()
方法,以實現(xiàn)自己特定的發(fā)聲方式。
在主程序中,我們創(chuàng)建了一個爸爸對象、一個狗狗對象和一個貓咪對象,并分別依次調(diào)用它們的 makeSound()
方法。不同類型的對象都能夠使用相同的方法名進(jìn)行調(diào)用,但實際執(zhí)行的是各自特定的實現(xiàn)方式,這就體現(xiàn)了多態(tài)性的特點。
3?? 面向?qū)ο缶幊蹋∣OP)和面向過程編程(PP)
面向?qū)ο缶幊蹋∣bject-Oriented Programming,OOP)和面向過程編程(Procedural Programming)是兩種不同的軟件開發(fā)范式。下面我來全面、完整和清晰地描述它們之間的區(qū)別。
?? 面向過程編程
- 面向過程編程是一種采用函數(shù)、線性代碼流和數(shù)據(jù)結(jié)構(gòu)的編程范式;
- 程序主要由一系列的過程或函數(shù)組成,這些過程按照固定的流程執(zhí)行,并處理傳遞給它們的數(shù)據(jù);
- 數(shù)據(jù)與方法(操作函數(shù))相分離,通常以參數(shù)的形式傳遞給函數(shù)進(jìn)行處理;
- 強(qiáng)調(diào)程序的執(zhí)行順序和對數(shù)據(jù)的直接操作,在程序中需要顯式地控制數(shù)據(jù)和函數(shù)之間的交互關(guān)系;
- 使用全局變量來共享數(shù)據(jù),導(dǎo)致代碼難以維護(hù)和重用。
例如,使用面向過程編程的方式實現(xiàn)計算兩個數(shù)的和:
# 面向過程編程示例
def add(x, y):
return x + y
result = add(3, 5)
?? 面向?qū)ο缶幊?/strong>
- 面向?qū)ο缶幊淌且环N基于對象和類的編程范式,對象是程序中的實體,具有屬性和操作(方法);
- 程序由一組互相協(xié)作的對象組成,通過彼此交互來完成任務(wù);
- 將相關(guān)的數(shù)據(jù)和方法組合成一個對象,對象之間通過消息傳遞進(jìn)行通信;
- 強(qiáng)調(diào)數(shù)據(jù)的封裝和隱藏,每個對象僅對外提供有限的接口,以保護(hù)數(shù)據(jù)的完整性和安全性;
- 類是對象的藍(lán)圖,定義了對象的屬性和行為,并可以創(chuàng)建多個具有相同屬性和行為的對象。
例如,使用面向?qū)ο缶幊痰姆绞綄崿F(xiàn)計算兩個數(shù)的和:
# 面向?qū)ο缶幊淌纠?/span>
class Calculator:
def __init__(self, x, y):
self.x = x
self.y = y
def add(self):
return self.x + self.y
calculator = Calculator(3, 5)
result = calculator.add()
綜上所述,面向過程編程和面向?qū)ο缶幊淘诰帉懘a的思維方式、組織結(jié)構(gòu)和數(shù)據(jù)處理方式上存在著顯著差異。面向過程強(qiáng)調(diào)順序執(zhí)行和直接操作數(shù)據(jù),而面向?qū)ο髣t強(qiáng)調(diào)對象和類的概念、數(shù)據(jù)封裝和代碼復(fù)用。 具體選擇何種編程范式應(yīng)取決于項目需求、代碼結(jié)構(gòu)和開發(fā)團(tuán)隊的偏好。
4?? 方法重載和方法重寫
?? 方法重載(Method Overloading)是指在同一個類中創(chuàng)建多個具有相同名稱但參數(shù)列表不同的方法。 編譯器會根據(jù)調(diào)用時傳遞的參數(shù)類型和數(shù)量來確定調(diào)用哪個方法。
下面是一個定義了方法重載的代碼:
class Calculation {
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
}
public class Main{
public static void main(String[] args) {
Calculation calc = new Calculation();
int result1 = calc.add(3, 4); // 使用 int 參數(shù)的 add 方法
double result2 = calc.add(2.5, 3.7); // 使用 double 參數(shù)的 add 方法
System.out.println(result1);
System.out.println(result2);
}
}
運(yùn)行結(jié)果:
7
6.2
在Calculation
類中,我定義了兩個add
方法。第一個方法接受兩個整數(shù)類型的參數(shù)a
和b
,返回這兩個整數(shù)的和。第二個方法接受兩個雙精度浮點數(shù)類型的參數(shù)a
和b
,返回這兩個浮點數(shù)的和。
在Main
類的main
方法中,首先創(chuàng)建了一個Calculation
對象 calc
。然后,通過調(diào)用calc
對象的add
方法,分別傳入不同的參數(shù)來計算兩個結(jié)果。result1
是將整數(shù)3和4作為參數(shù)傳遞給add(int a, int b)
方法得到的結(jié)果。result2
是將雙精度浮點數(shù)2.5和3.7作為參數(shù)傳遞給add(double a, double b)
方法得到的結(jié)果。
?? 方法重寫(Method Overriding)是指在派生類中重新定義繼承自父類的方法。方法重寫要求方法名稱、參數(shù)列表和返回類型完全相同。
下面是一個定義了方法重寫的代碼:
class Animal {
void makeSound() {
System.out.println("The animal makes a sound");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("The dog barks");
}
}
public class OverridingDemo{
public static void main(String[] args) {
Animal dog = new Dog();
dog.makeSound();
Animal animal = new Animal();
animal.makeSound();
}
}
運(yùn)行結(jié)果:
The dog barks
The animal makes a sound
?? 小結(jié):方法重載與方法重寫的區(qū)別?
- 方法重載是指在一個類中,方法名稱相同而參數(shù)列表(參數(shù)個數(shù)、類型、順序)不同,對權(quán)限符沒有要求;
-
方法重寫是指子類重寫父類的方法,方法的名稱、參數(shù)列表全部相同,重寫方法的權(quán)限符等級必須大于等于被重寫方法的權(quán)限符等級。
5?? 接口和抽象類
?? 接口(Interface)
接口(Interface)是一種規(guī)范,定義了一組方法但沒有實現(xiàn)。一個類可以實現(xiàn)一個或多個接口,強(qiáng)制其提供接口中定義的方法的實現(xiàn)。
下面是一個定義了接口及其實現(xiàn)類的代碼:
interface Drawable {
void draw();
}
class Rectangle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing a rectangle");
}
}
public class interfaceDemo{
public static void main(String[] args) {
Drawable drawable = new Rectangle();
drawable.draw(); // 輸出: "Drawing a rectangle"
}
}
運(yùn)行結(jié)果:
Drawing a rectangle
上面代碼定義了一個接口 Drawable
,該接口包含一個抽象方法 draw()
。接口中的方法聲明只有方法名和簽名,沒有方法體。
接著,代碼定義了一個實現(xiàn)了接口 Drawable
的類 Rectangle
。在該類中,使用 @Override
注解來表示該方法重寫了接口中的 draw()
方法。具體實現(xiàn)是打印出字符串 “Drawing a rectangle”。
接下來,在 interfaceDemo
類中,我在 main
方法中創(chuàng)建了一個 Drawable
接口的引用 drawable
并將其賦值為一個 Rectangle
對象。通過這種方式,我們可以使用 drawable
引用調(diào)用 draw()
方法。
最后,在調(diào)用 drawable.draw()
時,將會執(zhí)行 Rectangle
類中的實現(xiàn)。
?? 抽象類(Abstract Class)
抽象類(Abstract Class)是一個不能被實例化的類,可以包含抽象方法和具體方法。抽象方法是沒有實現(xiàn)的方法,需要在派生類中進(jìn)行實現(xiàn)。
下面是一個定義了抽象類及其實現(xiàn)類的代碼:
abstract class Shape {
abstract double area();
void display() {
System.out.println("Displaying shape");
}
}
class Circle extends Shape {
double radius;
@Override
double area() {
return Math.PI * radius * radius;
}
}
public class AbstractDemo{
public static void main(String[] args) {
Shape shape = new Circle();
shape.display(); // 輸出: "Displaying shape"
double area = shape.area(); // 調(diào)用 Circle 中的 area 方法
System.out.println(area);
}
}
運(yùn)行結(jié)果:
Displaying shape
12.566370614359172
這段代碼定義了一個抽象類 Shape
和一個繼承自 Shape
的子類 Circle
。
抽象類 Shape
中包含兩部分內(nèi)容:
-
abstract double area();
:這是一個抽象方法,沒有具體的實現(xiàn),子類必須提供實現(xiàn)。該方法的目的是計算圖形的面積,并返回一個double
類型的值。 -
void display()
:這是一個普通方法,它有具體的實現(xiàn)。
子類 Circle
繼承自 Shape
,它具有一個 radius
屬性和一個重寫了父類抽象方法 area()
的具體實現(xiàn)。在 Circle
類中,area()
方法使用圓的半徑計算并返回其面積。
在 main
方法中,首先創(chuàng)建了一個 Circle
對象賦值給父類 Shape
類型的引用變量 shape
。然后調(diào)用了 shape
的 display()
方法,輸出字符串 “Displaying shape”。接著,通過 shape
調(diào)用 area()
方法,實際上將調(diào)用到 Circle
類中重寫的 area()
方法,計算并返回了圓的面積。
?? 小結(jié):接口和抽象類的區(qū)別?
在Java中,接口和抽象類都可以用來定義對象的公共行為,但它們之間存在一些重要的區(qū)別。
具體區(qū)別說明如下:
- 定義的關(guān)鍵字不同:接口使用 interface 來定義,抽象類使用 abstract class來定義;
- 子類繼承或?qū)崿F(xiàn)關(guān)鍵字不同:接口實現(xiàn)使用 implements,抽象類繼承使用 extends ;
- 擴(kuò)展限制不同:抽象類有單繼承局限,而接口是多繼承,這也使得接口更加靈活;
- 方法權(quán)限修飾符:抽象類無限制,只是不能被 private 修飾;而接口默認(rèn)且只能是 public 修飾;
- 屬性權(quán)限修飾符:抽象類無限制,而接口默認(rèn)且只能是 public 修飾;
- 結(jié)構(gòu)不同:抽象類中可以有構(gòu)造方法、成員變量(變量及常量)、普通方法、抽象方法;而接口中只能有常量(默認(rèn) public static final 修飾)、抽象方法,在 JDK 8 加入了 static 修飾的靜態(tài)方法和 default 修飾的普通方法;
- 靜態(tài)代碼塊的使用不同:抽象類可以有靜態(tài)代碼塊,而接口不能有;
-
設(shè)計目的不同:抽象類主要是用于代碼復(fù)用和簡化設(shè)計,用于表示某個類的通用行為;而接口是一組規(guī)范,用于指導(dǎo)類的行為,以便多個類可以遵循同樣的規(guī)范進(jìn)行操作。
6?? 包和訪問修飾符
Java中的包(Package)是將相關(guān)類和接口組織在一起的方式。它們提供了命名空間管理、可見性控制和代碼結(jié)構(gòu)組織的好處。
訪問修飾符(Access Modifier)用于控制對類、方法和變量的訪問權(quán)限。
-
public
:可以從任何地方訪問; -
protected
:同一包內(nèi)和子類中可訪問; -
default
(默認(rèn),無修飾符):同一包內(nèi)可訪問; -
private
:僅在當(dāng)前類中可訪問。
下面這段代碼展示了Java中的訪問修飾符(access modifiers)的概念。
我分別定義了在本類、同包下的其他類、不同包的子類、不同包的其他類四種類,對于一個類中各種權(quán)限修飾符修飾的屬性的訪問權(quán)限。
以下是每個類和方法的詳細(xì)代碼及解釋:
package com.xiaoshan.accessmodifier.onepack;
import com.xiaoshan.accessmodifier.anotherpack.AnotherPackClass;
import com.xiaoshan.accessmodifier.anotherpack.AnotherPackSonClass;
public class OneClass {
public String publicVariable = "Public Variable";
protected String protectedVariable = "Protected Variable";
String defaultVariable = "Default Variable";
private String privateVariable = "Private Variable";
public void method(){
String variable = this.publicVariable;
System.out.println("本類可以訪問到:" + variable);
variable = this.protectedVariable;
System.out.println(variable);
variable = this.defaultVariable;
System.out.println(variable);
variable = this.privateVariable;
System.out.println(variable);
}
public static void main(String[] args) {
OneClass clazz = new OneClass();
clazz.method();
SamePackClass samePackClass = new SamePackClass();
samePackClass.method();
AnotherPackSonClass anotherPackSonClass = new AnotherPackSonClass();
anotherPackSonClass.method();
AnotherPackClass anotherPackClass = new AnotherPackClass();
anotherPackClass.method();
}
}
以上,我定義了一個OneClass
公開類。其中,定義了一個公開(public)變量 publicVariable
、一個受保護(hù)(protected)變量 protectedVariable
、一個默認(rèn)訪問修飾符(沒有訪問修飾符)的變量 defaultVariable
、一個私有(private)變量 privateVariable
。
然后定義了一個方法 method()
,用于演示不同訪問修飾符下的變量可見性。可以發(fā)現(xiàn)在本類方法內(nèi)部可以訪問到任何權(quán)限的變量和方法: variable
可以訪問到 publicVariable
、protectedVariable
、defaultVariable
以及 privateVariable
。
package com.xiaoshan.accessmodifier.onepack;
public class SamePackClass {
public void method() {
OneClass oneClass = new OneClass();
String variable = oneClass.publicVariable;
System.out.println("\n同包的類可以訪問到:" + variable);
variable = oneClass.protectedVariable;
System.out.println(variable);
variable = oneClass.defaultVariable;
System.out.println(variable);
// 編譯不通過,訪問不到
// oneClass.privateVariable;
}
}
以上,我定義了一個 SamePackClass
的和oneClass
同包下的類。在method()
方法內(nèi)部創(chuàng)建了 OneClass
的實例,可以發(fā)現(xiàn)能訪問到 publicVariable
、protectedVariable
以及defaultVariable
,但無法訪問 privateVariable
,因為它是 OneClass
中的私有變量。
package com.xiaoshan.accessmodifier.anotherpack;
import com.xiaoshan.accessmodifier.onepack.OneClass;
public class AnotherPackSonClass extends OneClass {
public void method() {
String variable = super.publicVariable;
System.out.println("\n不同包的子類可以訪問到:" + variable);
variable = super.protectedVariable;
System.out.println(variable);
// 編譯不通過,訪問不到
// variable = super.defaultVariable;
// variable = super.privateVariable;
}
}
以上,我定義了一個 AnotherPackSonClass
的類,它繼承自 OneClass
類。
可以發(fā)現(xiàn),在method()
方法內(nèi)部能通過super
訪問到 父類中的 publicVariable
、protectedVariable
。
但無法訪問 defaultVariable
和 privateVariable
,它們分別是 OneClass
中的默認(rèn)變量和私有變量。
package com.xiaoshan.accessmodifier.anotherpack;
import com.xiaoshan.accessmodifier.onepack.OneClass;
public class AnotherPackClass {
public void method() {
OneClass oneClass = new OneClass();
String variable = oneClass.publicVariable;
System.out.println("\n不同包的無關(guān)類可以訪問到:" + variable);
// 編譯不通過,訪問不到
// oneClass.protectedVariable;
// oneClass.defaultVariable;
// oneClass.privateVariable;
}
}
以上,我定義了一個 AnotherPackClass
的和 OneClass
不同包且無繼承關(guān)系的類。
可以發(fā)現(xiàn),在method()
方法內(nèi)部通過創(chuàng)建的 OneClass
的實例只能訪問到 publicVariable
,因為它是公開變量。
而無法訪問 protectedVariable
、defaultVariable
和 privateVariable
,它們分別是 OneClass
中的受保護(hù)、默認(rèn)和私有變量。
最后,運(yùn)行結(jié)果:
本類可以訪問到:Public Variable
Protected Variable
Default Variable
Private Variable
同包的類可以訪問到:Public Variable
Protected Variable
Default Variable
不同包的子類可以訪問到:Public Variable
Protected Variable
不同包的無關(guān)類可以訪問到:Public Variable
7?? 構(gòu)造函數(shù)和析構(gòu)函數(shù)
構(gòu)造函數(shù)(Constructor)是用于初始化對象的特殊方法。它們與類具有相同的名稱,并且沒有返回類型。當(dāng)創(chuàng)建對象時,構(gòu)造函數(shù)會被自動調(diào)用。如果不提供構(gòu)造函數(shù),則編譯器會生成一個默認(rèn)的無參構(gòu)造函數(shù)。
析構(gòu)函數(shù)(Destructor)是在對象被銷毀之前調(diào)用的方法。Java沒有顯式的析構(gòu)函數(shù),而是通過垃圾回收器(Garbage Collector)自動處理內(nèi)存的釋放。
public class Person {
String name;
int age;
// 構(gòu)造函數(shù)
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// ...
}
以上就是Java面向?qū)ο蟮娜恐R的詳細(xì)介紹。通過理解和應(yīng)用這些核心概念,您將能夠更好地進(jìn)行面向?qū)ο蟮木幊?,在Java中開發(fā)出高質(zhì)量的應(yīng)用程序。
8?? 成員變量和局部變量
成員變量(Instance Variables)和局部變量(Local Variables)是編程語言中兩種不同作用域的變量。
?? 成員變量
成員變量屬于一個類或?qū)ο?,并且在類的聲明中定義。它們存在于整個類的生存周期內(nèi),即使沒有被初始化,也會有默認(rèn)值。每個類的實例都擁有一組獨立的成員變量。特點:
- 成員變量的作用域為整個類,在類內(nèi)部的方法、構(gòu)造函數(shù)里都可以使用;
- 成員變量可以被所有該類的對象訪問和修改;
- 成員變量不需要顯式地進(jìn)行初始化,如果沒有賦初值,會有一個默認(rèn)值;
- 每個實例都有獨立的一套成員變量。
示例:
class Car {
String color; // 成員變量
int speed; // 成員變量
void drive() {
System.out.println("正在駕駛" + color + "顏色的車,時速為" + speed + "km/h");
}
}
在上述示例中,color
和speed
是Car類的成員變量,可以被類中的所有方法訪問和修改。
?? 局部變量
局部變量僅在定義它的代碼塊(方法、循環(huán)等)內(nèi)有效,超出該范圍后將無法訪問。局部變量必須顯式地聲明和初始化,否則編譯器會報錯。特點:
- 局部變量的作用域僅限于聲明它的代碼塊內(nèi)部;
- 只有在聲明期間和初始化后才能使用;
- 每次代碼塊執(zhí)行時都會創(chuàng)建一個新的局部變量,該變量只在當(dāng)前代碼塊中有效。
示例:
void calculateSum() {
int a = 5; // 局部變量
int b = 10; // 局部變量
int sum = a + b;
System.out.println("兩個數(shù)的和為:" + sum);
}
在上述示例中,a
和b
是calculateSum
方法中的局部變量,只能在該方法內(nèi)部訪問和使用,在方法執(zhí)行完畢后將無法訪問。
總之,成員變量是屬于類的實例的,存在整個類的生命周期中,而局部變量只在定義它的代碼塊內(nèi)有效,其作用范圍更加有限。
?? 成員變量和局部變量的區(qū)別?
變量定義時,根據(jù)變量所在的代碼塊范圍分為成員變量(也叫實例變量或?qū)ο笞兞浚┖途植孔兞績煞N。
成員變量指定義在類中方法外的變量,局部變量則是定義在方法體或代碼塊內(nèi)部的變量。
成員變量和局部變量的主要區(qū)別在于作用域和生命周期不同。成員變量擁有更長的生命周期和更廣泛的作用域,而局部變量的生命周期和作用域均限定在其聲明的代碼塊內(nèi)或方法調(diào)用過程中。
具體區(qū)別細(xì)節(jié)如下:
- 從語法形式上看,成員變量是屬于類或?qū)ο蟮?,而局部變量是在方法中定義的變量或方法參數(shù);
- 從修飾符上看,成員變量可以被 權(quán)限修飾符等其他關(guān)鍵字所修飾,而局部變量不能被權(quán)限修飾符及static所修飾,但二者都能被final所修飾;
- 從變量在內(nèi)存中的存儲方式及生命周期來看,成員變量是對象的一部分:被static修飾的成員變量也叫類變量,存儲于方法區(qū)當(dāng)中,生命周期與當(dāng)前類相同;未被static修飾的成員變量也叫實例變量,存儲于對象所在的堆內(nèi)存當(dāng)中,生命周期與對象相同。而局部變量存儲于棧內(nèi)存中,其生命周期隨方法的調(diào)用結(jié)束而結(jié)束,變量空間會自動的釋放;
- 從初始值上看,成員變量若未被賦初值,則會自動以對應(yīng)數(shù)據(jù)類型的默認(rèn)值來賦值(一種情況例外:被final修飾但沒有被static修飾的成員變量,必須顯式地賦值)。而局部變量則不會自動賦初值;
- 從使用范圍上看, 局部變量只能在聲明它的方法或代碼塊內(nèi)使用,不能被其他方法或代碼塊訪問。
?? 總結(jié)
本文主要總結(jié)介紹了面向?qū)ο缶幊蹋∣OP)和面向過程編程(PP)各自的編程思想范式特征,面向?qū)ο笾蓄惡蛯ο蟮母拍铌P(guān)系,面向?qū)ο缶幊痰娜筇匦裕悍庋b、繼承和多態(tài)。
以及類中一些相關(guān)概念及其區(qū)別:方法重載和方法重寫、接口和抽象類、包和訪問修飾符。此外,還包括類中成員的大致介紹:構(gòu)造函數(shù)和析構(gòu)函數(shù)、成員變量和局部變量。
?? 本文源碼下載地址
src.zip Java的面向?qū)ο缶幊獭ぶv解代碼文章來源:http://www.zghlxwxcb.cn/news/detail-585096.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-585096.html
到了這里,關(guān)于【Java基礎(chǔ)教程】特別篇:面向?qū)ο蠛陷嫛崎_Java 的面向?qū)ο笊w章時代,一起來發(fā)現(xiàn)OOP的編程秘密!~的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!