上一篇中簡單談了一下本身對Java的一些見解並起了一個頭,如今繼續總結java的相關語法。java語法整體上與C/C++同樣,因此對於一個C/C++程序員來講,天生就能看懂Java代碼。在學習java的時候,上手很是快。我感受本身就是這樣,看代碼,瞭解其中一些重點或者易錯點的時候發現,與C/C++裏面基本相似,甚至不少東西不用刻意去記,好像本身自己就知道坑在哪。因此這裏我想簡單列舉一下語法點,而後嘗試用C/C++的視角來解讀這些特性。
java
上一次,我總結一下java中的數據類型,在裏面提到,Java中有兩大數據類型,分爲基本數據類型和引用數據類型。而且說明了簡單數據類型,此次就從引用數據類型提及;
引用數據類型通常有:數組、字符串、類對象、接口、lambda表達式;此次主要經過數組和字符串來講明它程序員
引用數據類型在C/C++中對應指針或者引用。其實關注過我以前C/C++反彙編系列文章的朋友知道,在C/C++中引用實質上就是一個指針。因此這裏我也將java中引用類型理解爲指針。因此從本質上講,引用類型都是分配在堆上的動態內存。而後使用一個指針指向這塊內存。這是理解引用類型很重要的一點。編程
數組的定義以下數組
char[] a = new char[10]; char[] b = new char[] {'a', 'b', 'c'}; char[] c = {'a', 'b', 'c'};
其實這種形式更符合 變量類型 變量名 = 變量值
這種語法結構, char[]
就像是一種數據類型同樣,char
表示數組中元素類型, []
表示這是一個數組類型
字符串的簡單定義以下:安全
String s = "Hello world";
上面說到引用類型都是分配在堆上的。因此字符串和數組實質上都是new 出來的。即便有的寫法上並無new 這個關鍵字,可是虛擬機仍是幫助咱們進行了new 操做。有new就必定有delete了。那麼哪裏會delete呢?這些操做通常都由java的垃圾回收器來處理。咱們只管分配。這就幫助程序員從資源回收的工做中解放出來了。這也是java相比於C/C++來講比較優秀的地方。有人可能會說C++中有智能指針,也有垃圾回收機制。確實是這樣。可是我以爲仍是有點不同。java是天生就支持垃圾回收,就好像從孃胎生出來就有這個本能,而C/C++是由後天學會的,或者說要刻意的去進行操做。兩者仍是不同的。數據結構
下面有這樣一段代碼:函數
char[] a = new char[10]; System.out.println(a);
咱們打印這個a變量,發現它出現的是一個相似16進制數的一個東西。這個東西實際上是一個地址的hash值,爲何不用原始值呢?我估計是由於有大神可以根據變量的內存地址進行逆向破解,因此這裏爲了安全對地址值進行了一個加密。或者爲了完全貫徹Java不操做內存的信念。(我這個推斷不知道是否是真的,若是有誤,請評論區大牛指正。)這也就證實了我以前說的,引用類型本質上是一個指針。學習
char[] a = new char[]{'a', 'b', 'c'}; char[] b = new char[]{'a', 'b', 'c'}; System.out.println(a); System.out.println(b);
上面這段代碼,我想學過C/C++的人應該一眼就能看出,這裏打印出來的a和b應該是不一樣的值,這裏建立了兩塊內存。只是內存中放的東西是同樣的。大數據
char[] a = new char[]{'a', 'b', 'c'}; char[] b = a; b[0] = '0'; b[1] = '1'; b[2] = '2'; System.out.println(a); System.out.println(b);
這裏從C/C++的角度來看,也很容易理解:定義了兩個引用類型的變量,a、b都指向同一塊內存,無論經過a仍是b來尋址並寫內存,下次經過a、b訪問對應內存的時候確定會發現值與最早定義的不一樣。加密
String s = "hello"; System.out.println(s.hashCode()); s += "world"; System.out.println(s.hashCode());
因爲Java不具有直接訪問內存的能力,不能直接打印出它的內存地址,因此這裏用hashCode 獲得地址的hash值。經過打印結果說明這個時候s指向的地址已經變了。也就是說雖然能夠實現字符串的拼接,可是虛擬機在計算得出拼接的結果後又分配了一塊內存用來保存新的值。可是任然用s這個變量來存儲地址值,用趙本山的話來講就是「大爺仍是那個大爺,大媽已經不是原來的那個大媽了」。
也就是說Java分配內存的時候應該是按需分配,須要多少分配多少。不夠就回收以前的,再從新按需分配。這就致使了java中字符串和數組的長度是不能改變的。
String s = "hello"; System.out.println(s.hashCode()); (s.toCharArray())[0] = 'H'; System.out.println(s.hashCode()); System.out.println(s); // 這裏字符串的值不變
上述這段代碼,經過toCharArray將字符串轉化爲char類型的數組,而後修改數組中的某一個元素的值,我原來覺得這樣作至關於在String所在內存中修改,最終打印s時會出現 "Hello" ,可是從結果上來看並無出現這樣的狀況,s指向的地址確實沒變,可是s也是沒變的,那隻能解釋爲toCharArray 又開闢了一塊內存,將String中的值一一複製到數組中。
在學習中我嘗試過各類數據類型強轉
String s = "Hello World"; char[] a = (char[]) s; int p = (int)s;
像這樣的代碼我發現並不能經過編譯。在C/C++中,能夠進行任意類型到整型或者指針類型的轉化,常見的轉化方式就是將變量所在地址進行賦值或者將變量對應的前四個字節進行轉化做爲int或者指針類型。可是在java中這點好像行不通。Java中強轉好像只能在基本數據類型中實現,而在引用類型中一般由函數完成,而且完成時並非簡單的賦值,還涉及到新內存空間的分配問題。
因爲C/C++中提供了訪問內存的能力,並且因爲現代計算機的結構問題,C/C++中存在越界訪問的問題,越界訪問能夠帶來程序編寫的靈活性,可是也帶來的一些安全隱患。對於靈活性,相信學習過Windows或者Linux編程的朋友應該深有體會,系統許多數據結構的定義常常有這類:
struct s { char c; } s *p = (s*)new char[100];
這樣就簡單的建立了一個字符串的結構。這裏C變量只是提供了一個地址值,後續能夠根據c的地址來訪問它後續的內存。
安全問題就是大名鼎鼎的緩衝區溢出漏洞,我在相關博客中也詳細談到了緩衝區溢出漏洞的危害以及基本的利用方式。這裏就不在贅述。
那麼Java中針對這種問題是如何處理的呢? Java中因爲不具備內存訪問的能力,因此這裏它簡單記錄當前對象的長度,只要訪問超過這個長度,立馬就會報異常,報一個越界訪問的異常。(這裏我暫時沒有想到對應的java演示代碼,因此簡單說一下吧)
還記得C/C++指針中常見的一個NULL吧,既然Java中引用類型至關於一個指針,那麼它確定也存在空指針問題。在Java中空指針定義爲null。若是直接訪問null引用,通常會報空指針訪問異常。
char[] c = null; c[0] = 'A'; //異常
關於引用類型我暫時瞭解了這麼多東西。下面簡單列舉一下java中的運算符和相關語句結構
java中的運算符主要有下列幾個:
這些運算符用法、要點、執行順序與C/C++中徹底相同。因此這裏簡單列舉。不作任何說明
java中的順序結構與其餘語言中同樣,主要有3種
用於與其餘語言同樣,這裏須要注意的是,Java中須要判斷的地方只能使用bool值做爲判斷條件,好比 5 == 3
、 a == 5
這樣的。
在C/C++中有一條編程規範,像判斷語句中將常量寫在前面就像這樣
if(5 == a){ //.... }
這樣主要是爲了防止將 ==
誤寫成 =
,由於在C/C++中 只要表達式的值不爲0 就是真,像 if(a = 5)
這樣的條件是恆成立的。而Java中規定判斷條件必須是真或者假,而且規定boolean類型不能轉化爲其餘類型,其餘類型也不能轉化爲boolean,因此 if(a = 5)
這樣的語句在Java中編譯是不會經過的。