Java學習點滴——初識Java
基於《Java編程思想》第四版
前言
“程序就是算法加數據結構”,而算法就是控制語句加操作符,編寫一個程序就是使用控制語句加操作符去操作數據結構,因此我從Java的控制語句、操作符以及如何組織數據結構開始入手。因為有C/C++的基礎,所以不免會以對比的方式去理解Java。
控制語句
除了沒有goto
,Java的控制流程的關鍵字和C++是一樣的,很好理解。不過Java中的break
和continue
除了C++的正常作用外(跳出或繼續當前循環),還有類似C++中goto
的功能,但是使用上是有限制的,即標簽與for
、while(){}
、do{}while()
或switch
之間不能有其他語句,否則就會有編譯錯誤。
- 使用
break
跳轉到標簽後,會直接跳過標簽後緊跟著的循環或者switch
代碼,而不是從標簽位置重新開始執行。
int[] a = {1,2,3,4}; Label: // 這裏不能有任何語句 for( int i : a ){ System.out.println("loop 1 i = " + i); for( int j : a ){ System.out.println("loop 2 j = " + j); break Label; } } // break Lable後會直接執行下面的代碼,而不是繼續循環 System.out.println("loop over");
- 使用
continnue
跳轉到標簽後,會繼續執行標簽後緊跟著的循環代碼,但並非重頭開始執行而是從原本的基礎上繼續執行
int[] a = {1,2,3,4}; Label: // 這裏不能有任何語句 for( int i : a ){ System.out.println("loop 1 i = " + i); for( int j : a ){ System.out.println("loop 2 j = " + j); continue Label; } } System.out.println("loop over");
運行以上代碼就會發現break Label
的含義就是跳出Label
標識的循環,而continue Label
的含義是繼續循環Label
標識的循環,這和C++的goto
是不同的。C++中的goto
會將讓程序執行流回到Label
的位置,重新執行Label
後的代碼。
操作符
Java多了一個無符號右移操作符>>>
,其他都和C/C++一樣,也很好掌握。不過Java中無法進行操作符重載,因此理論上操作符應該只能作用於數值類型。但實際上,Java的String
類型也可以使用=
、+
和+=
操作符,我想應該是因為字符串的賦值和拼接是很常見的操作,所以Java就在內部偷偷給String
類型做了這些操作符的重載。這雖然帶來了一定的便利,但是因為String
類型的其他操作符並沒有重載,所以讓我感覺不一致,比較難受。比如下面這段代碼,因為String
的==
並未重載,所以並不會打印"same string"
String s1 = new String("hello");
String s2 = new String("hello");
if( s1 == s2 ){
System.out.println(“same string”);
}
在C++中,則可以通過操作符重載,使得std::string
類型可以使用==
操作符比較是否為相同字符串,很一致。
還有一點,Java中並沒有sizeof
操作符。我想這是因為Java不想讓程序員去關註內存分配,自然也就無需關心類型大小,再者基礎類型的大小在Java中是固定的,所以sizeof
就無用武之地了。C中分配內存的函數,如malloc
等,都是需要指定大小的,且基礎類型在不同平臺的大小可能是不一樣大的,因此必須有sizeof
計算大小。
類型
Java有以下基礎類型
這些基礎類型的大小都是固定的,而且所有數值類型都是有符號的。
Java通過class
關鍵字來自定義類型,其結構與C++類似,只是不需要在}
後加;
。
class MyType{
...
}
C++中的自定義類型除非指定繼承否則是沒有繼承關系的,但是在Java中所有類型都隱式繼承自Object
。Java中有很多已經定義好的類型,比如基礎類型的包裝器類型、String
等等,學習並使用這些已經定義好的類型是水磨工夫,起初了解一下就可以了。
Java的函數的定義語法和C++是一模一樣的,但是函數只能在類型的命名空間裏即只能在
class {這裏面}
定義,而不能在全局命名空間中定義。函數在Java中應該叫方法,不知道叫函數會不會有誤解。
Java的自定義類型中可以包含其他類型的變量或者繼續定義類型(內部類)。其他類型的變量,C++中叫成員變量,但似乎Java中叫域。
實例化類型
Java實例化類型的語法和C++一模一樣,但是有一些限制。
- 基礎類型只能直接實例化,無法用
new
實例化
int x = 1;
- 特定類型,比如基礎類型的包裝器類型、
String
類型等,可以直接賦值(本質上是編譯器幫你做了一次隱式轉換),或使用new
實例化
Integer n1 = 1;
Integer n2 = new Integer(1);
String s1 = "hello";
String s2 = new String("hello"); // 不推薦這麽用,轉換後的字節碼更多
- 其余類型的實例化必須使用
new
關鍵字
MyType m = new MyType(1);
// MyType m = 1; 不會進行一次隱式轉換,編譯報錯
可以感覺到很強烈的不一致!!
基礎類型的變量空間存儲的是真實數值,而其他類型的變量空間存儲的是實例化對象的引用。Java中的引用和C++的引用並不是一個意思,Java中的引用更像是C++中的指針。在C++中,引用是一個實例對象的別名,一旦確定就無法變更其引用的對象,但是在Java中可以變更引用的實例化對象,比如
Integer a = new Integer (1);
a = new Integer(2);
在函數傳參中,這點就更明顯了,比如下面的函數,我們叫a
和b
是引用,但實際呢,這只是值傳遞,swap()
中的交換並不會影響實際對象的值。整個函數就是交換了一下a
和b
這兩個局部變量指向的對象而已。
void swap(Integer a, Integer b){
Integer tmp = a;
a = b;
b = tmp;
}
就類似於以下C++代碼
void swap(int* a, int* b){
int* tmp = a;
a = b;
b = tmp;
}
從上面看所謂對象的引用其實就是把對象的地址值(不是內存地址,只需要是一個唯一位置的標識即可)保存到了變量空間裏。從這個角度去理解,基礎類型的變量和其他類型的變量存儲的東西可以認為是一樣的。Java中只有值傳遞。
訪問控制
和C++一樣,Java也有針對類、方法、域的訪問權限控制。Java除了public
、proteceted
和private
這些權限外,還有一種包訪問權限。當不帶另外三個權限關鍵字時,就是包訪問權限了。在Java中可以將一些源文件定義為一個包,因此就有了包訪問權限,即同一包內可以訪問。Java中的包類似於C++的動態庫,C++中雖然沒有明確說包(庫)訪問權限,但實際上是有的,比如Linux下可以通過鏈接時的參數version-script
指定動態庫的導出符號,那些未導出的符號就是包(庫)訪問權限了。
文件組織
一個.java
源文件中只能有一個public
類,且源文件名必須和這個public
類的名字保持一致。其他類只能是包訪問權限,當然內部類是不受這個限制的,可以是任意權限。每個類型都可以有一個public static void main(String[] agrs)
,這是執行的入口。因為函數都是在類的命名空間裏,所以存在多個main()
也是可以的,指定執行的類就會調用對應的main()
。
結語
因為IDE的強大,所以很多東西只需要腦子裏有點印象,做到寫代碼時看到錯誤提示就能想到是為什麽就可以了,熟能生巧。
Java學習點滴——初識Java