1. 程式人生 > >透析java本質的36個話題-第一章基本概念筆記

透析java本質的36個話題-第一章基本概念筆記

記得大三時在圖書館看過這本書,當時一口氣就看完了,參加工作後又回過頭來再看,還是收益很多,我是先看的這本書,然後再看了深入理解jvm虛擬機器這本經典之作,必須反覆看。現在又回過頭來看 透析java本質的36個話題 這本書,全書一共5章,我謹以5篇博文紀念。

1、開門見山—測試你的java水平

 當時趕腳自己連java新手都不是,大哭o(╥﹏╥)o

上面的問題都會在我的這5篇博文中找到答案 

2、世外隱者—隱居深山的關鍵字 

 

2.1 、 goto與const

Java中取消了goto的使用,取而代之的是使用迴圈標籤 outer

需要注意的是continue 和break 只針對第一次迴圈有效 ,對於多次迴圈只能用迴圈標籤

下面是使用outer的例子  

public class TestOuter {
    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                System.out.println("i=" + i + ", j=" + j);
            }
        }
    }
}

 這是一個雙層迴圈

我們使用outer跳出第二層迴圈

public class TestOuter {
    public static void main(String[] args) {
        //在外層迴圈處新增outer標籤 outer標籤使用格式為      字母: break 字母
        //所一冒號前面只要是合法識別符號就好了
        outer:for (int i = 0; i < 2; i++) {
                    for (int j = 0; j < 2; j++) {
                        if (i==0&&j==1){
                            break outer;//如果只是break 它只跳出內層迴圈
                        }
                        System.out.println("i=" + i + ", j=" + j);
                    }
                }
            }
}

 

 

 2.2、true、false與null

 

執行報錯

 2.3、關鍵字列表

 

3、疑團滿腹—識別符號更深層次的的思考

3.1、識別符號定義規則

 

這個好多面試題的答案就是這個,嗯嗯嗯呃呃

 

 合法的識別符號的個數

public static void main(String[] args) {
    int    start=0;
    int    part=0;
    for(int i=0x0000;i<0x10ffff;i++) {
        if(Character.isJavaIdentifierStart(i)) {
            //判斷int資料對應的字元是否可以作為java識別符號的首字母
            start++;
        }
        if(Character.isJavaIdentifierPart(i)) {
            //判斷int資料對應的字元是否可以作為java識別符號的一部分
            part++;
        }
    }
    System.out.println("Unicode字符集個數"+(0x10ffff+1));//1114112
    System.out.println("可作為識別符號首字母個數"+start);//101296
    System.out.println("可作為識別符號一部分的個數"+part);//103584
    System.out.println("二者只差"+(part-start));//2288
}

 

 3.2、“$” 惹的禍

public class Test${
    public static void main(String[] args) {
        System.out.println("我的類名有 $ ");
    }
}
這個可以正常執行

 

再看一個 

public class Test$User{
    public static void main(String[] args) {
        System.out.println("我的類名為 Test$User ");
    }
}
class Test{
    class User{
        void print(){
            System.out.println("我是Test中內部類User類中的print方法 !");
        }
    }
}

這下報錯了

3.3、識別符號的最大長度 

這裡class中常量字串的儲存在 深入理解jvm虛擬機器 這本書裡有介紹 

 

4、鞭長莫及---我的字串你不能用 

 

4.1、轉義字元介紹

public class Test{
    public static void main(String[] args) {

        char c1    ='\u0027'; //等價於  char c11 ="'"; 單引號
        char c2    ='\u005c'; //等價於 char c22 ="/";  反斜槓
        String s ="\u0022";    //等價於 String ss ="""; 雙引號
        //上述三行Unicode轉義不正確,經過轉義以後,單引號和雙引號都沒有合理的匹配。
        //而\是轉義字元需要與其他字元結合使用
      //\u代表Unicode轉義
        char c3    ='\400';
        char c4    ='\28';
        //這兩行時八進位制轉義,自然只能出現0-7;
        //而且他的合理範圍是0-255
      //\400的十進位制是256.超過了這個範圍
    }
}

 

 

對於Unicode字符集及其編碼的瞭解可以看看這個

http://www.cnblogs.com/wangduo/p/6225538.html 

4.2、三種轉義的聯絡

public class Test{
    public static void main(String[] args) {
        //字元A的三種表現形式
        char c1    ='A';
        char c2    ='\u0041';
        char c3    ='\101';
        //字串雙引號 “ 的三種現形式
        char e1    ='\"';
        char e2    ='\u0022';
        char e3    ='\42';

        System.out.println(c1==c2&&c2==c3);
        System.out.println(e1==e2&&e2==e3);
    }
}

 

