所谓的Java新特性现在都是指从JDK 1.5之后开始的,例如,在前面已经学习过两个新特性:switch支持String判断(JDK 1.7提供的)、自动装箱和拆箱、可变参数、foreach、静态导入、泛型、枚举、Annotation。
对于所有的新特性,我的个人建议:有些新特性你今天一定是不知道怎么用的,我们今天只是来看一下这些语法,至于使用方面,慢慢来观察。
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 ; } } |
可变参数最大的好处在于方法调用时,可以随意编写参数进行数据的传递,但是此类操作只会在系统类库的学习中遇见,你们自己写的代码,先别用。
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); // 每一个内容不再根据下标取得了 } } } |
此类操作在初学的时候不建议使用,因为你们要习惯下标问题。
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)); } } |
此时调用的方法不再加入类名称,那么就好比这些方法直接在主类中定义一样。
4、泛型(理解)
此部分的内容你先听明白,不要去思考怎么用。因为用起来没这么麻烦。
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。
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 |