【Java八股|第10篇】Java 中的包装类和自动拆装箱
前言这一篇我们继续学习 Java 基础中非常高频的面试题包装类和自动拆装箱。在 Java 中有基本数据类型比如int long double boolean char也有对应的包装类比如Integer Long Double Boolean Character刚开始学习时很多人会觉得它们差不多。比如inta10;Integerb10;看起来都是保存数字 10。但是在面试和实际开发中这里面有很多细节基本类型和包装类有什么区别什么是自动装箱和自动拆箱Integer a 127; Integer b 127;为什么是trueInteger a 128; Integer b 128;为什么是false包装类能不能为null为什么数据库实体类更常用包装类这一篇我们就把包装类和自动拆装箱系统梳理一下。一、基本数据类型和包装类Java 有 8 种基本数据类型基本类型包装类byteByteshortShortintIntegerlongLongfloatFloatdoubleDoublecharCharacterbooleanBoolean基本类型不是对象包装类是对象。比如inta10;Integerb10;a是基本类型变量直接保存数值。b是引用类型变量保存的是对象引用。二、为什么需要包装类既然有基本类型为什么还要有包装类主要原因有几个。1. 集合不能直接存基本类型Java 集合中不能直接存基本数据类型。比如ListintlistnewArrayList();这是错误写法。应该写成ListIntegerlistnewArrayList();因为泛型要求使用引用类型不能使用基本类型。2. 包装类可以表示 null基本类型不能为null。intagenull;这是错误的。但是包装类可以Integeragenull;在项目中null经常表示“没有值”或“未填写”。比如用户年龄没有填写用Integer就更合适。3. 包装类提供很多工具方法比如字符串转数字IntegernumInteger.valueOf(123);或者intnumInteger.parseInt(123);这些方法都在包装类中。三、什么是自动装箱自动装箱就是基本类型自动转换成对应的包装类。示例Integernum10;这里右边是基本类型int左边是包装类Integer。编译器会自动帮我们转换大致相当于IntegernumInteger.valueOf(10);这就是自动装箱。再看集合中的例子ListIntegerlistnewArrayList();list.add(10);list中存的是Integer但我们传的是int。编译器会自动装箱成list.add(Integer.valueOf(10));四、什么是自动拆箱自动拆箱就是包装类自动转换成对应的基本类型。示例Integernum10;intvaluenum;这里num是Integervalue是int。编译器会自动转换大致相当于intvaluenum.intValue();再看一个例子Integera10;Integerb20;intresultab;这里a b时Integer会自动拆箱成int然后进行加法运算。五、自动拆箱的空指针问题自动拆箱最容易出现的问题是空指针异常。看下面代码Integernumnull;intvaluenum;这段代码会报NullPointerException为什么因为自动拆箱时底层会调用num.intValue()但是num是null所以调用方法就会空指针。再看一个项目中常见例子publicvoidupdateStatus(Integerstatus){if(status1){System.out.println(启用);}}如果调用时传入updateStatus(null);这里status1会触发自动拆箱相当于status.intValue()1如果status是null就会空指针。更安全的写法if(Integer.valueOf(1).equals(status)){System.out.println(启用);}或者if(status!nullstatus1){System.out.println(启用);}六、Integer 缓存机制这是包装类中最经典的面试点。看下面代码Integera127;Integerb127;System.out.println(ab);输出true再看Integera128;Integerb128;System.out.println(ab);输出false为什么因为Integer有缓存机制。默认情况下Integer会缓存-128 到 127之间的整数对象。当写Integera127;底层相当于IntegeraInteger.valueOf(127);对于-128到127之间的数字Integer.valueOf()会从缓存中取对象。所以Integera127;Integerb127;a和b指向同一个缓存对象a b为true。但是Integera128;Integerb128;128 超出了默认缓存范围会创建不同对象所以a b为false。七、Integer 比较应该用什么包装类比较内容时建议使用equals比如Integera128;Integerb128;System.out.println(a.equals(b));输出true不要用ab因为比较的是对象地址。对于包装类尤其是Integer、Long用很容易因为缓存范围导致结果不一致。更安全写法Objects.equals(a,b)示例Integeranull;Integerb10;System.out.println(Objects.equals(a,b));Objects.equals可以避免空指针问题。八、new Integer 和 valueOf 的区别虽然现在不推荐使用new Integer()但面试可能会问。IntegeranewInteger(127);IntegerbnewInteger(127);System.out.println(ab);输出false因为new Integer(127)每次都会创建新对象不会使用缓存。而IntegeraInteger.valueOf(127);IntegerbInteger.valueOf(127);System.out.println(ab);输出true因为valueOf会使用缓存。所以实际开发中不应该手动new Integer()而应该使用自动装箱或Integer.valueOf()。九、parseInt 和 valueOf 的区别intaInteger.parseInt(123);IntegerbInteger.valueOf(123);区别是方法返回值parseIntint基本类型valueOfInteger包装类示例intaInteger.parseInt(123);IntegerbInteger.valueOf(123);如果你需要基本类型用parseInt。如果你需要包装类用valueOf。十、实体类中为什么常用包装类在 Spring Boot MyBatis 项目中实体类经常这样写publicclassEmp{privateIntegerid;privateStringname;privateIntegergender;privateLocalDateentryDate;}为什么不用privateintid;privateintgender;原因是包装类可以表示null。比如新增员工时前端可能没有传id因为 id 由数据库自增生成。如果用int默认值是0这会让人分不清id 是真的为 0 还是根本没传而Integer可以是null表示没有值。所以实体类、DTO、VO 中经常使用包装类。十一、包装类和数据库字段的关系数据库中字段可能为空gendertinyintunsignednull如果 Java 中用基本类型privateintgender;当数据库值是null时映射可能会出问题或者默认变成 0。但 0 不一定是合法业务值。更推荐privateIntegergender;这样数据库中的null可以准确映射成 Java 中的null。所以项目开发中数据库字段如果允许为空Java 实体类通常使用包装类。十二、面试标准回答如果面试官问基本数据类型和包装类有什么区别什么是自动拆装箱可以这样回答基本数据类型不是对象直接保存具体值包装类是对象是基本类型对应的引用类型比如int对应Integerlong对应Long。自动装箱是基本类型自动转换成包装类比如Integer num 10底层相当于Integer.valueOf(10)。自动拆箱是包装类自动转换成基本类型比如int value num底层相当于调用num.intValue()。使用包装类时要注意空指针问题。如果包装类对象是null自动拆箱时会调用对应方法比如intValue()就会抛出NullPointerException。另外Integer有缓存机制默认缓存-128到127。所以Integer a 127; Integer b 127;用比较是true但 128 可能是false。包装类比较值时建议使用equals或Objects.equals。十三、常见问题总结1. Integer a 127; Integer b 127; a b 是什么结果是true因为 127 在Integer默认缓存范围内。2. Integer a 128; Integer b 128; a b 是什么结果通常是false因为 128 超出默认缓存范围会创建不同对象。3. 包装类比较能不能用 不推荐。包装类是对象比较地址。比较值建议使用Objects.equals(a,b)4. 自动拆箱有什么风险如果包装类是null自动拆箱会抛出空指针异常。比如Integernumnull;intvaluenum;5. 实体类字段用基本类型还是包装类通常建议使用包装类。因为包装类可以表示null更适合和数据库字段、前端参数对应。十四、实际开发建议实际开发中可以注意下面几点集合泛型使用包装类不能使用基本类型实体类、DTO、VO 中数字字段优先使用包装类包装类比较值时使用Objects.equals不要依赖Integer缓存范围写业务逻辑注意自动拆箱导致的空指针异常金额不要用Double优先使用BigDecimal字符串转数字时注意捕获NumberFormatException不推荐手动使用new Integer()十五、总结这一篇主要学习了 Java 中的包装类和自动拆装箱。基本数据类型不是对象包装类是对象。Java 为 8 种基本类型都提供了对应包装类比如int对应Integerlong对应Long。自动装箱就是基本类型自动转包装类自动拆箱就是包装类自动转基本类型。它们让代码写起来更方便但也带来了空指针风险。Integer默认缓存-128到127所以包装类用比较时可能出现看起来奇怪的结果。实际开发中比较包装类值建议使用Objects.equals。在 Java 后端项目中实体类字段经常使用包装类因为包装类可以表示null更适合接收前端参数和映射数据库字段。