news 2026/4/16 23:40:40

蓝桥杯JAVA--启蒙之路(十二)包装类型 JavaBean

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
蓝桥杯JAVA--启蒙之路(十二)包装类型 JavaBean

一前言

今天照常更新,不知是不是我的内容太水,还是形式不好,或者其他原因,最近的阅读量直线下降,我是欢迎大家提供建议的,但无论怎样,我会坚持的。

二主要内容

包装类型

我们已经知道,Java的数据类型分两种:

  • 基本类型:byteshortintlongbooleanfloatdoublechar
  • 引用类型:所有classinterface类型。

引用类型可以赋值为null,表示空,但基本类型不能赋值为null

String s = null; int n = null; // compile error!

那么,如何把一个基本类型视为对象(引用类型)?

比如,想要把int基本类型变成一个引用类型,我们可以定义一个Integer类,它只包含一个实例字段int,这样,Integer类就可以视为int的包装类(Wrapper Class):

public class Integer { private int value; public Integer(int value) { this.value = value; } public int intValue() { return this.value; } }

定义好了Integer类,我们就可以把intInteger互相转换:

Integer n = null; Integer n2 = new Integer(99); int n3 = n2.intValue();

实际上,因为包装类型非常有用,Java核心库为每种基本类型都提供了对应的包装类型:

基本类型对应的引用类型
booleanjava.lang.Boolean
bytejava.lang.Byte
shortjava.lang.Short
intjava.lang.Integer
longjava.lang.Long
floatjava.lang.Float
doublejava.lang.Double
charjava.lang.Character

我们可以直接使用,并不需要自己去定义:

// Integer: public class Main { public static void main(String[] args) { int i = 100; // 通过new操作符创建Integer实例(不推荐使用,会有编译警告): Integer n1 = new Integer(i); // 通过静态方法valueOf(int)创建Integer实例: Integer n2 = Integer.valueOf(i); // 通过静态方法valueOf(String)创建Integer实例: Integer n3 = Integer.valueOf("100"); System.out.println(n3.intValue()); } }

Auto Boxing

因为intInteger可以互相转换:

int i = 100; Integer n = Integer.valueOf(i); int x = n.intValue();

所以,Java编译器可以帮助我们自动在intInteger之间转型:

Integer n = 100; // 编译器自动使用Integer.valueOf(int) int x = n; // 编译器自动使用Integer.intValue()

这种直接把int变为Integer的赋值写法,称为自动装箱(Auto Boxing),反过来,把Integer变为int的赋值写法,称为自动拆箱(Auto Unboxing)。

注意

自动装箱和自动拆箱只发生在编译阶段,目的是为了少写代码

装箱和拆箱会影响代码的执行效率,因为编译后的class代码是严格区分基本类型和引用类型的。并且,自动拆箱执行时可能会报NullPointerException

// NullPointerException public class Main { public static void main(String[] args) { Integer n = null; int i = n; } }

不变类

所有的包装类型都是不变类。我们查看Integer的源码可知,它的核心代码如下:

public final class Integer { private final int value; }

因此,一旦创建了Integer对象,该对象就是不变的。

对两个Integer实例进行比较要特别注意:绝对不能用==比较,因为Integer是引用类型,必须使用equals()比较:

// == or equals? public class Main { public static void main(String[] args) { Integer x = 127; Integer y = 127; Integer m = 99999; Integer n = 99999; System.out.println("x == y: " + (x==y)); // true System.out.println("m == n: " + (m==n)); // false System.out.println("x.equals(y): " + x.equals(y)); // true System.out.println("m.equals(n): " + m.equals(n)); // true } }

仔细观察结果的童鞋可以发现,==比较,较小的两个相同的Integer返回true,较大的两个相同的Integer返回false,这是因为Integer是不变类,编译器把Integer x = 127;自动变为Integer x = Integer.valueOf(127);,为了节省内存,Integer.valueOf()对于较小的数,始终返回相同的实例,因此,==比较“恰好”为true,但我们绝不能因为Java标准库的Integer内部有缓存优化就用==比较,必须用equals()方法比较两个Integer

最佳实践

按照语义编程,而不是针对特定的底层实现去“优化”。

因为Integer.valueOf()可能始终返回同一个Integer实例,因此,在我们自己创建Integer的时候,以下两种方法:

  • 方法1:Integer n = new Integer(100);
  • 方法2:Integer n = Integer.valueOf(100);

方法2更好,因为方法1总是创建新的Integer实例,方法2把内部优化留给Integer的实现者去做,即使在当前版本没有优化,也有可能在下一个版本进行优化。

我们把能创建“新”对象的静态方法称为静态工厂方法。Integer.valueOf()就是静态工厂方法,它尽可能地返回缓存的实例以节省内存。

最佳实践

创建新对象时,优先选用静态工厂方法而不是new操作符

