------- android培训、java培训、期待与您交流! ----------
(一)类加载器:
通过这些描述我们就可以知道类的加载都是由类加载器来完成的。
到目前为止我们已经知道把class文件加载到内存了,那么,如果我们仅仅站在这些class文件的角度,我们如何来使用这些class文件中的内容呢?这就是我们反射要研究的内容。
(二)反射
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
(2)如何获取字节码文件对象:
Person p = new Person();
Class c = p.getClass();
通过对象获取字节码文件对象
Class c2 = Person.class;
任意数据类型都具备一个class静态属性,看上去要比第一种方式简单.
Class c3 = Class.forName("Person");
将类名作为字符串传递给Class类中的静态方法forName即可。
/*
* 获取字节码文件对象的目的:将该类型的字节码文件对象获取,为了使用反射的方法,独立使用其内容成员(成员方法,成员变量,构造方法)
* Class类型的对象
*
* 三种获取字节码文件对象的方式
* 方式1:
* Object:
* public final Class<?> getClass()
* Person p = new Person();
* Class c = p.getClass();
* 通过对象获取字节码文件对象
* 方式2:
* Class c2 = Person.class;
* 任意数据类型都具备一个class静态属性,看上去要比第一种方式简单.
* 方式3:
* Class c3 = Class.forName("Person");
* 将类名作为字符串传递给Class类中的静态方法forName即可。
* public static Class<?> forName(String className) 通过类名返回字节码文件对象
throws ClassNotFoundException
*/
public class Demo03_reflect {
public static void main(String[] args) throws ClassNotFoundException {
//方式一:
Student s = new Student();
Class clazz = s.getClass();
System.out.println(clazz);
//方式二:
Class clazz2 = Student.class;
System.out.println(clazz2);
Class clazz4 =int[].class;
System.out.println(clazz4);
//方式三:
Class clazz3 = Class.forName("cn.itcast.Student");
System.out.println(clazz3);
}
}
(3)反射获取操作:
A:通过反射获取构造方法并使用。通过构造方法对象创建对象, 获取构造方法。
/*
* 反射操作构造方法
* Constructor:构造方法
*
* Declared:声明的
* 所有带Declared的方法,均是获取声明的内容
* 不带Declared的方法,均是获取公共的内容
*
* 1:反射到其构造方法
* public Constructor<?>[] getConstructors() 获取所有公共的构造方法
* public Constructor<?>[] getDeclaredConstructors() 获取所有声明的构造方法
*
* public Constructor<T> getConstructor(Class<?>... parameterTypes) 通过参数类型,获取单个构造
* public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 通过参数类型,获取单个构造
* 2:将反射到的构造方法使用起来
* Constructor:
* public T newInstance(Object... initargs) 使用构造方法创建对象
*
* 需要将private修饰的成员,进行暴力访问
* public void setAccessible(boolean flag) 取消其访问权限检查
*/
public class Demo04_reflect {
public static void main(String[] args) throws Exception {
//1:反射到其构造方法
//获取字节码文件对象
Class clazz = Student.class;
Constructor[] constructors = clazz.getConstructors();
System.out.println(Arrays.toString(constructors));
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor constructor : declaredConstructors) {
System.out.println(constructor);
}
System.out.println("===================");
// public Constructor<T> getConstructor(Class<?>... parameterTypes) 通过参数类型,获取单个构造
Constructor constructor0f = clazz.getDeclaredConstructor();
System.out.println(constructor0f);
Constructor constructor3f = clazz.getDeclaredConstructor(String.class,int.class,String.class);
System.out.println(constructor3f);
//2:将反射到的构造方法使用起来 对于构造方法,使用的方式是创建对象
Object newInstance = constructor0f.newInstance();
Student s = (Student)newInstance;
System.out.println(s);
//需要将private修饰的成员,进行暴力访问
//public void setAccessible(boolean flag) 取消其访问权限检查
constructor3f.setAccessible(true);
Object newInstance2 = constructor3f.newInstance("唐嫣",18,"福临公寓");
Student s2 = (Student)newInstance2;
System.out.println(s2);
}
}
将指定对象变量上此Field 对象表示的字段设置为指定的新值。
/*
* 反射操作成员变量
* Field:成员变量 字段
*
* Declared:声明的
* 所有带Declared的方法,均是获取声明的内容
* 不带Declared的方法,均是获取公共的内容
*
* 1:反射到其成员变量
* public Field getDeclaredField(String name) 获取声明的单个字段
* public Field getField(String name) 获取公共的单个字段
*
* 2:将反射到的成员方法使用起来
* Field:
* public void set(Object obj, Object value) obj:设置属性值的对象 value:该字段要赋的值
* public Object get(Object obj) obj:获取属性值的对象
*
* 需要将private修饰的成员,进行暴力访问
* public void setAccessible(boolean flag) 取消其访问权限检查
*/
public class Demo06_reflect {
public static void main(String[] args) throws Exception {
//1:反射到其成员变量
//获取字节码文件对象
Class clazz = Student.class;
Field field = clazz.getDeclaredField("name");
Field field2 = clazz.getDeclaredField("address");
//2:将反射到的成员变量使用起来 对于成员变量,使用的方式是赋值和取值
Student s = new Student();
field.set(s, "唐嫣");
//需要将private修饰的成员,进行暴力访问
field2.setAccessible(true);
field2.set(s, "四拨子");
System.out.println(s);
}
}
/*
* 反射操作成员方法
* Method:成员方法
*
* Declared:声明的
* 所有带Declared的方法,均是获取声明的内容
* 不带Declared的方法,均是获取公共的内容,
*
* 1:反射到其成员方法
* public Method[] getMethods() 获取所有公共的成员方法,包含父类继承过来的方法
* public Method[] getDeclaredMethods() 获取所有声明的成员方法
*
* public Method getMethod(String name, Class<?>... parameterTypes) 通过方法名与参数类型反射返回其成员方法对象
* public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 通过方法名与参数类型反射返回其成员方法对象
*
* 2:将反射到的成员方法使用起来
* Method:
* public Object invoke(Object obj, Object... args) 调用方法
* obj:需要调用方法的对象
* args:调用方法实参
*
* 需要将private修饰的成员,进行暴力访问
* public void setAccessible(boolean flag) 取消其访问权限检查
*/
public class Demo05_reflect {
public static void main(String[] args) throws Exception {
//1:反射到其成员方法
//获取字节码文件对象
Class clazz = Student.class;
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
Method declaredMethod = clazz.getDeclaredMethod("eat");
Method declaredMethod1f = clazz.getDeclaredMethod("hitBeanBean", int.class);
//2:将反射到的成员方法使用起来 对于成员方法,使用的方式是调用
Student s = new Student();
declaredMethod.invoke(s);
//需要将private修饰的成员,进行暴力访问
declaredMethod1f.setAccessible(true);
Object result = declaredMethod1f.invoke(s, 100);
System.out.println((String)result);
}
}
(4)暴力反射:取消访问权限检查
/*
* 反射绕过泛型检查
* 在编译期会进行泛型的语法检查。
*
* 原因:泛型检查存在擦除泛型的动作(即编译器认识泛型,而虚拟机不认识泛型),真正在运行时,仍然是泛型位置使用的是Object
*/
public class Test {
public static void main(String[] args) throws Exception {
ArrayList<String> list = new ArrayList<String>();
list.add("唐嫣");
list.add("戚薇");
list.add("刘诗诗");
list.add("杨幂");
// list.add(110);
//反射ArrayList类的add方法
//获取字节码文件对象
Class clazz = list.getClass();
//反射获取其add方法
Method addMethod = clazz.getDeclaredMethod("add", Object.class);
//使用其反射到的方法
addMethod.invoke(list, "隔壁老王");
addMethod.invoke(list, 110);
addMethod.invoke(list, new Person());
System.out.println(list);
}
}
(二)动态代理
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());
public interface Animal {
public void eat();
public String sleep();
}
public class Person implements Animal{
public void eat() {
System.out.println("我吃了");
}
public String sleep() {
return "我跟豆豆睡了";
}
public String hitBeanBean(int times) {
return "我今天打了豆豆"+times+"次";
}
}
/*
* 实现代理过程的类,创建该类对象,用于创建代理对象
*/
public class MyHandler implements InvocationHandler {
//定义成员变量,接收被代理的对象.被代理对象,也可以使用反射的方法完成创建对象
Person p;
public MyHandler(Person p) {
super();
this.p = p;
}
//完成方法代理
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//这里也可以通过反射的方式,创建被代理对象
//其他功能
//代理被代理者调用方法
Object result = method.invoke(p, args);
//其他功能
return result;
}
}
/*
* 代理对象(通过方法返回的对象) 代理 被代理对象(本包中的Person) 调用方法(任意方法,)
*
* 被代理对象 决定有什么方法
* 代理对象调用方法
*
* JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib。
*
* Proxy:Proxy 提供用于创建动态代理类和实例的静态方法 代理类。
* public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
* 返回代理对象
*
* ClassLoader loader:类加载器
* Class<?>[] interfaces:接口
* InvocationHandler h: 实现代理过程的对象
*
* Object invoke(Object proxy,Method method, Object[] args) throws Throwable
* Object proxy:代理实例 这里不使用
* Method method:真正被代理执行的方法
* args: 调用真正被执行方法时,传进来的参数
*
* InvocationHandler:
* 是代理实例的调用处理程序 实现的接口。 在该类当中,完成代理的动作。
*/
public class Demo07_proxy {
public static void main(String[] args) {
//被代理对象
Person p = new Person();
//代理对象
//获取类加载器
ClassLoader loader = p.getClass().getClassLoader();
//获取接口
Class<?>[] interfaces = p.getClass().getInterfaces();
//创建实现代理过程的对象,用于创建代理对象
InvocationHandler h = new MyHandler(p);
//创建代理对象
Animal proxy = (Animal)Proxy.newProxyInstance(loader, interfaces, h);
//代理调用方法
proxy.eat();
String sleep = proxy.sleep();
System.out.println(sleep);
}
}