精华内容
下载资源
问答
  • Java反射 千次阅读
    2021-08-27 14:46:18

    一、什么是反射

            反射就是把Java类中的各个成员映射成一个个的Java对象。
            即在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
            对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。


            了解反射需要先了解java的虚拟机,即JVM。我们可以把它理解为一个进程,一个程序,它的作用就是跑你的代码。

    名词解释:

    內存:即JVM内存,栈、堆、方法区啥的都是JVM内存,只是人为划分
    • .class文件:就是所谓的字节码文件,这里直观些成为 .class 文件


            简单执行一段代码:
            Object o = new Object();

    对象的创建过程如下所示:

            首先JVM会启动,代码会编译成一个.class字节码文件,然后被类加载器加载进JVM的内存中,类Object加载到方法区中,创建了Object类的Class对象到堆中,注意这个Class对象不是new出来的对象,而是类的类型对象。每个类只有一个Class对象,作为方法区类的数据结构的接口。jvm创建对象前,会先检查类是否加载,寻找类对应的class对象。若加载好,则为你的对象分配内存,初始化代码:new Object()

            为什么要讲这个呢?因为要理解反射必须知道它在什么场景下使用。
            Object对象是我们自己通过new出来的,是写死固定的代码放在JVM上跑的。假如,服务器在某个时刻遇到某个请求要调用其他的某个类,JVM中并没有加载这个类,就需要停止服务器,再写一段代码,重新启动服务器,特别麻烦。
            我们有时需要动态的加载一些运行时才需要的一些类,用不到时,不需要加载到JVM。
            很多项目的数据库,有时候需要用mysql,有时候需要用oracle,这就需要动态的加载对应的数据库驱动类。此时就可以使用反射来操作。
            有以下两个类:com.java.dbtest.mysqlConnectioncom.java.dbtest.oracleConnection在我们的项目中需要使用。程序可以写得比较动态化,通过
    Class tc = Class.forName("com.java.dbtest.TestConnection");即通过类的全类名让JVM在服务器中找到并加载这个类。如果想使用orcale,可以增加一个properties配置,获取该properties的配置信息,把orcale类的全类名当做参数传进去即可。

    二、反射机制原理

    • 反射机制允许程序在执行期借助于 ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等等),井能操作对象的属性及方法。反射在设计模式和框架底层都会用到
    • 加載完类之后,在堆中就产生了ー个Cass类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为反射。

    三、Java反射机制可以哪些

    • 在运行时判断任意一个对象所属的类
    • 在运行时构造任意一个类的对象
    • 在运行时得到任意一个类所具有的成员变量和方法
    • 在运行时调用任意一个对象的成员变量和方法
    • 生成动态代理

    四、反射相关的主要类

    Java反射机制的实现要借助以下四个类:

    • java.lang.Class:代表一个类, Class对象表示某个类加載后在堆中的对象
    • java. lang.reflec.Method:代表类的方法,Method对象表示某个类的方法
    • java.lng.reflect.Field:代表类的成员变量,Field对象表示,某个类的成员变量
    • java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器

    五、如何使用反射

    (1)实例化对象

    • 通过Object类的getClass方法获得(基本不用)
    package com.qinxue.demo;
    class Person {}
    public class TestDemo {
        public static void main(String[] args) throwsException {
            Person per = newPerson() ; // 正着操作
            Class<?> cls = per.getClass() ; // 取得Class对象
            System.out.println(cls.getName()); // 反着来
        }
    }
    
    • 使用:"类.class"方式取得
    package com.qinxue.demo;
    class Person {}
    public class TestDemo { 
        public static void main(String[] args) throwsException {
            Class<?> cls = Person.class; // 取得Class对象
            System.out.println(cls.getName()); // 反着来
        }
    }
    
    • 使用Class类内部定义的static forName( )方法
    package com.qinxue.demo;
    class Person {}
    public class TestDemo {
        public static void main(String[] args) throwsException {
            Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class对象
            System.out.println(cls.getName()); // 反着来
        }
    }
    

    (2)简单应用

            传统的工厂模式代码

    apackage com.qinxue.demo;
    interface Fruit {
        public void eat();
    }
    class Apple implements Fruit{
        public void eat() {
            System.out.println("吃苹果。");
        }
    }
    class Factory {
        public static Fruit getInstance (String className){
            if ("apple".equals(className)) {
                 returnnewApple();
             }
             return null;
         }
    }
    public class Factory Demo {
        public static void main(String[]args){
            Fruit f = Factory.getInstance("apple");
            f.eat();
         }
    }
    

            工厂设计模式之中有一个最大的问题:
            如果现在接口的子类增加了,那么工厂类肯定需要修改,这是它所面临的最大问题,而这个最大问题造成的关键性的病因是new,那么如果说现在不使用关键字new了,变为了反射机制呢?
    反射机制实例化对象的时候实际上只需要“包.类”就可以,于是根据此操作,修改工厂设计模式。

    package com.qinxue.demo;
    interface Fruit {
        public void eat();
    }
    class Apple implements Fruit {
        public void eat() {
            System.out.println("吃苹果。");
        }
    }
    class Orange implements Fruit {
        public void eat() {
            System.out.println("吃橘子。");
        }
    }
    class Factory {
        public static Fruit getInstance(String className) {
            Fruit f = null;
            try {
                f = (Fruit) Class.forName(className).newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return f;
        }
    }
    public class Factory Demo {
        public static void main(String[] args) {
            Fruit f = Factory.getInstance("cn.mldn.demo.Orange");
            f.eat();
        }
    }
    

            这个时候即使增加了接口的子类,工厂类照样可以完成对象的实例化操作,这个才是真正的工厂类,可以应对于所有的变化。
            如果单独从开发角度而言,与开发者关系不大,但是对于日后学习的一些框架技术这个就是它实现的命脉,在日后的程序开发上,如果发现操作的过程之中需要传递了一个完整的“包.类”名称的时候几乎都是反射机制作用。

    六、反射优化

    (1)关闭访问检查

    • Method和 Field、 Constructor对象都有 setAccessible( )方法
    • setAccessible作用是启动和禁用访问安全检查的开关
    • 参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率。参数值为 false则表示反射的对象执行访问检查

    七、获取Class对象

    • 已知一个类的全类名,且该类在类路径下,可通过 Class:类的静态方法forname0获取,可能抛出 Classnotfoundexception,实例:
      Class cls1=Class forname("java. lang Cat");多用于配置文件,读取类全路径,加载类。
    • 若已知具体的类,通过类的 class获取,该方式最为安全可靠,程序性能最高实例: Class cls2=Cat. class; 多用于参数传递,比如通过反射得到对应构造器对象。
    • 已知某个类的实例,调用该实例的 getclass(0方法获取Cass对象,实例:Class cla2=对象。 getclass( )。应用场景:通过创建好的对象,获取 Class对象。
    • 其他方式
      Classloader cl=car.getclass().getclassloader();
      Class clazz4=cl. loadclass(“类的全类名”);
    • 基本数据(int,char, boolean, float double,byte,long, short)按如下方式得到Class类
      Class cls = 基本数据类型.class
    • 基本数据类型对应的包装类,可以通过.type得到 Class类对象
      Class cls = 包装类.TYPE

    八、类的加载过程

    在这里插入图片描述
    在这里插入图片描述

    • 加载阶段
              JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,井生成一个代表该类的java.lang.Class对象
    • 连接阶段
      1、验证
      目的是为了确保 Class文件的字节流中包含的信息符合当前虚拟机的要求,井且不会危害虚拟机自身的安全
      包括:文件格式验证(是否以魔数 oxcafebabe开头)、元数据验证、字节码验证和符号引用验证
      可以考虑使用 -Xverify:none参数来关闭大部分的类验证措施,缩短虚拟机类加载
      2、准备
              JVM会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始值如0、0L、null、 false等)。这些变量所使用的内存都将在方法区中进行分配

    3、解析
    符号引用替换为直接引用
    (1)初始化阶段
    到初始化阶段,オ真正开始执行类中定义的Java程序代码,此阶段是执行< clinit>( )方法的过程( )方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。
    虚拟机会保证一个类的( )方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类。那么只会有一个线程去执行这个类的( )方法,其他线程都需要阻塞等待,直到活动线程执行( )方法完毕。

    更多相关内容
  • java反射知识点详细思维导图.xmind
  • 本篇内容主要给大家详细讲解了java反射获取方法以及调用方法,需要的朋友参考学习一下吧。
  • 主要介绍了Java反射如何修改private final成员变量值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 主要介绍了Java反射机制及Method.invoke详解,本文讲解了JAVA反射机制、得到某个对象的属性、得到某个类的静态属性、执行某对象的方法、执行某个类的静态方法等内容,需要的朋友可以参考下
  • 主要介绍了Java反射在实际工作中的应用笔记,具有一定借鉴价值,需要的朋友可以参考下。
  • 主要介绍了Java 反射和反射的应用场景的相关资料,帮助大家更好的理解和学习Java反射的相关知识,感兴趣的朋友可以了解下
  • Java反射获取私有构造函数、属性、方法
  • 利用反射,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个属性和方法。本源码示例了如何通过反射获取一个类的属性和方法,如何通过反射调用对象的属性和方法。
  • 文章目录一、Java反射定义二、Java反射机制实现1、Class对象获取2、获取class对象的摘要信息3、获取class对象的属性、方法、构造函数等三、反射的应用场景1、动态代理2、自定义注解实现日志管理 写在前面:Java反射...
  • Java反射调用工具类

    2019-04-26 01:24:07
    NULL 博文链接:https://shulinshulinzi.iteye.com/blog/2372372
  • java反射原理详解

    2017-08-10 11:32:26
    java反射原理详解
  • Java反射机制详解

    2020-09-03 01:54:51
    主要介绍了Java反射机制,首先简单介绍了反射机制的预备知识,进一步分析了Java反射机制的原理、实现技巧与应用方法,需要的朋友可以参考下
  • Java反射案例

    2019-01-09 14:48:42
    Java反射案例,可以参考: Java反射机制 - 相关API:https://blog.csdn.net/niuba123456/article/details/85951088
  • Java反射机制修改私有成员变量的实现_Reflection,普通方式无法修改私有成员变量的值,但通过反射机制可以实现。
  • java反射调用封装接口

    2016-09-01 12:57:24
    万能的反射封装接口,值得下载参考,当成util
  • 利用Java的反射机制实现的万能DAO工具类,包含对应的测试代码。具体功能包括:单表查询,多表查询,模糊查询,添加,修改,删除等。利用万能DAO可以对数据库中...阅读本代码需要掌握Java反射机制以及数据库DAO类基础。
  • 主要介绍了Java 反射机制实例详解的相关资料,这里对java反射机制进行了详细的分析,需要的朋友可以参考下
  • NULL 博文链接:https://747017186.iteye.com/blog/1935184
  • java 反射 调用私有方法(有参数私有方法)获取私有属性值
  • Java反射调用方法

    2014-11-13 15:47:38
    通过Java反射调用方法,适合初级自学者,通俗易懂
  • java 反射机制

    2018-05-09 13:40:08
    java 反射机制深入理解,java 反射机制深入理解,java 反射机制深入理解,
  • java反射调用实例代码

    2014-12-04 22:58:50
    该资源包含了一个利用反射执行的一些操作,包含了动态执行构造方法,动态运行私有方法,为属性初始化等等,简单易懂。
  • Java反射技术详解

    万次阅读 多人点赞 2019-04-14 23:11:32
    相信很多人都知道反射可以说是Java中最强大的技术了,它可以做的事情太多太多,很多优秀的开源框架都是通过反射完成的,比如最初的很多注解框架,后来因为java反射影响性能,所以被运行时注解APT替代了,java反射有...

    前言

      相信很多人都知道反射可以说是Java中最强大的技术了,它可以做的事情太多太多,很多优秀的开源框架都是通过反射完成的,比如最初的很多注解框架,后来因为java反射影响性能,所以被运行时注解APT替代了,java反射有个开源框架jOOR相信很多人都用过,不过我们还是要学习反射的基础语法,这样才能自己写出优秀的框架,当然这里所讲的反射技术,是学习Android插件化技术、Hook技术等必不可少的!

    一、基本反射技术

          1.1 根据一个字符串得到一个类

            getClass方法

     String name = "Huanglinqing";
     Class c1 = name.getClass();
     System.out.println(c1.getName());
    

         打印结果如下:

        

        Class.forName

        比如我们获取java.lang.String的类名 

       String name = "java.lang.String";
       Class c1 = null;
       try {
              c1 = Class.forName(name);
              System.out.println(c1.getName());
          } catch (ClassNotFoundException e) {
      }
    

        这里也通过捕获异常,因为我们传的这个字符串可能不合法,字符串合法命名是类的命名空间和类的名称组成

        打印结果如下:

        
       我们还可以通过c1.getSuperclass()获取到他的父类

       Type属性

        基本类型都有type属性,可以得到这个基本类型的类型,比如:

    Class c1 = Boolean.TYPE;
    Class c2 = Byte.TYPE;
    Class c3 = Float.TYPE;
    Class c4 = Double.TYPE;
    
     停停停!这些东西有啥子用,如此鸡肋! 别急,一切都是为后续做准备。

    二、获取类的成员

             当类中方法定义为私有的时候我们能调用?不能!当变量是私有的时候我们能获取吗?不能!但是反射可以,比如源码中有你需要用到的方法,但是那个方法是私有的,这个时候你就可以通过反射去执行这个私有方法,并且获取私有变量。

           获取类的构造函数

           为了便于测试,我们定义一个Test类,Test类如下:(省略get和set方法)

           Test类中我们定义是三个私有变量,生成两个公有的含参构造方法和一个私有的含参构造方法以及一个公有的无参构造方法。

    public class Test {
    
        private int age;
        private String name;
        private int testint;
    
        public Test(int age) {
            this.age = age;
        }
    
        public Test(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        private Test(String name) {
            this.name = name;
        }
    
        public Test() {
        }
    

          下面我们通过反射获取这些构造方法

           获取类的所有构造方法

     Test test = new Test();
     Class c4 = test.getClass();
     Constructor[] constructors ;
     constructors = c4.getDeclaredConstructors();
    

          通过getDeclaredConstructors可以返回类的所有构造方法,返回的是一个数组因为构造方法可能不止一个,通过getModifiers可以得到构造方法的类型,getParameterTypes可以得到构造方法的所有参数,返回的是一个Class数组,所以我们如果想获取所有构造方法以及每个构造方法的参数类型,可以有如下代码:

      for (int i = 0; i < constructors.length; i++) {
            System.out.print(Modifier.toString(constructors[i].getModifiers()) + "参数:");
            Class[] parametertypes = constructors[i].getParameterTypes();
            for (int j = 0; j < parametertypes.length; j++) {
                 System.out.print(parametertypes[j].getName() + " ");
           }
          System.out.println("");
      }
    

        运行结果如下所示:

        

        这样我们就得到了类中所有构造方法和构造方法中的参数,那么我们如何获取特定的构造方法呢?

        获取类中特定的构造方法

        我们可以通过getConstructors方法获取类中 所有的public类型的构造方法,代码和上面一样就不演示了。

        我们可以通过getDeclaredConstructor()方法传参获取特定参数类型的构造方法,这里注意是getDeclaredConstructor()不是  getDeclaredConstructors() ,所以返回的是一个Class对象而不是一个Class数组。

        获取无参构造方法直接不传参数,如下所示:

       try {
              constructors = c4.getDeclaredConstructor();
              System.out.print(Modifier.toString(constructors.getModifiers()) + );
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
          }
    

        这里要进行异常捕获,因为可能不存在对应的构造方法,打印结果如下:  

        

       如果我们想获取有两个参数分别为int和String类型的构造方法,代码如下:

      Class[] p = {int.class,String.class};
      try {
           constructors = c4.getDeclaredConstructor(p);
           System.out.print(Modifier.toString(constructors.getModifiers()) + "参数:");
           Class[] parametertypes = constructors.getParameterTypes();
           for (int j = 0; j < parametertypes.length; j++) {
                System.out.print(parametertypes[j].getName() + " ");
              }
           } catch (NoSuchMethodException e) {
                e.printStackTrace();
         }
    

      这里我们同样打印出构造方法的参数:


      

      调用构造方法

       从这里开始慢慢到了关键的一步,得到类的实例,我们主要借助于newInstance方法,为了方便演示我们将测试类的两个构造方法打印出来. 

       public Test(int age, String name) {
            this.age = age;
            this.name = name;
            System.out.println("hello" + name + "i am" + age);
        }
    
    
        private Test(String name) {
            this.name = name;
            System.out.println("My Name is" +
                    name);
        }
    

       我们先来调用public的方法,如下所示:

     Class[] p = {int.class,String.class};
     constructors = c4.getDeclaredConstructor(p);
     constructors.newInstance(24,"HuangLinqing");
    

     运行打印结果如下:


      

     那么调用私有构造方法呢,和上面一样,只是我们要设置constructors.setAccessible(true);代码如下:

      Class[] p = {String.class};
      constructors = c4.getDeclaredConstructor(p);
      constructors.setAccessible(true);
      constructors.newInstance("HuangLinqing");
    

      打印结果如下:


       

    调用类的私有方法

      如何调用类中的私有方法呢,我们先在测试类中编写一个测试的私有方法 如下:

      private void welcome(String tips){
            System.out.println(tips);
        }
    

      我们知道如果我们要正常的调用类的方法都是通过类.方法调用,所以我们调用私有方法也需要得到类的实例,而我们上面newInstace已经得到了类的实例,这样就好办了。

       Class[] p4 = {String.class};
       Method method = c4.getDeclaredMethod("welcome",p4);
       method.setAccessible(true);
    

       我们首先通过 getDeclaredMethod方法获取到这个私有方法,第一个参数是方法名,第二个参数是参数类型

       然后通过invoke方法执行,invoke需要两个参数一个是类的实例,一个是方法参数。

         Class[] p4 = {String.class};
         Method method = c4.getDeclaredMethod("welcome",p4);
         method.setAccessible(true);
         Object arg1s[] = {"欢迎关注代码男人技术公众号"};
         method.invoke(test,arg1s);
    

         test类的实例当不能new 获取的时候我们也可以通过反射获取,就是上面的newInstance方法。打印结果如下:


        

     获取类的私有字段并修改值

      看到这里你可能会说,有了set方法,什么私有不私有,test.set不就可以了,但是这里要注意我们是没有办法得到这个类的实例的,要不然都可以得到实例就没有反射一说了。我们在通过反射得到类的实例之后先获取字段:

    Field field = c4.getDeclaredField("name");
    field.setAccessible(true);
    field.set(o,"代码男人");
    

       o是我们上面通过反射构造方法获取的实例,  打印field.get(o).toString()的值如下:
      

       

       不过要注意的是我们修改了name的值只对当前的实例对象有效。

       Java的基本反射语法就是这样了,欢迎加入技术群一起探讨!

      最后反射封装类如下:

    package jnidemo.hlq.com.hookdemo;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    /**
     * @author Huanglinqing
     * @date 2019/4/28
     */
    
    public class Reflex {
    
        /**
         * 获取无参构造函数
         * @param className
         * @return
         */
        public static Object createObject(String className) {
            Class[] pareTyples = new Class[]{};
            Object[] pareVaules = new Object[]{};
    
            try {
                Class r = Class.forName(className);
                return createObject(r, pareTyples, pareVaules);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
        /**
         * 获取无参构造方法
         * @param clazz
         * @return
         */
        public static Object createObject(Class clazz) {
            Class[] pareTyple = new Class[]{};
            Object[] pareVaules = new Object[]{};
    
            return createObject(clazz, pareTyple, pareVaules);
        }
    
        /**
         * 获取一个参数的构造函数  已知className
         *
         * @param className
         * @param pareTyple
         * @param pareVaule
         * @return
         */
        public static Object createObject(String className, Class pareTyple, Object pareVaule) {
    
            Class[] pareTyples = new Class[]{pareTyple};
            Object[] pareVaules = new Object[]{pareVaule};
    
            try {
                Class r = Class.forName(className);
                return createObject(r, pareTyples, pareVaules);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
    
        /**
         * 获取单个参数的构造方法 已知类
         *
         * @param clazz
         * @param pareTyple
         * @param pareVaule
         * @return
         */
        public static Object createObject(Class clazz, Class pareTyple, Object pareVaule) {
            Class[] pareTyples = new Class[]{pareTyple};
            Object[] pareVaules = new Object[]{pareVaule};
    
            return createObject(clazz, pareTyples, pareVaules);
        }
    
        /**
         * 获取多个参数的构造方法 已知className
         * @param className
         * @param pareTyples
         * @param pareVaules
         * @return
         */
        public static Object createObject(String className, Class[] pareTyples, Object[] pareVaules) {
            try {
                Class r = Class.forName(className);
                return createObject(r, pareTyples, pareVaules);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
    
        /**
         * 获取构造方法
         *
         * @param clazz
         * @param pareTyples
         * @param pareVaules
         * @return
         */
        public static Object createObject(Class clazz, Class[] pareTyples, Object[] pareVaules) {
            try {
                Constructor ctor = clazz.getDeclaredConstructor(pareTyples);
                ctor.setAccessible(true);
                return ctor.newInstance(pareVaules);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
    
        /**
         * 获取多个参数的方法
         * @param obj
         * @param methodName
         * @param pareTyples
         * @param pareVaules
         * @return
         */
        public static Object invokeInstanceMethod(Object obj, String methodName, Class[] pareTyples, Object[] pareVaules) {
            if (obj == null) {
                return null;
            }
    
            try {
                //调用一个private方法 //在指定类中获取指定的方法
                Method method = obj.getClass().getDeclaredMethod(methodName, pareTyples);
                method.setAccessible(true);
                return method.invoke(obj, pareVaules);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
        /**
         * 获取一个参数的方法
         * @param obj
         * @param methodName
         * @param pareTyple
         * @param pareVaule
         * @return
         */
        public static Object invokeInstanceMethod(Object obj, String methodName, Class pareTyple, Object pareVaule) {
            Class[] pareTyples = {pareTyple};
            Object[] pareVaules = {pareVaule};
    
            return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);
        }
    
        /**
         * 获取无参方法
         * @param obj
         * @param methodName
         * @return
         */
        public static Object invokeInstanceMethod(Object obj, String methodName) {
            Class[] pareTyples = new Class[]{};
            Object[] pareVaules = new Object[]{};
    
            return invokeInstanceMethod(obj, methodName, pareTyples, pareVaules);
        }
    
    
        /**
         * 无参静态方法
         * @param className
         * @param method_name
         * @return
         */
        public static Object invokeStaticMethod(String className, String method_name) {
            Class[] pareTyples = new Class[]{};
            Object[] pareVaules = new Object[]{};
    
            return invokeStaticMethod(className, method_name, pareTyples, pareVaules);
        }
    
        /**
         * 获取一个参数的静态方法
         * @param className
         * @param method_name
         * @param pareTyple
         * @param pareVaule
         * @return
         */
        public static Object invokeStaticMethod(String className, String method_name, Class pareTyple, Object pareVaule) {
            Class[] pareTyples = new Class[]{pareTyple};
            Object[] pareVaules = new Object[]{pareVaule};
    
            return invokeStaticMethod(className, method_name, pareTyples, pareVaules);
        }
    
        /**
         * 获取多个参数的静态方法
         * @param className
         * @param method_name
         * @param pareTyples
         * @param pareVaules
         * @return
         */
        public static Object invokeStaticMethod(String className, String method_name, Class[] pareTyples, Object[] pareVaules) {
            try {
                Class obj_class = Class.forName(className);
                return invokeStaticMethod(obj_class, method_name, pareTyples, pareVaules);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
        /**
         * 无参静态方法
         * @param method_name
         * @return
         */
        public static Object invokeStaticMethod(Class clazz, String method_name) {
            Class[] pareTyples = new Class[]{};
            Object[] pareVaules = new Object[]{};
    
            return invokeStaticMethod(clazz, method_name, pareTyples, pareVaules);
        }
    
        /**
         * 一个参数静态方法
         * @param clazz
         * @param method_name
         * @param classType
         * @param pareVaule
         * @return
         */
        public static Object invokeStaticMethod(Class clazz, String method_name, Class classType, Object pareVaule) {
            Class[] classTypes = new Class[]{classType};
            Object[] pareVaules = new Object[]{pareVaule};
    
            return invokeStaticMethod(clazz, method_name, classTypes, pareVaules);
        }
    
        /**
         * 多个参数的静态方法
         * @param clazz
         * @param method_name
         * @param pareTyples
         * @param pareVaules
         * @return
         */
        public static Object invokeStaticMethod(Class clazz, String method_name, Class[] pareTyples, Object[] pareVaules) {
            try {
                Method method = clazz.getDeclaredMethod(method_name, pareTyples);
                method.setAccessible(true);
                return method.invoke(null, pareVaules);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
    
        public static Object getFieldObject(String className, Object obj, String filedName) {
            try {
                Class obj_class = Class.forName(className);
                return getFieldObject(obj_class, obj, filedName);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public static Object getFieldObject(Class clazz, Object obj, String filedName) {
            try {
                Field field = clazz.getDeclaredField(filedName);
                field.setAccessible(true);
                return field.get(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            return null;
        }
    
    
        public static void setFieldObject(Class clazz, Object obj, String filedName, Object filedVaule) {
            try {
                Field field = clazz.getDeclaredField(filedName);
                field.setAccessible(true);
                field.set(obj, filedVaule);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public static void setFieldObject(String className, Object obj, String filedName, Object filedVaule) {
            try {
                Class obj_class = Class.forName(className);
                setFieldObject(obj_class, obj, filedName, filedVaule);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
    
        public static Object getStaticFieldObject(String className, String filedName) {
            return getFieldObject(className, null, filedName);
        }
    
        public static Object getStaticFieldObject(Class clazz, String filedName) {
            return getFieldObject(clazz, null, filedName);
        }
    
        public static void setStaticFieldObject(String classname, String filedName, Object filedVaule) {
            setFieldObject(classname, null, filedName, filedVaule);
        }
    
        public static void setStaticFieldObject(Class clazz, String filedName, Object filedVaule) {
            setFieldObject(clazz, null, filedName, filedVaule);
        }
    }
    
     
    展开全文
  • 思考:在讲反射之前,先思考一个问题,java中如何创建一个对象,有哪几种方式? Java中创建对象大概有这几种方式: 1、使用new关键字:这是我们最常见的也是最简单的创建对象的方式 2、使用Clone的方法:无论何时...

    视频功能审核通过了,可以看视频啦!

    建议一定要花十几分钟时间把视频看完,然后再结合博客里的内容来理解

    看完后,相信对你了解Java中的反射一定会有所帮助!

    注意:因为网络原因,视频前一两分钟可能会比较模糊,过一会儿就好了

    记得点关注啊,视频里的wx二维码失效了,wx搜索:“聊5毛钱的java 或 扫码关注公众号,欢迎一起学习交流

    快扫码关注啦!关注可领取博主的Java学习视频+资料,保证都是干货

    用最通俗易懂的话来说一说Java中的反射机制

    思考:在讲反射之前,先思考一个问题,java中如何创建一个对象,有哪几种方式?

    Java中创建对象大概有这几种方式:

    1、使用new关键字:这是我们最常见的也是最简单的创建对象的方式

    2、使用Clone的方法:无论何时我们调用一个对象的clone方法,JVM就会创建一个新的对象,将前面的对象的内容全部拷贝进去

    3、使用反序列化:当我们序列化和反序列化一个对象,JVM会给我们创建一个单独的对象

    上边是Java中常见的创建对象的三种方式,其实除了上边的三种还有另外一种方式,就是接下来我们要讨论的 “反射”

    1、反射概述

    1.1什么是反射

    反射就是把Java类中的各个组成部分进行解剖,并映射成一个个的Java对象,拿到这些对象后可以做一些事情。

    既然说反射是解剖Java类中的各个组成部分,所以说咱们得知道一个类中有哪些部分?

    例如,一个类有:构造方法,方法,成员变量(字段),等信息,利用反射技术咱们可以把这些组成部分映射成一个个对象

    拿到映射后的构造方法,可以用它来生成对象;拿到映射后的方法,可以调用它来执行对应的方法;拿到映射后的字段,可以用它来获取或改变对应字段的值;

    1.2、反射能干什么

    说完反射的概念后,咱们说一下反射能干什么?

    一般来说反射是用来做框架的,或者说可以做一些抽象度比较高的底层代码,反射在日常的开发中用到的不多,但是咱们还必须搞懂它,因为搞懂了反射以后,可以帮助咱们理解框架的一些原理。所以说有一句很经典的话:反射是框架设计的灵魂。现在说完这个可能还不太能理解,不急,等下说完一个快速入门的例子后,应该会稍微有点感觉

    1.3、怎么得到想反射的类

    刚才已经说过,反射是对一个类进行解剖,想解剖一个东西,前提是首先你得拿到这个东西,那么怎么得到咱们想解剖的类呢?

    首先大家要明白一点,咱们写的代码是存储在后缀名是 .java的文件里的,但是它会被编译,最终真正去执行的是编译后的 .class文件。Java是面向对象的语言,一切皆对象,所以java认为 这些编译后的 class文件,这种事物也是一种对象,它也给抽象成了一种类,这个类就是Class,大家可以去AIP里看一下这个类

    所以拿到这个类后,就相当于拿到了咱们想解剖的类,那怎么拿到这个类?

    看API文档后,有一个方法forName(String className); 而且是一个静态的方法,这样咱们就可以得到想反射的类了

    到这里,看Class clazz = Class.forName("com.cj.test.Person");这个应该有点感觉了吧

    Class.forName("com.cj.test.Person");因为这个方法里接收的是个字符串,字符串的话,我们就可以写在配置文件里,然后利用反射生成我们需要的对象,这才是我们想要的。很多框架里都有类似的配置

    2、解剖类

    我们知道一个类里一般有构造函数、方法、成员变量(字段/属性)这三部分组成

    翻阅API文档,可以看到

    Class对象提供了如下常用方法:

    public Constructor getConstructor(Class<?>…parameterTypes)

    public Method getMethod(String name,Class<?>… parameterTypes)

    public Field getField(String name)

    public Constructor getDeclaredConstructor(Class<?>…parameterTypes)

    public Method getDeclaredMethod(String name,Class<?>… parameterTypes)

    public Field getDeclaredField(String name)

    这些方法分别用于帮咱们从类中解剖出构造函数、方法和成员变量(属性)。

    然后把解剖出来的部分,分别用Constructor、Method、Field对象表示。

    2.1反射构造方法

    2.1.1反射无参的构造函数

    可以看到 默认的无参构造方法执行了

    从上边的例子看出,要想反射,首先第一步就是得到类的字节码

    所以简单说一下得到类的字节码的几种方式

    (1)、Class.forName("com.cj.test.Person"); 这就是上边我们用的方式

    (2)、对象.getClass();

    (3)、类名.class;

    2.1.2反射“一个参数”的构造函数

    2.1.3反射“多个参数”的构造函数

    2.1.4反射“私有”的构造函数

    注意:在反射私有的构造函数时,用普通的clazz.getConstructor()会报错,因为它是私有的,所以提供了专门反射私有构造函数的方法 clazz.getDeclaredConstructor(int.class);//读取私有的构造函数,用这个方法读取完还需要设置一下暴力反射才可以

    c.setAccessible(true);//暴力反射

    2.1.5反射得到类中所有的构造函数

    2.2反射类中的方法

    package com.cj.test;
    
    import java.util.Date;
    
    public class Person {
    	
    	public Person(){
    		System.out.println("默认的无参构造方法执行了");
    	}
    
    	public Person(String name){
    		System.out.println("姓名:"+name);
    	}
    	
    	public Person(String name,int age){
    		System.out.println(name+"="+age);
    	}
    	
    	private Person(int age){
    		System.out.println("年龄:"+age);
    	}
    	
    	public void m1() {
    		System.out.println("m1");
    	}
    	
    	public void m2(String name) {
    		System.out.println(name);
    	}
    	
    	public String m3(String name,int age) {
    		System.out.println(name+":"+age);
    		return "aaa";
    	}
    	
    	private void m4(Date d) {
    		System.out.println(d);
    	}
    	
    	public static void m5() {
    		System.out.println("m5");
    	}
    	
    	public static void m6(String[] strs) {
    		System.out.println(strs.length);
    	}
    
            public static void main(String[] args) {
    		System.out.println("main");
    	}
    
    }
    
    
    
    
    
    package com.cj.test;
    
    import java.lang.reflect.Method;
    import java.util.Date;
    import org.junit.Test;
    
    public class Demo2 {
    
    	@Test//public void m1()
    	public void test1() throws Exception{
    		Class clazz = Class.forName("com.cj.test.Person");
    		Person p = (Person)clazz.newInstance();
    		Method m = clazz.getMethod("m1", null);
    		m.invoke(p, null);
    	}
    	@Test//public void m2(String name)
    	public void test2() throws Exception{
    		Class clazz = Person.class;
    		Person p = (Person) clazz.newInstance();
    		Method m = clazz.getMethod("m2", String.class);
    		m.invoke(p, "张三");
    	}
    	@Test//public String m3(String name,int age)
    	public void test3() throws Exception{
    		Class clazz = Person.class;
    		Person p = (Person) clazz.newInstance();
    		Method m = clazz.getMethod("m3", String.class,int.class);
    		String returnValue = (String)m.invoke(p, "张三",23);
    		System.out.println(returnValue);
    	}
    	@Test//private void m4(Date d)
    	public void test4() throws Exception{
    		Class clazz = Person.class;
    		Person p = (Person) clazz.newInstance();
    		Method m = clazz.getDeclaredMethod("m4", Date.class);
    		m.setAccessible(true);
    		m.invoke(p,new Date());
    	}
    	@Test//public static void m5()
    	public void test5() throws Exception{
    		Class clazz = Person.class;
    		Method m = clazz.getMethod("m5", null);
    		m.invoke(null,null);
    	}
    	@Test//private static void m6(String[] strs)
    	public void test6() throws Exception{
    		Class clazz = Person.class;
    		Method m = clazz.getDeclaredMethod("m6",String[].class);
    		m.setAccessible(true);
    		m.invoke(null,(Object)new String[]{"a","b"});
    	}
    	@Test
    	public void test7() throws Exception{
    		Class clazz = Person.class;
    		Method m = clazz.getMethod("main",String[].class);
    		m.invoke(null,new Object[]{new String[]{"a","b"}});
    	}
    }
    

    *****注意:看下上边代码里test6和test7的invoke方法里传的参数和其他的有点不一样

    这是因为 jdk1.4和jdk1.5处理invoke方法有区别

    1.5:public Object invoke(Object obj,Object…args)

    1.4:public Object invoke(Object obj,Object[] args)

    由于JDK1.4和1.5对invoke方法的处理有区别, 所以在反射类似于main(String[] args) 这种参数是数组的方法时需要特殊处理

    启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数个数不对的问题。

    上述问题的解决方法:

    (1)mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});

    这种方式,由于你传的是一个数组的参数,所以为了向下兼容1.4的语法,javac遇到数组会给你拆开成多个参数,但是由于咱们这个Object[ ] 数组里只有一个元素值,所以就算它拆也没关系

    (2)mainMethod.invoke(null,(Object)new String[]{"xxx"});

    这种方式相当于你传的参数是一个对象,而不是数组,所以就算是按照1.4的语法它也不会拆,所以问题搞定

    编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了

    对上边的描述进行一下总结:在反射方法时,如果方法的参数是一个数组,考虑到向下兼容问题,会按照JDK1.4的语法来对待(JVM会把传递的数组参数拆开,拆开就会报参数的个数不匹配的错误)
    解决办法:防止JVM拆开你的数组
        方式一:把数组看做是一个Object对象
        方式二:重新构建一个Object数组,那个参数数组作为唯一的元素存在。

    2.3反射类中的属性字段

    package com.cj.test;
    
    import java.util.Date;
    
    public class Person {
    	
    	public String name="李四";
    	private int age = 18;
    	public static Date time;
    	
    	public int getAge() {
    		return age;
    	}
    	
    	public Person(){
    		System.out.println("默认的无参构造方法执行了");
    	}
    
    	public Person(String name){
    		System.out.println("姓名:"+name);
    	}
    	
    	public Person(String name,int age){
    		System.out.println(name+"="+age);
    	}
    	
    	private Person(int age){
    		System.out.println("年龄:"+age);
    	}
    	
    	public void m1() {
    		System.out.println("m1");
    	}
    	
    	public void m2(String name) {
    		System.out.println(name);
    	}
    	
    	public String m3(String name,int age) {
    		System.out.println(name+":"+age);
    		return "aaa";
    	}
    	
    	private void m4(Date d) {
    		System.out.println(d);
    	}
    	
    	public static void m5() {
    		System.out.println("m5");
    	}
    	
    	public static void m6(String[] strs) {
    		System.out.println(strs.length);
    	}
    	
    	public static void main(String[] args) {
    		System.out.println("main");
    	}
    	
    }
    
    
    
    
    
    package com.cj.test;
    
    import java.lang.reflect.Field;
    import java.util.Date;
    import org.junit.Test;
    
    public class Demo3 {
    	//public String name="李四";
    	@Test
    	public void test1() throws Exception{
    		Class clazz = Person.class;
    		Person p = (Person)clazz.newInstance();
    		Field f = clazz.getField("name");
    		String s = (String)f.get(p);
    		System.out.println(s);
    		
    		//更改name的值
    		f.set(p, "王六");
    		System.out.println(p.name);
    	}
    	@Test//private int age = 18;
    	public void test2() throws Exception{
    		Class clazz = Person.class;
    		Person p = (Person)clazz.newInstance();
    		Field f = clazz.getDeclaredField("age");
    		f.setAccessible(true);
    		int age = (Integer)f.get(p);
    		System.out.println(age);
    		
    		f.set(p, 28);
    		age = (Integer)f.get(p);
    		System.out.println(age);
    	}
    	@Test//public static Date time;
    	public void test3() throws Exception{
    		Class clazz = Person.class;
    		Field f = clazz.getField("time");
    		f.set(null, new Date());
    		System.out.println(Person.time);
    	}
    }
    

    以上就是自己对Java中反射的一些学习总结,欢迎大家留言一起学习、讨论

    看完上边有关反射的东西, 对常用框架里的配置文件是不是有点思路了

    上边是Spring配置文件里的常见的bean配置,这看起来是不是可以用反射很轻易的就可以实现:解析xml然后把xml里的内容作为参数,利用反射创建对象。

    拓展:

    1、除了上述的Spring配置文件里会用到反射生成bean对象,其他常见的MVC框架,比如Struts2、SpringMVC等等一些框架里还有很多地方都会用到反射。

    前端夜页面录入的一些信息通过表单或者其他形式传入后端,后端框架就可以利用反射生成对应的对象,并利用反射操作它的set、get方法把前端传来的信息封装到对象里。

    感兴趣的话可以看下这篇:利用Java反射模拟一个Struts2框架 Struts2主要核心设计 手动实现Struts2核心代码,这篇里边包含了XML解析、反射的东西,模拟了一个Struts2的核心代码

    2、框架的代码里经常需要利用反射来操作对象的set、get方法,来把程序的数据封装到Java对象中去。

    如果每次都使用反射来操作对象的set、get方法进行设置值和取值的话,过于麻烦,所以JDK里提供了一套API,专门用于操作Java对象的属性(set/get方法),这就是内省

    关于内省相关的内容我也整理了一篇文章,感兴趣可以点击:Java反射——内省(Introspector)以及BeanUtils内省框架

    3、平常用到的框架,除了配置文件的形式,现在很多都使用了注解的形式。

    其实注解也和反射息息相关:使用反射也能轻而易举的拿到类、字段、方法上的注解,然后编写注解解析器对这些注解进行解析,做一些相关的处理

    所以说不管是配置文件还是注解的形式,它们都和反射有关。注解和自定义注解的内容,最近也抽时间大概整理了一下,感兴趣的小可爱可以点击了解:Java中的注解以及自定义注解

    写在最后:反射是框架的灵魂,具备反射知识和思想,是看懂框架的基础。希望看完文章后对你能有所帮助。

    铁子们,如果觉得文章对你有所帮助,可以点关注,点赞

    也可以关注下公众号:扫码 或 wx搜索:“聊5毛钱的java ,欢迎一起学习交流,关注公众号可领取博主的Java学习视频+资料,保证都是干货

    3Q~

    纯手敲原创不易,如果觉得对你有帮助,可以打赏支持一下,哈哈,感谢~

               

    展开全文
  • java反射机制和动态代理的原理,熟悉反射机制和动态代理
  • java反射机制

    千次阅读 2022-03-16 11:24:24
    java反射机制​​ ​​ 一.概念 自己理解:java反射机制可通过类的全路径名对类进行动态加载实例化,获取类信息及类里面的成员变量,方法,改造方法等;加载完类之后,在堆中就产生了一个Clas类型的对象(一个类只有...

    java反射机制​​

    ​​在这里插入图片描述

    一.概念

    自己理解:java反射机制可通过类的全路径名对类进行动态加载实例化,获取类信息及类里面的成员变量,方法,改造方法等;加载完类之后,在堆中就产生了一个Clas类型的对象(一个类只有一个对象),这个对象包含了类的完整结构信息,通过这个对象可以得到类的结构(方法,构造方法,属性,结构)。
    网上解释:
    (1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
    (2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

    二.使用

    1.在运行时判断任意一个对象所属的类
    2.在运行时构造任意一个类的对象
    3.在运行时得到任意一个类所具有的成员变量和方法
    4.在运行时调用任意一个对象的成员变量和方法
    5.生成动态代理

     //1.使用Properties
            Properties properties = new Properties();
            properties.load(new BufferedInputStream(new FileInputStream("src/main/resources/test.properties")));
            String classPath = properties.get("classfullpath").toString();
            String methodName = properties.get("method").toString();
            System.out.println(classPath);
            System.out.println(methodName);
    
            //2.使用反射机制解决
            //(1)加载类,返回class类型的对象cls
            Class cls = Class.forName(classPath);
            //1.9之后不能用了,因为只能调用无参构造,没办法构
            Object objPerson1 =instanceObj.newInstance();
            //1.9之后采用这个来实例化对象
            //(2)通过cls得到你加载的类的对象实例
            Object o=cls.getDeclaredConstructor().newInstance();
            System.out.println("o的运行类型=" + o.getClass());
            //(3)通过cls得到你加载的类里面的方法
            //即:在反射中,可以把方法视为对象(万物皆对象)
            Method method1 = cls.getMethod(methodName);
            //(4)通过method1调用方法:即通过方法对象来实现调用方法
            method1.invoke(o);//传统方法,对象.方法(),反射机制:方法.invoke(对象)
    
            Field nameField= cls.getField("age");
            System.out.println("nameField"+nameField.get(o));
            Constructor constructors = cls.getConstructor();
            System.out.println("constructors"+constructors);
    

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/67991e27b68546c99d55b11823fd1e79.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP5aSq6ZizLno=,size_20,color_FFFFFF,t_70,g_se,x_16

    三.反射相关类

    java.lang.refiect
    java.lang.Class:代表一个类
    java.lang.refiect.Method:代表类的方法
    java.lang.refiect.Field:代表类的成员变量
    java.lang.refiect.Constructors:代表类的构造方法

     //根据类路劲获取类信息
            Class clazz = Class.forName("com.example.content.test.Student");
            //实例化对象
            Object o = clazz.getConstructor().newInstance();
            //获取方法
            Method hi = clazz.getMethod("hi");
            //获取构造方法
            Constructor constructor = clazz.getConstructor();
            //获取属性(属性需要是public的才可以,不是会提示Exception in thread "main" java.lang.NoSuchFieldException)
            Field nameField = clazz.getField("name");
            System.out.println("获取方法="+hi);
            System.out.println("获取构造方法="+constructor);
            System.out.println("获取属性="+nameField);
    

    结果:

    获取方法=public void com.example.content.test.Student.hi()
    获取构造方法=public com.example.content.test.Student()
    获取属性=public java.lang.String com.example.content.test.Student.name
    

    四.反射的优点和缺点

    缺点:耗时,效率慢

     public static void main(String[] args) throws Exception {
            m1();
            m2();
            m3();
        }
        //创痛方法调用hi
        public static void m1() {
            Student student = new Student();
            long start = System.currentTimeMillis();
            for (int i = 0; i < 100000; i++) {
                student.hi();
            }
            long end = System.currentTimeMillis();
            System.out.println("传统方法耗时" + (end - start));
        }
        //创痛方法调用hi
        public static void m2() throws Exception {
            Class clazz = Class.forName("com.example.content.test.Student");
            Object cls = clazz.getConstructor().newInstance();
            Method method = clazz.getMethod("hi");
            long start = System.currentTimeMillis();
            for (int i = 0; i < 100000; i++) {
                method.invoke(cls);
            }
            long end = System.currentTimeMillis();
            System.out.println("反射方法耗时" + (end - start));
        }
        //创痛方法调用hi
        public static void m3() throws Exception {
            Class clazz = Class.forName("com.example.content.test.Student");
            Object cls = clazz.getConstructor().newInstance();
            Method method = clazz.getMethod("hi");
            method.setAccessible(true);
            long start = System.currentTimeMillis();
            for (int i = 0; i < 100000; i++) {
                method.invoke(cls);
            }
            long end = System.currentTimeMillis();
            System.out.println("反射优化方法耗时" + (end - start));
        }
    

    输出:

    传统方法耗时3
    反射方法耗时52
    反射优化方法耗时20
    

    反射调用优化-关闭访问监测(能提升一些,一定程度上优化性能)
    具体方法:AccessibleObject
    Method method = clazz.getMethod("hi"); method.setAccessible(true);

    五.Class类分析

    类图
    1.class也是类,继承object类
    2.创建类
    (1)传统方式:
    /**
    * public Class<?> loadClass(String name) throws ClassNotFoundException {
    * return loadClass(name, false);
    * }
    */
    Student student=new Student();
    (2)java反射:
    Class aClass = Class.forName(“com.example.content.test.Student”);
    对于某个类的class类对象,在内存中只存在一份,因此类只加载一次
    (3)每个类的实例都知道他是由哪个class实例所生成
    (4)通过class对象可以完整地得到一个类的完整结构,通过一系列api
    (5)class对象放在堆空间
    (6)类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法名,变量等等)

    五.Class类的常用方法

     //通过类路径获取类
            Class aClass = Class.forName("com.example.content.test.Student");
            System.out.println(aClass);//显示cla对象,是哪个类的Class对象
            System.out.println(aClass.getClass());//运行类型
            //得到包名
            Package aPackage = aClass.getPackage();
            System.out.println(aPackage);
            //得到全类名
            String className = aClass.getName();
            System.out.println(className);
            //创建对象实例
            Student student = (Student) aClass.getConstructor().newInstance();
            System.out.println(student);
            //得到属性
            Field nameField = aClass.getField("name");
            System.out.println(nameField.get(student));
            //设置属性
            nameField.set(student, "萌萌猫");
            System.out.println(nameField.get(student));
            //得到所有属性
            Field[] fields = aClass.getFields();
            System.out.println(fields);
    

    六.获取类对象的几种方式:

    (1)Class aClass = Class.forName(“com.example.content.test.Student”);
    前提:已知类的全路径,且该类在类路径下,通过此方法获取
    应用场景:多用于配置文件,读取类全路径,加载类
    (2) Class aClass1 = Student.class();
    前提:已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高
    应用场景:多用于参数传递,比如通过反射得到对应构造器对象
    (3) Class aClass1 = student.getClass();

        //通过类文件
            Class aClass = Class.forName("com.example.content.test.Student");
            //类名.class
            Class student=Student.class;
            //类名.class
            Student student1=new Student();
            Class aClass1=student1.getClass();
            //类加载器
            ClassLoader classLoader = student.getClassLoader();//得到类加载器
            Class<?> aClass2 = classLoader.loadClass("com.example.content.test.Student");//通过类加载器得到class对象
            System.out.println("aClass="+aClass);
            System.out.println("student="+student);
            System.out.println("aClass1="+aClass1);
            System.out.println("aClass2="+aClass2);
    

    运行结果:

    aClass=class com.example.content.test.Student
    student=class com.example.content.test.Student
    aClass1=class com.example.content.test.Student
    aClass2=class com.example.content.test.Student
    

    六.Java中类变量(静态变量)和实例变量区别

    成员变量:把类内、方法体外定义的变量称为成员变量。
    Java中的成员变量分为两种:
    一是没有static修饰的,这些成员变量是对象中的成员,称为实例变量。
    二是有static修饰的,称为类变量(静态变量)。
    类变量和实例变量的区别是:
    类变量在内存中只存一份,在程序运行时,系统只为类变量分配一次内存,只进行一次的初始化。在加载类的过程中完成类变量的内存分配。
    类变量可以通过类名访问。
    实例变量是属于对象中的成员,每创建一个类的对象,就会为实例变量分配一次内存,实例变量在内存中有多个拷贝,分别属于不同对象,他们之间互不影响。
    示例:

    
    public class scope {
    	static int a;
    	int b;
    	public static void main(String[] args) {
    		// TODO 自动生成的方法存根
    		a++;
    		scope s1 = new scope();
    		s1.a++;
    		s1.b++;
    		scope s2 = new scope();
    		s2.a++;
    		s2.b++;
    		scope.a++;
    		System.out.println("a="+a);
    		System.out.println("s1.a="+s1.a);
    		System.out.println("s2.a="+s2.a);
    		System.out.println("s1.b="+s1.b);
    		System.out.println("s2.b="+s2.b);
    	}
    }
    

    运行结果为:
    a=4
    s1.a=4
    s2.a=4
    s1.b=1
    s2.b=1
    a是类变量,b是实例变量。a在内存中只有一份,a可以通过类名访问,也可以通过对象名访问。无论哪种访问形式都是对同一个a进行操作,所以连续加1后,a的值为4.
    而对于b,每次创建一个对象,内存中就有一份b的空间,在这里对象s1和s2中分别有一个变量b,对他们操作需要通过不同的对象名,所以对他们的操作互不影响。在分别加1后,都为1。

    七.类加载:

    (1) 加载过程:加载-连接(验证,准备,解析)-初始化
    验证:(验证代码是否jvm的规范,文件格式是否正确,元数据验证,字节码验证,符号引用验证)
    准备(为类中定义的变量,默认初始化并分配空间,即静态变量分配内存并设置变量的初始值,通常情况为该数据类型的“零值”。)
    代码示例:

    class a{
        //属性-成员变量-字段 类加载的链接阶段-准备 属性是如何处理
        //1.a是实例尚需经,不是静态变量,因此在准备阶段,不会分配内存
        //2.b是静态变量,在此会分配内存并赋该数据类型上午默认初始化值0,而不是1
        //3.c是static final 是常量,是不可改变的,一旦赋值就不会发生变化,此处赋值1
        public int a=1;
        //会为该属性分配内存,并赋初始化值0,1放在初始化中
        public static int b=1;  //b=0
        public final static int c=1;// c=1
    }
    

    解析(解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,此过程伴随着第二阶段验证中符号引用验证,放到jre中,类处于可运行的状态);
    初始化(真正执行类中定义的java程序代码)
    代码示例:

    在这里插入图片描述
    在这里插入图片描述
    (2)类加载的两种形式
    静态加载:依赖性太强,编译时加载相关的类,如果没有则报错
    动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,依赖性不强
    应用实例:

     public static void main(String[] args) throws Exception {
            Scanner scanner=new Scanner(System.in);
            String key = scanner.next();
            switch (key){
                case "1":
                    Student student=new Student();//静态加载(该类必须存在,不然此处声明时会报错,无法成功编译)
                    student.hi();
                    break;
                case "2":
                    //动态加载(当程序执行到这里的时候才会去检测并编译,不会影响编译结果)
                    Class aClass = Class.forName("com.example.content.test.app");
                    Object o = aClass.getConstructor().newInstance();
                    Method method = aClass.getMethod("hi");
                    method.invoke(o);
                default:
                    System.out.println("************");
    
            }
        }
    

    运行结果:

    2
    Exception in thread "main" java.lang.ClassNotFoundException: com.example.content.test.app
    	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
    	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
    	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
    	at java.base/java.lang.Class.forName0(Native Method)
    	at java.base/java.lang.Class.forName(Class.java:315)
    	at com.example.content.test.Refiection06.main(Refiection06.java:27)
    
    1
    
    Process finished with exit code 0
    

    总结:
    动态加载:程序在运行时调用相应方法,即使其他方法是错误的,程序依旧会执行。通过动态加载可以让程序的可延长行大大提升,对以后的维护和扩展有重要意义。
    静态加载:程序在编译时执行。在执行过程中加载所有可能执行到的程序。在这种加载方式下,只要加载中一个方法出错,程序就不能运行。我们一般写程序默认的是静态加载。

    八.通过反射创建对象

    1.方式一:调用类中的public修饰的无参构造器
    2.方式二:调用类中的指定构造器

    public class Refiection09 {
        public static void main(String[] args) throws Exception {
            //1.先获取user类的class对象
            Class aClass = Class.forName("com.example.content.test.user");
            //2.通过public的无参构造器创建实例
            Object o = aClass.getConstructor().newInstance();
            System.out.println(o);
            //3.通过public的有参构造器创建实例
            Object o1 = aClass.getConstructor(String.class).newInstance("李四");
            System.out.println(o1);
            //4.通过非public的有参构造器创建实例
           /* Exception in thread "main" java.lang.NoSuchMethodException: com.example.content.test.user.<init>(int, java.lang.String)*/
            //Object o3 = aClass.getConstructor(int.class, String.class).newInstance(5, "王麻子");由于该方法是私有构造方法,会报上面的异常
            Constructor declaredConstructor = aClass.getDeclaredConstructor(int.class, String.class);//他可以获取所有构造方法
            System.out.println(declaredConstructor);
            declaredConstructor.setAccessible(true);//暴破【暴力破解】,使用反射可以访问private构造器
            Object o3 = declaredConstructor.newInstance(5, "王麻子");
            System.out.println(o3);
        }
    }
    
    class user {
        private int age = 1;
        private String name = "张三";
    
        public user() {
    
        }
    
        public user(String name) {
            this.name = name;
        }
    
        private user(int age, String name) {
            this.age = age;
            tuser[age=1,name=张三}
    user[age=1,name=李四}
    private com.example.content.test.user(int,java.lang.String)
    user[age=5,name=王麻子}his.name = name;
        }
    
        public String toString() {
            return "user[age=" + age + ",name=" + name + "}";
        }
    
    
    }
    

    运行结果:

    user[age=1,name=张三}
    user[age=1,name=李四}
    private com.example.content.test.empoless(int,java.lang.String)
    user[age=5,name=王麻子}
    

    九.通过反射操作属性和方法

    代码举例:

    public class Refiection10 {
        public static void main(String[] args) throws Exception {
            //1.先获取user类的class对象
            Class aClass = Class.forName("com.example.content.test.User");
            //2.通过public的无参构造器创建实例
            Object o = aClass.getDeclaredConstructor().newInstance();
            System.out.println(o);
          /*  Exception in thread "main" java.lang.NoSuchFieldException: age
            at java.base/java.lang.Class.getField(Class.java:1999)
            at com.example.content.test.Refiection10.main(Refiection10.java:19)*/
    //        Field age = aClass.getField("age");
    //        注意:当属性为私有的时候,需要暴破,如果只是普通属性,可以直接getField
            Field age = aClass.getDeclaredField("age");
            age.setAccessible(true);//获取私有属性字段之后还需要进行暴力破解,设置取消访问检查
            age.set(o, 15);
            Field name = aClass.getDeclaredField("name");
            name.setAccessible(true);
            name.set(o, "小明");
            /*Exception in thread "main" java.lang.NoSuchMethodException: com.example.content.test.User.a()
            at java.base/java.lang.Class.getMethod(Class.java:2108)
            at com.example.content.test.Refiection10.main(Refiection10.java:34)*/
            Method a = aClass.getMethod("a", int.class);//此处需要指定参数类型
            a.invoke(o, 11);
            Method b = aClass.getDeclaredMethod("b", int.class);//此处需要指定参数类型
            b.setAccessible(true);
            b.invoke(o, 22);
            Method c = aClass.getDeclaredMethod("c", int.class);//此处需要指定参数类型
            c.setAccessible(true);
            //静态方法的两种调用方式
            c.invoke(o,33);
            c.invoke(null,33);
            System.out.println(o);
        }
    }
    
    class User {
        private int age;
        private String name;
    
        public User() {
    
        }
    
        public User(int age) {
            this.age = age;
        }
    
        public User(String name) {
            this.name = name;
        }
    
        private User(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        public String a(int a) {
            System.out.println("调用公共方法" + a);
            return "调用公共方法" + a;
        }
    
        private String b(int b) {
            System.out.println("调用公共方法" + b);
            return "调用私有方法" + b;
        }
    
        private static String c(int c) {
            System.out.println("调用公共方法" + c);
            return "调用私有静态方法" + c;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    

    运行结果:

    User{age=0, name='null'}
    调用公共方法11
    调用公共方法22
    调用公共方法33
    调用公共方法33
    User{age=15, name='小明'}
    

    九.课后练习

    在这里插入图片描述
    代码:

    public class TestRefiect {
    
    
        public static void main(String[] args) throws Exception {
            //1.获取类路径
            Class<?> aClass = Class.forName("com.example.content.test.PrivateTest");
            //2.运行实例化
            Object o = aClass.getDeclaredConstructor().newInstance();
            //3.获取属性
            Field name = aClass.getDeclaredField("name");
            name.setAccessible(true);//需要暴破
            //4.获取方法
            Method nameMethod = aClass.getMethod("getName");
            //执行方法
            nameMethod.invoke(o);
            //4.获取属性值
            System.out.println(name.get(o));
            //5.设置值
            name.set(o, "kitty");
            nameMethod.invoke(o);
            System.out.println(name.get(o));
        }
    }
    
    class PrivateTest {
        private String name = "hellokitty";
        public String getName() {
            System.out.println("方法执行打印=:" + name);
            return name;
        }
    }
    

    运行结果:

    方法执行打印=:hellokitty
    hellokitty
    方法执行打印=:kitty
    kitty
    
    

    在这里插入图片描述

    package com.example.demo.test;/**
     * @Auther: 12855
     * @Date: 2022/3/21 14:30
     * @Description:
     */
    
    import java.io.File;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    /**
     * @Auther: 12855
     * @Date: 2022/3/21 14:30
     * @Description:
     */
    public class Study {
        public static void main(String[] args) throws Exception {
            Class aClass = Class.forName("java.io.File");
            Constructor[] declaredConstructors = aClass.getDeclaredConstructors();
            for (int i = 0; i < declaredConstructors.length; i++) {
                System.out.println("declaredConstructor=" + declaredConstructors[i]);
            }
            //获取实例对象
            //得到指定的构造器
            Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class);
            //设置参数
            String path = "d:\\aa.txt";
            //获取实例对象
            Object o = declaredConstructor.newInstance(path);
            //找出对应方法
            Method createNewFile = aClass.getMethod("createNewFile");
            //执行方法
            createNewFile.invoke(o);
            File file = new File("d:\\bb.txt");
            file.createNewFile();
    
        }
    }
    

    运行结果:

    declaredConstructor=public java.io.File(java.lang.String)
    declaredConstructor=public java.io.File(java.lang.String,java.lang.String)
    declaredConstructor=public java.io.File(java.io.File,java.lang.String)
    declaredConstructor=public java.io.File(java.net.URI)
    declaredConstructor=private java.io.File(java.lang.String,java.io.File)
    declaredConstructor=private java.io.File(java.lang.String,int)
    

    九.注解

    元注解:@Target,Retention,@Document,@Inherited
    @Target() :描述注解的使用范围
    @Retention:就是我们需要告诉编译器我们需要在什么级别保存该注释信息,用于描述注解的生命周期。
    @Document:
    @Inherited
    常用的内置注解:
    @Override:修辞方法,表示打算重写超类中的方法声明。
    @Deprecated:这个注释可以修辞方法,属性,类,表示不鼓励程序员使用这样的元素,通常是因为他很危险或有更好的选择。
    @SuperWarnings:这个注解主要是用来抑制警告信息的,我们在写程序时,可能会报很多黄线的警告,但是不影响运行,我们就可以用这个注解来抑制隐藏它。与前俩个注解不同的是我们必须给注解参数才能正确使用他。

    展开全文
  • Java 反射详解

    2016-09-16 11:05:45
    Java 反射详解
  • Java反射机制:跟着代码学反射

    千次阅读 多人点赞 2021-02-17 12:06:24
    微信搜索:码农StayUp ... 1. 前言 在OOP的世界里,万物皆对象。也就是说,我们可以将任何东西抽象成一个对象。 比如人,可以抽象成一个Person类,通过new Person()来实例化一个对象;...Java提供了一个特殊的类..

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 472,337
精华内容 188,934
关键字:

java反射

java 订阅