JDK5.0新特性

来源:互联网 发布:输出变压器数据 编辑:程序博客网 时间:2024/05/13 18:57

jdk5.0重要的语言变化:泛型(Generics)、循环增强(Enhanced for Loop)、自动封箱(Autoboxing)和解箱(Unboxing)、类型安全的枚举(Typesafe Enums)、可变参数(Varargs)、静态导入(Static Import)、注解(Annotations)。

1. 泛型(Generics)

没有使用泛型的问题:

(1)从集合中获得元素时,必须进行类型转换,这个类型转换过程比较麻烦。
(2)类型转换是不安全的,可能在运行时发生类型转换失败。
鉴于以上问题,如果能够告诉编译器集合中的类型,让编译器加入类型转换功能,编译器会保证类型转换的成功。这里值得注意的是:泛型是提供给Javac编译器使用的。编译器在编译带泛型的集合时会去掉“类型”信息,使程序运行效率不受影响,也就是说编译生成的字节码不会带有泛型的类型信息。
未使用泛型的例子:

// Removes 4-letter words from c; elements must be stringsstatic void expurgate(Collection c) {       for (Iterator i = c.iterator(); i.hasNext();) {              String s = (String) i.next();              if (s.length() == 4) {                i.remove();              }       }}
使用泛型的例子:

// Removes 4-letter words from cstatic void expurgate(Collection<String> c) {       for (Iterator<String> i = c.iterator(); i.hasNext();) {              if (i.next().length() == 4) {                i.remove();              }       }}
使用泛型后好处:
(1)更加清晰和安全。
(2)没有类型转换、额外的括号和临时变量。
(3)提供编译时的类型检查和消除类型转换。

2. 循环增强(Enhanced for Loop)

没有使用循环增强的问题:
(1)遍历集合比较麻烦。
(2)Iterator通常只有在获取元素时才会用到。
(3)使用Iterator容易产生错误:Iterator变量在循环中会出现3次、通常的拷贝粘贴错误。
鉴于以上问题:如果能让编译器来处理Iterator,隐藏Iterator背后的细节。
一般访问集合的例子:

void cancelAll(Collection c) {       for (Iterator i = c.iterator(); i.hasNext();) {              TimerTask tt = (TimerTask) i.next();              tt.cancel();       }}
使用循环增强的例子:
void cancelAll(Collection c) {       for (Object o : c) {              ((TimerTask) o).cancel();       }}
结合泛型的例子:
void cancelAll(Collection<TimerTask> c) {       for (TimerTask task : c) {              task.cancel();       }}
对数组使用循环增强:
//Returns the sum of the elements of aint sum(int[] a) {       int result = 0;       for (int i : a) {              result += i;       }       return result;}
使用循环增强后的好处:
(1)代码更加简洁、清晰和安全。
(2)和Iterator无关,不可能使用错误的Iterator。
(3)消除使用数组索引的错误。
(4)代码准确表达它所要做的。

3. 自动封箱(Autoboxing)和解箱(Unboxing)

没有使用Autoboxing/Unboxing的问题:
(1)不能将int放入集合,必须使用Integer。
(2)在获取时转换回来比较麻烦。
鉴于以上问题:如果能够让编译器做这些事不是更好?
一般的方法:

