java数据类型的站位_Java 数据类型在实际开发中应用
在前邊的文章中,我已經(jīng)介紹了Java核心的容器IO等,現(xiàn)在我來說一下java中的數(shù)據(jù)類型。在java中,一切東西皆為對象(這句話意思是java中絕大數(shù)情況都用對象),極少數(shù)不是對象的,也存在與之對應(yīng)的對象(比如基本數(shù)據(jù)類型存在與之對應(yīng)的包裝類,數(shù)組有List對象可以代替)
Java中數(shù)據(jù)類型 主要有“基本數(shù)據(jù)類型”、“String”、“引用類型” (基本的引用類型不多做介紹,在下一篇博文中著重介紹“枚舉”,也算是引用類型的一種)
一:基本數(shù)據(jù)類型
1.1基本數(shù)據(jù)類型的定義
byte、char、int、 float 、double、long...這些屬于java的基本數(shù)據(jù)類型。具體用法可以參照 ?(Java基本數(shù)據(jù)類型總結(jié)?) .在java看來,使用基本類型并不是面向?qū)ο蟮脑O(shè)計,于是提供一些專門的包裝類。實際開發(fā)中,不需要我們考慮到底是用基本類型還是包裝類(Java提供了自動裝箱機制)。當(dāng)然基本類型還是有必要學(xué)習(xí)一下的。
1.1.1按種類了解基本類型
基本類型可以分為三類,字符類型char,布爾類型boolean以及數(shù)值類型byte、short、int、long、float、double。JAVA中的數(shù)值類型不存在無符號的,它們的取值范圍是固定的,不會隨著機器硬件環(huán)境或者操作系統(tǒng)的改變而改變
Java決定了每種簡單類型的大小,并不隨著機器結(jié)構(gòu)的變化而變化。這正是Java程序具有很強移植能力的原因之一。下表列出了Java中定義的簡單類型、占用二進(jìn)制位數(shù)及對應(yīng)的封裝器類。
簡單類型
boolean
byte
char
short
Int
long
float
double
void
二進(jìn)制位數(shù)
1
8
16
16
32
64
32
64
--
封裝器類
Boolean
Byte
Character
Short
Integer
Long
Float
Double
Void
這張表可以簡單的看一下,但不推薦花費太多時間(實際開發(fā)不需要,如果是應(yīng)付考試還是需要記一下的)。因為 Java語言之所以流行就是希望程序員可以消耗更少的心力在語法上,從而省出更多的時間去整理具體的業(yè)務(wù)邏輯。在基本數(shù)據(jù)類型這一塊,Java提供自動裝箱機制,下面簡單介紹一下自動裝箱。
1.1.2Java中自動裝箱
自動裝箱就可以簡單的理解為將基本數(shù)據(jù)類型封裝為對象類型,來符合java的面向?qū)ο蟆1热缒憧梢灾苯影岩粋€int值復(fù)制給一個Integer對象
//聲明一個Integer對象
Integer num = 10;
自動裝箱的時候,存在一個細(xì)節(jié)點就是“對于值從–128到127之間的值,它們被裝箱為Integer對象后,會存在內(nèi)存中被重用,始終只存在一個對象”,測試如下
//在-128~127 之外的數(shù)
Integer num1 = 297;? Integer num2 = 297;
System.out.println("num1==num2: "+(num1==num2));
// 在-128~127 之內(nèi)的數(shù)
Integer num3 = 97;? Integer num4 = 97;
System.out.println("num3==num4: "+(num3==num4));
//測試結(jié)果:num1==num2: false? \n? num3==num4: true
有時候,只能用包裝類,不能用基本數(shù)據(jù)類型,比如集合內(nèi)
1.2 基本數(shù)據(jù)類型的保存位置
基本類型存儲在棧中,處于效率考慮,基本類型保存在棧中。延伸一下,包裝類保存在堆中(注意我之前說過的-127到128之間的包裝類Integer)
1.3 堆棧的簡單介紹(這個和基本數(shù)據(jù)類型沒有太大關(guān)系相當(dāng)于是擴(kuò)展內(nèi)容)
Java的堆是一個運行時數(shù)據(jù)區(qū),不需要程序代碼來顯式的釋放,由垃圾回收來負(fù)責(zé)即可。堆的優(yōu)勢是可以動態(tài)地分配內(nèi)存 大小,生存期也不必事先告訴編譯器(因為是在運行時動態(tài)分配內(nèi)存的,Java的垃圾收集器會自動收走這些不再使用的數(shù)據(jù))。但缺點是,由于要在運行時動態(tài)分配內(nèi)存,存取速度較慢。
棧的優(yōu)勢是,存取速度比堆要快,僅次于寄存器,棧數(shù)據(jù)可以共享。但缺點是,存在棧中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本類型的變量數(shù)據(jù)(int, short, long, byte, float, double, boolean, char)和引用數(shù)據(jù)類型(這個和基本數(shù)據(jù)類型無關(guān),不多做介紹)
1.3.1 數(shù)據(jù)共享簡單介紹
//基本數(shù)據(jù)類型的共享和對象不一樣,對象共享的本質(zhì)上是引用,對象修改會影響另外一個變量,基本類型只是共享的值,當(dāng)值修改時,實際上是讓變量又重新指向了另一個地方
public?static?void?main(String[] args) {?int?a = 5;?int?b = 5;//一定是先找棧里有沒有5,有就讓b也指向5 a?= 6; //先找棧里有沒有6,如果沒有則新建6并讓a指向
System.out.println(b); }
1.3.2 局部變量與成員變量保存位置簡單介紹
對于成員變量和局部變量:成員變量就是方法外部,類的內(nèi)部定義的變量;局部變量就是方法或語句塊內(nèi)部定義的變量。局部變量必須初始化。
形式參數(shù)是局部變量,局部變量的數(shù)據(jù)存在于棧內(nèi)存中。棧內(nèi)存中的局部變量隨著方法的消失而消失。
成員變量存儲在堆中的對象里面,由垃圾回收器負(fù)責(zé)回收
1.class BirthDate {
2.? ? private int day;? //day是成員變量
3.? ? private int month;
4.? ? private int year;
5.? ? public BirthDate(int d, int m, int y) {
6.? ? ? ? day = d;? ?//d是局部變量
7.? ? ? ? month = m;
8.? ? ? ? year = y;
9.? ? }
10.? ? 省略get,set方法………
11.}
1.3.3 java中常量簡單介紹
十六進(jìn)制整型常量:以十六進(jìn)制表示時,需以0x或0X開頭,如0xaa,0X9f。
八進(jìn)制整型常量:八進(jìn)制必須以0開頭,如0123,034。
長整型: ? ? ? ? ? ? 長整型必須以L作結(jié)尾,如9L,342L。
浮點數(shù)常量: ? ? ?由于小數(shù)常量的默認(rèn)類型是double型,所以float類型的后面一定要加f(F)。同樣帶小數(shù)的變量默認(rèn)為double類型。 如:float f;f=1.3f;//必須聲明f。
字符常量: ? ? ? ? 字符型常量需用兩個單引號括起來(注意字符串常量是用兩個雙引號括起來)。Java中的字符占兩個字節(jié)。
1.4 Java基本類型的“類型轉(zhuǎn)換”
簡單類型數(shù)據(jù)間的轉(zhuǎn)換,有兩種方式:自動轉(zhuǎn)換和強制轉(zhuǎn)換,通常發(fā)生在表達(dá)式中或方法的參數(shù)傳遞時。
1.4.1 自動轉(zhuǎn)換:是JVM根據(jù)條件自動幫助我們轉(zhuǎn)換,可以簡單了解一下轉(zhuǎn)換規(guī)則。
當(dāng)一個較"小"數(shù)據(jù)與一個較"大"的數(shù)據(jù)一起運算時,系統(tǒng)將自動將"小"數(shù)據(jù)轉(zhuǎn)換成"大"數(shù)據(jù),再進(jìn)行運算 ?。
這些類型由"小"到"大"分別為 (byte,short,char)--int--long--float—double,這里我們所說的"大"與"小",并不是指占用字節(jié)的多少,而是指表示值的范圍的大小 。所以byte --char--short之間不可以自動轉(zhuǎn)換
1.4.2 強制轉(zhuǎn)換:
將"大"數(shù)據(jù)轉(zhuǎn)換為"小"數(shù)據(jù)時,你必須使用強制類型轉(zhuǎn)換。即你必須采用下面這種語句格式: int n=(int)3.14159/2;可以想象,這種轉(zhuǎn)換肯定可能會導(dǎo)致溢出或精度的下降
表達(dá)式的數(shù)據(jù)類型自動提升 ?eg?所有的byte,short,char型的值將被提升為int型;
包裝類和基本類型轉(zhuǎn)換 :實際上Java存在一個自動裝箱的機制,他可以自動轉(zhuǎn)換。但是,我們可以用構(gòu)造器轉(zhuǎn)為包裝類;用包裝類對象的xxxValue()把包裝類轉(zhuǎn)為基本類型
其它類型間轉(zhuǎn)為字符串:①調(diào)用類的串轉(zhuǎn)換方法:X.toString(); ? ②自動轉(zhuǎn)換:X+""; ?③使用String的方法:String.volueOf(X);
字符串轉(zhuǎn)為其它類型 ?①先轉(zhuǎn)換成相應(yīng)的封裝器實例,再調(diào)用對應(yīng)的方法轉(zhuǎn)換成其它類型 new Double("3.1").doubleValue().或者Double.valueOf("32.1").doubleValue()②靜態(tài)parseXXX方法String s = "1";byte b = Byte.parseByte( s );
Date類與其它數(shù)據(jù)類型的相互轉(zhuǎn)換 :整型和Date類之間并不存在直接的對應(yīng)關(guān)系,只是你可以使用int型為分別表示年、月、日、時、分、秒,這樣就在兩者之間建立了一個對應(yīng)關(guān)系 。具體轉(zhuǎn)換,可能用到format類
1.4.3 備注
只有boolean不參與數(shù)據(jù)類型的轉(zhuǎn)換
二:String類型
在實際開發(fā)中 String使用非常廣泛。于是Java設(shè)計者針對String做了非常多的優(yōu)化來提高效率,這雖然提高了程序的效率,但是在一定程度上也會給我們開發(fā)提高了難度,于是在Thinking in Java中單獨把String當(dāng)作一個章節(jié)。下面我會從整體上總結(jié)一下String,一些具體的方法可以去查詢API(Java API)
2.1 String創(chuàng)建方式
new是按照面向?qū)ο蟮臉?biāo)準(zhǔn)語法,在內(nèi)存使用上存在比較大的浪費。所以String對象的創(chuàng)建是不需要new的(這樣可以提高效率,但是如果用new創(chuàng)建字符串也不會報錯)
2.1.1在這里,延伸一下,java中創(chuàng)建對象的方式一共存在五種:
分別是?new?關(guān)鍵字、Class類的?newInstance?方法、Constructor類的?newInstance?方法、String對象的?clone方法、反序列化機制。但是String對象還有一種特殊的創(chuàng)建方式,就是通過使用?“?或?’?包裹字符序列
public static void main(String[] args)
{
String s = "Hello World!";//實際上當(dāng)""的時候java就創(chuàng)建了該對象
System.out.println(s);
}
下面的代碼詳細(xì)的對比了java的正常創(chuàng)建形式(“”)和 new的區(qū)別 ?(參照自 ?深入理解Java:String? ?),在這里,我推薦一下?String的原理與用法總結(jié)? 。該博主圖畫的還是挺清晰的,一目了然
public static voidmain(String[] args) {
String s1 = "abc";
// ↑ 在字符串池創(chuàng)建了一個對象
String s2 = "abc";
// ↑ 字符串pool已經(jīng)存在對象“abc”(共享),所以創(chuàng)建0個對象,累計創(chuàng)建一個對象
System.out.println("s1 == s2 : " + (s1 ==s2));
// ↑ true 指向同一個對象,
System.out.println("s1.equals(s2) : " +(s1.equals(s2)));
String s3 = new String("abc");
// ↑ 創(chuàng)建了兩個對象,一個存放在字符串池中,一個存在與堆區(qū)中;
// ↑ 還有一個對象引用s3存放在棧中
String s4 = new String("abc");
// ↑ 字符串池中已經(jīng)存在“abc”對象,所以只在堆中創(chuàng)建了一個對象
System.out.println("s3 == s4 : " + (s3 ==s4));
// ↑false s3和s4棧區(qū)的地址不同,指向堆區(qū)的不同地址
System.out.println("s3.equals(s4) : " +(s3.equals(s4)));
// ↑true s3和s4的值相同
System.out.println("s1 == s3 : "+(s1==s3));
//↑false 存放的地區(qū)多不同,一個棧區(qū),一個堆區(qū)
System.out.println("s1.equals(s3) : "+(s1.equals(s3)));
//↑true? 值相同
/**
* 情景三:
* 由于常量的值在編譯的時候就被確定(優(yōu)化)了。
* 在這里,"ab"和"cd"都是常量,因此變量str3的值在編譯時就可以確定。
* 這行代碼編譯后的效果等同于: String str3 = "abcd";
*/String str1 = "ab" + "cd";? //1個對象
String str11 = "abcd";
System.out.println("str1 = str11 : "+ (str1 ==str11));
/**
* 情景四:
* 局部變量str2,str3存儲的是存儲兩個拘留字符串對象(intern字符串對象)的地址
* 第三行代碼原理(str2+str3):
* 運行期JVM首先會在堆中創(chuàng)建一個StringBuilder類,
* 同時用str2指向的拘留字符串對象完成初始化,
* 然后調(diào)用append方法完成對str3所指向的拘留字符串的合并,
* 接著調(diào)用StringBuilder的toString()方法在堆中創(chuàng)建一個String對象,
* 最后將剛生成的String對象的堆地址存放在局部變量str4中
* 而str5存儲的是字符串池中"abcd"所對應(yīng)的拘留字符串對象的地址。
* str4與str5地址當(dāng)然不一樣了
* 內(nèi)存中實際上有五個字符串對象:
*? 三個拘留字符串對象、一個String對象和一個StringBuilder對象。
*/String str2 = "ab";? //1個對象
String str3 = "cd";? //1個對象
String str4 = str2+str3;
String str5 = "abcd";
System.out.println("str4 = str5 : " + (str4==str5)); // false
//↑------------------------------------------------------over
/**
* 情景五:
*? JAVA編譯器對string + 基本類型/常量 是當(dāng)成常量表達(dá)式直接求值來優(yōu)化的。
*? 運行期的兩個string相加,會產(chǎn)生新的對象的,存儲在堆(heap)中
*/String str6 = "b";
String str7 = "a" +str6;
String str67 = "ab";
System.out.println("str7 = str67 : "+ (str7 ==str67));
//↑str6為變量,在運行期才會被解析。
final String str8 = "b";
String str9 = "a" +str8;
String str89 = "ab";
System.out.println("str9 = str89 : "+ (str9 ==str89));
//↑str8為常量變量,編譯期會被優(yōu)化
}
2.1.2 簡單的概括一下:
用“”創(chuàng)建對象的時候,String對象是放到常量池中,只會創(chuàng)建一個,每次都是先去找一下常量池有沒有該字符串
用 new創(chuàng)建對象,會在隊中創(chuàng)建一個對象,然后在棧內(nèi)創(chuàng)建該對象應(yīng)用,每次都是新創(chuàng)建
2.2?String類初始化后是不可變的(immutable)
String類初始化之后不可變,因為java設(shè)計者不希望我們方法傳參是字符串的時候,方法內(nèi)修改會影響外邊的串,所以采取了一種傳遞拷貝的方式(也就是傳值)
String ss = "this is the origen String";
TestString.showString(ss);
public static voidshowString(String s){
System.out.println(s);
}
2.2.1?Java中String不可變是怎么一回事
java中,一旦產(chǎn)生String對象,該對象就不會在發(fā)生變化。但是String另一方面的確提供了修改String的方法。這看起來很矛盾,實際上是我們沒有仔細(xì)的了解那些修改的方法
比如replace(),如果可以看到源碼,可以清楚的看到該方法實際上新產(chǎn)生一個字符串,替換操作是針對新的字符串。(下圖參考自參考Java進(jìn)階01 String類,簡單的表示replace()方法調(diào)用時
s的變化)
下邊的代碼 :我在原字符串的基礎(chǔ)上添加了一句話,然后判斷他們是否相同(如果是同一個對象修改,==輸出結(jié)果應(yīng)該是true)
String s1 = "我";
s1+="我想在加點東西";
system.out.println(s1 == s2)//輸出結(jié)果是false
思考一下 "s1指向的對象中的字符串是什么"(我們潛意識的認(rèn)為s1也會被修改,但是當(dāng)s2 = "s2"時,實際上s2的引用已經(jīng)被修改,它和s1沒關(guān)系了)
String s1 = "s1";
String s2 =s1;
s2 = "s2";//s1指向的對象中的字符串是什么?
System.out.println(s1);//輸出結(jié)果是s1
再重復(fù)一遍,無論是修改字符串的方法還是對字符串賦值,都和普通的對象不同。賦值是讓字符串指向一個新的字符串,方法傳參是copy一份值,傳入進(jìn)去。
再比如說:String str=”kv”+”ill”+” “+”ans”; 就是有4個字符串常量,首先”kv”和”ill”生成了”kvill”存在內(nèi)存中,然后”kvill”又和” ” 生成 “kvill “存在內(nèi)存中,最后又和生成了”kvill ans”;并把這個字符串的地址賦給了str
所以 + 會產(chǎn)生很多臨時變量。下文中會說到StringBuilder 來避免這種情況。不過有一種特殊情況。 "ab"+"cd" 在JVM編譯后和"abcd"一樣
String str1 = "ab" + "cd"; //1個對象
String str11 = "abcd";
System.out.println("str1 = str11 : "+ (str1 == str11));
2.2.2 Java是怎樣做到String的不可變的
在String源碼中 ,使用private final char value[]來實現(xiàn)字符串的存儲,就是因為final,才說String類型是不可變
/** The value is used for character storage. */
private final char value[];
2.3 String的保存位置
String常量是保存在常量池中。JVM中的常量池在內(nèi)存當(dāng)中是以表的形式存在的, 對于String類型,有一張固定長度的CONSTANT_String_info表用來存儲文字字符串值,注意:該表只存儲文字字符串值,不存儲符號引用。說到這里,對常量池中的字符串值的存儲位置應(yīng)該有一個比較明了的理解了。在程序執(zhí)行的時候,常量池會儲存在Method Area,而不是堆中。常量池中保存著很多String對象;?并且可以被共享使用,因此它提高了效率
什么是常量池
常量池指的是在編譯期被確定,并被保存在已編譯的.class文件中的一些數(shù)據(jù)。
除了包含代碼中所定義的各種基本類型(如int、long等等)和對象型(如String及數(shù)組)的常量值(final)還包含一些以文本形式出現(xiàn)的符號引用。
2.4主要的方法
s.length() ? ? ? ? ? ? ? ? 返回s字符串長度
s.charAt(2) ? ? ? ? ? ? ? ? 返回s字符串中下標(biāo)為2的字符
s.substring(0, 4) ? ? ? ? ? ? 返回s字符串中下標(biāo)0到4的子字符串
s.indexOf("Hello") ? ? ? ? ? ?返回子字符串"Hello"的下標(biāo)
s.startsWith(" ") ? ? ? ? ? ? 判斷s是否以空格開始
s.endsWith("oo") ? ? ? ? ? ? ?判斷s是否以"oo"結(jié)束
s.equals("Good World!") ? ? ? ? 判斷s是否等于"Good World!"?==只能判斷字符串是否保存在同一位置。需要使用equals()判斷字符串的內(nèi)容是否相同。
s.compareTo("Hello Nerd!") ? ? ? 比較s字符串與"Hello Nerd!"在詞典中的順序,?返回一個整數(shù),如果<0,說明s在"Hello Nerd!"之前? 如果>0,說明s在"Hello Nerd!"之后 如果==0,說明s與"Hello Nerd!"相等。
s.trim() ? ? ? ? ? ? ? ? ? 去掉s前后的空格字符串,并返回新的字符串
s.toUpperCase() ? ? ? ? ? ? ?將s轉(zhuǎn)換為大寫字母,并返回新的字符串
s.toLowerCase() ? ? ? ? ? ? ?將s轉(zhuǎn)換為小寫,并返回新的字符串
s.replace("World", "Universe") ? ?將"World"替換為"Universe",并返回新的字符串
2.5:簡單概括總結(jié)String中的細(xì)節(jié)點
總體來說,如果你還是對String感到困惑,不如把握住一點就是“ 創(chuàng)建時間”?如果編譯期就知道是啥,會丟到常量池.
單獨使用""引號創(chuàng)建的字符串都是常量,編譯期就已經(jīng)確定存儲到String Pool中;
使用new String("")創(chuàng)建的對象會存儲到heap中,是運行期新創(chuàng)建的;
使用只包含常量的字符串連接符如"aa" + "aa"創(chuàng)建的也是常量,編譯期就能確定,已經(jīng)確定存儲到String Pool中;
使用包含變量的字符串連接符如"aa" + s1創(chuàng)建的對象是運行期才創(chuàng)建的,存儲在heap中;
intern(): String實例調(diào)用該方法可以讓JVM檢查常量池,如果沒有實例的value屬性對應(yīng)的字符串序列,就將本實例放入常量池,如果有則返回常量池中相對應(yīng)的實例的引用而不是當(dāng)前實例的引用
2.6 StringBuilder 和StringBuffer 和 +
首先StringBuilder 和StringBuffer區(qū)別是 StringBuffer線程安全(存在一堆synchronized)
其次,我們推薦用StringBuilder 而不是+ ,雖然+號在jvm中本質(zhì)也是建StringBuilder,但是每s = s+"1";都會引入一個StringBuilder對象
String [] aaa = {"1","2","3"};
for(String s : aaa) {
s+="1";//每循環(huán)一次,都會產(chǎn)生一個StringBuilder對象
}
另外:StringBuilder允許我們在聲明的時候指定大小,避免我們多次分配緩存
三:Java引用類型
Java有 5種引用類型(對象類型):類 接口 數(shù)組 枚舉 標(biāo)注。
new 的對象會放到j(luò)ava堆中,��后把引用放到棧內(nèi),這里不多加敘述
總結(jié)
以上是生活随笔為你收集整理的java数据类型的站位_Java 数据类型在实际开发中应用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: icmp端口_pingtunnel搭建i
- 下一篇: java break递归_【Java】递