zoukankan      html  css  js  c++  java
  • Java 常用类(二):包装类(Wrapper)

    一、包装类概述

      1、为什么需要包装类

        Java并不是面向对象的语言。Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的。基本数据类型有它的优势:性能(效率高,节省空间)。

           在实际使用中经常需要将基本数据类型转成对象,这时就需要将基本数据类型数据转化为对象。

      2、包装类

        Java 提供了两个类型系统:基本类型引用类型

        使用基本类型在于效率,然后很多情况,需要创建对象使用,因为对象可以做更多的功能。

        

        基本类型对应的包装类      

    基本类型对应的包装类(位于java.lang包中)
    byte Byte
    short Short
    int Integer
    long Long
    float Float
    double Double
    char Character
    boolean Boolean

          有了类的特点,就可以调用类中的方法, Java才是真正的面向对象

          

    二、装箱与拆箱

       基本类型与对应包装类对象之间,来回转换的过程称为“装箱”与“拆箱”。

         装箱:从基本类型转换到对应的包装类对象;

            拆箱:从包装类对象转换到对应的基本类型。

      1、装箱:把基本类型的数据,包装到包装类中(基本类型的数据->包装类)

        构造方法

    Integer(int value) 构造一个新分配的 Integer 对象,它表示指定的 int 值。
    Integer(String s) 构造一个新分配的 Integer 对象,它表示 String 参数所指示的 int 值。传递的字符串,必须是基本类型的字符串,否则会抛出异常 如:"100" 正确;  "a" 抛出NumberFormatException数字格式化异常
    

      

        静态方法:

    static Integer valueof(int i):返回一个表示指定的 int 值的 Integer 实例。
    static Integer valueof(String a):返回保存指定的 String 的值的 Integer 对象。
    

      

      2、拆箱:在包装类中取出基本类型的数据(包装类->基本类型的数据)

        成员方法:

    int intValue() 以 int 类型返回该 Integer 的值。
    调用包装类的.xxxValue() 方法

      Demo:

     1  // 装箱
     2  //构造方法
     3 Integer in1 = new Integer(1);
     4 Integer in2 = new Integer("1");
     5 
     6  //静态方法
     7 Integer in3 = Integer.valueOf(1);
     8 Integer in4 = Integer.valueOf("1");
     9 
    10  // 拆箱
    11  int i = in1.intValue();

    三、自动装箱(auto_boxing)与自动拆箱(unboxing)

        由于经常要做基本类型与包装类之间的转换,从 Java5(JDK1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成。

      自动装箱:当把基本数据类型的值,赋值给包装类的变量时,就会自动装箱。

      自动拆箱:把包装类的对象赋值给对应的基本数据类型的变量时,就会自动拆箱。

      注意:自动装箱与拆箱都是只发送在对应类型上。

      Demo:

    1 Integer i = 3;     // 自动装箱。相当于Integer i = Integer.valueOf(3);
    2 i = i + 5;         // 等号右边:将 i 对象转成基本数值(自动拆箱) i.intValue() + 5;
    3                    // 加法运算完成后,再次装箱,把基本数值转成对象。

    四、基本数据类型、包装类与 String之间的转换

      1、基本数据类型 -》 包装类

        方式一:调用包装类的对应的构造器

    1int 转 Integer 为例:
    2 public Integer(int value) {
    3         this.value = value;
    4 }
    5 
    6 public Integer(String s) throws NumberFormatException {   //当参数为 String时,可能会抛异常
    7         this.value = parseInt(s, 10);
    8 }

        方式二:调用对应包装类静态方法 valueOf()

    1 public static Integer valueOf(int i) {
    2          if (i >= IntegerCache.low && i <= IntegerCache.high)
    3              return IntegerCache.cache[i + (-IntegerCache.low)];
    4          return new Integer(i);
    5 }
    6 
    7 public static Integer valueOf(String s) throws NumberFormatException {
    8         return Integer.valueOf(parseInt(s, 10));
    9     }

      2、包装类 -》 基本数据类型

        方式一:调用包装类Xxx的 xxxValue() 方法

    1 public int intValue() {
    2         return value;
    3 }

        扩展:可以发展 Integer中不仅仅有 intValue()方法

          

           调用其对应的 xxxValue() 方法,其实内部帮助我们做了强转。

        方式二:自动拆箱

      3、基本数据类型、包装类 -》String

        方式一:使用“+” 进行连接运算

    int num1 = 10;
    String str1 = num1 + "";

        方式二:调用 String的 valueOf(Xxx xxx)

     1    public static String valueOf(double d) {
     2         return Double.toString(d);
     3    }
     4    public static String valueOf(float f) {
     5         return Float.toString(f);
     6    }
     7    public static String valueOf(long l) {
     8         return Long.toString(l);
     9    }
    10     public static String valueOf(int i) {
    11         return Integer.toString(i);
    12     }
    13     public static String valueOf(char c) {
    14         char data[] = {c};
    15         return new String(data, true);
    16     }
    17     public static String valueOf(boolean b) {
    18         return b ? "true" : "false";
    19     }

           Demo:

    1 float f1 = 12.3f;
    2 String str2 = String.valueOf(f1);//"12.3"
    3         
    4 Double d1 = new Double(12.4);
    5 String str3 = String.valueOf(d1);
    6 System.out.println(str2);
    7 System.out.println(str3);//"12.4"

      4、String -》基本数据类型、包装类

        除了 Character 类之外,其他所有包装类都具有 parseXxx 静态方法可以将字符串参数转换为对应的基本类型。

    1 public static byte parseByte(String s):将字符串参数转换为对应的byte基本类型。
    2 public static short parseShort(String s):将字符串参数转换为对应的short基本类型。
    3 public static int parseInt(String s):将字符串参数转换为对应的int基本类型。
    4 public static long parseLong(String s):将字符串参数转换为对应的long基本类型。
    5 public static float parseFloat(String s):将字符串参数转换为对应的float基本类型。
    6 public static double parseDouble(String s):将字符串参数转换为对应的double基本类型。
    7 public static boolean parseBoolean(String s):将字符串参数转换为对应的boolean基本类型。

      注意如果字符串参数的内容无法正确转换为对应的基本类型,则会抛出java.lang.NumberFormatException异常。

      5、总结

        

    五、包装类的缓存(面试题)

      在实际编程时可能需要各种各样的包装类对象,如果只能通过 new 来创建,需要在堆中开辟大量值一样的包装类对象。

      这是非常不划算的,所以 Java 对于大部分包装类都设置了缓存。

      

      1、Boolean 类

        Boolean 的包装类型,缓存最简单,直接定义为静态常量就可以。

        部分源码:

    1 public final class Boolean implements java.io.Serializable, Comparable<Boolean> {
    2     public static final Boolean TRUE = new Boolean(true);
    3     public static final Boolean FALSE = new Boolean(false);  
    4     public static Boolean valueOf(boolean b) {
    5         return (b ? TRUE : FALSE);
    6     }
    7 }

      2、Character 类

        ASCII 码范围为 0-127,这里只缓存 ASCII 码范围的 char

        Character 静态内部类:CharacterCache

        部分源码:

     1 public final
     2 class Character implements java.io.Serializable, Comparable<Character> {
     3     private static class CharacterCache {
     4         private CharacterCache(){}
     5 
     6         static final Character cache[] = new Character[127 + 1];
     7 
     8         static {
     9             for (int i = 0; i < cache.length; i++)
    10                 cache[i] = new Character((char)i);
    11         }
    12     }
    13 
    14     public static Character valueOf(char c) {
    15         if (c <= 127) { // must cache
    16             return CharacterCache.cache[(int)c];
    17         }
    18         return new Character(c);
    19     }
    20   
    21 }

        通过源码可以看出来, Character 的缓存范围是 [0, 127]

      3、Byte 类

        byte 的范围是:[-128, 127],所以 Byte 一定是从缓存里面获取。

        部分源码:

     1 public final class Byte extends Number implements Comparable<Byte> {
     2     private static class ByteCache {
     3         private ByteCache(){}
     4 
     5         static final Byte cache[] = new Byte[-(-128) + 127 + 1];
     6 
     7         static {
     8             for(int i = 0; i < cache.length; i++)
     9                 cache[i] = new Byte((byte)(i - 128));
    10         }
    11     }
    12 
    13     public static Byte valueOf(byte b) {
    14         final int offset = 128;
    15         return ByteCache.cache[(int)b + offset];
    16     }
    17 
    18 }

        可以看出,Byte 一共缓存了 256个数,把整个Byte范围都缓存了。

      4、Short 类(缓存范围为 256)

        和byte 范围一样, 但是如果超过了 -128<=x<=127 这个范围,就不会从缓存中获取了。

        部分源码:

     1 public final class Short extends Number implements Comparable<Short> {
     2      private static class ShortCache {
     3         private ShortCache(){}
     4 
     5         static final Short cache[] = new Short[-(-128) + 127 + 1];
     6 
     7         static {
     8             for(int i = 0; i < cache.length; i++)
     9                 cache[i] = new Short((short)(i - 128));
    10         }
    11     }
    12 
    13     public static Short valueOf(short s) {
    14         final int offset = 128;
    15         int sAsInt = s;
    16         if (sAsInt >= -128 && sAsInt <= 127) { // must cache
    17             return ShortCache.cache[sAsInt + offset];
    18         }
    19         return new Short(s);
    20     }
    21 }

        可以看出,如果 Short 在范围内,会从缓存中获取,否则就 new 一共新的对象。

      5、Integer 类(可以通过参数配置上限)

        部分源码:

     1 public final class Integer extends Number implements Comparable<Integer> {
     2     private static class IntegerCache {
     3         static final int low = -128;
     4         static final int high;
     5         static final Integer cache[];
     6 
     7         static {
     8             // high value may be configured by property
     9             int h = 127;
    10             String integerCacheHighPropValue =
    11                 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    12             if (integerCacheHighPropValue != null) {
    13                 try {
    14                     int i = parseInt(integerCacheHighPropValue);
    15                     i = Math.max(i, 127);
    16                     // Maximum array size is Integer.MAX_VALUE
    17                     h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
    18                 } catch( NumberFormatException nfe) {
    19                     // If the property cannot be parsed into an int, ignore it.
    20                 }
    21             }
    22             high = h;
    23 
    24             cache = new Integer[(high - low) + 1];
    25             int j = low;
    26             for(int k = 0; k < cache.length; k++)
    27                 cache[k] = new Integer(j++);
    28 
    29             // range [-128, 127] must be interned (JLS7 5.1.7)
    30             assert IntegerCache.high >= 127;
    31         }
    32 
    33         private IntegerCache() {}
    34     }
    35 
    36     public static Integer valueOf(int i) {
    37         if (i >= IntegerCache.low && i <= IntegerCache.high)
    38             return IntegerCache.cache[i + (-IntegerCache.low)];
    39         return new Integer(i);
    40     }
    41 }

        这里的从缓存中获取的代码是一样的,重点是 IntegerCache 的范围是可以配置的。

        从源码中可以看出来 最低值一定是-128 是不可以修改的,但是上限值high 是可以修改的。

        通过jvm参数: -Djava.lang.Integer.IntegerCache.high=1024 修改为1024

    1. 判断是否有 jvm参数 java.lang.Integer.IntegerCache.high

    2. 解析为 int 类型(解析失败就用默认值 127)

    3. 修改最大值和最小值

    4. 将 low<=x<= high 的值加入到缓存

    5. 验证 IntegerCache.high > 127

      如果 小于 127,那么 high=127;

      6、Long 类(缓存范围 -128~127)

        部分源码:

     1 public final class Long extends Number implements Comparable<Long> {
     2     private static class LongCache {
     3         private LongCache(){}
     4 
     5         static final Long cache[] = new Long[-(-128) + 127 + 1];
     6 
     7         static {
     8             for(int i = 0; i < cache.length; i++)
     9                 cache[i] = new Long(i - 128);
    10         }
    11     }
    12 
    13     public static Long valueOf(long l) {
    14         final int offset = 128;
    15         if (l >= -128 && l <= 127) { // will cache
    16             return LongCache.cache[(int)l + offset];
    17         }
    18         return new Long(l);
    19     }
    20 }

      7、Float 与 Double 不被缓存

        Float 与 Double 调用 valueOf() 时,直接 new 包装类型的对象,然后返回,并没有返回。

        部分源码:

     1 public final class Float extends Number implements Comparable<Float> {
     2     public static Float valueOf(float f) {
     3         return new Float(f);
     4     }
     5 }
     6 
     7 public final class Double extends Number implements Comparable<Double> {
     8         public static Double valueOf(double d) {
     9         return new Double(d);
    10     }
    11 }

      总结:

        Boolean 的缓存对象:True 与 False;

        Character 缓存对象范围 0~127;

        Byte,Short,Integer,Long:都有缓存对象,范围 -128~127;

        Float,Double没有缓存对象。

        Demo1:

     1 (12 int a = 1;
     3 int b = 1;
     4 System.out.println(a == b);//true
     5 
     6 (27 int c = 130;
     8 int d = 130;
     9 System.out.println(c == d);//true
    10 
    11 (312 //自动装箱
    13 Integer a = 1;
    14 Integer b = 1;
    15 System.out.println(a == b);//true    a == b比较的也是地址值     a和b指向的是同一个缓存的常量对象
    16 
    17 (418 Integer c = 130;
    19 Integer d = 130;
    20 System.out.println(c == d);//false  c == d比较的也是地址值    c和d都是在堆中新建的Integer对象
    21 
    22 (523 Integer a = 1;
    24 Double b = 1.0;
    25 Long c = 1L;
    26 long d = 1L;
    27         
    28 //System.out.println(a == b);//无法比较,因为对象比较地址,必须是同一种类型或有父子类关系
    29 //System.out.println(a == c);//无法比较,因为对象比较地址,必须是同一种类型或有父子类关系
    30         
    31 System.out.println(a == d);// true 因为d是基本数据类型,a才会自动拆箱, int 与 long 比较,可以比较
    32 
    33 (634 Double d1 = 1.0;
    35 Double d2 = 1.0;
    36 System.out.println(d1 == d2);//false  Float与 Double没有缓存对象
    37 
    38 (739 Character c1 = '0';//ASCII码,Unicode码:48
    40 Character c2 = '0';
    41 System.out.println(c1 == c2);//true
    42         
    43 Character c3 = '中';
    44 Character c4 = '中';
    45 System.out.println(c3 == c4);//false

        Demo2:

     1 Integer i = 1;
     2 Integer j = 1;
     3 System.out.println(i == j);//true
     4 
     5 Integer i = 128;
     6 Integer j = 128;
     7 System.out.println(i == j);//false
     8 
     9 Integer i = new Integer(1);//新new的在堆中
    10 Integer j = 1;//这个用的是缓冲的常量对象,在方法区
    11 System.out.println(i == j);//false
    12 
    13 Integer i = new Integer(1);//新new的在堆中
    14 Integer j = new Integer(1);//另一个新new的在堆中
    15 System.out.println(i == j);//false
    16 
    17 Integer i = new Integer(1);
    18 int j = 1;
    19 System.out.println(i == j);//true,凡是和基本数据类型比较,都会先拆箱,按照基本数据类型的规则比较

      Demo3:

    1 public void test1() {
    2         Object o1 = true ? new Integer(1) : new Double(2.0);
    3         System.out.println(o1);// 1.0
    4 
    5 }

      这里由于应用了三元运算符,所以在编译时就会统一两边的类型。

      这里会先进行拆箱,然后把 int 类型提升为 double 类型,然后再进行计算。  

  • 相关阅读:
    SASS常用方法
    Vue 全局过滤和局部过滤
    Java进阶知识点8:高可扩展架构的利器
    InnoDB MVCC RR隔离级别下的数据可见性总结
    记一次诡异的网络故障排除
    数据库的读锁和写锁在业务上的应用场景总结
    Gradle配置IDEA正常识别JPA Metamodel Generator动态生成的代码
    程序员容易读错的单词
    Java进阶知识点7:不要只会写synchronized
    Java进阶知识点6:并发容器背后的设计理念
  • 原文地址:https://www.cnblogs.com/niujifei/p/13949790.html
Copyright © 2011-2022 走看看