可見,就本例而言,三種方式是等價的

 4.3、三種轉義的區別

 先看這個例子

public class Test{

    public  static char c1 = '\u00a';
    public  static char c2 = '\u00d';

    public static void main(String[] args) {
        System.out.println(c1);
        System.out.println(c2);
    }
}

 

編譯報錯了

註釋掉欄位並刪掉main函式

public class Test{

    //public  static char c1 = '\u00a';
    //public  static char c2 = '\u00d';

}

繼續報錯

書上還有個小問題題
其實第一個程式碼應該如下
public class Test{

//    char c1 = '\u00a';
//    char c2 = '\u00d';
//上面兩行程式碼是書上的,如果換成下面兩行仍不能通過編譯,
//會報這個錯誤  Error:(8, 28) java: 無法從靜態上下文中引用非靜態 變數 c1
    char c1 = 'a';
    char c2 = 'd';
    public static void main(String[] args) {
        System.out.println(c1);
        System.out.println(c2);
    }
}

 所有我給測試的類的欄位加了靜態公有

再回到上面編譯報錯的問題

 4.4、增補字串

這個就先給個定義,很繁瑣我也很懵懂

5、 移星換斗—從byte b=1談型別轉換的神祕

 

5.1、無形的轉換

 

public class Test{

    public static void print(short value){
        System.out.println(value);
    }

    /*public static void print(long value){
        System.out.println(value);
    }*/

    public static void main(String[] args) {
        print(60);//13行
    }
}

報錯

 

 5.2、整型的轉換

 

public class Test{
    public static void main(String[] args) {
        //這三行為隱式轉換,編譯期可以自行處理
        byte b = -23;
        short s = 60;
        char c = '中';

        //下面的需要轉換運算子
        b= (byte) c;
        c= (char) b;

        s= (short) c;
        c= (char) s;

        b= (byte) -b;
        s= (short) (s+b);
        b= (byte) (b+1);
        b=+1;//正確 等價於b= (byte) (b+1);
        //符合運算子在賦值時可以自動將運算結果轉換為左側的操作型別
    }
}

 

此處 程式碼省略

6、撲朔迷離-浮點型別的種種懸疑 

 

6.1、浮點型別只是近似的儲存 

 

public class Test{
    public static void main(String[] args) {
        double d1 = 0.1;
        double d2 = 0.2;
        double d3 = d1+d2;
        System.out.println(d3);
    }
}
執行

 

 下面在看看浮點型別儲存的值

public class Test{
    public static void main(String[] args) {
        System.out.println("使用BigDecimal儲存的浮點型別值,它能更精確的輸出浮點數的值");
        for(int i=1;i<=9;i++){
            double d = Double.parseDouble("0." + i);
            System.out.println(d);
            BigDecimal bd=new BigDecimal(d);
            System.out.println(bd);
        }
    }
}

 

6.2、數量級差很大的浮點數 

 看程式

public class Test{
    public static void main(String[] args) {
        float f1 =30000;
        float f2 =f1+1;
        System.out.println(f1);
        System.out.println(f2);
        System.out.println("f2>f1為"+(f2>f1));

        float f3= 30000000;
        float f4 = f3+1;
        System.out.println(f3);
        System.out.println(f4);
        System.out.println("f4>f3為"+(f4>f3));

    }
}

執行

 

6.3、整形到浮點型別的轉換  

 

 6.4、從浮點型別到整形的轉換

 

--1 如果浮點值為NaN,結果為0L, --2 如果浮點值為+Infinity,則為long型別的最大(小)值 --3 如果浮點值不是±Infinity,則將浮點值向0舍入為整型值 ----    如果該整型值再long型別的取值範圍內,結果就是long型別的整型值。 ----    如果該整型值不在long型別的取值範圍內,則為long型別的最大(小)值

--1 如果浮點值為NaN,結果為0(int型別), --2 如果浮點值為+Infinity,則為int型別的最大(小)值 --3 如果浮點值不是±Infinity,則將浮點值向0舍入為整型值 ----    如果該整型值再int型別的取值範圍內,結果就是int型別的整型值。 ----    如果該整型值不在int型別的取值範圍內,則為int型別的最大(小)值

下面的程式來驗證上面的規則 

