Java系列(十三)__Java新特性(泛型、枚举、Annotation)

所谓的Java新特性现在都是指从JDK 1.5之后开始的,例如,在前面已经学习过两个新特性:switch支持String判断(JDK 1.7提供的)、自动装箱和拆箱、可变参数、foreach、静态导入、泛型、枚举、Annotation。

         对于所有的新特性,我的个人建议:有些新特性你今天一定是不知道怎么用的,我们今天只是来看一下这些语法,至于使用方面,慢慢来观察。

1.1、可变参数(理解)

         如果说现在有这样一个要求,要求实现整数的加法操作,并且方法可以接收任意多个整型数据一起实现加法操作。

         如果按照传统的思路,现在的实现方式可以采用数组作为参数的类型。

package cn.mldn.demo;

public class TestDemo {

    public static void main(String[] args) {

        System.out.println(add(new int[] { 1, 2 }));

        System.out.println(add(new int[] { 1, 2, 3 }));

    }

    public static int add(int data[]) {

        int sum = 0 ;

        for (int x = 0; x < data.length; x++) {

            sum += data[x] ;

        }

        return sum ;

    }

}

         此处使用数组完全属于无奈,因为现阶段只能够通过数组传递多个数据。但是本操作虽然实现了功能,可是在调用形式上有问题,题目的本身要求:可以接收任意多个整型数据,每一个数据之间应该使用“,”分隔才最合适。所以为了解决这种传递任意参数的问题,从JDK 1.5之后增加了可变参数方法的定义。

[public | protected | private] [final] [static] 返回值类型方法名称(参数类型 ... 参数名称) [throws 异常,异常, ..] {

         [return [返回值] ;]

}

         虽然可变参数的语法形式上有些别扭,但是把它按照数组一样理解。

范例:使用可变参数

package cn.mldn.demo;

public class TestDemo {

    public static void main(String[] args) {

        System.out.println(add(new int[] { 1, 2 }));

        System.out.println(add(new int[] { 1, 2, 3 }));

        System.out.println(add(1, 2, 3));

    }

    public static int add(int ... data) {   // 可变参数

        int sum = 0 ;

        for (int x = 0; x < data.length; x++) {

            sum += data[x] ;

        }

        return sum ;

    }

}

         可变参数最大的好处在于方法调用时,可以随意编写参数进行数据的传递,但是此类操作只会在系统类库的学习中遇见,你们自己写的代码,先别用。

1.2、foreach输出(理解)

         如果说现在有一个数组要想实现数据的输出,一定使用for循环完成。

范例:使用for循环输出数组

package cn.mldn.demo;

public class TestDemo {

    public static void main(String[] args) {

        int data[] = new int[] { 1, 2, 3 };

        for (int x = 0; x < data.length; x++) {

            System.out.println(data[x]);

        }

    }

}

         而从JDK 1.5之后增加了一类新的输出结构

for(数据类型 变量 : 数组|集合) {

         // 操作代码

}

         本程序的意思是根据数组的长度进行循环操作,并且每次循环的时候依次取出数组之中的每一个元素,将其赋值给声明的变量。

范例:使用foreach输出

package cn.mldn.demo;

public class TestDemo {

    public static void main(String[] args) {

        int data[] = new int[] { 1, 2, 3 };

        for (int x : data) {    // 循环次数由数组长度定义

            System.out.println(x);  // 每一个内容不再根据下标取得了

        }

    }

}

         此类操作在初学的时候不建议使用,因为你们要习惯下标问题。

1.3、静态导入(没用,都不知道有什么用)

         首先来观察这样一种情况,如果说现在有一个类,这个类里面的方法全部是static方法。

范例:定义MyMath类

package cn.mldn.util;

public class MyMath {

    public static int add(int x, int y) {

        return x + y;

    }

    public static int sub(int x, int y) {

        return x - y;

    }

}

         如果在传统情况下,要使用以上类之中所定义的方法,应该先导入包.类,而后通过类名称调用。

package cn.mldn.demo;

import cn.mldn.util.MyMath;

public class TestDemo {