如果我们考察Byte.valueOf()方法的源码,可以看到,标准库返回的Byte实例全部是缓存实例,但调用者并不关心静态工厂方法以何种方式创建新实例还是直接返回缓存的实例。

进制转换

Integer类本身还提供了大量方法,例如,最常用的静态方法parseInt()可以把字符串解析成一个整数:

int x1 = Integer.parseInt("100"); // 100 int x2 = Integer.parseInt("100", 16); // 256,因为按16进制解析

Integer还可以把整数格式化为指定进制的字符串:

// Integer: public class Main { public static void main(String[] args) { System.out.println(Integer.toString(100)); // "100",表示为10进制 System.out.println(Integer.toString(100, 36)); // "2s",表示为36进制 System.out.println(Integer.toHexString(100)); // "64",表示为16进制 System.out.println(Integer.toOctalString(100)); // "144",表示为8进制 System.out.println(Integer.toBinaryString(100)); // "1100100",表示为2进制 } }

注意:上述方法的输出都是String,在计算机内存中,只用二进制表示,不存在十进制或十六进制的表示方法。int n = 100在内存中总是以4字节的二进制表示:

┌────────┬────────┬────────┬────────┐ │00000000│00000000│00000000│01100100│ └────────┴────────┴────────┴────────┘

我们经常使用的System.out.println(n);是依靠核心库自动把整数格式化为10进制输出并显示在屏幕上,使用Integer.toHexString(n)则通过核心库自动把整数格式化为16进制。

这里我们注意到程序设计的一个重要原则:数据的存储和显示要分离。

Java的包装类型还定义了一些有用的静态变量

// boolean只有两个值true/false,其包装类型只需要引用Boolean提供的静态字段: Boolean t = Boolean.TRUE; Boolean f = Boolean.FALSE; // int可表示的最大/最小值: int max = Integer.MAX_VALUE; // 2147483647 int min = Integer.MIN_VALUE; // -2147483648 // long类型占用的bit和byte数量: int sizeOfLong = Long.SIZE; // 64 (bits) int bytesOfLong = Long.BYTES; // 8 (bytes)

最后,所有的整数和浮点数的包装类型都继承自Number,因此,可以非常方便地直接通过包装类型获取各种基本类型:

// 向上转型为Number: Number num = new Integer(999); // 获取byte, int, long, float, double: byte b = num.byteValue(); int n = num.intValue(); long ln = num.longValue(); float f = num.floatValue(); double d = num.doubleValue();

处理无符号整型

在Java中,并没有无符号整型(Unsigned)的基本数据类型。byteshortintlong都是带符号整型,最高位是符号位。而C语言则提供了CPU支持的全部数据类型,包括无符号整型。无符号整型和有符号整型的转换在Java中就需要借助包装类型的静态方法完成。

例如,byte是有符号整型,范围是-128~+127,但如果把byte看作无符号整型,它的范围就是0~255。我们把一个负的byte按无符号整型转换为int

// Byte public class Main { public static void main(String[] args) { byte x = -1; byte y = 127; System.out.println(Byte.toUnsignedInt(x)); // 255 System.out.println(Byte.toUnsignedInt(y)); // 127 } }

因为byte-1的二进制表示是11111111,以无符号整型转换后的int就是255

类似的,可以把一个short按unsigned转换为int,把一个int按unsigned转换为long

小结

Java核心库提供的包装类型可以把基本类型包装为class

自动装箱和自动拆箱都是在编译期完成的(JDK>=1.5);

装箱和拆箱会影响执行效率,且拆箱时可能发生NullPointerException

包装类型的比较必须使用equals()

整数和浮点数的包装类型都继承自Number

包装类型提供了大量实用方法。

JavaBean

在Java中,有很多class的定义都符合这样的规范:

  • 若干private实例字段;
  • 通过public方法来读写实例字段。

例如:

public class Person { private String name; private int age; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public int getAge() { return this.age; } public void setAge(int age) { this.age = age; } }

如果读写方法符合以下这种命名规范:

// 读方法: public Type getXyz() // 写方法: public void setXyz(Type value)

那么这种class被称为JavaBean

上面的字段是xyz,那么读写方法名分别以getset开头,并且后接大写字母开头的字段名Xyz,因此两个读写方法名分别是getXyz()setXyz()

boolean字段比较特殊,它的读方法一般命名为isXyz()

// 读方法: public boolean isChild() // 写方法: public void setChild(boolean value)

我们通常把一组对应的读方法(getter)和写方法(setter)称为属性(property)。例如,name属性:

  • 对应的读方法是String getName()
  • 对应的写方法是setName(String)

只有getter的属性称为只读属性(read-only),例如,定义一个age只读属性:

  • 对应的读方法是int getAge()
  • 无对应的写方法setAge(int)

类似的,只有setter的属性称为只写属性(write-only)。

很明显,只读属性很常见,只写属性不常见。

属性只需要定义gettersetter方法,不一定需要对应的字段。例如,child只读属性定义如下:

