java 是強類型語言,每個變和每個表達式都有一個在編譯時就確定的類型,所以所有變量必須顯示聲明類型,也就是所有的變量必須先聲明,後使用。
java的數據類型主要分爲兩種,一種是基本數據類型,一種是引用類型
基本數據類型
基本數據類型的圖式爲:
我們定義基本數據類型往往會在兩個地方,方法中和類中:在方法中定義的基本數據類型被稱爲局部變量,在類中定義的基本數據類型被稱作成員變量,也就是全局變量。
在內存中,這兩種數據存儲的位置是不同的。
局部變量是隨着線程級的,存儲在jvm棧中,隨着線程的消亡而被回收,基本數據類型型在棧中存儲的方式如下:
對於單個棧針中的字節大小與操作系統相關,32位系統爲4個字節,64位系統爲8個字節。之所以把double和long劃分成兩個內存塊是因爲棧區會存放對象的引用地址和基本數據類型,而地址用一個字存下,其他數據類型1個字也能存下,而double和long則需要兩個字。這裏需要注意一點,如果一個方法中同時定義int a=1;int b=1;這裏a是一個 引用,爲a分配一個棧空間,裏面數據是1,而b會在棧中搜索1是否存在,如果存在的話就是a的地址。因此在這裏b=1和b=a是一樣的。都是將a的地址賦予b。
而對於全局變量是存在常量池(方法區)中,因爲棧是線程級的,對於全局變量來說,所有的方法都會使用,因此需要將全局變量存在方法區裏的常量池中。
基本數據類型的轉化如下圖
引用類型
由於java是面向對象編程,因此每一個基本類型都有對應的對象
這裏右側的是基本數據類型對應的封裝類,在創建以後需要實例化對象才能使用。
這裏重點說下Integer,也是有很多有趣的事情發生,運行程序
public static void main(String[] args) {
int a=200;
int b=200;
System.out.println(a == b);
Integer c=200;
int d=200;
System.out.println(c == d);
Integer e=2;
Integer f=2;
System.out.println(e == f);
Integer g=200;
Integer h=200;
System.out.println(g == h);
}
結果爲:true
true
true
false
首先明確(==比較的是地址,equal比較的是內容)
第一種情況,如我們上面所說,基本數據類型,當已經把a賦值爲1,則 棧中存在1的引用,當爲b賦值的時候,直接把引用地址賦給b這樣ab的地址一致。
第二種情況,在執行Integer c=200的時候實際上是java自動裝箱的過程,編譯器會自動展開成Integer c = Integer.valueOf(200);這裏用到的valueOf()方法是裝箱,而當比較的時候由於是Integer==int,會將Integer方法進行拆箱使用intValue()方法,這樣還是比較int==int,因此是true
第三種情況和第四種情況一起來說,由於有這樣的源碼
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
因爲上文提到,Integer g = 200;在進行裝箱操作,實際上進行的是Integer g = Integer.valueOf(200);的操作,而對於-128·127的整數來說,已經預加載進常量池IntegerChache中定義的 static final Integer cache[];靜態數組。因此會直接返回該常量的地址。因此在-128~127之間的整數地址相同,而不再這個區間的整數就會新建對象去存值了。
然後再來說下char,char是字符型,用於表示單個字符。字符量必須用''括起來。運行程序
public static void main(String[] args) {
char a = 'a';
a = ++a;
System.out.println(a);
}
輸出結果爲
b
這裏首先定義了一個字符型的引用a,然後在進行++a的操作的時候,會將a強制裝換爲int進行+1操作,然後賦給char型的a,最後輸出爲char型的字符b
再來看看浮點型,float和double,二者只是精度不同,這裏需要注意一點,就是除法的運算,也就是“/”的操作。
當兩個int類型的數字相除的時候,除數爲0時是會報錯的,而當有一個數字被定義爲浮點型,無論是除數還是被除數,因爲浮點型的0在存儲的時候可能不會是0而是0.00000001等非常接近於0的數,而且當做運算的時候,jvm會向上兼用大數據累型,因此0會被轉換成浮點型,因此除以0後不會報錯,而是顯示無窮大。
int a=100;
Integer i=0;
Float f = 0f;
Double d = 0d;
System.out.println(a/f);
System.out.println(a/d);
System.out.println(a/i);
結果顯示爲
Infinity
Infinity
Exception in thread "main"
java.lang.ArithmeticException: / by zero
at TestDemo.main(TestDemo.java:9)
最後我們來看下字符串類型String
String本身就是引用類型,是沒有對應的基本數據類型的,因此在定義的時候,也是完成了自動裝箱。運行程序
public static void main(String[] args) {
String str1_1 = String.valueOf("123");
String str1_2 = "123";
System.out.println(str1_1==str1_2);
String str2_1 = new String("123");
String str2_2 = "123";
System.out.println(str2_1==str2_2);
String str3_1 = new String("123");
String str3_2 = new String("123");
System.out.println(str3_1==str3_2);
String str4_1 = "123";
String str4_2 = "123";
System.out.println(str4_1==str4_2);
}
運行結果爲:
true
false
false
true
實際上主要區分String str = "123";與String str = new String("123");的區別,前者創建了一個對象。當再次定義String b = "123";時,jvm會去堆中尋找123的地址並賦給b,而new的對象一定是創建了一個新的對象,因此後者創建了兩個對象String和123。
數組也是一種引用類型。
對於數組定義爲
public static void main(String[] args) {
int[] arr;
arr = new int[3];
for(int i =0;i<arr.length;i++)
{
arr[i] = i;
System.out.println(arr[i]);
}
}
內存結構的變化爲
這是對於基本數據類型數組的內存變化。下面介紹下引用類型數組的內存變化
public static void main(String[] args) {
StudentDto[] arrStu;
arrStu = new StudentDto[3];
for(int i=0;i<arrStu.length;i++)
{
StudentDto studentDto =new StudentDto();
studentDto.setId(i);
studentDto.setName("學生"+i);;
System.out.println(studentDto.getId()+"-------"+studentDto.getName());
}
}