1、包装类
Java中的数据类型总体上分为基本数据类型和引用数据类型。引用类型的数据可以通过对象的属性和方法来进行操作,但对于基本数据类型的数据,我们能不能像操作对象那样来操作呢?为了实现这个目标,Java为8种基本数据类型分别设计了对应的类,这就是包装类(Wrapper Classes,外覆类,数据类型类)。因为这些包装类都是引用类型,所以我们就可以方便地操作这些数据的属性和方法了。包装类的设计主要是为了配合Java 5之后出现的泛型,在泛型的使用中,基本数据类型和泛型不能混用。比如我们想使用ArrayList、HashMap等集合时,这些集合都需要指明集合中数据的泛型,而这些集合容器的泛型都必须是Object类型,像int、double这些基本类型是不允许使用的,所以就需要包装类。包装类的类名,就是把基本类型的首字母变成大写,除了Integer和Character特殊一点。另外,Byte、Short、Integer、Long、Float、Double这六个类,都是Number的子类,它们具有一些共同的类型转换方法,方便我们进行类型之间的转换。具体如下图所示:
- 基本数据类型的数据存储在栈中,而包装类型的对象存储在堆中,因此基本数据类型的操作比包装类型的操作更加高效。建议尽量使用基本数据类型,而不是对应的包装类型。
2、自动装箱
自动装箱是指 Java 编译器把基本数据类型自动转换为对应的包装类。例如,将 int 类型转换为 Integer 类型,将 double 类型转换为 Double 类型等。如果转换以相反的方式进行,则称为开箱。以下代码就是自动装箱的例子。
Character ch = 'a';
List<Integer> li = new ArrayList<>();
for (int i = 1; i < 50; i += 2)
li.add(i); // 实际实现的是:li.add(Integer.valueOf(i));
3、自动拆箱
自动拆箱是指 Java 编译器把包装类自动转换为对应的基本数据类型。例如,将 Integer 类型转换为 int类型,将 Double 类型转换为double 类型等。以下代码就是自动拆箱的例子。
public static int sumEven(List<Integer> li) {
int sum = 0;
for (Integer i: li)
if (i % 2 == 0) //自动拆箱
sum += i; //自动拆箱
return sum;
}
//上面代码实际实现的是
public static int sumEven(List<Integer> li) {
int sum = 0;
for (Integer i : li)
if (i.intValue() % 2 == 0) //手动拆箱操作
sum += i.intValue(); //手动拆箱操作
return sum;
}
4、字符串与自动装箱
如果你在拼接字符串时涉及到数字或其他非字符串对象,那么Java会使用自动装箱将这些对象转换为字符串。所以在字符串拼接时,建议使用 StringBuilder 或 StringBuffer,而不是字符串连接符 “+”,以避免自动装箱导致的开销。
int a = 10;
String b = "World";
String c = a + b; // 自动装箱:这里的a会被自动转换为它的字符串表示形式"10"
5、注意点
自动装箱和拆箱对程序性能会有一定的影响,但也要具体情况具体分析。优化应该是有目的的,而不是盲目的。在大多数情况下,自动装箱和拆箱对程序性能的影响并不大。只有在你确定这是性能瓶颈时,才应该专注于优化这部分代码。其他情况下,稍加注意即可:
- 使用包装类型的目的明确:尽量只在需要的时候使用包装类型,并明确它们的使用目的。尽量避免在不需要的情况下使用它们。
- 使用原始类型:在已知变量类型的情况下,使用原始类型通常比使用包装类型更有效。
- 避免不必要的自动装箱:尽量避免在循环或大量计算中自动装箱。如果可能,手动进行装箱操作,以减少不必要的开销。
- 避免过度优化:在大多数情况下,自动装箱和拆箱对程序性能的影响并不大。过度优化可能会使代码变得难以阅读和维护。
- 使用StringBuilder进行字符串拼接:当需要大量拼接字符串时,使用StringBuilder或StringBuffer通常比使用"+"运算符更高效。
- 进行性能测试和分析:使用Java的性能分析工具(如JProfiler、VisualVM等)来监测和分析程序性能。通过这种方式,你可以确定自动装箱和其他开销较大的操作对程序性能的具体影响。