目录
1、介绍反射
反射就是把java类中的各种成分映射成一个个的Java对象
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
2、常用的方法介绍
(1)获取class对象
有三种方式
- 对象.getClass()
- 类.class
- Class.forName("完整类名")
(2)生成实例对象
有两种方式
- Class类的对象.newInstance()
- Constructor c = Class 对象.getConstructor(); 再c..newInstance();
第一种方式只支持无参构造,第二种方式允许有
但是要注意,第二种方式,getConstructor方法要声明参数类型和个数,newInstance要传实参
例如:
Class s = Class.forName("com.wmj.base017reflection.Student");
Constructor constructor2 = s.getConstructor(String.class,int.class);
Object student2 = constructor2.newInstance("张三",22);
(3)获取类属性、方法、构造器
以下方法都是通过class对象.来访问的,(2)中获得的实例对象在这里基本都是做参数用的
属性
带s的都是所有的属性或者方法,不带s的需要带参数,指定一下
带Declared都是可以获取所有的,哪怕是私有的属性或方法
- getFields()
- getDeclaredFields()
- getField("属性名")
- getDeclaredField("属性名")
方法
- getMethods()
- getDeclaredMethods()
- getMethod("方法名",参数类型.class)(可以是多个参数,逗号隔开就行)
- getDeclaredMethod("方法名",参数类型.class)
构造方法
- getConstructors()
- getDeclaredConstructors()
- getConstructor(参数类型.class)
- getDeclaredConstructor(参数类型.class)
其他方法
- getAnnotations()获取class对象的所有注解
- getAnnotation(Deprecated.class)获取指定注解
- getGenericSuperclass()获取class对象的直接超类的 Type
- getGenericInterfaces()获取class对象的所有接口的type集合
3、使用反射
以使用类的某个私有方法为例:
(1)获得Class类的对象
要想解剖一个类,先要获得字节码文件,所以要先获得字节码文件对应的Class类型的对象
(2)生成类的实例对象
(3)获取类的某个指定的私有方法
class对象.getDeclaredMethod(方法名);
(4)设置私有方法可用
如果要使用类的私有成员,需要在使用前,设置成可见
method.setAccessible(true);
(5)使用
method对象.invoke();
示例代码
public class Student{
public String name;
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
public void setAll(String name, int age) {
this.name = name;
this.age = age;
}
private void show() {
System.out.println("这是私有方法:Student [name=" + name + ", age=" + age + "]");
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test1 {
public static void main(String[] args) throws Exception {
//1.获取Class对象
/*
Class s = Student.class;
或者
Class s = Class.forName("com.wmj.base017reflection.Student");
或者
Student stu = new Student();
Class s = stu.getClass();
*/
Class s = Class.forName("com.wmj.base017reflection.Student");
//2.实例化对象
/*
Object student = s.newInstance();
或者
Constructor constructor = s.getConstructor(null);
Object student = constructor.newInstance(null);
*/
Constructor constructor = s.getConstructor(null);
Object student = constructor.newInstance(null);
//3.获取方法
Method showMethod = s.getDeclaredMethod("show");
//4.设置私有方法可用
showMethod.setAccessible(true);
//5.使用该方法
showMethod.invoke(student, null);
System.out.println("获取所有属性:");
Field[] fields1 = s.getFields();
for (Field field : fields1) {
System.out.println(field.getName());
}
System.out.println("获取所有属性,包括私有属性:");
Field[] fields = s.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
System.out.println("测试有参构造:");
Constructor constructor2 = s.getConstructor(String.class,int.class);
Object student2 = constructor2.newInstance("张三",22);
showMethod.invoke(student2, null);
System.out.println("测试有参方法:");
Method setAll = s.getMethod("setAll", String.class,int.class);
setAll.invoke(student2, "李四",44);
showMethod.invoke(student2, null);
}
}
4、动态代理
代理:客户端不直接与真正的对象交互,而是与目标对象的中间代理交互,这样代理可以在真正行为前后做一些特殊的动作
准备一个接口和一个实现类
public interface Animal {
public void eat();
}
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("狗吃狗粮中...");
}
}
(1)静态代理:
静态代理的实现,通过为接口创建代理实现类,代理实现类持有真实对象的引用,客户端不直接与真实对象交互,而是调用代理类,这样使得代理类可以在真实行为前后做一些扩展操作。这些代理类在编译器就被确定,从而称之为静态代理。
注意事项:
- 代理必须和目标类实现共同的接口
- 代理必须包含目标类的对象
- 代理的有参构造中,要传目标类的对象
静态代理类:
public class StaticProxy implements Animal {
Animal animal;
public StaticProxy(Animal animal) {
this.animal = animal;
}
@Override
public void eat() {
System.out.println("准备食物");
animal.eat();
System.out.println("收拾洗碗");
}
}
测试:
public static void main(String[] args) {
Animal animal = new Dog();
StaticProxy sp = new StaticProxy(animal);
sp.eat();
}
输出:
准备食物
狗吃狗粮中...
收拾洗碗
(2)动态代理:就是在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术,在运行时生成代理类,需要注意的是运行时生成的代理并像静态代理那样生成很多class文件,而是通过在运行时生成字节码加载到JVM中。动态代理常见的有JDK动态代理和CGLIB两种方式
(2.1)基于接口的动态代理
使用JDK官方的Proxy类创建代理对象
注意事项:
- 代理的目标对象必须实现接口InvocationHandler
- 提供getInstance方法,将目标类作为参数
- 通过Proxy.newProxyInstance生成相应的代理类,放入getInstance方法中
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxy implements InvocationHandler {
private Object animal;//用Object,这样可以接受任何的目标类作为被代理类
public Object getInstance(Object animal) {
this.animal = animal;
//与cglib的区别在于这里构建代理对象的时候需要传入被代理对象的接口对象作为第二个参数。而cglib不需要被代理对象实现任何接口
return Proxy.newProxyInstance(animal.getClass().getClassLoader(), animal.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JDK准备食物");
method.invoke(animal, args);
System.out.println("JDK收拾洗碗");
return null;
}
}
测试:
public static void main(String[] args) {
Animal animal = (Animal)new JDKProxy().getInstance(new Dog());
animal.eat();
}
这里的animal是代理类,不再是目标类了,debug时是null
输出:
JDK准备食物
狗吃狗粮中...
JDK收拾洗碗
(2.2)基于类的动态代理
使用CGLib的Enhancer类创建代理对象
注意事项:
- 实现MethodInterceptor接口
- 提供getInstance()方法
- 重写intercept()方法