Java 變量
Java資料類型圖:

1.基本資料類型
基本資料類型,也稱内置類型,是可以在棧直接配置設定記憶體的,Java保留基本資料類型最大的原因也在此:性能。關于這一點可以參考:Java為什麼需要保留基本資料類型。
另外,要注意,Java是基于JVM的,是以,其所占位元組固定,與機器平台無關,所有地方統一占用記憶體大小(除了boolean,以及byte/short/boolean數組的時候每個單元所占的記憶體是由各個JVM自己實作的)。
總共有四類八種基本資料類型(注1):
1).整型:全部是有符号類型。
1.byte:1位元組(8bit),高位為符号位,其餘7位為資料位,範圍:-2的7次方~2的7次方-1(1111,1111~0111,1111),即-128~127(下面的計算方式相同);
注意:byte類型雖然在語義(邏輯)上是占用1位元組,但實際上,JVM中是将其當做int看
的,也就是事實上是占用了32位,4位元組的,是以其運算效率和int沒差別,short也一樣。
之是以要有byte/short類型,一是因為某些地方要明确使用這些範圍類型,二是,
在byte[]數組中,JVM存儲的則是真的1位元組,short[]2位元組。(但也有的JVM其byte[]
數組也是4位元組1位)
2.short:2位元組(16bit),高位為符号位,其餘15位為資料位,範圍:-2的15次方~2的15次方-1,即-32768~32767;
3.int:4位元組(32bit),範圍-2的31次方~2的31次方-1;Java預設的整型類型,即:
long l = 0xfffffffffff;//0x表示這個數是16進制數,0表示8進制。
//編譯器報錯,因為右邊預設是int,但其超出了範圍(沒超出int範圍的話
//編譯器會隐式将int轉為long),故報錯(同樣的錯誤也會出現在float)。
同樣的還有:
short s = 123;//(這個123也是int類型,這裡,= 操作編譯器能隐式轉換)
s = s + 123;//編譯器報錯,那是因為s+1是int類型(編譯器先将s轉化為int,再+1),
//這裡,+ 操作編譯器不能隐式轉換(會提示失真,即精度可能會受損),正确的做法:
s = (short)(s + 123)//注意,不是(short)s + 123。
4.long:8位元組(64bit),範圍:-2的63次方~2的63次方-1;聲明大的long方法:
long l = 0xfffffffffffL;//即在後面加上L或l。
//(強制轉化:long l = (long)0xfffffffffff也沒用)
2).浮點型
5.float:4位元組(32bit),單精度,資料範圍:(-2^128)~(-2^(-23-126))-(0)-(2^-149)~2^128。浮點數,通俗來說就是小數,但是,這是有精度要求的,即在這區間float可不是能表達任意小數的,而是在一定精度下,比如float有效位7~8位(包括整數位和小數位,有效小數位是6~7位,這裡為什麼是7~8(6~7),即0.123456789後面的9JVM是不認識的(8能認識,整數位為0則不算是有效位,例如12.1234567後面的7也不認識,隻有6位有效小數位(注意,看的是有效位,不是有效小數位,float有7~8位有效位)),即:
if(0.123456781f == 0.123456789f){//注意後面加f/F,否則就是double
System.out.println("true");
}else{
System.out.println("false");
}
//列印結果:true
//事實上,浮點數值的比較是不能直接用==判斷的,這裡原因就要追究到浮點數的記憶體結構
//浮點數比較可以用一個內插補點,但這種情況隻是近似的比較
//如果想要精确,可以使用BigDecimal
System.out.println(Float.MIN_VALUE);//1.4E-45 = 2^-149
//這裡的“最小值”意味float能表示的最小小數,實際上float最小值等于最大值取負
System.out.println(Float.MAX_VALUE);//3.4028235E38 = 2^128
6.double:8位元組(64bit),雙精度,範圍:-2^1024~(-2^(-1022-52))-0-(2^-1074)~2^1024,Java預設的浮點類型,即若後面不加f/F,預設是double類型,即:
float f = 1.23;//編譯報錯,因為
float f = 1.23f;//或float f = 1.23F;
//預設是double,1.23(double)轉成float,做隐式轉換,但是double轉成float是
//取值範圍大的轉成取值範圍小的會損失精度,是以不能轉換(詳見Java資料類型轉換)
//那為什麼,int可以轉換成byte、short,int範圍更大不是?
//前面已經說過了,byte、short實際在JVM上就是int,是以編譯器是不會認為會損失精度的
//但是int是不能轉換成boolean,雖然boolean也是4位元組(一般JVM),但在JVM認為這
//兩者完全是兩個東西,當然不能轉換(強制也不行,你不能把貓強制轉換成鳥,完全兩個物種),而byte、short都是整型,同int是一個類型
3).字元型
7.char:2位元組(16bit),表示一個字元(可以是漢字),字元編碼采用Unicode(說的更準确點,字元集(charset)采用UCS-2,編碼(encoding)采用UTF-16),實際上就是一個16位的無符号整型,但是,要注意的是,因為随着發展,char所能代表的字元個數(UCS-2字元集)被限定死了,是以并不推薦使用。(更多内容,以及關于Unicode、UTF8/16參考:Unicode、UTF8以及Java char。)
char c = 3+5;//正确,char是無符号整型,但不能這樣
int a1 = 3;int a2 = 5;char c0 = a1+a2;//這裡需要強制轉換才行
char c1 = -3;//編譯錯誤,char不能表示負數,即使
char c2 = (char)-3;//編譯正确,但無意義(亂碼)
char c3 = '3';//正确,輸出字元3
char c4 = "3";//編譯錯誤,雙引号,表示的是字元串
char c5 = '65';//編譯錯誤,這裡65是兩個字元
4).布爾型
8.boolean:邏輯上:1bit,但是實際上,boolean并沒有具體規定,完全是看各個JVM實作,不過《Java虛拟機規範》給出了4個位元組(同byte解釋)和boolean數組一個位元組的定義。
注1:
(1).這種分法是一種比較流行的分法,事實上應該為兩種:數值類型與布爾型。數值類型分為整型和浮點型。整型包括:byte、short、int、long、char;浮點型:float、double;布爾型boolean。之是以将char認為是整型是因為char在JVM就是以無符号整型存在的。
(2).事實上Java中除去這8種以及對象類型,還有一種比較特殊的類型存在,那就是Void。java.lang.Void,是一個占位符類,不可執行個體化,儲存着Java關鍵字void的Class對象。為什麼說它特殊呢?明明是一個類,難道不是對象類型?那是因為void.class.isPrimitive()(這個方法是用來判斷一個Class對象是否是基本類型的)傳回的是true,是以Void也算是基本類型的一個了(錯了),隻不過它比較特殊,不能算是一種資料,隻是一種象征。
20160921 改:上面弄錯了,把Void和void兩個混為一體了,事實上,可以簡單的把這兩者的關系看成類似包裝類和基本類型的關系,像Integer和int的關系,java.lang.Void是一個不可執行個體化的占位符類來儲存一個引用代表了Java關鍵字void的Class對象:
public static final Class<Void> TYPE = Class.getPrimitiveClass("void");
而Integer也有類似的語句:
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
差別隻是,Void僅僅是為void服務,即所謂的占位符類,不做他用。是以Void類隻是一個普通類,而void則可以認作為如同int一樣的基本類型。
2.引用資料類型
也稱對象變量類型,複合資料類型,包含類、接口、數組(除了基本類型外,就是引用類型)。引用類型與基本類型最大的差別在于:
int a = 5;//這裡的a是對象(嚴格來說不算是對象,隻是個符号辨別),5是數值
Integer a = 5;//這裡的a是一個引用,5才是一個對象,更形象常見的是:
Object o = new Object();//o是引用(棧中),new Object()是對象(堆中)
//第二行代碼中,5被自動包裝成Integer對象
這裡的引用有點像C/C ++中的指針,但是同指針不同的是,你不能通過改變它的值進而去改變它所指向的值。即
ClassA p = new ClassA();//C++中,這個時候是可以這樣操作的:
p = p + 1;//向前移動一個單元,Java則不能
//這種操作,其實是對記憶體直接的操作,很顯然,Java是不允許程式員做這種操作的
其實質就是,Java的引用不支援對記憶體直接操作,而指針則可以,是以,Java用起來更安全,但不夠靈活,而指針,自由度大,但同時,要更加小心因為指針操作不當而引起的各種記憶體問題。在Java中,任何對象都需要通過引用才能通路到,沒有引用指向的對象被視為垃圾對象,将會被回收。
引用,其實質同指針一樣(可以了解為受限制的指針),存放的是一個位址,至于是執行個體對象的位址,還是一個指向句柄池的位址(這裡可以參考:(3) Java記憶體結構),完全是看各個JVM的實作了。
Java中的枚舉類型,都是Enum類的子類,算是類中的一種,也是引用類型。
引用類型又稱為對象變量類型,是相對于基本資料類型來說的(基本資料類型不是對象),而又被稱為複合資料類型,可以這樣了解,引用類型的資料最終都是由基本資料類型構成的。而像接口,接口是不能執行個體化的,最終的實作還是由類實作的;數組在JVM中的實作也是通過類實作的,每個類型的一維數組,二維數組……都是一個類,隻是這是一個特殊的類,它的對象頭有别于一般對象的對象頭(最主要的就是,數組對象頭有對象長度)
3.變量的作用域
規定了變量所能使用的範圍,*隻有在作用域範圍内變量才能被使用*。根據變量*聲明地點的不同*,變量的作用域也不同。
根據*作用域的不同*,一般将變量分為不同的類型:*類變量、局部變量、方法參數變量及異常處理參數變量*。下面對這幾種變量進行詳細說明。
4.變量類型
類變量
類變量也稱為成員變量,聲明在類中,不屬于任何一個方法,作用域是整個類。
**例 1:**假設在一個類中聲明了 3 個變量,下面編寫一個測試類輸出引起變量的值改變的示例代碼。變量聲明,實作代碼如下所示:
- public class DataClass
- {
- int price=100; //定義類變量 price
- price String name; //定義類變量 name
- name int num; //定義類變量 num
- }
測試類代碼如下所示:
1. public class Test 2. { 3. public static void main(String[] args) 4. { 5. DataClass dc=new DataCLass(); 6. System.out.println("name="+dc.name); 7. System.out.println("num="+dc.num); 8. System.out.println("price="+dc.price); 9. } 10. }
運作結果如下:
name=null
num=0
price=100
在第一段代碼中3 個成員變量,并對其中第一個變量 price 進行了初始化,而第二個 name 變量和第三個變量 num 沒有進行初始化。由輸出結果可以看出,第一個變量的值為顯示初始化的值,第二個和第三個變量的值則為系統預設初始化的值。
局部變量
局部變量是指在方法或者方法代碼塊中定義的變量,其作用域是其所在的代碼塊。
**例 2:**聲明兩個局部變量并輸出其值,實作代碼如下:
1. public class Test2 2. { 3. public static void main(String[] args) 4. { 5. int a=7; 6. if(5>3) 7. { 8. int s=3; //聲明一個int類型的局部變量 9. System.out.println("s="+s); 10. System.out.println("a="+a); 11. } 12. System.out.println("a="+a); 13. } 14. }
上述執行個體中定義了 a 和 s 兩個局部變量,其中 int 類型的 a 的作用域是整個 main() 方法,而 int 類型的變量 s 的作用域是 if 語句的代碼塊内,運作結果如下:
s=3
a=7
a=7
方法參數變量
***作為方法參數聲明*的變量的作用域是整個方法。
**例 3:**聲明一個方法參數變量,實作代碼如下:
testFun() 方法,該方法中包含一個 int 類型的參數變量 n,其作用域是 testFun() 方法體内。當調用方法時傳遞進了一個參數 3,是以其輸出控制台的 n 值是 3。1. public class Test3 2. { 3. public static void testFun(int n) 4. { 5. System.out.println("n="+n); 6. } 7. public static void main(String[] args) 8. { 9. testFun(B); 10. } 11. } 在上例中定義了一個
異常處理參數變量
1. public class Test4 2. { 3. public static void test() 4. { 5. try 6. { 7. System.out.println("Hello!Exception!"); 8. } 9. catch(Exception e) 10. { //異常處理塊,參數為 Exception 類型 11. e.printStackTrace(); 12. } 13. } 14. public static void main(String[] args) 15. { 16. test(); 17. } 18. }