成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Java中的String,這一篇就夠了

開發(fā) 前端
當(dāng)字符串是不可變時(shí),字符串常量池才有意義。字符串常量池的出現(xiàn),可以減少創(chuàng)建相同字面量的字符串,讓不同的引用指向池中同一個(gè)字符串,為運(yùn)行時(shí)節(jié)約很多的堆內(nèi)存。若字符串可變,字符串常量池失去意義,基于常量池的String.intern()方法也失效,每次創(chuàng)建新的 String 將在堆內(nèi)開辟出新的空間,占據(jù)更多的內(nèi)存。

今天我們一起看一下Java基礎(chǔ)類:String

定義

  1. String表示字符串類型,屬于引用數(shù)據(jù)類型,不屬于基本數(shù)據(jù)類型。
  2. 在java中隨便使用 雙引號括起來 的都是String對象。例如:"abc","def","hello world!",這是3個(gè)String對象。
  3. java中規(guī)定,雙引號括起來的字符串,是 不可變 的,也就是說"abc"自出生到最終死亡,不可變,不能變成"abcd",也不能變成"ab"

源碼解讀

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
   /**用來存儲字符串  */
   private final char value[];
 
   /** 緩存字符串的哈希碼 */
   private int hash; // Default to 0
 
   /** 實(shí)現(xiàn)序列化的標(biāo)識 */
   private static final long serialVersionUID = -6849794470754667710L;
}

這是一個(gè)用 final 聲明的常量類,不能被任何類所繼承,而且一旦一個(gè)String對象被創(chuàng)建, 包含在這個(gè)對象中的字符序列是不可改變的, 包括該類后續(xù)的所有方法都是不能修改該對象的,直至該對象被銷毀,這是我們需要特別注意的(該類的一些方法看似改變了字符串,其實(shí)內(nèi)部都是創(chuàng)建一個(gè)新的字符串,下面講解方法時(shí)會(huì)介紹)。

通過上述代碼可以發(fā)現(xiàn),一個(gè) String 字符串實(shí)際上是一個(gè) char 數(shù)組。

聲明方式

//注意這種字面量聲明的區(qū)別
String str1 = "abc";
String str2 = new String("abc");

JDK1.6

那么這兩種聲明方式有什么區(qū)別呢?在講解之前,我們先介紹 JDK1.7(不包括1.7)以前的 JVM 的內(nèi)存分布:

圖片圖片

  1. 程序計(jì)數(shù)器:也稱為 PC 寄存器,保存的是程序當(dāng)前執(zhí)行的指令的地址(也可以說保存下一條指令的所在存儲單元的地址),當(dāng)CPU需要執(zhí)行指令時(shí),需要從程序計(jì)數(shù)器中得到當(dāng)前需要執(zhí)行的指令所在存儲單元的地址,然后根據(jù)得到的地址獲取到指令,在得到指令之后,程序計(jì)數(shù)器便自動(dòng)加1或者根據(jù)轉(zhuǎn)移指針得到下一條指令的地址,如此循環(huán),直至執(zhí)行完所有的指令。線程私有。
  2. 虛擬機(jī)棧:基本數(shù)據(jù)類型、對象的引用都存放在這。線程私有。
  3. 本地方法棧:虛擬機(jī)棧是為執(zhí)行Java方法服務(wù)的,而本地方法棧則是為執(zhí)行本地方法(Native Method)服務(wù)的。在JVM規(guī)范中,并沒有對本地方法棧的具體實(shí)現(xiàn)方法以及數(shù)據(jù)結(jié)構(gòu)作強(qiáng)制規(guī)定,虛擬機(jī)可以自由實(shí)現(xiàn)它。在HotSopt虛擬機(jī)中直接就把本地方法棧和虛擬機(jī)棧合二為一。
  4. 方法區(qū):存儲了每個(gè)類的信息(包括類的名稱、方法信息、字段信息)、靜態(tài)變量、常量以及編譯器編譯后的代碼等。注意:在Class文件中除了類的字段、方法、接口等描述信息外,還有一項(xiàng)信息是常量池,用來存儲編譯期間生成的字面量和符號引用。
  5. 堆:用來存儲對象本身的以及數(shù)組(當(dāng)然,數(shù)組引用是存放在Java棧中的)。

JDK1.7及以后

在 JDK1.7 以后,方法區(qū)的常量池被移除放到堆中了,如下:

圖片圖片

