Java基础--反射

一、反射:一个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类的字节码对象
        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());
    }
}




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值