有沒有做維修的網(wǎng)站哪有免費(fèi)的網(wǎng)站
大家好,我是栗箏i,這篇文章是我的 “栗箏i 的 Java 技術(shù)棧” 專欄的第 006 篇文章,在 “栗箏i 的 Java 技術(shù)?!?這個(gè)專欄中我會(huì)持續(xù)為大家更新 Java 技術(shù)相關(guān)全套技術(shù)棧內(nèi)容。專欄的主要目標(biāo)是已經(jīng)有一定 Java 開發(fā)經(jīng)驗(yàn),并希望進(jìn)一步完善自己對(duì)整個(gè) Java 技術(shù)體系來(lái)充實(shí)自己的技術(shù)棧的同學(xué)。與此同時(shí),本專欄的所有文章,也都會(huì)準(zhǔn)備充足的代碼示例和完善的知識(shí)點(diǎn)梳理,因此也十分適合零基礎(chǔ)的小白和要準(zhǔn)備工作面試的同學(xué)學(xué)習(xí)。當(dāng)然,我也會(huì)在必要的時(shí)候進(jìn)行相關(guān)技術(shù)深度的技術(shù)解讀,相信即使是擁有多年 Java 開發(fā)經(jīng)驗(yàn)的從業(yè)者和大佬們也會(huì)有所收獲并找到樂(lè)趣。
–
上一篇文章中,我們討論了 Java 的流程控制,包括代碼塊、作用域、循環(huán)與依賴。本篇文章我們將深入了解 Java 語(yǔ)言中的類與對(duì)象,探討構(gòu)造器、
static
、final
、包和 JAR 文件。這些概念是面向?qū)ο缶幊痰暮诵?#xff0c;對(duì)于理解和掌握 Java 至關(guān)重要。最后在前言的末尾我補(bǔ)充一下,如果這篇文章,對(duì)大家有所幫助或收獲一定的樂(lè)趣和想法,那么非常歡迎大家能夠,點(diǎn)贊、評(píng)論、收藏、訂閱。這些也將是我持續(xù)更新的最大動(dòng)力。
文章目錄
- 1、類與對(duì)象
- 1.1、關(guān)于類
- 1.2、關(guān)于對(duì)象
- 1.2.1、Behavior(對(duì)象的行為)
- 1.2.2、State(對(duì)象的狀態(tài))
- 1.2.3、Identity(對(duì)象的標(biāo)識(shí))
- 1.3、類之間的關(guān)系
- 1.3.1、依賴關(guān)系(Dependency)
- 1.3.2、聚合關(guān)系(Aggregation)
- 1.3.3、繼承關(guān)系(Inheritance)
- 2、構(gòu)造器
- 2.1、構(gòu)造器的引入
- 2.2、構(gòu)造器的特點(diǎn)
- 3.3、子父類構(gòu)造器(涉及繼承)
- 3、`static` 關(guān)鍵字
- 3.1、`static` 修飾變量
- 3.2、`static` 修飾方法
- 3.3、`static` 代碼塊
- 3.4、構(gòu)造方法與代碼塊執(zhí)行順序
- 3.5、`static` 內(nèi)部類(涉及內(nèi)部類)
- 4、`final` 關(guān)鍵字
- 4.1、`final` 變量
- 4.2、`final` 方法
- 4.3、`final` 類
- 5、Java 包
- 5.1、包名
- 5.2、`import` 的用法
- 5.3、定義包
- 5.4、類路徑
- 6、JAR 文件
- 6.1、創(chuàng)建 JAR 文件
- 6.2、查看 JAR 文件內(nèi)容
- 6.3、運(yùn)行 JAR 文件
- 6.4、MANIFEST.MF 文件
- 6.5、解壓 JAR 文件
1、類與對(duì)象
面向?qū)ο蟪绦蛟O(shè)計(jì)(簡(jiǎn)稱 OOP)是當(dāng)今主流的程序設(shè)計(jì)范型,它已經(jīng)取代了 20 世紀(jì) 70 年代的"結(jié)構(gòu)化"過(guò)程化程序設(shè)計(jì)開發(fā)技術(shù)。
從一開始學(xué)習(xí) Java 這門技術(shù)時(shí),我們就會(huì)了解到 Java 是完全面向?qū)ο蟮?#xff0c;必須熟悉 OOP 才能夠編寫 Java 程序。同樣的 Java 之所以簡(jiǎn)單而具有優(yōu)勢(shì),就是因?yàn)槊嫦驅(qū)ο笏鶐?lái)的方便。這種方式免去了 C++ 中反復(fù)而難以理解的指針和多繼承,可以讓程序員以優(yōu)雅的思維方式進(jìn)行復(fù)雜的編程。而這之中最為核心也是最基礎(chǔ)的部分就是類與對(duì)象。
1.1、關(guān)于類
類(class)是構(gòu)造對(duì)象的模板或藍(lán)圖。我們可以將類想象成制作小甜餅的切割機(jī),將對(duì)象想象為小甜餅。由類構(gòu)造(construct)對(duì)象的過(guò)程稱為創(chuàng)建類的實(shí)例(instance)。
標(biāo)準(zhǔn)的 Java 庫(kù)提供了幾千個(gè)類,可以用于用戶界面設(shè)計(jì)、日期、日歷和網(wǎng)絡(luò)程序設(shè)計(jì)。盡管如此,還是需要在 Java 程序中創(chuàng)建一些自己的類,以便描述應(yīng)用程序所對(duì)應(yīng)的問(wèn)題域中的對(duì)象。
一個(gè)類一般包含以下幾部分:
- 類名:要遵循大駝峰命名法(UpperCamelCase),如
Person
、Car
; - 屬性:也稱為字段或成員變量,用于存儲(chǔ)對(duì)象的狀態(tài)信息。屬性通常使用訪問(wèn)修飾符(如
private
、protected
、public
)來(lái)控制其可見性; - 方法:也稱為成員方法,用于執(zhí)行操作或表示對(duì)象的行為。方法可以訪問(wèn)和修改對(duì)象的屬性,同樣的,方法通常也使用訪問(wèn)修飾符來(lái)控制其可見性;
- 構(gòu)造方法:特殊的方法,用于在創(chuàng)建對(duì)象時(shí)初始化對(duì)象的狀態(tài)。構(gòu)造方法的名稱必須與類名相同。
以下是一個(gè) Car
類的示例,描述了一個(gè)汽車的屬性和行為:
package com.lizhengi;/*** 汽車類,描述汽車的屬性和行為(類)* 組成部分:* 1. 成員變量(屬性):描述汽車的特性* 2. 構(gòu)造方法:用于創(chuàng)建汽車對(duì)象實(shí)例* 3. 方法:定義汽車的行為** @author Lizhengi*/
public class Car {/* 成員變量(屬性)*//** 汽車品牌 */private String make;/** 汽車型號(hào) */private String model;/** 出廠年份 */private int year;/*** 構(gòu)造方法,用于創(chuàng)建 Car 對(duì)象實(shí)例** @param make 汽車品牌* @param model 汽車型號(hào)* @param year 出廠年份*/public Car(String make, String model, int year) {this.make = make;this.model = model;this.year = year;}/*** 啟動(dòng)引擎的方法*/public void startEngine() {System.out.println("The engine is starting...");}
}
1.2、關(guān)于對(duì)象
對(duì)象與類的關(guān)系是:對(duì)象的共性抽象為類,類的實(shí)例化就是對(duì)象。
對(duì)象(object)是類的實(shí)例,是使用類的構(gòu)造方法創(chuàng)建的實(shí)體。對(duì)象代表現(xiàn)實(shí)生活中的實(shí)體,它擁有類中定義的屬性和方法。每個(gè)對(duì)象都有自己的一組屬性值,這些值定義了對(duì)象的狀態(tài)。通過(guò)調(diào)用對(duì)象的方法,可以改變對(duì)象的狀態(tài)或執(zhí)行某些操作。
例如,汽車(Car)類可以有很多對(duì)象,每個(gè)對(duì)象代表不同的汽車。每輛汽車都有自己的品牌、型號(hào)和出廠年份,但它們都是基于同一個(gè)類(Car)創(chuàng)建的。
要想使用 OOP,一定要清楚對(duì)象的三個(gè)主要特性:
- Behavior(對(duì)象的行為):可以對(duì)對(duì)象施加哪些操作,或可以對(duì)對(duì)象施加哪些方法?
- State(對(duì)象的狀態(tài)):當(dāng)施加那些方法時(shí),對(duì)象如何響應(yīng)?
- Identity(對(duì)象的標(biāo)識(shí)):如何辨別具有相同行為與狀態(tài)的不同對(duì)象?
1.2.1、Behavior(對(duì)象的行為)
對(duì)象的行為是指對(duì)象能夠執(zhí)行的操作或方法。這些操作定義了對(duì)象可以做什么。行為通常通過(guò)方法來(lái)實(shí)現(xiàn),方法可以對(duì)對(duì)象的狀態(tài)進(jìn)行操作或進(jìn)行其他操作。行為是類中定義的,是對(duì)象可以執(zhí)行的具體功能。
例如,汽車對(duì)象的行為可以包括啟動(dòng)引擎(startEngine
)、加速(accelerate
)、剎車(brake
)等。通過(guò)調(diào)用這些方法,能夠使對(duì)象執(zhí)行相應(yīng)的操作。
public class Car {private String make;private String model;private int year;public Car(String make, String model, int year) {this.make = make;this.model = model;this.year = year;}/*** 啟動(dòng)引擎的方法*/public void startEngine() {System.out.println("The engine is starting...");}/*** 加速的方法*/public void accelerate() {System.out.println("The car is accelerating...");}/*** 剎車的方法*/public void brake() {System.out.println("The car is braking...");}
}
1.2.2、State(對(duì)象的狀態(tài))
對(duì)象的狀態(tài)是指對(duì)象在某一時(shí)刻的屬性值。對(duì)象的狀態(tài)由其屬性(成員變量)的值決定。通過(guò)改變屬性的值,可以改變對(duì)象的狀態(tài)。狀態(tài)反映了對(duì)象在特定時(shí)刻的特征和條件。
例如,汽車對(duì)象的狀態(tài)可以包括汽車品牌(make
)、車型(model
)和出廠年份(year
)。這些屬性的值定義了對(duì)象的當(dāng)前狀態(tài)。
public class Car {private String make;private String model;private int year;public Car(String make, String model, int year) {this.make = make;this.model = model;this.year = year;}/* Getter 和 Setter 方法 *//*** 獲取汽車品牌** @return 汽車品牌*/public String getMake() {return make;}/*** 設(shè)置汽車品牌** @param make 汽車品牌*/public void setMake(String make) {this.make = make;}/*** 獲取汽車型號(hào)** @return 汽車型號(hào)*/public String getModel() {return model;}/*** 設(shè)置汽車型號(hào)** @param model 汽車型號(hào)*/public void setModel(String model) {this.model = model;}/*** 獲取出廠年份** @return 出廠年份*/public int getYear() {return year;}/*** 設(shè)置出廠年份** @param year 出廠年份*/public void setYear(int year) {this.year = year;}
}
1.2.3、Identity(對(duì)象的標(biāo)識(shí))
對(duì)象的標(biāo)識(shí)是指每個(gè)對(duì)象在內(nèi)存中的唯一標(biāo)識(shí)。即使兩個(gè)對(duì)象具有相同的狀態(tài)和行為,它們?cè)趦?nèi)存中也是不同的實(shí)體。對(duì)象的標(biāo)識(shí)使得可以區(qū)分具有相同行為和狀態(tài)的不同對(duì)象。
在 Java 中,對(duì)象的標(biāo)識(shí)由內(nèi)存地址決定。即使兩個(gè)對(duì)象的屬性值完全相同,它們?nèi)匀皇遣煌膶?duì)象,因?yàn)樗鼈冊(cè)趦?nèi)存中的地址不同。
public class Main {public static void main(String[] args) {// 創(chuàng)建兩個(gè)具有相同屬性值的 Car 對(duì)象Car car1 = new Car("Toyota", "Corolla", 2020);Car car2 = new Car("Toyota", "Corolla", 2020);// 比較兩個(gè)對(duì)象的內(nèi)存地址if (car1 == car2) {System.out.println("car1 and car2 are the same object.");} else {System.out.println("car1 and car2 are different objects.");}// 比較兩個(gè)對(duì)象的屬性值if (car1.getMake().equals(car2.getMake()) &&car1.getModel().equals(car2.getModel()) &&car1.getYear() == car2.getYear()) {System.out.println("car1 and car2 have the same state.");} else {System.out.println("car1 and car2 have different states.");}}
}
輸出結(jié)果為:
car1 and car2 are different objects.
car1 and car2 have the same state.
通過(guò)理解對(duì)象的行為、狀態(tài)和標(biāo)識(shí),可以更好地掌握面向?qū)ο缶幊痰暮诵母拍?#xff0c;編寫出更具模塊化和可維護(hù)性的代碼。
1.3、類之間的關(guān)系
在面向?qū)ο缶幊讨?#xff0c;類之間的關(guān)系至關(guān)重要,它們決定了系統(tǒng)的結(jié)構(gòu)和行為。常見的類之間的關(guān)系有:依賴、聚合和繼承。
1.3.1、依賴關(guān)系(Dependency)
依賴關(guān)系是指一個(gè)類使用另一個(gè)類的實(shí)例。通常表現(xiàn)為一個(gè)類的方法接收另一個(gè)類的對(duì)象作為參數(shù),或在方法中創(chuàng)建另一個(gè)類的對(duì)象。這種關(guān)系是最弱的耦合關(guān)系。
依賴關(guān)系可以理解為"使用"關(guān)系,即一個(gè)類依賴于另一個(gè)類來(lái)完成某些功能。
示例:
/*** Driver 類表示駕駛員* 依賴關(guān)系:Driver 類依賴于 Car 類*/
public class Driver {public void drive(Car car) {car.startEngine();System.out.println("The driver is driving the car.");}
}
在這個(gè)例子中,Driver
類依賴于 Car
類,drive
方法使用了 Car
類的對(duì)象。
1.3.2、聚合關(guān)系(Aggregation)
聚合關(guān)系是一種"整體-部分"關(guān)系,一個(gè)類包含另一個(gè)類的實(shí)例,但這種關(guān)系并不表示強(qiáng)依賴。被包含的對(duì)象可以獨(dú)立存在,而不會(huì)因?yàn)榘膶?duì)象被銷毀而銷毀。
聚合關(guān)系通常使用成員變量來(lái)實(shí)現(xiàn),一個(gè)類擁有另一個(gè)類的實(shí)例作為其成員變量。
示例:
/*** Engine 類表示引擎*/
public class Engine {public void start() {System.out.println("Engine started.");}
}/*** Car 類表示汽車* 聚合關(guān)系:Car 類聚合了 Engine 類*/
public class Car {private Engine engine;public Car(Engine engine) {this.engine = engine;}public void startCar() {engine.start();System.out.println("Car started.");}
}
在這個(gè)例子中,Car
類聚合了 Engine
類,Car
對(duì)象包含一個(gè) Engine
對(duì)象,但 Engine
對(duì)象可以獨(dú)立存在。
1.3.3、繼承關(guān)系(Inheritance)
繼承關(guān)系是面向?qū)ο缶幊讨械囊环N強(qiáng)依賴關(guān)系,它表示一個(gè)類是另一個(gè)類的子類。子類繼承了父類的屬性和方法,可以復(fù)用父類的代碼,增加代碼的可維護(hù)性和擴(kuò)展性。
繼承關(guān)系是一種"是一種"關(guān)系,子類是父類的一種特殊形式。
示例:
/*** Vehicle 類表示交通工具* 父類*/
public class Vehicle {private String brand;public Vehicle(String brand) {this.brand = brand;}public String getBrand() {return brand;}
}/*** Car 類表示汽車* 繼承關(guān)系:Car 類繼承了 Vehicle 類*/
public class Car extends Vehicle {private String model;public Car(String brand, String model) {super(brand);this.model = model;}public String getModel() {return model;}public void startEngine() {System.out.println("The car engine is starting...");}
}
在這個(gè)例子中,Car
類繼承了 Vehicle
類,Car
類不僅擁有 Vehicle
類的屬性和方法,還可以定義自己的屬性和方法。
2、構(gòu)造器
理解構(gòu)造器之前,首先我們需要了解 Java 中為什么要引入構(gòu)造器,以及構(gòu)造器的作用。在很久之前,程序員們編寫 C 程序總會(huì)忘記初始化變量(這真的是一件瑣碎但必須的事),因此后來(lái) C++ 引入了構(gòu)造器(constructor)的概念,這是一個(gè)在創(chuàng)建對(duì)象時(shí)被自動(dòng)調(diào)用的特殊方法。Java 也采用了構(gòu)造器。
構(gòu)造器也被稱為構(gòu)造方法,是一種特殊的方法,調(diào)用構(gòu)造方法可以創(chuàng)建新對(duì)象。構(gòu)造方法可以執(zhí)行任何操作,實(shí)際應(yīng)用中,構(gòu)造方法一般用于初始化操作,例如初始化對(duì)象的數(shù)據(jù)域。
構(gòu)造函數(shù)與普通方法的主要區(qū)別如下:
-
名稱:構(gòu)造函數(shù)的名稱必須與類名相同,而普通方法可以有任何有效的標(biāo)識(shí)符作為名稱;
-
返回類型:構(gòu)造函數(shù)沒有返回類型,而普通方法必須有返回類型;
-
調(diào)用方式:構(gòu)造函數(shù)在創(chuàng)建對(duì)象時(shí)自動(dòng)調(diào)用,無(wú)需手動(dòng)調(diào)用。而普通方法需要手動(dòng)調(diào)用;
-
用途:構(gòu)造函數(shù)主要用于初始化對(duì)象的狀態(tài)(即設(shè)置屬性的初始值)。而普通方法用于描述對(duì)象的行為。
2.1、構(gòu)造器的引入
構(gòu)造器的定義:在定義構(gòu)造器時(shí),首先使用修飾符(如 public
、private
等)來(lái)指定構(gòu)造器的可見性,然后構(gòu)造方法名必須與類名相同,最后是參數(shù)列表,可以為空也可以包含參數(shù)。構(gòu)造器的主體包含在 {}
內(nèi),用于初始化對(duì)象。
修飾符 構(gòu)造方法名 (參數(shù)列表) {}
引入構(gòu)造器幫助我們解決了哪些問(wèn)題呢?假設(shè)我們每定義一個(gè)類都必須定義一個(gè) initialize()
方法,該方法提醒你,每次使用對(duì)象之前都要執(zhí)行一次該方法,這意味著用戶每次都必須記得自己去調(diào)用此方法,這和上文提到的 C 程序員一樣,很容易就忘記了。Java 構(gòu)造器的出現(xiàn)很好的規(guī)避掉了這種問(wèn)題,創(chuàng)建對(duì)象時(shí),java 會(huì)在使用對(duì)象之前調(diào)用相應(yīng)的構(gòu)造器,保證對(duì)象正確初始化。
首先,讓我們看一下沒有構(gòu)造器時(shí)的情況:
public class Car {private String make;private String model;private int year;// initialize 方法,用于初始化對(duì)象public void initialize(String make, String model, int year) {this.make = make;this.model = model;this.year = year;}public void displayInfo() {System.out.println("Car make: " + make + ", model: " + model + ", year: " + year);}public static void main(String[] args) {Car car = new Car();// 必須手動(dòng)調(diào)用 initialize 方法進(jìn)行初始化car.initialize("Toyota", "Corolla", 2020);car.displayInfo();}
}
在這個(gè)例子中,我們必須手動(dòng)調(diào)用 initialize
方法來(lái)初始化 Car
對(duì)象。如果忘記調(diào)用 initialize
方法,Car
對(duì)象的屬性將保持默認(rèn)值,這可能導(dǎo)致程序錯(cuò)誤。
現(xiàn)在,讓我們看一下使用構(gòu)造器的情況:
public class Car {private String make;private String model;private int year;// 構(gòu)造器,用于初始化對(duì)象public Car(String make, String model, int year) {this.make = make;this.model = model;this.year = year;}public void displayInfo() {System.out.println("Car make: " + make + ", model: " + model + ", year: " + year);}public static void main(String[] args) {// 創(chuàng)建對(duì)象時(shí)自動(dòng)調(diào)用構(gòu)造器進(jìn)行初始化Car car = new Car("Toyota", "Corolla", 2020);car.displayInfo();}
}
在這個(gè)例子中,構(gòu)造器 Car(String make, String model, int year)
在創(chuàng)建對(duì)象時(shí)自動(dòng)調(diào)用,確保對(duì)象在使用前已被正確初始化。這樣就不必?fù)?dān)心忘記調(diào)用初始化方法,從而提高了代碼的安全性和可維護(hù)性。
2.2、構(gòu)造器的特點(diǎn)
構(gòu)造器具有以下特點(diǎn):
- 每一個(gè)類都必須有一個(gè)構(gòu)造方法:如果自己不寫,編譯的時(shí)候,系統(tǒng)會(huì)給出默認(rèn)構(gòu)造方法。默認(rèn)構(gòu)造器(又名無(wú)參構(gòu)造器)是沒有形式參數(shù)的,它創(chuàng)建的是 “默認(rèn)對(duì)象”;
- 構(gòu)造器的命名必須與類名相同:這確保了構(gòu)造器可以正確地識(shí)別和關(guān)聯(lián)到類;
- 構(gòu)造器沒有返回類型:包括沒有
void
,也不需要寫返回值。因?yàn)樗菫闃?gòu)建對(duì)象的,對(duì)象創(chuàng)建完,方法就執(zhí)行結(jié)束; - 構(gòu)造器可以有參數(shù),可以重載:有默認(rèn)無(wú)參構(gòu)造,也有帶參構(gòu)造。為了滿足不同的初始化需求,我們通常會(huì)需要定義多個(gè)帶參構(gòu)造器,由于都是構(gòu)造器,它們的名稱必須相同,為了讓方法名相同而參數(shù)不同的方法存在,我們就必須使用方法重載,這是構(gòu)造器所必須的;
- 構(gòu)造器在創(chuàng)建對(duì)象時(shí)自動(dòng)調(diào)用:而且只執(zhí)行一次
以下是帶有多個(gè)構(gòu)造器的類示例:
public class Car {private String make;private String model;private int year;// 默認(rèn)構(gòu)造器public Car() {this.make = "Unknown";this.model = "Unknown";this.year = 0;System.out.println("默認(rèn)構(gòu)造器被調(diào)用");}// 帶參構(gòu)造器public Car(String make, String model) {this.make = make;this.model = model;this.year = 0;System.out.println("帶參構(gòu)造器(make, model)被調(diào)用");}// 帶參構(gòu)造器public Car(String make, String model, int year) {this.make = make;this.model = model;this.year = year;System.out.println("帶參構(gòu)造器(make, model, year)被調(diào)用");}public void displayInfo() {System.out.println("Car make: " + make + ", model: " + model + ", year: " + year);}public static void main(String[] args) {// 調(diào)用默認(rèn)構(gòu)造器Car car1 = new Car(); car1.displayInfo();// 調(diào)用帶參構(gòu)造器(make, model)Car car2 = new Car("Toyota", "Corolla"); car2.displayInfo();// 調(diào)用帶參構(gòu)造器(make, model, year)Car car3 = new Car("Honda", "Civic", 2022); car3.displayInfo();}
}
在這個(gè)例子中,Car
類有三個(gè)構(gòu)造器:一個(gè)默認(rèn)構(gòu)造器和兩個(gè)帶參構(gòu)造器。通過(guò)實(shí)例化 Car
類,我們可以看到不同的構(gòu)造器被調(diào)用,并初始化不同的對(duì)象屬性。
3.3、子父類構(gòu)造器(涉及繼承)
在創(chuàng)建子類對(duì)象時(shí),父類的構(gòu)造方法會(huì)先執(zhí)行,因?yàn)樽宇愔兴袠?gòu)造方法的第一行有默認(rèn)的隱式 super();
語(yǔ)句,它是用來(lái)訪問(wèn)父類中的空參數(shù)構(gòu)造方法,進(jìn)行父類成員的初始化操作。
this()
是調(diào)用本類的構(gòu)造方法,super()
是調(diào)用父類的構(gòu)造方法,且兩條語(yǔ)句不能同時(shí)存在。
子類構(gòu)造方法時(shí)使用 super()
和 this
的注意點(diǎn):
- 子類的所有構(gòu)造方法,直接或間接必須調(diào)用到父類構(gòu)造方法。所以在 Java 中,每個(gè)構(gòu)造方法都必須在其構(gòu)造方法體的第一行顯式或隱式地調(diào)用
super()
或this()
。 super()
和this()
調(diào)用父類的構(gòu)造方法,必須在構(gòu)造方法的第一行。super()
與this()
不能同時(shí)被構(gòu)造方法調(diào)用!(因?yàn)槎呔蠓旁诘谝恍胁判?。
此外,構(gòu)造方法是可以被 private
修飾,作用是:其他程序無(wú)法創(chuàng)建該類的對(duì)象。而當(dāng)父類使用 private
修飾構(gòu)造方法時(shí),子類將無(wú)法正常調(diào)用 super()
方法。
以下是一個(gè)示例,展示了如何在子類構(gòu)造方法中使用 super()
和 this()
:
class Parent {/** 父類的名字 */private String name;/** * 父類無(wú)參構(gòu)造方法 */public Parent() {this.name = "Default Parent";System.out.println("Parent class no-arg constructor called");}/** * 父類帶參構(gòu)造方法 * @param name 父類的名字*/public Parent(String name) {this.name = name;System.out.println("Parent class parameterized constructor called");}
}class Child extends Parent {/** 子類的年齡 */private int age;/** * 子類無(wú)參構(gòu)造方法,調(diào)用父類的無(wú)參構(gòu)造方法 */public Child() {// 默認(rèn)調(diào)用 super();this.age = 0;System.out.println("Child class no-arg constructor called");}/** * 子類帶參構(gòu)造方法,調(diào)用父類的帶參構(gòu)造方法 * @param name 子類的名字* @param age 子類的年齡*/public Child(String name, int age) {super(name); // 調(diào)用父類的帶參構(gòu)造方法this.age = age;System.out.println("Child class parameterized constructor called");}/** * 子類帶單個(gè)參數(shù)的構(gòu)造方法,調(diào)用本類的無(wú)參構(gòu)造方法 * @param age 子類的年齡*/public Child(int age) {this(); // 調(diào)用本類的無(wú)參構(gòu)造方法this.age = age;System.out.println("Child class parameterized constructor (age) called");}
}public class Main {public static void main(String[] args) {// 創(chuàng)建子類對(duì)象,調(diào)用不同的構(gòu)造方法Child child1 = new Child();System.out.println();Child child2 = new Child("John", 10);System.out.println();Child child3 = new Child(20);}
}
運(yùn)行結(jié)果:
Parent class no-arg constructor called
Child class no-arg constructor calledParent class parameterized constructor called
Child class parameterized constructor calledParent class no-arg constructor called
Child class no-arg constructor called
Child class parameterized constructor (age) called
在這個(gè)示例中,我們可以看到:
- 當(dāng)調(diào)用
Child
類的無(wú)參構(gòu)造方法時(shí),隱式調(diào)用了父類的無(wú)參構(gòu)造方法; - 當(dāng)調(diào)用
Child
類的帶參構(gòu)造方法時(shí),顯式調(diào)用了父類的帶參構(gòu)造方法; - 當(dāng)調(diào)用
Child
類的帶有單個(gè)參數(shù)的構(gòu)造方法時(shí),顯式調(diào)用了本類的無(wú)參構(gòu)造方法,然后進(jìn)行了額外的初始化。
3、static
關(guān)鍵字
static
關(guān)鍵字是 Java 中用于定義類成員(變量或方法)的關(guān)鍵字。使用 static
關(guān)鍵字修飾的成員屬于類本身,而不是類的實(shí)例。static
關(guān)鍵字可以用于變量、方法、代碼塊和內(nèi)部類。
- 靜態(tài)變量:
static
關(guān)鍵字用來(lái)聲明獨(dú)立于對(duì)象的靜態(tài)變量,無(wú)論一個(gè)類實(shí)例化多少對(duì)象,它的靜態(tài)變量只有一份拷貝。 靜態(tài)變量也被稱為類變量。局部變量不能被聲明為static
變量。 - 靜態(tài)方法:
static
關(guān)鍵字用來(lái)聲明獨(dú)立于對(duì)象的靜態(tài)方法。靜態(tài)方法不能使用類的非靜態(tài)變量。靜態(tài)方法從參數(shù)列表得到數(shù)據(jù),然后計(jì)算這些數(shù)據(jù)。 - 靜態(tài)代碼塊:
static
關(guān)鍵字還可以形成靜態(tài)代碼塊以優(yōu)化程序性能。static
代碼塊在類加載的時(shí)候就運(yùn)行了,而且只運(yùn)行一次,同時(shí)運(yùn)行時(shí)機(jī)是在構(gòu)造函數(shù)之前。
總的來(lái)說(shuō),static
關(guān)鍵字主要有以下幾個(gè)作用:實(shí)現(xiàn)類的成員共享,節(jié)省內(nèi)存;優(yōu)化程序性能,提高運(yùn)行效率。作為工具類的方法修飾符,方便調(diào)用。
3.1、static
修飾變量
static
變量,也稱為類變量或靜態(tài)變量,是屬于類本身的變量。與實(shí)例字段的,在每個(gè)實(shí)例中都有自己的一個(gè)獨(dú)立 “空間” 不同,靜態(tài)字段只有一個(gè)共享 “空間”,而所有實(shí)例都會(huì)共享該字段。當(dāng)一個(gè)實(shí)例修改該變量時(shí),所有其他實(shí)例都可以看到修改后的值。
雖然實(shí)例可以訪問(wèn)靜態(tài)字段,但是它們指向的其實(shí)都是Person class
的靜態(tài)字段。所以,所有實(shí)例共享一個(gè)靜態(tài)字段。
因此,不推薦用 實(shí)例變量.靜態(tài)字段
去訪問(wèn)靜態(tài)字段,因?yàn)樵?Java 程序中,實(shí)例對(duì)象并沒有靜態(tài)字段。在代碼中,實(shí)例對(duì)象能訪問(wèn)靜態(tài)字段只是因?yàn)榫幾g器可以根據(jù)實(shí)例類型自動(dòng)轉(zhuǎn)換為 類名.靜態(tài)字段
來(lái)訪問(wèn)靜態(tài)對(duì)象。推薦用類名來(lái)訪問(wèn)靜態(tài)字段
示例:
public class Counter {/** 靜態(tài)變量 count */private static int count = 0;public Counter() {count++;}public static int getCount() {return count;}public static void main(String[] args) {Counter c1 = new Counter();Counter c2 = new Counter();Counter c3 = new Counter();// 打印出 3,因?yàn)閯?chuàng)建了三個(gè) Counter 實(shí)例System.out.println("Count: " + Counter.getCount());}
}
在這個(gè)例子中,count
是一個(gè) static
變量,所有 Counter
實(shí)例共享同一個(gè) count
變量。每次創(chuàng)建 Counter
實(shí)例時(shí),count
變量都會(huì)增加。
3.2、static
修飾方法
static
方法是屬于類本身的方法,可以直接通過(guò)類名調(diào)用,而不需要?jiǎng)?chuàng)建類的實(shí)例。static
方法不能訪問(wèn)實(shí)例變量和實(shí)例方法,但可以訪問(wèn)靜態(tài)變量和靜態(tài)方法。
示例:
public class MathUtils {/** 靜態(tài)方法 add */public static int add(int a, int b) {return a + b;}public static void main(String[] args) {// 通過(guò)類名直接調(diào)用靜態(tài)方法int sum = MathUtils.add(5, 3);System.out.println("Sum: " + sum);}
}
在這個(gè)例子中,add
是一個(gè) static
方法,可以通過(guò)類名 MathUtils
直接調(diào)用,而不需要?jiǎng)?chuàng)建 MathUtils
的實(shí)例。
3.3、static
代碼塊
static
代碼塊用于初始化類級(jí)別的資源,它在類加載時(shí)執(zhí)行,并且只執(zhí)行一次。static
代碼塊通常用于初始化靜態(tài)變量。
示例:
public class Configuration {/** 靜態(tài)變量 config */private static String config;/** 靜態(tài)代碼塊 */static {config = "Default Configuration";System.out.println("Static block executed");}public static String getConfig() {return config;}public static void main(String[] args) {// 靜態(tài)代碼塊已在類加載時(shí)執(zhí)行System.out.println("Config: " + Configuration.getConfig());}
}
在這個(gè)例子中,靜態(tài)代碼塊在類加載時(shí)執(zhí)行,初始化靜態(tài)變量 config
。
3.4、構(gòu)造方法與代碼塊執(zhí)行順序
在 Java 中,類的靜態(tài)代碼塊、非靜態(tài)代碼塊和構(gòu)造方法有特定的執(zhí)行順序。理解這些執(zhí)行順序有助于我們更好地掌握類的初始化過(guò)程,確保代碼按預(yù)期運(yùn)行。
當(dāng)一個(gè)類的實(shí)例被創(chuàng)建時(shí),執(zhí)行順序如下:
父類B靜態(tài)代碼塊 > 子類A靜態(tài)代碼塊 > 父類B非靜態(tài)代碼塊 > 父類B構(gòu)造函數(shù) > 子類A非靜態(tài)代碼塊 > 子類A構(gòu)造函數(shù)
為了更好地理解這種順序,我們來(lái)看一個(gè)示例:
class Parent {/** 父類靜態(tài)代碼塊 */static {System.out.println("父類 B 靜態(tài)代碼塊");}/** 父類非靜態(tài)代碼塊 */{System.out.println("父類 B 非靜態(tài)代碼塊");}/** 父類構(gòu)造方法 */public Parent() {System.out.println("父類 B 構(gòu)造方法");}
}class Child extends Parent {/** 子類靜態(tài)代碼塊 */static {System.out.println("子類 A 靜態(tài)代碼塊");}/** 子類非靜態(tài)代碼塊 */{System.out.println("子類 A 非靜態(tài)代碼塊");}/** 子類構(gòu)造方法 */public Child() {System.out.println("子類 A 構(gòu)造方法");}public static void main(String[] args) {System.out.println("創(chuàng)建子類 A 的實(shí)例:");new Child();}
}
運(yùn)行結(jié)果:
父類 B 靜態(tài)代碼塊
子類 A 靜態(tài)代碼塊
創(chuàng)建子類 A 的實(shí)例:
父類 B 非靜態(tài)代碼塊
父類 B 構(gòu)造方法
子類 A 非靜態(tài)代碼塊
子類 A 構(gòu)造方法
從輸出結(jié)果可以看出執(zhí)行順序如下:
- 父類靜態(tài)代碼塊:首先執(zhí)行父類的靜態(tài)代碼塊。靜態(tài)代碼塊只在類加載時(shí)執(zhí)行一次;
- 子類靜態(tài)代碼塊:接著執(zhí)行子類的靜態(tài)代碼塊。靜態(tài)代碼塊只在類加載時(shí)執(zhí)行一次;
- 父類非靜態(tài)代碼塊:然后執(zhí)行父類的非靜態(tài)代碼塊。非靜態(tài)代碼塊在每次創(chuàng)建對(duì)象時(shí)都會(huì)執(zhí)行;
- 父類構(gòu)造方法:在父類的非靜態(tài)代碼塊之后,執(zhí)行父類的構(gòu)造方法;
- 子類非靜態(tài)代碼塊:接下來(lái)執(zhí)行子類的非靜態(tài)代碼塊。非靜態(tài)代碼塊在每次創(chuàng)建對(duì)象時(shí)都會(huì)執(zhí)行;
- 子類構(gòu)造方法:最后執(zhí)行子類的構(gòu)造方法。
3.5、static
內(nèi)部類(涉及內(nèi)部類)
static
內(nèi)部類是使用 static
關(guān)鍵字修飾的內(nèi)部類。靜態(tài)內(nèi)部類不能訪問(wèn)外部類的非靜態(tài)成員,但可以訪問(wèn)外部類的靜態(tài)成員。
示例:
public class OuterClass {private static String staticOuterField = "Static Outer field";/** 靜態(tài)內(nèi)部類 */public static class StaticInnerClass {public void display() {System.out.println("Static Outer field: " + staticOuterField);}}public static void main(String[] args) {OuterClass.StaticInnerClass staticInner = new OuterClass.StaticInnerClass();staticInner.display();}
}
在這個(gè)例子中,StaticInnerClass
是 OuterClass
的靜態(tài)內(nèi)部類,可以訪問(wèn)外部類的靜態(tài)成員 staticOuterField
。
4、final
關(guān)鍵字
final
關(guān)鍵字是 Java 中的一個(gè)修飾符,用于表示最終、不可變的含義。final
關(guān)鍵字可以修飾變量、方法和類,每種用法都有其特定的意義和作用。
4.1、final
變量
在變量層面,final
關(guān)鍵字用于聲明常量,一旦被賦值,就無(wú)法再修改。這有助于提高代碼的可讀性和可維護(hù)性,同時(shí)也避免了一些潛在的 Bug。
final
變量可以是基本類型或引用類型,對(duì)于引用類型,final
表示引用本身不可改變,但引用的對(duì)象內(nèi)容可以改變。
示例:
public class Constants {/** 定義常量 */public static final int MAX_VALUE = 100;public static void main(String[] args) {// MAX_VALUE = 200; // 編譯錯(cuò)誤,final 變量不能被重新賦值System.out.println("Max value: " + MAX_VALUE);}
}
在這個(gè)例子中,MAX_VALUE
是一個(gè) final
變量,表示常量,其值不能被改變。
4.2、final
方法
在方法級(jí)別,final
關(guān)鍵字表示該方法不能被子類重寫。這對(duì)于確保某些方法的邏輯不被修改是非常有用的,尤其是一些關(guān)鍵的算法或者安全性相關(guān)的方法,這在設(shè)計(jì)一個(gè)類的 API 時(shí)非常有用,可以防止子類改變父類中的關(guān)鍵行為。
示例:
public class Parent {/** final 方法 */public final void show() {System.out.println("This is a final method.");}
}public class Child extends Parent {// 試圖重寫 final 方法會(huì)導(dǎo)致編譯錯(cuò)誤// public void show() {// System.out.println("Cannot override final method.");// }
}
在這個(gè)例子中,show
方法被聲明為 final
,因此不能在 Child
類中被重寫。
4.3、final
類
當(dāng)我們使用 final
修飾一個(gè)類時(shí),意味著這個(gè)類不能被繼承,也就是說(shuō),它是一個(gè)終結(jié)類,不允許其他類再來(lái)繼承它。這樣做的好處是防止其他類修改或擴(kuò)展該類,保護(hù)了類的完整性。
示例:
public final class ImmutableClass {private final int value;public ImmutableClass(int value) {this.value = value;}public int getValue() {return value;}
}// 試圖繼承 final 類會(huì)導(dǎo)致編譯錯(cuò)誤
// public class SubClass extends ImmutableClass {
// }
在這個(gè)例子中,ImmutableClass
是一個(gè) final
類,不能被繼承。
總的來(lái)說(shuō),final
關(guān)鍵字的作用是為了讓我們的代碼更加穩(wěn)定和可靠,避免不必要的修改和繼承。當(dāng)你看到某個(gè)類、方法或者變量被標(biāo)記為 final
時(shí),就知道它是不可變的,可以更加放心地使用。
5、Java 包
Java 包(Package)是用于組織類和接口的一種機(jī)制,它提供了命名空間來(lái)管理類和接口,避免命名沖突,并且可以控制訪問(wèn)權(quán)限。通過(guò)使用包,可以將相關(guān)的類和接口組織在一起,形成模塊化的代碼結(jié)構(gòu),便于維護(hù)和管理。
5.1、包名
Java 允許使用包將類組織起來(lái)。借助于包可以方便地組織自己的代碼,并將自己的代碼與別人提供的代碼庫(kù)分開管理。使用包的主要原因是確保類名的唯一性。同名的類放置在不同的包中,就不會(huì)產(chǎn)生沖突。如同硬盤的目錄嵌套一樣,也可以使用嵌套層次組織包。
示例:
package com.example.myapp;
在這個(gè)例子中,com.example.myapp
是包名。通常,包名使用小寫字母,以便區(qū)分類名。
為了保證包名的絕對(duì)唯一性,Sun 公司建議將公司的因特網(wǎng)域名以逆序的形式作為包名,并且對(duì)于不同的項(xiàng)目使用不同的子包。所以,我們經(jīng)常在類庫(kù)中看到一堆包名像這樣的:com.sun
和 org.apache
等等。
5.2、import
的用法
import
語(yǔ)句用于引入其他包中的類或接口,使得在當(dāng)前類中可以直接使用它們。可以導(dǎo)入具體的類,也可以使用通配符 *
導(dǎo)入整個(gè)包。
示例:
// 導(dǎo)入具體的類
import java.util.ArrayList;// 導(dǎo)入整個(gè)包
import java.util.*;
在這個(gè)例子中,ArrayList
類被導(dǎo)入,可以在代碼中直接使用。同時(shí),使用 *
可以導(dǎo)入 java.util
包中的所有類和接口。
一個(gè)完整的類名是「包名+類名」,在沒有 import
導(dǎo)入的情況下,使用一個(gè)類需要給出完整的類名,如 java.util.Date
。為了方便,Java 自動(dòng)導(dǎo)入兩個(gè)包:java.lang
包和默認(rèn)包。
注意,從編譯器的角度來(lái)看,嵌套的包之間沒有任何關(guān)系、例如,java.util
包和 java.util.jar
包毫無(wú)關(guān)系。每一個(gè)都擁有獨(dú)立的類集合。
還有,只要使用星號(hào)(*
)來(lái)導(dǎo)入一個(gè)包,而不能使用 import java.*
來(lái)導(dǎo)入以 java 為前綴的所有包。而且,如果要同時(shí)使用兩個(gè)類名相同的類,只能在使用的時(shí)候給出類的完整包名。
在包中定位類是編譯器的工作。因此,.class 文件中的字節(jié)碼是使用完整的包名來(lái)引用其他的類。這樣 Jvm 就會(huì)直接根據(jù)這個(gè)包名來(lái)找到對(duì)應(yīng)的 .class 文件。
Ps:
import
語(yǔ)句不僅可以導(dǎo)入類,還可以導(dǎo)入靜態(tài)方法和靜態(tài)域。例如通過(guò)import static java.lang.System.*;
就可以使用 System 類的靜態(tài)方法和靜態(tài)域,而不必加類名前綴。
5.3、定義包
在 Java 中,通過(guò)在源文件的第一行使用 package
語(yǔ)句來(lái)定義包。包名與目錄結(jié)構(gòu)對(duì)應(yīng),編譯器根據(jù)包名將類文件放在相應(yīng)的目錄中。
示例:
package com.example.myapp;public class MyClass {public void display() {System.out.println("Hello from MyClass in package com.example.myapp");}
}
假設(shè)源文件存放在 src/com/example/myapp/MyClass.java
,編譯后的類文件將存放在 bin/com/example/myapp/MyClass.class
。
5.4、類路徑
類路徑(Classpath)是 Java 虛擬機(jī)和編譯器用來(lái)尋找類文件的路徑。可以通過(guò)設(shè)置類路徑,使得 JVM 和編譯器可以找到包和類。
設(shè)置類路徑的方法有多種:
- 通過(guò)命令行參數(shù)設(shè)置:
javac -cp path/to/classes com/example/myapp/MyClass.java
java -cp path/to/classes com.example.myapp.MyClass
- 通過(guò)環(huán)境變量
CLASSPATH
設(shè)置:
export CLASSPATH=path/to/classes
- 通過(guò)
manifest
文件設(shè)置:在 JAR 文件的META-INF/MANIFEST.MF
文件中添加類路徑。
示例:
# 編譯并運(yùn)行 MyClass
javac -d bin src/com/example/myapp/MyClass.java
java -cp bin com.example.myapp.MyClass
在這個(gè)例子中,javac
命令使用 -d
選項(xiàng)將編譯后的類文件放在 bin
目錄中,java
命令使用 -cp
選項(xiàng)設(shè)置類路徑為 bin
目錄,然后運(yùn)行 MyClass
。
6、JAR 文件
Java Archive (JAR) 文件是用于打包多個(gè) Java 類、元數(shù)據(jù)和資源(如圖像、音頻文件等)的壓縮文件格式。JAR 文件使用 ZIP 文件格式進(jìn)行打包,可以包含多個(gè)文件和目錄結(jié)構(gòu)。JAR 文件的主要用途是分發(fā)和部署 Java 應(yīng)用程序和庫(kù)。
6.1、創(chuàng)建 JAR 文件
要?jiǎng)?chuàng)建一個(gè) JAR 文件,可以使用 jar
命令行工具。假設(shè)我們有以下目錄結(jié)構(gòu):
myapp/
├── com/
│ └── example/
│ └── MyApp.class
└── resources/└── config.properties
要?jiǎng)?chuàng)建一個(gè)包含 com.example.MyApp
類和 resources/config.properties
文件的 JAR 文件,可以使用以下命令:
jar cvf myapp.jar -C myapp .
參數(shù)說(shuō)明:
c
:創(chuàng)建新的 JAR 文件。v
:生成詳細(xì)輸出。f
:指定 JAR 文件名。-C
:切換到指定目錄并包含目錄內(nèi)容。
此命令會(huì)在當(dāng)前目錄下創(chuàng)建一個(gè)名為 myapp.jar
的 JAR 文件,包含 myapp
目錄中的所有文件和目錄結(jié)構(gòu)。
6.2、查看 JAR 文件內(nèi)容
可以使用 jar
命令查看 JAR 文件的內(nèi)容:
jar tf myapp.jar
此命令會(huì)列出 JAR 文件中的所有文件和目錄。
6.3、運(yùn)行 JAR 文件
要運(yùn)行一個(gè)包含主類(包含 main
方法)的 JAR 文件,需要在創(chuàng)建 JAR 文件時(shí)指定主類。在創(chuàng)建 JAR 文件時(shí),可以使用 -e
參數(shù)指定主類:
jar cvfe myapp.jar com.example.MyApp -C myapp .
參數(shù)說(shuō)明:
e
:指定主類。
然后,可以使用 java -jar
命令運(yùn)行 JAR 文件:
java -jar myapp.jar
6.4、MANIFEST.MF 文件
JAR 文件中包含一個(gè)特殊的文件 META-INF/MANIFEST.MF
,用于存儲(chǔ)關(guān)于 JAR 文件的元數(shù)據(jù)。MANIFEST.MF
文件可以包含以下信息:
Manifest-Version
:清單文件的版本。Main-Class
:指定 JAR 文件的主類。Class-Path
:指定依賴的 JAR 文件路徑。
示例 MANIFEST.MF
文件內(nèi)容:
Manifest-Version: 1.0
Main-Class: com.example.MyApp
Class-Path: lib/dependency.jar
可以手動(dòng)創(chuàng)建或編輯 MANIFEST.MF
文件,然后在創(chuàng)建 JAR 文件時(shí)將其包含在內(nèi):
jar cvfm myapp.jar MANIFEST.MF -C myapp .
6.5、解壓 JAR 文件
可以使用 jar
命令解壓 JAR 文件:
jar xvf myapp.jar
此命令會(huì)將 JAR 文件中的所有文件解壓到當(dāng)前目錄。