黑马程序员 — JDK1.5 新特性

本文详细介绍了JDK1.5引入的新特性,包括泛型的类型参数限制、自定义泛型方法、类型推断,枚举的使用,可变参数的便利性,自动包装和解包,更优化的for-each循环以及静态引用的引入,这些特性极大地提高了代码的清晰性和效率。
摘要由CSDN通过智能技术生成
------- android培训java培训、期待与您交流! ----------


 

JDK1.5的新特性




泛型:


在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,
而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。


泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。



规则和限制:

1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。

2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。

3、泛型的类型参数可以有多个。
4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为“有界类型”。
5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String");

泛型是提供给javac编译器使用的,右以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译吕编译带类型说明的集合时会去掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法返回的值和原始类型完全一样,由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个集合中添加其它类型的数据,例如: 用反射得到集合,再调用其add方法即可



ArrayList<E>类定义和ArrayList<Integer>类引用涉及如下术语:

1.整个称为ArrayList<E>泛型类型
2.ArrayList<E>中的E称为类型变量或类型参数
3.整个ArrayList<Integer>称为参数化类型
4.ArrayList<Integer>中的Integer被称为类型参数的实例或实际类型参数
5.ArrayList<Integer>中的<>念 typeOf
6.ArrayList称为原始类型


参数化类型与原始类型的兼容性:

参数化类型可以引用一个原始类型的对象,编译报告警告,例如: Collection<String> c = new Vector();

原始类型可以引用一个参数化类型的对象,编译报告警告,例如: Collection c = new Vector<String>(); //原来的方法接收一个集合参数,新的类型也要能传进去

参数化类型不考虑类型参数的继承关系:

Vector<String> v = new Vector<Object>(); // 错误: 因为Object可以为任意类型,
Vector<Object> v = new Vector<String>(); // 错误 
数组的元素不能使用参数化类型,例如: Vector<Object> vectorList[] = new Vector<Integer>[10];





泛型中的?通配符:

使用?通配符可以引用其它各种参数化的类型,?通配符定义变量主要用作引用,可以调用于参数无关的方法,不能调用与参数有关的方法


错误方式:

public static void printClooection(Collection<Object> cols)
{
for (Object obj : cols)
{
System.out.println(obj);
}
cols.add("String"); // 正确: 因为String是Object类型的子类
cols = new HashSet<Date>();  //错误
}

正确方式 :

public static void printCollection(Collection<?> cols)
{
cols.add("String"); //  错误: 因为它不知道自己未来匹配的就一定是String
cols.size();  //正确: 此方法与类型参数没有关系
}


泛型中的?通配符的扩展:

限定通配符的上边界:


正确: Vector<?> extends Number > x = new Vector<Integer>();  // 因为Integer是Number的子类


错误: Vector<?> extends Number > x = new Vector<String>();   //String不是Number的子类


限定通配符的下边界:

正确: Vector<?> super Integer > x = new Vector<Number>();   //因为Number是Integer的父类

错误: Vector<?> super Integer > x = new Vector<Byte>();     //因为Byte的Integer是同类



自定义泛型:



交换数组中的两个元素位置的泛型方法语法定义如下:



static <E> void swap(E[] a, int i, int j)
{
E t = a[i]; a[i] = a[j]; a[j] = t;
}


用于放置泛型的类型参数的尖括号就出现在方法的其它所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前,按照惯例,类型参数通常用单个大写字母表示 


只有引用类型才能作为泛型方法的实际参数,swap(new int[3],3,5), 语句会报告编译错误,这是因为编译器不会对new int[3]中的int自动拆箱和装箱了,

因为new int[3]本身已经是对象,你想要的有可能就是int数组呢,它装箱岂不是弄巧成拙了





类型参数的类型推断:



编译吕判断泛型方法实际类型参数的过程称为类型推断,类型推断是相对于知觉推断的,其实现方法是非学复杂的过程


根据调用泛型方法时传递参数类型或返回值的类型来推断,具体规则如下:


当某个类型变量只在某个参数列表中的所有参数和返回值中一处被应用了,那么根据调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值类型来决定泛型参数的类型,例如:

swap(new String[3],3,4) -> Static <E>void swap(E[] a, int i, int j);


当某个类形迹在整个参数数列表中的所有参数和返回中的多处被应用了,如果调用方法这多处实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来 


例如:  add(3,5) -> static <T> T add(T a, T b);  


当某个类形迹在整个参数数列表中的所有参数和返回中的多处被应用了,如果调用方法这多处实际应用类型都对应到了不同的类型,且没有返回值 ,这时候取多个参数中的最大交集类型例如: 下面的语句实际对应的类型就是Number了,编译没问题,只是运行时出问题


fill(new Integer[3],3,5) -> static <T> void fill(T[] a, T v);


当某个类形迹在整个参数数列表中的所有参数和返回中的多处被应用了,如果调用方法这多处实际应用类型都对应到了不同的类型,并且使用了返回值,这时候优先考虑返回值类型,

