JVM - 1.类加载子系统

2024-08-23 14:44
文章标签 java 加载 jvm 子系统

本文主要是介绍JVM - 1.类加载子系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.类加载子系统

1.作用

  • 1.负责从文件系统或网络中加载字节码(.class)文件,即将物理磁盘上的字节码文件加载到内存中,生成供程序使用的类对象
    在这里插入图片描述
  • 2.字节码文件要求在文件开头有特定的文件标识(CA FE BA BE)
  • 3.类加载器(ClassLoader)只负责字节码文件的加载,是否可运行,由执行引擎(Execution Engine)决定
  • 4.类加载器是指特定的加载器,而类加载子系统是一个系统流程的统称
  • 5.加载生成的的类信息存放在称为方法区的内存空间中
  • 6.除了类的信息外,方法区还会存放运行时常量池(字节码文件中的Constant pool在运行时加载到内存中称为运行时常量池)信息,可能还包括字符串字面量数字常量,参考附录1

2.角色

在这里插入图片描述

  • 1.Car类通过编译器(javac)编译后生成Car.calss字节码文件并存在于本地磁盘上
  • 2.程序执行时字节码文件通过类加载器加载到JVM中,生成一个类对象
  • 3.通过该类对象可获取到类的构造器,根据该构造器可实例化出多个实例,通过实例的getClass方法也可以获取类对象本身
    在这里插入图片描述
    在这里插入图片描述
  • 4.字节码文件加载到JVM中,被称为DNA元数据模板,放在方法区
  • 5..class文件 -> JVM -> 元数据模板,该过程通过类加载器(Class Loader)实现
  • 6.物理磁盘上的字节码文件通过二进制流的方式加载到内存

3.类的加载过程

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

1.加载

  • 1.通过一个类的全限定名获取定义此类的二进制字节流
    在这里插入图片描述
  • 2.将这个字节流所代表的静态存储结构转化为方法区运行时数据结构
  • 3.方法区:抽象概念;落地实现:1.7及以前叫永久代,之后叫元空间
  • 4.并在内存中生成一个代表这个类的java.lang.Class对象,作为方法区该对象各种数据的访问入口
    在这里插入图片描述
    在这里插入图片描述

2.链接

1.验证(Verify)
  • 1.确保class文件的字节流包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机的自身安全
  • 2.主要包括四种验证:文件格式验证,元数据验证,字节码验证,符号引用验证
  • 3.例:文件标识验证(CA FE BA BE),使用Binary Viewer工具进行查看
    在这里插入图片描述
2.准备(Prepare)
  • 1.为类变量/静态变量分配内存空间并且设置该类变量默认初始值
    • 1.成员变量:定义在方法体和语句块之外,不属于任何一个方法,作用域是整个类
      • 1.静态变量/类变量:用 static 修饰的成员变量
      • 2.全局变量/实例变量:无 static 修饰的成员变量
    • 2.局部变量:定义在方法或代码块中的变量,作用域是其所在的代码块
  • 2.例如:
    • 1.private static int a = 1,准备阶段会赋默认初始值为0,即a=0,然后在初始化(initial)阶段会赋值a = 1
  • 3.注意
    • 1.不同类型的类变量默认初始值不同
    • 2.这里不包含final修饰的static类变量,因为final修饰的是常量而不是变量,常量后期不会再被修改,所以在编译阶段就已经分配值,准备阶段只是显示初始化
    • 3.这里不会为实例变量默认初始化,因为当前还没创建对象,只是加载过程,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆
3.解析(Resolve)
  • 1.将常量池内的符号引用转换为直接引用的过程
  • 2.事实上解析操作往往会在JVM执行完初始化后再执行
  • 3.符号引用:一组符号来描述所引用的目标,符号引用的字面量形式明确定义在《java虚拟机规范》的class文件格式中
  • 4.直接引用:直接指向目标的指针,相对偏移量或一个间接定位到目标的句柄
  • 5.解析动作主要针对类或接口,字段,类方法,接口方法,方法类型等
  • 6.通过反编译可以查看class文件中的符号引用和直接引用
