1. 程式人生 > >Java language specification 9 筆記

Java language specification 9 筆記

Java程式語言是強型別的,靜態型別的語言; 每個變數或表示式都要在編譯時有一個已知的型別。型別限制了變數所能代表的值,限制了表示式所能產生的值,限制了值所能支援的操作,也決定了操作的意義。強型別,靜態型別有助於在編譯時幫忙檢測錯誤。

Compile time:將程式轉化為獨立於機器的二進位制碼。 Rum time:包括載入、連結程式執行需要的類,生成機器語言(可選),程式的動態優化以及執行程式。

原始型別和引用型別,以及特殊的"null" type 原始型別在所有的機器和實現上都是一樣的;包括boolean型別,數字型別:整形(byte(8-bit)、short(16-bit)、int(32-bit)、long(64-bit))、浮點型(float, double)、和Unicode char(16-bit)型別。 byte、short、int、long是帶符號整數。 char是不帶符號的整數,代表UTF-16 字元。

引用型別包括: 類型別、介面型別和陣列型別。引用型別指向類/陣列的例項物件,一個物件可以有多個引用。

特殊的"null" type:沒有名字,所以沒有辦法宣告變數型別或者強制轉換,不過"null reference"可以被轉換成其他型別; 實際程式設計時,可以把null當成特殊的字面量。

很多情況下,裝箱和拆箱是由編譯器自動完成的。 Variables are typed storage locations:變數是型別化的儲存位置。 列舉類,可以有自己的方法。 Java語言支援陣列的陣列而不是多維陣列,這兩者有什麼區別嗎?

有三種類型的exception:

  1. checked exceptions(大部分user-defined exception都是這種型別)
  2. run-time exceptions,Java虛擬機器偵測到的程式裡的Invalid operation會導致這種exception,例如NullPointerException。
  3. errors,Java虛擬機器偵測到的failures會導致errors,例如OutOfMemoryError。

Java語言不會自動初始化區域性變數,以免掩蓋程式設計錯誤。

字面量就是一個值,這個值可能是原始型別的,也可能是String型別或者null 型別的。 需要注意的一點是對於整型和浮點型字面量允許使用"_"來分割數字。

EscapeSequence(轉義字元): \ b (backspace BS, Unicode \u0008 ) \ t (horizontal tab HT, Unicode \u0009 ) \ n (linefeed LF, Unicode \u000a ) \ f (form feed FF, Unicode \u000c ) \ r (carriage return CR, Unicode \u000d ) \ " (double quote " , Unicode \u0022 ) \ ’ (single quote ’ , Unicode \u0027 ) \ \ (backslash \ , Unicode \u005c )

String 物件擁有常量值; String literal(String 字面量,如“java”)是String例項的引用,相同的String literal是同一個String例項的引用; 因為String literals 或者更廣泛的來說,字串型別的常量表達式是"interned"(關押), 所以使用String.intern方法可以共用同一個String例項。

字串型別的常量表達式: “The integer " + Long.MAX_VALUE + " is mighty big.” 這樣的表示式會在編譯時計算,所以會被當成String literal。

package testPackage;

class Test {
public static void main(String[] args) {
String hello = "Hello", lo = "lo";
System.out.print((hello == "Hello") + " ");
System.out.print((Other.hello == hello) + " ");
System.out.print((other.Other.hello == hello) + " ");
System.out.print((hello == ("Hel"+"lo")) + " ");
System.out.print((hello == ("Hel"+lo)) + " ");
System.out.println(hello == ("Hel"+lo).intern());同包同類內的String literal是同一個String例項的引用
}
}
class Other { static String hello = "Hello"; }
and the compilation unit:
package other;
public class Other { public static String hello = "Hello"; }

produces the output:
true true true true false true

This example illustrates six points:

• Literal strings within the same class (§8 (Classes)) in the same package (§7 (Packages and Modules)) represent references to the same String object (§4.3.1). 同包同類內的String literal是同一個String例項的引用 • Literal strings within different classes in the same package represent references to the same String object. 同包不同類內的String literal是同一個String例項的引用 • Literal strings within different classes in different packages likewise represent references to the same String object. 不同包不同類內的String literal是同一個String例項的引用 • Strings computed by constant expressions (§15.28) are computed at compile time and then treated as if they were literals. 字串型別的常量表達式會在編譯時計算,所以會被當成String literal • Strings computed by concatenation at run time are newly created and therefore distinct. 字元連線的計算是在run time進行的,所以對應的String是新建立的,所以會不同。 • The result of explicitly interning a computed string is the same string as any pre-existing literal string with the same contents. 對一個計算得來的string使用intern得到的引用會和已存在的string literal(相同內容)指向同一個String例項物件。

變數的型別: 類變數,例項變數和陣列成員變數在被建立的時候會初始化為預設值。

  1. 類變數 在類的field域中被static修飾的變數,或者在介面宣告的變數(有或者沒有static都一樣)。

  2. 例項變數 在類的field域中沒有被static修飾的變數

  3. 陣列成員變數(Array components) 陣列成員是沒有名字的變數,當陣列建立的時候就別建立並初始化到了預設值。

  4. 方法引數變數 方法引數變數在方法呼叫的時候會被建立,並使用引數值初始化。

  5. 建構函式引數變數 當例項建立或者建構函式被呼叫的時候引數變數會被建立,並使用引數值初始化。

  6. Lambda 引數變數 當Lambda表示式實現的方法被呼叫時,引數變數會被建立,並使用方法呼叫時的引數值初始化。

  7. 異常引數變數 當catch語句捕捉到異常時異常引數變數會被建立,新變數會使用和exception相關的物件初始化。

  8. 區域性變數 當程式控制流進入一個語句塊(block)或者for迴圈的時候,相應的區域性變數便會被建立。 區域性變數初始化可能使用了表示式,只有當表示式執行了,變數才會被初始化。

