[JAVA] 只知對象屬性,不知類屬性?就算類答應,static都不答應

由淺入深——Java 類、對象、static成員

對象

在面向對象的思想中,一切事物均可以認爲是對象——萬物皆對象,把對象定義成包含狀態和行爲的一個實體,存在於現實世界中而且能夠與其餘實體區分開來的。對象具備狀態和行爲;好比:想一想你心儀的小姐姐,能夠把這個小姐姐看做是一個對象,那麼該對象有兩方面的定義:狀態和行爲;狀態,如身高,年齡,三圍,頭髮(長髮或者短髮)等;行爲,如調戲你、跳舞,玩手機等。java

經過多個相同類型的對象的狀態和行爲分析,能夠把對象抽象成類(class);咱們把具備相同特性(狀態)和行爲(功能)的對象的抽象定義類,對象的抽象是類,類實例化後即是對象,類的實例是對象,類其實就是對象的數據類型,但其和基本數據類型的差別在於類是程序員爲了解決某些問題而自定義的,基本數據類型是計算機中的數據存儲單元。程序員

Java 對象

在Java中,對象的狀態,用成員變量來描述;對象的行爲,用方法來描述;故Java中類能夠這樣定義,語法以下:設計模式

Java 類的語法

類定義示例代碼:數組

類定義示例代碼

定義Java 類時有一些必要的規範須要遵照:jvm

  1. 類名一概使用英文或者國際通用的拼音符號,作到見名知義,如taobao,weixin,雖然是拼音,但倒是國際通用的,可使用;
  2. 若是類使用了public修飾符,必須保證當前java文件名稱和當前類名相同,並且在一個java文件中,只能有一個public修飾的類(class);
  3. 類名首字母大寫,若是類名是多個單詞組成的,使用駝峯命名法,如: OperatingSystem(操做系統);

對象比較操做

先考慮下面的代碼:函數

對象比較示例代碼

上述示例代碼運行結果爲:工具

對象比較結果

爲何會出現這樣的結果呢?都是一樣的值,爲何會有不一樣的比較結果?那是由於==!=這兩個比較運算符在比較基本數據類型和對象對象類型時是由區別的性能

  • 對於基本數據類型來講,比較的是值,也就是變量存儲的數據內容;
  • 對於引用數據類型來講,比較的是對象的引用,也就是其在堆內存中的地址值,每次使用new關鍵字建立對象,都會在堆中新開闢一塊內存空間存儲新建立的對象, 而且會爲該內存空間生成一個惟一的地址,故內存空間不一樣,內存空間的地址值也就不一樣。

那麼哪些數據類型時基本數據類型,哪些是引用數據類型呢?spa

  • 基本數據類型:byte、short、char、int、long、float、double、boolean
  • 引用數據類型:除基本數據類型之外的全部數據類型都是引用數據類型,包括String和基本數據類型的封裝類型;

因此,若是要對對象的值作比較,就必需要是用對象的equals()方法了;這裏須要注意,equals()方法並不適用於基本數據類型,對於基本數據類型的變量來講,使用== 和 !=足夠了。下面用一個例子來實踐,代碼以下:操作系統

"equals()方法 案例"

上述案例輸出結果爲:

"equals()方法案例 運行結果"

由此可看出,使用對象的equals()方法是能正確比較對象的值的,由於Integer已經自定義了equals方法了,下面是源碼:

"Integer equals方法源碼"

不難發現,Integer的equals()方法的底層是使用基本數據類型的值作==比較的。若是是咱們自定義的類,並且沒有從新定義equals()方法呢,結果又會是怎樣的,一塊兒來看看:

"沒有從新定義equals()方法的案例"

輸出結果爲:false。

由於在Java中,有一個全部引用類型都直接或者間接繼承的父類,Object;所以,也能夠說在java中,全部類都是Object的子類,那麼,若是咱們沒從新實現equals()方法,會默認調用Object的equals()方法,Object的equals()方法比較的是對象的引用,因此結果輸出爲false。