public class Freq {       private static final Integer ONE = new Integer(1);       public static void main(String[] args) {              // Maps word (String) to frequency (Integer)              Map m = new TreeMap();              for (int i = 0; i < args.length; i++) {                Integer freq = (Integer) m.get(args[i]);                m.put(args[i], (freq == null ? ONE : new Integer(                         freq.intValue() + 1)));              }              System.out.println(m);       }}
结合自动封箱、泛型和增强循环的例子:
public class Freq {       public static void main(String[] args) {              Map<String, Integer> m = new TreeMap<String, Integer>();              for (String word : args) {                Integer freq = m.get(word);                m.put(word, (freq == null ? 1 : freq + 1));              }              System.out.println(m);       }}
使用Autoboxing/Unboxing的好处:
(1)Autoboxing:需要一种类型的对象时,这种基本类型就自动地封装到与它相同类型的包装中。
(2)Unboxing:需要一个值时,被装箱对象中的值就被自动地提取出来,不需要调用intValue()和doubleValue()等方法。

4. 类型安全的枚举(Typesafe Enums)

在之前的版本中,表示int枚举类型的标准方式:

// int Enum Pattern - has severe problems!public static final int SEASON_WINTER = 0;public static final int SEASON_SPRING = 1;public static final int SEASON_SUMMER = 2;public static final int SEASON_FALL   = 3;
这样的方式存在的问题:
(1)不是类型安全的,你必须确保它是int。
(2)没有命名空间,没有常量字符串前缀来避免与其他int枚举类型冲突。
(3)脆弱性:常量被编译到客户程序中,如果常量改变,客户必须重新编译。
(4)打印出的值不提供详细信息,打印出来的值都是一个数字。
安全类型的Enum模式的例子:
import java.io.Serializable;import java.util.Arrays;import java.util.Collections;import java.util.List;public final class Season implements Comparable, Serializable {       private final String name;       public String toString() {              return name;       }       private Season(String name) {              this.name = name;       }       public static final Season WINTER = new Season("winter");       public static final Season SPRING = new Season("spring");       public static final Season SUMMER = new Season("summer");       public static final Season FALL = new Season("fall");        private static int nextOrdinal = 0;       private final int ordinal = nextOrdinal++;        public int compareTo(Object o) {              return ordinal - ((Season) o).ordinal;       }        private static final Season[] PRIVATE_VALUES = { WINTER, SPRING, SUMMER, FALL };       public static final List VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));       private Object readResolve() {              // Canonicalize              return PRIVATE_VALUES[ordinal];       }}
以上方法的基本想法是:使用导出自定义类型的常量,不提供public构造方法,这样可以修正前面所有的缺点,还能够添加任意的方法、域变量,能够实现接口。缺点是代码冗余过长,容易出现错误:每个常量出现3次,不能在switch语句中使用。
鉴于以上问题:这些事情由编译器来处理。
安全类型的Enum结构:
(1)编译器支持安全类型的Enum模式.
(2)类似典型的Enum --> enum Season { WINTER, SPRING, SUMMER, FALL }
(3)能够在switch语句中使用。
(4)安全类型的Enum模式的所有优点。
结合泛型和增强循环的Enum例子:
import java.util.*;public class Card {    public enum Rank { DEUCE, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE }    public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES }    private final Rank rank;    private final Suit suit;    private Card(Rank rank, Suit suit) {        this.rank = rank;        this.suit = suit;    }    public Rank rank() { return rank; }    public Suit suit() { return suit; }    public String toString() { return rank + " of " + suit; }    private static final List<Card> protoDeck = new ArrayList<Card>();    // Initialize prototype deck    static {        for (Suit suit : Suit.values())            for (Rank rank : Rank.values())                protoDeck.add(new Card(rank, suit));    }    public static ArrayList<Card> newDeck() {        return new ArrayList<Card>(protoDeck); // Return copy of prototype deck    }}

5. 可变参数(Varargs)

在之前的版本中,编写具有任意数量的参数的方法,必须使用数组:

Object[] arguments = {    new Integer(7),    new Date(),    "a disturbance in the Force"};String result = MessageFormat.format("At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.", arguments);
创建和初始化数组是件麻烦的事情,鉴于以上问题:如果由编译器来实现不是更好?
使用可变参数(Varargs)的例子:
public static String format(String pattern, Object... arguments);String result = MessageFormat.format("At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.",    7, new Date(), "a disturbance in the Force");

6. 静态导入(Static Import)

在之前版本中,为了访问静态成员,必须导入来自的类的引用。例如:

double r = Math.cos(Math.PI * theta);
现在你只需在静态导入,然后就可以直接在代码中使用静态方法和静态字段,例如:import static java.lang.Math.PI; 或者 import static java.lang.Math.*;就可以使用它们了:
double r = cos(PI * theta);

7. 注解(Annotations)

注解(Annotations),也叫元数据。代码级别的说明。与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
使用自定义的注解类型:
/** * Describes the Request-For-Enhancement(RFE) that led * to the presence of the annotated API element. */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Inherited@Documentedpublic @interface RequestForEnhancement {    int    id();    String synopsis();    String engineer() default "[unassigned]";     String date()    default "[unimplemented]"; }
一旦定义了注解类型,就可以使用该注解了。我们可以看到这个注解类型本身也被注解了,这种注解叫做元注释。
@Retention(RetentionPolicy.RUNTIME)表示这种类型的注解被JVM保留从而使其能够通过反射在运行时读取。
@Target(ElementType.METHOD)表示这种注解只能用来注解在方法上。
@Inherited表示该注解可以被子类继承,默认不可继承。
@Documented表示javadoc自动生成文档时,也包含该注解,默认不包含。
按照惯例,注解应该写在其他修饰符的前面,例如:
@RequestForEnhancement(id = 2868724, synopsis = "Enable time-travel", engineer = "Mr. Peabody", date = "4/1/3007")public static void travelThroughTime(Date destination) { //... }



0 0