一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Java里的字符串, String類簡單介紹.

 月影曉風 2014-11-07

String類在java面試中也是1個常見的問題點. 所以也是寫在這里方便以后查閱了.


大家都知道c語言里是沒有String 字符串這個數據類型的.

只能用字符數組的1個特殊形式來表示一個字符串, 就是這個字符數組的最后1元素必須是以'\0'(空) 來結尾的.

例如:

char c[] = "abcd" 是1個字符串, 但是它的長度是5, char[4] = '\0'

char d[6] = "abcde" 也是1個字符串.

但是, char e[5] = "abcde" 只是1個字符數組, 因為它最后1個元素不是 = "\0" ,如果對字符數組e執(zhí)行string.h里面的函數的話, 就肯定會出錯了.

因為檢測不到'\0'字符啊.


當然, 用c語言寫1個字符串容器不難.  而java是由c語言發(fā)展而來, sun公司已經幫我們寫好了1個字符串容器, 這個容器就是String類了.


一, Java里的字符串.

首先聲明:

1.1 字符串跟String類是不同的概念


本文涉及兩個重點,  1個是字符串, 1個是String類. 它們雖然有聯(lián)系, 但是卻是完全不同的兩個概念!


我們可以參考jdk api中文里對String類的解釋:

public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence


String 類代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作為此類的實例實現。
字符串是常量;它們的值在創(chuàng)建之后不能更改。字符串緩沖區(qū)支持可變的字符串。因為 String 對象是不可變的,所以可以共享。


由上面的解析我們見到幾個不易理解的地方:

例如:

字符串是常量?

它們的值不能改?

因為..所以..  這什么邏輯?


在實際編程當中, 我們覺得字符串變量的值可以更改的呀?


本人認為, 大家沒必要擔心自己的理解能力, 中文jdk api的翻譯實在是很沒有節(jié)操的.

上面解釋的最后一句話的原文是這樣的:

Strings are constant; their values cannot be changed after they are created.String buffers support mutablestrings. Because String objects are immutable they can be shared


本人渣翻:

字符串是常量; 它們的值一旦創(chuàng)建不能更改. 然而String類的引用(變量 or 內存)卻可以指向不同的字符串. 是因為字符串對象雖然是不能修改的, 但是它們的地址可以共享.


原文和本人翻譯都有兩種顏色的單詞,  紅色就是指上面第一個概念字符串.  而藍色指的另1個概念String類.

相信即使本人修改了翻譯, 仍然會有人覺得還是不能理解, 請往下看:



1.2 java里字符串的定義

注意這個不是String類的定義哦, 定義如下:

Java里的字符串就是存放于數據區(qū)(靜態(tài)區(qū))以Unicode編碼的字符集合.

可見java里的字符串跟c語言的字符串如下兩個本質上的區(qū)別:


1.2.1 Java里字符串用Unicode編碼

c語言中的字符串里面的字符串是用ASCII編碼的, ASCII只用1個字節(jié)(byte)的內存表示1個字符. 但是1個字節(jié)的內存數量不足以表示全世界那么多種字符.

例如1個漢子就需要2個字節(jié)來表示.


所以c語言里的某些字符處理函數, 如果參數傳入1個漢字可能就會出錯, 因為畢竟字符的占用內存長度不同.


而Unicdoe也叫萬國碼, 它用兩個字節(jié)去表示任何1個字節(jié), 無論是字母還是漢字. 所以利用java來做內存處理更加方便, 跨平臺性非常好.

缺點就是比c語言字符處理更加耗內存.



1.2.2 Java里字符串存放在數據區(qū)(靜態(tài)區(qū)).

我之前的博文見過, java程序類似于c語言, 運行時會把程序占用的內存大致分割成幾個部分.

分別是

stuck(棧區(qū)), Heap(堆區(qū)), Data(數據區(qū))和代碼區(qū) 

其中數據區(qū)用于存放靜態(tài)變量和字符串常量.

見下圖


一, Java里的字符串.



