可变参数
public [static] [final] 返回值类型 方法名称 (参数类型 ... 变量) { // 虽然定义方式改变了,但本质上还是个数组
[return [返回值] ;]
}
foreach
for (数据类型 变量 : 数组 | 集合) {
// 操作代码
}
这表示通过自动的方式将数组之中的每一个元素赋值给变量,而后在for循环的方法体之中进行操作
静态导入
如果一个类之中的全部方法都是static型的,则可以使用如下的语法进行静态导入:
import static 包.类.* ;
表示的是将这个指定类之中的全部方法导入进来,最后就好象这些方法全部是在主类之中定义的方法一样。
这种比较难受的语法,也只是出现在讲课之中,本人是绝对不会使用的,你们也可以忘了它,而且就算不忘也别使。
JDK 1.5三大主要新特性 —— 泛型
泛型的引出(重点)
下面首先通过一个简单的分析来研究一下泛型出现的主要目的,例如:现在要求定义一个表示坐标的操作类(Point),这个类可以表示三种类型的坐标:
· 整数坐标:x = 10、y = 20;
· 小数坐标:x = 10.1、y = 20.3;
· 字符串数据:x = "东经100度"、y = "北纬20度"。
类之中如果要想保存以上的数据,一定需要定义x和y两个属性,而这两个属性可以接收三种数据类型,那么只能使用Object类来定义会比较合适,这样会发生如下的几种转换关系:
· 整数:int è 自动装箱为Integer è 向上转型为Object;
· 小数:double è 自动装箱为Double è 向上转型为Object;
· 字符串:字符串 è 向上转型为Object。
范例:定义Point类,使用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; } } |
下面开始设置不同的数据类型,以测试程序。
范例:设置整型
public class TestDemo { public static void main(String[] args) throws Exception { // 一层设置 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); } } |
范例:设置小数
public class TestDemo { public static void main(String[] args) throws Exception { // 一层设置 Point point = new Point() ; point.setX(10.2) ; point.setY(20.3) ; // 一层取出 double x = (Double) point.getX() ; double y = (Double) point.getY() ; System.out.println("X的坐标是:" + x + ",Y的坐标是:" + y); } } |
范例:设置字符串
public class TestDemo { public static void main(String[] args) throws Exception { // 一层设置 Point point = new Point() ; point.setX("东经100度") ; point.setY("北纬20度") ; // 一层取出 String x = (String) point.getX() ; String y = (String) point.getY() ; System.out.println("X的坐标是:" + x + ",Y的坐标是:" + y); } } |
看起来现在的功能都实现了,并且根据之前所学的内容,也只能做到这些了,但是本程序是否有问题?
本程序解决问题的关键就在于Object类,所有的类型都可以向Object转换,但是成是Object,败也是Object。
public class TestDemo { public static void main(String[] args) throws Exception { // 一层设置 Point point = new Point() ; point.setX(10) ; // 此处设置成int型(Integer型) point.setY("北纬20度") ; // 一层取出 String x = (String) point.getX() ; String y = (String) point.getY() ; System.out.println("X的坐标是:" + x + ",Y的坐标是:" + y); } } |
这个时候程序并没有出现任何的语法错误,因为数字10被装箱成了Integer,可以使用Object接收,从技术上而言,本操作没有问题,但是从实际来讲,数据是有错误的,因为没有统一,所以在取得数据并且执行向下转型的过程之中就会出现如下的错误提示信息:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String |
所以,就可以得出一个结论:以上的代码存在了安全隐患,并且这一安全隐患并没有在程序编译的过程之中检查出来,而现在就可以利用泛型来解决这种尴尬的问题?
泛型:类之中操作的属性或方法的参数的类型不在定义的时候声明,而是在使用的时候动态设置。
class Point private T x ; private T y ; public void setX(T x) { this.x = x; } public void setY(T y) { this.y = y; } public T getX() { return x; } public T getY() { return y; } } |
此时就表示point类之中的x和y两个属性的类型暂时还不知道,等待程序时候的时候动态配置。
范例:使用泛型
public class TestDemo { public static void main(String[] args) throws Exception { // 一层设置 Point point.setX("东经100度") ; point.setY("北纬20度") ; // 一层取出 String x = point.getX() ; String y = point.getY() ; System.out.println("X的坐标是:" + x + ",Y的坐标是:" + y); } } |
此时没有了向下转型的这些操作关系,那么程序就避免了安全性的问题,而且如果设置的类型不统一,在程序编译的过程之中也是可以很好的解决了,直接会报出语法错误。
而当用户在使用Point类声明对象的时候没有设置泛型,那么程序在编译的过程之中会出现警告信息,而且为了保证程序不出现错误,所有的类型都将使用Object进行处理。使用泛型可以很好的解决数据类型的统一问题。
但是在此处需要提醒的是,JDK 1.5和JDK 1.7在定义泛型的时候是稍微有些区别的。
范例:JDK 1.5的时候声明泛型对象操作
Point |
以上是JDK 1.5的语法,在声明对象和实例化对象的时候必须都同时设置好泛型类型。
范例:JDK 1.7的时候简化了
Point |
这个时候实例化对象时的泛型类型就通过声明时的泛型类型来定义了。
通配符(重点)
泛型的出现的确是可以解决了数据类型的统一问题以及避免了向下转型操作,但同时又会带来新的问题,下面先通过一段程序来观察一下会产生什么问题?
范例:为了简化操作,下面定义一个简单的泛型类
class Message<T> { private T info; public void setInfo(T info) { this.info = info; } public T getInfo() { return info; } } |
范例:使用以上类对象执行引用传递
public class TestDemo { public static void main(String[] args) throws Exception { Message msg.setInfo("Hello World ."); print(msg); // 引用传递 } public static void print(Message System.out.println(temp.getInfo()); // 只是输出 } } |
但是,如果现在定义的泛型类型不是String了呢?例如:换成了int(不能写基本类型,只能是包装类)。
public class TestDemo { public static void main(String[] args) throws Exception { Message msg.setInfo(100); print(msg); // 无法进行引用传递 } public static void print(Message System.out.println(temp.getInfo()); // 只是输出 } } |
发现这个时候的print()方法无法再接收Message
public class TestDemo { public static void main(String[] args) throws Exception { Message msg.setInfo(100); print(msg); // 无法进行引用传递 } public static void print(Message System.out.println(temp.getInfo()); // 只是输出 } public static void print(Message System.out.println(temp.getInfo()); // 只是输出 } } |
这个时候发现按照之前的方式根本就无法进行方法的重载,方法的重载没有说为一个类而定义的,因为方法重载的时候观察的不是泛型类型,而是类的名称,或者是说数据类型的,那么现在就可以发现,这个给出了泛型类之后,就相当于将一个类又划分成了若干个不同的小类型。
那么现在的问题:方法接收的参数问题又严重了,而且比之前使用对象多态性解决问题时出现的麻烦更大了,至少那个时候可以利用重载来接收一个类的所有子类对象,而现在呢连重载的机会都不给了。
这个时候,有人提出了,干脆在定义方法的时候就别写泛型类型了。
范例:定义方法时不写上泛型类型
public class TestDemo { public static void main(String[] args) throws Exception { Message msg.setInfo(100); print(msg); // 无法进行引用传递 } public static void print(Message temp) { System.out.println(temp.getInfo()); // 只是输出 } } |
虽然现在在print()方法的参数上出现了警告,但是现在的程序可算是正常了。但是新的问题又来了,问题就在于方法操作中,没有类型限制了:
public static void print(Message temp) { temp.setInfo(100); // 设置Integer System.out.println(temp.getInfo()); // 只是输出 } |
发现此时在print()方法之中操作的时候,由于没有设置泛型类型,那么所有的类型都统一变为了Object,也就可以修改了,而通过本程序也就发现了,必须找到一种方法:此方法可以接收任意的泛型类型的设置,并且不能够修改,只能够输出。为了解决这样的问题,可以使用通配符“?”表示。
public static void print(Message<?> temp) { System.out.println(temp.getInfo()); // 只是输出 } |
由于“?”出现的情况较多,尤其是在学习一些类库的时候,所以对于?就记住一点,表示任意类型,如果有参数返回的时候也是这个“?”,就当成Object进行理解。
既然现在谈到了Object,那么现在实际上又有了另外一个问题:对于所有的子类,都是Object子类,那么如果对于之前的程序使用Message
Message Message |
因为Object要比String的范围大,把我去超市买的东西理解为“new Message
而在通配符“?”上又衍生出了两个子符号:
· 设置泛型的上限:? extends 类;
|- 例如:? extends Number,表示只能是Number或者是Number的子类Integer等;
· 设置泛型的下限:? super 类;
|- 例如:? super String,表示只能是String或者是String的父类(Object)。
范例:设置泛型上限
package cn.mldn.demo; class Message private T info; public void setInfo(T info) { this.info = info; } public T getInfo() { return info; } } public class TestDemo { public static void main(String[] args) throws Exception { Message msg.setInfo(100); print(msg); // 引用传递 } public static void print(Message> temp) { System.out.println(temp.getInfo()); // 只是输出 } } |
范例:设置泛型下限,在方法的参数上使用
package cn.mldn.demo; class Message private T info; public void setInfo(T info) { this.info = info; } public T getInfo() { return info; } } public class TestDemo { public static void main(String[] args) throws Exception { Message msg.setInfo("Hello World ."); print(msg); // 引用传递 } // 只能是String的泛型或者是Object的泛型使用 public static void print(Message super String> temp) { System.out.println(temp.getInfo()); // 只是输出 } } |
所有的符号实际上只是要求可以看懂,还没到用的时候。
泛型接口
在之前所有定义的泛型之中,都是在类上定义的,而对于接口也是可以进行泛型定义的,而使用泛型定义的接口可以称为泛型接口。
interface Message public String echo(T msg) ; } |
而对于泛型接口的实现,在Java之中有两种方式:
方式一:在子类上继续定义泛型,同时此泛型在接口上继续使用
package cn.mldn.demo; interface Message public String echo(T msg) ; } class MessageImpl public String echo(T msg) { return "ECHO : " + msg; } } public class TestDemo { public static void main(String[] args) throws Exception { Message System.out.println(msg.echo("张三")); } } |
方式二:在子类上设置具体类型
package cn.mldn.demo; interface Message public String echo(T msg) ; } class MessageImpl implements Message public String echo(String msg) { return "ECHO : " + msg; } } public class TestDemo { public static void main(String[] args) throws Exception { Message System.out.println(msg.echo("张三")); } } |
以上的两种方式要求懂就可以了,日后会遇到这样的开发,而且使用起来也会很容易。
泛型方法
对于泛型除了可以定义在类上之外,也可以在方法上进行定义,而在方法上定义泛型的时候,这个方法不一定非要在泛型类中定义。
package cn.mldn.demo; public class TestDemo { public static void main(String[] args) throws Exception { Integer result[] = get(1, 2, 3); for (int temp : result) { // 连自动拆箱都包含了 System.out.println(temp); } } public static return args; } } |
泛型方法在以后的学习之中是一定会见到,见到的时候只要不觉得恶心就行了。