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

JDK源碼中一些實(shí)用的“小技巧”總結(jié)

 更新時(shí)間:2018年03月28日 08:33:19   作者:方老司  
這篇文章主要給大家總結(jié)介紹了關(guān)于JDK源碼中一些實(shí)用的“小技巧”,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用jdk源碼具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。

前言

這段時(shí)間比較閑,就看起了jdk源碼。一般的一個(gè)高級(jí)開發(fā)工程師, 能閱讀一些源碼對(duì)自己的提升還是蠻大的。本文總結(jié)了一些JDK源碼中的“小技巧”,分享出來供大家參考學(xué)習(xí),下面話不多說了,來一起看看詳細(xì)的介紹吧。

1 i++ vs i--

String源碼的第985行,equals方法中

 while (n--!= 0) {
  if (v1[i] != v2[i])
   return false;
  i++;   
 }

這段代碼是用于判斷字符串是否相等,但有個(gè)奇怪地方是用了i--!=0來做判斷,我們通常不是用i++么?為什么用i--呢?而且循環(huán)次數(shù)相同。原因在于編譯后會(huì)多一條指令:

i-- 操作本身會(huì)影響CPSR(當(dāng)前程序狀態(tài)寄存器),CPSR常見的標(biāo)志有N(結(jié)果為負(fù)), Z(結(jié)果為0),C(有進(jìn)位),O(有溢出)。i > 0,可以直接通過Z標(biāo)志判斷出來。

i++操作也會(huì)影響CPSR(當(dāng)前程序狀態(tài)寄存器),但只影響O(有溢出)標(biāo)志,這對(duì)于i < n的判斷沒有任何幫助。所以還需要一條額外的比較指令,也就是說每個(gè)循環(huán)要多執(zhí)行一條指令。

簡單來說,跟0比較會(huì)少一條指令。所以,循環(huán)使用i--,高端大氣上檔次。

2 成員變量 vs 局部變量

JDK源碼在任何方法中幾乎都會(huì)用一個(gè)局部變量來接受成員變量,比如

public int compareTo(String anotherString) {
  int len1 = value.length;
  int len2 = anotherString.value.length;

因?yàn)榫植孔兞砍跏蓟笫窃谠摲椒ň€程棧中,而成員變量初始化是在堆內(nèi)存中,顯然前者更快,所以,我們?cè)诜椒ㄖ斜M量避免直接使用成員變量,而是使用局部變量。

3 刻意加載到寄存器 && 將耗時(shí)操作放到鎖外部

在ConcurrentHashMap中,鎖segment的操作很有意思,它不是直接鎖,而是類似于自旋鎖,反復(fù)嘗試獲取鎖,并且在獲取鎖的過程中,會(huì)遍歷鏈表,從而將數(shù)據(jù)先加載到寄存器中緩存中,避免在鎖的過程中在便利,同時(shí),生成新對(duì)象的操作也是放到鎖的外部來做,避免在鎖中的耗時(shí)操作

  final V put(K key, int hash, V value, boolean onlyIfAbsent) {
    /** 在往該 segment 寫入前,需要先獲取該 segment 的獨(dú)占鎖
      不是強(qiáng)制lock(),而是進(jìn)行嘗試 */
    HashEntry<K,V> node = tryLock() ? null :
      scanAndLockForPut(key, hash, value);

scanAndLockForPut()源碼

private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
  HashEntry<K,V> first = entryForHash(this, hash);
  HashEntry<K,V> e = first;
  HashEntry<K,V> node = null;
  int retries = -1; // negative while locating node

  // 循環(huán)獲取鎖
  while (!tryLock()) {
    HashEntry<K,V> f; // to recheck first below
    if (retries < 0) {
      if (e == null) {
        if (node == null) // speculatively create node
          //該hash位無值,新建對(duì)象,而不用再到put()方法的鎖中再新建
          node = new HashEntry<K,V>(hash, key, value, null);
        retries = 0;
      }
      //該hash位置key也相同,退化成自旋鎖
      else if (key.equals(e.key))
        retries = 0;
      else
        // 循環(huán)鏈表,cpu能自動(dòng)將鏈表讀入緩存
        e = e.next;
    }
    // retries>0時(shí)就變成自旋鎖。當(dāng)然,如果重試次數(shù)如果超過 MAX_SCAN_RETRIES(單核1多核64),那么不搶了,進(jìn)入到阻塞隊(duì)列等待鎖
    //  lock() 是阻塞方法,直到獲取鎖后返回,否則掛起
    else if (++retries > MAX_SCAN_RETRIES) {
      lock();
      break;
    }
    else if ((retries & 1) == 0 &&
         // 這個(gè)時(shí)候是有大問題了,那就是有新的元素進(jìn)到了鏈表,成為了新的表頭
         //   所以這邊的策略是,相當(dāng)于重新走一遍這個(gè) scanAndLockForPut 方法
         (f = entryForHash(this, hash)) != first) {
      e = first = f; // re-traverse if entry changed
      retries = -1;
    }
  }
  return node;
}