1.3 為什么說java里的字符串是常量, 不可修改的.

1.3.1 一般的類指向的是變量

關于這點, 需要對比才能講得清楚.

這里我們利用1個自定的類來舉個例子:

  1. package String_kng;  
  2.   
  3. class Human_1{  
  4.     int id;  
  5.     int age;  
  6.     public Human_1(int id, int age){  
  7.         this.id = id;  
  8.         this.age = age;  
  9.     }  
  10.     public String toString(){  
  11.         return "id is " + id + ","  + " age is " + age;  
  12.     }  
  13. }  
  14.   
  15. public class String_2{  
  16.     public static void f(){  
  17.         Human_1 h = new Human_1(1,30);  
  18.         Human_1 h2 = h; //  
  19.         System.out.printf("h: %s\n", h.toString());   
  20.         System.out.printf("h2: %s\n\n", h.toString());   
  21.   
  22.         h.id = 3;  
  23.         h.age = 32;  
  24.         System.out.printf("h: %s\n", h.toString());   
  25.         System.out.printf("h2: %s\n\n", h.toString());   
  26.   
  27.         System.out.println( h == h2 );  
  28.     }  
  29. }  

上面例子中定義了1個Human_1的類, 只有2個成員id和age.

下面f()中首先實例化了1個Human_1的對象h.

然后定義另外1個引用h2, 然后把h的地址賦予給h2 ( Human_1 h2 = h)

然后輸出h, h2的值, 它們是一樣的.


然后修改h的值,

再次輸出h h2的值, 發(fā)現h2的值也被修改.

最后用 " == " 來比較h 和 h2所指向的地址.

明顯它們兩者所指向的地址是相同.


輸出:

  1. [java] h: id is 1, age is 30  
  2. [java] h2: id is 1, age is 30  
  3. [java]   
  4. [java] h: id is 3, age is 32  
  5. [java] h2: id is 3, age is 32  
  6. [java]   
  7. [java] true  

其實上面例子可以理解成:

首先用1個容器h2 來保存 h1所指向的地址.

然后修改h1的值, 最后h1的地址沒變化.


也就是說:

h這個對象雖然值被修改了, 但是指向的內存地址沒有變化, 變的是該內存的內容(值)


畫張圖便于理解:



如上圖可見:

1  對象名h 和 h2 本身是都是局部變量, 位于棧區(qū)中, 里面存放的是1個地址.

2. 該地址指向的是堆區(qū)的一塊內存, 這塊內存是用new Human()劃分出來的. 而且把頭部地址賦予對象名h

3. 該內存包括兩個部分, 1個用于存放成員id的值, 另1個存放成員age的值.


可見, 無論對象h成員的值如何改變, 變的只是堆區(qū)內存的存放內容, 而堆區(qū)內存地址是沒變化的. 對象引用h的指向也沒有變化.

我們一般把這種內存地址不變, 值可以改變的東西成為變量.

意思就是內存地址不變的前提下內存的內容是可變的.


注意, 上面的例子不說是對象引用h是1個變量,  而是說h指向的內存是1個變量

1.3.2 java里的字符串是常量

將上面的例子改一下, 把Human_1類改成String類:

  1. package String_kng;  
  2.   
  3. public class String_3{  
  4.     public static void f(){  
  5.         String s = "cat";  
  6.         String s2 = s;  
  7.   
  8.         System.out.printf("s: %s\n", s);   
  9.         System.out.printf("s2: %s\n", s2);   
  10.         System.out.println(s == s2);   
  11.   
  12.         s = "dog";  
  13.         System.out.printf("\ns: %s\n", s);   
  14.         System.out.printf("s2: %s\n", s2);   
  15.         System.out.println(s == s2);   
  16.     }  
  17. }  

邏輯跟上面的例子基本沒區(qū)別, 也是首先實例化1個String對象s, 它的值是s;

然后將s所指向的地址保存在另個引用s2.


這時輸出s 和 s2的值, 它們當然是相等的.

這時"修改"s的值為"dog"

