摘要:操作也會影響當(dāng)前程序狀態(tài)寄存器,但只影響有溢出標(biāo)志,這對于的判斷沒有任何幫助。個字節(jié)表示的稱為,另外的作為和拼接組成由字節(jié)表示的字符。對于中日韓統(tǒng)一表意文字部分?jǐn)U展字符集則無法表示。
均摘選自JDK源碼,俺的講座《Java基礎(chǔ)教程-手寫JDK》會詳細(xì)講解這些知識點,大家不妨圍觀下:)
1 i++ vs i--String源碼的第985行,equals方法中
while (n--!= 0) { if (v1[i] != v2[i]) return false; i++; }
這段代碼是用于判斷字符串是否相等,但有個奇怪地方是用了i--!=0來做判斷,我們通常不是用i++么?為什么用i--呢?而且循環(huán)次數(shù)相同。原因在于編譯后會多一條指令:
i-- 操作本身會影響CPSR(當(dāng)前程序狀態(tài)寄存器),CPSR常見的標(biāo)志有N(結(jié)果為負(fù)), Z(結(jié)果為0),C(有進(jìn)位),O(有溢出)。i > 0,可以直接通過Z標(biāo)志判斷出來。
i++操作也會影響CPSR(當(dāng)前程序狀態(tài)寄存器),但只影響O(有溢出)標(biāo)志,這對于i < n的判斷沒有任何幫助。所以還需要一條額外的比較指令,也就是說每個循環(huán)要多執(zhí)行一條指令。
簡單來說,跟0比較會少一條指令。所以,循環(huán)使用i--,高端大氣上檔次。
2 成員變量 vs 局部變量JDK源碼在任何方法中幾乎都會用一個局部變量來接受成員變量,比如
public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length;
因為局部變量初始化后是在該方法線程棧中,而成員變量初始化是在堆內(nèi)存中,顯然前者更快,所以,我們在方法中盡量避免直接使用成員變量,而是使用局部變量。
3 刻意加載到寄存器 && 將耗時操作放到鎖外部在ConcurrentHashMap中,鎖segment的操作很有意思,它不是直接鎖,而是類似于自旋鎖,反復(fù)嘗試獲取鎖,并且在獲取鎖的過程中,會遍歷鏈表,從而將數(shù)據(jù)先加載到寄存器中緩存中,避免在鎖的過程中在便利,同時,生成新對象的操作也是放到鎖的外部來做,避免在鎖中的耗時操作
final V put(K key, int hash, V value, boolean onlyIfAbsent) { /** 在往該 segment 寫入前,需要先獲取該 segment 的獨占鎖 不是強(qiáng)制lock(),而是進(jìn)行嘗試 */ HashEntrynode = tryLock() ? null : scanAndLockForPut(key, hash, value);
scanAndLockForPut()源碼
private HashEntry4 判斷對象相等可先用==scanAndLockForPut(K key, int hash, V value) { HashEntry first = entryForHash(this, hash); HashEntry e = first; HashEntry node = null; int retries = -1; // negative while locating node // 循環(huán)獲取鎖 while (!tryLock()) { HashEntry f; // to recheck first below if (retries < 0) { if (e == null) { if (node == null) // speculatively create node //該hash位無值,新建對象,而不用再到put()方法的鎖中再新建 node = new HashEntry (hash, key, value, null); retries = 0; } //該hash位置key也相同,退化成自旋鎖 else if (key.equals(e.key)) retries = 0; else // 循環(huán)鏈表,cpu能自動將鏈表讀入緩存 e = e.next; } // retries>0時就變成自旋鎖。當(dāng)然,如果重試次數(shù)如果超過 MAX_SCAN_RETRIES(單核1多核64),那么不搶了,進(jìn)入到阻塞隊列等待鎖 // lock() 是阻塞方法,直到獲取鎖后返回,否則掛起 else if (++retries > MAX_SCAN_RETRIES) { lock(); break; } else if ((retries & 1) == 0 && // 這個時候是有大問題了,那就是有新的元素進(jìn)到了鏈表,成為了新的表頭 // 所以這邊的策略是,相當(dāng)于重新走一遍這個 scanAndLockForPut 方法 (f = entryForHash(this, hash)) != first) { e = first = f; // re-traverse if entry changed retries = -1; } } return node; }
在判斷對象是否相等時,可先用==,因為==直接比較地址,非常快,而equals的話會最對象值的比較,相對較慢,所以有可能的話,可以用a==b || a.equals(b)來比較對象是否相等
5 關(guān)于transienttransient是用來阻止序列化的,但HashMap源碼中內(nèi)部數(shù)組是定義為transient的
/** * The table, resized as necessary. Length MUST Always be a power of two. */ transient Entry[] table = (Entry []) EMPTY_TABLE;
那豈不里面的鍵值對都無法序列化了么,網(wǎng)絡(luò)中用hashmap來傳輸豈不是無法傳輸,其實不然。
Effective Java 2nd, Item75, Joshua大神提到:
For example, consider the case of a hash table. The physical
representation is a sequence of hash buckets containing key-value
entries. The bucket that an entry resides in is a function of the hash
code of its key, which is not, in general, guaranteed to be the same
from JVM implementation to JVM implementation. In fact, it isn"t even
guaranteed to be the same from run to run. Therefore, accepting the
default serialized form for a hash table would constitute a serious
bug. Serializing and deserializing the hash table could yield an
object whose invariants were seriously corrupt.
怎么理解? 看一下HashMap.get()/put()知道, 讀寫Map是根據(jù)Object.hashcode()來確定從哪個bucket讀/寫. 而Object.hashcode()是native方法, 不同的JVM里可能是不一樣的.
打個比方說, 向HashMap存一個entry, key為 字符串"STRING", 在第一個java程序里, "STRING"的hashcode()為1, 存入第1號bucket; 在第二個java程序里, "STRING"的hashcode()有可能就是2, 存入第2號bucket. 如果用默認(rèn)的串行化(Entry[] table不用transient), 那么這個HashMap從第一個java程序里通過串行化導(dǎo)入第二個java程序后, 其內(nèi)存分布是一樣的, 這就不對了.
舉個例子,比如向HashMap存一個鍵值對entry, key="方老司", 在第一個java程序里, "方老司"的hashcode()為1, 存入table[1],好,現(xiàn)在傳到另一個在JVM程序里, "方老司" 的hashcode()有可能就是2, 于是到table[2]去取,結(jié)果值不存在。
HashMap現(xiàn)在的readObject和writeObject是把內(nèi)容 輸出/輸入, 把HashMap重新生成出來.
6 不要用charchar在Java中utf-16編碼,是2個字節(jié),而2個字節(jié)是無法表示全部字符的。2個字節(jié)表示的稱為 BMP,另外的作為high surrogate和 low surrogate 拼接組成由4字節(jié)表示的字符。比如String源碼中的indexOf:
//這里用int來接受一個char,方便判斷范圍 public int indexOf(int ch, int fromIndex) { final int max = value.length; if (fromIndex < 0) { fromIndex = 0; } else if (fromIndex >= max) { // Note: fromIndex might be near -1>>>1. return -1; } //在Bmp范圍 if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { // handle most cases here (ch is a BMP code point or a // negative value (invalid code point)) final char[] value = this.value; for (int i = fromIndex; i < max; i++) { if (value[i] == ch) { return i; } } return -1; } else { //否則轉(zhuǎn)到四個字節(jié)的判斷方式 return indexOfSupplementary(ch, fromIndex); } }
所以Java的char只能表示utf-16中的bmp部分字符。對于CJK(中日韓統(tǒng)一表意文字)部分?jǐn)U展字符集則無法表示。
例如,下圖中除Ext-A部分,char均無法表示。
此外還有一種說法是要用char,密碼別用String,String是常量(即創(chuàng)建之后就無法更改),會保存到常量池中,如果有其他進(jìn)程可以dump這個進(jìn)程的內(nèi)存,那么密碼就會隨著常量池被dump出去從而泄露,而char[]可以寫入其他的信息從而改變,即是被dump了也會減少泄露密碼的風(fēng)險。
但個人認(rèn)為你都能dump內(nèi)存了難道是一個char能夠防范的住的?除非是String在常量池中未被回收,而被其它線程直接從常量池中讀取,但恐怕也是非常罕見的吧。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/76371.html
摘要:前段時間分享了閱讀跟蹤源碼的幾個小技巧是基于版本的,看大家的留言都是想要版本的源碼閱讀技巧。這樣你就能知道整個源碼的總體數(shù)量,你也可以把已經(jīng)閱讀的做統(tǒng)計,慢慢實現(xiàn)源碼閱讀的攻破。熟練的運用中各個小技巧,讓閱讀跟蹤源碼變得更輕松。 前段時間分享了《閱讀跟蹤 Java 源碼的幾個小技巧》是基于 Eclipse 版本的,看大家的留言都是想要 IDEA 版本的源碼閱讀技巧。 所以,為了滿足眾多...
摘要:今日看公司代碼時發(fā)現(xiàn),在和數(shù)組轉(zhuǎn)換的過程中,大量的無聊。所以寫了本文,作一個基本編程知識的小科普。分享一個編程的小技巧,簡單實用。而最要命的是,是繼承自類。 今日看公司代碼時發(fā)現(xiàn),在string和byte數(shù)組轉(zhuǎn)換的過程中,大量的無聊try catch。所以寫了本文,作一個java基本編程知識的小科普。 分享一個java編程的小技巧,簡單實用。 建議其實內(nèi)容就一句話: 在做String和...
摘要:重度強(qiáng)迫癥患者取消大小寫敏感,讓自動完成更齊全。如何為當(dāng)前項目工程添加多個模塊啊答對著工程右鍵選擇通常選擇,如圖寫在最后歡迎關(guān)注喜歡和點贊后續(xù)將推出更多的工具集教程,敬請期待。公眾號回復(fù)面試,領(lǐng)取面試實戰(zhàn)學(xué)習(xí)資源。 showImg(https://segmentfault.com/img/remote/1460000018862588); 前言:IntelliJ IDEA 如果說Int...
摘要:何為簡單點來定義就是切面,是一種編程范式。定義一個切面的載體定義一個切點定義一個為,并指定對應(yīng)的切點一個注冊配置類,啟動容器,初始化時期獲取對象,獲取對象時期,并進(jìn)行打印好了,這樣我們整體的代理就已經(jīng)完成。 問題:Spring AOP代理中的運行時期,是在初始化時期織入還是獲取對象時期織入? 織入就是代理的過程,指目標(biāo)對象進(jìn)行封裝轉(zhuǎn)換成代理,實現(xiàn)了代理,就可以運用各種代理的場景模式。 ...
閱讀 3480·2023-04-26 02:44
閱讀 1622·2021-11-25 09:43
閱讀 1510·2021-11-08 13:27
閱讀 1881·2021-09-09 09:33
閱讀 899·2019-08-30 15:53
閱讀 1762·2019-08-30 15:53
閱讀 2771·2019-08-30 15:53
閱讀 3106·2019-08-30 15:44