`
jackcode
  • 浏览: 8479 次
  • 性别: Icon_minigender_1
  • 来自: 广州
最近访客 更多访客>>
社区版块
存档分类
最新评论

JDK5.0新特性之:枚举、泛型、其它

    博客分类:
  • Java
阅读更多

JDK5.0新特性之:枚举
文/陈刚 2005-11-09

一、前言
  JDK5.0出来有快一年了吧,泛泛的浏览过一些资料,不知道是文章写得生涩,还是我愚笨,总之是没太明白。反正所做的项目也没有用到,所以放在了一边。近来公司平台升级到JBOSS4和JDK5,看来要仔佃看它一看看。上网搜索了一些资料,花了两小时阅读,算明白了一些,好象也不是很难。现将心得和理解结集成文,以便后来者。
  JDK5.0主要有哪些新特性呢?如下列表
• 泛型
• 增强的for循环
• 自动装箱和自动拆箱
• 类型安全的枚举
• 可变长度参数
• 静态引入
• 元数据(注解)
• C风格的格式化输出
  本文先讲“类型安全的枚举”。
二、旧的历史
  举个例:有一个如下的“花(flower)”类,它有一个颜色(color)属性,用int型来表示其颜色。
public class Flower {
    private int color;
    public void setColor(int color) {
        this.color = color;
    }
   
    public int getColor(){
        return color;
    }
}
  这时一般我们会创建一个常量类,来存放颜色常量,如下:
public interface Color {
    int RED = 0;
    int BLACK = 1;
    int YELLOW = 2;
    int BLUE = 3;
    int WHITE = 4;
    int ORANGE = 5;
}
  以后我们用的时候就可以这样:
public class Client {
    public static void main(String[] args) {
        Flower f = new Flower();
        f.setColor(Color.RED);
    }
}
  Color类就是一个我们自己来实现的枚举类,当然它并不是严格意义上的枚举类,而且JDK5.0之前还没有枚举。这时会出现一个问题:setColor参数是int型,那么也许用户会不小心用一个非法的int值赋进去,这样就可能产生BUG,如:f.setColor(100);显示100并非是我们设定中的颜色值。
  在那本有名的java著作<<Effective java>> Joshua Bloch,用了专门的一章(第5章)来讨论这个问题,书中给出了一个比较严谨的解决方案,如下代码所示
(1)用Color类更改如下:
public class Color {
    private final int color;
    private Color(int color) {
        this.color = color;
    }
    public int getColorValue() {
        return color;
    }
    public static final Color RED = new Color(0);
    public static final Color BLACK = new Color(1);
    public static final Color YELLOW = new Color(2);
    public static final Color BLUE = new Color(3);
    public static final Color WHITE = new Color(4);
    public static final Color ORANGE = new Color(5);
}
(2)Flower类也要相应的更改,将int改为Color型。
public class Flower {
    private Color color;
    public void setColor(Color color) {
        this.color = color;
    }
    public Color getColor() {
        return color;
    }
}
(3)客户端Client不用修改。
  这样Flower类的setColor方法就只接收Color,也不用担心用户不心传入非法的参数值了。
三、新的特性:JDK5.0枚举
(1)简单的例子
  JDK5.0的枚举,正好可以解决上面我们讨论的问题,而且更简洁。还是用上面的代码做示例,只需要将Color类修改如下,Flower、Client都无法更改。
public enum Color {
    RED, BLACK, YELLOW, BLUE, WHITE, ORANGE
}
 很简单吧。接下来我们做更深一步的说明:
• 所有枚举类都是java.lang.Enum的子类(JDK内核自动将Color隐性继承自Enum)
• RED...ORANGE都是Color的实例对象
 上面的Color枚举类,功能好象还和以前的例子不完全一样,我们再次修改如下。
public enum Color {
    RED(0), BLACK(1), YELLOW(2), BLUE(3), WHITE(4), ORANGE(5);
   
    int color;
    Color(int color){
        this.color=color;
    }
   
    public int getColorValue(){
        return color;
    }
}
  从这里我们可以看到枚举类是如何创建构造函数及方法的,有一些隐性的东西要说明一下:
• 枚举类的构造函数要求是private的,如果象例子里那样不加修饰符,则Java会自动给加上。但如果你想加上public则会报错。
• RED(0), BLACK(1)...等枚举值必须定义在前面,而且最后一个枚举值ORANGE(5);未尾用分号结束。
(2)在循环中使用枚举类
  枚举类都有一个values方法可以得到它值的数组:Color[] colors=Color.values();而且枚举类都支持泛型(以后的文章会重点说到),所以我们可以这样来遍历这个数组:
        Color[] colors = Color.values();
        for (Color c : colors) {
            System.out.println("color value=" + c.getColorValue());
        }
更简洁的写法是这样:
        for (Color c : Color.values()) {
            System.out.println("color value=" + c.getColorValue());
        }
(3)在switch(分支)中使用枚举
  如下代码示例,这里要注意将“case RED:”写成“case Color.RED:”是会报错的,因为JDK5.0可以自动根据“switch (color)”就判断入你要使用什么枚举类型了。从这里也可以看出JDK5.0确实为用户做了帖心的考虑,尽量让程序员少写代码,写更简单的代码。
        Color color = Color.RED;
        switch (color) {
        case RED:
            //process ...
            break;
        case BLACK:
            //process ...
            break;
        case WHITE:
            //process ...
            break;
        default:
            //process ...
        }
(4)JDK5.0新增的集合:EnumMap 和EnumSet
  给出两个例子,一切尽在不言中了。(说明:这两个例子用到了JDK5.0中泛型的知识)
        Map<Color, String> map = new EnumMap<Color, String>(Color.class);
        map.put(Color.RED, "红色");
        map.put(Color.BLACK, "黑色");
        map.put(Color.WHITE, "白色");
       
        for (Color c : Color.values()) {
            System.out.println(c + " chinese is: " + map.get(c));
        }

        Set all = EnumSet.allOf(Color.class);
        EnumSet warmColors = EnumSet.of(Color.RED, Color.YELLOW);
        Set notWarmColors = EnumSet.complementOf(warmColors);
        Set notBlack = EnumSet.range(Color.RED, Color.YELLOW);
四、参考资料
http://dev.yesky.com/451/2044451_1.shtml
五、作者简介
陈刚,广西桂林人,著作有《Eclipse从入门到精通》
您可以通过其博客了解更多信息和文章:http://www.ChenGang.com.cn
JDK5.0新特性之:泛型
文/陈刚 2005-11-09


JDK5.0新特性之:范型
一、前言
  泛型这个词在现在的JAVA挺时髦,光从字面上你是无法知道它代表些什么东东的,所以我们还是不要从字面去理解,而是从一些实例去了解它吧。
二、泛型之前的日子
  JDK1.4之前是没有泛型的概念的,所以我们才会有下面的代码:
        List list = new ArrayList();
        list.add("aaaa");
        list.add("bbbb");
        list.add("cccc");
        for (Iterator it = list.iterator(); it.hasNext();) {
            String str = (String) it.next();
            System.out.println(str);
        }
  上面是一段很平常的代码,在一个List集合加入一些字符串,然后再用一个遍历循环把它打印出来。“String str = (String) it.next()”这一句我们可以看到List取出值都是Object,所以我们要得String型,还要做一个类型转换,真是麻烦。更麻烦的是list.add(Object obj)的参数是Object类型,所以如果我们一不小心把list.add("cccc");写成list.add(new Integer(76));程序在循环打印的类型转换中就会出错。
  问题:我们能不能让add方法只认String型呢?
  回答:可以!用JDK5.0的泛型。
三、泛型后的幸福生活
  JAVA有了泛型后,就象十年的老光棍讨了老婆,那个好处自不待言。我们来看看上面的例子改成泛型的写法是怎么样的:
        List<String> list = new ArrayList<String>();
        list.add("aaaa");
        list.add("bbbb");
        list.add("cccc");
        for (Iterator<String> it = list.iterator(); it.hasNext();) {
            String str=it.next();
            System.out.println(str);
        }
  看到差别了吗?泛型其实很简单,就是在定义类型的后面加上"<类型>"这样子的声明就行了,它主要还有以下差别:
• list.add方法只能接受String类型。list.add(new Integer(76))这样的语句不需要运行程序,在编译时就会检查通不过。
• it.next()的返回值不再是Object,而变成了String
  当然我们其实在循环部份也可以象下面这么写,是不是简洁了很多呢 :-)
        List<String> list = new ArrayList<String>();
        list.add("aaaa");
        list.add("bbbb");
        list.add("cccc");
        for (String str : list) {
            System.out.println(str);
        }
  当然需要说明的是,List不仅可以List<String>,也可以是List<Integer>等等其他任何类型。
四、更深入了解泛型
(1)层层推进的泛型声明
  “List<List> list;”表示什么呢?就是只接收List型的参数,比如:
        List<List> list = new ArrayList<List>();
        list.add(new ArrayList());
        list.add(new Vector());
        list.add(new LinkedList());
  这里要注意List是接口,ArrayList、Vector、LinkedList都是这一接口下的实现类。下面这个有点怪异了,“List<List<String>> list;”表示它只接受List型的参数,而且这种List型的参数又是只是只接受String型,有点层层推进的味道在里面了。
        List<List<String>> list = new ArrayList<List<String>>();
        list.add(new ArrayList<String>());
        list.add(new Vector<String>());
        list.add(new LinkedList<String>());
(2)使用泛型上限通通配符:extends
  这里要着重强调一点:变量的泛型声明和方法的参数的泛型声明有很大差别。
  变量声明成某类型,同时也可以接受它的子类。比如说Integer、Long、Float都是抽象类Number的子类,所以下面的代码一点问题也没有:
        List<Number> list = new ArrayList<Number>();
        list.add(new Integer(1));
        list.add(new Long(1));
        list.add(new Float(1.2));
  但如果换成方法参数的泛型声明则要严格得多了:子类也是不行的。比如下面的代码就是错误的,因为printList参数只接受Number值的List,就是是Number子类的Integer值的List也不行。
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        list.add(new Integer(1));
        list.add(new Integer(2));
        printList(list);
    }
   
    private static void printList(List<Number> list){
        for (Number num : list) {
            System.out.println(num);
        }
    }
 上面代码修改的方法有两个,如下
修改方法一:改变量的泛型声明
  将 List<Integer> list = new ArrayList<Integer>();
  改为 List<Number> list = new ArrayList<Number>();
修改方法二:用界限通配符改方法参数的泛型声明
  将 printList(List<Number> list)
  改为 printList(List<? extends Number> list)
  说明:extends 的含义就是表示参数可以接受Number型的子类。
(3)使用泛型下限通通配符:super
    在上限就有下限,下限行就是super,用法和extends一样,含义则和extends相反。比如printList(List<? super Integer> list)表示参数可以接受Integer型及Integer型的超类,即Number了,当然也包括Object这个顶级类。
(4)配置符:?
  ?表示可以接受任何类型,不过我觉得它用得不多,因为printList(List<?> list)和printList(List list)的作用是一样的。
五、创建一个支持泛型的类
(1)创建一个泛型的类
public class Point<T> {
    T x;
    T y;
    public T getX() {
        return x;
    }
    public T getY() {
        return y;
    }
    public void setX(T x) {
        this.x = x;
    }
    public void setY(T y) {
        this.y = y;
    }
}
  使用这个类的代码如下:
        Point<Integer> p = new Point<Integer>();
        p.setX(new Integer(1));
        p.setY(new Integer(2));
       
        Point<String> b = new Point<String>();
        b.setX("1");
        b.setY("2");
  说明:在Point<T>的定义中,T并非关键字,你也可以这样定义Point<ABC>,当然一般还是写T吧,简单也规范。
(2)泛型类的继承与实现
  java.util.Comparator类是JDK里用来排序的,其源代码如下:
package java.util;
public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}
   一个实现此接口的类如下:
    public class MyComparator<T> implements Comparator<ObjectInstance> {
        public int compare(ObjectInstance o1, ObjectInstance o2) {
            String s1 = o1.getObjectName().getCanonicalName();
            String s2 = o2.getObjectName().getCanonicalName();
            return s1.compareToIgnoreCase(s2);
        }
    }
  说明:ObjectInstance可能大家还太明白,这是我实际项目中的一段代码(关于JMX的),ObjectInstance全称javax.management.ObjectInstance。MyComparator的使用代码如下:
Set set = ......(省略)
List<ObjectInstance> mbeans = new ArrayList<ObjectInstance>(set);
Collections.sort(mbeans, new MyComparator<ObjectInstance>());
六、最后的感言
  JAVA有了泛型就象老光棍讨了老婆,好处大大的,但和女人一样麻烦也跟着来了:它的严格类型检查,使隐藏的BUG更少。有些地方确实也使代码简洁了,有些地方却会使得代码更复杂。所以运用之妙在于是否用得适当,尽量把泛型往简单里用,别越搞越复杂了。
参考资料
J2SE 5.0中的泛型 http://www.matrix.org.cn/resource/article/43/43634_java_generics.html
作者简介
陈刚,广西桂林人,著作有《Eclipse从入门到精通》
您可以通过其博客了解更多信息和文章:http://www.ChenGang.com.cn


JDK5.0新特性之:其它
一、自动装箱与拆箱
  这是一个很体帖的改进。在JDK5.0以前我们常看到下面的代码。
        Vector v=new Vector();
        v.add(new Integer(1));
  因为Vector.add只接受对象,而1不是对象,所以我们还得把整数1装箱到Integer对象里,真麻烦:( 。现在我们可以这样了,是不是方便许多呢?如下:
        Vector<Integer> v=new Vector<Integer>(1);
        v.add(1);
二、新的for循环
  在前面讲枚举的时候已经用到了,再把进面的代码粘贴一次吧,如下:
        Color[] colors = Color.values();
        for (Color c : colors) {
            System.out.println("color value=" + c.getColorValue());
        }
  注意:不要以为Color[]是数组,就以为for-each循环只适用于数组,对于集合(List、Set)一样可以的,如下:
        List<String> list = new ArrayList<String>();
        for (String str : list) {
            System.out.println(str);
        }
三、参数个数的可变性
  参数的个数是可变的,如下代码中的test方法就是一个int型的可变参数,当然你可以将参数设成String、Object等。
public class Test {
    public static void main(String[] args) {
        test();
        test(1);
        test(2, 3, 4);
    }
    public static void test(int... ints) {
        for (int i : ints) {
            System.out.println(i);
        }
    }
}
四、静态引用
  什么是静态引用,先看下面的代码:
package jdk5;
public class Model {
    public static void operate() {}
    public static String ABC;
}
Model类有一个静态方法operate和一个静态变量(必须是静态的),这是一个很普通的类。而下面的Test类就有些奇怪了:
package jdk5;
import static jdk5.Model.ABC;
import static jdk5.Model.operate;
public class Test {
    public static void test() {
        operate();
        String s = ABC;
    }
}
有那些奇怪的地方呢?
(1)import 后面多了一个static
(2)jdk5.Model后面多写了方法operate和变量ABC
  有了上面奇怪的写法之后,以后在Test类中要使用import static的方法、变量就可以象本地方法和变量一样用了。
五、C风格格式化输出
  以前我们在这里写代码:
    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        int sum = x + y;
        System.out.printf(x + " + " + y + " = " + sum);
    }
  在JDK5.0后,我们这样写代码:
    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        int sum = x + y;
        System.out.printf("%d + %d = %d", x, y, sum);
    }
  两段代码的效果都是一样的“10 + 20 = 30”,但第二种C风格的写法显示方便易读多了。
六、元数据(注解)
  这是JDK5.0学XDoclt的,有了注解,以后我们可以不必写接口,EJB写起来会方便很多。EJB3.0要精简写法,注解要占一些功劳。
  介绍一个常用的注解:@Override,示例如下:
public class Test extends ATest{

    @Override
    public void test() {
    }
}
 在方法前加了这个注解,就限定死了Test的这个方法一定要是覆盖自ATest的方法test,否则就会报错。比如你不心把方法test()写成了tesd(),编译时就会报错。另一个要注意的是@Override用于定义覆盖接口的方法,也就是说ATest必须是一个抽象类、普通类,但不能是接口。
   另一个常见到的注解是@Deprecated,表明该项(类、字段、方法)不再被推荐使用。不过我们自己一般很少用到这个注解。
  好了,注解只讲这两个吧,也不必了解太多,知道个概念,以后用到的时候再说吧。关于注解,建议看看XDoclt,这是一个开源小工具,在项目开发中非常好用。

 

分享到:
评论

相关推荐

    JDK5一些新特性关于枚举泛型等

    JDK5.0的新特性一、枚举类型generic-泛型自动解包装与自动包装的功能

    jdk5.0新特性汇总(超级详细)

    加强for循环,静态导入,枚举,泛型,反射,注解,类加载器,代理等。张孝祥总结,绝对经典。

    JDK5.0新特性 自动包装和解包等新功能

    自动包装和解包 更优化的循环语句 参数可变的方法 printf 枚举 静态引用 泛型

    Java JDK 5.0学习笔记(第二部分)

    本书是作者良葛格本人近几年来学习Java的心得笔记,结构按照作者的学习脉络依次展开,从什么是Java、如何配置Java开发环境、基本的Java语法到程序流程控制、管理类文件、异常处理、枚举类型、泛型、J2SE中标准的API...

    良葛格Java JDK 5.0学习笔记

    本书是作者良葛格本人近几年来学习Java的心得笔记,结构按照作者的学习脉络依次展开,从什么是Java、如何配置Java开发环境、基本的Java语法到程序流程控制、管理类文件、异常处理、枚举类型、泛型、J2SE中标准的API...

    JDK5.0的11个主要新特征.doc

    1 泛型(Generic) 2 增强循环(Enhanced for Loop) 3 可变参数(Variable Arguments) 4 自动实现装箱和解箱操作(Boxing/Unboxing Conversions) 5 静态导入(Static Imports) 6 枚举类(Enumeration Classes) 7 元数据...

    Java JDK 5.0学习笔记(第一部分)

    本书是作者良葛格本人近几年来学习Java的心得笔记,结构按照作者的学习脉络依次展开,从什么是Java、如何配置Java开发环境、基本的Java语法到程序流程控制、管理类文件、异常处理、枚举类型、泛型、J2SE中标准的API...

    Java JDK 7学习笔记(国内第一本Java 7,前期版本累计销量5万册)

    著作:《java jdk 5.0学习笔记》、《java se 6技术手册》、《spring技术手册》等 译作:《ajax实战手册》、《jquery实战手册(第2版)》 个人网站:http://openhome.cc 目录 《java jdk 7学习笔记》 chapter1 ...

    Java2核心技术.part5

    附录B更新的JDK 5.0代码 Java2核心技术II卷.高级特性 目录: 译者序 前言 第1章 多线程 1.1 什么是线程 1.2 中断线程 1.3 线程状态 1.4 线程属性 1.5 同步 1.6 阻塞队列 1.7 线程安全的集合 1.8 ...

    java jdk实列宝典 光盘源代码

    16 j2se5.0 新特性 自动装箱和拆箱; 新的for循环; 枚举类型; 静态导入; 格式化输出; 使用ProcessBuilder执行本地命令; 泛型编程; 监控和管理虚拟机;新的线程执行架构; 线程锁; 线程条件; 线程同步装置:semaphore ...

    Java2核心技术.part3

    附录B更新的JDK 5.0代码 Java2核心技术II卷.高级特性 目录: 译者序 前言 第1章 多线程 1.1 什么是线程 1.2 中断线程 1.3 线程状态 1.4 线程属性 1.5 同步 1.6 阻塞队列 1.7 线程安全的集合 1.8 ...

    Java2核心技术.part1

    附录B更新的JDK 5.0代码 Java2核心技术II卷.高级特性 目录: 译者序 前言 第1章 多线程 1.1 什么是线程 1.2 中断线程 1.3 线程状态 1.4 线程属性 1.5 同步 1.6 阻塞队列 1.7 线程安全的集合 1.8 Callable和Future...

    Java2核心技术.part6

    附录B更新的JDK 5.0代码 Java2核心技术II卷.高级特性 目录: 译者序 前言 第1章 多线程 1.1 什么是线程 1.2 中断线程 1.3 线程状态 1.4 线程属性 1.5 同步 1.6 阻塞队列 1.7 线程安全的集合 1.8 ...

    Java2核心技术.part4

    附录B更新的JDK 5.0代码 Java2核心技术II卷.高级特性 目录: 译者序 前言 第1章 多线程 1.1 什么是线程 1.2 中断线程 1.3 线程状态 1.4 线程属性 1.5 同步 1.6 阻塞队列 1.7 线程安全的集合 1.8 ...

    Java2核心技术.part2

    附录B更新的JDK 5.0代码 Java2核心技术II卷.高级特性 目录: 译者序 前言 第1章 多线程 1.1 什么是线程 1.2 中断线程 1.3 线程状态 1.4 线程属性 1.5 同步 1.6 阻塞队列 1.7 线程安全的集合 1.8 ...

    Java JDK实例宝典

    全部代码出自电子工业出版社夏先波的《Java JDK实例宝典》一书,本书以J2SE 5.0为开发环境,选取Java应用的典型实例,循序渐进地介绍了Java语言的各种开发方法和技巧,实例代码注释详细规范,思路清晰。 第1章 ...

    javaSE代码实例

    17.1.2 JavaSE 5.0中固定尺寸线程池的基本知识 374 17.1.3 自定义尺寸固定线程池的使用 375 17.1.4 单任务线程池的使用 377 17.1.5 可变尺寸线程池的使用 378 17.1.6 延迟线程池的使用 380 17.1.7 使用...

Global site tag (gtag.js) - Google Analytics