例如: 下面语句实际对应的类型就是Integer了,编译将报告错误,将变量x的类型改为float,对比Eclipse报告的错误提示,接着再将变量x类型改为Number,则正确

int x = (3,3.5f) -> static <T> T add(T a , T b);











枚举:


  代码示例:
  
  构建一个表示色彩的枚举,并赋值,在1.5版本中可以写为:
  
  public enum MyColor{ Red, Yellow, Blue }
  
  MyColor color = MyColor.Red;
  
  for ( MyColor mycolor : MyColor.values() )
  
  System.out.println( mycolor );

以往的Java版本中没有enum关键词,1.5版本中终于加入了进来,这确实是一个令人高兴的改进。此外,enum还提供了一个名为values()的静态方法,用以返回枚举的所有值的集合。所以,以上程序的输出结果是把“Red”、“Yellow”、“Blue”分行输出。

  

而enum提供的静态方法valueOf()则将以字符串的形式返回某一个具体枚举元素的值,比如“MyColor.valueOf(“Red”)”会返回“Color.Red”。静态方法name()则返回某一个具体枚举元素的名字,比如“MyColor.Red.name()”会返回“Red”。类似的方法还有不少。此外,enum自身还可以有构造方法。


可变参数:

  

代码示例:
  

当不能确定一个方法的入口参数的个数时,以往版本的Java中,通常的做法是将多个参数放在一个数组或者对象集合中作为参数来传递,1.5版本以前的写法是:

  int sum(Integer[] numbers)

  {

  int nSum = 0;

  for(int i: numbers)

  nSum += i;

  return nSum;

  }

  ...  //在别处调用该方法:   sum(new Integer[] {12,13,20});
 
  而在1.5版本中可以写为:

  int sum(Integer... numbers)

  {

  int nSum = 0;

  for(int i: numbers)

  nSum += i;

  return nSum;

  }  //在别处调用该方法:  sum(12,13,20);

  

  
  显然,1.5版本的写法更为简易,也更为直观,尤其是方法的调用语句,不仅简化很多,而且更符合通常的思维方式,更易于理解。
  
  1.5版本自身就有一个应用该特征的典型例子,即C风格的格式化输出方法——printf。
  
  代码示例
  
  输出一个加法算式,1.5版本以前的写法是:
  
  int x = 5;
  
  int y = 7;
  
  int nSum = x + y;
  
  System.out.println(x + " + " + y + " = " + nSum);
  
  而在1.5版本中可以写为:
  
  System.out.printf("%d + %d = %d\n", x, y, nSum);
  
  以上两种写法的输出结构是一样的,即“5 + 7 = 12”。
  

这种改变不仅仅是形式上的,printf还可以提供更为灵活、强大的输出功能,比如限定按照两位整数的形式输出,可以写为“System.out.printf("%02d + %02d = %02d\n", x, y, nSum);”,输出结果将是“05 + 07 = 12”。


自动包装和解包

  

代码示例:
  

往一个ArrayList中加入一个整数,1.5版本以前的版本写法是:

List list = new ArrayList();

list.add( new Integer( 10 ) )

而在1.5版本中可以写为:

list.add( 10 );

  

因为,在1.5版本中,对一个整数进行包装,使之成为一个Integer对象(即包装,boxing),然后加入到一个ArrayList中的做法被认为是没有必要的,反之,包(unboxing)的做法也是没有必要的,这样的代码只是增加了程序的文本长度而已,所以1.5版本支持了自动包装和解包操作,对于bool/Boolean,byte/Byte,double/Double,short/Short,int/Integer,long/Long,float/Float的相应包装/解包操作都进行了支持,从而使代码变得简单


更优化的循环语句

  

代码示例:
  
  一个典型的遍历数组的循环语句,1.5版本以前的写法是:
  
  for ( Iterator iterator = list.iterator(); iterator.hasNext(); )
  {

  Integer n = (Integer)iterator.next();

  ...
  
  }//for
  
  而在1.5版本中可以写为:
  
  for ( Integer n : list )
  {

  ...

  }//for
  

显然1.5版本的写法比以前是大大简化了,但是在需要修改集合,比如删除其中元素时不能采用这种写法。之所以Java1.5版本没有象C#那样干脆定义一个foreach关键词,主要是因为SUN认为增加一个专门的关键词成本太高了(too costly)。但1.4版本中就曾经增加了assert关键词,1.5版本中也新增加了enum关键词,因此这一解释恐怕并不那么令人信服


静态引用:


代码示例:
  
  当我们要获取一个随即数时,1.5版本以前的写法是:
  
  import java.lang.Math; //程序开头处
  ...
  double x = Math.random();
  而在1.5版本中可以写为:
  import static java.lang.Math.random; //程序开头处
  …
  double x = random();
  

静态引用使我们可以象调用本地方法一样调用一个引入的方法,当我们需要引入同一个类的多个方法时,只需写为“import static java.lang.Math.*”即可。这样的引用方式对于枚举也同样有效。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值