常量池:Java運(yùn)行時(shí)會(huì)維護(hù)一個(gè)String Pool(String池), 也叫“字符串緩沖區(qū)”。String池用來存放運(yùn)行時(shí)中產(chǎn)生的各種字符串,并且池中的字符串的內(nèi)容不重復(fù)。

  1. 字面量創(chuàng)建字符串或者純字符串(常量)拼接字符串會(huì)先在字符串池中找,看是否有相等的對象,沒有的話就在字符串池創(chuàng)建該對象;有的話則直接用池中的引用,避免重復(fù)創(chuàng)建對象。
  2. new關(guān)鍵字創(chuàng)建時(shí),直接在堆中創(chuàng)建一個(gè)新對象,變量所引用的都是這個(gè)新對象的地址,但是如果通過new關(guān)鍵字創(chuàng)建的字符串內(nèi)容在常量池中存在了,那么會(huì)由堆在指向常量池的對應(yīng)字符;但是反過來,如果通過new關(guān)鍵字創(chuàng)建的字符串對象在常量池中沒有,那么通過new關(guān)鍵詞創(chuàng)建的字符串對象是不會(huì)額外在常量池中維護(hù)的。
  3. 使用包含變量表達(dá)式來創(chuàng)建String對象,則不僅會(huì)檢查維護(hù)字符串池,還會(huì)在堆區(qū)創(chuàng)建這個(gè)對象,最后是指向堆內(nèi)存的對象。

內(nèi)存分析

1. String str = "Hello";
public class stringclass {
    public static void main(String[] args) {
        String str="Hello";
        String str2="Hello";
        System.out.println(str==str2);
        str="World";
    }
}
//輸出結(jié)果:true

圖片圖片

2. String str = new String ("Hello");
public class stringclass {
    public static void main(String[] args) {
        String str= new String("Hello");
        String str2= new String("Hello");
        String str3 = "Hello";
        System.out.println(str==str2);
        System.out.println(str==str3);
    }
}  
//輸出結(jié)果:false  false
3. String str = "Hello" + "World";
public class stringclass {
    public static void main(String[] args) {
        //當(dāng)一個(gè)字符串由多個(gè)字符串常量連接而成時(shí),它自己肯定也是字符串常量。
        //該字符串是在編譯期就能確定。先是在池里生成“a”和“b”,再通過拼接的方式在池里生成"ab"。
        String str="Hello" + "World";
    }
}

圖片圖片

4. String str = new String ("Hello") + new String("World");

當(dāng)使用了變量字符串的拼接(+, sb.append)都只會(huì)在堆區(qū)創(chuàng)建該字符串對象, 并不會(huì)在常量池創(chuàng)建新生成的字符串

public class stringclass {
    public static void main(String[] args) {
        String str=new String("Hello") + new String("World");
    }
}

圖片圖片

常見操作

1. equals(Object anObject)

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;
}

String 類重寫了 equals 方法,比較的是組成字符串的每一個(gè)字符是否相同,如果都相同則返回true,否則返回false。

2. hashCode()

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
   }
   return h;
}

String 類的 hashCode 算法很簡單,主要就是中間的 for 循環(huán),計(jì)算公式如下:

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

s 數(shù)組即源碼中的 val 數(shù)組,也就是構(gòu)成字符串的字符數(shù)組。這里有個(gè)數(shù)字 31 ,為什么選擇31作為乘積因子,而且沒有用一個(gè)常量來聲明?主要原因有兩個(gè):

  • 31是一個(gè)不大不小的質(zhì)數(shù),是作為 hashCode 乘子的優(yōu)選質(zhì)數(shù)之一。
  • 31可以被 JVM 優(yōu)化,31 * i = (i << 5) - i。因?yàn)橐莆贿\(yùn)算比乘法運(yùn)行更快更省性能。

3. charAt(int index)

public char charAt(int index) {
   //如果傳入的索引大于字符串的長度或者小于0,直接拋出索引越界異常
   if ((index < 0) || (index >= value.length)) {
       throw new StringIndexOutOfBoundsException(index);
   }
   return value[index];//返回指定索引的單個(gè)字符
}

我們知道一個(gè)字符串是由一個(gè)字符數(shù)組組成,這個(gè)方法是通過傳入的索引(數(shù)組下標(biāo)),返回指定索引的單個(gè)字符。

4. compareTo(String anotherString)

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;
}

