Java 中的反射机制

首先先讲解一下动态语言和非动态语言
动态语言:在程序运行期间,允许改变程序结构或者变量类型 例如:Perl, Python,Ruby
与之相对的是静态语言,C++,Java,C#

在动态语言中允许 var a=1; a=”aaaa”; a=false;

反射机制(Reflection)基本概念

java 的反射机制:动态获取类的信息以及动态调用对象的方法。
对于任意一个对象都够知道这个类具有哪些属性和方法
对于任意一个对象,能够调用它的任意一个方法。

java 中有一个非常突出的动态相关机制,反射机制reflection(被视为准动态语言的一个关键性质),
java程序可以加载一个运行时才得知名称的 class, 获悉其完整的构造(不包括method定义),获取其内部信息,并生成实例化对象,或对其fileds设值,或唤起其methods
这种看透class的能力被称为内省(introspection),它与reflection 是常被提起的两个术语
这种反射机制可以获取filed和methods的所有信息,并可于运行时改变fileds的内容或者调用Methods

java的反射机制提供了这样的功能:都是运行时的事情
1. 运行时判断任意一个对象所属的类
2. 运行时构造任意一个类的对象
3. 运行时判断任意一个类具有的属性和方法
4. 运行时调用任意一个对象的方法

反射机制 java.lang.reflect包中 :

    /*
     * Class类   对应一个类 ,在java.lang包下,其他全在java.lang,reflect包下
     * Filed类, 对应类成员变量
     * Method类  对应类成员方法
     * Constructor类  对应类的构造方法
     * Array类  提供了动态创建数组,以及访问数组的元素的静态方法
     * java中不管产生了多少对象,都会对应于唯一的同一个 .class
     */
  // 获取String类
          Class<?> clas = Class.forName("java.lang.String");
          // 获取String类的所有方法
          Method[] method=clas.getDeclaredMethods();
          // 获取String类的所有成员变量
          Field[] filed = clas.getDeclaredFields();
          // 获取String类的所有构造方法
          Constructor[] con = clas.getDeclaredConstructors();

如何通过反射机制实例化一个对象,并调用其成员方法
下面是例子,详细介绍了步骤

public class ReflectTest{

    public int  add(int a, int b, int c){
        return a+b+c;
    }
    public static void main(String[] args) throws Exception {
        // 如何实现 实例化一个ReflexTest对象,并调用其add方法
        // 第一种方法,new
//      ReflectTest reflect = new ReflectTest();
//      reflect.add(1,2,3);

        // 第二种方法, 通过反射机制实现

        //第一步:首先获取类的信息
        Class<?> classType = ReflectTest.class;

        // 第二步: 通过类信息实例化一个对象,调用newInstance() 方法
        Object  instance = classType.newInstance();

        // 第三步,获取Method方法,利用getMethod方法,获取类的特定成员方法,
        Method addMethod = classType.getMethod("add", new Class[] {int.class,int.class,int.class});
    //  Method addMethod = classType.getMethod("add", int.class, int.class, int.class);   //上面一句的等价写法,后面一个变量为可变参数,离散输入或者以数组方式输入都可以
        // public Method getMethod(String name, Class<?>... parameterTypes) 第一个参数为方法名,第二个为方法里面的变量的类的信息,为什么需要类的信息,因为java中允许重载的,所以 需要方法名,参数个数,参数类型才可以完全确定一个方法

        // 第四步: 利用获取的方法的invoke()方法,调用类的成员方法,第一个参数为获取的实例,第二个参数为具体的传入的对象,注意的是返回值的类型是Object类型,使用的时候需要进行一步强制类型转换
        Object result = addMethod.invoke(instance, new Object[]{1, 2, 3});
        //Object result = addMethod.invoke(instance, 1, 2, 3); // 上面一句的等价写法
        //public Object invoke(Object obj, Object... args) 后面一个变量为可变参数,离散输入或者以数组方式输入都可以

        System.out.println((Integer)result);    

    }

}

要想使用反射,首先需要获得待处理类或对象所对应的 Class对象。

获取某个类或某个对象所对应的 Class对象的常用的 3种方式:
a) 使用Class类的静态方法 forName:Class.forName(“java.lang.String”);
b) 使用类的.class语法:String.class;
c) 使用对象的 getClass()方法:String s = “aa”; Class

public class ReflectTest2 {
    public static void main(String[] args) throws Exception {
        ReflectTest2  r1 = new ReflectTest2();
        Person p1 = new Person(1,"wang", 20);
        Person p2 = (Person) r1.copy(p1);
        System.out.println(p2.getId()+p2.getName()+p2.getAge());

    }