public class Person { private String name; private int age; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public int getAge() { return this.age; } public void setAge(int age) { this.age = age; } public boolean isChild() { return age <= 6; } }

可以看出,gettersetter也是一种数据封装的方法。

JavaBean的作用

JavaBean主要用来传递数据,即把一组数据组合成一个JavaBean便于传输。此外,JavaBean可以方便地被IDE工具分析,生成读写属性的代码,主要用在图形界面的可视化设计中。

通过IDE,可以快速生成gettersetter。例如,在Eclipse中,先输入以下代码:

public class Person { private String name; private int age; }

然后,点右键,在弹出的菜单中选择“Source”,“Generate Getters and Setters”,在弹出的对话框中选中需要生成gettersetter方法的字段,点击确定即可由IDE自动完成所有方法代码。

枚举JavaBean属性

要枚举一个JavaBean的所有属性,可以直接使用Java核心库提供的Introspector

import java.beans.*; //测试类,一般写代码会分开 public class Main { public static void main(String[] args) throws Exception { BeanInfo info = Introspector.getBeanInfo(Person.class); for (PropertyDescriptor pd : info.getPropertyDescriptors()) { System.out.println(pd.getName()); System.out.println(" " + pd.getReadMethod()); System.out.println(" " + pd.getWriteMethod()); } } } //JavaBean格式 class Person { //定义变量 private String name; private int age; //空参/实参 public String getName() { return name; } public void setName(String name) { this.name = name; } //get/set public int getAge() { return age; } public void setAge(int age) { this.age = age; } //通常还会有对行为的描述,就是前面的变量 }

运行上述代码,可以列出所有的属性,以及对应的读写方法。注意class属性是从Object继承的getClass()方法带来的。

小结

JavaBean是一种符合命名规范的class,它通过gettersetter来定义属性;

属性是一种通用的叫法,并非Java语法规定;

可以利用IDE快速生成gettersetter

使用Introspector.getBeanInfo()可以获取属性列表。

三最后一语

今天的内容也比较重要,尤其是JavaBean,这是以后写代码的基础,也是固定的格式,像我这样的新手面对新的东西总像找个东西模仿,而这个就是最好的参照物。

某年某个星期几,某时某地你为我淋雨--雨过后的风景

感谢观看,共勉!!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 11:14:34

惠普5200LX打印机驱动下载方法:告别失效困扰,3大路径高效适配

“驱动下载踩坑80%是因为找错渠道&#xff01;惠普5200LX适配有章可循” 作为深耕打印机问题解决领域5年的博主&#xff0c;小编每天都会收到大量用户咨询——“惠普5200LX驱动突然失效怎么办&#xff1f;”“下载的驱动安装后打印机还是无法使用”。这类问题看似琐碎&#xf…

作者头像 李华
网站建设 2026/4/16 12:44:57

程序员如何实现微信个人号API接口的高效对接?

在数字化运营的今天&#xff0c;私域团队常常陷入“时间陷阱”——员工70%的精力消耗在添加好友、社群维护、重复答疑等机械任务上&#xff0c;真正创造价值的策略与创意反而被压缩。这不仅消耗团队精力&#xff0c;更限制了企业的增长潜力。现在&#xff0c;破局时刻已至。什么…

作者头像 李华
网站建设 2026/4/16 12:01:30

30分钟搞定 Ingress Nginx 到 Higress 迁移?AI 帮我干完了脏活累活

作者&#xff1a;一个不想加班的网关运维 起因 周五下午四点半&#xff0c;老板把一条 Kubernetes 官方声明甩到群里&#xff1a; Ingress NGINX 将于 2026 年 3 月正式退役。 选择在退役后继续使用 Ingress NGINX&#xff0c;将使你和你的用户面临安全攻击风险。现有替代方案…

作者头像 李华
网站建设 2026/4/16 15:33:40

天远车辆过户查询API对接实战:基于VIN码的二手车流转轨迹追踪技术详解

消除二手车交易的信息迷雾&#xff1a;基于车架号的历史溯源方案 在二手车交易、汽车金融信贷以及车辆资产评估中&#xff0c;最大的痛点往往源于“信息不对称”。一辆外观崭新的车辆&#xff0c;可能在短短一年内经历了多次过户&#xff0c;这通常暗示着车辆可能存在严重的性能…

作者头像 李华
网站建设 2026/4/16 10:45:40

从 Demo 到生产:构建企业级 AI 智能体的 14 步实战指南

当下&#xff0c;AI 智能体&#xff08;AI Agent&#xff09;成为技术圈的绝对热点&#xff0c;几乎所有团队都在尝试搭建属于自己的智能体。但现实的鸿沟却异常清晰&#xff1a;在 Notebook 里把大模型和两三个工具简单串联&#xff0c;做出一个能跑通流程的 Demo&#xff0c;…

作者头像 李华