ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

关于java中的反射机制与浅谈类加载

2021-09-18 20:58:31  阅读:169  来源: 互联网

标签:反射 java 浅谈 对象 toString new Class 加载


反射机制说实话有点抽象,写篇文章总结一下所学。因为反射机制会涉及到类加载,只有理解一点类加载的过程才能理解反射机制,所以顺便写一点类加载。

对于一个程序,首先是写好程序的源代码,例如Test.java,通过javac命令让java编译器将其编译为对应的字节码文件Test.class。当运行这个字节码文件的时候,jvm会先用一个底层类classloader进入类加载阶段。

这个阶段又分为三个小阶段,加载--->链接--->初始化。

加载阶段将各个类的字节码文件(可能来自class文件,jar包或者网络)转化为二进制字节流加载到内存(方法区),并为每个加载的类在堆区创建一个Java.lang.Class 对象。没错,不是所有的对象都是new出来的,Class对象就是类加载器通过loadClass方法生成的。

注意这一步加载叫做静态加载,程序里每一个new出来的对象都要被加载进来,如果没有就会报错,依赖性很强。而反射生成的对象属于动态加载,只有执行到的时候才会被加载。

这儿有个小技巧,就是大型应用程序的启动,可以先启动一个页面,然后慢慢动态加载那些用到的类,这比起一开始就静态加载所有类更容易给用户一种启动速度比较快的错觉。

比如我写个变量a=10,写个代码块if a==1 创建一个Person对象,但是我没有写Person对象,理论上这个程序运行没问题,但是编译都过不去,因为静态加载的时候没有找到对应的类。那假如我写个反射通过Class对象来创造Person对象(嗯,也没有Person对应的Class对象),却可以顺利通过编译。

链接阶段又分为三个小阶段,验证,准备和解析。

验证阶段检查安全性。

准备阶段为静态变量默认初始化并分配内存。这儿主要是对类的一些静态成员进行初始化。因为静态成员都是类本身的属性而不是对象的属性嘛,那些对象的属性要等到运行阶段new之后才会加载。当然还有一种是final修饰的常量,这种一旦赋值就不可更改,所以在这个阶段初始化就是给它程序里写的值。

解析阶段将符号引用转化为直接引用,也就是替换为实际地址。

这两个阶段都是jvm控制的,第三个阶段初始化阶段就是程序员可以控制的了。这儿程序会执行<clinit>()方法,这个方法会让编译器按语句在源码中出现顺序收集所有静态变量赋值和静态代码块。

整个类加载阶段都只是程序的准备工作,还没有执行main里的代码,也没有new任何对象。

最后就是经常饱受调试的运行阶段了,该执行的执行,该new的new。注意不管是通过什么方法new出来的对象都知道自己的类是谁,也就知道自己类对应的Class对象是谁。

比如一个Test类,对应一个Class<Test>的反射类对象。

反射有什么用?反射可以满足设计模式的ocp开闭原则:通过对配置文件的修改在不修改源码的情况下控制程序。反射机制允许一个程序在执行期间取得一个类的内心信息,并能创造这个类的实例。当然如果不强转的话得到的对象是object对象,不能直接用一些方法, 只能通过reflection提供的方法来用。当然现在来用始终有些不知所以的感觉,听说反射是后面框架学习的灵魂,学到后面也许自然就懂它的意义了。

反射怎么用?可以通过一个我想要的类对应的Class的对象,用newInstance 方法可以创建我想要的类的对象,GetMethod方法可以创建我想要的类的方法。。等等。

注意由于newInstance之后我们使用Object来接收对象的,虽然可以强转成想要的类,但是为了实现不修改源码的目的我们通常不这么做,而是用java.lang.reflect里面的一些类来实现对对象的操作。其中Method,Field,Constructor分别代表方法,成员和构造器。

我们通过对Class对象的方法:.getMethod等等来构造它对应的类的属性对象(数组),然后再对属性对象用invoke方法来实现属性调用。

简而言之,正常对象是对象.属性,而通过反射实现的是属性.invoke(对象),是不是很有反射的feel。

只要有了我想要的类的相关信息(通常保存在某个配置文件中),就可以通过反射查看使用这个类的一切信息。打个比方,fate里每个英灵的名字假设是唯一的,当我得知这个英灵的真名,我就可以去查历史书来知道他的宝具,他的绝活,他的心病,而不需要召唤一个真正的英灵来搜魂。

历史书上的记载真真假假,但是java反射类确实实打实的。每一个类都会有一个唯一且对应的Class对象,这个对象就好像原来那个类的镜子里的虚像,纤毫不差,完全可以根据虚像里的信息造出一个新对象来。

注意以上都没有考虑private关键字,理论上没有东西能访问一个对象的私有属性,除了这个对象本身。但是反射可以,只要用setAccessible(true)就行了,被称为暴破(暴力破解)。《java核心技术》一书中作者说这个方法离灭亡不远了(能够不受限制地访问类内部的日子已经屈指可数),书中举了一个万能toString的例子,就是说写一个类,打个比方就叫tostring类吧,这个类中有一个toString方法,参数是个object,能通过反射把参数object的所有属性,方法等信息都写进一个String里。那么对于每个自定义类,当要实现toString方法时我们可以通过new 一个tostring类并调用其toString方法,把当前自定义类的对象传进去就好了。

虽然很懒但这一通啰嗦属实抽象,写一点伪代码吧。

public class Tostring {
    public String toString(Object o)
    {
        Class cl=o.getClass();
        String ret="";
        //通过对cl的处理得到一些信息,这里写点简单的
        ret+=cl.getName();
        Field []fields=cl.getDeclaredFields();
        for(Field f:fields)
        {
            //ret+=toString(f)  这里要细写会涉及循环引用的处理。。
        }
        return ret;
    }
}
public class Cat {
   private String name;
    public String toString()
    {
        return new Tostring().toString(this);
    }
    
}

是不是挺美好,再也不用写toString方法了。这段代码的关键就是Tostring类可以通过反射访问Cat类的私有属性,而这其实有点走后门的感觉,有利有弊,见仁见智吧。

标签:反射,java,浅谈,对象,toString,new,Class,加载
来源: https://blog.csdn.net/qq_52612346/article/details/120369332

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有