再輸出s 和 s2的值, 卻發(fā)現s的值變成dog了, 但是s2的值還是cat..  而且它們的所指向的地址也不再相等.

輸出結果:

  1. [java] s: cat  
  2. [java] s2: cat  
  3. [java] true  
  4. [java]   
  5. [java] s: dog  
  6. [java] s2: cat  
  7. [java] false  

為什么s 和 s2所指向的地址一開始是相等的, 一旦s的修改為dog后, s 和 s2所指向的地址就不等呢.

原因就是這句代碼:

s = "dog"; 

并不是修改s所指向的內存地址, 而是改變了s的指向, 也就是修改了s的所指向的地址啊.


畫兩張圖:

s的值"修改"前:



由上圖可見:

1. String類也是java的類, 所以它的實例化對象也需要在堆區(qū)劃分內存。

2. 兩個對象引用s 和 s2這時都指向了堆區(qū)同1塊對象內存。所以它們的所指向地址是相等的。

3. 字符串真正的地址不是再堆區(qū)中, 是在數據區(qū)中的。 而堆區(qū)對象內存中有其中1個對象成員保存了該字符串在數據區(qū)的真正地址。


s的值"修改"為dog后:



由上圖可見:

1. s = "dog" 并不是修改s所指向的內容. 而是在堆區(qū)和數據區(qū)各劃分了1個新的內存. 其中數據區(qū)劃分1個新的字符串"dog" , 堆區(qū)劃分1個新的String對象內存, 保存了dog的字符串地址.

2. 當然之前那個堆區(qū)對象內存和數據"cat"的內存是由 String s = "cat" 這條語句創(chuàng)建,關于String類語法機制后面會再講。

3. s = "dog" 不但在數據區(qū)和堆區(qū)都各自創(chuàng)建1個新內存, 而且還改變了自己所指向的地址, 所以這時s 和 s2 不再相等.

4. 關鍵是原來數據的字符"cat" 并沒有被修改! 也不可能被修改.


我們一般把這種內存值不能改變, 只能通過引用去指向另1塊的東西叫做常量.


1.3.3 java里字符串不能修改的一些小結.

其實從另外一些方面一也能體現出java字符串不能修改的.

例如一些java的內部類, 如Calendar(日期)一般都會提供一些setXXXX的方法讓程序猿去修改對應的值. 例如 setYear(), setDate().等等

而String類是沒有這類setXXXX方法.


雖然字符串在數據區(qū)中的內存不能修改, 但是我們可以為String類的對象指向另一塊內存. 所以這個特性在編程造成的影響不大.

那么原來的內存怎么辦呢? 放心, java的內存回收機制會收拾它們的..



二, Java里的String類.

2.1 java里 String 類的 本質

String類的書面解釋在本文的1.1 章里提高過了, 是1個用于字符串的類.

但是這個解釋并沒有指明String類的本質.


我們知道, Java類的本質大致上可以理解為 成員(屬性) 和 方法的集合體.

String類也一樣, 只不過String類有1個關鍵的成員, 這個成員保存著數據區(qū)的某個字符串的內存地址. 可以理解為1個指針.

而String類的方法是一些對對應字符串的查詢方法(例如indexOf(), charAt()等). 注意, 并沒有對這個字符串進行修改的方法哦, 字符串是常量, 不能修改.

雖然String類不能修改字符串, 但是上面保存字符串地址的成員卻是可以被改變的, 也就是說String類的對象可以指向另1個字符串.


畫個圖:




見上圖, java的String類實例化1個對象后, 會在堆區(qū)劃分一塊對象的內存, 其中1個關鍵成員存放的是數據區(qū)字符串的地址.

而下面若干個方法內存, 存放的是該函數(方法)在代碼區(qū)的2進制代碼的地址.



2.2 String類實例化對象的第一個方法. new String("abc")

當然, String類的構造函數有很多個(參數不同), 但是在coding中,常用的實例化對象方法無非是兩種.

第一種就是與其他類一樣, 利用構造方法.

  1. String s = new String("abc");  

