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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java - String源码解析及常见面试问题

發布時間:2025/3/21 java 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java - String源码解析及常见面试问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • Pre
  • Q1: String 是如何實現的?
  • Q2: String 有哪些重要的方法?
    • 構造函數
    • equals()
    • compareTo()
    • 【equals() vs compareTo() 】
    • 其他重要方法
  • Q3: 為什么 String 類型要用 final 修飾
  • Q4: == 和 equals 的區別是什么
  • Q5: String 和 StringBuilder、StringBuffer 有什么區別
  • Q6: String 類型在 JVM中是如何存儲的?編譯器對 String 做了哪些優化

Pre

Java Version : 主流版本JDK 8


Q1: String 是如何實現的?

看到了吧 , 底層存儲是 char 數組

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {// the value is used for character storage 存儲字符串的值private final char value[];// Cache the hash code for the string 緩存字符串的 hash codeprivate int hash; // Default to 0// ...... }

Q2: String 有哪些重要的方法?

構造函數

挑幾個比較重要的

// String 為參數的構造方法 public String(String original) {this.value = original.value;this.hash = original.hash; } // char[] 為參數構造方法 public String(char value[]) {this.value = Arrays.copyOf(value, value.length); } // StringBuffer 為參數的構造方法 public String(StringBuffer buffer) {synchronized(buffer) {this.value = Arrays.copyOf(buffer.getValue(), buffer.length());} } // StringBuilder 為參數的構造方法 public String(StringBuilder builder) {this.value = Arrays.copyOf(builder.getValue(), builder.length()); }

這里需要提一下的是: 以 StringBuffer 和 StringBuilder 為參數的構造函數容易被忽略,因為String 、 StringBuffer、StringBuilder 這三種數據類型, 通常都是單獨使用的哇。 知道就行,反正平常也不這么寫

還有其他構造函數 ,大家可以自行看一下


equals()

比較兩個字符串是否相等

來看下源碼

public boolean equals(Object anObject) {// 如果是對象引用,直接返回true if (this == anObject) {return true;}// 類型判斷 如果不是String類型則直接返回 falseif (anObject instanceof String) {String anotherString = (String)anObject;int n = value.length;if (n == anotherString.value.length) {// 把兩個字符串都轉換為 char 數組對比char v1[] = value;char v2[] = anotherString.value;int i = 0;while (n-- != 0) { // 循環比對兩個字符串的每一個字符if (v1[i] != v2[i]) // 如果其中有一個字符不相等就直接返回false,否則繼續對比,直接到結束return false;i++;}return true;}}return false;}

equals() 是String 類型重寫的 Object 中的 方法,Object#equals() 方法需要傳遞一個 Object 類型的參數值所以才有了上面的instanceof 類型判斷 。 當判斷參數為 String 類型之后,會循環對比兩個字符串中的每一個字符,當所有字符都相等時返回 true,否則則返回 false。

【Object#equals()】

public boolean equals(Object obj) {return (this == obj); // 僅判斷的對象引用,即比較的是對象在內存中的地址}

【instanceof 用法】

Object a= "123"; Object b= 123; System.out.println(a instanceof String); // true System.out.println(b instanceof String); // false

另外還有一個 equalsIgnoreCase(), 忽略字符串的大小寫之后進行字符串對比。


compareTo()

比較兩個字符串

public int compareTo(String anotherString) {int len1 = value.length;int len2 = anotherString.value.length;int lim = Math.min(len1, len2); // 取兩個字符串中長度最短的那個字符串的長度 char v1[] = value;char v2[] = anotherString.value;int k = 0;while (k < lim) { // 對比每一個字符char c1 = v1[k];char c2 = v2[k];if (c1 != c2) {// 有字符不相等時返回差值 return c1 - c2; }k++;}return len1 - len2;}

從源碼總可以看到compareTo() 方法會循環對比所有的字符,當兩個字符串中有任意一個字符不相同時,則 return c1 - c2。

舉個例子

“53334433”.compareTo(“3”) ----> 2 【取最小長度,第一個字符 5 和 3 比,轉成char 比較, 不相等 返回 5 - 3 = 2】

再來個例子: 兩個字符串分別存儲的是 1 和 2,返回的值是 -1;如果存儲的是 1 和 1,則返回的值是 0 ,如果存儲的是 2 和 1,則返回的值是 1。

還有個compareToIgnoreCase 忽略大小寫后比較兩個字符串。


【equals() vs compareTo() 】

可以看出 compareTo() 方法和 equals() 方法都是用于比較兩個字符串的,但它們有兩點不同:

  • equals() 可以接收一個 Object 類型的參數,而 compareTo() 只能接收一個 String 類型的參數
  • equals() 返回值為 Boolean,而 compareTo() 的返回值則為 int

它們都可以用于兩個字符串的比較,當 equals() 方法返回 true 時,或者是 compareTo() 方法返回 0 時,則表示兩個字符串完全相同


其他重要方法

  • indexOf():查詢字符串首次出現的下標位置
  • lastIndexOf():查詢字符串最后出現的下標位置
  • contains():查詢字符串中是否包含另一個字符串
  • toLowerCase():把字符串全部轉換成小寫
  • toUpperCase():把字符串全部轉換成大寫
  • length():查詢字符串的長度
  • trim():去掉字符串首尾空格
  • replace():替換字符串中的某些字符
  • split():把字符串分割并返回字符串數組
  • join():把字符串數組轉為字符串

Q3: 為什么 String 類型要用 final 修飾

從源碼中可以知道String是final修飾的?

為啥子嘞?

高司令以前回答過: 他會更傾向于使用 final,因為它能夠緩存結果,當你在傳參時不需要考慮誰會修改它的值;如果是可變類的話,則有可能需要重新拷貝出來一個新值進行傳參,這樣在性能上就會有一定的損失。

String 類設計成不可變的另一個原因是安全,當你在調用其他方法時,比如調用一些系統級操作指令之前,可能會有一系列校驗,如果是可變類的話,可能在你校驗過后,它的內部的值又被改變了,這樣有可能會引起嚴重的系統崩潰問題,這是迫使 String 類設計成不可變類的一個重要原因。

總之,使用 final 修飾的第一個好處是安全;第二個好處是高效

我們以JVM中的常量池來舉個例子

String s1 = "java"; String s2 = "java";

只有字符串是不可變時,我們才能實現字符串常量池。

字符串常量池可以為我們緩存字符串,這樣的話不用每次都去開辟一塊內存地址存放,自然就提高了運行效率。

如果String是可變的,那字符串常量池就歇菜了。。。。。


Q4: == 和 equals 的區別是什么

【==】

  • 對于基本數據類型來說, 比較 “值”是否相等的
  • 對于引用類型來說, 比較引用地址是否相同的

Object#equals() 其實就是 ==

public boolean equals(Object obj) {return (this == obj); }

String#equal這是重寫了父類Object的equals方法,把它修改成了比較兩個字符串的值是否相等,分析如上。


Q5: String 和 StringBuilder、StringBuffer 有什么區別

簡單來說:

  • String 不可變 ,正是因為不可變,所以字符串在拼接時,效率低,所以才有了下面兩個
  • StringBuffer 線程安全
  • StringBuilder 線程不安全

String 類型是不可變的,所以在字符串拼接的時候如果使用 String 的話性能會很低。

因此我們就需要使用另一個數據類型 StringBuffer,它提供了 append 和 insert 方法可用于字符串的拼接,它使用 synchronized 來保證線程安全

@Override public synchronized StringBuffer append(Object obj) {toStringCache = null;super.append(String.valueOf(obj));return this; } @Override public synchronized StringBuffer insert(int offset, String str) {toStringCache = null;super.insert(offset, str);return this; }

因為它使用了 synchronized 來保證線程安全,所以性能不是很高。

于是在 JDK 1.5 就有了 StringBuilder,它同樣提供了 append 和 insert 的拼接方法,但它沒有使用 synchronized 來修飾,因此在性能上要優于 StringBuffer,所以在非并發操作的環境下可使用 StringBuilder 來進行字符串拼接。

@Overridepublic StringBuilder append(String str) {super.append(str);return this;}@Overridepublic StringBuilder insert(int offset, String str) {super.insert(offset, str);return this;}

當然了,append 和 insert的方法入參有很多,這里僅僅列舉出了一個,主要是讓你看下 synchronized實現上的區別。


Q6: String 類型在 JVM中是如何存儲的?編譯器對 String 做了哪些優化

String 常見的創建方式有兩種

  • new String()
  • 直接賦值

直接賦值的方式會先去字符串常量池中查找是否已經有此值,如果有則把引用地址直接指向此值,否則會先在常量池中創建,然后再把引用指向此值;

new String() 一定會先在堆上創建一個字符串對象,然后再去常量池中查詢此字符串的值是否已經存在,如果不存在會先在常量池中創建此字符串,然后把引用的值指向此字符串

舉個例子

String s1 = new String("Java"); String s2 = s1.intern(); String s3 = "Java"; System.out.println(s1 == s2); // ------> false System.out.println(s2 == s3); // ------> true

JDK 1.7 之后把永久代代換成的元空間,把字符串常量池從方法區移到了 Java 堆上

除此之外編譯器還會對 String 字符串做一些優化,例如以下代碼

String s1 = "Ja" + "va"; String s2 = "Java"; System.out.println(s1 == s2);

輸出 true

javap -c 反匯編看一下

從編譯代碼 #2 可以看出,代碼 “Ja”+“va” 被直接編譯成了 “Java” ,因此 s1==s2 的結果才是 true,這就是編譯器對字符串優化的結果。


總結

以上是生活随笔為你收集整理的Java - String源码解析及常见面试问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。