一、由内省(IntroSpector)引出JavaBean
1、内省:IntroSpector ,主要用于对javaBean进行操作。
2、什么是JavaBean?
一个Java类,只要含有get、set开头的方法,都可以看做是JavaBean。
JavaBean一般用于数据传递,所以其属性一般私有,但是可以通过get、set方法获取属性名。去掉get、set方法的get,set获得的单词,如果第二个字母小写,就将第一个字母变小;如果第二个字母大写,则得到的单词不动,结果就是属性名。JavaBean是一个特殊的Java类,也可以作为Java类来操作。
3、对JavaBean的简单内省操作。
如果两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象:Value Object(VO),里面方法很少,只是传递值。
正常情况下,我们获得和设置属性的方法,直接调用get和set方法,但是有些情况下,java类只提供给你所要操作属性的名字,这时就可以使用内省的方法操作JavaBean,获取和设置属性值。示例代码:
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MyIntroSpector {
public static void main(String[] args) throws Exception {
//对JavaBean的简单内省操作
Data d = new Data();//获取数据对象
String propName = "num";//给出要操作的属性名
PropertyDescriptor pd = new PropertyDescriptor(propName,d.getClass());//获得该属性在JavaBean中的描述
Method setNameMethod = pd.getWriteMethod();//获取JavaBean的set方法
setNameMethod.invoke(d, 8);//设置d对象中的该属性值为8,注意两个参数都是对象Object类型,这里整数8自动装箱
Method getNameMethod = pd.getReadMethod();//获取JavaBean的get方法
Object s = getNameMethod.invoke(d);//获得d对象中的该属性值,返回值是Object类型
System.out.println(s);//打印结果
}
}
Data类代码:
public class Data {
private int id;
private String name;
private int num;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
4、对JavaBean的复杂内省操作。
使用Introspector的getBeanInfo方法获得一个BeanInfo类对象,然后利用该类的getBeanInfo方法获取所有属性信息,然后比较获得要找的属性。
示例代码:
//对JavaBean的复杂内省操作
BeanInfo binInfo = Introspector.getBeanInfo(Data.class);
PropertyDescriptor[] pds = binInfo.getPropertyDescriptors();//获取所有属性信息
for(PropertyDescriptor pd1:pds){
if(pd1.getName().equals(propName)){//对比符合所要操作的属性就进行操作
Method setNumMethod1 = pd1.getWriteMethod();//设置
setNumMethod1.invoke(d, 13);
Method getNumMethod1 = pd1.getReadMethod();//获取
Object s1 = getNumMethod.invoke(d);
System.out.println(s1);
break;
}
}
5、使用BeanUtils工具包操作JavaBean。
Apache提供了专门针对JavaBean进行操作的工具类BeanUtils。要使用它需要先把工具类加载进来,需要导入两个包common-beanutils.jar和commons-logging-1.1.jar(从官网下载common-beanutils-current.zip和common-logging-1.1.zip解压得到这两个包)。
获取操作:System.out.println(BeanUtils.getProperty(d,"num"));
设置操作:BeanUtils.setProperty(d,"num","9");//注意这里给int型赋值用的是字符串形式,因为BeanUtils会自动转化格式。
BeanUtil操作属性时,int属性,在设置和获取时,都是使用字符串操作,BeanUtils会进行自动转换,这是BeanUtils的好处之一。
BeanUtils的特殊之处:支持属性的级联操作。例如Data类中定义一个Date类型成员:birthday,birthday是一个复合属性,不是一个基本属性,因为Date类里面还有set get方法,也可以看做是一个JavaBean。可以单独设置Date对象的单个成员,例如birthday.time。
代码举例:
BeanUtils.setProperty(d,"birthday.time","111");//这样就把Date对象birthday的成员变量time的值设置成了111.
同样也能这样获得级联属性:BeanUtils.getProperty(d,"birthday.time");
另外BeanUtils还可以将一个属性的值拷贝给另外一个属性:copyProperties(java.lang.Object dest,java.lang.object orig); BeanUtils还可以使JavaBean和map集合互转。BeanUtils还可以设置map集合中的值:BeanUtils.setProperty(map,"name","value");
6、BeanUtils是以字符串形式对JavaBean进行操作。
BeanUtils.setProperty(p,"x","9");
而PropertyUtils是以属性本身的类型进行操作。
PropertyUtils.setProperty(p,"x",9);
这是二者的区别,需要转换类型时就用BeanUtils,不需要转型或者BeanUtils转型不能准确时,就可以使用PropertyUtils。
BeanUtils.setProperty(p,"x","9");
而PropertyUtils是以属性本身的类型进行操作。
PropertyUtils.setProperty(p,"x",9);
这是二者的区别,需要转换类型时就用BeanUtils,不需要转型或者BeanUtils转型不能准确时,就可以使用PropertyUtils。
二、注解Annotation
1、注解:相当于一种标记,在程序中加了注解就等于为程序打上了某种标记。在javac编译器,开发工具和其他程序可以用来反射来了解你的类及各种元素上有无何种标记,根据有什么标记,做相应操作。
该标记可以加在包,类,字段,方法,方法的参数及局部变量上。
2、三个基本注解:
*@Deprecated 方法过时,该方法上会有删除线,eclipse会提出警告
*@Override 子类覆盖父类方法,出现覆盖错误会提示
*@SuppressWarning 压缩警告,编译不会提示警告信息
3、自定义应用注解。
注解相当于特殊的类,关键字@interface。注解应用结构图如下:
注解类A:
public @interface A {
}
应用了注解类A的B,及通过反射获取B类的注解A的实例对象,以便进一步操作:
@A
public class B {
public static void main(String[] args){
if(B.class.isAnnotationPresent(A.class)){
A a = B.class.getAnnotation(A.class);//只能用此法获得注解的实例对象,其他没有办法创建注解实例对象
}
}
}
4、元注解:注解的注解。
自定义的注解一般也需要再加上一些元注解,作为自定义注解的限定。如,@Retention(RetentionPolicy.RUNTIME)表示该注解的生命阶段。@Retention有三个取值:SOURCE,源文件阶段,编译器要丢弃的注释。CLASS,class文件阶段,编译器将把注释记录在class文件中,但运行时丢弃。RUNTIME,运行阶段,编译器将把注释记录在class文件中,在运行时也保留,因此可以反射性地读取。默认是CLASS阶段。
@Target({ElementType.METHOD,ElementType.TYPE})定义该注解加在哪个成分上,这里表示可以加载方法上和类、接口、枚举等上。
5、注解的属性。
属性很像方法,如String color();//前缀是public abstract,默认的不需要写
使用时设置属性的值:@ItcastAnnotation(color = "red");
特殊的属性:value。如果注解只有value属性,或者其他属性都有默认指定值时(String color() default "blue"),设置value的值时可以省略value=例如:@ItcastAnnotation("123")。
注解A代码:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.lang.model.element.Element;
@Retention(RetentionPolicy.RUNTIME)//表示注解A的生命阶段
@Target(ElementType.TYPE)//表示注解A应该修饰哪些成分,可以写多个@Target({ElementType.TYPE,ElementType.METHOD})
public @interface A {
String color();//默认前缀是public abstract
String name() default "lilei";//设置该属性的默认值是lilei,也可以在使用时修改其属性值
int[] arr();
}
使用注解A的类B代码:
@A(color = "red",arr = {1,2,3})//属性name有默认值,可以设置也可以不设置
public class B {
public static void main(String[] args){
if(B.class.isAnnotationPresent(A.class)){
A a = B.class.getAnnotation(A.class);//只能用此法获得注解的实例对象,其他没有办法创建注解实例对象
System.out.println(a.color());//打印注解A的color属性,将会得到“red”
}
}
}
6、注解属性的返回值类型都能有哪些?
只能是八个原始类型,另外还有String 、Class类型,枚举类型,注解类型,以及前面那些类型的数组。