    public Object copy(Object obj) throws Exception{
        // 首先获取类的信息,并实例化一个对象
        Class<?> classType = obj.getClass();
        Object instance = classType.newInstance(); 

        // newInstance() 方法生成一个对象,是调用无参数的构造方法,
        //   Object instance = classType.newInstance(); 

        // 下面的两句等价于上面的一句,
        //  Constructor con = classType.getConstructor(new Class[]{});
        //   instance = con.newInstance( new Objec[]{});

        //  但是因为Class的newInstance只能处理没有参数的情况,所以不合适所有的情况,Constrator因此提供了多个函数的构造方法
        // 采用Constrator 实现,首先获得其构造函数,多个参数的
//      Constructor conn = classType.getConstructor(new Class[]{int.class,String.class,int.class});
//      Object instance2 = conn.newInstance(new Object[]{1,"wang",18}); 

        // 获取所有的成员变量

        Field[] field = classType.getDeclaredFields();
        for( Field f : field ){
            // 首先获取成员变量的名字
            String s = f.getName();
            // 要获取成员变量的值,得通过它的get方法,所以获取其get方法
            String firstLetter = s.substring(0,1).toUpperCase(); // substring 截取字符串,toUpperCase 将字符串大写 

            // 首先得到get成员方法的名字,然后再得到get方法
            String getMethod ="get"+ firstLetter + s.substring(1);
            Method getMeth = classType.getMethod(getMethod, new Class[]{});

            // 获取其set方法
            String setMethod ="set"+ firstLetter + s.substring(1);
            Method setMeth = classType.getMethod(setMethod, new Class[]{f.getType()});

            // 从要复制的对象中取出成员变量的值,然后再复制到要复制的那个对象中去,通过set方法赋值
             Object o = getMeth.invoke(obj, new Object[]{});
             setMeth.invoke(instance, new Object[]{o});
        }

         return instance;

    }
}

class Person{
    private int id;
    private String name;
    private int age;
    public Person() {
        // TODO Auto-generated constructor stub
    }
    public Person(int id, String name, int age) {
        // TODO Auto-generated constructor stub
        this.id=id;
        this.name=name;
        this.age=age;
    }
    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 getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

}

需要注意的一点:
Integer.TYPE 返回的是 int,
而Integer .class返回的是 Integer 类所对应的Class对象。

定义Array数组

public class ReflectTest3 {
    Class<?> classType = String.class;
    //  申请一个10个类型为classType的数组
    Object obj = Array.newInstance(classType,10);
    // 获取数组中下标为5的 classType 类型的对象
    Object obj2 = Array.get(obj,5);

     int[] dimension = new int[]{5,10,15};
     // 这里其实是定义了一个三维数组
     Object array = Array.newInstance(Integer.TYPE, dimension);
      int [][][] arraycast = (int[][][]) array;

重点:如何利用反射机制调用对象的私有方法,访问对象的私有成员变量

public class ReflectTest4 {
     public static void main(String[] args) throws Exception{

         // 使用类的私有成员方法
         SayHello reflect = new SayHello();
         Class<?> classType = reflect.getClass();
         // 两种获取Method的方法,但是是有区别的
    //   Method method1 = classType.getMethod("sayHello", new Class[]{String.class});   // 返回的方法必须是public的
         Method method2 = classType.getDeclaredMethod("sayHello", new Class[]{String.class});  // 返回的方法没有要求

         method2.setAccessible(true);  // 压制java的访问控制检查

         // 直接使用会报错的,因为是调用的私有的方法,java进行访问检查, 因此之前需要加上上面一句,压制他的访问检查
         method2.invoke(reflect, "world"); //helloworld
         method2.invoke(reflect, new Object[]{"world2"});  //helloworld2
         // 后面学的一些框架 如 hebirnate等都是使用了此框架

         // 修改私有成员变量的值
         SayHello sayHello2 = new SayHello();
         Class<?> classType2 = sayHello2.getClass();
         Field field = classType2.getDeclaredField("name");
         field.setAccessible(true);
         field.set(sayHello2, "lisi");
         System.out.println(sayHello2.getName()); // lisi


    }

}

class SayHello{
     private String name = "zhangsan";
     public String getName(){
         return name;
     }
     private void sayHello(String s){
         System.out.println("hello"+s);
     }
}

在获取对象的类的信息时,特别需要注意的一点

Class<?> classType = Integer.TYPE; // 得到的是 int
Class<?> classType = Integer.class; // 得到的是Integer
// 同理可知,其他的其中原生数据类型的包装类也是同样的原理

//实际开发的时候很少用反射,但是如果自己开发框架,肯定会用到

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值