java进阶之java的反射机制

1.反射机制简介

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

Java反射机制主要提供了以下功能

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法;
  • 生成动态代理。

下面,我们通过代码来看一下反射的厉害之处。

2. 在运行时判断任意一个对象所属的类

package com.mystery.refl;

import java.sql.Date;

public class Person {
    private int age;
    private String name;
    private Date birthday;


    public Person() {
        super();
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public void sayChina(){
        System.out.println("hello ,china");
    }

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

    @Override
    public String toString() {
        return "姓名:"+name+"       生日:"+birthday;
    }

}

这是一个简单的pojo类,接下来的代码都会用到这个类。

public class TestReflect {
    @Test
    public void test1(){
        Person p = new Person();
        //暂时先这样写,下面的代码会使用反射的方法来创建对象
        String className = p.getClass().getName();
        System.out.println(className);
    }
}
//打印结果:com.mystery.refl.Person

通过对象的getClass()我们可以获得对象的运行时类(java.lang.Class),这个方法定义在java.lang.Object中。

在java中,所有类的对象其实都是Class的实例。

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

3. 在运行时构造任意一个类的对象

@Test
public void test2(){
    Class<?> test1=null;
    Class<?> test2=null;
    Class<?> test3=null;

    try {
        test1=Class.forName("com.mystery.refl.Person");
        //一般都会用这种方式
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

    test2=new Person().getClass();
    test3=Person.class;

    System.out.println("类名   "+test1.getName());
    System.out.println("类名   "+test2.getName());
    System.out.println("类名   "+test3.getName());
}
打印结果:
//com.mystery.refl.Person
//com.mystery.refl.Person
//com.mystery.refl.Person

这里产生的不是Person对象,而是Class对象!

4.在运行时判断任意一个类所具有的成员变量

    @Test
    public void test3(){
        Class<?> test=null;

        try {
            test=Class.forName("com.mystery.refl.Person");

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // 取得本类的全部属性
        Field[] field = test.getDeclaredFields();
        for (int i = 0; i < field.length; i++) {
            // 权限修饰符
            int mo = field[i].getModifiers();
            String priv = Modifier.toString(mo);
            // 属性类型
            Class<?> type = field[i].getType();
            System.out.println(priv + " " + type.getName() + " " + field[i].getName() + ";");
        }
    }
    //打印结果
    //private int age;
    //private java.lang.String name;
    //private java.sql.Date birthday;

这里通过java.lang.reflect.Field类的方法获得到了Person类中的属性,简单总结一下Field类的几个常用方法

返回类型方法名作用
Objectget(Object obj返回指定对象上此 Field 表示的字段的值
StringgetName()返回此 Field 对象表示的字段的名称
voidset(Object obj, Object value)将指定对象变量上此 Field 对象表示的字段设置为指定的新值
ClassgetType()返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型


博客最后的小案例我们会用到这些方法。

5.在运行时调用任意一个类所具有的方法

    @Test
    public void test4(){
        Class<?> test=null;

        try {
            test=Class.forName("com.mystery.refl.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        try{
            //调用Person类中的sayChina方法
            Method method=test.getMethod("sayChina");
            method.invoke(test.newInstance());
            //调用Person的sayHello方法
            method=test.getMethod("sayHello",
                    String.class,int.class);
            method.invoke(test.newInstance(),"jack",21);

        }catch (Exception e) {
            e.printStackTrace();
        }
    }
    //打印结果
    //hello ,china
    //jack  21

这里用到了java.lang.reflect.Method中的方法。

6.查看某一对象的其他属性

@Test
    public void test5(){
        Class<?> test=null;

        try {
            test=Class.forName("com.mystery.refl.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //获得类所实现的接口
        Class<?> intes[]=test.getInterfaces();
        for (int i = 0; i < intes.length; i++) {
            System.out.println("实现的接口   "+intes[i].getName());
        }

        //获得类所继承的父类
        Class<?> temp=test.getSuperclass();
        System.out.println("继承的父类为:   "+temp.getName());

        //获得类中所有的构造函数
        Constructor<?>cons[]=test.getConstructors();
        for (int i = 0; i < cons.length; i++) {
            System.out.println("构造方法:  "+cons[i]);
        }


    }

7.通过反射,给某个对象赋值

    @Test
    public void test6(){
        Class<?> test=null;

        try {
            test=Class.forName("com.mystery.refl.Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        Person per=null;
        try {
            per=(Person)test.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        per.setName("jack");
        per.setBirthday(new Date(95, 11, 27));;
        System.out.println(per);
    }
    //打印结果
    //姓名:jack       生日:1995-12-27

注意:此时,Person类中必须含有无参构造函数。除此之外,还可以通过直接调用对象的构造函数来进行赋值。

8.一个小案例

下面通过一个小案例来整合一下反射。

案例:利用反射,来改变person中各属性的值。

    @Test
    public void test7() {
        Person p = new Person();
        p.setAge(20);
        p.setBirthday(new Date(95, 11, 27));
        p.setName("jack");

        Field[] fields = p.getClass().getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field f = fields[i];
            f.setAccessible(true);
            try {
                Object value =  f.get(p);
                if (value != null) {
                    f.set(p, deal(value));
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        System.out.println(p);
        System.out.println(p.getAge());
    }

    private Object deal(Object value) {
        if (value instanceof Integer) {
            return 50;
        } else if (value instanceof String) {
            return "tom";
        } else if (value instanceof Date) {
            return new Date(1,1,1);
        }
        return value;
    }
//打印结果
//姓名:tom       生日:1901-02-01
//50

可以看到,我们已经将p对象的属性值全部修改了。

9.题外话

也许有人会说,我直接p.setAge(),p.setName()不就可以了?为什么非要写这么多呢?

那么我问你,如果Person类中有100个属性呢?也一个一个写?当然,这个例子举得不是特别好,我只是想通过一段代码来讲解如何使用java的反射。

使用反射可以使你的代码更加灵活,不用将代码写死,还可以直接从配置文件中加载信息,极大地提高了编程效率。

关于动态代理,之后我会专门写一篇文章来讲解。

下一篇文章我会用反射写一个通用的dao类,为什么会想到数据库呢,因为第一次接触反射是在学数据库连接时遇到的(class.forName(“com.jdbc.driver.mysql”))。。。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值