在学习反射之前必须要了解的一个概念就是:Class
Class对象基本概念
Java中的类用来表示具有相同属性的方法的对象的集合,是抽象的概念。对象是类创建的,同一个类的不同对象具有不同的属性值。Java中定义的所有类都属于同一类事物,可以用Class来表示。Class类的对象就是不同的类对应的字节码。
获取Clas对象由三种方法:
1、通过类名直接访问Class:
Class stringCls = String.class;
2、通过实例访问getClass:
String s = "";
Class stringCls2 = s.getClass();
3、通过Class类的静态方法forName(类名)
Class stringCls3 = Class.forName("java.lang.String");
class (包括 interface )的本质是数据类型( Type )。 class 是由 JVM 在执行过 程中动态加载的。 JVM 在第一次读取到一种 class 类型时,将其加载进内存。每加载一种 c lass , JVM 就为其创建一个 Class 类型的实例,并关联起来。
反射
反射就是把Java类中的各个组成部分映射成相应的Java类;
一个类的组成部分包括:属性,方法,构造方法,包等。这些组成部分都会被映射成相应的类;Class类定义了一系列方法来获取java类的属性、方法、构造方法,包等信息,这些信息都有来自相应的类来表示,分别是Field、Method、Constructor、Package等。
注意:要进行反射操作必须要拿到这个类对应的Class对象
反射的意义:java运行环境的缓存中保存了类的无参构造方法所对应的Construtor对象。
反射的特征:
(1)反射机制极大的提高了程序的灵活性和扩展性,降低模块的耦合性,提高自身的适应能力。
(2)通过反射机制可以让程序创建和控制任何类的对象,无需提前硬编码目标类。
(3)使用反射机制能够在运行时构造一个类的对象、判断一个类所具有的成员变量和方法、调用一个对象的方法。
(4)反射机制是构建框架技术的基础所在,使用反射可以避免将代码写死在框架中。正是反射有以上的特征,所以它能动态编译和创建对象,极大的激发了编程语言的灵活性,强化了多态的特性,进一步提升了面向对象编程的抽象能力。
通过调用构造方法
可以调用Class提供的newInstance方法
Object obj1 = clazz.newInstance();
但是,调用 Class.newInstance() 的局限是,它只能调用该类的 public 无参数构造方法。如果构造方法带有参数,或者不是 public ,就无法直接通过 Class.newInstance() 来调用。所以为了调用任意的构造方法, Java 的反射 API 提供了 Constructor 对象,它包含一个构造方法的所有信息,可以创建一个实例。 Constructor 对象是一个构造方法,调用结果总是返回实例。
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test04 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class clazz = Example.class;
// 获取该class对象的所有构造方法
Constructor[] cons = clazz.getConstructors();
for (Constructor c : cons) {
System.out.println(c);
}
System.out.println("------------------------");
// 调用有参的构造方法 传入参数的class对象
Constructor con = clazz.getConstructor(int.class, double.class);
Example e = (Example) con.newInstance(120, 12.23);
System.out.println("---------------------------");
Constructor[] conss = clazz.getDeclaredConstructors();
for (Constructor c : conss) {
System.out.println(c);
}
Constructor conn = clazz.getDeclaredConstructor(String.class);
conn.setAccessible(true);
Example ee = (Example) conn.newInstance("ddc");
}
}
class Example {
public Example() {
}
private Example(String s) {
System.out.println(s);
}
protected Example(boolean is) {
System.out.println(is);
}
public Example(int a) {
System.out.println("a=" + a);
}
public Example(int a, double b) {
System.out.println("a = " + a + " b = " + b);
}
}
(1)getConstructors():获取该class对象的所有public的构造方法;
(2)getDeclaredConstructors():获取该class对象定义的所有构造方法;
(3)getConstructor(Class class...):获取某个public的构造方法;
(4)getDeclaredConstructor(Class class):获取某个定义的构造方法。
查看class对象实现的接口以及父类
// 实现的接口
Class[] clss = MyCompareable.class.getInterfaces();
for (Class c : clss) {
System.out.println(c.getName());
}
// 父类
System.out.println(MyCompareable.class.getSuperclass().getName());
(1)getInterfaces():获取该class对象是实现的所有接口;
(2)getSuperclass():获取该class对象的父类;
注意:Object类是所有类的父类,所以如果对Object调用getSuperclass()会返回null
补充:instanceof 与 isAssignableFrom()的区别
import java.io.Serializable;
public class Test01 {
public static void main(String[] args) {
// instanceof 运算符 : 判断“引用”和 “类型”之间的关系
// 可以指向该类实现的接口以及父类
Object obj = Integer.valueOf(123);
System.out.println((obj instanceof String));
System.out.println((obj instanceof Integer));
System.out.println((obj instanceof Double));
System.out.println((obj instanceof Number));
System.out.println((obj instanceof Comparable));
System.out.println((obj instanceof Serializable));
System.out.println("--------------------------------");
// isAssignableFrom() 方法 : 判断类型与类型之间的关系
System.out.println("Integer <= Integer:" + Integer.class.isAssignableFrom(Integer.class));
System.out.println("Integer <= Number:" + Integer.class.isAssignableFrom(Number.class));
System.out.println("Number <= Integer:" + Number.class.isAssignableFrom(Integer.class));
System.out.println("Comparable <= Integer:" + Comparable.class.isAssignableFrom(Integer.class));
}
}
通过上述代码可以发现:instanceof 运算符是判断“引用”和 “类型”之间的关系,并且可以指向该类实现的接口以及父类,而isAssignableFrom() 方法是用来判断类型与类型之间的关系。
访问字段Field
Field类用来表示类中的属性(字段)。
常用方法:
(1)getFields():包含父类的public字段;
(2)getDeclaredFields():得到该对象的定义的所有字段名称(只包含自己的);
(3)getDeclaredField("bookName"):通过指定字段名称获取字段。
(4)getField("bookName"):包含父类的public字段某个字段;
设置字段值
import java.lang.reflect.Field;
public class Test03 {
public static void main(String[] args)
throws InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
// 运行期
// 通过反射的方式 完成成员变量保存值
Class clazz = Book.class;
Object obj = clazz.newInstance();
// 通过指定字段名称获取字段
Field field1 = clazz.getDeclaredField("bookName");
// 参数1:目标对象
// 参数2:存入成员变量的值
field1.set(obj, "ddc");
System.out.println(obj);
}
}
获取字段值
public class Test04 {
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
Book book = new Book();
book.setBookName("ddc");
book.setNumber(123);
book.setAge("12345");
book.setPrice(123.456);
printInfo(book);
}
public static void printInfo(Object obj) throws IllegalArgumentException, IllegalAccessException {
Class clazz = obj.getClass();
Field[] fs = clazz.getDeclaredFields();
for (Field f : fs) {
System.out.println("字段名称:" + f.getName());
// 判断该字段是否可以访问
if (!f.isAccessible()) {
f.setAccessible(true); // 设置私有成员变量允许访问
}
// 获取字段值
System.out.println("字段值:" + f.get(obj));
System.out.println();
}
}
}
调用方法
Method用来表示类中的方法。通过Class对象的如下方法得到Method对象:
(1)Method getMethod(String name,Class<?>…parameterTypes)按名称得到某个特定的public方法(包括从父类或者接口继承的方法);
(2)Method[] getMethods():得到public方法(包括从父类或者接口继承的方法);
(3)Method[] getDeclaredMethods():得到所有的方法(不包括继承的方法);
(4)Method getDeclaredMethod(String name,Class<?>…parame-terTypes):按名称得到某个特定的方法(不包括继承)。
得到某个方法对应的Method对象后,需要调用如下方法来在某个对象上执行该方法:
(1)invoke(Object obj,Object…obj)方法用来调用Method所表示的方法。其中,第一个参数表示此方法作用于哪一个对象。
(2)如果调用的时静态方法,那么invoke()方法中的第一个参数用null表示。
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
public class Test05 {
public static void main(String[] args) {
Class clazz = Book.class;
// 包含父类的public方法
// Method[] methods = clazz.getMethods();
// 不包含父类的所有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method m : methods) {
System.out.println("访问修饰符:" + Modifier.toString(m.getModifiers()));
System.out.println("方法名称:" + m.getName());
System.out.println("方法返回值类型:" + m.getReturnType());
// 获得所有的参数对象
Parameter[] ps = m.getParameters();
for (Parameter p : ps) {
System.out.println("参数名称:" + p.getName());
System.out.println("参数类型:" + p.getType());
System.out.println();
}
System.out.println();
}
}
}
调用方法使用invoke函数
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test06 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class clazz = Base.class;
Object obj = clazz.newInstance();
// 调用无参数的create
// Method method = clazz.getMethod("create");
// 调用有参的create
Method method = clazz.getMethod("create", int.class);
// 以反射的方式执行方法
int r = (int) method.invoke(obj, 1000);
System.out.println(r);
}
}
class Base {
public int create() {
return create(100);
}
public int create(int x) {
return (int) (Math.random() * x);
}
}
调用静态方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test07 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
// 调用静态方法
Class clazz = Math.class;
Method method = clazz.getMethod("log10", double.class);
int n = Double.valueOf((double) method.invoke(null, 12312312)).intValue() + 1;
System.out.println(n);
}
}
调用静态方法的时候需要将invoke函数的第一个参数写为null,因为静态方法属于类
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test08 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
// 反射时候多态依然存在
Method method = Person.class.getMethod("hello");
method.invoke(new Student());
}
}
class Person {
public void hello() {
System.out.println("Person.hello");
}
}
class Student extends Person {
public void hello() {
System.out.println("Student.hello");
}
}
在使用反射调用方法的时候,我们看可以看到多态依然存在
动态代理
JDK动态代理主要涉及两个类: java.lang.reflect.Proxy 和 java.lang.reflect.InvocationHandler 。我们通过编写一个调用逻辑处理器 LogHandler 类案例来提供日志增强功能,并实现 InvocationHandler接口;在 LogHandler 中维护一个目标对象,这个对象是被代理的对象 (真实主题角色);在 invoke() 方法中编写方法调用的逻辑处理。
首先定义接口类 和接口类的具体实现类
public interface UserService {
void select();
void update();
int ddc(int a);
}
//真正的实现类
public class UserServiceImpl implements UserService {
@Override
public void select() {
System.out.println("select * ..................");
System.out.println("数据库中完成用户信息的查询执行!");
}
@Override
public void update() {
System.out.println("update ...................");
System.out.println("数据库中用户状态的更新执行!");
}
@Override
public int ddc(int a) {
int b = a * 10;
return b;
}
}
然后定义LogInvocationHandlerImpl为InvocationHandler接口实现类:代理类中扩展逻辑抽取封装。
实现给接口需要覆写invoke方法 里面有三个参数分别为:
1、proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0;
2、method:我们所要调用某个对象真实的方法的Method对象;
3、args:指代代理对象方法传递的参数。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
// InvocationHandler接口实现类:代理类中扩展逻辑抽取封装
public class LogInvocationHandlerImpl implements InvocationHandler {
private Object obj;
public LogInvocationHandlerImpl(Object obj) {
this.obj = obj;
}
// proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
// method:我们所要调用某个对象真实的方法的Method对象
// args:指代代理对象方法传递的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法" + method.getName() + "开始执行了");
// 执行目标对象的目标方法
Object returnValue = method.invoke(obj, args);
System.out.println(Arrays.toString(args));
System.out.println("方法" + method.getName() + "执行结束");
return returnValue;
}
}
其中定义了我们需要扩展的方法,在没有直接改变具体实现类代码的情况下
下面定义客户端类
import java.lang.reflect.Proxy;
// 动态代理
public class Client {
public static void main(String[] args) {
// 创建目标对象
LogInvocationHandlerImpl handler1 = new LogInvocationHandlerImpl(new UserServiceImpl());
// 创建UserService接口的动态代理对象
// 一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
// 一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一
// 个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
// 一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。
UserService proxy = (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),
new Class[] { UserService.class }, handler1);
// 通过代理对象调用方法
// proxy.select();
// System.out.println(proxy.getClass());
System.out.println(proxy.ddc(10));
OrderServiceImpl order = new OrderServiceImpl();
LogInvocationHandlerImpl handler2 = new LogInvocationHandlerImpl(order);
OrderService proxy2 = (OrderService) Proxy.newProxyInstance(order.getClass().getClassLoader(),
order.getClass().getInterfaces(), handler2);
// proxy2.createOrder();
// System.out.println(proxy2.getClass());
}
}
主要实现的是Proxy.newProxyInstance方法,其中有三个参数分别为:
1、一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载;
2、一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法;
3、一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。
由此可见,动态代理相比较于静态代理具有较大的优势