您的位置:首页 > 编程语言 > Java开发

Java基础--反射

2017-10-09 17:01 417 查看
一、反射:一个Java类中,包含成员变量、成员方法等信息。反射就是把类中的这些信息映射成一个个的类。

反射的基本使用:

获取类的字节码对象有三种方法:

类名.class
对象名.getClass()
Class.forName(类的全名)

 Class对象提供了如下常用方法:
   public Constructor getConstructor(Class<?>...parameterTypes)
   public Method getMethod(String name, Class<?>...parameterTypes)
   public Field getField(String name)
   public Constructor getDeclaredConstructor(Class<?>...parameterTypes)
   public Method getDeclaredMethod(String name, Class<?>...parameterTypes)
   public Field getDeclaredField(String name)

举例:
Student类

public class Student {
public String str = "hello";
private int age = 18;
public static Date time;

public Student() {
}

public Student(String name){
System.out.println("姓名:" + name);
}

public Student(String name, int age){
System.out.println(name + " " + age);
}

private Student(int age){
System.out.println("年龄:" + age);
}
public void m1(){
System.out.println("m1");
}

public void m2(String name){
System.out.println("m2 " + name);
}

public String m3(String name, int age){
System.out.println("m3 " + name + " " + age);
return "m3:OK";
}

private void m4(int age){
System.out.println("m4 " + age);
}

public static void m5(){
System.out.println("m5");
}

private static void m6(String[] str){
System.out.println(str.length);
}
}


反射测试类:

public class Reflect {
public static void main(String[] args) throws Exception {
Class cl = Class.forName("com.ztq.test.Student"); //获取Student类的字节码对象
4000
Student s = (Student) cl.newInstance();  //利用字节码对象cl,建立Student类的实例,调用的是默认构造方法

Constructor c1 = cl.getConstructor(String.class);  //获取相应的构造方法(只能是public修饰的)
c1.newInstance("Chris");  //创建新实例

Constructor c2 = cl.getDeclaredConstructor(int.class);  //获取相应的构造方法(所有的,包括private修饰的)
c2.setAccessible(true);
c2.newInstance(22);

Constructor[] c3 = cl.getDeclaredConstructors();  //获取所有构造方法(包括private)

Method m1 = cl.getMethod("m1", null); //获取名为m1,参数为null的方法
m1.invoke(s, null);  //调用该方法,其中s为该类的一个实例,null为该方法的参数

Method m2 = cl.getMethod("m2", String.class);
m2.invoke(s, "Chris");

Method m3 = cl.getMethod("m3", String.class, int.class);
String str = (String) m3.invoke(s, "Chris", 18);

Method m4 = cl.getDeclaredMethod("m4", int.class);
m4.setAccessible(true);
m4.invoke(s, 20);

Method m5 = cl.getMethod("m5", null);
m5.invoke(s, null);
//        m5.invoke(null, null);  对于静态方法,对象可以为s,也可以为null

Method m6 = cl.getDeclaredMethod("m6", String[].class);
m6.setAccessible(true);
m6.invoke(null, (Object)new String[]{"a", "b", "c"});

Field f1 = cl.getField("str");
f1.set(s, "hehe");  //更改类中的str字段

Field f2 = cl.getDeclaredField("age");
f2.setAccessible(true);
f2.set(s, 30);

Field f3 = cl.getField("time");
f3.set(null, new Date());

}
}


注:
参数中带有数组的,如方法“m6”,要将数组转换成一个对象。这是因为,jdk1.5及以上版本要兼容1.4。在1.4中,数组中的每个元素都对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,会出现参数数量错误。解决方法:

invoke(null, new Object[]{new String[]{"xxx", "xx"}});

或者

invoke(null, (Object)new String[]{"xxx", "xx"});

这样编译器会做特殊处理,编译时不会把参数当做数组看待。

二、内省:

WIKI上的解释:内省是指计算机程序在运行时(Run time)检查对象(Object)类型的一种能力,通常也可以称作运行时类型检查。

相对于内省,反射更进一步,是指计算机程序在运行时(Run time)可以访问、检查和修改它本身状态或行为的一种能力。

内省和反射的区别:

反射是在运行状态把Java类中的各种成分映射成相应的Java类,可以动态的获取所有的属性以及动态调用任意一个方法,强调的是运行状态。

内省机制是通过反射来实现的,BeanInfo用来暴露一个bean的属性、方法和事件,以后我们就可以操纵该JavaBean的属性了。



在Java内省中,用到的基本就是上述几个类。通过BeanInfo这个类就可以获取到类中的方法和属性。

举例:

Person类

public class Person {
private String name = "Tom";
private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public void eat(){
System.out.println("eat food!");
}
}

public class Reflect {
public static void main(String[] args) throws Exception {
Person p = new Person();

//获取Person类中的属性,被封装到了BeanInfo中,若想获得(不包括Object类中的)属性,则参数为(Person.class, Object, class)
BeanInfo bi = Introspector.getBeanInfo(Person.class);
PropertyDescriptor[] pds = bi.getPropertyDescriptors();  //获得类中所有属性描述器
System.out.println("属性描述器个数:"  + pds.length); //3个,因为Object类中的getClass()方法,也算作1个属性
for(PropertyDescriptor pd : pds){
System.out.println(pd.getName());
}

PropertyDescriptor pd = new PropertyDescriptor("name", Person.class);
Method m = pd.getReadMethod();  //获取getName()方法,即读方法
String value = (String)m.invoke(p, null);
System.out.println(value);

Method m1 = pd.getWriteMethod();
m1.invoke(p, "Jerry");
System.out.println(p.getName());
}
}


输出:

属性描述器个数:3
age
class
name
Tom
Jerry
setName


三、BeanUtils工具包是Apache开发的一套快速操作JavaBean get/set方法的API,需要commons-beanutils.jar, commons-logging.jar

注:BeanUtils可以进行类型的自动转换,但仅限基本类型

举例:

在原有Person类中加入private Date birthday;,以及get/set方法

public class Reflect {
public static void main(String[] args) throws Exception {
Person p = new Person();
String name = BeanUtils.getProperty(p, "name"); //调用getName()方法
System.out.println(name);

BeanUtils.setProperty(p, "name", "Jerry");
System.out.println(p.getName());

//非基本类型的属性设置,给BeanUtils注册类型转换器
ConvertUtils.register(new Converter() {
//type目标类型
//value当前传入的值
@Override
public Object convert(Class type, Object value) {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
if (value instanceof String) {
String v = (String) value;
Date d = null;
try {
d = df.parse(v);
} catch (ParseException e) {
e.printStackTrace();
}
return d;
} else {
Date d = (Date)value;
return df.format(d);
}
}
}, Date.class);
BeanUtils.setProperty(p, "birthday", "2017-10-10");
System.out.println(p.getBirthday());

/*
也可以采取这种方式
ConvertUtils.register(new DateLocaleConverter(), Date.class); //DateLocaleConverter是对上面代码的封装
BeanUtils.setProperty(p, "birthday", "1999-09-09");
System.out.println(p.getBirthday());
*/
}
}


BeanUtils可以将Map属性自动放到Bean中

注:原则是,Map的key必须要与Bean的属性一致

举例:

public class Reflect {
public static void main(String[] args) throws Exception {
HashMap map = new HashMap();
map.put("name", "zhang");
Person p = new Person();
BeanUtils.populate(p, map);
System.out.println(p.getName());
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: