Java-反射

1、动态语言和静态语言

  • 动态语言
    1、c#,php,python,JavaScript等!都属于动态语言。
    2、概念:在程序运行中可以改变程序的结构和逻辑。
  • 静态语言
    1、c++,java等!属于静态语言。
    2、概念:在程序运行时就不能改了,只能重新运行。
  • 图片介绍
    静态vs动态语言.png

2、反射

  • Reflection(反射)是Java被视为动态语言的关键
  • 反射机制允许程序执行期借助于Reflection API取得任何类的内部信息并能直接操作任意对象的内部属性及方法
  • 加载完类之后,在堆内存中会生成一个class对象(一个类只有一个对象),这个对象包含了完整的类结构信息,通过对象可以看到类的结构,这个对象就想一面镜子一样可以看到类的结构,所有我们形象的称之为:反射
  • 通过下面的代码中获取hashcode可以看出一个类只能生成一个对象
        Class<?> aClass = Class.forName("com.tools.server.Reflection.Test04_反射");
        Class<?> aClass1 = Class.forName("com.tools.server.Reflection.Test04_反射");
        Class<?> aClass2 = Class.forName("com.tools.server.Reflection.Test04_反射");
        System.out.println(aClass.hashCode());
        System.out.println(aClass1.hashCode());
        System.out.println(aClass2.hashCode());

打印:
471910020
471910020
471910020

  • 图片介绍
    正常方式vs反射方式.png

3、Java反射机制提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所拥有的成员变量和方法
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时处理注解
  • 生成动态代理
  • .............

4、Java反射优点和缺点

  • 优点
    1、可以实现动态创建对象和编译
  • 缺点
    1、对性能有影响。
    2、使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。
    3、这种操作总是慢于 直接执行相同的操作
    4、反射出来的对象相比new出来的对象,相差十几倍

5、反射相关的主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器
  • ................

6、Class类(678910做为class了解)

  • 对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。 对于每个类而言,JRE都为其保留一个不变的Class类型的对象。 一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
  • Class本身也是一个类
  • Class对象只能由系统建立对象
  • 一个加载的类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由哪个Class实例所生成
  • 通过Class可以完整地得到一个类中的所有被加载的构造
  • Class类是Reflection的根源,针对任何你想动态加载、运行的类,只能先获得相应的Class对象
  • Class类的常用方法
    • newInstance()方法本质是调用了无参构造器
      a) 类必须有一个无参数的构造器
      b) 类的构造器的访问权限需要足够
  • Class类的几种创建方式
//Class类的创建方式
        //方式1、将类的路径作为参数,可能抛出ClassNotFoundException
        Class<?> aClass = Class.forName("com.tools.server.Reflection.User");

        //方式2、最安全可靠,性能高
        Class<User> aClass2 = User.class;

        //方式3、调用某个类的实例的getClass()方法获取
        User user = new User(1,20,"whl");
        Class<? extends User> aClass3 = user.getClass();

        System.out.println(aClass.hashCode());
        System.out.println(aClass2.hashCode());
        System.out.println(aClass3.hashCode());
        //打印结果:
        //          531885035
        //          531885035
        //          531885035
  • 所有类型的Class
        Class c1 = Object.class;//类
        Class c2 = Comparable.class;//接口
        Class c3 = String[].class;//一维数组
        Class c4 = int[][].class;//二维数组
        Class c5 = void.class;//空类型
        Class c6 = Override.class;//注解
        Class c7 = ElementType.class;//枚举
        Class c8 = Integer.class;//基本数据类型

        System.out.println("c1:"+c1);    class java.lang.Object
        System.out.println("c2:"+c2);    interface java.lang.Comparable
        System.out.println("c3:"+c3);    class [Ljava.lang.String;
        System.out.println("c4:"+c4);    class [[I
        System.out.println("c5:"+c5);    void
        System.out.println("c6:"+c6);    interface java.lang.Override
        System.out.println("c7:"+c7);    class java.lang.annotation.ElementType
        System.out.println("c8:"+c8);    class java.lang.Integer

7、类加载内存分析

  • Java内存分析

    • a) 存放new的对象和数组
      b) 可以被所有的线程共享,不会存放别的对象引用(某个类的实例,一个引用可以指向多个对象)

    • a) 存放基本变量类型(会包含这个基本类型的具体数值)
      b) 引用对象的变量(会存放这个引用在堆中具体的位置)
    • 方法区
      a) 可以被所有线程共享
      b) 包含了所有的class和static变量
    • 最后总结,在java虚拟机中,对象的引用是存在栈中的,而对象是存放在堆中的

  • 类的加载过程
    • 触发前景:当程序主动使用某个类时,如果该类未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。

  • 类的加载与ClassLoader的理解

  • 代码实现
