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();
    
      }
      }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,646评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,979评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,391评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,356评论 0 215
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,740评论 3 293
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,836评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,022评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,764评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,487评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,728评论 2 252
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,200评论 1 263
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,548评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,217评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,134评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,921评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,919评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,766评论 2 274

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,296评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,105评论 18 139
  • 一:java概述:1,JDK:Java Development Kit,java的开发和运行环境,java的开发工...
    ZaneInTheSun阅读 2,584评论 0 11
  • 命运的礼物晚一点儿,慢一点儿,波折一点儿,只是为了用心扎个漂亮的蝴蝶结。 别总抱怨自己命运多舛。 世界那么大,多的...
    G宠儿阅读 388评论 0 2
  • 【总裁的人力资源管理202】”用人五步法” "识人",人海茫茫,如何寻找到对的人是机缘也是策略。机缘是与Ta寻找的...
    季元Irene心语教练阅读 482评论 0 0