2、萬物皆對象


本章你將瞭解到 Java 程序的基本組成,學習在 Java 中萬物(幾乎)皆對象的思想。

1、對象操縱

Java 利用萬物皆對象的思想和單一一致的語法方式來簡化問題。雖萬物皆可爲對象,但我們所操縱的標識符實際上只是對對象的“引用” 。僅僅有一個“引用”並不意味着你必然有一個與之關聯的“對象”。

創建一個 String 引用

    String s;

這裏我們只是創建了一個 String 對象的引用,而非對象。直接拿來使用會出現錯誤:因爲此時你並沒有給變量 s 賦值–指向任何對象。通常更安全的做法是:創建一個引用的同時進行初始化。

    String s = "asdf";

2、對象創建

“引用”用來關聯“對象”。
在 Java 中,通常我們使用new操作符來創建一個新對象。new 關鍵字代表:創建一個新的對象實例

    String s = new String("asdf");

3、數據存儲

程序在運行時是如何存儲?內存是怎麼分配?
有5個不同的地方可以存儲數據:

  1. 寄存器(Registers) 最快的存儲區域,位於 CPU 內部。寄存器的數量十分有限,所以寄存器根據需求進行分配。我們對其沒有直接的控制權,也無法在自己的程序裏找到寄存器存在的蹤跡。(另一方面,C/C++ 允許開發者向編譯器建議寄存器的分配)。
  2. 棧內存(Stack) 存在於常規內存 RAM(隨機訪問存儲器,Random Access Memory)區域中,可通過棧指針獲得處理器的直接支持。棧指針下移分配內存,上移釋放內存,這是一種快速有效的內存分配方法,速度僅次於寄存器。創建程序時,Java 系統必須準確地知道棧內保存的所有項的生命週期。這種約束限制了程序的靈活性。因此,雖然在棧內存上存在一些 Java 數據,特別是對象引用,但 Java 對象卻是保存在堆內存的。
  3. 堆內存(Heap) 這是一種通用的內存池(也在 RAM 區域),所有 Java 對象都存在於其中。與棧內存不同,編譯器不需要知道對象必須在堆內存上停留多長時間。因此,用堆內存保存數據更具靈活性。創建一個對象時,只需用 new 命令實例化對象即可,當執行代碼時,會自動在堆中進行內存分配。這種靈活性是有代價的:分配和清理堆內存要比棧內存需要更多的時間如果可以用 Java 在棧內存上創建對象,就像在 C++ 中那樣的話)。隨着時間的推移,Java 的堆內存分配機制現在已經非常快,因此這不是一個值得關心的問題了。
  4. 常量存儲(Constant storage) 常量值通常直接放在程序代碼中,因爲它們永遠不會改變。如需嚴格保護,可考慮將它們置於只讀存儲器 ROM (只讀存儲器,Read Only Memory)中。
  5. 非 RAM 存儲(Non-RAM storage) 數據完全存在於程序之外,在程序未運行以及脫離程序控制後依然存在。兩個主要的例子:(1)序列化對象:對象被轉換爲字節流,通常被髮送到另一臺機器;(2)持久化對象:對象被放置在磁盤上,即使程序終止,數據依然存在。這些存儲的方式都是將對象轉存於另一個介質中,並在需要時恢復成常規的、基於 RAM 的對象。Java 爲輕量級持久化提供了支持。而諸如 JDBC 和 Hibernate 這些類庫爲使用數據庫存儲和檢索對象信息提供了更復雜的支持。

4、基本類型存儲

它們的創建並不是通過 new 關鍵字來產生。
通常 new 出來的對象都是保存在堆內存中的,以此方式創建小而簡單的變量往往是不划算的。所以對於這些基本類型的創建方法,Java 使用了和 C/C++ 一樣的策略。也就是說,不是使用 new 創建變量,而是使用一個“自動”變量。 這個變量直接存儲"值",並置於棧內存中,因此更加高效。

Java 確定了每種基本類型的內存佔用大小。 這些大小不會像其他一些語言那樣隨着機器環境的變化而變化。這種不變性也是 Java 更具可移植性的一個原因。
在這裏插入圖片描述

  1. 都是有正/負符號的。
  2. 布爾(boolean)類型的大小沒有明確的規定,通常定義爲取字面值 “true” 或 “false” 。

如果你希望在堆內存裏表示基本類型的數據,就需要用到它們的包裝類。

char c = 'x';
Character ch = new Character(c);

自動裝箱

Character ch = new Character('x');

自動拆箱

char c = ch;

5、高精度數值

BigIntegerBigDecimal,用於高精度的計算。它們並沒有對應的基本類型。
個類包含的方法提供的操作,必須要通過調用它們的方法來實現而非運算符。

BigInteger 支持任意精度的整數。可用於精確表示任意大小的整數值,同時在運算過程中不會丟失精度。 BigDecimal 支持任意精度的定點數字。

6、數組存儲

在 Java 中,數組使用前需要被初始化,並且不能訪問數組長度以外的數據。
這種範圍檢查,是以每個數組上少量的內存開銷運行時檢查下標的額外時間爲代價的,但由此換來的安全性和效率的提高是值得的。

7、對象清理

