3.11
反射的作用-实现框架功能
框架与框架要解决的核心问题:框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
框架要解决的核心问题:因为在写程序是无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象了,而要用反射方式来做。
例:
配置文件config.properties
className=java.util.HashSet
实现类
public static void main(String[] args) throws Exception{
InputStream ips=new FileInputStream("config.properties");
Properties pro=new Properties();
pro.load(ips);
ips.close();
String className=pro.getProperty("className");
Collection collections=(Collection)Class.forName(className).newInstance();
}
在配置文件中只要修改className的值,实现类就会产生不同的结果。
实现了一个小框架的作用。
找配置文件我学习了几种方法可以用InputStream ips=new FileInputStream("config.properties");注意,在目录的最前面不能加“/”,否则会出错。
如果配置文件放在包里面,和类放在一起,可以用
InputStream ips=类名.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
相对路径:InputStream ips=类名.class.getResourceAsStream("config.properties");
绝对路径:InputStream ips=类名.class.getResourceAsStream("/cn/itcast/day1/config.properties");
但是最好将配置文件放在classpass目录下。配置文件往往都是用类加载器加载
内省(IntroSpector)——主要对javaBean进行操作
javabean是一种特殊的java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个javabean中,这种javabean的实例对象通常称为值对象(Value Object,简称VO)。这些信息在类中用私有字段
来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问,javaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。如果方法
名为setId或getId,去掉set或get前缀,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改写成小的。
例:
class Person{
private int x;
public int getAge(){
return x;
}
public void setAge(int age){
this.x=age;
}
}
此类中的属性名为age
例:gettime的属性名为time
setTime的属性名为time
getCPU的属性名为CPU,如果第二个字母是大写,则第一个字母大小写不变。
总之,一个类被当作javabean来使用时,javabean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。
一个符合javabean特点的类可以当作普通类一样进行使用,但把它当javabean用带来的好处有:
1、在javaEE开发中,经常要使用到javabean。很多环境就要求按javabean方式进行操作。
2、JDK中提供了对javabean进行操作的一些API,这套API就称为内省。
示例代码:
public class javaBeanPoint {
private int x;
private int y;
public int getX(){
return x;
}
public void setX(int x){
this.x=x;
}
public int getY(){
return y;
}
public void setY(int y){
this.y=y;
}
public javaBeanPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class IntroSpectorTest {
/**
* @param args
*/
public static void main(String[] args) throws Exception{
javaBeanPoint jbp=new javaBeanPoint(3,5);
String propertyName="x";
Object retVal = getProperty(jbp, propertyName);
System.out.println(retVal);
Object value=10;
setProperty(jbp, propertyName, value);
System.out.println(jbp.getX());
}
private static void setProperty(javaBeanPoint jbp, String propertyName,
Object value) throws IntrospectionException,
IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd1=new PropertyDescriptor(propertyName, jbp.getClass());
Method methodSetX=pd1.getWriteMethod();
methodSetX.invoke(jbp,value);
}
private static Object getProperty(Object jbp, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
/*PropertyDescriptor pd=new PropertyDescriptor(propertyName, jbp.getClass());
Method methodGetX=pd.getReadMethod();
Object retVal=methodGetX.invoke(jbp);*/
BeanInfo beanInfo=Introspector.getBeanInfo(jbp.getClass());
PropertyDescriptor[] pds=beanInfo.getPropertyDescriptors();
Object retVal=null;
for(PropertyDescriptor pd:pds){
if(pd.getName().equals(propertyName)){
Method methodGetX=pd.getReadMethod();
retVal=methodGetX.invoke(jbp);
break;
}
}
return retVal;
}
}
演示了用eclipse自动生成javaBeanPoint类的setter和getter方法并演示了如何读取和设置属性值。再new一个PropertyDescription对象,用一段代码读取javabean的属性,然后
再用一段代码设置javabean的属性
只要调用这个方法,并给这个方法传递了一个对象,属性名和设置值,就可以完成属性修改的功能。
得到BeanInfo最好采用“obj.getClass()”方式,而不要采用“类名.class”方式,这样程序更加通用。
采用遍历BeanInfo的所有属性方式来查找和设置某个javaBeanPoint对象的x属性。在程序中把一个类当作javabean来看,就是调用IntroSpector.getBeanInfo方法,得到的
BeanInfo对象封装了把这个类当作javabean看的结果信息。
Beanutils工具包
在eclipse中加入jar包,先引入beanutils包,等程序运行出错后再引入logging包,
用BeanUtils类先get原来设置好的属性,再将其set为一个新值,get属性时返回的结果为字符串,set属性时可以接受任意类型的对象,通常使用字符串。
用PropertyUtils类先get原来设置好的属性,再将其set为一个新值,get属性时返回的结果为该属性本来的类型,set属性时只接受该属性本来的类型。
例:
public static void main(String[] args) throws Exception{
ReflectPoint rp=new ReflectPoint(3,5);
System.out.println(BeanUtils.getProperty(rp,"x"));
System.out.println(BeanUtils.getProperty(rp,"x").getClass().getName);
BeanUtils.setProperty(rp,"x","22");
System.out.println(rp.getX());
/*
Map map={name:"zxx",age:22};
BeanUtils.setProperty(map,"name","wangli");
*/
BeanUtils.setProperty(rp,"birthday.time","1989");
System.out.println(BeanUtils.getProperty(rp,"birthday.time"));
PropertyUtils.setProperty(rp,"x",12);
System.out.println(PropertyUtils.getProperty(rp,"x"));
System.out.println(PropertyUtils.getProperty(rp,"x").getClass().getName);
}
import java.util.Date;
public class ReflectPoint{
private Date birthday=new Date();
private int x;
public int y;
public ReflectPoint(int x,int y){
super();
this.x=x;
this.y=y;
}
public Date getBirthday(){
return birthday;
}
public void setBirthday(Date birthday){
this.birthday=birthday;
}
public int getX(){
return x;
}
public void setX(int x){
this.x=x;
}
public int getY(){
return y;
}
public void setY(int y){
this.y=y;
}
}
打印结果为:
5
java.lang.String
22
1989
12
java.lang.Integer
BeanUtils和PropertyUtils的区别:
BeanUtils是以字符串的形式对java进行操作
PropertyUtils是以属性本身的类型对java进行操作
注解
注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等于没有某种标记,在javac编译器,开发工具和其他程序可以用反射来观察你的类及各种元素上
有无何种标记,有什么标记,就会执行相应标记的动作。标记可以加在包,类,字段,方法,方法的参数以及局部变量上。
java提供的几个基本注解
@SuppressWarnings("deprecation")
用在已知道此方法过时,让编译器或开发工具不给予提示
@Deprecated
如果在方法上加此注解,在其他地方调用的时候就会提示此方法已过时
@Override
是否覆盖了父类的方法
自定义一个简单的注解:public @interface itcastAnnotation{}
把此注解加在某个类上:
@itcastAnnotation
public class AnnotationTest{}
用反射进行测试AnnotationTest的定义上是否有@itcastAnnotation
@Retention元注解的三种取值:
RetetionPolicy.SOURCE java源文件、
RetetionPolicy.ClASS class文件、
RetetionPolicy.RUNTIME 内存中的字节码
@Override注解的属性值为SOURCE
@SuppressWarnings注解的属性值为SOURCE
@Deprecated注解的属性值为RUNTIME
@Target元注解:Target的默认值为任何元素,设置Target等于ElementType.METHOD,就必须将注解加在method上,设置Target等于ElementType.TYPE,可以将注解加在类,接口,
枚举等等上
注解的属性
一个注解相当于一个胸牌,如果有胸牌,说明就是某个学校的学生,但还不知道是哪个班的学生,如果在胸牌上增加一个班级的属性就可以明显的知道你是哪个班的同学。
定义基本类型的属性和应用属性
在注解类中增加String color();
@itcastAnnotation(color="red")
用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法
itcastAnnotation ita=(itcastAnnotation)AnnotationTest.class.getAnnotation(itcastAnnotation.class);
System.out.println(ita.color());
可以认为上面的@itcastAnnotation是itcastAnnotation类的一个实例对象
为属性指定缺省值:String color() default "blue";
value属性:
String value() default "wangli";
如果注解中有一个名称为value的属性,且只想设置value属性(即其他属性都采用默认值或者只有一个value属性),可以省略“value=”部分,如:@itcastAnnotation
("wangli")。
数组类型的属性
int[] array() default{1,2,3};
@itcastAnnotation(array={4,5,6});
如果数组属性中只有一个元素,这时候属性值部分可以省略大括号
枚举类型的属性
enumTest.TrafficLamp lamp();
@itcastAnnotation(lamp=enumTest.TrafficLamp.RED)
注解类型的属性
MetaAnnotation metaAtt() default @MetaAnnotation("wangli");
@itcastAnnotation(metaAtt=@MetaAnnotation("abc"))
可以认为@itcastAnnotation是itcastAnnotation累的一个实例对象,@MetaAnnotation是MetaAnnotation类的一个实例对象。
具体实例:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface itcastAnnotation {
String color() default "blue";
String value();
int[] array() default {1,2,3};
eunmTest.TrafficLamp lamp() default eunmTest.TrafficLamp.RED;
MetaAnnotation matt() default @MetaAnnotation("123");
}
public @interface MetaAnnotation {
String value();
}
/*也可以写为@itcastAnnotation("abc"),因为color为缺省属性,可以将value=去掉*/
@itcastAnnotation(matt=@MetaAnnotation("111"),color="red",value="abc",array={4,5,6})
public class AnnotationTest {
/**
* @param args
*/
@SuppressWarnings("deprecated")
@itcastAnnotation("123")
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
System.runFinalizersOnExit(true);
if(AnnotationTest.class.isAnnotationPresent(itcastAnnotation.class)){
itcastAnnotation ita=(itcastAnnotation)AnnotationTest.class.getAnnotation(itcastAnnotation.class);
System.out.println(ita.color());
System.out.println(ita.value());
System.out.println(ita.array().length);
System.out.println(ita.lamp().nextLamp().name());
System.out.println(ita.matt().value());
}
}
}