日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java里的字符串, String类简单介绍.

發(fā)布時(shí)間:2025/3/20 java 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java里的字符串, String类简单介绍. 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

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


大家都知道c語言里是沒有String 字符串這個(gè)數(shù)據(jù)類型的.

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

例如:

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

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

但是, char e[5] = "abcde" 只是1個(gè)字符數(shù)組, 因?yàn)樗詈?個(gè)元素不是 = "\0" ,如果對(duì)字符數(shù)組e執(zhí)行string.h里面的函數(shù)的話, 就肯定會(huì)出錯(cuò)了.

因?yàn)闄z測不到'\0'字符啊.


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


一, Java里的字符串.

首先聲明:

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


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


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

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


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


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

例如:

字符串是常量?

它們的值不能改?

因?yàn)?.所以..? 這什么邏輯?


在實(shí)際編程當(dāng)中, 我們覺得字符串變量的值可以更改的呀?


本人認(rèn)為, 大家沒必要擔(dān)心自己的理解能力, 中文jdk api的翻譯實(shí)在是很沒有節(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 內(nèi)存)卻可以指向不同的字符串. 是因?yàn)?strong>字符串對(duì)象雖然是不能修改的, 但是它們的地址可以共享.


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

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



1.2 java里字符串的定義

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

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

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


1.2.1 Java里字符串用Unicode編碼

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

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


所以c語言里的某些字符處理函數(shù), 如果參數(shù)傳入1個(gè)漢字可能就會(huì)出錯(cuò), 因?yàn)楫吘棺址恼加脙?nèi)存長度不同.


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

缺點(diǎn)就是比c語言字符處理更加耗內(nèi)存.



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

我之前的博文見過, java程序類似于c語言, 運(yùn)行時(shí)會(huì)把程序占用的內(nèi)存大致分割成幾個(gè)部分.

分別是

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

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

見下圖


一, Java里的字符串.



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

1.3.1 一般的類指向的是變量

關(guān)于這點(diǎn), 需要對(duì)比才能講得清楚.

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

package String_kng;class Human_1{int id;int age;public Human_1(int id, int age){this.id = id;this.age = age;}public String toString(){return "id is " + id + "," + " age is " + age;} }public class String_2{public static void f(){Human_1 h = new Human_1(1,30);Human_1 h2 = h; //System.out.printf("h: %s\n", h.toString()); System.out.printf("h2: %s\n\n", h.toString()); h.id = 3;h.age = 32;System.out.printf("h: %s\n", h.toString()); System.out.printf("h2: %s\n\n", h.toString()); System.out.println( h == h2 );} }
上面例子中定義了1個(gè)Human_1的類, 只有2個(gè)成員id和age.

下面f()中首先實(shí)例化了1個(gè)Human_1的對(duì)象h.

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

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


然后修改h的值,

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

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

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


輸出:

[java] h: id is 1, age is 30[java] h2: id is 1, age is 30[java] [java] h: id is 3, age is 32[java] h2: id is 3, age is 32[java] [java] true

其實(shí)上面例子可以理解成:

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

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


也就是說:

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


畫張圖便于理解:



如上圖可見:

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

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

3. 該內(nèi)存包括兩個(gè)部分, 1個(gè)用于存放成員id的值, 另1個(gè)存放成員age的值.


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

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

意思就是內(nèi)存地址不變的前提下內(nèi)存的內(nèi)容是可變的.


注意, 上面的例子不說是對(duì)象引用h是1個(gè)變量,? 而是說h指向的內(nèi)存是1個(gè)變量

1.3.2 java里的字符串是常量

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

package String_kng;public class String_3{public static void f(){String s = "cat";String s2 = s;System.out.printf("s: %s\n", s); System.out.printf("s2: %s\n", s2); System.out.println(s == s2); s = "dog";System.out.printf("\ns: %s\n", s); System.out.printf("s2: %s\n", s2); System.out.println(s == s2); } }

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

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


這時(shí)輸出s 和 s2的值, 它們當(dāng)然是相等的.

這時(shí)"修改"s的值為"dog"

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

輸出結(jié)果:

[java] s: cat[java] s2: cat[java] true[java] [java] s: dog[java] s2: cat[java] false

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

原因就是這句代碼:

s = "dog";?

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


畫兩張圖:

s的值"修改"前:



由上圖可見:

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

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

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


s的值"修改"為dog后:



由上圖可見:

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

2. 當(dāng)然之前那個(gè)堆區(qū)對(duì)象內(nèi)存和數(shù)據(jù)"cat"的內(nèi)存是由 String s = "cat" 這條語句創(chuàng)建,關(guān)于String類語法機(jī)制后面會(huì)再講。

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

4. 關(guān)鍵是原來數(shù)據(jù)的字符"cat" 并沒有被修改! 也不可能被修改.


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


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

其實(shí)從另外一些方面一也能體現(xiàn)出java字符串不能修改的.

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

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


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

那么原來的內(nèi)存怎么辦呢? 放心, java的內(nèi)存回收機(jī)制會(huì)收拾它們的..



二, Java里的String類.

2.1 java里 String 類的 本質(zhì)

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

但是這個(gè)解釋并沒有指明String類的本質(zhì).


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

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

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

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


畫個(gè)圖:




見上圖, java的String類實(shí)例化1個(gè)對(duì)象后, 會(huì)在堆區(qū)劃分一塊對(duì)象的內(nèi)存, 其中1個(gè)關(guān)鍵成員存放的是數(shù)據(jù)區(qū)字符串的地址.

而下面若干個(gè)方法內(nèi)存, 存放的是該函數(shù)(方法)在代碼區(qū)的2進(jìn)制代碼的地址.



2.2 String類實(shí)例化對(duì)象的第一個(gè)方法. new String("abc")

當(dāng)然, String類的構(gòu)造函數(shù)有很多個(gè)(參數(shù)不同), 但是在coding中,常用的實(shí)例化對(duì)象方法無非是兩種.

第一種就是與其他類一樣, 利用構(gòu)造方法.

String s = new String("abc");
上面的代碼做了下面若干個(gè)事情.

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

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

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


下面是1個(gè)例子:

package String_kng;public class String_4{public static void f(){String s = new String("cat");String s2 = new String("cat");System.out.printf("s: %s\n", s); System.out.printf("s2: %s\n", s2); System.out.println(s == s2); System.out.println(s.equals(s2));} }

上面利用new 實(shí)例化了兩個(gè)對(duì)象s和s2 , 它們所指向的字符串值都是"cat"

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

輸出:

[java] s: cat[java] s2: cat[java] false[java] true

可見用equals 來比較s 和 s2, 它們是相等的, 因?yàn)樗鼈兊膬?nèi)容相同. 而且equals方法在String類里重寫過了.

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

