jdk5 新特性

JDK5中新增了很多新的java特性,利用这些新语法可以帮助开发人员编写出更加高效、清晰,安全的代码。 

1.静态导入
2.自动装箱/拆箱
3.增强for循环
4.可变参数
5.枚举
6.反射
7.泛型

1.静态导入
JDK 1.5 增加的静态导入语法用于导入类的某个静态属性或方法。使用静态导入可以简化程序对类静态属性和方法的调用。
语法:
import static 包名.类名.静态属性|静态方法|*
例如:
import static java.lang.System.out
import static java.lang.Math.*

2.自动装箱/拆箱
JDK5.0的语法允许开发人员把一个基本数据类型直接赋给对应的包装类变量, 或者赋给 Object 类型的变量,这个过程称之为自动装箱。
自动拆箱与自动装箱与之相反,即把包装类对象直接赋给一个对应的基本类型变量。
典型应用:
    List list = new ArrayList();
    list.add(1);
    int j = (Integer)list.get(0);
 
3.增强for循环
引入增强for循环的原因:在JDK5以前的版本中,遍历数组或集合中的元素,需先获得数组的长度或集合的迭代器,比较麻烦!
因此JDK5中定义了一种新的语法——增强for循环,以简化此类操作。增强for循环只能用在数组、或实现Iterator接口的集合类上
语法格式:                                             
    for(变量类型 变量 :需迭代的数组或集合){}
小细节:向集合中取元素时同时加元素,需要 使用迭代器来完成。
 
使用增强for循环的几个注意事项(以下程序的输出结果?)
case1:
int arr[] = new int[5];
for(int num : arr){
    num = 1;
}
System.out.println(arr[0]);
 
case2:
List<String> list = new ArrayList<String>();
list.add("xxx");
for(String str : list){
    str = "yyy";
}
System.out.println(list.get(0));
结果是不变的,因为增强for循环使用的是他们的副本操作
 
关于遍历的总结:
1)迭代数组/List/Set/Map
2)迭代Map的二种方式:keySet()/entrySet()      //一个键值对编程一个整体类,存入set集合中,再遍历
3)对于集合在迭代过程中,更新元素,需要使用ListIterator<String> it = list.listIterator();
    即能过迭代器完成,让迭代器自动更新集合
 
 
4.可变参数
测试JDK中具有可变参数的类Arrays.asList()方法。分别传多个参、传数组,传数组又传参的情况。
注意:传入基本数据类型数组的问题。
从JDK 5开始, Java 允许为方法定义长度可变的参数。语法:
        public void foo(int … args){
        }
注意事项:
调用可变参数的方法时, 编译器将自动创建一个数组保存传递给方法的可变参数,因此,程序员可以在方法体中以数组的形式访问可变参数
可变参数只能处于参数列表的最后, 所以一个方法最多只能有一个可变参数
可变参数总结:
  1)语法结构:String... args
  2)可以使用传统方式调用,或直接使用单个值调用
  3)编译器会将可变传数看成一个一维数组
  4)可变参数只能放置在参数的最后
  5)可变参数只能在参数中最多1个
 
5.枚举类
为什么需要枚举?
一些方法在运行时,它需要的数据不能是任意的,而必须是一定范围内的值,此类问题在JDK5以前采用自定义带有枚举功能的类解决,Java5以后可以直接使用枚举予以解决。
JDK 5新增的 enum 关键字用于定义一个枚举类。
 
枚举类具有如下特性:
    1.枚举类也是一种特殊形式的Java类。
    2.枚举类中声明的每一个枚举值代表枚举类的一个实例对象。
    3.与java中的普通类一样,在声明枚举类时,也可以声明属性、方法和构造函数,但枚举类的构造函数必须为私有的(这点不难理解)。
    4.枚举类也可以实现接口、或继承抽象类。
    5.JDK5中扩展了switch语句,它除了可以接收int, byte, char, short外,还可以接收一个枚举类型。
    6.若枚举类只有一个枚举值,则可以当作单态设计模式使用。
练习:请编写一个关于星期几的枚举WeekDay,要求:
枚举值:Mon,Tue,Wed,Thu,Fri,Sat,Sun 
该枚举要有一个方法,调用该方法返回中文格式的星期。
 