    public static void main(String[] args) {

        System.out.println(MyMath.add(10, 20));

        System.out.println(MyMath.sub(30, 20));

    }

}

         但是有一部分人群就有疑问。在之前学习过,如果在主类中定义的方法,并且由主方法直接调用的话,则方法上必须加上static,那么能不能把以上导入包中的static方法不使用类名称而直接调用呢(相当于将方法定义在主类之中)。

范例:静态导入

package cn.mldn.demo;

import static cn.mldn.util.MyMath.*;

public class TestDemo {

    public static void main(String[] args) {

        System.out.println(add(10, 20));

        System.out.println(sub(30, 20));

    }

}

         此时调用的方法不再加入类名称,那么就好比这些方法直接在主类中定义一样。

1.4、泛型(理解)

         此部分的内容你先听明白,不要去思考怎么用。因为用起来没这么麻烦。

1.4.1、泛型的引出

         例如,现在要求用户设计一个表示坐标点的类,但是此坐标保存数据有可能有如下几种:

                   · 整型数据:x = 10、y = 20;

                   · 小数数据:x = 10.2、y = 20.3;

                   · 字符串数据:x = 东经100度、y = 北纬20度。

         那么现在很明显,Point类表示坐标,那么针对于坐标点有三类数据,而在Point类里面应该定义有两个属性:x、y,所以现在首先要解决的问题就是确定出x、y属性的数据类型。

         既然现在要保存有int、double、String或者以后有可能的其它类型,则自然想到使用Object,因为存在如下转换关系:

                   · 保存整型:int è 自动装箱为Integer è 向上转型为Object;

                   · 保存小数:double è 自动装箱为Double è 向上转型为Object;

                   · 保存字符串:String è 向上转型为Object。

范例:实现代码

class Point {

    private Object x ;

    private Object y ;

    public void setX(Object x) {

        this.x = x;

    }

    public void setY(Object y) {

        this.y = y;

    }

    public Object getX() {

        return x;

    }

    public Object getY() {

        return y;

    }

}

范例:保存int型数据

public class TestDemo {

    public static void main(String[] args) {

        // 第一层次:设置坐标数据

        Point point = new Point() ;

        point.setX(10); // 向上转型为Object

        point.setY(20); // 向上转型为Object

        // 第二层次:取得坐标数据

        int x = (Integer) point.getX() ;    // 向下转型

        int y = (Integer) point.getY() ;    // 向下转型

        System.out.println("x = " + x + ",y = " + y);

    }

}

范例:保存double型数据

public class TestDemo {

    public static void main(String[] args) {

        // 第一层次:设置坐标数据

        Point point = new Point() ;

        point.setX(10.2);   // 向上转型为Object

        point.setY(20.3);   // 向上转型为Object

        // 第二层次:取得坐标数据

        double x = (Double) point.getX() ;  // 向下转型

        double y = (Double) point.getY() ;  // 向下转型

        System.out.println("x = " + x + ",y = " + y);

    }

}

范例:保存String型数据

public class TestDemo {

    public static void main(String[] args) {

        // 第一层次:设置坐标数据

        Point point = new Point() ;

        point.setX("东经100度"); // 向上转型为Object

        point.setY("北纬20度");  // 向上转型为Object

        // 第二层次:取得坐标数据

        String x = (String) point.getX() ;  // 向下转型

        String y = (String) point.getY() ;  // 向下转型

        System.out.println("x = " + x + ",y = " + y);

    }

}

         此时的代码已经很好的完成了给出的系统要求,但是本程序依靠的是Object可以接收所有数据类型这一特征展开的,于是成是Object,败笔也在于次,因为Object保存的范围太大了。那么在程序运行之中就有可能出现隐患。

范例:观察问题

public class TestDemo {

    public static void main(String[] args) {

        // 第一层次:设置坐标数据

        Point point = new Point() ;

        point.setX(10); // 向上转型为Object

        point.setY("北纬20度");  // 向上转型为Object

        // 第二层次:取得坐标数据

        String x = (String) point.getX() ;  // 向下转型

        String y = (String) point.getY() ;  // 向下转型

        System.out.println("x = " + x + ",y = " + y);

    }

}

         此时程序编译的时候没有任何的错误,但是执行的时候会出现“ClassCastException”,所以来讲此时的程序就存在有安全隐患,事实上,所有对象的向下转型都有可能存在这种安全隐患。最好的做法就是别转型。

         以上的问题在JDK 1.5之前根本就无法解决,而在JDK 1.5之后由于引入了泛型的处理机制,所以此类问题很好的解决了。所谓的泛型指的是类之中定义的属性,在程序编译的时候不给出具体的类型,只给出一个类型的占位标记,而后在使用此类产生对象时,再设置具体类型。