public class Test{
    public static void main(String[] args) {
       //public static final double NaN = 0.0d / 0.0
        double d = Double.NaN;
        System.out.println("(long)Double.NaN="+(long)d);
        System.out.println("(int)Double.NaN="+(int)d);
        System.out.println();

        d=3e30;//很大的浮點正數值
        System.out.println("(long)3e30="+(long)d);
        System.out.println("(int)3e30="+(int)d);
        System.out.println();

        d=-8e28;//很小的浮點負數值
        System.out.println("(long)-8e28="+(long)d);
        System.out.println("(int)-8e28="+(int)d);
        System.out.println();

        d=Double.POSITIVE_INFINITY;//正無窮
        System.out.println("(long)infinity="+(long)d);
        System.out.println("(int)infinity="+(int)d);
        System.out.println();

        d=Double.NEGATIVE_INFINITY;//負無窮
        System.out.println("(long)-infinity="+(long)d);
        System.out.println("(int)-infinity="+(int)d);
        System.out.println();

        d=-12345678.6;//在int型別的取值範圍內
        System.out.println("(long)-12345678.6="+(long)d);
        System.out.println("(int)-12345678.6="+(int)d);
        System.out.println("(byte)-12475678.6="+(byte)d);
        System.out.println("(int)(char)-12345678.6="+(int)(char)d);
        System.out.println("(short)-12345678.6="+(short)d);
        System.out.println();
    }
}

執行

 在第三點  浮點型別的收縮轉換中

浮點型別先收縮轉換為int型別再轉換為目標型別(byte short char)

 對應程式中的最後幾句

 浮點型別先收縮轉換為int型別如果不是特殊值需要向0取整,那什麼是向0取整呢,寫個例子看看

public class Test{
    public static void main(String[] args) {
        double d = 12345678.6;
        System.out.println("(int)-12345678.6="+(int)d);
        d=-1.123456789;
        System.out.println("(int)-1.123456789="+(int)d);
        d=-1.923456789;
        System.out.println("(int)-1.923456789="+(int)d);
        d=1234567899.87654321;
        System.out.println("(int)1234567899.87654321="+(int)d);
    }
}

可見本文提到的收縮轉換中的向0取整僅僅是把小數點後面的數字截掉了 

 

 注意,計算機中是使用補碼的運算的

7、水落石出-浮點結構的最終解密 

7.1、浮點型別的儲存 

這裡先說說十進位制的浮點數與二進位制的轉換

 下來再瞭解下 浮點型別中什麼是指數

對於指數本來以為很簡單,實際上概念也很混淆,就用一段程式碼來驗證下

float f1 =8.1f;
System.out.println(f1);//8.1
f1 =8.123456f;
System.out.println(f1); //8.123456
f1 =8000000.123456f;//整數部分8後面有6個零
System.out.println(f1); //8000000.0
f1 =80000000.123456f;//整數部分8後面有7個零
System.out.println(f1);//8.0E7
f1 =800000000.123456f;//整數部分8後面有8個零
System.out.println(f1);//8.0E8,有的書上說E後面的數字就是他的指數
f1 =812345678.123456f;//整數部分8後面還有8位數字
System.out.println(f1);//8.1234566E8

f1 =0.11223344556677888f;
System.out.println(f1);//0.112233445


/**
 * floa轉換為2進位制
 */

//我們知道7的二進位制為 4+2+1 即 0111
//我們知道8的二進位制為 1000
System.out.println(Integer.toBinaryString(7));//111
System.out.println(Integer.toBinaryString(8));//1000
//即Integer.toBinaryString就是一個數的二進位制表現形式

f1=8.1f;
System.out.println("8.1f的二進位制為"+ Integer.toBinaryString(Float.floatToIntBits(f1)));
f1=99.5f;
int fi = Float.floatToIntBits(f1);
System.out.println("99.5f的二進位制為"+ Integer.toBinaryString(fi));
//100 0010 1100 0111 0000 0000 0000 0000
//float是32位的前面可以補一個0
//0100 0010 1100 0111 0000 0000 0000 0000


繼續往下看

 

 

 

 

 

 7.2、近似儲存

 

 

 7.3、浮點數值之間的間隙

 

 

 

 回過頭在討論下指數域

一個數表示成 x.……*2^n,這個n就數指數域要存的值

7.4、最近舍入模式

 

 

 

 

 

 8、龍虎爭霸-基本for迴圈和增強for迴圈

 8.1、語法對比

先看一下二者在運算元組和集合之間的差別 

 

public static void main(String[] args) {         int[] array = new int[] {1,2,3};         List<String> list = new ArrayList<String>();         list.add("top");         list.add("middle");         list.add("bottom");         //基本for迴圈         for(int i = 0;i<array.length;i++) {             System.out.println(array[i]);         }         Iterator<String> iterator = list.iterator();         //相當於while(iterator.hasNext())         for(;iterator.hasNext();) {             System.out.println(iterator.next());         }                  //增強for迴圈,for each         for (int i : array) {             System.out.println(i);         }         for (String string : list) {             System.out.println(string);         }     } 

 

 

 8.2、加強for迴圈的極限

 

 

8.3、加強for迴圈的處理實現