国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

【Java系列】從JVM角度深度解析Java核心類String的不可變特性

afishhhhh / 863人閱讀

摘要:性能,大量運用在哈希的處理中,由于的不可變性,可以只計算一次哈希值,然后緩存在內(nèi)部,后續(xù)直接取就好了。這是目前的一個底層字節(jié)碼的實現(xiàn),那么是不是沒有使用或者的必要了呢。

凱倫說,公眾號ID: KailunTalk,努力寫出最優(yōu)質(zhì)的技術(shù)文章,歡迎關(guān)注探討。

1. 前言

最近看到幾個有趣的關(guān)于Java核心類String的問題。

String類是如何實現(xiàn)其不可變的特性的,設(shè)計成不可變的好處在哪里。

為什么不推薦使用+號的方式去形成新的字符串,推薦使用StringBuilder或者StringBuffer呢。

翻閱了網(wǎng)上的一些博客和stackoverflow,結(jié)合自己的理解做一個匯總。

2. String類是如何實現(xiàn)不可變的

String類的一大特點,就是使用Final類修飾符。

A class can be declared final if its definition is complete and no subclasses are desired or required.

Because a final class never has any subclasses, the methods of a final class are never overridden .

Java SE 7 官方手冊中的定義如上,如果你認(rèn)為這個類已經(jīng)定義完全并且不需要任何子類的話,可以將這個類聲明為Final,F(xiàn)inal類中的方法將永遠(yuǎn)不會被重寫。

在Java中,String是被設(shè)計成一個不可變(immutable)類,一旦創(chuàng)建完后,字符串本身是無法通過正常手段被修改的。

private final char value[];      // 一旦初始化后,引用不能被修改

public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }

選了substring方法來做一個代表,其他常見的涉及String操作的方法都是類似,如果你操作后的內(nèi)容會和目前String中的內(nèi)容不一致的話,那么都是重新創(chuàng)建一個新的String類返還,不會讓你去修改內(nèi)部的內(nèi)容。

將String類設(shè)計成Final類,能夠避免其方法被子類重寫,從而破壞了它本身方法的實現(xiàn),進(jìn)而破壞了不可變的特性。

2.1 String類設(shè)計成不可變的好處

我們都不是Java語言的設(shè)計者,不知道其為何一定要設(shè)計成不可變,試著做一些猜想。

可以實現(xiàn)多個變量引用JVM內(nèi)存中的同一個字符串實例。見后文String Pool的介紹。

安全性,String類的用途實在太廣了,如果可以隨意修改的,是不是很恐怖。

性能,String大量運用在哈希的處理中,由于String的不可變性,可以只計算一次哈希值,然后緩存在內(nèi)部,后續(xù)直接取就好了。如果String類是可變的話,在進(jìn)行哈希處理的時候,需要進(jìn)行大量的哈希值的重新計算。

這是結(jié)合個人理解和stackoverflow上看的匯總,我們來看看Java語言的爸爸James Gosling是怎么說的。

From a strategic point of view, they tend to more often be trouble free. And there are usually things you can do with immutables that you can"t do with mutable things, such as cache the result. If you pass a string to a file open method, or if you pass a string to a constructor for a label in a user interface, in some APIs (like in lots of the Windows APIs) you pass in an array of characters. The receiver of that object really has to copy it, because they don"t know anything about the storage lifetime of it. And they don"t know what"s happening to the object, whether it is being changed under their feet.

You end up getting almost forced to replicate the object because you don"t know whether or not you get to own it. And one of the nice things about immutable objects is that the answer is, "Yeah, of course you do." Because the question of ownership, who has the right to change it, doesn"t exist.

One of the things that forced Strings to be immutable was security. You have a file open method. You pass a String to it. And then it"s doing all kind of authentication checks before it gets around to doing the OS call. If you manage to do something that effectively mutated the String, after the security check and before the OS call, then boom, you"re in. But Strings are immutable, so that kind of attack doesn"t work. That precise example is what really demanded that Strings be immutable.

這是James Gosling在2001年5月的一次訪談中,談到了不可變類和String,大意就是 他會更傾向于使用不可變類,它能夠緩存結(jié)果,當(dāng)你在傳參的時候,使用不可變類不需要去考慮誰可能會修改其內(nèi)部的值,這個問題不存在的。如果使用可變類的話,可能需要每次記得重新拷貝出里面的值,性能會有一定的損失。

老爺子還說了,迫使String類設(shè)計成不可變的另一個原因是安全,當(dāng)你在調(diào)用其他方法,比如調(diào)用一些系統(tǒng)級操作之前,可能會有一系列校驗,如果是可變類的話,可能在你校驗過后,其內(nèi)部的值被改變了,可能引起嚴(yán)重的系統(tǒng)崩潰問題,這是迫使String類設(shè)計成不可變類的重要原因。

2.2 String Pool

上文說了,設(shè)計成不可變后,可以多個變量引用JVM上同一塊地址,可以節(jié)省內(nèi)存空間,相同的字符串不用重復(fù)占用Heap區(qū)域空間。

String test1 = "abc";
String test2 = "abc";

通常我們平時在使用字符串是,都是通過這種方式使用,那么JVM中的大致存儲就是如下圖所示。

