Java 數據類型在實際開發中應用

  在前邊的博文中,我已經介紹了Java核心的容器IO等,如今我來講一下java中的數據類型。在java中,一切東西皆爲對象(這句話意思是java中絕大數狀況都用對象),極少數不是對象的,也存在與之對應的對象(好比基本數據類型存在與之對應的包裝類,數組有List對象能夠代替)html

  Java中數據類型 主要有「基本數據類型」、「String」、「引用類型」 (基本的引用類型很少作介紹,在下一篇博文中着重介紹「枚舉」,也算是引用類型的一種)java

一:基本數據類型 

1.1基本數據類型的定義

  byte、char、int、 float 、double、long...這些屬於java的基本數據類型。具體用法能夠參照  (Java基本數據類型總結 ) .在java看來,使用基本類型並非面向對象的設計,因而提供一些專門的包裝類。實際開發中,不須要咱們考慮究竟是用基本類型仍是包裝類(Java提供了自動裝箱機制)。固然基本類型仍是有必要學習一下的。程序員

1.1.1按種類瞭解基本類型

   基本類型能夠分爲三類,字符類型char,布爾類型boolean以及數值類型byte、short、int、long、float、double。JAVA中的數值類型不存在無符號的,它們的取值範圍是固定的,不會隨着機器硬件環境或者操做系統的改變而改變   api

Java決定了每種簡單類型的大小,並不隨着機器結構的變化而變化。這正是Java程序具備很強移植能力的緣由之一。下表列出了Java中定義的簡單類型、佔用二進制位數及對應的封裝器類。 數組

簡單類型緩存

boolean安全

byteoracle

charapp

shortjvm

Int

long

float

double

void

二進制位數

1

8

16

16

32

64

32

64

--

封裝器類

Boolean

Byte

Character

Short

Integer

Long

Float

Double

Void

  這張表能夠簡單的看一下,但不推薦花費太多時間(實際開發不須要,若是是應付考試仍是須要記一下的)。由於 Java語言之因此流行就是但願程序員能夠消耗更少的心力在語法上,從而省出更多的時間去整理具體的業務邏輯。在基本數據類型這一塊,Java提供自動裝箱機制,下面簡單介紹一下自動裝箱。

1.1.2Java中自動裝箱

   自動裝箱就能夠簡單的理解爲將基本數據類型封裝爲對象類型,來符合java的面向對象。好比你能夠直接把一個int值複製給一個Integer對象

//聲明一個Integer對象
Integer num = 10;

   自動裝箱的時候,存在一個細節點就是「對於值從–128到127之間的值,它們被裝箱爲Integer對象後,會存在內存中被重用,始終只存在一個對象」,測試以下

