1. 程式人生 > >《Java深入解析-滲透java本質的36個話題》總結

《Java深入解析-滲透java本質的36個話題》總結

浮點型別的種種懸疑

浮點型別只是近似的儲存,這很顯而易見,因為在計算機內部是使用二進位制來儲存數值的,而對於0.3333之類的奇數小數就沒辦法儲存,因此採用的是一種近似值的方式進行儲存的,所以就會造成一個誤差:下面程式碼將輸出false,因為對於浮點數運算會存在誤差:

double x = 0.1, y = 0.2, z = 0.3;
System.out.println(x+y==z);
//真正的0.1+0.2等於0.3000000004左右,後面會攜帶偏差值
System.out.println(x+y);
++i和i++解析(目前我看到的最好理解的講解方式)

首先,我們知道i++和i++的一個明顯的區別就是:前者先i先進行其他運算操作再自增,後者i先自增再進行其他運算操作。那麼如果對於下面這種情況呢?

int i = 1;
int y = 2;
i = i++;
y = ++y;
System.out.println(i);
System.out.println(j);

首先說一下他們的結果分別為:i=1,j=3; 我們可以將它改變成一個容易看懂的操作:

int i = l;
int y = i++;

那麼這個我們就能拆分成下面這個:

int i = 1;
int temp = i;
i++;
int y = temp;

最終y的值就是i,那麼i也同樣是這樣:

int i = 1;
int temp = i;
i++;
i = temp;

因此i最終還是為i,我們再來看y:

int y = 2;
y += 1;
y = y;

因此最終y自增了,可以看出,i++和++j的區別就在於前者建立了一個臨時的變數用於賦值操作,而其實兩者的自增操作都是發生再進行運算操作之前。

i+++j的運算結果(貪心規則)

按照編譯器的貪心規則來說,i最終匹配到自增運算,最終的結果為:

i++;
i+j;

貪心機制:編譯器再運算分析符號的時候,會盡可能多的結合有效的符號。我們知道,在進行轉義字元的時候,比如\431它為什麼不匹配\43,就是因為編譯器的貪心規則,不然就會永遠都無法正確的轉義\431

三元運算子的結果型別

三元運算子第一個為boolean型別,但是後面兩個表示式的型別可能不同,那麼當他們型別不同的時候,該運算子到底返回誰的型別呢?他們的具體判斷順序如下:

1.如果型別相同,則返回表示式1的型別
2.如果一個為基本型別,另一個為對應的包裝型別,則返回基本型別
3.如果一個為null,一個為引用型別,則返回引用型別
4.如果一個為null,一個為基本型別,則返回基本型別對應的包裝型別
+號連線字串的效能

在進行+來連線字串的時候,它會先建立一個臨時的StringBuilder物件,進行String物件連線之後,在呼叫toString方法轉化成String物件。

當使用+號連線的兩邊的字串為編譯時期就能確定的常量時,編譯器不會建立臨時StringBuilder物件,而會在編譯期就計算出該字串的值,不會造成效能開銷

String型別常量都是儲存在常量池裡面的,我們也可以呼叫字串的intern方法手動的將字串儲存到常量池中。這樣,當String編譯時期常量的值出現在常量池中 的時候,會直接返回常量池中的物件。intern方法返回值:如果常量池中有該物件,則返回常量池中 的該物件,否則返回自身

對於執行時建立的物件,會被分配到堆中,系統不會自動呼叫intern方法加入到常量池

特殊的main方法

我們通常都是通過run類來呼叫main方法,其實main方法也可以在其他方法中被呼叫:

public class Main_{
 public static int i = 0;
 public static void main(String[] args){
    if(i==0){
        main2();
        System.out.println(0);
    }
    System.out.println(1);
 }
 public static void main2(){
  i = 1;
  main();
 }
}

同樣,main方法也是可以繼承的,如果我們在Main2類中繼承Main類,那麼也可以直接啟動Main2;

我們知道,靜態方法是不能被重寫的,那麼是不是每次啟動Main2類都只能呼叫Main的main方法呢?答案肯定不是的,靜態方法雖然不能被重寫,但是可以被隱藏,也就是說,我們在Main2類中在定義main方法,那麼啟動Main2將是呼叫自身定義的main方法,而父類的main方法將被隱藏

過載時的方法選擇

方法過載時,類呼叫的方法在編譯時期就已經確定了,如下程式碼:

public class Main{
 public static void a(String s){
  System.out.println("a1");
 }
 public static void a(Object o){
  System.out.println("a2");
 }
 public static void main(String[] args){
  Object o = new String();
  a(o);
 }
}

最終呼叫的方法為a(Object o);呼叫過載方法時,過載方法的引數型別是根據引用的靜態型別來決定的,o的靜態引用型別為Object,那麼就呼叫a(Object o);

構造器

構造器其實並沒有建立物件,它只是負責物件的一系列屬性的初始化操作

當我們沒有定義構造器時,編譯器會自動生成一個空的預設無參構造器,這個構造器並不是一個空的方法,至少它呼叫了父類構造器進行父類初始化

this在哪裡

我們知道,當我們呼叫一個例項方法的時候,我們可以直接使用一個this物件,那麼該this物件是哪來的呢?其實,在構造器或者例項方法中,都含有一個隱藏的引數,該引數就是類的物件,當呼叫構造器或者例項方法時,就會將這個物件作為該引數傳進去,比如:

obj.test();
//實質上裡面包含一個隱含引數this,該this指向obj
obj.test(this);
Integer包裝型別的快取

Integer內部維護者一個快取陣列,預設的範圍是-128到127,當數值在這個範圍裡面時,會從快取裡面取物件,否則就會新建一個物件。其中-128快取下限是固定不能變得,而127上限可以通過修改系統屬性來改變:

java -Djava.lang.Integer.IntegerCache.high=200 Main

使用上述語句來啟動Main就會將Integer得內部快取上限更改至200

java的多維陣列

java的多維陣列其實是採用陣列的陣列的形式實現的,也就是說高維陣列的每一個元素都是一個低維陣列