4 判斷對(duì)象相等可先用==

在判斷對(duì)象是否相等時(shí),可先用==,因?yàn)?=直接比較地址,非??欤鴈quals的話會(huì)最對(duì)象值的比較,相對(duì)較慢,所以有可能的話,可以用a==b || a.equals(b)來比較對(duì)象是否相等

5 關(guān)于transient

transient是用來阻止序列化的,但HashMap源碼中內(nèi)部數(shù)組是定義為transient的

 /**
   * The table, resized as necessary. Length MUST Always be a power of two.
   */
  transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

那豈不里面的鍵值對(duì)都無法序列化了么,網(wǎng)絡(luò)中用hashmap來傳輸豈不是無法傳輸,其實(shí)不然。

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()來確定從哪個(gè)bucket讀/寫. 而Object.hashcode()是native方法, 不同的JVM里可能是不一樣的.

打個(gè)比方說, 向HashMap存一個(gè)entry, key為 字符串"STRING", 在第一個(gè)java程序里, "STRING"的hashcode()為1, 存入第1號(hào)bucket; 在第二個(gè)java程序里, "STRING"的hashcode()有可能就是2, 存入第2號(hào)bucket. 如果用默認(rèn)的串行化(Entry[] table不用transient), 那么這個(gè)HashMap從第一個(gè)java程序里通過串行化導(dǎo)入第二個(gè)java程序后, 其內(nèi)存分布是一樣的, 這就不對(duì)了.

舉個(gè)例子,比如向HashMap存一個(gè)鍵值對(duì)entry, key="方老司", 在第一個(gè)java程序里, "方老司"的hashcode()為1, 存入table[1],好,現(xiàn)在傳到另一個(gè)在JVM程序里, "方老司" 的hashcode()有可能就是2, 于是到table[2]去取,結(jié)果值不存在。

HashMap現(xiàn)在的readObject和writeObject是把內(nèi)容 輸出/輸入, 把HashMap重新生成出來.

6 不要用char

char在Java中utf-16編碼,是2個(gè)字節(jié),而2個(gè)字節(jié)是無法表示全部字符的。2個(gè)字節(jié)表示的稱為 BMP,另外的作為high surrogate和 low surrogate 拼接組成由4字節(jié)表示的字符。比如String源碼中的indexOf:

 //這里用int來接受一個(gè)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)到四個(gè)字節(jié)的判斷方式
      return indexOfSupplementary(ch, fromIndex);
    }
  }

所以Java的char只能表示utf­16中的bmp部分字符。對(duì)于CJK(中日韓統(tǒng)一表意文字)部分?jǐn)U展字符集則無法表示。

例如,下圖中除Ext-A部分,char均無法表示。

此外還有一種說法是要用char,密碼別用String,String是常量(即創(chuàng)建之后就無法更改),會(huì)保存到常量池中,如果有其他進(jìn)程可以dump這個(gè)進(jìn)程的內(nèi)存,那么密碼就會(huì)隨著常量池被dump出去從而泄露,而char[]可以寫入其他的信息從而改變,即是被dump了也會(huì)減少泄露密碼的風(fēng)險(xiǎn)。