Java中声明的枚举类,均是java.lang.Enum类的孩子,它继承了Enum类的所有方法。常用方法:
    name()
    ordinal()
    valueOf(Class enumClass, String name)
 
values() 此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便。
 
枚举总结
  1)可以在运行时对需要的值通过正则表达式验证
  2)可以在编译时限定参数的取值
  3)当一个枚举类中的抽象方法时,可以使用每个枚举值对该抽象方法通过匿名内部类实现
  4)switch()参数是也可以使用枚举类型参数变量
  5)只有一个枚举值的枚举类类似于单例设计模式[在单线程情况下]
  6)枚举类是扩展/继承java.lang.Enum类,常用的方法如下:
    name()
    ordinal()
    values()
    valueOf()
 
 
6.反射
什么是反射?
反射就是把Java类中的各种成分映射成一个个的java对象。例如,一个类有:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。
反射用在哪里?框架
学习反射应该掌握些什么?
 
6.1 Class类
Class类用于表示.class文件,画图演示一个对象的创建过程。
如何得到某个class文件对应的class对象,方法有:
   类名.class
   对象.getClass()
   Class.forName(“类名”) 
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int.class,int[].class,void.class…
 
6.2 Constructor类
Constructor类的实例对象代表类的一个构造方法。
得到某个类所有的构造方法,例:
    Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法,例:        
    Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
利用构造方法创建实例对象:
    String str = (String)constructor.newInstance(“abc”);
Class类的newInstance()方法也可创建类的实例,其内部工作原理是先得无参的构造方法,再用构造方法创建实例对象。
String obj =(String)Class.forName("java.lang.String").newInstance();
 
反射公共,私有和保护的构造方法
// public Person()
@Test
public void test1() throws Exception {
Class clazz = Class.forName("cn.itcast.java.reflect.Person");
Constructor c = clazz.getConstructor(null);
Person p = (Person) c.newInstance(null);
}
// protected Person()或private Person()
@Test
public void test2() throws Exception {
Class clazz = Class.forName("cn.itcast.java.reflect.Person");
Constructor c = clazz.getDeclaredConstructor(null);
c.setAccessible(true);
Person p = (Person) c.newInstance(null);
}
 
6.3 Field类
Field类代表某个类中的一个成员变量
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。(注意访问权限的问题)
 
6.4 Method类
Method类代表某个类中的一个成员方法
得到类中的某一个方法:
例子:     Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: System.out.println(charAt.invoke(str, 1)); 
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
 
用反射方式执行某个类中的main方法
问题:
启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的 语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定 要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组拆分成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码 mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问 题。
解决办法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{“xxx”}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会将数组拆分成若干个参数了
 
反射总结:
  1)在做通用程序或框架时用到
  2)反射是解析出一个类的方方面面(构造方法/字段/方法)
  3)反射的前提是需要取得该类对Class(字节码对象)
  4)//访问非public的成员Constructor c = clazz.getDeclaredConstructor(null);
  5)//强行访问非public的成员c.setAccessible(true);
  6)对于main方法的解析,存在JDK1.4和JDK1.5的差异,觖决方案如下:
    m.invoke(null,(Object)new String[]{"A","B","C","D"});
    //m.invoke(null,new Object[]{new String[]{"A","B","C","D"}});
 


7.1泛型(Generic)—泛形的作用
JDK5以前,对象保存到集合中就会失去其特性,取出时通常要程序员手工进行类型的强制转换,这样不可避免就会引发程序的一些安全性问题。例如:
ArrayList list = new ArrayList();
list.add("abc");
Integer num = (Integer) list.get(0); //运行时会出错,但编码时发现不了
 
list.add(new Random());
list.add(new ArrayList());
for(int i=0;i<list.size();i++){
    (?)list.get(i); //此处取出来的对象应转换成什么类型
}
 
    JDK5中的泛形允许程序员在编写集合代码时,就限制集合的处理类型,从而把原来程序运行时可能发生问题,转变为编译时的问题,以此提高程序的可读性和稳定性(尤其在大型程序中更为突出)。