如下圖:


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

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

堆區(qū)有兩個(gè)對(duì)象內(nèi)存, 它們分別保存了各自對(duì)應(yīng)的字符串地址.

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



2.3 String類實(shí)例化對(duì)象的另一個(gè)方法.? = "abc"

事實(shí)上, 我們?cè)诰幊讨行陆?個(gè)字符串更多情況下會(huì)用如下的方式:

String s = "abc";

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

package String_kng;public class String_5{public static String g(){String s4 = "cat";return s4;}public static void f(){String s = new String("cat");String s2 = "cat";String s3 = "cat";System.out.printf("s: %s\n", s); System.out.printf("s2: %s\n", s2); System.out.printf("s3: %s\n", s3); System.out.println(s == s2); System.out.println(s2 == s3); System.out.println(s2 == g()); } }


這個(gè)例子步驟也不復(fù)雜:


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

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

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

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



輸出:

[java] s: cat[java] s2: cat[java] s3: cat[java] false[java] true[java] true

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


所以結(jié)論如下:

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

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

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


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

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

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

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


如下圖:


這個(gè)例子說明了, 在同1個(gè)java程序中, 所有用 " = "abc" " 方式創(chuàng)建的而且具有相同值的多個(gè)String對(duì)象其實(shí)都是同1個(gè)對(duì)象. 因?yàn)樗鼈冎赶蛲粔K堆區(qū)的內(nèi)存.

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



三, String類的常用方法.

下面是應(yīng)付面試的, 大家看看就好.

1.

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


2.

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


3.

public int indexOf(String str)

返回字符串中出現(xiàn)str的第1個(gè)位置


4.

public int indexOf(String str, int fromIndex)

返回字符串中, 從第fromIndex個(gè)字符數(shù)起, 出現(xiàn)str的第1個(gè)位置, 這個(gè)方法是上面方法的重載


5.

public boolean equalsIgnoreCase(String str)

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


6.

public String replace(char oldChar, char newChar)

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


7.

public boolean startsWith(String prefix)

判斷字符串是否以 prefix 開頭


8.

public boolean endsWith(String suffix)

判斷字符產(chǎn)是否以suffix 結(jié)尾


9.

public String subString(int beginIndex)

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


10.

public String subString(int beginIndex, int endIndex)

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


11.

public static String valueOf(...)

注意這個(gè)是靜態(tài)方法. 可以把其他基本數(shù)據(jù)類型轉(zhuǎn)換成String對(duì)象


12.

Integer.parseInt(String s)

這個(gè)是另1個(gè)類Integer 的敬愛函數(shù), 可以把字符串轉(zhuǎn)換成int類型.? 會(huì)拋出異常..





































總結(jié)

以上是生活随笔為你收集整理的Java里的字符串, String类简单介绍.的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。