public class Test06 {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.m);
        /*
        1.加载到内存,会产生一个类对应Class对象
        2.链接,链接结束后 m = 0
        3.初始化
            <clinit>(){
                System.out.println("静态代码块初始化");
                System.out.println("A方法无参构造初始化");
                m = 200;
                m = 100;
            }
         */
    }
}
class A{
    public A(){
        System.out.println("A方法无参构造初始化");
    }
    static{
        System.out.println("静态代码块初始化");
        m = 200;
    }
    static int m = 100;
}

1、方法区:先加载Test06类的信息
2、方法区:在加载A类的信息
3、堆:生成Test06和A类的class对象
4、栈:main方法初始化m=0,链接完毕,执行new A()
5、堆:new A():A类的对象可以代替(3、)去方法区找A类的信息
6、方法区:将类的初始值和静态代码块合并执行,返回到栈

  • 初始化过程

8、什么时候会发生类初始化?

  • 类的主动引用(一定会发生类的初始化)
    • 当虚拟机启动,先初始化main方法所在的类
    • new一个类的对象
    • 调用类的静态成员(除了final常量)和静态方法
    • 使用java.lang.reflect包的方法对类进行反射调用
    • 当初始化一个类,如果其父类没有被初始化,则先会初始化他的父类
  • 类的被动引用(不会发生类的初始化)
    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
    • 通过数组定义类引用,不会触发此类的初始化
    • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
  • 代码测试
public class Test07_分析类的初始化 {
    
    static {
        System.out.println("main类被加载");
    }
    
    public static void main(String[] args) throws ClassNotFoundException {
        //主动引用
        //1、他会先加载父类,在加载子类
        Son son = new Son();
        //2、反射会引发主动引用:他使用java.lang.reflect包中的方法了
        Class.forName("com.tools.server.Reflection.Son");
        //3、调用类的静态方法
        Son.method_s();

        //被动引用
        //1、引用父类的静态变量或静态方法:不会导致子类初始化
        System.out.println(Son.b);
        Son.method_f();
        //2、数组定义类引用
        Son[] array = new Son[9];
        //3、引用常量
        System.out.println(Son.A);
    }
}
class Father{
    static int b = 2;
    static {
        System.out.println("父类被加载!");
    }
    public static void method_f(){
        System.out.println("method_f!");
    }
}
class Son extends Father{
    static int m = 100;
    static {
        System.out.println("子类被加载");
    }
    static final int A = 1;
    public static void method_s(){
        System.out.println("method_f!");
    }
}

9、类加载器

类加载器的作用.png
        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //获取系统类加载器的父类加载器-->扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        //获取扩展类的父类加载器-->根加载器(c/c++)
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);//引导类加载器是java核心类库,无法直接获取

        //测试当前类是哪个加载器加载的
        ClassLoader classLoader = Class.forName("com.tools.server.Reflection.Test08_类加载器").getClassLoader();
        System.out.println(classLoader);

        //测试JDK内置的类是哪个加载器加载的
        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader1);//引导类加载器是java核心类库,无法直接获取

        //如何获得系统类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));

