最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

    1. <span id="tmo8w"><div id="tmo8w"></div></span><sup id="tmo8w"></sup>
      <pre id="tmo8w"></pre>
        <pre id="tmo8w"><abbr id="tmo8w"></abbr></pre>
      1. java String源碼和String常量池的全面解析

         更新時(shí)間:2017年12月18日 08:56:06   作者:NiceCui  
        下面小編就為大家分享一篇java String源碼和String常量池的全面解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧

        1. String 介紹,常用方法源碼分析

        2. String 常量池分析

        常用方法

        equals

        trim

        replace

        concat

        split

        startsWith 和 endsWith

        substring

        toUpperCase() 和 toLowerCase()

        compareTo

        String 介紹

        String類被final所修飾,也就是說String對(duì)象是不可變量,并發(fā)程序最喜歡不可變量了。String類實(shí)現(xiàn)了Serializable, Comparable, CharSequence接口。

        從一段代碼說起:

        public void stringTest(){
         String a = "a"+"b"+1;
         String b = "ab1";
         System.out.println(a == b);
        }

        大家猜一猜結(jié)果如何?如果你的結(jié)論是true。好吧,再來一段代碼:

        public void stringTest(){
         String a = new String("ab1");
         String b = "ab1";
         System.out.println(a == b);
        }

        結(jié)果如何呢?正確答案是false。

        讓我們看看經(jīng)過編譯器編譯后的代碼如何

        //第一段代碼
        public void stringTest() {
         String a = "ab1";
         String b = "ab1";
         System.out.println(a == b);
        }
        //第二段代碼
        public void stringTest() {
         String a1 = new String("ab1");
         String b = "ab1";
         System.out.println(a1 == b);
        }

        也就是說第一段代碼經(jīng)過了編譯期優(yōu)化,原因是編譯器發(fā)現(xiàn)"a"+"b"+1和"ab1"的效果是一樣的,都是不可變量組成。但是為什么他們的內(nèi)存地址會(huì)相同呢?如果你對(duì)此還有興趣,那就一起看看String類的一些重要源碼吧。

        源碼

        一、 String屬性

        String類中包含一個(gè)不可變的char數(shù)組用來存放字符串,一個(gè)int型的變量hash用來存放計(jì)算后的哈希值。

        /** The value is used for character storage. */
        private final char value[];
        /** Cache the hash code for the string */
        private int hash; // Default to 0
        /** use serialVersionUID from JDK 1.0.2 for interoperability */
        private static final long serialVersionUID = -6849794470754667710L;
        

        二、 String構(gòu)造函數(shù)

        //不含參數(shù)的構(gòu)造函數(shù),一般沒什么用,因?yàn)関alue是不可變量
        public String() {
         this.value = new char[0];
        }
        //參數(shù)為String類型
        public String(String original) {
         this.value = original.value;
         this.hash = original.hash;
        }
        //參數(shù)為char數(shù)組,使用java.utils包中的Arrays類復(fù)制
        public String(char value[]) {
         this.value = Arrays.copyOf(value, value.length);
        }
        //從bytes數(shù)組中的offset位置開始,將長度為length的字節(jié),以charsetName格式編碼,拷貝到value
        public String(byte bytes[], int offset, int length, String charsetName)
         throws UnsupportedEncodingException {
         if (charsetName == null)
         throw new NullPointerException("charsetName");
         checkBounds(bytes, offset, length);
         this.value = StringCoding.decode(charsetName, bytes, offset, length);
        }
        //調(diào)用public String(byte bytes[], int offset, int length, String charsetName)構(gòu)造函數(shù)
        public String(byte bytes[], String charsetName)
         throws UnsupportedEncodingException {
         this(bytes, 0, bytes.length, charsetName);
        }

        三、 String常用方法

        1. equals

        boolean equals(Object anObject)
        public boolean equals(Object anObject) {
         //如果引用的是同一個(gè)對(duì)象,返回真
         if (this == anObject) {
         return true;
         }
         //如果不是String類型的數(shù)據(jù),返回假
         if (anObject instanceof String) {
         String anotherString = (String) anObject;
         int n = value.length;
         //如果char數(shù)組長度不相等,返回假
         if (n == anotherString.value.length) {
          char v1[] = value;
          char v2[] = anotherString.value;
          int i = 0;
          //從后往前單個(gè)字符判斷,如果有不相等,返回假
          while (n-- != 0) {
          if (v1[i] != v2[i])
           return false;
          i++;
          }
          //每個(gè)字符都相等,返回真
          return true;
         }
         }
         return false;
        }
        String e1 = "good";
        String e2 = "good everyDay";
        e1.equals(e2); // 返回 false

        1 首先判斷是否 引用同一個(gè)對(duì)象 == 也就是判斷 這兩個(gè)引用的 內(nèi)存地址是否相同,如果相同 直接返回 true

        2 會(huì)判斷是否類型 相同,是否是同一種數(shù)據(jù)類型

        3 類型 相同 就會(huì)比較 轉(zhuǎn)換成的 字符 數(shù)組的長度 是否相同

        4 從后往前 比較 每一個(gè)字符 是否 相同

        判斷順序 =》 1.內(nèi)存地址 2.數(shù)據(jù)類型 3.字符數(shù)組長度 4.單個(gè)字符比較

        2. compareTo

        int compareTo(String anotherString)
        public int compareTo(String anotherString) {
         //自身對(duì)象字符串長度len1
         int len1 = value.length;
         //被比較對(duì)象字符串長度len2
         int len2 = anotherString.value.length;
         //取兩個(gè)字符串長度的最小值lim
         int lim = Math.min(len1, len2);
         char v1[] = value;
         char v2[] = anotherString.value;
         int k = 0;
         //從value的第一個(gè)字符開始到最小長度lim處為止,如果字符不相等,返回自身(對(duì)象不相等處字符-被比較對(duì)象不相等字符)
         while (k < lim) {
         char c1 = v1[k];
         char c2 = v2[k];
         if (c1 != c2) {
          return c1 - c2;
         }
         k++;
         }
         //如果前面都相等,則返回(自身長度-被比較對(duì)象長度)
         return len1 - len2;
        }
        String co1 = "hello" ;
        String co2 = "hello";
        String co3 = "hello you";
          
        System.out.println(co1.compareTo(co2)); // 0
        System.out.println(co1.compareTo(co3)); // -4

        這個(gè)方法寫的很巧妙,先從0開始判斷字符大小。

        如果兩個(gè)對(duì)象能比較字符的地方比較完了還相等,就直接返回自身長度減被比較對(duì)象長度,如果兩個(gè)字符串長度相等,則返回的是0,巧妙地判斷了三種情況。

        3.hashCode

        int hashCode()
        public int hashCode() {
         int h = hash;
         //如果hash沒有被計(jì)算過,并且字符串不為空,則進(jìn)行hashCode計(jì)算
         if (h == 0 && value.length > 0) {
          char val[] = value;
          //計(jì)算過程
          //s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
          for (int i = 0; i < value.length; i++) {
           h = 31 * h + val[i];
          }
          //hash賦值
          hash = h;
         }
         return h;
        }
        String a = "toyou";
        char val[] = a.toCharArray();
        char c1 = 't';
        char c2 = 'a';
        int f = c1; 
        int e = c2;
           
        System.out.println(e);   // 97 a
        System.out.println(f);   // 116 t
        System.out.println(31*val[0]); // 3596
        System.out.println(31*c1);  // 3596
        // hashCode 計(jì)算中 因?yàn)閏har 字符可以自動(dòng)轉(zhuǎn)換成對(duì)應(yīng)的 int 整形

        String類重寫了hashCode方法,Object中的hashCode方法是一個(gè)Native調(diào)用。

        String類的hash采用多項(xiàng)式計(jì)算得來,我們完全可以通過不相同的字符串得出同樣的hash,所以兩個(gè)String對(duì)象的hashCode相同,并不代表兩個(gè)String是一樣的。

        同一個(gè)String 對(duì)象 hashCode 一定相同, 但是 hashCode相同 ,不一定是同一個(gè)對(duì)象

        4.startsWith

        boolean startsWith(String prefix,int toffset)
        public boolean startsWith(String prefix, int toffset) {
         char ta[] = value;
         int to = toffset;
         char pa[] = prefix.value;
         int po = 0;
         int pc = prefix.value.length;
         // Note: toffset might be near -1>>>1.
         //如果起始地址小于0或者(起始地址+所比較對(duì)象長度)大于自身對(duì)象長度,返回假
         if ((toffset < 0) || (toffset > value.length - pc)) {
          return false;
         }
         //從所比較對(duì)象的末尾開始比較
         while (--pc >= 0) {
          if (ta[to++] != pa[po++]) {
           return false;
          }
         }
         return true;
        }
        public boolean startsWith(String prefix) {
         return startsWith(prefix, 0);
        }
        public boolean endsWith(String suffix) {
         return startsWith(suffix, value.length - suffix.value.length);
        }
        String d = "www.58fxp.com";
           
         System.out.println(d.startsWith("www")); // true
         System.out.println(d.endsWith("com")); // true

        起始比較和末尾比較都是比較經(jīng)常用得到的方法,例如在判斷一個(gè)字符串是不是http協(xié)議的,或者初步判斷一個(gè)文件是不是mp3文件,都可以采用這個(gè)方法進(jìn)行比較。

        5.concat

        String concat(String str)
        public String concat(String str) {
         int otherLen = str.length();
         //如果被添加的字符串為空,返回對(duì)象本身
         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);
        }
        String cat = "much";
           
        String newcat = cat.concat(" yes"); // much yes

        concat方法也是經(jīng)常用的方法之一,它先判斷被添加字符串是否為空來決定要不要?jiǎng)?chuàng)建新的對(duì)象。

        1 如果 拼接的字符 長度為0 直接返回 原字符對(duì)象

        2 拼接的字符 不為空 返回 新的 字符對(duì)象

        判斷字符長度 生成新對(duì)象

        6.replace

        String replace(char oldChar,char newChar)
        public String replace(char oldChar, char newChar) {
         //新舊值先對(duì)比
         if (oldChar != newChar) {
          int len = value.length;
          int i = -1;
          char[] val = value; /* avoid getfield opcode */
          //找到舊值最開始出現(xiàn)的位置
          while (++i < len) {
           if (val[i] == oldChar) {
            break;
           }
          }
          //從那個(gè)位置開始,直到末尾,用新值代替出現(xiàn)的舊值
          if (i < len) {
           char buf[] = new char[len];
           for (int j = 0; j < i; j++) {
            buf[j] = val[j];
           }
           while (i < len) {
            char c = val[i];
            buf[i] = (c == oldChar) ? newChar : c;
            i++;
           }
           return new String(buf, true);
          }
         }
         return this;
        }
        String r1 = "how do you do";
           
        String r2 = r1.replace("do","is");
        System.out.println(r2); // how is you is

        這個(gè)方法也有討巧的地方,例如最開始先找出舊值出現(xiàn)的位置,這樣節(jié)省了一部分對(duì)比的時(shí)間。

        replace(String oldStr,String newStr)方法通過正則表達(dá)式來判斷。

        7.trim

        String trim()
        public String trim() {
         int len = value.length;
         int st = 0;
         char[] val = value; /* avoid getfield opcode */
         //找到字符串前段沒有空格的位置
         while ((st < len) && (val[st] <= ' ')) {
          st++;
         }
         //找到字符串末尾沒有空格的位置
         while ((st < len) && (val[len - 1] <= ' ')) {
          len--;
         }
         //如果前后都沒有出現(xiàn)空格,返回字符串本身
         return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
        }
        String t1 = " public void "; // 前后各一個(gè)空格
           
        System.out.println("t1:"+t1.length()); // 13 帶空格長度
           
        String t2 = t1.trim();
           
        System.out.println("t2:"+t2.length()); // 11 去掉空格
           
        System.out.println(t2);

        8.intern

        String intern()
        public native String intern();
        
        String dd = new String("bb").intern();

        ntern方法是Native調(diào)用,它的作用是在方法區(qū)中的常量池里通過equals方法尋找等值的對(duì)象,

        如果沒有找到則在常量池中開辟一片空間存放字符串并返回該對(duì)應(yīng)String的引用,否則直接返回常量池中已存在String對(duì)象的引用。

        可以為new方法創(chuàng)建的 字符對(duì)象 也去強(qiáng)制查看常量池 是否已存在

        將引言中第二段代碼

        //String a = new String("ab1");
        //改為
        String a = new String("ab1").intern();

        則結(jié)果為為真,原因在于a所指向的地址來自于常量池,而b所指向的字符串常量默認(rèn)會(huì)調(diào)用這個(gè)方法,所以a和b都指向了同一個(gè)地址空間。

        int hash32()
        private transient int hash32 = 0;
        int hash32() {
         int h = hash32;
         if (0 == h) {
          // harmless data race on hash32 here.
          h = sun.misc.Hashing.murmur3_32(HASHING_SEED, value, 0, value.length);
          // ensure result is not zero to avoid recalcing
          h = (0 != h) ? h : 1;
          hash32 = h;
         }
         return h;
        }

        在JDK1.7中,Hash相關(guān)集合類在String類作key的情況下,不再使用hashCode方式離散數(shù)據(jù),而是采用hash32方法。

        這個(gè)方法默認(rèn)使用系統(tǒng)當(dāng)前時(shí)間,String類地址,System類地址等作為因子計(jì)算得到hash種子,通過hash種子在經(jīng)過hash得到32位的int型數(shù)值。

        public int length() {
         return value.length;
        }
        public String toString() {
         return this;
        }
        public boolean isEmpty() {
         return value.length == 0;
        }
        public char charAt(int index) {
         if ((index < 0) || (index >= value.length)) {
          throw new StringIndexOutOfBoundsException(index);
         }
         return value[index];
        }

        以上是一些簡單的常用方法。

        總結(jié)

        String對(duì)象是不可變類型,返回類型為String的String方法每次返回的都是新的String對(duì)象,除了某些方法的某些特定條件返回自身。

        String對(duì)象的三種比較方式:

        ==內(nèi)存比較:直接對(duì)比兩個(gè)引用所指向的內(nèi)存值,精確簡潔直接明了。

        equals字符串值比較:比較兩個(gè)引用所指對(duì)象字面值是否相等。

        hashCode字符串?dāng)?shù)值化比較:將字符串?dāng)?shù)值化。兩個(gè)引用的hashCode相同,不保證內(nèi)存一定相同,不保證字面值一定相同。

        字符串常量池的設(shè)計(jì)思想

        一.字符串常量池設(shè)計(jì)初衷

        每個(gè)字符串都是一個(gè)String對(duì)象,系統(tǒng)開發(fā)中將會(huì)頻繁使用字符串,如果像其他對(duì)像那樣創(chuàng)建銷毀將極大影響程序的性能。

        JVM為了提高性能和減少內(nèi)存開銷,在實(shí)例化字符串的時(shí)候進(jìn)行了優(yōu)化

        為字符串開辟了一個(gè)字符串常量池,類似于緩存區(qū)

        創(chuàng)建字符串常量時(shí),首先判斷字符串常量池是否存在該字符串

        存在該字符串返回引用實(shí)例,不存在,實(shí)例化字符串,放入池中

        實(shí)現(xiàn)基礎(chǔ)

        實(shí)現(xiàn)該優(yōu)化的基礎(chǔ)是每個(gè)字符串常量都是final修飾的常量,不用擔(dān)心常量池存在數(shù)據(jù)沖突

        運(yùn)行時(shí)實(shí)例創(chuàng)建的全局字符串常量池中有一個(gè)表,總是為池中每個(gè)唯一的字符串對(duì)象維護(hù)一個(gè)引用,這就意味著它們一直引用著字符串常量池中的對(duì)象,所以,在常量池中的這些字符串不會(huì)被垃圾收集器回收

        堆、棧、方法區(qū)

        了解字符串常量池,首先看一下 堆棧方法區(qū)

        image

        存儲(chǔ)的是對(duì)象,每個(gè)對(duì)象都包含一個(gè)與之對(duì)應(yīng)的class

        JVM只存在一個(gè)堆區(qū),被所有線程共享,堆中不存在基本類型和對(duì)象引用,只存在對(duì)象本身

        對(duì)象由垃圾回收器負(fù)責(zé)回收,因此大小和生命周期不需要確定

        每個(gè)線程都包含一個(gè)棧區(qū),棧區(qū)只存放基礎(chǔ)數(shù)據(jù)類型對(duì)象和自定義對(duì)象引用

        每個(gè)棧中的數(shù)據(jù)(原始類型和對(duì)象引用)都是私有的

        棧分為三個(gè)部分,基本類型變量區(qū)、執(zhí)行環(huán)境上下文、操作指令區(qū)(存放操作指令)

        數(shù)據(jù)大小和生命周期是可以確定的,當(dāng)沒有引用指向這個(gè)數(shù)據(jù)時(shí),這個(gè)數(shù)據(jù)就會(huì)消失

        方法區(qū)

        靜態(tài)區(qū),跟堆一樣,被所有的線程共享

        方法區(qū)包含的都是在整個(gè)程序中永遠(yuǎn)唯一的元素,如class、static變量;

        字符串常量池

        字符串常量池存在于方法區(qū)

        代碼:堆棧方法區(qū)存儲(chǔ)字符串

        String str1 = “abc”;
        String str2 = “abc”;
        String str3 = “abc”;
        String str4 = new String(“abc”);
        String str5 = new String(“abc”);

        image

        面試題

        String str4 = new String(“abc”) 創(chuàng)建多少個(gè)對(duì)象?

        拆分: str4 = 、 new String()、"abc"

        通過new 可以創(chuàng)建一個(gè)新的對(duì)象,new 方法創(chuàng)建實(shí)例化對(duì)象不會(huì)去常量池尋找是否已存在,只要new 都會(huì)實(shí)例化一個(gè)新的對(duì)象出來

        "abc"每個(gè)字符串 都是一個(gè)String 對(duì)象,如果常量池中沒有則會(huì)創(chuàng)建一個(gè)新對(duì)象放入常量池,否則返回對(duì)象引用

        將對(duì)象地址賦值給str4,創(chuàng)建一個(gè)引用

        所以,常量池中沒有“abc”字面量則創(chuàng)建兩個(gè)對(duì)象,否則創(chuàng)建一個(gè)對(duì)象,以及創(chuàng)建一個(gè)引用

        String str1 = new String("A"+"B") ; 會(huì)創(chuàng)建多少個(gè)對(duì)象? String str2 = new String("ABC") + "ABC" ; 會(huì)創(chuàng)建多少個(gè)對(duì)象?

        以上這篇java String源碼和String常量池的全面解析就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

        相關(guān)文章

        最新評(píng)論

        万载县| 普兰店市| 嘉荫县| 盐源县| 天祝| 阿坝| 孟村| 墨竹工卡县| 漳浦县| 德州市| 彝良县| 渝北区| 孝感市| 深圳市| 南阳市| 吕梁市| 天气| 克拉玛依市| 获嘉县| 朝阳县| 德令哈市| 洪江市| 密山市| 化隆| 云梦县| 九台市| 青海省| 民乐县| 政和县| 沾益县| 庐江县| 龙胜| 宣汉县| 盘山县| 合山市| 长治县| 江北区| 昭苏县| 温州市| 万州区| 曲周县|