zoukankan      html  css  js  c++  java
  • 反射技术初步

    转载请注明出处,谢谢!

    类的加载概述

    当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
    

    加载

    • 就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。

    连接

    • 验证 是否有正确的内部结构,并和其他类协调一致
    • 准备 负责为类的静态成员分配内存,并设置默认初始化值
    • 解析 将类的二进制数据中的符号引用替换为直接引用

    初始化

    类的加载时机

    • 创建类的实例
    • 访问类的静态变量,或者为静态变量赋值
    • 调用类的静态方法
    • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
    • 初始化某个类的子类
    • 直接使用java.exe命令来运行某个主类

    类加载器的概述

    负责将.class文件加载到内存中,并为之生成对应的Class对象。
    

    类加载器的分类

    • Bootstrap ClassLoader 根类加载器
    • Extension ClassLoader 扩展类加载器
    • Sysetm ClassLoader 系统类加载器

    类加载器的作用

    Bootstrap ClassLoader 根类加载器

    • 也被称为引导类加载器,负责Java核心类的加载
    • 比如,System,String 等。在JDK中JRE的lib目录下rt.jar文件中

    Extension ClassLoader 扩展类加载器

    • 负责JRE的扩展目录中jar包的加载。
    • 在JDK中JRE的lib目录下的ext目录

    Sysetm ClassLoader 系统类加载器

    • 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

    反射技术

    就是动态加载一个指定的类,并获取该类中的所有的内容。将字节码文件和字节码文件中的内容都封装成对象,这样以来,就可以方便地进行如下操作:
    1. 在运行时判断一个对象所属的类;
    2. 在运行时构造类的对象;
    3. 在运行时访问java对象的属性,方法,构造方法等。
    

    Class类

    Java程序在运行期间,Java运行时系统始终会为所有对象维护一个被称为运行时的类型标识(runtime type ID)。每个标识跟踪着每个对象所属的类,保存这些标识的类就是Class类。

    1. 如果拿到了对象,不知道是什么类型,用于获得对象的类型。
      Object obj = new Person();
      Class clazz1 = obj.getClass();
    2. 根据指定的类名来获得,用于类加载。如果字符串不是类名或接口名就会抛出checkedexception,无论何时使用这个方法,都应该提供一个异常处理器。
      String classname = "com.mysql.jdbc.Driver";
      Class clazz = Class.forName(classname);
    3. 明确地获得某个类的Class对象,主要用于传参。
      Class clazz = Person.class;
      Class clazz = int.class;
    • 注意,一个Class对象实际上描述了一个类型,而这个类型未必一定是一种类。例如int不是类,但int.class一个class类型的对象。

    Class.forName()读取配置文件举例

    创建输入流对象,关联配置文件
    BufferedReader br = new BufferedReader(new FileReader("config.properties"));
    读取配置文件一行内容,获取该类的字节码对象
    Class<?> clazz = Class.forName(br.readLine());
    通过字节码对象创建实例对象.方法调用默认的构造器(无参的),如果没有无参构造器就会抛出一个异常。
    Fruit f = (Fruit) clazz.newInstance();
    使用
    Juicer j = new Juicer();
    j.run(f);

    通过反射获取带参构造方法

    Constructor
    Class类的newInstance()方法是使用该类无参的构造函数创建对象, 如果一个类没有无参的构造函数, 就不能这样创建了,可以调用Class类的getConstructor(String.class,int.class)方法获取一个指定的构造函数然后再调用Constructor类的newInstance("张三",17)方法创建对象。

    通过反射获取成员变量

    Field
    Class.getField(String)方法可以获取类中的指定域(非私有的), 通过set(obj, "李四")方法可以设置指定对象上该域的值。如果是私有的可以用getDeclaedField("name")方法获取域, 然后调用setAccessible(true)设置访问权限,再调用get(obj)可以获取指定对象中该域的值。

    通过反射获取方法

    Method
    Class.getMethod(String name, Class<?>... parameterTypes) 和Class.getDeclaredMethod(String name, Class<?>... parameterTypes)方法可以获取类中的指定方法。
    调用invoke(Object, Object...)可以调用该方法, 如,Class.getMethod("run",int.class).invoke(obj,10)

    通过反射越过泛型检查

    泛型只在编译期有效,在运行期会被擦除掉。

    ArrayList<Integer>的一个对象list中,添加一个字符串数据。

    ArrayList<Integer> list = new ArrayList<>();
    list.add(100);
    list.add(200);
    Class clazz = Class.forName("java.util.ArrayList"); //获取字节码对象
    Method m = clazz.getMethod("add", Object.class); //获得add(E e)方法
    m.invoke(list, "abc"); // invoke(obj, Obj...args)
    System.out.println(list);
    

    动态代理

    在Java中`java.lang.reflect`包下提供了一个`Proxy`类和一个`InvocationHandler`接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。
    

    public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

    • 利用代理可以在运行时创建一个实现了一组特定接口的新类。

    • 这种功能只有在编译时无法确定要实现哪些接口时才有必要使用。

    • 代理类能够实现指定的接口。尤其是,它具有下列方法:

      1. 指定接口的全部方法。
      2. Object类中的全部方法。
    不能在运行时直接定义上述方法的新代码。而是要提供一个调用处理器(invocationhandler)。调用处理器是实现了`InvocationHandler`接口的类对象。
    
    • InvocationHandler接口中有一个方法:
      Object invoke(Object proxy , Method method , Object[] args)
     `public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)`最终会调用`InvocationHandler`的方法`InvocationHandler Object invoke(Object proxy,Method method,Object[] args)`
    
    • 无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用参数。
      调用处理器必须给出处理调用的方式

    实现代理:

    public class TestProxy {
    
    	public static void main(String[] args) {
    		StuendImp stu = new StuendImp();
    		MyInvocationHandler myinvocationhandler =  new MyInvocationHandler(stu);
    		Student s = (Student)Proxy.newProxyInstance(stu.getClass().getClassLoader(), stu.getClass().getInterfaces(), myinvocationhandler);
    		stu.login();
    		stu.submit();
    		System.out.println("-------代理后--------");
    		s.login();
    		s.submit();
    	}
    
    }
    

    stu为被代理对象。 s为代理对象。myinvocationhandler为调用处理器

    接口:

    public interface Student {
    	public void login();
    	public void submit();
    }
    

    实现类:

    public class StuendImp implements Student {
    	@Override
    	public void login() {
    		System.out.println("登录操作");
    
    	}
    	@Override
    	public void submit() {
    		System.out.println("提交操作");
    	}
    }
    

    调用处理器:

    public class MyInvocationHandler implements InvocationHandler {
    	private Object target;
    	public MyInvocationHandler(Object object) {
    		super();
    		this.target = object;
    	}
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		System.out.println("权限校验..");
    		method.invoke(target, args);
    		System.out.println("日志记录..");
    		return null;
    	}
    
    }
    


    ——@guoyangde http://www.cnblogs.com/LittleTreasureBox/p/8904016.html

  • 相关阅读:
    20080619 SQL SERVER 输入 NULL 的快捷键
    20090406 Adobe的“此产品的许可已停止工作”错误的解决办法
    20080908 Office Powerpoint 2007 不能输入中文的解决办法
    20080831 ClearGertrude Blog Skin 's cnblogs_code class
    20080603 Facebook 平台正式开放
    20080519 安装 Microsoft SQL Server 2000 时提示 创建挂起的文件操作
    test
    Linux—fork函数学习笔记
    SOA的设计理念
    Why BCP connects to SQL Server instance which start with account of Network Service fail?
  • 原文地址:https://www.cnblogs.com/LittleTreasureBox/p/8859115.html
Copyright © 2011-2022 走看看