?

兩個變量同時引用了String Pool中的abc,如果String類是可變的話,也就不能存在String Pool這樣的設(shè)計了。
在平時我們還會通過new關(guān)鍵字來生成String,那么新創(chuàng)建的String是否也會和上文中的示例一樣共享同一個字符串地址呢。

        String test1 = "abc";
        String test2 = "abc";
        String test3 = new String("abc");

答案是不會,使用new關(guān)鍵字會在堆區(qū)在創(chuàng)建出一個字符串,所以使用new來創(chuàng)建字符串還是很浪費內(nèi)存的,內(nèi)存結(jié)構(gòu)如下圖所示。

?

2.3 不推薦使用+來拼裝字符串的原因。

首先我們來看這一段代碼,應(yīng)該是之前寫代碼比較常見的。

String test1 = "abc";
String test2 = "abc";
String test3 = test1 + test2;

test3通過test1和test2拼接而成,我們看一下這個過程中的字節(jié)碼。

?

從以上圖我們可以看到,目前的JDK7的做法是,會通過新建StringBuilder的方式來完成這個+號的操作。這是目前的一個底層字節(jié)碼的實現(xiàn),那么是不是沒有使用StringBuilder或者StringBuffer的必要了呢。還是有的,看下一個例子。

String test2 = "abc";
String test3 = "abc";

for (int i = 0; i < 5; i++) {
    test3 += test2;
}

在上述代碼中,我們還是使用+號進(jìn)行拼接,但這次我們加了一個循環(huán),看一下字節(jié)碼有什么變化。
?

每次循環(huán)都會創(chuàng)建一個StringBuilder,在末尾再調(diào)用toString返還回去,效率很低。繼續(xù)看下一個例子,我們直接使用StringBuilder,來做拼接。

String test2 = "abc";
// 使用StringBuilder進(jìn)行拼接
StringBuilder test4 = new StringBuilder("abc");
for (int i = 0; i < 5; i++) {
    test4.append(test2);
}

?

每次循環(huán)體中只會調(diào)用之前創(chuàng)建的StringBuilder的append方法進(jìn)行拼接,效率大大提高。

至于StringBuilder 的內(nèi)部實現(xiàn),諸位有興趣可以自己再去看一下,本質(zhì)上也是一個char數(shù)組上的操作,和StringBuffer的區(qū)別在于,StringBuffer是有做同步處理的,而StringBuilder沒有。

3. 總結(jié)

本文主要探討了String類設(shè)計為Final修飾和不可變類的原因,以及為何在日常工作中不推薦使用+號進(jìn)行字符串拼接。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/67468.html

相關(guān)文章

  • Java系列字節(jié)碼角度深度理解Java函數(shù)調(diào)用傳參方式

    摘要:下文將從字節(jié)碼的角度,分析中基本類型傳參和對象傳參。主函數(shù)執(zhí)行時,操作棧會推入主函數(shù)棧幀,其中包含了主函數(shù)的局部變量表,字節(jié)碼,返回值等信息。主函數(shù)的棧幀會被推入棧,成為當(dāng)前操作棧。 個人網(wǎng)站地址: http://kailuncen.me/2017/06/0... 一個小問題 在開源中國看到這樣一則問題 https://www.oschina.net/quest...,其中的變量a前...

    LdhAndroid 評論0 收藏0
  • Flink 源碼解析 —— 深度解析 Flink 是如何管理好內(nèi)存的?

    摘要:減少垃圾收集壓力因為所有長生命周期的數(shù)據(jù)都是在的管理內(nèi)存中以二進(jìn)制表示的,所以所有數(shù)據(jù)對象都是短暫的,甚至是可變的,并且可以重用。當(dāng)然,并不是唯一一個基于且對二進(jìn)制數(shù)據(jù)進(jìn)行操作的數(shù)據(jù)處理系統(tǒng)。 showImg(https://segmentfault.com/img/remote/1460000020044119?w=1280&h=853); 前言 如今,許多用于分析大型數(shù)據(jù)集的開源系...

    Edison 評論0 收藏0
  • Java開發(fā)

    摘要:大多數(shù)待遇豐厚的開發(fā)職位都要求開發(fā)者精通多線程技術(shù)并且有豐富的程序開發(fā)調(diào)試優(yōu)化經(jīng)驗,所以線程相關(guān)的問題在面試中經(jīng)常會被提到。將對象編碼為字節(jié)流稱之為序列化,反之將字節(jié)流重建成對象稱之為反序列化。 JVM 內(nèi)存溢出實例 - 實戰(zhàn) JVM(二) 介紹 JVM 內(nèi)存溢出產(chǎn)生情況分析 Java - 注解詳解 詳細(xì)介紹 Java 注解的使用,有利于學(xué)習(xí)編譯時注解 Java 程序員快速上手 Kot...

    LuDongWei 評論0 收藏0
  • 后臺開發(fā)常問面試題集錦(問題搬運工,附鏈接)

    摘要:基礎(chǔ)問題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識點總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機(jī)制解讀抽象類與三大特征時間和時間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對象鎖和類鎖的區(qū)別,,優(yōu)缺點及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...

    spacewander 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<