网站开发目的简介,门窗 东莞网站建设,用cms创建自己带数据库的网站和在本机搭建网站运行平台的心得体会,视频发布到哪些平台可以赚钱准备 我是小C同学编写得一个java文件#xff0c;如何实现我的功能呢#xff1f;需要去JVM(Java Virtual Machine)这个地方旅行。 变身 我高高兴兴的来到JVM#xff0c;想要开始JVM之旅#xff0c;它确说#xff1a;“现在的我还不能进去#xff0c;需要做一次转换#x…准备 我是小C同学编写得一个java文件如何实现我的功能呢需要去JVM(Java Virtual Machine)这个地方旅行。 变身 我高高兴兴的来到JVM想要开始JVM之旅它确说“现在的我还不能进去需要做一次转换生成class文件才行”。为什么这样呢 JVM不能直接加载java文件的原因: Java源代码中包含了许多高级语言特性和语法比如类、继承、多态、异常处理等等。这些高级特性在JVM中没有直接对应的形式只有通过编译器的处理才能转化为JVM可以理解的字节码指令。 Java源代码需要经过编译器的编译过程才能生成相应的字节码文件然后再由JVM加载、解释执行。在编译过程中编译器对源代码进行语法分析、类型检查、优化等操作最终生成与目标平台兼容的Java字节码文件。 JVM只能够加载和运行符合Java虚拟机规范的.class字节码文件而不能够直接加载和运行Java源代码文件。 编译 知道原因后我又问JVM我怎么才能变成class文件呢JVM告诉我可以通过javac命令。 javac javac 是 Java 编译器命令用于将 Java 源代码文件编译成字节码文件.class 文件。 命令格式 javac [options] [source files] options为编译选项可以控制编译器的行为例如指定类路径、生成调试信息、压缩文件等。 source files为需要编译的 Java 源代码文件可以指定多个文件用空格隔开。如果不指定源代码文件则 javac 命令会在当前目录查找所有扩展名为 .java 的文件进行编译。 需要注意的是javac 命令需要在正确配置 JDK 环境后才能使用。JDKJava Development Kit是 Java 开发工具包的缩写是 Java 应用程序开发的核心组件之一。 具体实现 编译器在编译源文件时需要对源文件进行语法分析、语义分析和类型检查等操作。 语法分析javac命令首先将源文件读入内存然后进行词法分析和语法分析。词法分析器负责将源文件中的字符序列转换成一个个单词Token然后语法分析器将单词组合成可以被解释执行的语法结构形成抽象语法树AST。 语义分析javac命令在生成AST之后进行语义分析。语义分析器主要是为了检查程序中是否存在语义错误例如变量未定义、类型不匹配等如果发现语义错误编译器会输出错误信息并中止编译过程不会生成字节码文件。 类型检查javac命令在语义分析的基础上进行类型检查。类型检查器主要是检查程序的类型是否匹配和兼容如果类型不匹配或不兼容编译器会在编译期间报告错误。 代码生成javac命令在生成抽象语法树后对其进行优化和转化最终生成字节码文件。编译器会根据目标代码的平台和版本生成适当的字节码文件。 执行 知道怎么变身后我立即通过javac命令让自己变成可以被JVM执行的class文件。 加载 变成class文件后我怎么能进入JVM内部呢是走着去还是坐车去呢JVM告诉我要通过类加载器进入。 类加载器 Java类加载器是Java虚拟机JVM中的一个重要组件它负责将类文件.class文件加载到JVM中。 分类 Java 中的类加载器是按照其加载类的特点进行分类的主要有以下几种类型 启动类加载器Bootstrap ClassLoader负责加载 JRE/lib/rt.jar 中的核心 Java 类库是最顶层的类加载器不是 Java 类因为在 JVM 实现时就已经存在。 扩展类加载器Extension ClassLoader负责加载 JRE/lib/ext 目录下的扩展类库也是由 C 实现的类加载器。 应用程序类加载器APP ClassLoader负责加载应用程序的类包括在 CLASSPATH 中指定的类库或者目录中的 Java 类。 自定义类加载器Custom ClassLoader继承自 ClassLoader 类实现自己的类加载器主要用于加载一些自定义的类或者修改某些类的字节码。 查看使用的类加载器 代码 public class ClassLoaderTest {public static void main(String[] args) {//启动类加载器System.out.println(String.class.getClassLoader());//扩展类加载器System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());//应用程序类加载器System.out.println(ClassLoaderTest.class.getClassLoader());//扩展类加载器的父加载器System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader().getParent());//应用程序类加载器的父加载器System.out.println(ClassLoaderTest.class.getClassLoader().getParent());}
} 执行结果 自定义类加载器 自定义类加载器主要包括两种类型 独立的自定义类加载器通过重载 ClassLoader 类中的 findClass 方法来实现加载类文件的功能 基于 URLClassLoader 类实现的自定义类加载器使用 URL 的形式来指定类文件的位置。 重载ClassLoader 代码 public class CustomClassLoader extends ClassLoader {private String basePath;public CustomClassLoader(String basePath) {this.basePath basePath;}Overridepublic Class? findClass(String name) throws ClassNotFoundException {byte[] data getClassData(name);if (data null) {throw new ClassNotFoundException();} else {// 使用 defineClass 方法将 byte 数组转换为 Class 对象return defineClass(name, data, 0, data.length);}}private byte[] getClassData(String className) {String path basePath File.separatorChar className.replace(., File.separatorChar) .class;try (InputStream inputStream new FileInputStream(path);ByteArrayOutputStream outputStream new ByteArrayOutputStream()) {byte[] buffer new byte[1024];int length;while ((length inputStream.read(buffer)) ! -1) {outputStream.write(buffer, 0, length);}return outputStream.toByteArray();} catch (IOException e) {e.printStackTrace();return null;}}
} 说明 上述代码继承了ClassLoader类并重写了其中的findClass()方法实现从指定目录中加载类文件的功能。 在findClass()方法中首先通过getClassData()方法读取并返回类文件的字节数组如果获取的字节数组为空则抛出ClassNotFoundException异常否则使用defineClass()方法将字节数组转换为 Class 对象并返回该对象。 在getClassData()方法中根据传入的类名生成类文件路径并使用FileInputStream将类文件读入字节数组中。 使用: public class CustomClassLoaderTest {public static void main(String[] args) throws Exception {// 创建自定义类加载器指定类文件所在的目录CustomClassLoader classLoader new CustomClassLoader(F:\\classes);// 使用自定义类加载器加载 Hello 类Class? clazz classLoader.loadClass(com.example.something.Hello);Object obj clazz.getDeclaredConstructor().newInstance();System.out.println(obj);}
} 基于 URLClassLoader 代码 public class CustomURLClassLoader extends URLClassLoader {public CustomURLClassLoader(URL[] urls, ClassLoader parent) {super(urls, parent);}Overrideprotected Class? findClass(String name) throws ClassNotFoundException {try {// 调用父类 loadClass 方法进行委托加载Class? clazz super.findClass(name);return clazz;} catch (ClassNotFoundException e) {// 如果父类无法加载则尝试在 URL 中加载byte[] data getClassData(name);if (data null) {throw new ClassNotFoundException();} else {// 使用 defineClass 方法将 byte 数组转换为 Class 对象return defineClass(name, data, 0, data.length);}}}private byte[] getClassData(String className) {String path className.replace(., /) .class;URL[] urls getURLs();for (URL url : urls) {try {URL classUrl new URL(url, path);// 使用 URLConnection 检查类文件是否存在try (InputStream is classUrl.openStream();ByteArrayOutputStream os new ByteArrayOutputStream()) {byte[] buffer new byte[1024];int length;while ((length is.read(buffer)) ! -1) {os.write(buffer, 0, length);}return os.toByteArray();}} catch (IOException e) {// ignore and try next URL}}return null;}
} 说明 上述代码继承了 URLClassLoader 类并重写了其中的 findClass() 方法实现先尝试使用父类加载器进行加载如果无法加载则尝试使用 URL 加载类文件的功能。在 getClassData() 方法中会遍历 URLClassLoader 中定义的 URL检查类文件是否存在并返回类文件的字节数组如果无法找到类文件则返回 null。 使用 public class CustomURLClassLoaderTest {public static void main(String[] args) throws Exception {// 创建 URL 数组指定类文件所在的 URLURL[] urls { new URL(file:F:\\classes) };// 创建父类加载器使用系统类加载器ClassLoader parent ClassLoader.getSystemClassLoader();// 创建自定义 URL 类加载器CustomURLClassLoader classLoader new CustomURLClassLoader(urls, parent);// 使用自定义 URL 类加载器加载 Hello 类Class? clazz classLoader.loadClass(com.example.something.Hello);Object obj clazz.getDeclaredConstructor().newInstance();System.out.println(obj);}
} 双亲委派 加载器那么多我具体是哪个类进行加载得呢双亲委派机制告诉我答案. 定义 双亲委派是一种Java类加载器的工作机制它将类加载请求委派给父类加载器直到顶级系统类加载器。基本思想是除非有特殊需求否则所有类的加载任务都应该由父类加载器完成从而保证Java核心库的类型安全和稳定性并防止恶意代码的自行布置。如果一个类没有在父类加载器中被发现子类加载器才会尝试加载该类。这种类加载器之间的父子关系被称为“双亲委派模型”. 如图 意义 为什么通过双亲委派进行加载呢 避免重复加载 提高安全性 维护Java平台的一致性 代码优化 Linking 加载过后我是否就可以被使用了呢答案是否定的我还要经历Lingking 阶段包括Verification、Preparation 和 Resolution。 Verification(验证) 在验证阶段Java虚拟机会进行语法与语义的检查以保证class文件的完整性和正确性同时保证被加载的class与虚拟机的版本兼容。主要的检查内容包括文件格式、字节码语义、符号引用等。 Preparation(准备) 在准备阶段Java虚拟机会为类变量分配内存并且赋予初始值。如果类变量包含有静态变量那么这时也会初始化静态变量。因此在这个阶段类变量所使用的空间已经被分配将其设置为默认初始值即可。 Resolution(解析) 在解析阶段将类或接口中的符号引用转化为直接引用的过程。在 Java 虚拟机加载类时符号引用是一种指向常量池中某个符号的引用而直接引用则是指向内存中某个位置的直接指针。解析阶段可以理解为是在解决类之间的依赖关系使各个类之间可以像使用自身成员一样使用别的类中的成员。 初始化 在验证、准备和解析后我还要经过初始化才能被使用。 定义 初始化是指在类加载过程的最后一步JVM要对类进行一些初始化的操作确保类可以安全地使用。在这个阶段往往包括静态变量显式赋值和静态代码块执行。 内容 静态变量显式赋值 当类加载器完成类的加载、验证、准备后在初始化阶段JVM对类的静态变量进行显式赋值。如果类定义了多个静态变量JVM会按照代码中声明的顺序进行初始化并且若发现此过程需要访问到其他未初始化的类JVM会先完成这些类的初始化。 静态代码块的执行 除了静态变量的显式赋值类的静态代码块也会在初始化阶段执行。当JVM执行类加载的Initializing阶段时会执行类中所有静态代码块的内容如果类中没有定义静态代码块则不执行。这个过程一般用于在使用之前对类进行初始化。 接口初始化 当一个类在初始化时如果发现其父类还未进行初始化JVM会先对其父类进行初始化。如果该类实现了接口也会对这个接口进行初始化操作接口的初始化过程和类一样都会进行静态变量显式赋值及静态代码块执行同时还会检查接口中的所有静态方法。 功能实现 初始化之后我才真正的进入JVM中其它小伙伴需要我的时候只需要创建我的实例就可以使用我的功能了得到我帮助得小伙伴都很感谢我。 GC 在JVM中我过得很开心也留下了很多足迹。在我走后如何让我得足迹不对其他小伙伴有影响呢GC可以帮我解决这个问题。 定义 GCGarbage Collection是JVM提供的垃圾回收机制。在Java中对象是动态分配的内存是由JVM自动管理而不是由程序员手动分配和释放。当一个对象不再被程序引用时就应该由垃圾回收器回收其占用的内存这样可以防止内存泄漏和提高内存的。 小结 通过我的旅行你知道JVM是怎么加载一个类的了么我们通过加载、Linking、初始化和使用等各个阶段将Java类完整地载入内存并执行其中定义的方法和变量。这个过程中每个阶段都扮演着不同的角色并为类的正常运行提供了必要的支持。 文章转载自京东云技术团队
原文链接https://www.cnblogs.com/jingdongkeji/p/17814733.html