JDK5.0新特性

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 strings
static 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 c
static 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 a
int 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
@Documented
public @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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值