JAVA高级编程之类加载-反射-动态代理

Java高级编程之类加载

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

  1. 加载
    就是指将class文件读入内存,并为之创建一个Class对象。
    任何类被使用时系统都会建立一个Class对象。
  2. 连接
  3. 验证 是否有正确的内部结构,并和其他类协调一致
  4. 准备 负责为类的静态成员分配内存,并设置默认初始化值
  5. 解析 将类的二进制数据中的符号引用替换为直接引用
  6. 初始化 就是我们以前讲过的初始化步骤

类初始化时机

在以下情况下会对类进行初始化

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

类加载器

类加载器的作用

  • 负责将.class文件加载到内在中,并为之生成对应的Class对象。
  • 虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行

类加载的组成

  1. Bootstrap ClassLoader 根类加载器
  2. Extension ClassLoader 扩展类加载器
  3. Sysetm ClassLoader 系统类加载器
Bootstrap ClassLoader 根类加载器

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

Extension ClassLoader 扩展类加载器

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

Sysetm ClassLo*ader 系统类加载器

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

Java高级编程之反射

通过类加载我们拿到了class文件,接下来就是使用反射来玩。

java反射机制

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有(包括private)属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
拿到class文件就可以去用它,而不是java文件。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
总之就是通过class文件对象,去使用该文件的成员变量,成员方法,构造方法。

如何获取Class类型的对象

一个Class类包含:

  1. 成员变量 Field
  2. 构造方法 Constructor
  3. 成员方法 Method

Object类中的getClass()方法

Person p=new Person()
Class c=p.getClass()
Person p2=new Person()
Class c2=p2.getClass()
试问(c1==c2)结果是true还是false?
通过对象获取的是Class文件对象(字节码文件对象)Person就一个Class文件所以答案为true。

数据类型的静态属性class

Person.class String.class

Class类中的静态方法

//path必须写全路径
Class.forName(path);

一般到底使用谁?
A.自己玩任选一种,第二种更方便
B. 开发选第三种因为第三种是一个字符串而不是一个具体的类名,这样我们就可以把这个字符串配置到配置文件中。

通过反射获取构造方法并使用

  1. 获得字节码文件对象
    Class c =Class.forName("cn.zts.Person");

  2. 获取构造方法

     //public Constructor[] getConstructors() //获取公共的构造方法
     //public Constructor[] getDeclaredConstructors()  //获取所有的构造方法包括泛型
     //获取单个构造方法(只能获取公共的)
     // public Constructor getConstructor(Class<?>... paramterType)
     //参数表示的是:你要获取构造方法的构造参数个数及数据类型的CLass字节码文件对象.如String.class
     Constructor conn=c.getConstructor();返回的是构造方法的对象
     //通过该对象创建该类的实例。newInstance()  
     Object object=conn.newInstance(Object...initarge);
    

以上内容就相当于Person object=new Person();

暴力访问

获取私有的构造方法

Constructor con=c.getDeclaredConstructor(String.class);  

//如果直接访问会报非法的访问异常。所以此时我们需要暴力访问
con.setAccessible(true);//值为true时指示反射的对象在使用时取消java语言访问检查
Object obj=con.newInstance("zts");

通过反射获取成员变量并且使用

  1. 获得字节码文件对象
    Class c =Class.forName("cn.zts.Person");
  2. 获取成员变量
    c.getFields()//获取所有的公共成员变量
    c.getDeclaredFields()//获取所有的成员变量
    //获取单个成员变量
    Field addressField=c.getField("address");
    //通过无参构造方法创建对象
    Constructor con=c.getConstructor();
    Object obj=con.newInstance();
  3. 给成员变量赋值
    //public void set(Object obj,Object value);
    //将指定对象变量上找Field,对象表示的字段设置为指定的新值.
    addressField.set(obj,"北京");

set方法的意思就是给obj对象的addressField字段设置值为北京。

暴力访问

获取私有的成员变量
Field addressField=c.getDeclaredField("address");
//如果直接访问会报非法的访问异常。所以此时我们需要暴力访问
con.setAccessible(true);//值为true时指示反射的对象在使用时取消java语言访问检查

通过反射获取成员方法并且使用

  1. 获得字节码文件对象
    Class c =Class.forName("cn.zts.Person");
  2. 获取成员方法
    //获取所有成员方法、
    Method[] methods=c.getMethods()//获取自己包括父亲的所有公共方法。
    Method[] methods=c.getDeclaredMethods()//获取自己所有的方法
    //通过无参构造方法创建对象
    Constructor con=c.getConstructor();
    Object obj=con.newInstance();
    //获取单个方法并且使用getMethod(String name,Class<?> ...paraterType)
    Method m1=c.getMethod("show");
    //obj.m1()//错误
    //使用该方法--invoke(Object object,Object...args);
    m1.invoke(obj);//调用obj对象的m1方法。

