参照タイプ?定数プール?パッケージクラスキャッシュ?くりを一つあげる

10690 ワード

テスト用のエンティティークラス

@Data
@Accessors(chain = true)
class Student{
    private Integer no;
    private String name;
    private String lesson;
}

1つのmapの値を取り出して変数に値を割り当てます。この変数は変更され、このmapも変更されます。

        // map
        Map hashMap = new HashMap<>();
        // studentMap 
        Map studentMap = new HashMap<>();
        studentMap.put("sname", "hi");
        studentMap.put("sno", 1);
        studentMap.put("lesson", "Python");
        // map 
        hashMap.put("hiObj",studentMap);
        // map 
        Student student = new Student().setNo(1).setName("OO").setLesson("C++");
        hashMap.put("stu", student);

        // 
        System.out.println(" :"+hashMap.get("hiObj"));
        System.out.println(" :"+hashMap.get("stu"));
        System.out.println(" :"+hashMap);
        
        // key "hiObj" studentMap 
        Map hiObj = (Map) hashMap.get("hiObj");
        hiObj.put("sname", "HELLO");
        hiObj.put("sno", 3);
        hiObj.put("lesson", "JavaScript");
        student.setLesson("Java").setName("HH").setNo(2);

        // 
        System.out.println(" :"+hashMap.get("hiObj"));
        System.out.println(" :"+hashMap.get("stu"));
        System.out.println(" :"+hashMap);
