java反射机制详解

一、什么是反射

JAVA反射机制是在运行状态中,对于任何一个类,都能够知道这个类的所有属性和方法;对于任何一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为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()+",");
    }
}

console:

2、属性

在学习spring ioc之时,对未提供set方法的private属性依然可以注入感到神奇万分,现在看来,这神奇的根源自然是来自于java的反射,常用的方法如下:

2.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.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、方法

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);

3.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.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()+",");
    }
}

三、动态代理的概述及实现

1、动态代理概述

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

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

2、动态代理的实现

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

2.1 new出代理对象,通过实现InvacationHandler接口,然后new出代理对象来。

2.2 通过Proxy类中的静态方法newProxyInstance,来将代理对象假装成那个被代理的对象,也就是如果叫人帮我们代买火车票一样,那个代理就假装成我们自己本人。

2.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的三个参数,第一个,类加载器,第二个被代理对象的接口,第三个代理对象。

posted @ 2019-10-26 11:05  MyRichard  阅读(5246)  评论(0编辑  收藏  举报