compile-time type & run-time type run-time type:在Java 虛擬機器裡,每個物件都屬於一些特殊的類,這類叫做"class of object“; 物件是這個類或者這個類所有父類的例項。陣列物件也有對應的類。

變數的compile-time type是聲明瞭的,表示式的compile-time type在編譯時可以推斷出來。

compile-time type和run-time type不一定是對應的,因為: 1.在run-time,classed和interfaces是JVM使用class loader載入的,所以同一個類使用不同的class loader載入後,在run-time中也是不一樣的。

  1. 型別變數和型別引數在run-time是不可具體化的,所以run-time中的類或者介面可能代表了compile-time中的多個引數化的型別; 特別是泛型的compile-time引數化使用的是同一個run-time 型別。如: Vector x = new Vector(); Vector y = new Vector(); x.getClass() 和 y.getClass()是相同的。

轉換的型別:

class Test {
public static void main(String[] args) {
// 1.Casting conversion (5.4) of a float literal to
// type int. Without the cast operator, this would
// be a compile-time error, because this is a
// narrowing conversion (5.1.3):
int i = (int)12.5f;

// 2.String conversion (5.4) of i's int value:
System.out.println("(int)12.5f==" + i);

// 3.Assignment conversion (5.2) of i's value to type
// float. This is a widening conversion (5.1.2):
float f = i;

// 4.String conversion of f's float value:
System.out.println("after float widening: " + f);

// 5.Numeric promotion (5.6) of i's value to type
// float. This is a binary numeric promotion.
// After promotion, the operation is float*float:
System.out.print(f);
f = f * i;

// 6.Two string conversions of i and f:
System.out.println("*" + i + "==" + f);

// 7.Invocation conversion (5.3) of f's value
// to type double, needed because the method Math.sin
// accepts only a double argument:
double d = Math.sin(f);

// 8.Two string conversions of f and d:
System.out.println("Math.sin(" + f + ")==" + d);
}
}

Widening Primitive Conversion: 可能會丟失精度,但是不會發生run-time exception • byte to short , int , long , float , or double • short to int , long , float , or double • char to int , long , float , or double • int to long , float , or double • long to float or double • float to double

Narrowing Primitive Conversion: 可能會丟失資料,高位可能會丟失; 但是也不會發生run-time exception • short to byte or char • char to byte or short • int to byte , short , or char • long to byte , short , char , or int • float to byte , short , char , int , or long • double to byte , short , char , int , long , or float

byte to char的過程:byte會先轉換成int,然後再轉換成char。

裝箱轉換: 裝箱轉換可能會導致OutOfMemoryError, 因為包裝類物件可能沒有充足的空間。 • From type boolean to type Boolean • From type byte to type Byte • From type short to type Short • From type char to type Character • From type int to type Integer • From type long to type Long • From type float to type Float • From type double to type Double • From the null type to the null type

String conversion: 基本資料型別在轉換為string 型別時,先轉換為對應的包裝類型別,再呼叫toString方法(如果是null,會直接會直接使用null 字串)。對於值為x的基本資料型別T,如果 T 是 boolean型別, 那麼使用new Boolean(x)。

6.1已經讀完,主要講了命名的規範。

shadowing:名字相同的變數,scope重疊的情況。

8.Classes

Top level class和nested class: Nested class包括成員類,區域性類和匿名類。

Inner class non-static nested class是Inner class。 介面內的nested class預設是static的,所以不是一個inner class。 inner class不能有static初始化塊,不能定義static成員(常量除外),但是可以繼承static成員。 inner class例項中有一個外部類的例項(enclosing instance) 如果inner class訪問了外部類的例項變數,那麼在這個過程中外部類的例項物件會被用到。

靜態內部類訪問不了外部類的例項變數。 抽象類可以實現介面類,而不實現具體方法。 阻止類例項化的方法就是隻宣告一個private無參的構造方法,並且不呼叫它; 這種類一般是有類方法或者類變數。

父類的private成員是不會被子類繼承的。 父類中只有宣告為protected和public的成員才可以被非同package的子類繼承。 建構函式,靜態初始化塊和例項初始化塊都不屬於成員,所以也就不會被子類繼承。

一個非public的類,無法在其他package的直接訪問,但是可以通過間接的方法訪問(通過它的public的父類或者介面)。

子類中特定名字的field會hide父類中相同名字的field, 無論型別是否相同,無論static還是non-static。

transient 修飾的變數,會變化頻繁, 不會被存放在永久性的記憶體區域。

volatile 修飾的變數用於多執行緒訪問的情景,保證每個執行緒訪問到的都是最新的值。

static常量會比其他static域先初始化。(Why?)

列舉類中可以宣告abstract方法。

在run-time,machine-code生成器或者優化器可以將final方法內聯(inline)。

native方法是用平臺相關的code實現的,比如C。

synchronized方法在執行之前需要一個monitor,對於class 方法,這個monitor是這個class相關的Class 物件; 對於例項方法,這個monitor是物件例項(this)。

子類不能從superinterface中繼承private和static方法。

static方法不能被overriden,但是可以被hiden。

static方法不能hiden例項方法,但static field可以hiden例項field。 可以通過super. 或者強制型別轉換來訪問父類中被hiden的方法,但是不能通過將子類強制型別轉換為父類型別來訪問父類中被override的方法。

如果呼叫的是例項方法,那麼在run-time才會確定真正呼叫的方法(dynamic method lookup)。

一個非私有的(non-private)內部類,可以被編譯它的編譯器訪問,也可以被其他編譯器訪問,所以二進位制名字是有規範的(predictable); 但是區域性內部類或者匿名內部類只能被編譯它的編譯器訪問。