专业的编程技术博客社区

网站首页 > 博客文章 正文

学习廖雪峰的JAVA教程---java核心类(包装类型)

baijin 2024-10-10 04:30:19 博客文章 11 ℃ 0 评论

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

  • 基本类型:byte,short,int,long,boolean,float,double,char
  • 引用类型:所有class和interface类型

引用类型可以赋值为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类,我们就可以把int和Integer互相转换:

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

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

基本类型对应的引用类型booleanjava.lang.Booleanbytejava.lang.Byteshortjava.lang.Shortintjava.lang.Integerlongjava.lang.Longfloatjava.lang.Floatdoublejava.lang.Doublecharjava.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

因为int和Integer可以互相转换:

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

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

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;
 }
}
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()对于较小的数(这里的较小数指的是-128~127,因为这个静态方法的源码里面对于这个范围内的数都是从静态缓存里面返回实例,只有超过这个范围才会重新开辟内存),始终返回相同的实例,因此,==比较“恰好”为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实例全部是缓存实例,但调用者并不关心静态工厂方法以何种方式创建新实例还是直接返回缓存的实例。

【关键:

  1. 引用类型可以赋值为null,表示空,但基本类型不能赋值为null
  2. 因为Integer.valueOf()可能始终返回同一个Integer实例
  3. 能创建“新”对象的静态方法称为静态工厂方法。Integer.valueOf()就是静态工厂方法,它尽可能地返回缓存的实例以节省内存

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表