10、获取类运行时结构

        Class aClass = Class.forName("com.tools.server.Reflection.User");
        //获取类名
        System.out.println(aClass.getSimpleName());//获取类名
        System.out.println(aClass.getName());//获取包名+类名
        /*打印:
            User
            com.tools.server.Reflection.User
         */

        //获取类的属性
        Field[] fields = aClass.getFields();//获取public属性
        Field[] fieldsall = aClass.getDeclaredFields();//获取全部的属性
        System.out.println("-----------------------------------------------------");
        for (int i = 0; i < fieldsall.length; i++) {
            System.out.println(fieldsall[i]);
        }
        /*打印:
            private int com.tools.server.Reflection.User.id
            private int com.tools.server.Reflection.User.age
            private java.lang.String com.tools.server.Reflection.User.name
         */

        //获取指定属性
        System.out.println("-----------------------------------------------------");
        System.out.println(aClass.getDeclaredField("name"));
        /*打印:
            private java.lang.String com.tools.server.Reflection.User.name
         */

        //获取类的方法
        System.out.println("-----------------------------------------------------");
        Method[] declaredMethodsall = aClass.getMethods();//获取本类和父类所有的public方法
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (int i = 0; i < declaredMethods.length; i++) {
            System.out.println(declaredMethods[i]);//获取本类的所有方法
        }
        /*打印:
            public java.lang.String com.tools.server.Reflection.User.toString()
            public java.lang.String com.tools.server.Reflection.User.getName()
            public void com.tools.server.Reflection.User.setName(java.lang.String)
            public int com.tools.server.Reflection.User.getId()
            public void com.tools.server.Reflection.User.setId(int)
            public int com.tools.server.Reflection.User.getAge()
            public void com.tools.server.Reflection.User.setAge(int)
            public void com.tools.server.Reflection.User.test()
         */

        //获取指定方法
        System.out.println("-----------------------------------------------------");
        System.out.println(aClass.getMethod("getAge", null));
        System.out.println(aClass.getMethod("setAge", int.class));
        /*打印:
            public int com.tools.server.Reflection.User.getAge()
            public void com.tools.server.Reflection.User.setAge(int)
         */

        //获取类全部的构造器
        System.out.println("-----------------------------------------------------");
        Constructor[] constructors = aClass.getConstructors();//获取本类构造器的public方法
        Constructor[] constructorsall = aClass.getDeclaredConstructors();//获取本类构造器的全部方法
        for (int i = 0; i < constructors.length; i++) {
            System.out.println(constructors[i]);
        }
        /*打印:
            public com.tools.server.Reflection.User(int,int,java.lang.String)
            public com.tools.server.Reflection.User()
         */

        //获取指定的构造器
        System.out.println("-----------------------------------------------------");
        Constructor declaredConstructor = aClass.getConstructor(int.class, int.class, String.class);
        System.out.println(declaredConstructor);
        /*打印:
            public com.tools.server.Reflection.User(int,int,java.lang.String)
         */

11、动态创建对象执行方法(通过反射创建对象)

  • 调用指定方法
  • 取消安全检测setAccessible()方法
  • 代码测试
        //获取class对象
        Class aClass = Class.forName("com.tools.server.Reflection.User");

        //构造一个对象
        User user = (User) aClass.newInstance();//本质是调用了无参构造器,如果类中没有无参构造器就会报错
        System.out.println(user);

        //通过构造器构造一个对象
        Constructor constructor = aClass.getDeclaredConstructor(int.class,int.class,String.class);//参数顺序不能变
        User user2 = (User) constructor.newInstance( 001, 18,"wyz");//参数顺序不能变,否则异常
        System.out.println(user2);

        //通过反射调用普通方法
        User user3 = (User) aClass.newInstance();
        System.out.println(user3.getName());
        //通过反射获取一个方法
        Method setName = aClass.getDeclaredMethod("setName", String.class);
        setName.invoke(user3,"whl");//invoke(激活)传递参数(对象,值)
        System.out.println(user3.getName());

        //通过反射操作属性
        User user4 = (User) aClass.newInstance();
        Field name = aClass.getDeclaredField("name");
        name.setAccessible(true);//取消安全检测,不加他访问不了private方法
        name.set(user4,"whl2");
        System.out.println(user4.getName());

12、性能对比

  • 正常方式
        User user = new User();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            user.getName();
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime-startTime+" ms");//打印:4ms
  • 安全检测
        User user = new User();
        Class<?> aClass = Class.forName("com.tools.server.Reflection.User");//class获取对象
        Method getName = aClass.getDeclaredMethod("getName", null);//获取方法
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            getName.invoke(user,null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime-startTime+" ms");//打印:47ms
  • 关闭安全检测
        User user = new User();
        Class<?> aClass = Class.forName("com.tools.server.Reflection.User");//class获取对象
        Method getName = aClass.getDeclaredMethod("getName", null);//获取方法
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000; i++) {
            getName.invoke(user,null);
            getName.setAccessible(true);//关闭安全检测
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime-startTime+" ms");//打印:17ms
  • 总结
    如果程序中反射用的多的话,可以使用关闭安全检测提升效率!

通过反射还可以获取泛型、注解等,个人觉得没意义就不做展示了

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