Java 5.0

Java从5.0版本开始，加入许多新特性，是Java历史中修改最大的版本，许多特点模仿自C#，因而被认为是为了与C#对抗。

## 新的特性

### 自动装箱／拆箱（Auto-Boxing/Unboxing）

将Java中8个基本类型实现自动对象化和值化转换，通过编译器自动完成相关转换代码的生成。

没有自动装箱／拆箱：

```java
 int int1 = 1;
 Integer integer2 = new Integer(int1);
 int int3 = integer2.intValue();
```

有自动装箱／拆箱：

```java
 int int1 = 1;
 Integer integer2 = int1;     // 自動裝箱 
 int int3 = integer2;         // 自動拆箱
```

实际上将相关字节码反编译后，会还原出其隐式调用的转换方法：

```java
 int int1 = 1;
 Integer integer2 = Integer.valueOf(int1);     // 自動裝箱的本质，通过调用valueOf将值对象化
 int int3 = integer2.intValue();               // 自動拆箱的本质，通过调用xxxValue将对象值化
```

自动装箱的新功能，可能是从C#语言身上学习来的，Java已经越来越像C#。然而Java对自动装箱／拆箱的支援，仅是利用编译器实现，在Java Bytecode中，并无自动装箱／拆箱的操作码（opcode）。

### 泛型（Generic Types）

泛型就像是C++的模板。原有的Collection API加上泛型支援后，增加对型别的检查，减少程式错误的机会。

没有泛型：

```java
 HashMap hm = new HashMap();
 int i=1;
 String tt="test";
 hm.put(new Integer(i), tt);
```

使用Generic：

```java
 HashMap <Integer, String>hm = new HashMap<Integer, String>();
 int i=1;
 String tt = "test";
 hm.put(i, tt);      // 在這裏對int自動裝箱成Integer，也使用了參數的型別檢查
```

### 注解（Annotation）

Annotation全名是Program Annotation Facility，是Java SE 5.0的新功能。Java的Annotation类似于.NET的属性（Attribute）。Java的注解是一种接口（interface），继承自java.lang.annotation.Annotation。Class File则贴上ACC_ANNOTATION标签。

从5.0开始，javadoc的`@deprecated`（代表不建议使用的方法或类别）也被Annotation中的`@Deprecated`取代；另外，使用Java实作SOP的AspectJ与Spring也使用了大量的Annotation。

```java
 // JDK 1.4
 /**
  * @todo to be implemented
  **/
 void gimmeSomeLoving() {
   throw new Exception("not implemented");
 }
```

```java
 // JDK 1.5
 @todo 
 void gimmeSomeLoving() {
   throw new Exception("not implemented");
 }
```

### 枚举类型（enum）

枚举类型也是J2SE 5.0的新功能。过去Java认为enum的关键字是不必要的功能，因为用public static int field就可以取代enum，因此过去一直不用。J2SE 5.0中的class如果是enum，在class file中会被贴上一个ACC_ENUM标签。

Enum 一般用来表示一组相同类型的常量。如性别、日期、月份、颜色等。对这些属性用常量的好处是显而易见的，不仅可以保证单例，且比较时候可以用 “==” 来替换 equals。是一种好的习惯。JDK1.5 之前没有 Enum 这个类型，那时候一般用接口常量来替代。Java有了Enum 之后，可以更贴近的表示这种常量。

```java
 // JDK 1.4
 class JavaTech {
        public static final int J2ME = 1;
        public static final int J2SE = 2;
        public static final int J2EE = 3;
 }
```

```java
 // JDK 1.5
 public enum NewJavaTech {
        J2ME, J2SE, J2EE
 }
```

### 国际化

Java语言严格区分字节和字符。字符的存储格式为UCS-2，也就是只能使用位于基本多文种平面的字元，从Java 5开始支持UTF-16字符。

另外，从5.0开始Java的程式也开始可以使用Unicode字元进行命名。

下面就是一个合法的Java程式，里面包含了中文字符作为字串的名称，这个程式可以在编译器中通过编译。

```java
public class HelloWorld {
   private String文本 = "HelloWorld";
}
```

### 输入输出

在jdk1.5及其以后版本中，`java.util.Scanner`和`java.util.Formatter`类别被应用到输入输出中。另外，也出现了类似C语言的`printf()`函式。

### foreach循环

foreach循环，有时又称forin循环，在许多程式语言（包括C#、Ruby、JavaScript）中都有出现，可以直接将一个Array或Map展开，而不必由程序员自行检查边界，可以有效减少错误的机会。

```Java
 int[] array1 = {1, 3, 5};
 
 for(int i : array1){ // foreach迴圈
     System.out.println("Number: "+i);
 }
```

### 可变长度的引数

长久以来一直有使用者要求加入`printf()`函式，受限于Java函式必须要有固定引数的限制，始终无法实现，在加入这个功能之后，连带`printf()`也变为可能。

### static引入

这个特性允许程序员将一个类别中的静态内容引入到程式中。

```java
static import java.lang.System.*;

public class HelloWorld {
    public static void main(String args[]){
        out.println("Hello World.");
    }
}
```

## 批评

Java 5.0虽然加入许多的新特性，但为了与旧版本相容，JVM并没有随之改变，而仅只是从编译器动手脚，因而引发许多问题。讨论Java语言问题的专书《Java Puzzle》就有专门的篇幅讨论5.0之后造成的问题。

### 自动装箱／拆箱的矛盾

自动装箱这功能也造成了一些矛盾，例如：

```java
 Integer int1 = new Integer(1);
 Integer int2 = new Integer(1);
 
 System.out.println(int1 >= int2);  // 檢查兩者的值（触发了自动拆箱），     true
 System.out.println(int1 <= int2);  // 檢查兩者的值（触发了自动拆箱），     true
 System.out.println(int1 != int2);   // 檢查兩者的參考位置（没有触发自动拆箱），true！
 System.out.println(int1 == int2);  // 实际为檢查兩者的參考位置，false！ 不能认为这是int值类型间的对比，此时还没有发生自动拆箱行为。
```

### 泛型擦除

和C#，C++的泛型不同，Java的泛型只用在型别检查，使用的时候还要再做一次转型和类型检查。泛型信息在编译时会从代码中抹除（只保留作为元数据给反射功能获得），泛型对应的值类型在字节码中为java.lang.Object类型，这是为了降低JVM的修改难度和保证老旧代码的运行兼容。