一個變量需要存活多久?
如果我們想銷燬它,應該什麼時候去做呢?

7.1、作用域

在 C、 C++ 和 Java 中,作用域是由大括號 {} 的位置決定的。
Java 的變量只有在其作用域內纔可用。Java 是一種自由格式的語言,額外的空格、製表符和回車並不會影響程序的執行結果。
在 Java 中,你不能執行以下操作:

{
    int x = 12;
    {
        int x = 96; // Illegal
    }
}

Java 編譯器會提示變量 x 已經被定義過了。(C/C++中可以)。

7.2、對象作用域

Java 對象與基本類型具有不同的生命週期。
使用 new 關鍵字來創建 Java 對象時,它的生命週期將會超出作用域。

{
    String s = new String("a string");
} 
// 作用域終點

引用 s 在作用域終點就結束了。但是,引用 s 指向的字符串對象依然還在佔用內存。在這段代碼中,我們無法在這個作用域之後訪問這個對象,因爲唯一對它的引用 s 已超出了作用域的範圍。

我們在 Java 中並沒有主動清理這些對象,那麼它是如何避免 C++ 中出現的內存被填滿從而阻塞程序的問題呢?
答案是:Java 的垃圾收集器會檢查所有 new 出來的對象並判斷哪些不再可達,繼而釋放那些被佔用的內存,供其他新的對象使用。

8、類的創建

8.1、類型

大多數面向對象的語言都使用 class 關鍵字類來描述一種新的對象。 通常在 class 關鍵字的後面的緊跟類的的名稱。

8.2、字段

可以往類裏存放兩種類型的元素:方法(method)字段(field)
類的字段可以是基本類型,也可以是引用類型。
如果類的字段是對某個對象的引用,那麼必須要初始化該引用將其關聯到一個實際的對象上(通過之前介紹的創建對象的方法)。
通常,字段不在對象間共享。

必須通過這個對象的引用來指定字段值。

8.3、基本類型默認值

在這裏插入圖片描述這些默認值僅在 Java 初始化類的時候纔會被賦予。這種方式確保了基本類型的字段始終能被初始化(在 C++ 中不會),從而減少了 bug 的來源。爲了安全,我們最好始終顯式地初始化變量。

局部變量 —— 那些不屬於類的字段的變量,不會自動初始化。

8.4、方法使用

在 Java 中,我們使用術語 方法(method)來表示“做某事的方式”

 [返回類型] [方法名](/*參數列表*/){
     // 方法體
 }

8.4.1、返回類型

返回類型表明了當你調用它時會返回的結果類型。
參數列表則顯示了可被傳遞到方法內部的參數類型及名稱。
作爲方法的唯一標識。方法名和參數列表統稱爲方法簽名(signature of the method)
Java 中的方法只能作爲類的一部分創建。它只能被對象所調用 ,並且該對象必須有權限來執行調用。

8.4.2、參數列表

int storage(String s) {
    return s.length() * 2;
}

參數列表必須指定每個對象的類型和名稱。同樣,我們並沒有直接處理對象,而是在傳遞對象引用

return 關鍵字,它執行兩項操作。首先,它意味着“方法執行結束”。其次,如果方法有返回值,那麼該值就緊跟 return 語句之後。

如果我們不想方法返回數據,則可以通過給方法標識 void 來表明這是一個無需返回值的方法。當返回類型爲 void 時, return 關鍵字僅用於退出方法,因此在方法結束處的 return 可被省略。

9、程序編寫

9.1、命名可見性

如果你在兩個模塊中使用相同的命名,那麼如何區分這兩個名稱,並防止兩個名稱發生“衝突”呢?

  • C++ 將函數嵌套在類中,所以它們不會和嵌套在其他類中的函數名衝突。然而,C++ 還是允許全局數據和全局函數,因此仍有可能發生衝突。爲了解決這個問題,C++ 使用附加的關鍵字引入了命名空間。
  • Java 爲一個類庫生成一個明確的名稱。整個包名都是小寫。

9.2、使用其他組件

如果一個類位於其他文件中,又會怎樣呢?
要解決此問題,你必須通過使用 import 關鍵字來告訴 Java 編譯器具體要使用的類。import 指示編譯器導入一個包,也就是一個類庫。
在其他語言中,一個庫不僅包含類,還可能包括函數和數據,但請記住 Java 中的所有代碼都必須寫在類裏)。大多數時候,我們都在使用 Java 標準庫中的組件。有了這些構件,你就不必寫一長串的反轉域名。

9.3 static

當面臨下面兩種情況時:

  1. 有時你只想爲特定字段(注:也稱爲屬性、域)分配一個共享存儲空間,而不去考慮究竟要創建多少對象,甚至根本就不創建對象。
  2. 創建一個與此類的任何對象無關的方法。也就是說,即使沒有創建對象,也能調用該方法。
    使用類名直接引用靜態變量是首選方法,因爲它強調了變量的靜態屬性。

相比非靜態的對象,static 屬性改變了數據創建的方式。同樣,當 static 關鍵字修飾方法時,它允許我們無需創建對象就可以直接通過類的引用來調用該方法。

【參考】
On Java 8中文翻譯

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