程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> java中的字符串相關知識整理,java字符串相關知識

java中的字符串相關知識整理,java字符串相關知識

編輯:JAVA綜合教程

java中的字符串相關知識整理,java字符串相關知識


字符串為什麼這麼重要

寫了多年java的開發應該對String不陌生,但是我卻越發覺得它陌生。每學一門編程語言就會與字符串這個關鍵詞打不少交道。看來它真的很重要。

字符串就是一系列的字符組合的串,如果寫過C/C++的應該就了解,在字符串的操作上會有許多操作的函數與類,用於簡化代碼的開發。一方面是因為字符串在代碼中會頻繁用到,另一方面是因為字符串的操作非常麻煩。

最初我知道String的特殊待遇就是在delphi中,因為String在delphi裡是一個關鍵字存在,與其他的基本類型是不一樣的。那時就了解到了許多相關的知識。在java/.net也都對string做了專門的處理,可見重要性。

正因為字符串在程序中用的多,而且操作也多這就會帶來內存占用與性能的問題,所需要特殊的關照一下,想象一下一個日志記錄系統一天時間得用上多少字符串變量。

了解一下java中的String

java中提供了String類支持字符串的功能,畢竟字符串本質就是一堆字符的組合,那麼就來看看它有什麼特點吧。

  • String的特點

String把字符串還是存放在一個char數組中的,數據的操作圍繞它展開,但有點特別的地方,代碼如下

private final char value[];

可以發現這個char value[]是加了final的,也就是說一旦創建了值就不可變。這樣就會導致每一次創建String只會有一個值,再對其進行字符串操作也必須生成新的值。java對這個處理使用了字符串常量池的概念。就是把字符串丟到一個池裡,如果相同就用相同的。當然這也有個前提,就是要用下面的方式

String s = "abc";

這樣做的時候jvm會在編譯期就確定了,在運行時會先在常量池裡查找是否有"abc",沒有就添加並返回,有的話返回常量池的對象。這樣做的好處是對於相同的字符串就不需要重復創建啦。 但是如果使用下面的代碼

String s1 = new String("abc");

這個時候情景就變了,這裡jvm會在堆棧裡創建一個對象s1,只不過s1裡的value也是指向"abc"的。後面在看字符串比較的時候會發現區別。

  • 字符串比較 看一段代碼:
String s = "abc";
String s1 = "abc";

if (s == s1) {
    System.out.println("s == s1");
}

問:這時s==s1嗎?

答案是相等的,為什麼呢?其實jvm會在s1創建時去常量區查找是否有相同值的字符串,如果有就返回給s1,這樣s1就和s指向了同一個字符串,所以是相等的。

但是還有一種情況就不一樣,

String s = "abc";
String s3 = new String("abc");
if (s == s3) {
	System.out.println("s == s3");
}
else {
    System.out.println("s != s3");
}

這個時候應該print出s != s3,這是因為new一個String對象後確實會創建一個新的變量。所以使用==比較的話自然就返回false了。

用到equals比較呢?

String s = "abc";
String s2 = new String("abc");
if (s.equals(s2)) {
	System.out.println("s = s2");
}
else {
    System.out.println("s != s2");
}

打印是s = s2,因為==是用於比較兩個地址,而equals是用於比較兩個變量的值。可以看一下equals的代碼

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

在equals中,先是比較是否地址相同,如果不相同比較value,因為value都是"abc"自然就返回true。

  • intern方法

String裡有一個intern方法,我們可以先試一下面的代碼。

String s = "abc";
String s3 = new String("abc");
if (s.intern() == s3.intern()) {
	System.out.println("s.intern = s3.intern");
}
else {
    System.out.println("s.intern != s3.intern");
}

還是上面的s和s3,如果使用各自的intern方法返回的值比較則會輸出s.intern = s3.intern。找了找資料結合注釋了解到,這個intern方法其實是從字符串常量池裡返回當前字符串,如果當前字符串已經存在了則返回當前字符串,如果當前字符串不存在,則將當前字符串放入常量池再返回。

有了這個解釋就明白了,s和s3都通過intern返回的那麼都是常量池裡的"abc"咯,所以intern比較時是相等的。

認識一下StringBuffer和StringBuilder

  • StringBuffer和StringBuilder哪一個是線程安全的?

面試時遇到的這個問題,我突然有點懵,沒太注意過這兩個類,而且印象中java裡只有一個StringBuffer呀?回來看了一下代碼原來StringBuffer是線程安全的,也就是在字符串操作的方法上都有synchronized。

於是打開代碼注釋發現是Jdk1.5才開始有的StringBuilder,而且在後面版本加了個不加鎖的類,看樣子是解決非並發場景下的效率問題,不加鎖對於操作大字符串還是有性能提升的。

嗯,不錯,get了一個小知識。

出於好奇看了一下這兩個類的代碼,與String真有些類似,只不過這時的chat[] 已經是不帶final的咯,這樣就避免了String類操作時產生一堆字符串對象的問題。

char[] value;
  • StringBuffer和StringBuilder的作用

既然已經有了String,那這兩個家伙有什麼用呢?其實問題還是要和String的原理有關系。因為String是通過常量池管理的,這樣解決的是相同字符串重復創建的問題,但大部分字符串都是不一樣的,特別是在做字符串拼接操作時,如果用String的+進行拼接就會產生大量的字符串常量,非常的消耗性能與空間。

為解決這個問題就用到StringBuffer,本質上也就是通過一個可變的字符序列,在字符串操作時不需要生成新的對象,從而提升內存使用。

看看StringBuffer是怎麼提升這個拼接性能的吧。 查看StringBuffer/StringBuilder的代碼(JDK1.5+)發現它們都繼承於AbstractStringBuilder。很多的代碼其實都是在AbstractStringBuilder裡完成的。因為這個問題由拼接引出的,在此我們就主要關注一下append方法吧。

public AbstractStringBuilder append(String str) {
    if (str == null)
        return appendNull();
    int len = str.length();
    ensureCapacityInternal(count + len);//確定容量
    str.getChars(0, len, value, count);//取出str的字符放入到value數組中
    count += len;//count累加
    return this;
}

代碼還是比較清楚的,整個過程最重要的就是使用String的getChars方法將str的值寫入到當前對象的value中。而String的getChars方法如下:

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
    if (srcBegin < 0) {
        throw new StringIndexOutOfBoundsException(srcBegin);
    }
    if (srcEnd > value.length) {
        throw new StringIndexOutOfBoundsException(srcEnd);
    }
    if (srcBegin > srcEnd) {
        throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
    }
    System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}

可以看出最終是做了一個數組的復制,因為在AbstractStringBuilder中的value是個可變的char數組,這樣的話對於字符串操作只需要在char數組上進行即可。不會像String那樣生成新對象,所以說自然就變的高效了。

 

注:此文章為原創,歡迎轉載,請在文章頁面明顯位置給出此文鏈接! 若您覺得這篇文章還不錯請點擊下右下角的推薦,非常感謝! http://www.cnblogs.com/5207/

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved