質問
これに似たコードを見ました:
public class Scratch
{
public static void main(String[] args)
{
Integer a = 1000, b = 1000;
System.out.println(a == b);
Integer c = 100, d = 100;
System.out.println(c == d);
}
}
実行すると、このコードブロックが印刷されます。
false
true
最初がなぜなのか理解できました false
: :2つのオブジェクトが別々のオブジェクトであるため、 ==
参照を比較します。しかし、私は理解することはできません、なぜ2番目の声明が戻っているのか true
?整数の値が特定の範囲にあるときにキックする奇妙な自動ボクシングルールはありますか?何が起きてる?
解決
true
ラインは実際に言語仕様によって保証されます。から セクション5.1.7:
箱入りの値pが真、false、バイト、範囲 u0000〜 u007fのchar、または-128から127の間のintまたは短い数の場合、R1とR2を2つのボクシング変換の結果としますp。 R1 == R2の場合は常に当てはまります。
議論は続き、2番目の出力が保証されているが、最初の行は保証されていないことを示唆している(以下の引用の最後の段落を参照):
理想的には、特定のプリミティブ値pをボクシングすると、常に同じ参照が得られます。実際には、これは既存の実装手法を使用して実現可能ではない場合があります。上記のルールは実用的な妥協です。上記の最終条項では、特定の共通の値を常に区別できないオブジェクトにボックス化することが必要です。実装は、これらを怠zyまたは熱心にキャッシュする場合があります。
他の値については、この定式化は、プログラマー側の箱入り値のアイデンティティに関する仮定を拒否します。これにより、これらの参照の一部またはすべての共有が可能になります(必要ありません)。
これにより、ほとんどの一般的なケースでは、特に小型デバイスに過度のパフォーマンスペナルティを課すことなく、動作が望ましいものになることが保証されます。メモリに限定された実装が少ない場合、たとえば、すべてのキャラクターとショートパンツ、および-32K- +32Kの範囲の整数とロングをキャッシュする場合があります。
他のヒント
public class Scratch
{
public static void main(String[] args)
{
Integer a = 1000, b = 1000; //1
System.out.println(a == b);
Integer c = 100, d = 100; //2
System.out.println(c == d);
}
}
出力:
false
true
うん、最初の出力は、参照を比較するために生成されます。 「A」と「B」 - これらは2つの異なる参照です。ポイント1では、実際には、次のような2つの参照が作成されます。
Integer a = new Integer(1000);
Integer b = new Integer(1000);
2番目の出力が生成されます JVM
メモリを保存しようとします Integer
範囲(-128から127)に落ちます。ポイント2では、「d」用のタイプ整数の新しい参照は作成されていません。整数型参照変数「D」の新しいオブジェクトを作成する代わりに、「C」で参照された以前に作成されたオブジェクトでのみ割り当てられています。これらはすべてによって行われます JVM
.
これらのメモリ保存ルールは、整数だけではありません。メモリ保存の目的では、次のラッパーオブジェクトの2つのインスタンス(ボクシングを介して作成されています)は、常にプリミティブ値が同じである==です -
- ブール
- バイト
- からのキャラクター u0000 に
\u007f
(7Fは10進数で127です) - 短くて整数から -128 に 127
ある範囲の整数オブジェクト(おそらく-128〜127)がキャッシュされて再利用されます。その範囲外の整数は、毎回新しいオブジェクトを取得します。
はい、値が特定の範囲にあるときに始まる奇妙な自動ボクシングルールがあります。オブジェクト変数に定数を割り当てると、言語定義には新しいオブジェクトが表示されないものはありません しなければならない 作成されます。既存のオブジェクトをキャッシュから再利用する場合があります。
実際、JVMは通常、この目的のために小さな整数のキャッシュと、boolean.trueやboolean.falseなどの値を保存します。
それは興味深い点です。本の中で 効果的なJava あなた自身のクラスの平等を常にオーバーライドすることを提案します。また、Javaクラスの2つのオブジェクトインスタンスの平等を確認するには、常にEqualsメソッドを使用します。
public class Scratch
{
public static void main(String[] args)
{
Integer a = 1000, b = 1000;
System.out.println(a.equals(b));
Integer c = 100, d = 100;
System.out.println(c.equals(d));
}
}
戻り値:
true
true
私の推測では、Javaは、新しいオブジェクトを作成するよりも既存のオブジェクトを再利用するために多くの時間を節約するため、すでに「箱入り」である小さな整数のキャッシュを保持しています。
Javaでは、整数の-128〜127の範囲でボクシングが機能します。この範囲で番号を使用している場合、==演算子と比較できます。範囲外の整数オブジェクトの場合、使用する必要があります。
INTリテラルの整数参照への直接割り当ては、オブジェクト変換コードに対するリテラル値がコンパイラによって処理される自動ボクシングの例です。
したがって、コンパイルフェーズコンパイラコンバージョン中に Integer a = 1000, b = 1000;
に Integer a = Integer.valueOf(1000), b = Integer.valueOf(1000);
.
だからそうです Integer.valueOf()
実際に整数オブジェクトを提供する方法、およびのソースコードを見ると Integer.valueOf()
方法-128〜127(包括的)の範囲で整数オブジェクトをキャッシュする方法をはっきりと見ることができます。
/**
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
したがって、新しい整数オブジェクトを作成して返す代わりに、 Integer.valueOf()
このメソッドは、内部から整数オブジェクトを返します IntegerCache
通過したINTリテラルが-128を超え、127未満の場合。
Javaはこれらの整数オブジェクトをキャッシュします。なぜなら、この整数の範囲は、間接的に記憶を保存する日々のプログラミングで多く使用されるからです。
キャッシュは、静的ブロックのためにクラスがメモリにロードされるときに最初の使用法で初期化されます。キャッシュの最大範囲は、 -XX:AutoBoxCacheMax
JVMオプション。
このキャッシング動作は、整数オブジェクトのみに適用できず、整数に似ています。 ByteCache, ShortCache, LongCache, CharacterCache
にとって Byte, Short, Long, Character
それぞれ。
私の記事で詳細を読むことができます Java integerキャッシュ -なぜinteger.valueof(127)== integer.valueof(127)が真実です.
Java 5では、メモリを保存し、整数型オブジェクトハンドリングのパフォーマンスを向上させるために新しい機能が導入されました。整数オブジェクトは内部でキャッシュされ、同じ参照オブジェクトを介して再利用されます。
これは、–127〜 +127(最大整数値)の範囲の整数値に適用されます。
この整数キャッシングは、自動ボクシングでのみ機能します。整数オブジェクトは、コンストラクターを使用して構築されたときにキャッシュされません。
詳細については、plsを以下に説明します。
のソースコードを確認した場合 Integer
肥大化した源が見つかります valueOf
このような方法:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
その理由を説明できます Integer
-128からの範囲のオブジェクト(Integer.low
)127(Integer.high
)、自動ボクシング中に参照されたオブジェクトと同じです。そして、クラスがあることがわかります IntegerCache
世話をします Integer
キャッシュアレイ。 Integer
クラス。
別の興味深い例があります。この奇妙な状況を理解するのに役立つかもしれません。
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Class cache = Integer.class.getDeclaredClasses()[0];
Field myCache = cache.getDeclaredField("cache");
myCache.setAccessible(true);
Integer[] newCache = (Integer[]) myCache.get(cache);
newCache[132] = newCache[133];
Integer a = 2;
Integer b = a + a;
System.out.printf("%d + %d = %d", a, a, b); //The output is: 2 + 2 = 5
}