1. 程式人生 > 其它 >Java學習筆記:2022年1月13日(其一)

Java學習筆記:2022年1月13日(其一)

Java學習筆記:2022年1月13日(其一)

摘要:這篇筆記主要講解了Java中的自定義類、以及構造一個類時所需要了解的一些重點知識。

@

目錄

1.類的組成

​ 眾所周知,Java是書寫在類中的語言,我們在寫Java程式碼時用到的很多操作都要先擁有一個類的例項,進而呼叫類得方法,或者是有一些類擁有靜態方法,我們直接使用類名呼叫這個方法,但總之都離不開類,這些類多是別人寫好的,我們自己寫Java程式也離不開類,我們需要經常的自己寫各種各樣的類,那麼一個類需要那些組成部分呢?

​ 一個類的組成部分為:域(屬性)、構造方法以及自身具備的普通方法。如下:

class ClassName{
	field1;
	field2;
	...
	consturctor1;
	consturctor2;
	...
	method1;
	method2;
}

​ 類中還有一種東西叫註解,屬於類資訊的一種,嚴格意義上講它並不參與Java類的結構性組成,並不是一個必要的部分,因此我們通常認為一個Java類由域(屬性)、構造方法、普通方法構成,下面我們就詳細說明這三個基本組成部分。

1.域(屬性)

​ 域實際上就是屬性,在歐美國家的文獻中,他們稱一個在Java類中定義的變數為“field

”,直接翻譯的話就是“領域”的意思,實際上它對應的就是我們經常說的屬性,使用屬性這一個詞對我們中國人來說更加容易理解。類中直接定義的變數就是類的屬性,類的屬性是一個類最重要的組成部分之一,它直接描述了一個類的特徵,同時類的屬性也基本上是問題的處理中所以資料資訊的載體了,我們是通過一系列的屬性來確定一個類的性質的,正如我們根據各種各樣的特徵確定一種現實中的物品。在各種專案的實施中,基本上我們都在和屬性這個東西打交道,屬性也被稱為域,之後見到域這個名詞,知道它指的是屬性即可

2.構造方法

​ 在之前我們已經比較詳細的解答過構造方法是什麼以及構造方法在一個物件生成語句中的作用了。構造方法沒有返回值型別,它必須和類同名,除此之外,我們可以隨意定義一個構造方法。構造方法並不是真正構造一個物件,構造物件的實際上是new引起的各種操作,構造方法起到的作用實際上是在構造好物件例項之後,在返回例項地址之前初始化這個例項,至於如何初始化,這和我們在構造方法中書寫的程式碼有關係。之前我們也成構造方法為構造器,這只是叫法不同,我們可以稱這種結構為構造方法,也可以稱呼為構造器,有時為了和普通方法進行區分,就會稱它為構造器

3.普通方法

​ 人類有著姓名、性別、年齡之類的屬性,他們同時也可以行走,跑步,吃喝,而Java中的類也一樣,既有自己的屬性,也可以做出一些可以被觀測到的行為,這些行為我們稱之為“方法”。這些方法不同於構造器,它們在類中就是單純的作為方法存在,它們通常是一段封裝的程式碼段,使用方法定義的形式進行書寫之後,呼叫方法名就可以直接執行這一段程式碼。

​ Java類中的普通方法的定義基本上如下所示:

修飾符(如public、private) 返回值型別(如int、String) 方法名(形參列表){
	程式碼塊;
}

​ 一個方法在呼叫時通常需要指明:隱式引數、方法名、顯式引數(形參列表)。其中隱式引數是這個方法所在的物件或者是類,在Java中,方法存在於類中,如果這個類不是靜態方法,那麼在這個類有一個例項之前,這個方法是不存在於記憶體中的,首先要對類進行例項化獲得一個物件,然後通過這個物件呼叫這個方法。如果這個方法是靜態方法,那麼這個方法就會獲取一塊在方法區中的記憶體,這個方法就不會屬於任何類的物件而是屬於這個類,這是需要通過這個類的路徑來呼叫方法。呼叫方式為:

類名 變數名 = new 類名();
變數名.方法名(形參列表);

類名.方法名(形參列表);

​ 通常來說,我們不能直接呼叫一個方法,因為我們需要通過路徑找到方法在記憶體上的地址,如果想使用一個類中的方法,這個類的路徑必須是在當前程式目錄中能夠被找到,也就是說必須將這個類的路徑匯入到當前程式中,在Java中我們使用import的關鍵字進行匯入路徑。在一個類的內部,我們可以不用隱式引數來呼叫這個類的所有方法,因為在類的內部,所有方法的地址都是已知的,不需要任何路徑就可以找到任一方法,就像在同一個目錄下,我們無需加入任何字首路徑就可以訪問當前目錄下的所有檔案。但是為了進行更好的區分,或者刻意體現隱式引數這一部分,很多人喜歡使用this關鍵詞來在類的內部呼叫一個類自己的方法,如:

class Cat{
	fields;
	private method1(){
		...
	}
	public method2(){
		this.method1();
	}
}

​ 這實際上和下面的程式碼無甚區別:

class Cat{
	fields;
	private method1(){
		...
	}
	public method2(){
		method1();
	}
}

​ 只不過看起來更加清晰明瞭,我建議這樣寫,因為這樣程式碼的可讀性更高。this實際上是表示當前物件的意思,隱式引數為this,實際上指的是使用自身物件,呼叫一個方法。

​ 類的構造器以及普通方法都可以進行方法的過載,當方法名相同,但是整體的方法簽名不同時,這兩個方法仍然可以被認定為是不同的方法,這樣兩個同名方法就可以同時存在並且被正常呼叫,這個過程是過載。方法簽名在之前有過詳細的講解,由方法名和形參列表構成,它們的組合才是區分不同方法的標識。

