透析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迴圈的處理實現