上面的代碼做了下面若干個事情.

1. 在數據區(qū)中劃分一塊內存存放字符串, 值是"abc", 這塊內存一旦創(chuàng)建, 值"abc" 不能被修改.

2. 在堆區(qū)劃分1塊對象內存, 其中小塊用于存放上面字符串的地址, 另一些用于存放函數指針.

3. 在棧區(qū)劃分一塊內存, 存放上面堆區(qū)的頭部地址.


下面是1個例子:

  1. package String_kng;  
  2.   
  3. public class String_4{  
  4.     public static void f(){  
  5.         String s = new String("cat");  
  6.         String s2 = new String("cat");  
  7.   
  8.         System.out.printf("s: %s\n", s);   
  9.         System.out.printf("s2: %s\n", s2);   
  10.         System.out.println(s == s2);   
  11.         System.out.println(s.equals(s2));  
  12.     }  
  13. }  

上面利用new 實例化了兩個對象s和s2 , 它們所指向的字符串值都是"cat"

然后用 "==" 和 equals來比較兩者

輸出:

  1. [java] s: cat  
  2. [java] s2: cat  
  3. [java] false  
  4. [java] true  

可見用equals 來比較s 和 s2, 它們是相等的, 因為它們的內容相同. 而且equals方法在String類里重寫過了.

而用 "==" 比較的是兩個對象s 和 s2所指向的地址, 它們所指向的地址是不同的.

如下圖:


亦即系講, 兩個new語句分別在數據區(qū)和堆區(qū)各自都劃分2個內存.

數據區(qū)中有兩個字符串內存, 它們的值是一樣的都是"cat".

堆區(qū)有兩個對象內存, 它們分別保存了各自對應的字符串地址.

而stuck區(qū)中兩個s1 s2 保存了各自的堆區(qū)內存地址.  這兩個地址明顯是不同的. 也就是 s == s2 返回false的原因.



2.3 String類實例化對象的另一個方法.  = "abc"

事實上, 我們在編程中新建1個字符串更多情況下會用如下的方式:

  1. String s = "abc";  

這種方式更上面那種有什么區(qū)別呢?

  1. package String_kng;  
  2.   
  3. public class String_5{  
  4.     public static String g(){  
  5.         String s4 = "cat";  
  6.         return s4;  
  7.     }  
  8.   
  9.     public static void f(){  
  10.         String s = new String("cat");  
  11.         String s2 = "cat";  
  12.         String s3 = "cat";  
  13.         System.out.printf("s: %s\n", s);   
  14.         System.out.printf("s2: %s\n", s2);   
  15.         System.out.printf("s3: %s\n", s3);   
  16.           
  17.         System.out.println(s == s2);   
  18.         System.out.println(s2 == s3);   
  19.         System.out.println(s2 == g());   
  20.     }  
  21. }  



這個例子步驟也不復雜:


首先f()方法里 利用第一種方法實例化了1個值為"cat"的對象s

然后里利用第二種方法 又 創(chuàng)建了兩個String 對象s2 和 s3, 它們的值都是"cat".

然后用"==" 來比較它們.

然后f()方法調用g()方法, g()方法利用第二方式實例化了1個值為"cat"的String 對象s4
最后用 "==" 比較s2 和 s4 的地址.



輸出:

  1. [java] s: cat  
  2. [java] s2: cat  
  3. [java] s3: cat  
  4. [java] false  
  5. [java] true  
  6. [java] true  

由結果得知, s4 和 s2 和 s3的地址是相同的! 而由第一種方法創(chuàng)建的s 跟前面三者地址不同.


所以結論如下:

利用 = "cat" 方式創(chuàng)建1個String對象時, java 首先會檢測當前進程的數據區(qū)是否有1個以相同方式創(chuàng)建的值是一樣的字符串存在.

如果無, 則類似 new Sring("cat")方式, 在數據區(qū)和堆區(qū)都各自劃分一塊新內存, 用于該創(chuàng)建的對象.