源碼也很好理解,該方法是按字母順序比較兩個(gè)字符串,是基于字符串中每個(gè)字符的 Unicode 值。當(dāng)兩個(gè)字符串某個(gè)位置的字符不同時(shí),返回的是這一位置的字符 Unicode 值之差,當(dāng)兩個(gè)字符串都相同時(shí),返回兩個(gè)字符串長度之差。

compareToIgnoreCase() 方法在 compareTo 方法的基礎(chǔ)上忽略大小寫,我們知道大寫字母是比小寫字母的Unicode值小32的,底層實(shí)現(xiàn)是先都轉(zhuǎn)換成大寫比較,然后都轉(zhuǎn)換成小寫進(jìn)行比較。

5. concat(String str)

public String concat(String str) {
    int otherLen = str.length();
    if (otherLen == 0) {
        return this;
    }
    int len = value.length;
    char buf[] = Arrays.copyOf(value, len + otherLen);
    str.getChars(buf, len);
    return new String(buf, true);
}

該方法是將指定的字符串連接到此字符串的末尾。

首先判斷要拼接的字符串長度是否為0,如果為0,則直接返回原字符串。如果不為0,則通過 Arrays 工具類的copyOf方法創(chuàng)建一個(gè)新的字符數(shù)組,長度為原字符串和要拼接的字符串之和,前面填充原字符串,后面為空。接著在通過 getChars 方法將要拼接的字符串放入新字符串后面為空的位置。

注意:返回值是 new String(buf, true),也就是重新通過 new 關(guān)鍵字創(chuàng)建了一個(gè)新的字符串,原字符串是不變的。這也是前面我們說的一旦一個(gè)String對象被創(chuàng)建, 包含在這個(gè)對象中的字符序列是不可改變的。

6. indexOf(int ch)

public int indexOf(int ch) {
        return indexOf(ch, 0);//從第一個(gè)字符開始搜索
    }
  public int indexOf(int ch, int fromIndex) {
      final int max = value.length;//max等于字符的長度
      if (fromIndex < 0) {//指定索引的位置如果小于0,默認(rèn)從 0 開始搜索
          fromIndex = 0;
      } else if (fromIndex >= max) {
          //如果指定索引值大于等于字符的長度(因?yàn)槭菙?shù)組,下標(biāo)最多只能是max-1),直接返回-1
          return -1;
      }
  
     if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {//一個(gè)char占用兩個(gè)字節(jié),如果ch小于2的16次方(65536),絕大多數(shù)字符都在此范圍內(nèi)
         final char[] value = this.value;
         for (int i = fromIndex; i < max; i++) {//for循環(huán)依次判斷字符串每個(gè)字符是否和指定字符相等
             if (value[i] == ch) {
                 return i;//存在相等的字符,返回第一次出現(xiàn)該字符的索引位置,并終止循環(huán)
             }
         }
         return -1;//不存在相等的字符,則返回 -1
      }else {//當(dāng)字符大于 65536時(shí),處理的少數(shù)情況,該方法會(huì)首先判斷是否是有效字符,然后依次進(jìn)行比較
         return indexOfSupplementary(ch, fromIndex);
    }
}

indexOf(int ch),參數(shù) ch 其實(shí)是字符的 Unicode 值,這里也可以放單個(gè)字符(默認(rèn)轉(zhuǎn)成int),作用是返回指定字符第一次出現(xiàn)的此字符串中的索引。其內(nèi)部是調(diào)用 indexOf(int ch, int fromIndex),只不過這里的 fromIndex =0 ,因?yàn)槭菑?0 開始搜索;而 indexOf(int ch, int fromIndex) 作用也是返回首次出現(xiàn)的此字符串內(nèi)的索引,但是從指定索引處開始搜索。

7. substring(int beginIndex)

public String substring(int beginIndex) {
    if (beginIndex < 0) {//如果索引小于0,直接拋出異常
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    int subLen = value.length - beginIndex;//subLen等于字符串長度減去索引
    if (subLen < 0) {//如果subLen小于0,也是直接拋出異常
        throw new StringIndexOutOfBoundsException(subLen);
    }
    //1、如果索引值beginIdex == 0,直接返回原字符串
    //2、如果不等于0,則返回從beginIndex開始,一直到結(jié)尾
    return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}

返回一個(gè)從索引 beginIndex 開始一直到結(jié)尾的子字符串。

String不可變性

String 類為什么要這樣設(shè)計(jì)成不可變呢?我們可以從性能以及安全方面來考慮:

  • 安全

引發(fā)安全問題,譬如,數(shù)據(jù)庫的用戶名、密碼都是以字符串的形式傳入來獲得數(shù)據(jù)庫的連接,或者在socket編程中,主機(jī)名和端口都是以字符串的形式傳入。因?yàn)樽址遣豢勺兊模运闹凳遣豢筛淖兊模駝t黑客們可以鉆到空子,改變字符串指向的對象的值,造成安全漏洞。

