浅谈包装类缓存池技术

  • 回顾包装类的装箱和拆箱
  public class IntegerTest{
	  public static void main(String[]args){
		 Integer i=new Integer(4);//手动装箱 调用构造方法
		 Integer a=5;//自动装箱  自动调用valueOf方法
		 int b=i.intValue();//手动拆箱 调用intValue方法
		 int e=a;//自动拆箱 自动调用intValue方法
		 
	  }
  }

我们可以发现两种方法的拆箱调用的函数相同,但在装箱时调用的函数不同,下面我们将重点分析拆箱两种方法的优劣

Integer i = new Integer(xxx)和Integer i =xxx的区别

  • Integer i =xxx
    在实际进行时将自动调用valueOf()方法,该方法函数头为public static Integer valueOf(int i)返回一个对象,所有的基本类型都有这个方法,根据类不同返回不同类的对象
  • 包装类常量池(缓冲池技术)(以Integer类举例)
    在java中所有的包装类都有一个常量池,会将各种类数值在一定范围的对象被创建,在常量池(对象池)中Integer已经默认创建了数值【-128-127】的Integer缓存数据。所以使用Integer a=1时,JVM会直接在该在对象池找,找到该对象则直接指向值为1的对象。也就是说这种方式声明一个Integer对象时,JVM首先会在Integer对象的缓存池中查找有木有值为1的对象,如果有直接返回该对象的引用; 但是如果数值超出了这个范围,就将不会以这种方法创建对象,此时将会调用 new Integer()创建一个对象

这种方法可以推断出其创建对象的效率或者说速度会有很大的提升,但同时也可以发现他将极大的消耗内存,这是一个以以空间换时间的策略

  • Integer i = new Integer(xxx)
    用该类的构造方法创建对象,这是一个普遍的创建对象的方式,每次创建将会在堆中开辟一块内存用于存放对象

两种方式的区别
1)第一种方式不会触发自动装箱的过程;而第二种方式会触发;

    2)在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况,当值不在(-128-127)之间时(注意这并不是绝对的)。

  • 缓冲池作用理解举例
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y);    // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k);   // true
------

原文链接:https://pdai.tech/md/java/basic/java-basic-lan-basic.html

new Integer(123) 与 Integer.valueOf(123) 的区别在于:
new Integer(123) 每次都会新建一个对象Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用

此时valueOf方法的实现源码

public static Integer valueOf(int i) {
    final int offset = 128;
    if (i >= -128 && i <= 127) { // must cache
        return IntegerCache.cache[i + offset];
    }
        return new Integer(i);
}

先判断其是否在常量池中,如果在的话则直接返回缓冲池里面的内容

  //验证缓存池技术
  public class IntegerTest{
	  public static void main(String[]args){
		 Integer i=456;
		 Integer a=456;
		 System.out.println(i==a);//false
		 
	  }
  }

当超出其常量池的范围将会以构造函数的方式创建对象

看下面一个例子


  //验证缓存池技术
  public class IntegerTest{
	  public static void main(String[]args){
		 Double i=34.3;
		 Double j=34.3;
		 Double a=2.1;
		 Double b=2.1;
		 System.out.println(i==j);//false
		 System.out.println(a==b);//false
		 
		 
	  }
  }

可能会以为会输出true,但实际为false,在这里只解释一下为什么Double类的valueOf方法会采用与Integer类的valueOf方法不同的实现。很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是。

对包装类的valueOf进行总结
Integer派系: Integer Short Character Boolean Long的valueOf的实现是类似的
Double派系: Double Float的实现是类型的,每次返回不同的对象

一个例子说明一些情况

public class Main {
    public static void main(String[] args) {
         
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Integer d = 3;
        Integer e = 321;
        Integer f = 321;
        Long g = 3L;
        Long h = 2L;
         
        System.out.println(c==d);
        System.out.println(e==f);
        System.out.println(c==(a+b));
        System.out.println(c.equals(a+b));
        System.out.println(g==(a+b));
        System.out.println(g.equals(a+b));
        System.out.println(g.equals(a+h));
    }
}

以上代码原文

false
true
true
true
false
true

一些必知知识点

  • ==比较的是对象的地址
  • Object类的equals函数也是实现的地址的比较,如果其子类没有将equals函数覆写,调用equals函数将会调用Object类的equals
  • 当包装类的对象进行运算时会自动发生发生装箱和拆箱操作
    举例:
  public class IntegerTest{
	  public static void main(String[]args){
		int e=2;  
		Integer a=4;
		Integer b=5;
		Integer f=a+e;//a先拆箱成基本类型完成计算然后装箱成对象
		Integer c=a+b;//a 和b先自动拆箱进行计算然后又装箱成对象
		Integer d=a*b;//a和b先 拆箱进行计算然后装箱成对象
		System.out.println(f);//自动拆箱成基本类型
		System.out.println(c);//该过程会自动拆箱为基本类型输出
		System.out.println(d);//该过程会自动拆箱为基本类型输出
	  }
  }
  output:
6
9
20

当对象和对象或者对象和基本类型进行一些运算时,可以用自动拆箱和装箱,使其运算变得合理

  • 对于包装器类型,equals方法并不会进行类型转换

输出解释
第二个因为超出了常量池的范围,调用了构造方法创建了对象,地址值不同。第三个先会进行括号里的运算,先自动拆箱进行运算然后装箱成对象。第四个和第三个相同。第五个对象的类型不同将会进行对象的类型转换,所有为true。第六个对象类型不同但equals方法不能进行对象类型的转换所有为false。第七个括号里面先会自动拆箱,然后基本类型会都转换成long进行运算,然后自动装箱为Long类,所有最后为true

参考:
https://pdai.tech/
https://www.cnblogs.com/Pjson/p/8777940.html

posted @ 2022-11-15 12:54  一往而深,  阅读(130)  评论(0编辑  收藏  举报