范例:利用泛型修改

public class TestDemo {

    public static void main(String[] args) {

        // 第一层次:设置坐标数据

        Point point = new Point() ;

        point.setX("东经100度");

        point.setY("北纬20度");

        // 第二层次:取得坐标数据

        String x = point.getX() ;   // 向下转型

        String y = point.getY() ;   // 向下转型

        System.out.println("x = " + x + ",y = " + y);

    }

}

         那么此时由于泛型技术的出现,取消了向下转型,而向下转型取消后就相当于消除了所有的ClassCastException这种安全隐患。但是需要注意的是,在设置泛型类型的时候只能够使用引用类型,即:如果要保存的是int或double,应该使用包装类操作。

范例:保存int

public class TestDemo {

    public static void main(String[] args) {

        // 第一层次:设置坐标数据

        Point point = new Point() ;

        point.setX(10);

        point.setY(20);

        // 第二层次:取得坐标数据

        int x = point.getX() ;

        int y = point.getY() ;

        System.out.println("x = " + x + ",y = " + y);

    }

}

         请把以上的分析理解了,代码可以暂时不会。但是提示一下,为了照顾JDK 1.5之前所编写的代码。为了保证以前的代码没有任何的错误,所以如果在使用泛型标记类的时候没有设置泛型类型,那么会按照Object做默认处理。

public class TestDemo {

    public static void main(String[] args) {

        // 第一层次:设置坐标数据

        Point point = new Point() ;

        point.setX(10);

        point.setY(20);

        // 第二层次:取得坐标数据

        int x = (Integer) point.getX() ;

        int y = (Integer) point.getY() ;

        System.out.println("x = " + x + ",y = " + y);

    }

}

         这类的情况一直到今天还会继续发生着,所以如果不设置泛型请一定要记住,类型就是Object。

1.4.2、通配符(次重点)

         下面为了方便讲解,重新定义一个使用泛型的类。

class Info {

    private T msg ;

    public void setMsg(T msg) {

        this.msg = msg;

    }

    public T getMsg() {

        return msg;

    }

}

         下面要针对于Info类的对象实现一次引用传递。

public class TestDemo {

    public static void main(String[] args) {

        Info info = new Info() ;

        info.setMsg("Hello World .");

        fun(info) ;

    }

    public static void fun(Info temp) {

        System.out.println(temp.getMsg());

    }

}

         但是既然是设置泛型,肯定不可能只是String一种,有可能设置其它类型。此时,如果传递的是一个“Info”那么fun()方法是不可能使用的。如果用重载也不可能,因为重载的看重的是数据类型。于是现在发现,泛型一旦使用之后,在之前好不容易解决的参数统一问题,现在又重新回来了。于是这个时候有同学说了,那么既然参数上设置泛型会存在有问题,那么不如就别设置了。但是问题又回来了,如果不设置泛型,那么参数表示的类型就是Object。

范例:参数上不使用泛型

public class TestDemo {

    public static void main(String[] args) {

        Info info = new Info() ;

        info.setMsg("可乐");

        fun(info) ;

    }

    public static void fun(Info temp) {

        temp.setMsg(100)  // 现在修改为Integer

        System.out.println(temp.getMsg());

    }

}

         所以这个时候发现,不设置泛型,操作的数据形式就可能出现混乱,于是现在就可以总结出来,我们需要的是一个可以接收任意的泛型类型,但是又不能修改里面数据,并且可以取得里面数据的操作。那么现在就只能够利用通配符“?”来完成此功能。

范例:使用“?”解决

public class TestDemo {

    public static void main(String[] args) {

        Info info = new Info() ;

        info.setMsg("可乐");

        fun(info) ;

    }

    public static void fun(Info

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值