黑马程序员---JDK5.0新特性(下)
2013-10-24 16:05
681 查看
------- android培训、java培训、期待与您交流!
----------
1 、反射
Class类:Class类用于表示.class文件,加载类有三种方法:Class.forName(类的完整名称); 对象.getClass; 对象.class.以上三种方式,JVM都会把相应class文件装载到一个class对象中(只装载一次)。
创建类的实例:Class.newInstance方法。
数组类型的Class实例对象
Class.isArray()
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int,void…反射就是把Java类中的各种成分映射成一个个的java对象。例如,一个类有:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部 分映射成一个个对象。掌握反射技术的要点在于:如何从一个class中反射出各个组成部分。反射出来的对象怎么用。
得到某个类所有的构造方法:
例子:Constructor []constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子: //获得方法时要用到类型
Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
创建实例对象:
通常方式:String str = newString(new StringBuffer("abc"));
反射方式: String str =(String)constructor.newInstance(new StringBuffer("abc"));
class.newInstance()方法:例子:String obj =(String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段field X 代表的是x的定义,而不是具体的x变量。(注意访问权限的问题)
示例代码:
ReflectPointpoint = new ReflectPoint(1,7);
Fieldy =Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));
//Fieldx = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");
Fieldx =Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
x.setAccessible(true);
System.out.println(x.get(point));
得到类中的某一个方法:
例子: 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})形式。
问题:
启动Java程序的main方法的参数是一个字符串数组,即publicstatic void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,newString[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。
解决办法:
mainMethod.invoke(null,new Object[]{newString[]{"xxx"}});
mainMethod.invoke(null,(Object)newString[]{"xxx"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
1.6、内省
·内省(Introspector) — JavaBean
Introspector 类为通过工具学习有关目标 Java Bean 支持的属性、事件和方法的知识提供了一个标准方法。
什么是JavaBean和属性的读写方法?
有get或set方法就是一个属性,另外所有类继承了Object类的getClass()方法,所以还有一个属性class。
访问JavaBean属性的两种方式:
直接调用bean的setXXX或getXXX方法。
通过内省技术访问(java.beans包提供了内省的API),内省技术访问也提供了两种方式。
通过PropertyDescriptor类操作Bean的属性
通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法。
例:
1.7、内省---beanutils工具包
Apache组织开发了一套用于操作JavaBean的API,这套API考虑到了很多实际开发中的应用场景,因此在实际开发中很多程序员使用这套API操作JavaBean,以简化程序代码的编写。
在工程下新建lib目录,导入commons-beanutils-1.8.3.jar 和支持包commons-logging-1.1.1.jar
选中两个包,右键build path/addto build path
Beanutils工具包的常用类:
BeanUtils
PropertyUtils
ConvertUtils.regsiter(Converter convert,Class clazz)
自定义转换器:
2、泛型
2.1、泛型的作用
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
2.2、泛型的典型应用
使用迭代器迭代泛形集合中的元素。
使用增强for循环迭代泛形集合中的元素。
存取HashMap中的元素。
使用泛形时的几个常见问题:
使用泛形时,泛形类型须为引用类型,不能是基本数据类型
//使用泛型时,如果两边都使用到泛型,两边必须一样
ArrayList<String> list = newArrayList<Object>(); //bad
ArrayList<Object> list = newArrayList<String>(); //bad
ArrayList<String> list = newArrayList ();//ok
ArrayList list = newArrayList<String>();//ok
代码示例:
2.3、自定义泛型---泛型方法
Java程序中的普通方法、构造方法和静态方法中都可以使用泛型。方法使用泛形前,必须对泛形进行声明,语法:<T>,T可以是任意字母,但通常必须要大写。<T>通常需放在方法的返回值声明之前。例如: public static <T> voiddoxx(T t);
在泛型中可以同时有多个类型,例如:
publicstatic <K,V> V getValue(K key) { return map.get(key);}
2.4、自定义泛型---泛型类
如果一个类多处都要用到同一个泛型,可以把泛形定义在类上(即类级别的泛型),语法格式如下:
publicclass GenericDao<T> {
privateT field1;
publicvoid save(T obj){}
publicT getId(int id){}
}
注意,静态方法不能使用类定义的泛形,而应单独定义泛形。
经典应用:hibernate基础Dao
2.5、泛型的高级应用---通配符
定义一个方法,接收一个集合,并打印出集合中的所有元素,如下所示:
void print (Collection<String> c) {
for(String e : c) {
System.out.println(e);
}
}
问题:该方法只能打印保存了Object对象的集合,不能打印其它集合。通配符用于解决此类问题,方法的定义可改写为如下形式:
void print (Collection<?> c) { //Collection<?>(发音为:"collectionof unknown")
for (Object e : c) {
System.out.println(e);
}
}
此种形式下需要注意的是:由于print方法c参数的类型为Collection<?>,即表示一种不确定的类型,因此在方法体内不能调用与类型相关的方法,例如add()方法。
总结:使用?通配符主要用于引用对象,使用了?通配符,就只能调对象与类型无关的方法,不能调用对象与类型有关的方法。
2.6、泛型的高级应用---有限制的通配符
限定通配符的上边界:
正确:Vector<?extendsNumber> x = new Vector<Integer>();
错误:Vector<?extendsNumber> x = new Vector<String>();
限定通配符的下边界:
正确:Vector<?superInteger> x = new Vector<Number>();
错误:Vector<?superInteger> x = new Vector<Byte>();
问题:以下代码行不行?
public void add(List<? extendsString> list){
list.add("abc");
}
行、因为?代表String以及其子类。
3、Annotation(注解)
3.1、概述
从 JDK 5.0 开始, Java 增加了对元数据(MetaData)的支持, 也就是 Annotation(注解)。
什么是Annotation,以及注解的作用?三个基本的 Annotation:
@Override: 限定重写父类方法, 该注解只能用于方法
@Deprecated: 用于表示某个程序元素(类, 方法等)已过时
@SuppressWarnings: 抑制编译器警告.
Annotation 其实就是代码里的特殊标记, 在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。
掌握注解技术的要点:
如何定义注解
如何反射注解
3.2、自定义注解
定义新的 Annotation 类型使用 @interface 关键字
声明注解的属性:
Annotation 的属性声明方式:String name();
Annotation 属性默认值声明方式:
String name() default “xxx”;
特殊属性value:如果注解中有一个名称为value的属性,那么使用注解时,可以省略value=部分,例如:@MyAnnotation(“xxx")。
3.3、JDK的元注解
元 Annotation指修饰Annotation的Annotation。JDK中定义了如下元Annotation:
@Retention: 只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留的域, @Retention包含一个 RetentionPolicy 类型的成员变量, 通过这个变量指定域。
RetentionPolicy.CLASS: 编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注释. 这是默认值
RetentionPolicy.RUNTIME:编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注释. 程序可以通过反射获取该注释
RetentionPolicy.SOURCE: 编译器直接丢弃这种策略的注释
@Target: 指定注解用于修饰类的哪个成员. @Target 包含了一个名为 value 的成员变量.
@Documented: 用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档.
@Inherited: 被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的 Annotation, 则其子类将自动具有该注解
3.4、反射注解信息
JDK 5.0 在 java.lang.reflect 包下新增了AnnotationElement 接口, 该接口代表程序中可以接受注释的程序元素
当一个 Annotation 类型被定义为运行时 Annotation 后, 该注释才是运行时可见, 当 class 文件被载入时保存在 class 文件中的 Annotation 才会被虚拟机读取
程序可以调用 AnnotationElement对象的如下方法来访问 Annotation 信息
4、动态代理
在动态代理技术里,由于不管用户调用代理对象的什么方法,都是调用开发人员编写的处理器的invoke方法(这相当于invoke方法拦截到了代理对象的方法调用)。并且,开发人员通过invoke方法的参数,还可以在拦截的同时,知道用户调用的是什么方法,因此利用这两个特性,就可以实现一些特殊需求,例如:拦截用户的访问请求,以检查用户是否有访问权限、动态为某个对象添加额外的功能。
5、类加载器
5.1、类加载器
类加载器负责将 .class 文件(可能在磁盘上, 也可能在网络上) 加载到内存中, 并为之生成对应的 java.lang.Class 对象,当 JVM 启动时,会形成由三个类加载器组成的初始类
类加载器的结构为:
5.2、bootstarp classloader
bootstrap classloader:引导(也称为原始)类加载器,它负责加载Java的核心类。这个加载器的是非常特殊的,它实际上不是java.lang.ClassLoader的子类,而是由JVM自身实现的。可以通过执行以下代码来获得bootstrap classloader加载了那些核心类库:
URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i].toExternalForm());
}
因为JVM在启动的时候就自动加载它们,所以不需要在系统属性CLASSPATH中指定这些类库
5.3、extension classloader
extension classloader -扩展类加载器,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中的JAR包。这为引入除Java核心类以外的新功能提供了一个标准机制。因为默认的扩展目录对所有从同一个JRE中启动的JVM都是通用的,所以放入这个目录的 JAR类包对所有的JVM和system classloader都是可见的。
5.4、system classloader
system classloader -系统(也称为应用)类加载器,它负责在JVM被启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或者 CLASSPATH操作系统属性所指定的JAR类包和类路径。
可以通过静态方法ClassLoader.getSystemClassLoader()找到该类加载器。如果没有特别指定,则用户自定义的任何类加载器都将该类加载器作为它的父加载器。
5.5、全盘负责委托机制(父类委托机制)
classloader 加载类用的是全盘负责委托机制。
全盘负责:即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的其它Class通常也由这个classloader负责载入。
委托机制:先让parent(父)类加载器寻找,只有在parent找不到的时候才从自己的类路径中去寻找。
类加载还采用了cache机制:如果 cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么修改了Class但是必须重新启动JVM才能生效,并且类只加载一次的原因。
------- android培训、java培训、期待与您交流!
----------
----------
1 、反射
1.1、什么是反射、以及Class类
定义:反射就是加载类,并解剖出这个类的各个组成部分,例如,一个类有:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象Class类:Class类用于表示.class文件,加载类有三种方法:Class.forName(类的完整名称); 对象.getClass; 对象.class.以上三种方式,JVM都会把相应class文件装载到一个class对象中(只装载一次)。
创建类的实例:Class.newInstance方法。
数组类型的Class实例对象
Class.isArray()
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int,void…反射就是把Java类中的各种成分映射成一个个的java对象。例如,一个类有:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部 分映射成一个个对象。掌握反射技术的要点在于:如何从一个class中反射出各个组成部分。反射出来的对象怎么用。
1.2、Constructor类
Constructor类代表某个类中的一个构造方法得到某个类所有的构造方法:
例子:Constructor []constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子: //获得方法时要用到类型
Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
创建实例对象:
通常方式:String str = newString(new StringBuffer("abc"));
反射方式: String str =(String)constructor.newInstance(new StringBuffer("abc"));
class.newInstance()方法:例子:String obj =(String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
1.3、Filed类
Field类代表某个类中的一个成员变量问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段field X 代表的是x的定义,而不是具体的x变量。(注意访问权限的问题)
示例代码:
ReflectPointpoint = new ReflectPoint(1,7);
Fieldy =Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));
//Fieldx = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");
Fieldx =Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
x.setAccessible(true);
System.out.println(x.get(point));
1.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})形式。
1.5、反射并执行一个类中的main方法
目标:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。用普通方式调完后,大家要明白为什么要用反射方式去调啊?问题:
启动Java程序的main方法的参数是一个字符串数组,即publicstatic void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,newString[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。
解决办法:
mainMethod.invoke(null,new Object[]{newString[]{"xxx"}});
mainMethod.invoke(null,(Object)newString[]{"xxx"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
1.6、内省
·内省(Introspector) — JavaBeanIntrospector 类为通过工具学习有关目标 Java Bean 支持的属性、事件和方法的知识提供了一个标准方法。
什么是JavaBean和属性的读写方法?
有get或set方法就是一个属性,另外所有类继承了Object类的getClass()方法,所以还有一个属性class。
访问JavaBean属性的两种方式:
直接调用bean的setXXX或getXXX方法。
通过内省技术访问(java.beans包提供了内省的API),内省技术访问也提供了两种方式。
通过PropertyDescriptor类操作Bean的属性
通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取属性的描述器(PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法。
例:
package cn.itcast.introspector; //该类共有5个属性 public class Person { private String name; private String password; private int age; public void setAb(int a){ } public String getName() { return name; } public String getPassword() { return password; } public int getAge() { return age; } public void setName(String name) { this.name = name; } public void setPassword(String password) { this.password = password; } public void setAge(int age) { this.age = age; } }
package cn.itcast.introspector; import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import o f70a rg.junit.Test; public class Demo1 { //得到bean所有属性 public void test1() throws IntrospectionException{ BeanInfo info=Introspector.getBeanInfo(Person.class); //去掉Object里的属性 BeanInfo info2=Introspector.getBeanInfo(Person.class,Object.class); PropertyDescriptor[] pds=info.getPropertyDescriptors(); for(PropertyDescriptor pd:pds){ System.out.println(pd.getName()); //ab age class name password } } //操纵bean的指定属性:age @Test public void test2() throws Exception{ Person p=new Person(); PropertyDescriptor pd=new PropertyDescriptor("age", Person.class); //得到属性的写方法,为属性赋值 Method method=pd.getWriteMethod(); method.invoke(p, 45); System.out.println(p.getAge());//45 //获取属性的值 method=pd.getReadMethod(); System.out.println(method.invoke(p, null));//45 } //高级内容,获取当前操作的属性的类型 @Test public void test3() throws Exception{ Person p=new Person(); PropertyDescriptor pd=new PropertyDescriptor("age", Person.class); //得到属性的写方法,为属性赋值 Method method=pd.getWriteMethod(); System.out.println(pd.getPropertyType());//int method.invoke(p, 45); System.out.println(p.getAge());//45 //获取属性的值 method=pd.getReadMethod(); System.out.println(method.invoke(p, null));//45 } }
1.7、内省---beanutils工具包
Apache组织开发了一套用于操作JavaBean的API,这套API考虑到了很多实际开发中的应用场景,因此在实际开发中很多程序员使用这套API操作JavaBean,以简化程序代码的编写。在工程下新建lib目录,导入commons-beanutils-1.8.3.jar 和支持包commons-logging-1.1.1.jar
选中两个包,右键build path/addto build path
Beanutils工具包的常用类:
BeanUtils
PropertyUtils
ConvertUtils.regsiter(Converter convert,Class clazz)
自定义转换器:
package cn.itcast.beanutils; import java.util.Date; public class Person { private String name; private String password; private int age; private Date birthday; public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getName() { return name; } public String getPassword() { return password; } public int getAge() { return age; } public void setName(String name) { this.name = name; } public void setPassword(String password) { this.password = password; } public void setAge(int age) { this.age = age; } }
package cn.itcast.beanutils; import java.lang.reflect.InvocationTargetException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.ConversionException; import org.apache.commons.beanutils.ConvertUtils; import org.apache.commons.beanutils.Converter; import org.apache.commons.beanutils.locale.converters.DateLocaleConverter; import org.junit.Test; //使用beanUtils操纵bean的属性 ( 第三方) public class Demo1 { @Test public void test1() throws Exception{ Person p=new Person(); BeanUtils.setProperty(p, "age", 456); System.out.println(p.getAge());//456 } @Test public void test2() throws Exception{ String name="aaaa"; String age="123"; String password="pw"; Person p=new Person(); //支持8种基本类型自动转换 BeanUtils.setProperty(p, "name", name); BeanUtils.setProperty(p, "age", age); BeanUtils.setProperty(p, "password", password); System.out.println(p.getName());//aaaa System.out.println(p.getAge());//123 System.out.println(p.getPassword());//pw } @Test public void test3() throws Exception{ String birthday="1983-12-1"; //为了让日期赋值到bean的birthday属性上,给beanUtils注册一个日期转换器 //ConvertUtils.register(converter, clazz); ConvertUtils.register(new Converter(){ public Object convert(Class type, Object value) { if(value==null) return null; if(!(value instanceof String)){ throw new ConversionException("只支持String类型的转换"); } String str=(String)value; if(str.trim().equals("")) return null; SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd",Locale.US); try { return df.parse(str); } catch (ParseException e) { throw new RuntimeException(e); } } }, Date.class); Person p=new Person(); BeanUtils.setProperty(p, "birthday", birthday); System.out.println(p.getBirthday());//pw System.out.println("___"+BeanUtils.getProperty(p, "birthday")); } public void test5() throws Exception { Map map=new HashMap(); map.put("name", "aaa"); map.put("password", "123"); map.put("brithday", "1980-09-09"); ConvertUtils.register(new DateLocaleConverter(), Date.class); Person p=new Person(); //用map集合填充bean属性,map关键字和bean属性要一致 BeanUtils.populate(p, map); } }
2、泛型
2.1、泛型的作用
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
2.2、泛型的典型应用
使用迭代器迭代泛形集合中的元素。使用增强for循环迭代泛形集合中的元素。
存取HashMap中的元素。
使用泛形时的几个常见问题:
使用泛形时,泛形类型须为引用类型,不能是基本数据类型
//使用泛型时,如果两边都使用到泛型,两边必须一样
ArrayList<String> list = newArrayList<Object>(); //bad
ArrayList<Object> list = newArrayList<String>(); //bad
ArrayList<String> list = newArrayList ();//ok
ArrayList list = newArrayList<String>();//ok
代码示例:
package cn.itcast.generic; import java.util.ArrayList; import java.util.List; import org.junit.Test; public class Demo1 { @Test public void test1(){ List list=new ArrayList(); list.add("111"); list.add("222"); list.add("333"); //传统方式手工转换 String i=(String) list.get(0); //下面注释代码手工转换编辑不报错,运行错误 //Integer ii=(Integer) list.get(0); System.out.println(i); } @Test public void test2(){ List <String>list=new ArrayList<String>(); list.add("111"); list.add("222"); list.add("333"); //现在不需要强制转换 String i=list.get(0); //下面注释代码编译通不过 //Integer ii=(Integer) list.get(0); System.out.println(i); }
package cn.itcast.generic; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.Test; public class Demo2 { @Test public void test1(){ List <String>list=new ArrayList<String>(); list.add("111"); list.add("222"); list.add("333"); //传统 Iterator<String> it=list.iterator(); while(it.hasNext()){ String value=it.next(); System.out.println(value); } //增强for for(String s:list) System.out.println(s); } @Test public void test2(){ Map<Integer,String> map=new HashMap<Integer,String>(); map.put(1, "aaa"); map.put(2, "bbb"); map.put(3, "ccc"); //传统 keyset entryset Set <Map.Entry<Integer, String>> set=map.entrySet(); Iterator <Map.Entry<Integer, String>>it=set.iterator(); while(it.hasNext()){ Map.Entry<Integer, String>entry=it.next(); int key=entry.getKey(); String value=entry.getValue(); System.out.println(key+"="+value); } //增强for(重点) for(Map.Entry<Integer, String> entry:map.entrySet()){ int key=entry.getKey(); String value=entry.getValue(); } } }
2.3、自定义泛型---泛型方法
Java程序中的普通方法、构造方法和静态方法中都可以使用泛型。方法使用泛形前,必须对泛形进行声明,语法:<T>,T可以是任意字母,但通常必须要大写。<T>通常需放在方法的返回值声明之前。例如: public static <T> voiddoxx(T t);在泛型中可以同时有多个类型,例如:
publicstatic <K,V> V getValue(K key) { return map.get(key);}
2.4、自定义泛型---泛型类
如果一个类多处都要用到同一个泛型,可以把泛形定义在类上(即类级别的泛型),语法格式如下:
publicclass GenericDao<T> {
privateT field1;
publicvoid save(T obj){}
publicT getId(int id){}
}
注意,静态方法不能使用类定义的泛形,而应单独定义泛形。
经典应用:hibernate基础Dao
2.5、泛型的高级应用---通配符
定义一个方法,接收一个集合,并打印出集合中的所有元素,如下所示:void print (Collection<String> c) {
for(String e : c) {
System.out.println(e);
}
}
问题:该方法只能打印保存了Object对象的集合,不能打印其它集合。通配符用于解决此类问题,方法的定义可改写为如下形式:
void print (Collection<?> c) { //Collection<?>(发音为:"collectionof unknown")
for (Object e : c) {
System.out.println(e);
}
}
此种形式下需要注意的是:由于print方法c参数的类型为Collection<?>,即表示一种不确定的类型,因此在方法体内不能调用与类型相关的方法,例如add()方法。
总结:使用?通配符主要用于引用对象,使用了?通配符,就只能调对象与类型无关的方法,不能调用对象与类型有关的方法。
2.6、泛型的高级应用---有限制的通配符
限定通配符的上边界:正确:Vector<?extendsNumber> x = new Vector<Integer>();
错误:Vector<?extendsNumber> x = new Vector<String>();
限定通配符的下边界:
正确:Vector<?superInteger> x = new Vector<Number>();
错误:Vector<?superInteger> x = new Vector<Byte>();
问题:以下代码行不行?
public void add(List<? extendsString> list){
list.add("abc");
}
行、因为?代表String以及其子类。
3、Annotation(注解)
3.1、概述
从 JDK 5.0 开始, Java 增加了对元数据(MetaData)的支持, 也就是 Annotation(注解)。什么是Annotation,以及注解的作用?三个基本的 Annotation:
@Override: 限定重写父类方法, 该注解只能用于方法
@Deprecated: 用于表示某个程序元素(类, 方法等)已过时
@SuppressWarnings: 抑制编译器警告.
Annotation 其实就是代码里的特殊标记, 在Java技术里注解的典型应用是:可以通过反射技术去得到类里面的注解,以决定怎么去运行类。
掌握注解技术的要点:
如何定义注解
如何反射注解
3.2、自定义注解
定义新的 Annotation 类型使用 @interface 关键字声明注解的属性:
Annotation 的属性声明方式:String name();
Annotation 属性默认值声明方式:
String name() default “xxx”;
特殊属性value:如果注解中有一个名称为value的属性,那么使用注解时,可以省略value=部分,例如:@MyAnnotation(“xxx")。
3.3、JDK的元注解
元 Annotation指修饰Annotation的Annotation。JDK中定义了如下元Annotation:@Retention: 只能用于修饰一个 Annotation 定义, 用于指定该 Annotation 可以保留的域, @Retention包含一个 RetentionPolicy 类型的成员变量, 通过这个变量指定域。
RetentionPolicy.CLASS: 编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 不会保留注释. 这是默认值
RetentionPolicy.RUNTIME:编译器将把注释记录在 class 文件中. 当运行 Java 程序时, JVM 会保留注释. 程序可以通过反射获取该注释
RetentionPolicy.SOURCE: 编译器直接丢弃这种策略的注释
@Target: 指定注解用于修饰类的哪个成员. @Target 包含了一个名为 value 的成员变量.
@Documented: 用于指定被该元 Annotation 修饰的 Annotation 类将被 javadoc 工具提取成文档.
@Inherited: 被它修饰的 Annotation 将具有继承性.如果某个类使用了被 @Inherited 修饰的 Annotation, 则其子类将自动具有该注解
3.4、反射注解信息
JDK 5.0 在 java.lang.reflect 包下新增了AnnotationElement 接口, 该接口代表程序中可以接受注释的程序元素当一个 Annotation 类型被定义为运行时 Annotation 后, 该注释才是运行时可见, 当 class 文件被载入时保存在 class 文件中的 Annotation 才会被虚拟机读取
程序可以调用 AnnotationElement对象的如下方法来访问 Annotation 信息
4、动态代理
在动态代理技术里,由于不管用户调用代理对象的什么方法,都是调用开发人员编写的处理器的invoke方法(这相当于invoke方法拦截到了代理对象的方法调用)。并且,开发人员通过invoke方法的参数,还可以在拦截的同时,知道用户调用的是什么方法,因此利用这两个特性,就可以实现一些特殊需求,例如:拦截用户的访问请求,以检查用户是否有访问权限、动态为某个对象添加额外的功能。5、类加载器
5.1、类加载器
类加载器负责将 .class 文件(可能在磁盘上, 也可能在网络上) 加载到内存中, 并为之生成对应的 java.lang.Class 对象,当 JVM 启动时,会形成由三个类加载器组成的初始类类加载器的结构为:
5.2、bootstarp classloader
bootstrap classloader:引导(也称为原始)类加载器,它负责加载Java的核心类。这个加载器的是非常特殊的,它实际上不是java.lang.ClassLoader的子类,而是由JVM自身实现的。可以通过执行以下代码来获得bootstrap classloader加载了那些核心类库:URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i].toExternalForm());
}
因为JVM在启动的时候就自动加载它们,所以不需要在系统属性CLASSPATH中指定这些类库
5.3、extension classloader
extension classloader -扩展类加载器,它负责加载JRE的扩展目录(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系统属性指定的)中的JAR包。这为引入除Java核心类以外的新功能提供了一个标准机制。因为默认的扩展目录对所有从同一个JRE中启动的JVM都是通用的,所以放入这个目录的 JAR类包对所有的JVM和system classloader都是可见的。5.4、system classloader
system classloader -系统(也称为应用)类加载器,它负责在JVM被启动时,加载来自在命令java中的-classpath或者java.class.path系统属性或者 CLASSPATH操作系统属性所指定的JAR类包和类路径。可以通过静态方法ClassLoader.getSystemClassLoader()找到该类加载器。如果没有特别指定,则用户自定义的任何类加载器都将该类加载器作为它的父加载器。
5.5、全盘负责委托机制(父类委托机制)
classloader 加载类用的是全盘负责委托机制。全盘负责:即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的其它Class通常也由这个classloader负责载入。
委托机制:先让parent(父)类加载器寻找,只有在parent找不到的时候才从自己的类路径中去寻找。
类加载还采用了cache机制:如果 cache中保存了这个Class就直接返回它,如果没有才从文件中读取和转换成Class,并存入cache,这就是为什么修改了Class但是必须重新启动JVM才能生效,并且类只加载一次的原因。
------- android培训、java培训、期待与您交流!
----------
相关文章推荐
- (黑马程序员)JDK5.0新特性,高级for的使用
- 黑马程序员----------java基础知识之JDK5.0新特性总结
- 黑马程序员---JDK5.0新特性(上)
- 黑马程序员_学习笔记:11) Jdk5.0新特性:增强for循环、可变参数(...)、静态导入、枚举:关键字 enum、自动拆装箱、泛型
- 黑马程序员_java jdk5.0新特性和枚举的学习笔记
- 黑马程序员——JDK5.0新特性(泛型)
- 黑马程序员——JDK5.0新特性
- 黑马程序员——JDK5.0新特性
- 黑马程序员_java jdk5.0新特性和枚举的学习笔记
- 黑马程序员——JAVA学习笔记——Jdk5.0新特性
- 黑马程序员--JDK5.0 新特性
- 黑马程序员——JDK5.0新特性
- 黑马程序员--基础--第一篇--JDK5.0新特性泛型 静态倒入 自动拆装箱 不定项参数 增强for如循环 枚举
- 黑马程序员--Jdk5.0新特性
- 黑马程序员--12--collections类与JDK5.0新特性
- 黑马程序员:jdk1.5新特性4(注解泛型)
- 20-常用对象API(集合框架-JDK5.0特性-ForEach循环) 21-常用对象API(集合框架-JDK5.0特性-函数可变参数). 1 2
- 黑马程序员--JDK新特性
- 黑马程序员 JDK新特性
- 黑马程序员 Java面向对象——JDK 1.5新特性