Java 反射机制原理及用法解析

什么是反射机制

Java反射机制概念

  • 前提:在运行状态中
  • 条件1:对于任何一个类,都能够知道这个类的所有属性和方法;
  • 条件2:对于任何一个方法,都能调用它的任意方法和属性;

在以上述的前提条件的支持下,这种动态获取信息以及动态调用对象的功能称为Java的反射机制。

反射机制的实现原理

要使用反射机制,首先要获取该类的字节码文件对象(class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。

获取字节码文件对象(获取class对象的方式)的三种方式:

  1. 根据类名:类名.class
  2. 根据对象:对象.getClass()
  3. 根据全限定类名:Class.forName(全限定类名)

通过反射机制获取信息

  1. 构造函数

    //获取字节码文件
    Class classs = Class.forName("com.zcbq.reflect.User");
    //先获取有参构造,parameterTypes:表述参数列表,也可以不写
    Constructor constructor = classs.getConstructor(int.class,String.class,int.class,String.class);
    //通过构造器来实例化对象,将实际的参数传进去
    User user = (User) constructor.newInstance(01,"小轩",13,"打球");
    

    获取全部构造函数:

    //获取字节码文件
    Class classs = Class.forName("com.zcbq.reflect.User");
    //获取所有构造函数
    Constructor constructor[] = classs.getConstructors();
    //遍历所有构造函数
    for(int i=0; i<constructor.length; i++){
        //获取每个构造函数中的参数类型字节码对象
        Class[] parameterTypes = constructor[i].getParameterTypes();
        System.out.println("第"+i+"个构造函数:");
        for(int j=0; j<parameterTypes.length; j++){
            System.out.println(parameterTypes[j].getName()+",");
        }
    }
    
  2. 属性
    在学习spring ioc之时,对未提供set方法的private属性依然可以注入感到神奇万分,现在看来,这神奇的根源自然是来自于java的反射,常用的方法如下:

    1. 获取指定成员变量

      //获取字节码文件
      Class classs = Class.forName("com.zcbq.reflect.User");
      //获取其实例对象
      User user = (User) classs.newInstance();
      //获取成员变量classs.getField(name);通过name来获取指定成员变量
      //如果该成员变量是私有的,则应该使用getDeclaredField(name);
      Field declaredField = classs.getDeclaredField("userName");
      //因为属性是私有的,获得其对象后,还要让打开可见权限
      declaredField.setAccessible(true);
      //对成员变量进行操作
      //赋值操作
      declaredField.set(user, "Richard");
      System.out.println(user.getUserName());
      
    2. 获取全部属性

      //获取字节码文件
      Class classs = Class.forName("com.zcbq.reflect.User");
      //获取其实例对象
      User user = (User) classs.newInstance();
      //赋值操作
      user.setUserNum(01);
      user.setUserName("小轩");
      //将私有属性一并获得
      Field[] fields = classs.getDeclaredFields();
      //遍历所有属性
      for (int i=0; i<fields.length; i++){
      	//打开可见权限
      	fields[i].setAccessible(true);
      	System.out.println(fields[i].get(user));
      }
      
  3. 方法

    1. 不带参数的方法

      //获取字节码文件
      Class classs = Class.forName("com.zcbq.reflect.User");
      //获取其实例对象
      User user = (User) classs.newInstance();
      //不带参数的方法,name为不带参数的方法
      /*
      * classs.getMethod(name,paraMeterTypes)
      * name:方法的名称
      * paraMeterTypes:方法的参数类型,没有则什么都不填 例如:String.class
      */
      Method method = classs.getMethod("name");
      //调用方法
      /*
      * method.invoke(obj,args)
      * obj:方法的对象
      * args:实际的参数值,没有则不填
      */
      method.invoke(user);
      
    2. 带参数的方法

      //获取字节码文件
      Class classs = Class.forName("com.zcbq.reflect.User");
      //获取其实例对象
      // User user = (User) classs.newInstance();
      //获取带参数的方法,为方法名
      // Method method = classs.getDeclaredMethod("namess", String.class);
      //设置可见性
      // method.setAccessible(true);
      //调用方法
      // method.invoke(user, "text");
      
    3. 获取所有的方法

      //获取字节码文件
      Class classs = Class.forName("com.zcbq.reflect.User");
      //获取其实例对象
      User user = (User) classs.newInstance();
      //获取所有的方法
      Method[] methods = classs.getMethods();
      //遍历所有方法
      for (Method method : methods){  
      	//设置可见性
          method.setAccessible(true);
          System.out.println(method.getName());
          //获得方法的参数    
          Class<?>[] parameterTypes = method.getParameterTypes();    
          for(int i=0; i<parameterTypes.length; i++){
              //获得构造函数中参数类型
              System.out.println(parameterTypes[i].getName()+",");
          }
      }
      

动态代理的概述及实现

动态代理概述

动态代理:利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对象);

代理的是接口(Interfaces),不是类(Class),更不是抽象类。

动态代理的实现

分三步,但是注意JDK提供的代理正能针对接口做代理,也就是下面的第二步返回的必须要是一个接口。

  1. new出代理对象,通过实现InvacationHandler接口,然后new出代理对象来。
  2. 通过Proxy类中的静态方法newProxyInstance,来将代理对象假装成那个被代理的对象,也就是如果叫人帮我们代买火车票一样,那个代理就假装成我们自己本人。
  3. 执行方法,代理成功

附属代码:

package com.zcbq.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHander implements InvocationHandler {
    private Object target;   

	public MyInvocationHander(){        
		super();
    }    

	public MyInvocationHander(Object target){        
		super();
        this.target = target;    
     }
    
	@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        System.err.println("开始");
        method.invoke(target, args); //执行被代理的target对象的方法
        System.out.println("结束");
        return null;    
    }
}

附属代码:

Student student = new StuImp();
MyInvocationHander m = new MyInvocationHander(student);
/**
* student.getClass().getClassLoader():类加载器
* student.getClass().getInterfaces():被代理对象的接口
* m:代理对象
*/
Student s = (Student) Proxy.newProxyInstance(student.getClass().getClassLoader(), student.getClass().getInterfaces(), m);
s.login();
s.logout();

注意newProxyInstance的三个参数,第一个类加载器; 第二个被代理对象的接口; 第三个代理对象。

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值