Java 變量數據類型

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。

類型轉化詳見:Java 數據類型轉化
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),參考:Java中float/double取值範圍與精度),即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中的實現也是通過類實現的,每個類型的一維數組,二維數組……都是一個類,只是這是一個特殊的類,它的對象頭有別於一般對象的對象頭(最主要的就是,數組對象頭有對象長度)。
  另外,關於Java引用可以參考:Java中的引用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章