反射原理
一、反射的概念
反射就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
//方式1:
Person p = new Person();
Class c = p.getClass();
//方式2:
Class c2 = Person.class;//任意数据类型都具备一个class静态属性,看上去要比第一种方式简单。
//方式3:
Class c3 = Class.forName("Person");//将类名作为字符串传递给Class类中的静态方法forName
/*第三种和前两种的区别:前两种你必须明确Person类型,后面是你给我这种类型的字符串就行。这种扩展更强,我不需要知道你的类,我只提供字符串,按照配置文件加载就可以了。*/
二、通过反射获取构造方法并使用
1、获取构造方法
getConstructors;getDeclaredConstructors//前者获取公共构造方法,后者获取全部,包括私有构造方法
2、创建对象
newInstance();con.newInstance(“zhangsan", 25);//无参或带参初始化实例
【案例】
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
* 需求:通过反射获取私有构造方法和公共构造方法并使用
* private Human(String name){}
* public Human(String name, int age, String address) {}
*
* 不用反射之前只能获取公共构造方法:
* Human p = new Human("德艺双馨",25,"中国·广东");
* p.show();
*/
public class ReflectDemo {
public static void main(String[] args) {
try {
// 获取字节码文件对象
Class c = Class.forName("Human");
// Constructor con1=c.getConstructor(String.class);//NoSuchMethodException
// 原因是一开始我们使用的方法只能获取公共的,下面这种方式就可以了。
Constructor con = c.getDeclaredConstructor(String.class);
// 用该私有构造方法创建对象出现IllegalAccessException,通过以下方式暴力访问
con.setAccessible(true);// 值为true则指示反射的对象在使用时应该取消Java语言访问检查。
Object obj = con.newInstance("德艺双馨");// 通过私有构造方法初始化对象
((Human) obj).show();// 德艺双馨:0——未填写
Constructor con2 = c.getConstructor(String.class,
int.class, String.class);// 这个是公共构造方法
Object obj2 = con2.newInstance("德艺双馨", 25, "中国·广东");//通过公共构造方法初始化
((Human) obj2).show();// 德艺双馨:25——中国·广东
} catch (Exception e) {
e.printStackTrace();
}
}
}
//创建一个Human类
class Human {
private String name;
private int age = 0;
private String address = "未填写";
private Human(String name) {
this.name = name;
}
public Human(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public void show() {
System.out.println(name + ":" + age + "——" + address);
}
}
三、通过反射获取成员变量并使用
1、获取所有成员
getFields,getDeclaredFields;//同理,前者获取所有公共成员变量,后者获取或有包括私有的成员变量
2、获取单个成员
getField;getDeclaredField; //后者能获取私有变量而前者不能
3、修改成员的值
set(Object obj,Objectvalue);//将指定对象变量上此Field对象表示的字段设置为指定的新值
【案例】
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectField {
public static void main(String[] args) {
Class clazz = Human.class;// 获取字节码文件对象
// 获取所有的成员变量
// Field[] fields = c.getFields();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
/*输出:
* private java.lang.String Human.name
* private int Human.age
* private java.lang.String Human.address
*/
}
// 获取单个的成员变量并赋值
try {
// 通过带参构造方法创建对象
Constructor con = clazz.getConstructor(String.class, int.class,
String.class);// 这个是公共构造方法
Object obj = con.newInstance("德艺双馨", 25, "中国·广东");
// 获取address并对其赋值
// Field addressField = clazz.getField("address");//NoSuchFieldException
Field addressField = clazz.getDeclaredField("address");
// public void set(Object obj,Object value)
// 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
addressField.setAccessible(true);// 因为IllegalAccessException,所有要暴力访问
addressField.set(obj, "北京"); // 给obj对象的addressField字段设置值为"北京"
// 获取name并对其赋值
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);// 因为IllegalAccessException,所有要暴力访问
nameField.set(obj, "index80");
// 获取age并对其赋值
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(obj, 25);
((Human) obj).show();// index80:25——北京
} catch (Exception e) {
e.printStackTrace();
}
}
}
四、通过反射获取成员方法并使用
1、获取所有方法
getMethods;getDeclaredMethods;
2、获取单个方法
getMethod;getDeclaredMethod;
3、暴力访问
method.setAccessible(true);//取消java语法检查
【案例】
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectMethod {
public static void main(String[] args) {
Class klass = Human.class;// 获取字节码文件对象
// 用反射的方式调用方法
try {
// 通过带参构造方法创建对象
Constructor con = klass.getDeclaredConstructor(String.class);// 这个是私有构造方法
con.setAccessible(true);// 因为IllegalAccessException,所有要暴力访问
Object obj = con.newInstance("德艺双馨");
Method m = klass.getMethod("show");// 获取show()方法
m.invoke(obj);// 调用obj对象的方法,输出 德艺双馨:0——未填写
} catch (Exception e) {
e.printStackTrace();
}
}
}
五、反射的应用——动态代理
1、在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。
2、Proxy类中的方法创建动态代理类对象
public static Object newProxyInstance(ClassLoaderloader,Class<?>[]interfaces,InvocationHandler h);
//最终会调用InvocationHandler的方法。
3、InvocationHandler
Object invoke(Object proxy,Methodmethod,Object[]args)
【案例】
(1)学生接口
public interface StudentDao {
public abstract void login();
public abstract void regist();
}
(2)用户操作接口
/*
* 用户操作接口
*/
public interface UserDao {
public abstract void add();
public abstract void delete();
public abstract void update();
public abstract void find();
}
(3)学生接口实现类
public class StudentDaoImpl implements StudentDao {
@Override
public void login() {
System.out.println("登录功能");
}
@Override
public void regist() {
System.out.println("注册功能");
}
}
(4)用户接口实现
public class UserDaoImpl implements UserDao {
@Override
public void add() {
System.out.println("添加功能");
}
@Override
public void delete() {
System.out.println("删除功能");
}
@Override
public void update() {
System.out.println("修改功能");
}
@Override
public void find() {
System.out.println("查找功能");
}
}
(5)实现InvocationHandler接口
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target; // 目标对象
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("权限校验");
Object result = method.invoke(target, args);
System.out.println("日志记录");
return result; // 返回的是代理对象
}
}
(6)测试类
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
UserDao ud = new UserDaoImpl();
ud.add();
ud.delete();
ud.update();
ud.find();
System.out.println("-----------");
// 我们要创建一个动态代理对象
// Proxy类中有一个方法可以创建动态代理对象
// public static Object newProxyInstance(ClassLoader loader,Class<?>[]
// interfaces,InvocationHandler h)
// 我准备对ud对象做一个代理对象
MyInvocationHandler handler = new MyInvocationHandler(ud);
UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass()
.getClassLoader(), ud.getClass().getInterfaces(), handler);
proxy.add();
proxy.delete();
proxy.update();
proxy.find();
System.out.println("-----------");
StudentDao sd = new StudentDaoImpl();
MyInvocationHandler handler2 = new MyInvocationHandler(sd);
StudentDao proxy2 = (StudentDao) Proxy.newProxyInstance(sd.getClass()
.getClassLoader(), sd.getClass().getInterfaces(), handler2);
proxy2.login();
proxy2.regist();
}
}
总结:
Proxy类中创建动态代理对象的方法的三个参数:
ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke方法来进行调用。
InvocationHandler接口中invoke方法的三个参数:
proxy:代表动态代理对象
method:代表正在执行的方法
args:代表调用目标方法时传入的实参
Proxy.newProxyInstance
创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,以$开头,proxy为中,最后一个数字表示对象的标号。
System.out.println(u.getClass().getName());