本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內(nèi)容請到我的倉庫里查看
創(chuàng)新互聯(lián)專注于新化企業(yè)網(wǎng)站建設(shè),響應(yīng)式網(wǎng)站,電子商務(wù)商城網(wǎng)站建設(shè)。新化網(wǎng)站建設(shè)公司,為新化等地區(qū)提供建站服務(wù)。全流程專業(yè)公司,專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
https://github.com/h3pl/Java-Tutorial
喜歡的話麻煩點下Star哈
文章首發(fā)于我的個人博客:
www.how2playlife.com
本文是微信公眾號【Java技術(shù)江湖】的《夯實Java基礎(chǔ)系列博文》其中一篇,本文部分內(nèi)容來源于網(wǎng)絡(luò),為了把本文主題講得清晰透徹,也整合了很多我認為不錯的技術(shù)博客內(nèi)容,引用其中了一些比較好的博客文章,如有侵權(quán),請聯(lián)系作者。
該系列博文會告訴你如何從入門到進階,一步步地學(xué)習Java基礎(chǔ)知識,并上手進行實戰(zhàn),接著了解每個Java知識點背后的實現(xiàn)原理,更完整地了解整個Java技術(shù)體系,形成自己的知識框架。為了更好地總結(jié)和檢驗?zāi)愕膶W(xué)習成果,本系列文章也會提供部分知識點對應(yīng)的面試題以及參考答案。
如果對本系列文章有什么建議,或者是有什么疑問的話,也可以關(guān)注公眾號【Java技術(shù)江湖】聯(lián)系作者,歡迎你參與本系列博文的創(chuàng)作和修訂。
變量就是申請內(nèi)存來存儲值。也就是說,當創(chuàng)建變量的時候,需要在內(nèi)存中申請空間。
內(nèi)存管理系統(tǒng)根據(jù)變量的類型為變量分配存儲空間,分配的空間只能用來儲存該類型數(shù)據(jù)。
因此,通過定義不同類型的變量,可以在內(nèi)存中儲存整數(shù)、小數(shù)或者字符。
Java語言提供了八種基本類型。六種數(shù)字類型(四個整數(shù)型,兩個浮點型),一種字符類型,還有一種布爾型。
byte:
short:
int:
long:
float:
double:
boolean:
char:
//8位
byte bx = Byte.MAX_VALUE;
byte bn = Byte.MIN_VALUE;
//16位
short sx = Short.MAX_VALUE;
short sn = Short.MIN_VALUE;
//32位
int ix = Integer.MAX_VALUE;
int in = Integer.MIN_VALUE;
//64位
long lx = Long.MAX_VALUE;
long ln = Long.MIN_VALUE;
//32位
float fx = Float.MAX_VALUE;
float fn = Float.MIN_VALUE;
//64位
double dx = Double.MAX_VALUE;
double dn = Double.MIN_VALUE;
//16位
char cx = Character.MAX_VALUE;
char cn = Character.MIN_VALUE;
//1位
boolean bt = Boolean.TRUE;
boolean bf = Boolean.FALSE;
`127`
`-128`
`32767`
`-32768`
`2147483647`
`-2147483648`
`9223372036854775807`
`-9223372036854775808`
`3.4028235E38`
`1.4E-45`
`1.7976931348623157E308`
`4.9E-324`
`?`
`true`
`false`
常量在程序運行時是不能被修改的。
在 Java 中使用 final 關(guān)鍵字來修飾常量,聲明方式和變量類似:
final double PI = 3.1415927;
雖然常量名也可以用小寫,但為了便于識別,通常使用大寫字母表示常量。
字面量可以賦給任何內(nèi)置類型的變量。例如:
byte a = 68;
char a = 'A'
Java 5增加了自動裝箱與自動拆箱機制,方便基本類型與包裝類型的相互轉(zhuǎn)換操作。在Java 5之前,如果要將一個int型的值轉(zhuǎn)換成對應(yīng)的包裝器類型Integer,必須顯式的使用new創(chuàng)建一個新的Integer對象,或者調(diào)用靜態(tài)方法Integer.valueOf()。
//在Java 5之前,只能這樣做
Integer value = new Integer(10);
//或者這樣做
Integer value = Integer.valueOf(10);
//直接賦值是錯誤的
//Integer value = 10;`
在Java 5中,可以直接將整型賦給Integer對象,由編譯器來完成從int型到Integer類型的轉(zhuǎn)換,這就叫自動裝箱。
`//在Java 5中,直接賦值是合法的,由編譯器來完成轉(zhuǎn)換`
`Integer value = 10;`
`與此對應(yīng)的,自動拆箱就是可以將包裝類型轉(zhuǎn)換為基本類型,具體的轉(zhuǎn)換工作由編譯器來完成。`
`//在Java 5 中可以直接這么做`
`Integer value = new Integer(10);`
`int i = value;`
自動裝箱與自動拆箱為程序員提供了很大的方便,而在實際的應(yīng)用中,自動裝箱與拆箱也是使用最廣泛的特性之一。自動裝箱和自動拆箱其實是Java編譯器提供的一顆語法糖(語法糖是指在計算機語言中添加的某種語法,這種語法對語言的功能并沒有影響,但是更方便程序員使用。通過可提高開發(fā)效率,增加代碼可讀性,增加代碼的安全性)。
在八種包裝類型中,每一種包裝類型都提供了兩個方法:
靜態(tài)方法valueOf(基本類型):將給定的基本類型轉(zhuǎn)換成對應(yīng)的包裝類型;
實例方法xxxValue():將具體的包裝類型對象轉(zhuǎn)換成基本類型;
下面我們以int和Integer為例,說明Java中自動裝箱與自動拆箱的實現(xiàn)機制??慈缦麓a:
class Auto //code1
{
public static void main(String[] args)
{
//自動裝箱
Integer inte = 10;
//自動拆箱
int i = inte;
//再double和Double來驗證一下
Double doub = 12.40;
double d = doub;
}
}
上面的代碼先將int型轉(zhuǎn)為Integer對象,再講Integer對象轉(zhuǎn)換為int型,毫無疑問,這是可以正確運行的??墒?,這種轉(zhuǎn)換是怎么進行的呢?使用反編譯工具,將生成的Class文件在反編譯為Java文件,讓我們看看發(fā)生了什么:
class Auto//code2
{
public static void main(String[] paramArrayOfString)
{
Integer localInteger = Integer.valueOf(10);
int i = localInteger.intValue();
Double localDouble = Double.valueOf(12.4D);
double d = localDouble.doubleValue();
}
}
我們可以看到經(jīng)過javac編譯之后,code1的代碼被轉(zhuǎn)換成了code2,實際運行時,虛擬機運行的就是code2的代碼。也就是說,虛擬機根本不知道有自動拆箱和自動裝箱這回事;在將Java源文件編譯為class文件的過程中,javac編譯器在自動裝箱的時候,調(diào)用了Integer.valueOf()方法,在自動拆箱時,又調(diào)用了intValue()方法。我們可以看到,double和Double也是如此。
實現(xiàn)總結(jié):其實自動裝箱和自動封箱是編譯器為我們提供的一顆語法糖。在自動裝箱時,編譯器調(diào)用包裝類型的valueOf()方法;在自動拆箱時,編譯器調(diào)用了相應(yīng)的xxxValue()方法。
在使用自動裝箱與自動拆箱時,要注意一些陷阱,為了避免這些陷阱,我們有必要去看一下各種包裝類型的源碼。
Integer源碼
public final class Integer extends Number implements Comparable {
private final int value;
/*Integer的構(gòu)造方法,接受一個整型參數(shù),Integer對象表示的int值,保存在value中*/
public Integer(int value) {
this.value = value;
}
/*equals()方法判斷的是:所代表的int型的值是否相等*/
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
/*返回這個Integer對象代表的int值,也就是保存在value中的值*/
public int intValue() {
return value;
}
/**
* 首先會判斷i是否在[IntegerCache.low,Integer.high]之間
* 如果是,直接返回Integer.cache中相應(yīng)的元素
* 否則,調(diào)用構(gòu)造方法,創(chuàng)建一個新的Integer對象
*/
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
/**
* 靜態(tài)內(nèi)部類,緩存了從[low,high]對應(yīng)的Integer對象
* low -128這個值不會被改變
* high 默認是127,可以改變,最大不超過:Integer.MAX_VALUE - (-low) -1
* cache 保存從[low,high]對象的Integer對象
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
以上是Oracle(Sun)公司JDK 1.7中Integer源碼的一部分,通過分析上面的代碼,得到:
1)Integer有一個實例域value,它保存了這個Integer所代表的int型的值,且它是final的,也就是說這個Integer對象一經(jīng)構(gòu)造完成,它所代表的值就不能再被改變。
2)Integer重寫了equals()方法,它通過比較兩個Integer對象的value,來判斷是否相等。
3)重點是靜態(tài)內(nèi)部類IntegerCache,通過類名就可以發(fā)現(xiàn):它是用來緩存數(shù)據(jù)的。它有一個數(shù)組,里面保存的是連續(xù)的Integer對象。
(a) low:代表緩存數(shù)據(jù)中最小的值,固定是-128。
(b) high:代表緩存數(shù)據(jù)中最大的值,它可以被該改變,默認是127。high最小是127,最大是Integer.MAX_VALUE-(-low)-1,如果high超過了這個值,那么cache[ ]的長度就超過Integer.MAX_VALUE了,也就溢出了。
(c) cache[]:里面保存著從[low,high]所對應(yīng)的Integer對象,長度是high-low+1(因為有元素0,所以要加1)。
4)調(diào)用valueOf(inti)方法時,首先判斷i是否在[low,high]之間,如果是,則復(fù)用Integer.cache[i-low]。比如,如果Integer.valueOf(3),直接返回Integer.cache[131];如果i不在這個范圍,則調(diào)用構(gòu)造方法,構(gòu)造出一個新的Integer對象。
5)調(diào)用intValue(),直接返回value的值。
通過3)和4)可以發(fā)現(xiàn),默認情況下,在使用自動裝箱時,VM會復(fù)用[-128,127]之間的Integer對象。
Integer a1 = 1;
Integer a2 = 1;
Integer a3 = new Integer(1);
//會打印true,因為a1和a2是同一個對象,都是Integer.cache[129]
System.out.println(a1 == a2);
//false,a3構(gòu)造了一個新的對象,不同于a1,a2
System.out.println(a1 == a3);
//基本數(shù)據(jù)類型的常量池是-128到127之間。
// 在這個范圍中的基本數(shù)據(jù)類的包裝類可以自動拆箱,比較時直接比較數(shù)值大小。
public static void main(String[] args) {
//int的自動拆箱和裝箱只在-128到127范圍中進行,超過該范圍的兩個integer的 == 判斷是會返回false的。
Integer a1 = 128;
Integer a2 = -128;
Integer a3 = -128;
Integer a4 = 128;
System.out.println(a1 == a4);
System.out.println(a2 == a3);
Byte b1 = 127;
Byte b2 = 127;
Byte b3 = -128;
Byte b4 = -128;
//byte都是相等的,因為范圍就在-128到127之間
System.out.println(b1 == b2);
System.out.println(b3 == b4);
//
Long c1 = 128L;
Long c2 = 128L;
Long c3 = -128L;
Long c4 = -128L;
System.out.println(c1 == c2);
System.out.println(c3 == c4);
//char沒有負值
//發(fā)現(xiàn)char也是在0到127之間自動拆箱
Character d1 = 128;
Character d2 = 128;
Character d3 = 127;
Character d4 = 127;
System.out.println(d1 == d2);
System.out.println(d3 == d4);
`結(jié)果`
`false`
`true`
`true`
`true`
`false`
`true`
`false`
`true`
Integer i = 10;
Byte b = 10;
//比較Byte和Integer.兩個對象無法直接比較,報錯
//System.out.println(i == b);
System.out.println("i == b " + i.equals(b));
//答案是false,因為包裝類的比較時先比較是否是同一個類,不是的話直接返回false.
int ii = 128;
short ss = 128;
long ll = 128;
char cc = 128;
System.out.println("ii == bb " + (ii == ss));
System.out.println("ii == ll " + (ii == ll));
System.out.println("ii == cc " + (ii == cc));
結(jié)果
i == b false
ii == bb true
ii == ll true
ii == cc true
//這時候都是true,因為基本數(shù)據(jù)類型直接比較值,值一樣就可以。
通過上面的代碼,我們分析一下自動裝箱與拆箱發(fā)生的時機:
(1)當需要一個對象的時候會自動裝箱,比如Integer a = 10;equals(Object o)方法的參數(shù)是Object對象,所以需要裝箱。
(2)當需要一個基本類型時會自動拆箱,比如int a = new Integer(10);算術(shù)運算是在基本類型間進行的,所以當遇到算術(shù)運算時會自動拆箱,比如代碼中的 c == (a + b);
(3) 包裝類型 == 基本類型時,包裝類型自動拆箱;
需要注意的是:“==”在沒遇到算術(shù)運算時,不會自動拆箱;基本類型只會自動裝箱為對應(yīng)的包裝類型,代碼中最后一條說明的內(nèi)容。
在JDK 1.5中提供了自動裝箱與自動拆箱,這其實是Java 編譯器的語法糖,編譯器通過調(diào)用包裝類型的valueOf()方法實現(xiàn)自動裝箱,調(diào)用xxxValue()方法自動拆箱。自動裝箱和拆箱會有一些陷阱,那就是包裝類型復(fù)用了某些對象。
(1)Integer默認復(fù)用了[-128,127]這些對象,其中高位置可以修改;
(2)Byte復(fù)用了全部256個對象[-128,127];
(3)Short服用了[-128,127]這些對象;
(4)Long服用了[-128,127];
(5)Character復(fù)用了[0,127],Charater不能表示負數(shù);
Double和Float是連續(xù)不可數(shù)的,所以沒法復(fù)用對象,也就不存在自動裝箱復(fù)用陷阱。
Boolean沒有自動裝箱與拆箱,它也復(fù)用了Boolean.TRUE和Boolean.FALSE,通過Boolean.valueOf(boolean b)返回的Blooean對象要么是TRUE,要么是FALSE,這點也要注意。
本文介紹了“真實的”自動裝箱與拆箱,為了避免寫出錯誤的代碼,又從包裝類型的源碼入手,指出了各種包裝類型在自動裝箱和拆箱時存在的陷阱,同時指出了自動裝箱與拆箱發(fā)生的時機。
上面自動拆箱和裝箱的原理其實與常量池有關(guān)。
public void(int a)
{
int i = 1;
int j = 1;
}
方法中的i 存在虛擬機棧的局部變量表里,i是一個引用,j也是一個引用,它們都指向局部變量表里的整型值 1.
int a是傳值引用,所以a也會存在局部變量表。
class A{
int i = 1;
A a = new A();
}
i是類的成員變量。類實例化的對象存在堆中,所以成員變量也存在堆中,引用a存的是對象的地址,引用i存的是值,這個值1也會存在堆中??梢岳斫鉃橐胕指向了這個值1。也可以理解為i就是1.
3 包裝類對象怎么存
其實我們說的常量池也可以叫對象池。
比如String a= new String(“a”).intern()時會先在常量池找是否有“a”對象如果有的話直接返回“a”對象在常量池的地址,即讓引用a指向常量”a”對象的內(nèi)存地址。
public native String intern();
Integer也是同理。
下圖是Integer類型在常量池中查找同值對象的方法。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
所以基本數(shù)據(jù)類型的包裝類型可以在常量池查找對應(yīng)值的對象,找不到就會自動在常量池創(chuàng)建該值的對象。
而String類型可以通過intern來完成這個操作。
JDK1.7后,常量池被放入到堆空間中,這導(dǎo)致intern()函數(shù)的功能不同,具體怎么個不同法,且看看下面代碼,這個例子是網(wǎng)上流傳較廣的一個例子,分析圖也是直接粘貼過來的,這里我會用自己的理解去解釋這個例子:
[java] view plain copy
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
輸出結(jié)果為:
[java] view plain copy
JDK1.6以及以下:false false
JDK1.7以及以上:false true
JDK1.6查找到常量池存在相同值的對象時會直接返回該對象的地址。
JDK 1.7后,intern方法還是會先去查詢常量池中是否有已經(jīng)存在,如果存在,則返回常量池中的引用,這一點與之前沒有區(qū)別,區(qū)別在于,如果在常量池找不到對應(yīng)的字符串,則不會再將字符串拷貝到常量池,而只是在常量池中生成一個對原字符串的引用。
那么其他字符串在常量池找值時就會返回另一個堆中對象的地址。
下一節(jié)詳細介紹String以及相關(guān)包裝類。
具體請見: https://blog.csdn.net/a724888/article/details/80042298
關(guān)于Java面向?qū)ο笕筇匦裕垍⒖迹?/p>
https://blog.csdn.net/a724888/article/details/80033043
https://www.runoob.com/java/java-basic-datatypes.html
https://www.cnblogs.com/zch2126/p/5335139.html
https://blog.csdn.net/jreffchen/article/details/81015884
https://blog.csdn.net/yuhongye111/article/details/31850779
黃小斜是跨考軟件工程的 985 碩士,自學(xué) Java 兩年,拿到了 BAT 等近十家大廠 offer,從技術(shù)小白成長為阿里工程師。
作者專注于 JAVA 后端技術(shù)棧,熱衷于分享程序員干貨、學(xué)習經(jīng)驗、求職心得和程序人生,目前黃小斜的CSDN博客有百萬+訪問量,知乎粉絲2W+,全網(wǎng)已有10W+讀者。
黃小斜是一個斜杠青年,堅持學(xué)習和寫作,相信終身學(xué)習的力量,希望和更多的程序員交朋友,一起進步和成長!
原創(chuàng)電子書:
關(guān)注微信公眾號【黃小斜】后回復(fù)【原創(chuàng)電子書】即可領(lǐng)取我原創(chuàng)的電子書《菜鳥程序員修煉手冊:從技術(shù)小白到阿里巴巴Java工程師》這份電子書總結(jié)了我2年的Java學(xué)習之路,包括學(xué)習方法、技術(shù)總結(jié)、求職經(jīng)驗和面試技巧等內(nèi)容,已經(jīng)幫助很多的程序員拿到了心儀的offer!
程序員3T技術(shù)學(xué)習資源:一些程序員學(xué)習技術(shù)的資源大禮包,關(guān)注公眾號后,后臺回復(fù)關(guān)鍵字 “資料”即可免費無套路獲取,包括Java、python、C++、大數(shù)據(jù)、機器學(xué)習、前端、移動端等方向的技術(shù)資料。
如果大家想要實時關(guān)注我更新的文章以及分享的干貨的話,可以關(guān)注我的微信公眾號【Java技術(shù)江湖】
這是一位阿里 Java 工程師的技術(shù)小站。作者黃小斜,專注 Java 相關(guān)技術(shù):SSM、SpringBoot、MySQL、分布式、中間件、集群、Linux、網(wǎng)絡(luò)、多線程,偶爾講點Docker、ELK,同時也分享技術(shù)干貨和學(xué)習經(jīng)驗,致力于Java全棧開發(fā)!
(關(guān)注公眾號后回復(fù)”Java“即可領(lǐng)取 Java基礎(chǔ)、進階、項目和架構(gòu)師等免費學(xué)習資料,更有數(shù)據(jù)庫、分布式、微服務(wù)等熱門技術(shù)學(xué)習視頻,內(nèi)容豐富,兼顧原理和實踐,另外也將贈送作者原創(chuàng)的Java學(xué)習指南、Java程序員面試指南等干貨資源)
Java工程師必備學(xué)習資源:一些Java工程師常用學(xué)習資源,關(guān)注公眾號后,后臺回復(fù)關(guān)鍵字 “Java”即可免費無套路獲取。
?