console:
 :{sno=1, sname=hi, lesson=Python}
 :Student(no=1, name=OO, lesson=C++)
 :{stu=Student(no=1, name=OO, lesson=C++), hiObj={sno=1, sname=hi, lesson=Python}}
 :{sno=3, sname=HELLO, lesson=JavaScript}
 :Student(no=2, name=HH, lesson=Java)
 :{stu=Student(no=2, name=HH, lesson=Java), hiObj={sno=3, sname=HELLO, lesson=JavaScript}}
  • hashMap.get(「hiObj」)はアドレスを変数に割り当てるもので、ポインタアドレスが指すオブジェクトは1つしかなく、本質的には1つのオブジェクトに対する修正であるが、異なる変数がそれを参照しているだけである、hashMap.get(「stu」)は同じです.

  • 文字列オブジェクトの参照

  • 文字列定数とnew文字列オブジェクトのアドレスの違いをどのように証明しますか
  • String json = new String("Json");
    System.out.println("json : "+json.hashCode());
    String json2 = new String("Json");
    System.out.println("json2 : "+json2.hashCode());
    System.out.println("test addr1;"+"Cola".hashCode());
    System.out.println("test addr2;"+"Cola".hashCode());      
    
    console:
    json : 2318600
    json2 : 2318600
    test addr1;2106113
    test addr2;2106113
    
  • 明らかに、StringタイプのhashCode()は書き換えた後も同じ文字列であればhashCodeは同じであるため、アドレス差
  • は見られない.
  • ここではSystemのnativeメソッドSystemを用いる.identityHashCode()は、オブジェクトがhashCode()を書き換えるかどうかにかかわらず、取得した物理メモリのアドレス値
  • である.
    String json = new String("Json");
    System.out.println("json : "+System.identityHashCode(json));
    String json2 = new String("Json");
    System.out.println("json2 : "+System.identityHashCode(json2));
    System.out.println("test addr1;"+System.identityHashCode("Cola"));
    System.out.println("test addr2;"+System.identityHashCode("Cola"));
    
    console;
    json : 939047783
    json2 : 1237514926
    test addr1;548246552
    test addr2;548246552
    
  • ここで、newの1つの文字列オブジェクトは、文字列が同じであってもアドレスが異なるが、字面量付与を直接使用するアドレスは、定数プールの同じメモリアドレス
  • を指すため、同じであることがわかる.
  • 上記のmapの例を理解しようとしなかったとき、「hiObj」のアドレスコピーをhiObjというmap変数に付与しただけで、その変更は元のhashMapの内部のオブジェクト状態に影響しないと思っていました.次の例
  • のように.
            Student student = new Student().setNo(new Integer(128)).setName(new String("Json")).setLesson("C++");
            System.out.println(" :" + student);
            System.out.println("student.getName() addr : " + System.identityHashCode(student.getName()));
            String sname = student.getName();
            System.out.println("sname addr :" + System.identityHashCode(sname));
            sname += "hui";
            System.out.println("after sname addr : " + System.identityHashCode(sname));
            System.out.println("after student.getName() addr : " + System.identityHashCode(student.getName()));
    
            System.out.println("student.getNo() addr : " + System.identityHashCode(student.getNo()));
            Integer num = student.getNo();
            System.out.println("num addr :" + System.identityHashCode(num));
            num += 130;
            System.out.println("after num addr : " + System.identityHashCode(num));
            System.out.println("after student.getNo() addr : " + System.identityHashCode(student.getNo()));
            System.out.println(" :" + student);
    
    console:
     :Student(no=128, name=Json, lesson=C++)
    student.getName() addr : 939047783
    sname addr :939047783
    after sname addr : 1237514926
    after student.getName() addr : 939047783
    student.getNo() addr : 548246552
    num addr :548246552
    after num addr : 835648992
    after student.getNo() addr : 548246552
     :Student(no=128, name=Json, lesson=C++)
    
  • StringもIntegerも、元のオブジェクトstudentにアドレスを変数に割り当てる.getXxx()は影響しませんが、hiObjアドレスは変わりません.本質的にはこのアドレスのデータの変更です.変更するかどうかにかかわらず、このアドレスはhashMapのkey「hiObj」によって参照されています.

  • Stringオブジェクトと文字数の文字列の古典的な比較

            String s1 = "good";
            String s2 = "good";
            String s3 = new String("good");
            String s4 = "go";
            String s5 = "od";
            String s6 = new String("go");
            String s7 = new String("od");
            String s8 = "go" + "od";
            String s9 = new String("go" + "od");
    
            System.out.println("s1==s2 : " + (s1 == s2));
            System.out.println("s1==s3 : " + (s1 == s3));
            System.out.println("s1==s4+s5 : " + (s1 == s4 + s5));
            System.out.println("s1==s4+s6 : " + (s1 == s4 + s6));
            System.out.println("s1==s6+s7 : " + (s1 == s6 + s7));
            System.out.println("s1==s6+s5 : " + (s1 == s6 + s5));
            System.out.println("s3==s6+s7 : " + (s3 == s6 + s7));
            System.out.println("s1==s8 : " + (s1 == s8));
            System.out.println("s3==s8 : " + (s3 == s8));
            System.out.println("s1==s9 : " + (s1 == s9));
            System.out.println("s3==s9 : " + (s3 == s9));
    console:
    s1==s2 : true
    s1==s3 : false
    s1==s4+s5 : false
    s1==s4+s6 : false
    s1==s6+s7 : false
    s1==s6+s5 : false
    s3==s6+s7 : false
    s1==s8 : true
    s3==s8 : false
    s1==s9 : false
    s3==s9 : false
    
  • は定数プールと同じ文字列定数アドレスを指すが、s 4+s 5とs 8の違いは、s 8の2つの文字列がコンパイル期間中に定数プールと「good」に接続され、定数プールにはすでに「good」が存在するため、アドレスをs 8に割り当て、s 4+s 5は実行期間中に動的に接続を実行し、メモリアドレスを接続された文字列に再割り当てすることである.
  • 他のnewに関連するものは、スタックに異なるメモリアドレス
  • を割り当てる.

    包装類Integerの経典の対比

    Student student = new Student().setNo(new Integer(128)).setName(new String("Json")).setLesson("C++");
            Integer num = student.getNo();
            System.out.println("1,num ==new Integer(128) : "+(num ==new Integer(128)));
            System.out.println("2,128 ==new Integer(128) : "+(128 ==new Integer(128)));
            System.out.println("3,num.equals(new Integer(128)) : "+num.equals(new Integer(128)));
            System.out.println("4,new Integer(128).equals(128) : "+new Integer(128).equals(128));
    
            Integer a=127;
            Integer b=127;
            Integer c = 0;
            Integer d = new Integer(128);
            Integer e = new Integer(128);
            Integer f = new Integer(0);
            Integer g=128;
            Integer h=128;
            
            System.out.println("a==b   " + (a == b));
            System.out.println("a==b+c   " + (a == b + c));
            System.out.println("a==d   " + (a == d));
            System.out.println("d==e   " + (d == e));
            System.out.println("d.equals(e)   " + d.equals(e));
            System.out.println("d==e+f   " + (d == e + f));
            System.out.println("127==e+f   " + (127 == e + f));
            System.out.println("g==h   " + (g == h));
            System.out.println("g.equals(h)   " + g.equals(h));
    
    console:
    1,num ==new Integer(128) : false
    2,128 ==new Integer(128) : true
    3,num.equals(new Integer(128)) : true
    4,new Integer(128).equals(128) : true
    a==b   true
    a==b+c   true
    a==d   false
    d==e   false
    d.equals(e)   true
    d==e+f   true
    127==e+f   false
    g==h   false
    g.equals(h)   true
    
  • "=="比較アドレス、1,2アドレスが
  • 異なる
  • ,3,4比較値は,Integer書き換えequalsメソッドがintValueメソッドを呼び出し,最終的にはintを比較するため,Integerオブジェクトを比較する際にequalsメソッドを使用することを提案する.
  • public boolean equals(Object obj) {
            if (obj instanceof Integer) {
                return value == ((Integer)obj).intValue();
            }
            return false;
        }
    
  • 127==e+f、Integerはintと比較して、箱を分解してintになってから比較して、演算子に出会っても先に箱を分解してから計算します.
  • a=b,g=h,1つはtrue,1つはfalseの原因はIntegerCacheであり,値の範囲が[-128127]であればキャッシュ配列から直接値を取り,メモリアドレスは同じであるが,そうでなければ異なる.
  • private static class IntegerCache {
            static final int low = -128;
            static final int high;
            static final Integer cache[];
    
            static {
                // high value may be configured by property
                int h = 127;
                String integerCacheHighPropValue =
                    sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
                if (integerCacheHighPropValue != null) {
                    try {
                        int i = parseInt(integerCacheHighPropValue);
                        i = Math.max(i, 127);
                        // Maximum array size is Integer.MAX_VALUE
                        h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                    } catch( NumberFormatException nfe) {
                        // If the property cannot be parsed into an int, ignore it.
                    }
                }
                high = h;
    
                cache = new Integer[(high - low) + 1];
                int j = low;
                for(int k = 0; k < cache.length; k++)
                    cache[k] = new Integer(j++);
    
                // range [-128, 127] must be interned (JLS7 5.1.7)
                assert IntegerCache.high >= 127;
            }
    
            private IntegerCache() {}
        }
    //valueOf(int i) 
    public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }
    

    ここまでもし手落ちがあれば,皆さんのご指導をお願いいたします