​ 過載和覆寫不同,覆寫是子類中擁有父類的同名方法時,這個新寫的方法會覆蓋掉從父類中繼承來的同名方法,兩個概念是截然不同的,這裡需要注意。

2.關於自定義類的重要知識

1.final修飾符

​ 關於final修飾符之前重點提過多次,這裡再重複一次:被final修飾的類不可以被繼承;被final修飾的方法不可被重寫;final修飾的常量不可以被第二次賦值;final防止指令重新排序,保證多執行緒下的安全。這個知識點是面試重點,一定要記住!

​ final修飾符和static修飾符不是一個概念的修飾符,二者相互使用不相互衝突。當我們對一個變數使用final修飾符時,受到限制的是這個變數的控制代碼,這個變數的控制代碼的值變得不可被修改,因此一個基本型別的變數被設定上final修飾符之後,它在被第一次賦值之後就真的不能改變了,而引用型別的變數的值實際上不是它的真實值,而是真實值的地址,因此當我們為一個引用型別變數加上final修飾符之後,這個引用型別變數的指向就不能改變了,但是它的值可以被改變,我們仍然可以在原地址上進行值的修改,因此使用final修飾一個引用型別變數意義並不是特別大,當然對於字串這種不可在原地址上修改的變數型別,使用final修飾仍然和final修飾基本型別一樣奏效

​ 當final修飾一個屬性的時候,它的真正含義為:在這個屬性被分配記憶體空間的時候,必須被初始化並且不可變。關於初始化,實際上就是第一次賦值的這個過程,儘管每個變數被宣告出來的時候都有一個預設值0,但這個值被系統認定為除防止空指標異常之外沒有更多的意義,簡而言之就是這個值僅僅是為了安全性,對於問題的解決沒有一點意義,因此Java虛擬機器系統不認為這是初始化,必須是人為的賦值才是初始化。因此對於一個屬性來說,當它被final修飾的時候,在被分配記憶體空間的時候必須進行初始化。因此對於一個類我們可以這樣寫:

public class Cat{
    public final int a = 1;
    public Test(){
    }
}

​ 在宣告時就進行初始化。也可以像下邊這樣:

public class Cat{
    public final int a;
    public Test(){
        a = 1;
    }
}

​ 在構造器中初始化,同時需要注意的是,當我們有多個構造器的時候,必須保證每個構造器中都要對a變數賦值,這樣才能保證a在被分配記憶體的時候一定會進行初始化。

​ 既然知道了final這個特性,那麼結合static的特性,我們可以得出一個結論:靜態常量在被宣告的時候就必須被初始化。這是因為static的特性,被static修飾的屬性在類載入的時候,就會被分配給記憶體,因此這種靜態常量屬性在例項化之前,或者說整個類被加入到方法區的時候,在它進入系統的最開始的開始的那一刻,裡邊的一個靜態常量屬性就已經被分配給記憶體了,因此在這個時候它就需要進行初始化了,因此我們必須在宣告它的地方就給它賦值,否則就會直接違背final的規則,因為當系統檢測到類程式碼中它被宣告的那個地方時,它的記憶體就已經要被建立了,這時如果不為它初始化,就會違反final對於屬性的限制規範。

​ final對於普通的變數沒有這樣的約束,當使用final識別符號來修飾普通變數時,這個變數只要在當前的方法塊中被初始化一次就行,至於在哪裡進行初始化沒有關係,但是不能在整個塊中都不初始化,否則也會報錯。

2.工廠模式

​ 工廠模式是設計模式的一種,在大學期間的Java課中會講到工廠模式,但實際上這種模式是一種非常複雜的設計模式,沒有大量的程式碼經驗是很難真正理解的,因此初學的我在此不進行深入學習,先了解即可。常見的設計模式有23中,目前先記住這些即可。

3.程式碼塊的執行

​ 在類中可以加入程式碼塊,也就是類似於方法中的邏輯程式碼。程式碼塊在類載入的時候就被被執行,因此當我們在一個類中寫了程式碼塊的時候,這個類被載入到記憶體的方法區上的時候,也就是這個類第一次進入系統的時候,就會被執行。實際上類中的域的宣告就屬於一種程式碼塊,域的宣告也會在類載入的時候進行,只不過普通的域不會在這個時候被分配記憶體,只有靜態域才會被分配記憶體。當然程式碼塊和域有著本質上的區別,但是他們都會在類載入的時候被執行,關於類載入的時候,類中程式碼的執行順序在之後會進行詳細解析。

4.跨包呼叫

​ 包在Java中實際上就是資料夾,Java類存在包中,一個包內的類可以進行相互訪問,而不同包內的類難以進行相互訪問,那麼如何進行跨包訪問呢?實際上我們只要將一個類的路徑匯入到一個程式中,這個程式就可以通過路徑直接訪問這個類,我們如果將包路徑匯入進一個程式,那麼這個程式就可以直接訪問這個包中型別為public的類了。這個過程實際上就是使用import操作進行導包的過程。

​ 需要注意的是隻有類宣告前加了public的類也就是主類才能挎包呼叫,我們都知道在一個Java原始檔中只能存在一個主類,其他的類是不能跨包使用的,只能供包內使用。

5.靜態方法

​ 靜態方法不是屬於某一個物件的,而是屬於所有物件的,或者說是屬於這個類的,靜態方法也會在類載入的時候獲得記憶體,從Java執行時上來看,靜態方法會在類載入的時候在方法區的靜態資源區獲得一片記憶體空間並變得可以被呼叫,呼叫靜態方法直接使用類名進行呼叫。

6.關於Java的傳參方式

​ 這個問題之前已經進行了詳細的解釋說明,Java中僅存在值傳遞,不存在任何引用傳遞,詳情點選此處檢視