package com.java;public class HelloApp {private static int a = 1;public HelloApp() {}public static void main(String[] args) {System.out.println(a);}
}
如下如示:Constant pool是常量池,其中以#开头的是符号引用,其余的是直接引用F:\文档\笔记\代码\JVMDemo\out\java>javap -v HelloApp.class
Classfile /F:/文档/笔记/代码/JVMDemo/out/java/HelloApp.classLast modified 2022-11-9; size 608 bytesMD5 checksum 5964d34bba8f8bf4e817be8fe95a17feCompiled from "HelloApp.java"
public class com.java.HelloAppminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref          #6.#23         // java/lang/Object."<init>":()V#2 = Fieldref           #24.#25        // java/lang/System.out:Ljava/io/PrintStream;#3 = Fieldref           #5.#26         // com/atguigu/java/HelloApp.a:I#4 = Methodref          #27.#28        // java/io/PrintStream.println:(I)V#5 = Class              #29            // com/atguigu/java/HelloApp#6 = Class              #30            // java/lang/Object	<=符号引用 #7 = Utf8               a	<=直接引用#8 = Utf8               I#9 = Utf8               <init>#10 = Utf8               ()V#11 = Utf8               Code#12 = Utf8               LineNumberTable#13 = Utf8               LocalVariableTable#14 = Utf8               this#15 = Utf8               Lcom/atguigu/java/HelloApp;#16 = Utf8               main#17 = Utf8               ([Ljava/lang/String;)V#18 = Utf8               args#19 = Utf8               [Ljava/lang/String;#20 = Utf8               <clinit>#21 = Utf8               SourceFile#22 = Utf8               HelloApp.java#23 = NameAndType        #9:#10         // "<init>":()V#24 = Class              #31            // java/lang/System#25 = NameAndType        #32:#33        // out:Ljava/io/PrintStream;#26 = NameAndType        #7:#8          // a:I#27 = Class              #34            // java/io/PrintStream#28 = NameAndType        #35:#36        // println:(I)V#29 = Utf8               com/atguigu/java/HelloApp#30 = Utf8               java/lang/Object#31 = Utf8               java/lang/System#32 = Utf8               out#33 = Utf8               Ljava/io/PrintStream;#34 = Utf8               java/io/PrintStream#35 = Utf8               println#36 = Utf8               (I)V
{public com.java.HelloApp();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 7: 0LocalVariableTable:Start  Length  Slot  Name   Signature0       5     0  this   Lcom/atguigu/java/HelloApp;public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=1, args_size=10: getstatic     #2                  // Field >java/lang/System.out:Ljava/io/PrintStream;3: getstatic     #3                  // Field a:I6: invokevirtual #4                  // Method >java/io/PrintStream.println:(I)V9: returnLineNumberTable:line 12: 0line 13: 9LocalVariableTable:Start  Length  Slot  Name   Signature0      10     0  args   [Ljava/lang/String;static {};descriptor: ()Vflags: ACC_STATICCode:stack=1, locals=0, args_size=00: iconst_11: putstatic     #3                  // Field a:I4: returnLineNumberTable:line 8: 0
}
SourceFile: "HelloApp.java"

3.初始化

  • 1.初始化阶段就是执行类构造器方法(<clinit>())的过程,可使用jclasslib查看(下载地址)
  • 2.此方法不需要定义,是javac编译器自动收集类中的所有类变量的赋值动作(类变量的显式赋值)和静态代码块中的语句合并而来,注意不包含静态方法
  • 3.类构造器方法中指定按语句在源文件中出现的顺序执行
  • 4.<clinit>()不同于类的构造器,类的构造器方法对应的是init()方法
  • 5.若该类具有父类,JVM会保证子类的<clinit>()执行前,父类的<clinit>()已经执行完毕
  • 6.虚拟机必须保证一个类的<clinit>()方法在多线程下被同步加锁(多线程下如果有一个线程加载,则其他加载线程会被阻塞;即保证类只会被加载一次,加载后的类对象保存在方法区)
  • 7.<clinit>()只有在类中有对静态变量静态代码块操作时才会有,其他情况不会存在(已测试静态方法不会存在)