因此想要使用自定義對象的equals方法比較對象的值,那麼就必須從新實現equals方法。

對象的打印操做

默認狀況下,Java對象打印的效果是:類的名稱@十六進制的hashCode,好比:

"Java對象打印的案例"

輸出爲:com.strlite.admin.demo.Value@79b4d0f,com.strlite.admin.demo.Value是類的名稱,79b4d0f是一個十六進制的數,是對象在堆中的內存地址。

重寫toString() 方法

能夠經過重寫toString() 方法來改變對象的打印效果:

"重寫toString方法的案例"

輸出爲:

  • i = 13

對象的生命週期

對象的開始:每次使用new關鍵字建立對象,就會在內存中開闢新的空間存儲對象信息,此時對象開始存在。

對象的結束:當堆中的對象,沒有被任何變量所引用,此時該對象就成了垃圾,等待垃圾回收器(GC)來回收;當對象被回收後,對象被銷燬,對象佔用的內存空間被釋放,對象的生命週期結束。

匿名對象

對象建立以後沒有將其賦給某一個變量。匿名對象只是在堆中開闢一塊新的內存空間,可是沒有把該空間地址賦給任何變量。由於沒有變量引用指向,因此匿名對象僅僅只能使用一次,通常會把匿名對象做爲方法的參數傳遞。

  • new Integer(); // 建立的就是匿名對象

構造器

  • Integer i = new Integer();

在建立對象時使用的特殊方法,出現new 關鍵字以後的方法,稱之爲構造方法、構造器、構造函數(Constructor)

構造器的做用

  1. 用於建立對象,可是必須和 new 一塊兒使用;好比:new Integer(13);
  2. 完成對象的初始化操做,能夠建立帶參數的構造器,爲成員變量賦初始值;

構造器的特色

  1. 構造器的名稱和當前所在類的名稱相同;
  2. 構造器是一個特殊的方法,其沒有定義返回類型,全部沒必要使用void做爲返回類型。 假設須要寫返回類型,也應該這樣寫:Integer Integer(); 但沒有這樣的必要;
  3. 在構造器中,不須要使用return語句,其實構造器是有返回值的,會默認返回當前建立對象的引用。

若是類中沒有構造器,編譯器會自動建立一個默認的無參構造器

"沒有構造器的案例"

咱們將上述代碼通過編譯,獲得字節碼文件,再將字節碼文件反編譯,反編譯的結果以下:

"默認構造器案例的反編譯效果"

經過反編譯後的結果,不難發現,即使咱們沒有建立構造器,編譯器也會爲咱們建立一個默認的,編譯器建立的默認構造器有如下的特色:

  • 符合構造器特色;
  • 無參數的;
  • 無方法體;
  • 若是類沒有使用public修飾, 則編譯器問起建立的構造器也沒有public修飾;使用了public修飾,則編譯器建立的構造器也使用public修飾;

"默認構造器"

若是類中沒有構造器,編譯器會自動建立一個默認的無參構造器。可是,若是咱們顯式地定義了一個構造器,則編譯器再也不建立默認構造器。案例以下所示:

"編譯器再也不建立默認構造器"

經過上述對比,不難發現,當類中存在一個構造器時,編譯器便不會建立默認的構造器,而是使用咱們定義的構造器,由此可得出:在一個類中,至少存在一個構造器

static 修飾符

假如每一個人都有name和age兩個狀態,可是不一樣人的name和age是不同的;也就說name和age是屬於對象的。可是在生活中有些東西並非單單屬於某一個對象的,而是屬於整個類的,好比:每一個人都會老去、都會死。

因此,狀態和行爲的所屬也應該有對象和類之分。 有的狀態和行爲應該屬於對象,不一樣的對象,狀態和行爲能夠不同;而有的狀態和行爲應該屬於類,不屬於對象。爲了區別與對象的狀態和行爲,引入static修飾符來修飾類的狀態和行爲。

