黑马程序员——反射

------- android培训java培训、期待与您交流! ----------

(一)类加载器:

    类的加载
        •当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个               类进行初始化。
        加载
            -就是指将class文件读入内存,并为之创建一个Class对象。
            -任何类被使用时系统都会建立一个Class对象。
        连接
            -验证 是否有正确的内部结构,并和其他类协调一致
            -准备 负责为类的静态成员分配内存,并设置默认初始化值
            -解析 将类的二进制数据中的符号引用替换为直接引用
        初始化 就是我们以前讲过的初始化步骤
    类的加载时机
        创建类的实例
        访问类的静态变量,或者为静态变量赋值
        调用类的静态方法
        使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
        初始化某个类的子类
        直接使用java.exe命令来运行某个主类
    类加载器
        负责将.class文件加载到内存中,并为之生成对应的Class对象。
        虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
    类加载器的组成
        Bootstrap ClassLoader 根类加载器
        Extension ClassLoader 扩展类加载器
        System ClassLoader 系统类加载器
    Bootstrap ClassLoader 根类加载器
        -也被称为引导类加载器,负责Java核心类的加载
        -比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
    Extension ClassLoader 扩展类加载器
        -负责JRE的扩展目录中jar包的加载。
        -在JDK中JRE的lib目录下ext目录
    System ClassLoader 系统类加载器
        -负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

通过这些描述我们就可以知道类的加载都是由类加载器来完成的。

到目前为止我们已经知道把class文件加载到内存了,那么,如果我们仅仅站在这些class文件的角度,我们如何来使用这些class文件中的内容呢?这就是我们反射要研究的内容。

(二)反射

       JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
       (1)通过字节码文件对象去使用构造方法,成员变量,成员方法。

              要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
       (2)如何获取字节码文件对象:

       三种获取字节码文件对象的方式

       方式1:

       Person p = new Person();

       Class c = p.getClass();

       通过对象获取字节码文件对象

       方式2:

       Class c2 = Person.class;

       任意数据类型都具备一个class静态属性,看上去要比第一种方式简单.

       方式3:

       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:通过反射获取构造方法并使用。通过构造方法对象创建对象, 获取构造方法。

              获取构造方法
                     -getConstructors
                     -getDeclaredConstructors

              创建对象
                     -newInstance()
                     -con.newInstance(“zhangsan", 20);
/*
 * 反射操作构造方法
 * 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);
	}

}
                B:通过反射获取成员变量并使用。通过成员变量对象给成员变量赋值。

               获取成员
              获取所有成员
                     -getFields,getDeclaredFields
              获取单个成员
                     -getField,getDeclaredField
              修改成员的值
                     -set(Object obj,Object value)
                           将指定对象变量上此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);
		
		
	}

}
              C:通过反射获取成员方法并使用。通过成员方法对象调用成员方法。

              获取普通方法
              获取所有方法
                     -getMethods
                     -getDeclaredMethods
              获取单个方法
                     -getMethod
                     -getDeclaredMethod
              暴力访问
                     -method.setAccessible(true);
/*
 * 反射操作成员方法
 * 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)暴力反射:取消访问权限检查

              通过配置文件运行类中的方法
              绕过ArrayList<Integer>的一个对象的泛型检查,在这个集合中添加一个字符串数据。只需要将add方法使用反射的方式调用即可。
              原因:泛型检查存在擦除泛型的动作(即编译器认识泛型,而虚拟机不认识泛型),真正在运行时,仍然是泛型位置使用的是Object。
/*
 * 反射绕过泛型检查
 * 在编译期会进行泛型的语法检查。
 * 
 * 原因:泛型检查存在擦除泛型的动作(即编译器认识泛型,而虚拟机不认识泛型),真正在运行时,仍然是泛型位置使用的是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);
	}

}

(二)动态代理

    •代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。
        -举例:春季回家买票让人代买
    动态代理:在程序运行过程中产生的这个对象
        -而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理
    在代理过程中,可以在本类基础上添加新的功能,使其功能更强大
    在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做          代理。我们有更强大的代理cglib。
    Proxy类中的方法创建动态代理类对象
        -public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
        -最终会调用InvocationHandler的方法
    InvocationHandler
        -Object invoke(Object proxy,Method method,Object[] args)

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

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值