但個(gè)人認(rèn)為你都能dump內(nèi)存了難道是一個(gè)char能夠防范的住的?除非是String在常量池中未被回收,而被其它線程直接從常量池中讀取,但恐怕也是非常罕見的吧。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • Java中生成微信小程序太陽碼的實(shí)現(xiàn)方案

    Java中生成微信小程序太陽碼的實(shí)現(xiàn)方案

    這篇文章主要介紹了Java中生成微信小程序太陽碼的實(shí)現(xiàn)方案,本文講解了如何生成微信小程序太陽碼,通過微信提供的兩種方案都可以實(shí)現(xiàn),在實(shí)際的項(xiàng)目中建議采用第二種方案,需要的朋友可以參考下
    2022-05-05
  • 學(xué)習(xí)Java之自定義異常與NullPointerException的處理

    學(xué)習(xí)Java之自定義異常與NullPointerException的處理

    有時(shí)候Java自身提供的異常類并不能很好地表達(dá)我們的需求,所以這時(shí)候我們就可以自定義異常,也就是說,我們可以制造出一個(gè)自己的異常類,這樣就可以拋出或捕獲自己的異常了,本文就給大家詳細(xì)講講Java自定義異常與NullPointerException的處理
    2023-08-08
  • 詳解Spring基于xml的兩種依賴注入方式

    詳解Spring基于xml的兩種依賴注入方式

    這篇文章主要介紹了詳解Spring基于xml的兩種依賴注入方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • pdf2swf+flexpapers實(shí)現(xiàn)類似百度文庫pdf在線閱讀

    pdf2swf+flexpapers實(shí)現(xiàn)類似百度文庫pdf在線閱讀

    這篇文章主要介紹了pdf2swf+flexpapers實(shí)現(xiàn)類似百度文庫pdf在線閱讀的相關(guān)資料,需要的朋友可以參考下
    2014-10-10
  • SpringBoot java-jar命令行啟動(dòng)原理解析

    SpringBoot java-jar命令行啟動(dòng)原理解析

    這篇文章主要介紹了SpringBoot java-jar命令行啟動(dòng)原理解析,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Spring Data MongoDB 數(shù)據(jù)庫批量操作的方法

    Spring Data MongoDB 數(shù)據(jù)庫批量操作的方法

    在項(xiàng)目開發(fā)中經(jīng)常會(huì)批量插入數(shù)據(jù)和更新數(shù)據(jù)的操作,這篇文章主要介紹了Spring Data MongoDB 數(shù)據(jù)庫批量操作的方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2018-11-11
  • Java transient關(guān)鍵字原理解析

    Java transient關(guān)鍵字原理解析

    這篇文章主要介紹了Java transient關(guān)鍵字原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • SpringBoot獲取配置信息的三種方式總結(jié)

    SpringBoot獲取配置信息的三種方式總結(jié)

    這篇文章給大家介紹了SpringBoot獲取配置信息的三種方式,@Value屬性值注入,綁定配置類和通過 environment獲取這三種方式,文中通過代碼示例給大家介紹的非常詳細(xì),具有一定的參考價(jià)值,需要的朋友可以參考下
    2024-01-01
  • 如何通過JVM角度談?wù)凧ava的clone操作

    如何通過JVM角度談?wù)凧ava的clone操作

    java中僅有的創(chuàng)建對(duì)象的兩種方式:①.使用new操作符創(chuàng)建對(duì)象;②.使用clone方法復(fù)制對(duì)象。下面這篇文章主要通過JVM角度給大家詳細(xì)談?wù)凧ava的clone操作的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2018-06-06
  • django 遞歸查詢?cè)u(píng)論的示例詳解

    django 遞歸查詢?cè)u(píng)論的示例詳解

    文章介紹了將表數(shù)據(jù)轉(zhuǎn)換為樹狀結(jié)構(gòu)以及如何處理扁平化數(shù)據(jù)的方法,探討了在數(shù)據(jù)管理中的應(yīng)用和實(shí)現(xiàn)技術(shù),感興趣的朋友一起看看吧
    2025-01-01

最新評(píng)論

肇东市| 信丰县| 余江县| 蓬莱市| 宣武区| 临邑县| 莲花县| 山丹县| 赤峰市| 禹城市| 壤塘县| 时尚| 灵璧县| 蓬安县| 格尔木市| 徐闻县| 略阳县| 台中市| 喀什市| 行唐县| 三都| 顺义区| 安多县| 合阳县| 融水| 呼图壁县| 吴桥县| 鄂尔多斯市| 无锡市| 赤峰市| 禄丰县| 白水县| 潼关县| 五河县| 乌拉特中旗| 滁州市| 二手房| 同心县| 东乡县| 隆尧县| 黑水县|