注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。
泛形的基本术语,以ArrayList<E>为例:<>念着typeof
    ArrayList<E>中的E称为类型参数变量
    ArrayList<Integer>中的Integer称为实际类型参数
    整个称为ArrayList<E>泛型类型
    整个ArrayList<Integer>称为参数化的类型ParameterizedType 
 
典型应用
    1.使用迭代器迭代泛形集合中的元素。
    2.使用增强for循环迭代泛形集合中的元素。
    3.存取HashMap中的元素。
使用泛形时的几个常见问题:
    使用泛形时,泛形类型须为引用类型,不能是基本数据类型。Java中保存什么内容,由其变量决定。
    ArrayList<String> list = new ArrayList<Object>();//错,只能一边指定泛型或二边相同
    ArrayList<Object> list = new ArrayList<String>();//错,只能一边指定泛型或二边相同
    ArrayList<String> list = new ArrayList ();兼容1.4//对
    ArrayList list = new ArrayList<String>(); 兼容1.4//对
  
7.2 自定义泛形——泛型方法
Java程序中的普通方法、构造方法和静态方法中都可以使用泛型。方法使用泛形前,必须对泛形进行声明,语法:<T> ,T可以是任意字母,但通常必须要大写。<T>通常需放在方法的返回值声明之前。例如:
    public static <T> void doxx(T t);
练习:
编写一个泛形方法,实现二元素的交换。
注意:
只有对象类型才能作为泛型方法的实际参数。
在泛型中可以同时有多个类型,例如:
        public static <K,V> V getValue(K key) { return map.get(key);}
 
泛型方法的产生
1.public void show(Double money){
System.out.println("苹果单价:" + money);
}
2.public void show(Integer money){
System.out.println("苹果数量:" + money);
}
3.public <T> void show(T info){
System.out.println("苹果信息:" + info);
}
 
自定义泛形——泛型类
如果一个类多处都要用到同一个泛型,这时可以把泛形定义在类上(即类级别的泛型),语法格式如下:
    public class BaseDao<T> {
        private T field1;
        public void save(T obj){}
        public T getId(int id){}
    }
注意,静态方法不能使用类定义的泛形,而应单独定义泛形。
(public static void show(T t){} 是错误的,因为泛型类是在实例化时确定具体的类型,而静态方法无需类实例化,是属于类级别调用的。)
泛形的典型应用:BaseDao和反射泛型

7.3 泛型的高级应用——通配符
定义一个方法,接收一个集合,并打印出集合中的所有元素,如下所示:
  void print (Collection<String> c) {
           for (String e : c) {
                 System.out.println(e);
           }
问题:该方法只能打印保存了Object对象的集合,不能打印其它集合。通配符用于解决此类问题,方法的定义可改写为如下形式:
  void print (Collection<?> c) {   //Collection<?>(发音为:"collection of unknown") 
for (Object e : c) {
    System.out.println(e);
}
}
     此种形式下需要注意的是:由于print方法c参数的类型为Collection<?>,即表示一种不确定的类型,因此在方法体内不能调用与类型相关的方法,例如add()方法。
      总结:使用?通配符主要用于引用对象,使用了?通配符,就只能调对象与类型无关的方法,不能调用对象与类型有关的方法。
 
有限制的通配符
限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();
错误:Vector<? extends Number> x = new Vector<String>();
限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();
错误:Vector<? super Integer> x = new Vector<Byte>();
继承关系:
java.lang.Object
       java.lang.Number
                java.lang.Integer
问题:以下代码行不行?
public void add(List<? extends Number> list){
    list.add("abc");
}
(public final class String 不能被扩展,因此是错误的。)
 
泛型总结:
 1)泛型主要作用在编译时,在运行时,编译器会将其“擦除”
  2)Type 是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型
  3)如果特指参数化类型使用ParameterizedType
  4)泛型语法二边要么都不写,要么保持一致,要么一边有泛型一边无泛型
  5)当一个类中有N个类似的方法,可以使用泛型方法
  6)泛型方法定义的语法:public <T> void buy(T t)    
  7)当一个类使用了泛型类时,在创建时要给定具体的类型
  8)在泛型中使用?号通配符,其只能调用与具体元素无关的方法,例如:取集合长度,判段该集合是否为空.


你可能感兴趣的:(jdk)