• 引用类型?常量池?包装类缓存?举个栗子


    测试用的实体类

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

    将一个map中的值取出来赋值给变量,此变量更改,这个map也会更改

            //存放对象的map
            Map<String, Object> hashMap = new HashMap<>();
            //新建一个studentMap存信息
            Map<Object, Object> 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<Object, Object> hiObj = (Map<Object, Object>) 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")是把地址赋值给变量,指针地址所指的对象只有一个,本质上都是对一个对象的修改,只是不同的变量在引用它,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一个字符串对象即使字符串相同但是地址是不一样的,但是直接使用字面量赋值的地址是一样的,因为指向的是常量池的同一内存地址
    • 我没想通上面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
    
    • 指向常量池同一个字符串常量地址相同,而s4+s5和s8的区别在于,s8的两个字符串在编译期已经存在与常量池,拼接成"good",而常量池中已经存在"good",
      所以将地址赋值给s8,而s4+s5是在运行期动态执行拼接的,会重新分配内存地址给拼接好的字符串。
    • 其他和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,一个为true,一个为false的原因是IntegerCache,如果值的范围是[-128,127],那直接从缓存数组中取值,内存地址就是同一个,否则不同。
    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);
        }
    

    至此,若有纰漏,望各位不吝赐教

  • 相关阅读:
    CSS揭秘(六用户体验)
    CSS揭秘(五字体排印)
    CSS揭秘(四视觉效果)
    java split() 使用 . 来分割,转义(“” "|" "*" "+")
    springboot get请求405 Method Not Allowed
    websocket前端消息读取问题
    java解决中文乱码问题(jar包运行时中文返回前端数据或者控制台输出乱码问题)
    解决mysql 允许执行 XA RECOVER语句(atomikos 解决分布式事务报错)
    navicat修改mysql密码
    mybatisplus添加字段填充
  • 原文地址:https://www.cnblogs.com/0nePlece/p/13258639.html
Copyright © 2020-2023  润新知