Java反射、类加载、初始化
Java反射、类加载、初始化
一、 先了解一下基本知识:
1. 字节码和机器码是什么,有什么联系
二、反射、类加载器、初始化
2. 反射:
2.1,反射理解:
2.2,Class类常用方法:
2.3,获取某个类的class 实例四种方法:
2.4,反射优点、缺点:
2.5,反射在实际开发中的使用场景:
3,类加载ClassLoader:
3.1,类加载器的一个主要方法:getResourceAsStream(String str): 获取类路径下的指定文件的输入流
3.2 类加载器和inputStream 加载配置文件的区别:
3.3 配置文件的路径问题:
3.4 Class.getResource(String path) 与 Class.getClassLoader.getResource (String path) 区别:
4,类的加载机制
4.1 类的加载:
4.2类加载的时机
4.3 类加载器分类:
4.4 类加载的双亲委派模型
转载1:https://www.cnblogs.com/qiumingcheng/p/5400265.html 《机器码和字节码》
转载2:https://www.cnblogs.com/yangyongjie/p/11002844.html 《Java类加载和初始化》
转载3:https://blog.csdn.net/iechenyb/article/details/81871306 《JAVA类加载与初始化顺序》
转载4:https://blog.csdn.net/qq_28505705/article/details/106462462 《Java基础----反射 (一) 反射机制、Class类、类的加载和类加载器ClassLoader、反射的使用》
一、 先了解一下基本知识:
1. 字节码和机器码是什么,有什么联系
▪机器码:顾名思义,机器可以读懂的码;就是0、1啦;原生码(Native Code),是电脑的CPU可直接解读的数据。
▪字节码:字节码(Bytecode)是一种包含执行程序、由一序列 op 代码/数据对 组成的二进制文件。字节码是一种中间码,它比机器码更抽象,需要直译器转译后才能成为机器码的中间代码。(ps:OP (操作码operation code))
举例:java bytecode; 首先通过java语言编译器编写代码,生成.java 文件,然后编译器实行javac 指令,生成.class 文件(字节码文件),然后jvm 跨平台,在直译器的翻译下生成机器码,让CPU 解读。(ps:编译器的作用:编译器里我们通过高级java 高级语言,编写代码,理由编译器自带的javac 命令,生成字节码.class 文件,然后再通过编译器的java 命令,运行代码,查看结果。)
✿联系:字节码是一种中间状态(中间码)的二进制代码(文件)。需要直译器转译后才能成为机器码。
二、反射、类加载器、初始化
2. 反射:
2.1,反射理解:“照镜子”
对象通过反射可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
概念:Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Refle反射的API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
在Object类中定义了public final Class getClass()方法,方法返回值的类型是一个Class类。
✿Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。
2.2,Class类常用方法:
2.3,获取某个类的class 实例四种方法:
法1: // 已知具体的类,调用运行时类的属性:.class;它主要应用于原始数据类型,并且仅在我们知道类的名称时才使用。要创建的Class对象的类名在编译时确定。
Class<?> clazz = String.class; 法2: //已知某个类的实例,调用该实例的getClass()方法获取Class对象 String str = new String("string"); Class c3 = str.getClass(); 法3:调用Class的静态方法:forName(String classPath) Class<?> clazz = Class.forName(“某个类的类名(路径)”); 法4:方式四:使用类的加载器:ClassLoader ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class<?> clazz4 = classLoader.loadClass("reflection_mechanism.Person");
2.4,反射优点、缺点:
优点:提高了程序的灵活性和扩展性,降低模块的耦合性;例如通过反射机制可以让程序创建和控制任何类的对象,无需提前硬编码目标类;反射是框架的灵魂,使用反射可以避免将代码写死在框架中。
缺点:性能问题:反射相当于一系列解释操作,通知jvm要做的事情,性能比直接的java代码要慢很多。把本来应该在项目启动阶段做的事情,延迟到了项目运行阶段来做。这样就增加了项目运行阶段的系统开销。安全限制:使用反射技术要求程序必须在一个没有安全限制的环境中运行。因为有内部的暴露。
2.5,反射在实际开发中的使用场景:
♢反射使用不好,对性能影响比较,一般项目中很少直接使用,主要用在底层框架中。使用反射机制,①模块开发,通过反射调用对应字节码;②动态代理模式;③spring框架、Hibernate框架等;
✿反射是框架设计的灵魂。
举例:
- ① 用 IoC 来注入和组装 bean;
- ②工厂模式:Factory类中用反射的话,添加了一个新的类之后,就不需要再修改工厂类Factory
- ③数据库JDBC中通过Class.forName(Driver).来获得数据库连接驱动;
- ④动态代理、面向切面、bean 对象中的方法替换与增强,也使用了反射;
- ⑤定义的注解,也是通过反射查找
ps:什么是IoC?--- “控制反转”, 获的对象的过程被反转了,依赖注入。 控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是有外部容器的创建和维护。例如将对象的依赖交给配置文件来配置
3,类加载ClassLoader:
3.1,类加载器的一个主要方法:getResourceAsStream(String str): 获取类路径下的指定文件的输入流
3.2 类加载器和inputStream 加载配置文件的区别:
✿类加载器作用:
加载“.classpath”的资源文件,例如xml、properities文件。注意:该方式只能读取类路径(.classpath)的配置文件
✿InputStream 读取配置文件:
注:该方式的优点在于可以读取任意路径下的配置文件
3.3 配置文件的路径问题:
类加载器作用:加载“.classpath”的资源文件,例如xml、properities文件
注意:该方式只能读取类路径(.classpath)的配置文件
ps:查看eclipse中的.classpath 文件:Window-》show View-》Navigator(Deprecated)
☆ 配置文件的路径是个疑惑!!!,从使用相对路径时频繁报错!!!,其实Class.getClassLoader.getResource (String path) 是默认是从.classpath根下获取。
//PolymorphicFunction这一行的完整代码 pro.load(HeroFactories.class.getClassLoader().getResourceAsStream("PolymorphicFunction/Pro.properties"));
3.4 Class.getResource(String path) 与 Class.getClassLoader.getResource (String path) 区别:
Class.getResource(String path)
☆ path不以'/'开头时,默认是从此类所在的包下取资源;
☆path以'/'开头时,则是从项目的ClassPath根下获取资源
class.getResource("/") == class.getClassLoader().getResource("")
Class.getClassLoader.getResource (String path)
♢默认是从ClassPath根下获取,path不能以’/'开头,根是src(bin),classPath文件到配置文件xml或者properties文件;
☆ path不能以'/'开头,path是指类加载器的加载范围,在资源加载的过程中,使用的逐级向上委托的形式加载的,'/'表示Boot ClassLoader,类加载器中的加载范围,因为这个类加载器是C++实现的,所以加载范围为null。
4,类的加载机制
4.1 类的加载:
类的加载:指的是将类的.class字节码文件中的二进制数据读入到jvm内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装类在方法区内的数据结构。
4.2类加载的时机:
类从被加载到虚拟机内存中开始,到卸载出内存为止,整个生命周期包括以下7个阶段:加载、验证、准备、解析、初始化、使用、卸载。
其中验证、准备、解析3个部分统称为连接。 因此生命周期可以简记为:加载、连接(验、准、解)、初始化、使用、卸载(加连初使卸)
4.3 类加载器分类:
加载器主要有四种:
① jvm启动类加载器bootstrap loader,用c++实现为jvm的一部分(仅指sun的hotspot),负责 JAVA_HOME/lib下面的类库中的类的加载,这个加载器,java程序无法引用到。
② 扩展类加载器Extension Loader,由sun.misc.Launcher$ExtClassLoader类实现,可在java中使用,负责JAVA_HOME/lib/ext 目录和java.ext.dir目录中类库的类的加载。
③ 应用系统类加载器Application System Loader,由sun.misc.Louncher$AppClassLoader实现,负责加载用户类路径中类库中的类,如果没有使用自定义的加载器,这个就是默认的 加载器!
④ 用户自定义加载器 自己定义从哪里加载类的二进制流。
4.4 类加载的双亲委派模型:
各个加载器都是先委托自己的父加载器加载类,若确实没加载到再自己来加载,于是java默认的类查找加载顺序是自顶向下的,树状结构。
本文来自博客园,作者:一乐乐,转载请注明原文链接:https://www.cnblogs.com/shan333/p/14749678.html