eg:

    // 获取字节码文件对象
    Class c = Class.forName("cn.itcast_01.Person");

    // 获取所有的方法
    // Method[] methods = c.getMethods(); // 获取自己的包括父亲的公共方法
    // Method[] methods = c.getDeclaredMethods(); // 获取自己的所有的方法
    // for (Method method : methods) {
    // System.out.println(method);
    // }

    Constructor con = c.getConstructor();
    Object obj = con.newInstance();

    /*
     * Person p = new Person(); p.show();
     */

    // 获取单个方法并使用
    // public void show()
    // public Method getMethod(String name,Class<?>... parameterTypes)
    // 第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型
    Method m1 = c.getMethod("show");
    // obj.m1(); // 错误
    // public Object invoke(Object obj,Object... args)
    // 返回值是Object接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数
    m1.invoke(obj); // 调用obj对象的m1方法

    System.out.println("----------");
    // public void method(String s)
    Method m2 = c.getMethod("method", String.class);
    m2.invoke(obj, "hello");
    System.out.println("----------");

    // public String getString(String s, int i)
    Method m3 = c.getMethod("getString", String.class, int.class);
    Object objString = m3.invoke(obj, "hello", 100);
    System.out.println(objString);
    // String s = (String)m3.invoke(obj, "hello",100);
    // System.out.println(s);
    System.out.println("----------");

    // private void function()
    Method m4 = c.getDeclaredMethod("function");
    m4.setAccessible(true);
    m4.invoke(obj);

通过反射越过泛型检查

比如。我们有个集合ArrayList<Integer> list我们需要给他添加个字符串类型的数据

创建集合对象  
ArrayList<Integer> array = new ArrayList<Integer>();

    // array.add("hello");
    // array.add(10);

    Class c = array.getClass(); // 集合ArrayList的class文件对象
    Method m = c.getMethod("add", Object.class);

    m.invoke(array, "hello"); // 调用array的add方法,传入的值是hello
    m.invoke(array, "world");
    m.invoke(array, "java");

    System.out.println(array);

java高级编程之动态代理

  1. 代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。
  2. 举例:春季回家买票让人代买
  3. 动态代理:在程序运行过程中产生的这个对象

而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib

Proxy类中的方法创建动态代理类对象

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
最终会调用InvocationHandler的方法(InvocationHandler是个接口,我们需要自定义一个类实现这个接口)
InvocationHandler
Object invoke(Object proxy,Method method,Object[] args)

eg:我们在如下例子实现在增删改查中添加权限校验和日志记录用动态代理实现
  • StudentDao.java

      public interface StudentDao {
      public abstract void login();
      public abstract void regist();
      }  
    
  • StudentDaoImp.java

      public class StudentDaoImpl implements StudentDao {
    
      @Override
      public void login() {
        System.out.println("登录功能");
      }
    
      @Override
      public void regist() {
           System.out.println("注册功能");
      }
    
      }
    
  • MyInvocationHandler.java

      public class MyInvocationHandler implements InvocationHandler {
       private Object target; // 目标对象
    
       public MyInvocationHandler(Object target) {
          this.target = target;
       }
    
      @Override
      public Object invoke(Object proxy, Method method, Object[] args)
          throws Throwable {
      System.out.println("权限校验");
      Object result = method.invoke(target, args);
      System.out.println("日志记录");
      return result; // 返回的是代理对象
      }
      }
    
  • Test.java

      public class Test {
      public static void main(String[] args) {
      UserDao ud = new UserDaoImpl();
         ud.add();
         ud.delete();
         ud.update();
          ud.find();
         System.out.println("-----------");
         // 我们要创建一个动态代理对象
         // Proxy类中有一个方法可以创建动态代理对象
         // public static Object newProxyInstance(ClassLoader    loader,Class<?>[]
         // interfaces,InvocationHandler h)
        // 我准备对ud对象做一个代理对象
        MyInvocationHandler handler = new MyInvocationHandler(ud);
        UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass()
              .getClassLoader(), ud.getClass().getInterfaces(), handler);
          proxy.add();
          proxy.delete();
          proxy.update();
          proxy.find();
    
      }
      }

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 26,336评论 17 394
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 104,968评论 12 126
  • 一:java概述:1,JDK:Java Development Kit,java的开发和运行环境,java的开发工...
    ZaneInTheSun阅读 1,540评论 0 9
  • 百战程序员_ Java1573题 QQ群:561832648489034603 掌握80%年薪20万掌握50%年薪...
    Albert陈凯阅读 13,211评论 3 33
  • 命运的礼物晚一点儿,慢一点儿,波折一点儿,只是为了用心扎个漂亮的蝴蝶结。 别总抱怨自己命运多舛。 世界那么大,多的...
    G宠儿阅读 101评论 0 2