public class HelloApp2 {private static int num = 1; //prepare:num = 0 ---> initial : num = 1 ---> num = 2static {num = 2;number = 20;}private static int number = 10; //prepare:number = 0 ---> initial : number = 20 ---> number = 10public static void main(String[] args) {System.out.println(num);  // num = 2System.out.println(number); // number = 10}
}

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

  • 8.非法的前项引用,可以提前赋值,但不是不能提前引用

在这里插入图片描述

4.类加载器

  • 1.JVM支持两种类型的类加载器:
    • 1.引导类加载器(Bootstrap ClassLoader)
    • 2.自定义类加载器(User-Defined ClassLoader)
  • 2.一般来说自定义类加载器指的是程序中由开发人员自定义的类加载;但是《Java虚拟机规范》中将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器
  • 3.程序中最常见的三个类加载器
    • 1.Bootstrap Class Loader:引导类加载器
    • 2.Extension Class Loader:扩展类加载器
    • 3.System Class Loader:系统类加载器
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
  • 4.以上四者之间(引导,扩展,系统,自定义)是包含关系,不是上层下层,也不是子父类的继承关系
  • 5.引导类加载器通过C/C++语言编写无法直接获取;扩展类加载器包含系统类加载器
    public class ClassLoaderTest {public static void main(String[] args) {//获取系统类加载器ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2//获取其上层:扩展类加载器ClassLoader extClassLoader = systemClassLoader.getParent();System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@1540e19//获取其上层:获取不到引导类加载器ClassLoader bootstrapClassLoader = extClassLoader.getParent();System.out.println(bootstrapClassLoader);//null//对于用户自定义类来说:默认使用系统类加载器进行加载ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2//String类使用引导类加载器进行加载的。---> Java的核心类库都是使用引导类加载器进行加载ClassLoader classLoader1 = String.class.getClassLoader();System.out.println(classLoader1);//nullClassLoader classLoader2 = Integer.class.getClassLoader();System.out.println(classLoader2);//null}
    }
    
  • 7.通过下列代码可以动态获取到引导类,扩展类,系统类加载器负责加载的类,其中越底层能加载的类就越多
    public class ClassLoaderTest1 {public static void main(String[] args) {System.out.println("**********引导类加载器**************");//获取BootstrapClassLoader能够加载的api的路径URL[] urLs = sun.misc.Launcher.getBootstrapClassPath().getURLs();for (URL element : urLs) {System.out.println(element.toExternalForm());}//从上面的路径中随意选择一个类,来看看他的类加载器是什么:引导类加载器ClassLoader classLoader = Provider.class.getClassLoader();System.out.println(classLoader);System.out.println("***********扩展类加载器*************");String extDirs = System.getProperty("java.ext.dirs");for (String path : extDirs.split(";")) {System.out.println(path);}//从上面的路径中随意选择一个类,来看看他的类加载器是什么:扩展类加载器ClassLoader classLoader1 = CurveDB.class.getClassLoader();System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@1540e19dSystem.out.println("***********系统类加载器*************");String appDirs = System.getProperty("java.class.path");for (String path : appDirs.split(";")) {System.out.println(path);}//从上面的路径中随意选择一个类,来看看他的类加载器是什么:系统类加载器ClassLoader classLoaderTest = ClassLoaderTest1.class.getClassLoader();System.out.println(classLoaderTest);//sun.misc.Launcher$AppClassLoader@18b4aac2}
    }
    

1.引导类加载器(Bootstrap ClassLoader)

  • 1.该类加载器使用C/C++语言实现,是JVM的一部分,通过Java代码是无法获取的
  • 2.该类加载器用来加载Java的核心库,提供JVM自身需要的类
    • 1.JAVA_HOME/jre/lib目录下的rt.jarresources.jar
    • 2.sun.boot.class.path路径下的内容
  • 3.该类加载器并不继承自java.lang.ClassLoader,没有父加载器
  • 4.该类加载器也用来加载扩展类和系统类加载器,并指定为他们的父类加载器
  • 5.出于安全考虑,引导类加载器只加载包名为javajavaxsun等开头的类

2.扩展类加载器(Extension ClassLoader)

  • 1.Java语言编写,由sun.misc.Launcher$ExtClassLoader实现(内部类),该加载器是JVM自带的
  • 2.ExtClassLoader派生于ClassLoader抽象类
  • 3.该类加载器的父类加载器为引导类加载器
  • 4.从java.ext.dirs系统属性所指定的目录中加载类库或从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库
  • 5.如果用户创建的jar包放在此目录(jre/lib/ext)下,也会自动由扩展类加载类加载,主要用来加载核心包外的扩展目录下的jar包
    在这里插入图片描述

3.系统类加载器(System Class Loader)

  • 1.Java语言编写,由sun.misc.Launcher$AppClassLoader实现(内部类),该加载器是JVM自带的
  • 2.AppClassLoade派生于ClassLoader抽象类
  • 3.该类加载器的父类加载器为扩展类加载器
  • 4.该类加载器负责加载环境变量classpath或系统属性java.class.path指定路径下的类库
  • 5.该类加载器是程序中默认的类加载器,一般来说Java应用的类都是由它来完成加载
  • 6.通过ClassLoader.getSystemClassLoader()方法可以获取到该类加载器
    在这里插入图片描述

4.用户自定义类加载器

  • 1.Java开发可以自定义类加载器,定制类的加载方式
  • 2.自定义类加载器的优势
    • 1.隔离加载类(不同中间件的加载是隔离的,确保加载jar包时相同名称的路径不会冲突)
    • 2.修改类加载的方式(修改为需要的时候动态的加载)
    • 3.扩展加载源(本地磁盘,网络,扩展其他加载源)
    • 4.防止源码泄露(自定义类加载器实现加密解密)
  • 3.实现步骤
    • 1.通过继承抽象类java.class.ClassLoader的方式,实现自定义类加载器
    • 2.JDK1.2之前,自定义类加载器会去继承ClassLoader类并重写loadClass()方法,从而实现自定义的类加载类
    • 3.JDK1.2之后,不建议覆盖loadClass()方法,建议把自定义的类加载逻辑写在findClass()方法中
    • 4.编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承URLClassLoader类,这样可以避免去编写findClass()方法以及获取字节码流的方式,使自定义类加载器编写更加简洁
    package com.java;import java.io.FileNotFoundException;/*** 自定义用户类加载器*/
    public class CustomClassLoader extends ClassLoader {@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] result = getClassFromCustomPath(name);if(result == null){throw new FileNotFoundException();}else{//如果不为null则继续调用该方法return defineClass(name,result,0,result.length);}} catch (FileNotFoundException e) {e.printStackTrace();}throw new ClassNotFoundException(name);}//根据指定路径已二进制流的方式读入到内存中形成字节数组private byte[] getClassFromCustomPath(String name){//从自定义路径中加载指定类:细节略//如果指定路径的字节码文件进行了加密,则需要在此方法中进行解密操作。return null;}public static void main(String[] args) {CustomClassLoader customClassLoader = new CustomClassLoader();try {Class<?> clazz = Class.forName("One",true,customClassLoader);Object obj = clazz.newInstance();System.out.println(obj.getClass().getClassLoader());} catch (Exception e) {e.printStackTrace();}}
    }
    

5.ClassLoader

  • 1.ClassLoader是一个抽象类,除引导类加载器其余的类加载器都继承自ClassLoaser
  • 2.sun.misc.LauncherJVM的入口应用,ExtClassLoaderAppClassLoader都是Launcher的内部类
  • 3.加载类生成Class的方式:
    • 1.通过loadClass方法传入一个想要加载的路径然后返回Class的类实例
    • 2.通过findClassdefineClass配合传入一个想要加载的路径然后返回Class的类实例

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

1.获取ClassLoader的途径

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

6.双亲委派机制

  • 1.Java虚拟机class文件采用的是按需加载的方式,当需要使用该类时才会将它的class文件加载到内存生成Class对象
  • 2.Java虚拟机加载某个类的class文件时,采用的双亲委派机制,即把请求交由父类处理,它是一种任务委派模式

1.工作原理

  • 1.如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行
  • 2.如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的引导类加载器
  • 3.如果父类加载器可以完成类加载任务,则成功返回,如果父类加载器无法完成此加载任务,子类才会尝试自己去加载,这就是双亲委派机制
    在这里插入图片描述

2.实例

  • 1.本地创建一个和Java.lang.String同样包级的String类,如果该类被加载,则会输出静态代码块中的内容
    package java.lang;public class String {static{System.out.println("我是自定义的String类的静态代码块");}
    }
    
  • 2.创建一个测试类,调用Java.lang.String
    public class StringTest {public static void main(String[] args) {java.lang.String str = new java.lang.String();System.out.println("hello");StringTest test = new StringTest();System.out.println(test.getClass().getClassLoader()); //自定义的类加载器一般是系统类加载器System.out.println(str.getClass().getClassLoader()); //核心类库加载器是引导类加载器}
    }
    
  • 3.测试结果发现自动调用的是核心类库中String类而不是本地中的String类(只限测试使用),因此可防止恶意攻击导致项目崩溃
    在这里插入图片描述
  • 4.上述自定义String类添加main方法并执行,因为双亲委派机制,最后本地String类的加载交给引导类加载器去加载核心类库中的String类,而该类不存在main方法,所以会报错
    在这里插入图片描述
  • 5.系统类接口由引导类加载器加载,实现其功能的第三方的jar包加载一般是通过线程上下文类加载器加载,默认为系统类型加载器
    在这里插入图片描述

3.优势

  • 1.避免类的重复加载
  • 2.保护程序的安全,防止核心API被随意篡改,防止自定义的类使JVM崩溃
    • 例:自定义类java.lang.String
  • 3.禁止自定义包名与系统包名冲突,因为该包名下的类由引导类加载器加载,但是该类并不存在于引导类加载器要加载的路径中
    在这里插入图片描述

4.沙箱安全机制

  • 1.沙箱:是一个限制程序运行的环境
  • 2.沙箱机制:是将Java代码限定在虚拟机JVM特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏,可参考Java沙箱机制
  • 3.简单来说
    • 1.上述自定义String类,加载的时候会优先使用引导类加载器加载
    • 2.引导类加载器在加载的过程中会先加载jdk自带的文件(rt.jar包中java\lang\String.class)
    • 3.上述报错信息说没有main方法就是因为加载的是rt.jar包中的String类,这样可以保证对java核心源代码的保护,这就是沙箱安全机制

5.判断两个Class对象是否为同一个类

  • 1.JVM中表示两个Class对象是否为同一个类存在两个必要条件
    • 1.类的完整类名必须一致,包括包名
    • 2.加载这个类的classLoader(指:ClassLoader实例对象)必须相同(例:上述两个String类不同,因为类加载器不同)
  • 2.JVM中即使两个类对象(Class对象)来源同一个class文件,被同一个虚拟机所加载,但只要加载它们的ClassLoader实例对象不同,那么这两个对象也是不相等的
  • 3.JVM必须知道一个类型是由引导类加载器加载还是由自定义类加载器加载的
  • 4.如果一个类型是由自定义类加载器加载的,JVM会将这个类加载器的一个引用作为类型信在这里插入代码片息的一部分保存在方法区中,引导类除外因为该类标识为null
  • 5.当解析一个类型到另一个类型的引用的时候,JVM需要保证这两个类型的类加载器是相同的(动态链接)

6.类的主动使用和被动使用

  • 1.Java程序对类的使用方式分为:主动使用和被动使用
    • 1.主动使用:分为七种情况
      • 1.创建类的实例
      • 2.访问某个类或接口的静态变量,或者对该静态变量赋值
      • 3.调用类的静态方法
      • 4.反射(Class.forName等)
      • 5.初始化一个类的子类
      • 6.Java虚拟机启动被标明启动类的类
      • 7.JDK7开始提供的动态语言支持:
        • 1.java.lang.invoke.MethodHandle实例的解析结果
        • 2.REF_getStatic,REF_putStatic,REF_invokeStatic句柄对应的类没有初始化,则初始化
    • 2.被动使用
      • 1.除以上七种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化
  • 2.不管是主动使用还是被动使用都会被加载,只要使用都会被加载,但是只有主动使用会执行类加载的初始化步骤

这篇关于JVM - 1.类加载子系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1099673

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定