如果有, 則直接把該對象的地址指向 已存在的堆區(qū)內存地址.


也就是講, 在f() 里的String s2 = "cat" 相當于執(zhí)行了 String s2 = new String("cat");

而在f()里的 String   s3 = "cat" 相當執(zhí)行了String s3 = s2;

而在g()里, 理論上g()是不能訪問f()里的 局部變量的, 但是g()還是檢測到數據區(qū)存在用相同方式創(chuàng)建而且值1個樣的字符串.

所以s4 也指向了堆區(qū)的那一塊內存.


如下圖:


這個例子說明了, 在同1個java程序中, 所有用 " = "abc" " 方式創(chuàng)建的而且具有相同值的多個String對象其實都是同1個對象. 因為它們指向同一塊堆區(qū)的內存.

由于這種特性, 所以這種用" = "abc"" 方式創(chuàng)建的對象十分適合做 synchronized對象鎖 要鎖的對象.   不用擔心鎖的是兩個不同的對象導致 多線程同步失敗.



三, String類的常用方法.

下面是應付面試的, 大家看看就好.

1.

public char charAt(int index) //返回字符串中第index個字符


2.

public int length()  //返回字符串的長度


3.

public int indexOf(String str)

返回字符串中出現str的第1個位置


4.

public int indexOf(String str, int fromIndex)

返回字符串中, 從第fromIndex個字符數起, 出現str的第1個位置, 這個方法是上面方法的重載


5.

public boolean equalsIgnoreCase(String str)

忽略大小寫, 比較兩個字符是否相等.


6.

public String replace(char oldChar, char newChar)

返回1個新字符串, 該新字符串內的oldChar被newChar替換掉, 注意舊字符串沒有被修改.


7.

public boolean startsWith(String prefix)

判斷字符串是否以 prefix 開頭


8.

public boolean endsWith(String suffix)

判斷字符產是否以suffix 結尾


9.

public String subString(int beginIndex)

截取從第beginIndex個字符開始到最后1個字符, 返回1個新字符串


10.

public String subString(int beginIndex, int endIndex)

截取從第beginIndex個字符開始, 第endIndex個字符, 返回1個新字符串, 是上面方法的重載


11.

public static String valueOf(...)

注意這個是靜態(tài)方法. 可以把其他基本數據類型轉換成String對象


12.

Integer.parseInt(String s)

這個是另1個類Integer 的敬愛函數, 可以把字符串轉換成int類型.  會拋出異常..





































    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    色哟哟哟在线观看视频| 欧美一级黄片欧美精品| 亚洲中文字幕在线乱码av| 日韩熟妇人妻一区二区三区 | 美女被后入视频在线观看| 加勒比人妻精品一区二区| 国产成人精品午夜福利| 久久精品蜜桃一区二区av| 国产超碰在线观看免费| 黄色美女日本的美女日人| 日韩在线精品视频观看| 欧美日韩在线第一页日韩| 亚洲一区二区三区四区| 国产日韩久久精品一区| 国产av一区二区三区麻豆| 91久久精品国产成人| 粉嫩内射av一区二区| 一区二区日韩欧美精品| 91人人妻人人爽人人狠狠| 一区二区三区欧美高清| 男人和女人干逼的视频| 欧美性欧美一区二区三区| 91人人妻人人爽人人狠狠| 国产午夜福利片在线观看| 国产丝袜美女诱惑一区二区| 精品一区二区三区不卡少妇av| 日韩精品在线观看一区| 欧美一级片日韩一级片 | 成人三级视频在线观看不卡| 黑丝袜美女老师的小逼逼| 欧美一区日韩二区亚洲三区| 日本午夜福利视频免费观看| 嫩草国产福利视频一区二区| 国产在线观看不卡一区二区| 中文字幕一区二区熟女| 亚洲精品福利入口在线| 色婷婷在线视频免费播放| av在线免费观看一区二区三区| 亚洲黄香蕉视频免费看| 区一区二区三中文字幕| 中国黄色色片色哟哟哟哟哟哟|