保證線程安全,在并發(fā)場景下,多個(gè)線程同時(shí)讀寫資源時(shí),會(huì)引競態(tài)條件,由于 String 是不可變的,不會(huì)引發(fā)線程的問題而保證了線程。

HashCode,當(dāng) String 被創(chuàng)建出來的時(shí)候,hashcode也會(huì)隨之被緩存,hashcode的計(jì)算與value有關(guān),若 String 可變,那么 hashcode 也會(huì)隨之變化,針對于 Map、Set 等容器,他們的鍵值需要保證唯一性和一致性,因此,String 的不可變性使其比其他對象更適合當(dāng)容器的鍵值。

  • 性能
  • 當(dāng)字符串是不可變時(shí),字符串常量池才有意義。字符串常量池的出現(xiàn),可以減少創(chuàng)建相同字面量的字符串,讓不同的引用指向池中同一個(gè)字符串,為運(yùn)行時(shí)節(jié)約很多的堆內(nèi)存。若字符串可變,字符串常量池失去意義,基于常量池的String.intern()方法也失效,每次創(chuàng)建新的 String 將在堆內(nèi)開辟出新的空間,占據(jù)更多的內(nèi)存。
責(zé)任編輯:武曉燕 來源: Java技術(shù)指北
相關(guān)推薦

2024-04-10 08:22:44

2020-08-03 10:00:11

前端登錄服務(wù)器

2023-04-24 08:00:00

ES集群容器

2022-04-07 10:39:21

反射Java安全

2020-07-03 08:21:57

Java集合框架

2019-08-13 15:36:57

限流算法令牌桶

2022-08-01 11:33:09

用戶分析標(biāo)簽策略

2021-04-08 07:37:39

隊(duì)列數(shù)據(jù)結(jié)構(gòu)算法

2023-09-11 08:13:03

分布式跟蹤工具

2020-02-18 16:20:03

Redis ANSI C語言日志型

2022-06-20 09:01:23

Git插件項(xiàng)目

2020-05-14 16:35:21

Kubernetes網(wǎng)絡(luò)策略DNS

2023-02-10 09:04:27

2023-09-28 08:59:38

2020-07-06 08:06:00

Java模塊系統(tǒng)

2023-09-04 08:00:00

開發(fā)Java線程

2017-03-11 22:19:09

深度學(xué)習(xí)

2021-03-03 14:55:10

開發(fā)MySQL代碼

2020-03-09 17:28:51

NoSQLMongoDB數(shù)據(jù)庫

2023-11-18 09:30:42

模型AI
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 成人美女免费网站视频 | 精品国产乱码久久久久久丨区2区 | 欧美一级一| 精品国产一二三区 | 日韩淫片免费看 | 99在线免费视频 | 91精品国产综合久久久久 | 中文字幕不卡在线观看 | 国产伦精品一区二区三区高清 | 欧美一区二区三区在线播放 | 神马福利| 一级毛片视频在线 | 一区二区三区四区不卡 | 国产乱码一二三区精品 | 看一级黄色毛片 | a在线视频观看 | 国产精品久久久久久中文字 | 夜夜骚| 精品伊人久久 | 91最新在线视频 | 黄色在线观看网站 | 福利精品在线观看 | 精品一区二区久久久久久久网站 | 颜色网站在线观看 | 久久免费视频观看 | 中文字幕一页二页 | 精品综合久久 | 亚州中文字幕 | 一级黄色毛片子 | 少妇午夜一级艳片欧美精品 | 欧美一区二区三区视频在线观看 | 亚洲www啪成人一区二区麻豆 | 91精品国产91久久综合桃花 | 亚洲欧洲成人av每日更新 | 中文字幕第一页在线 | 欧美一级二级视频 | 国产精品久久影院 | 色综合天天天天做夜夜夜夜做 | 国产精品乱码一二三区的特点 | 超碰在线人人 | 伊人久久麻豆 |