static修飾符表示靜態的,可修飾字段、方法、內部類,其修飾的成員屬於類,static修飾的資源屬於類級別,區別於對象級別。static的真正做用是用來區別字段、方法、內部類、初始化代碼塊是屬於對象仍是屬於類自己。

static修飾符的特色

  1. static修飾的成員(字段/方法),隨着所在類的加載而加載,當JVM把字節碼加載進JVM的時候,static修飾的成員已經在內存中存在了。
  2. 優先於對象的存在,對象是咱們手動經過new關鍵字建立出來的,static成員是JVM建立的;
  3. satic修飾的成員被該類型的全部對象所共享,該類建立的任何對象均可以訪問該類的static成員;
  4. 直接使用類名訪問static成員由於static修飾的成員直接屬於類,不屬於對象,因此能夠直接使用類名訪問static成員.

下面咱們經過一個案例來實踐static關鍵字的使用:

"static關鍵字的使用案例"

static修改的變量稱爲常量,會長時間存在於JVM內存中,因此JVM也會爲它分配必定的存儲空間,如下即是static常量在jvm 中的內存模型:

"static常量在jvm 中的內存模型"

JVM會將靜態變量存儲在方法區中,以便於及時調用;並保證其可以長時間存儲於JVM中。

類成員和實例成員的訪問

類中的成員:字段,方法,內部類。

  • 類成員:使用static修飾的成員,直接屬於類,經過類名.static成員來訪問;
  • 實例成員:沒有使用static修飾的成員,實例成員只屬於對象, 經過對象來訪問非static字段和非static方法;

通常狀況下,類成員只能訪問類成員,實例成員只能訪問實例成員;但深究發現,對象其實能夠訪問類成員,可是底層依然使用類名訪問的。

static方法

static方法中,只能調用static成員;非static方法,能夠訪問靜態成員,也能夠訪問實例成員;

那何時定義成static的字段和方法:

  • 若是這個一個狀態/行爲屬於整個事物(類),被全部對象所共享,就直接使用static修飾;
  • 在開發中,每每把工具方法使用static修飾,好比:數組中經常使用的java.util.Arrays中的方法;

若是不使用static修飾,則這些方法屬於該類的對象,咱們得先建立對象才能調用方法,在開發中工具對象只須要一份便可,可能建立N個對象,此時能夠考慮使用單例設計模式。

"成員生命週期"

類成員的使用

好處:對對象的共享數據進行單獨空間的存儲,節省空間,沒有必要每個對象中都存儲一份,能夠直接被類名調用。

弊端:生命週期過長。

局部變量初始化

局部變量定義後,必須顯式初始化後才能使用,由於JVM不會爲局部變量執行初始化操做。這就意味着,定義局部變量後,JVM並未爲這個變量分配內存空間。直到程序爲這個變量賦值時,系統纔會爲局部變量分配內存,並將初始值保存到該內存中。

局部變量不屬於任何類或實例,所以它是保存在其所在方法的棧幀內存中。

  • 基本數據局部變量:基本數據類型變量的值會直接保存到該變量所對應的內存中。
  • 引用數據局部變量:變量內存中存的是堆中對象的地址,經過該地址引用到該變量實際指向的堆裏的對象。

棧幀內存中的變量隨方法或代碼塊的運行結束而銷燬,無須JVM回收。

一點小建議

  • 開發中應該儘可能縮小變量的做用範圍,如此在內存中停留時間越短,性能也就更高。
  • 合理使用static修飾,通常只有定義工具方法的時候使用;
  • static方法須要訪問的變量,只有該變量確實屬於類,才使用static修飾字段;
  • 儘可能使用局部變量;

完結。老夫雖不正經,但老夫一身的才華

相關文章
相關標籤/搜索