//在-128~127 以外的數
Integer num1 = 297;   Integer num2 = 297;           
System.out.println("num1==num2: "+(num1==num2)); // 在-128~127 以內的數 Integer num3 = 97; Integer num4 = 97; System.out.println("num3==num4: "+(num3==num4)); //測試結果:num1==num2: false \n num3==num4: true 

  有時候,只能用包裝類,不能用基本數據類型,好比集合內

  具體的細節能夠參考這幾位仁兄的博客 (Java 自動裝箱與拆箱(Autoboxing and unboxing)java 自動裝箱與拆箱

1.2 基本數據類型的保存位置

   基本類型存儲在棧中,處於效率考慮,基本類型保存在棧中。延伸一下,包裝類保存在堆中(注意我以前說過的-127到128之間的包裝類Integer)

1.3 堆棧的簡單介紹(這個和基本數據類型沒有太大關係至關因而擴展內容)

  Java的堆是一個運行時數據區,不須要程序代碼來顯式的釋放,由垃圾回收來負責便可。堆的優點是能夠動態地分配內存 大小,生存期也沒必要事先告訴編譯器(由於是在運行時動態分配內存的,Java的垃圾收集器會自動收走這些再也不使用的數據)。但缺點是,因爲要在運行時動態分配內存,存取速度較慢。

  棧的優點是,存取速度比堆要快,僅次於寄存器,棧數據能夠共享。但缺點是,存在棧中的數據大小與生存期必須是肯定的,缺少靈活性。棧中主要存放一些基本類型的變量數據(int, short, long, byte, float, double, boolean, char)和引用數據類型(這個和基本數據類型無關,很少作介紹)

1.3.1 數據共享簡單介紹

//基本數據類型的共享和對象不同,對象共享的本質上是引用,對象修改會影響另一個變量,基本類型只是共享的值,當值修改時,其實是讓變量又從新指向了另外一個地方
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 局部變量與成員變量保存位置簡單介紹

對於成員變量和局部變量:成員變量就是方法外部,類的內部定義的變量;局部變量就是方法或語句塊內部定義的變量。局部變量必須初始化。 

  • 形式參數是局部變量,局部變量的數據存在於棧內存中。棧內存中的局部變量隨着方法的消失而消失。 
  • 成員變量存儲在堆中的對象裏面,由垃圾回收器負責回收
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中常量簡單介紹

  • 十六進制整型常量:以十六進制表示時,需以0x或0X開頭,如0xaa,0X9f。
  • 八進制整型常量:八進制必須以0開頭,如0123,034。
  • 長整型:             長整型必須以L做結尾,如9L,342L。
  • 浮點數常量:      因爲小數常量的默認類型是double型,因此float類型的後面必定要加f(F)。一樣帶小數的變量默認爲double類型。 如:float f;f=1.3f;//必須聲明f。
  • 字符常量:         字符型常量需用兩個單引號括起來(注意字符串常量是用兩個雙引號括起來)。Java中的字符佔兩個字節。

1.4 Java基本類型的「類型轉換」

  簡單類型數據間的轉換,有兩種方式:自動轉換和強制轉換,一般發生在表達式中或方法的參數傳遞時。

1.4.1 自動轉換:是JVM根據條件自動幫助咱們轉換,能夠簡單瞭解一下轉換規則。

  當一個較"小"數據與一個較"大"的數據一塊兒運算時,系統將自動將"小"數據轉換成"大"數據,再進行運算  。

這些類型由"小"到"大"分別爲 (byte,short,char)--int--long--float—double,這裏咱們所說的"大"與"小",並非指佔用字節的多少,而是指表示值的範圍的大小 。因此byte --char--short之間不能夠自動轉換

1.4.2 強制轉換:

 將"大"數據轉換爲"小"數據時,你必須使用強制類型轉換。即你必須採用下面這種語句格式: int n=(int)3.14159/2;能夠想象,這種轉換確定可能會致使溢出或精度的降低

  1.  表達式的數據類型自動提高  eg 全部的byte,short,char型的值將被提高爲int型;
  2.  包裝類和基本類型轉換 :實際上Java存在一個自動裝箱的機制,他能夠自動轉換。可是,咱們能夠用構造器轉爲包裝類;用包裝類對象的xxxValue()把包裝類轉爲基本類型
  3.  其它類型間轉爲字符串:①調用類的串轉換方法:X.toString();   ②自動轉換:X+"";  ③使用String的方法:String.volueOf(X);
  4.  字符串轉爲其它類型  ①先轉換成相應的封裝器實例,再調用對應的方法轉換成其它類型 new Double("3.1").doubleValue().或者Double.valueOf("32.1").doubleValue()②靜態parseXXX方法String s = "1";byte b = Byte.parseByte( s );
  5. Date類與其它數據類型的相互轉換 :整型和Date類之間並不存在直接的對應關係,只是你可使用int型爲分別表示年、月、日、時、分、秒,這樣就在二者之間創建了一個對應關係 。具體轉換,可能用到format類

1.4.3 備註              

    只有boolean不參與數據類型的轉換  

二:String類型

   在實際開發中 String使用很是普遍。因而Java設計者針對String作了很是多的優化來提升效率,這雖然提升了程序的效率,可是在必定程度上也會給咱們開發提升了難度,因而在Thinking in Java中單獨把String看成一個章節。下面我會從總體上總結一下String,一些具體的方法能夠去查詢API(Java API

 

2.1 String建立方式

  new是按照面向對象的標準語法,在內存使用上存在比較大的浪費。因此String對象的建立是不須要new的(這樣能夠提升效率,可是若是用new建立字符串也不會報錯)

 

 2.1.1在這裏,延伸一下,java中建立對象的方式一共存在五種:

  分別是 new 關鍵字、Class類的 newInstance 方法、Constructor類的 newInstance 方法、String對象的 clone方法、反序列化機制。可是String對象還有一種特殊的建立方式,就是經過使用  或  包裹字符序列

 

    public static void main(String[] args)
    {
        String s = "Hello World!";//實際上當""的時候java就建立了該對象
        System.out.println(s); 
}

下面的代碼詳細的對比了java的正常建立形式(「」)和 new的區別  (參照自  深刻理解Java:String   ),在這裏,我推薦一下  String的原理與用法總結  。該博主圖畫的仍是挺清晰的,一目瞭然

public static void main(String[] args) {
        String s1 = "abc"; // ↑ 在字符串池建立了一個對象 String s2 = "abc"; // ↑ 字符串pool已經存在對象「abc」(共享),因此建立0個對象,累計建立一個對象 System.out.println("s1 == s2 : " + (s1 == s2)); // ↑ true 指向同一個對象, System.out.println("s1.equals(s2) : " + (s1.equals(s2))); String s3 = new String("abc"); // ↑ 建立了兩個對象,一個存放在字符串池中,一個存在與堆區中; // ↑ 還有一個對象引用s3存放在棧中 String s4 = new String("abc"); // ↑ 字符串池中已經存在「abc」對象,因此只在堆中建立了一個對象 System.out.println("s3 == s4 : " + (s3 == s4)); // ↑false s3和s4棧區的地址不一樣,指向堆區的不一樣地址 System.out.println("s3.equals(s4) : " + (s3.equals(s4))); // ↑true s3和s4的值相同 System.out.println("s1 == s3 : "+(s1==s3)); //↑false 存放的地區多不一樣,一個棧區,一個堆區 System.out.println("s1.equals(s3) : "+(s1.equals(s3))); //↑true 值相同 /** * 情景三: * 因爲常量的值在編譯的時候就被肯定(優化)了。 * 在這裏,"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首先會在堆中建立一個StringBuilder類, * 同時用str2指向的拘留字符串對象完成初始化, * 而後調用append方法完成對str3所指向的拘留字符串的合併, * 接着調用StringBuilder的toString()方法在堆中建立一個String對象, * 最後將剛生成的String對象的堆地址存放在局部變量str4中
* 而str5存儲的是字符串池中"abcd"所對應的拘留字符串對象的地址。 * str4與str5地址固然不同了 * 內存中實際上有五個字符串對象: * 三個拘留字符串對象、一個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 + 基本類型/常量 是當成常量表達式直接求值來優化的。 * 運行期的兩個string相加,會產生新的對象的,存儲在堆(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爲常量變量,編譯期會被優化 }

2.1.2 簡單的歸納一下:

  用「」建立對象的時候,String對象是放到常量池中,只會建立一個,每次都是先去找一下常量池有沒有該字符串

  用 new建立對象,會在隊中建立一個對象,而後在棧內建立該對象應用,每次都是新建立

2.2 String類初始化後是不可變的(immutable)

  String類初始化以後不可變,由於java設計者不但願咱們方法傳參是字符串的時候,方法內修改會影響外邊的串,因此採起了一種傳遞拷貝的方式(也就是傳值)

 String ss = "this is the origen String";
 TestString.showString(ss);
public static void showString(String s){ System.out.println(s); }

2.2.1 Java中String不可變是怎麼一回事

  java中,一旦產生String對象,該對象就不會在發生變化。可是String另外一方面的確提供了修改String的方法。這看起來很矛盾,其實是咱們沒有仔細的瞭解那些修改的方法

好比replace(),若是能夠看到源碼,能夠清楚的看到該方法實際上新產生一個字符串,替換操做是針對新的字符串。(下圖參考自參考Java進階01 String類,簡單的表示replace()方法調用時

s的變化)

                                  

下邊的代碼 :我在原字符串的基礎上添加了一句話,而後判斷他們是否相同(若是是同一個對象修改,==輸出結果應該是true)

 String s1 = "我";

s1+="我想在加點東西";
system.out.println(s1 == s2)//輸出結果是false

思考一下 "s1指向的對象中的字符串是什麼"(咱們潛意識的認爲s1也會被修改,可是當s2 = "s2"時,實際上s2的引用已經被修改,它和s1不要緊了)

String s1 = "s1";
String s2 = s1; s2 = "s2";//s1指向的對象中的字符串是什麼?
System.out.println(s1);//輸出結果是s1

再重複一遍,不管是修改字符串的方法仍是對字符串賦值,都和普通的對象不一樣。賦值是讓字符串指向一個新的字符串,方法傳參是copy一份值,傳入進去

  再好比說:String str=」kv」+」ill」+」 「+」ans」; 就是有4個字符串常量,首先」kv」和」ill」生成了」kvill」存在內存中,而後」kvill」又和」 」 生成 「kvill 「存在內存中,最後又和生成了」kvill ans」;並把這個字符串的地址賦給了str

因此 + 會產生不少臨時變量。下文中會說到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[]來實現字符串的存儲,就是由於final,才說String類型是不可變

 /** The value is used for character storage. */
    private final char value[];

2.3 String的保存位置

   深刻一點,咱們瞭解一下String保存的位置(能夠參考java+內存分配及變量存儲位置的區別[轉]java中的String類常量池詳解

   String常量是保存在常量池中。JVM中的常量池在內存當中是以表的形式存在的, 對於String類型,有一張固定長度的CONSTANT_String_info表用來存儲文字字符串值,注意:該表只存儲文字字符串值,不存儲符號引用。說到這裏,對常量池中的字符串值的存儲位置應該有一個比較明瞭的理解了。在程序執行的時候,常量池會儲存在Method Area,而不是堆中。常量池中保存着不少String對象; 而且能夠被共享使用,所以它提升了效率

  什麼是常量池

  常量池指的是在編譯期被肯定,並被保存在已編譯的.class文件中的一些數據。

除了包含代碼中所定義的各類基本類型(如int、long等等)和對象型(如String及數組)的常量值(final)還包含一些以文本形式出現的符號引用。

2.4主要的方法   

  • s.length()                 返回s字符串長度
  • s.charAt(2)                 返回s字符串中下標爲2的字符
  • s.substring(0, 4)             返回s字符串中下標0到4的子字符串
  • s.indexOf("Hello")            返回子字符串"Hello"的下標
  • s.startsWith(" ")             判斷s是否以空格開始
  • s.endsWith("oo")              判斷s是否以"oo"結束
  • s.equals("Good World!")         判斷s是否等於"Good World!" ==只能判斷字符串是否保存在同一位置。須要使用equals()判斷字符串的內容是否相同。
  • s.compareTo("Hello Nerd!")       比較s字符串與"Hello Nerd!"在詞典中的順序, 返回一個整數,若是<0,說明s在"Hello Nerd!"以前  若是>0,說明s在"Hello Nerd!"以後 若是==0,說明s與"Hello Nerd!"相等。
  • s.trim()                   去掉s先後的空格字符串,並返回新的字符串
  • s.toUpperCase()              將s轉換爲大寫字母,並返回新的字符串
  • s.toLowerCase()              將s轉換爲小寫,並返回新的字符串
  • s.replace("World", "Universe")    將"World"替換爲"Universe",並返回新的字符串

2.5:簡單歸納總結String中的細節點

  整體來講,若是你仍是對String感到困惑,不如把握住一點就是「 建立時間」 若是編譯期就知道是啥,會丟到常量池.

  1.  單獨使用""引號建立的字符串都是常量,編譯期就已經肯定存儲到String Pool中;

  2.  使用new String("")建立的對象會存儲到heap中,是運行期新建立的;  

  3.  使用只包含常量的字符串鏈接符如"aa" + "aa"建立的也是常量,編譯期就能肯定,已經肯定存儲到String Pool中;

  4.  使用包含變量的字符串鏈接符如"aa" + s1建立的對象是運行期才建立的,存儲在heap中;

  5.  intern(): String實例調用該方法可讓JVM檢查常量池,若是沒有實例的value屬性對應的字符串序列,就將本實例放入常量池,若是有則返回常量池中相對應的實例的引用而不是當前實例的引用

2.6 StringBuilder 和StringBuffer 和 +

  首先StringBuilder 和StringBuffer區別是 StringBuffer線程安全(存在一堆synchronized)

  其次,咱們推薦用StringBuilder 而不是+ ,雖然+號在jvm中本質也是建StringBuilder,可是每s = s+"1";都會引入一個StringBuilder對象

 

  String [] aaa = {"1","2","3"};
  for (String s : aaa) { s+="1";//每循環一次,都會產生一個StringBuilder對象 }

 另外:StringBuilder容許咱們在聲明的時候指定大小,避免咱們屢次分配緩存  

三:Java引用類型

Java有 5種引用類型(對象類型):類 接口 數組 枚舉 標註。

new 的對象會放到java堆中,而後把引用放到棧內,這裏很少加敘述

相關文章
相關標籤/搜索