Java类加载过程,以及静态变量和静态常量赋值

目录

1. 过程一:Loading(加载)阶段 

1.1 加载(Loading)阶段

1.2. 二进制流的获取方式

1.3. 类模型与 Class 实例的位置

2.4. 数组类的加载

2. 过程二:Linking(链接)阶段

2.1. 环节 1:链接阶段之 Verification(验证)

2.2. 环节 2:链接阶段之 Preparation(准备)

2.3. 环节 3:链接阶段之 Resolution(解析)

3. 过程三:Initialization(初始化)阶段

3.1. static 与 final 的搭配问题

3.2. ()的线程安全性

3.3. 类的初始化情况:主动使用 vs 被动使用

4. 过程四:类的 Using(使用)

5. 过程五:类的 Unloading(卸载)

5.1. 类、类的加载器、类的实例之间的引用关系

5.2.类的生命周期

5.3. 具体例子

5.4. 类的卸载

5.5. 方法区的垃圾回收

6. 静态变量以及静态常量赋值问题

6.1 静态变量

6.2 静态常量


JVM中类的加载过程分为加载(Loading)、链接(Linking)、初始化(Initialization),其中链接(Linking)阶段又分为验证(Verification)、准备(Preparation)、解析(Resolution),下面主要分析各个阶段主要做了哪些事情。

1. 过程一:Loading(加载)阶段 

1.1 加载(Loading)阶段

加载的理解:

所谓加载,简而言之就是将Java类的字节码文件加载到机器内存中,并在内存(方法区)中构建出Java类的原型--类模板对象,并在堆区生成相应的Class实例对象。所谓类模板对象,其实就是Java类在JVM内存中的一个快照,JVM将从字节码文件中解析出的常量池、类字段、类方法等信息存储到类模板中,这样JVM在运行期便能通过类模板获取到Java类中的任意信息,能够对Java类的成员变量进行遍历,也能进行Java方法的调用。

反射的机制即基于这一基础。如果JVM没有将Java类的声明信息存储起来,则JVM在运行期也无法反射。

加载完成的操作:

加载阶段,简而言之,查找并加载类的二进制数据,生成Class的实例。

在加载类时,Java虚拟机必须完成以下三件事情:

  • 通过类的全名,获取类的二进制数据流。
  • 解析类的二进制数据流生成方法区内的数据结构。(Java类模型\模板数据)
  • 在堆区创建java.lang.Class类的实例,表示该类型。作为方法区这个类的各种数据的访问入口。

1.2. 二进制流的获取方式

对于类的二进制数据流,虚拟机可以通过多种途径产生或获得。(只要所读取的字节码符合 JVM 规范即可)

  • 虚拟机可能通过文件系统读入一个 class 后缀的文件(最常见)

  • 读入 jar、zip 等归档数据包,提取类文件。

  • 事先存放在数据库中的类的二进制数据

  • 使用类似于 HTTP 之类的协议通过网络进行加载

  • 在运行时生成一段 class 的二进制信息等

  • 在获取到类的二进制信息后,Java 虚拟机就会处理这些数据,并最终转为一个 java.lang.Class 的实例。

如果输入数据不是 ClassFile 的结构,则会抛出 ClassFormatError。

1.3. 类模型与 Class 实例的位置

类模型的位置

加载的类在 JVM 中创建相应的类结构,类结构会存储在方法区(JDKl.8 之前:永久代;J0Kl.8 及之后:元空间)。

Class 实例的位置

类将.class 文件加载至元空间后,会在堆中创建一个 Java.lang.Class 对象,用来封装类位于方法区内的数据结构,该 Class 对象是在加载类的过程中创建的,每个类都对应有一个 Class 类型的对象。

                  image-20210430221037898

2.4. 数组类的加载

创建数组类的情况稍微有些特殊,因为数组类本身并不是由类加载器负责创建,而是由 JVM 在运行时根据需要而直接创建的,但数组的元素类型仍然需要依靠类加载器去创建。创建数组类(下述简称 A)的过程:

  • 如果数组的元素类型是引用类型,那么就遵循定义的加载过程递归加载和创建数组 A 的元素类型;

  • JVM 使用指定的元素类型和数组维度来创建新的数组类。

如果数组的元素类型是引用类型,数组类的可访问性就由元素类型的可访问性决定。否则数组类的可访问性将被缺省定义为 public。

2. 过程二:Linking(链接)阶段

2.1. 环节 1:链接阶段之 Verification(验证)

当类加载到系统后,就开始链接操作,验证是链接操作的第一步。

它的目的是保证加载的字节码是合法、合理并符合规范的。

验证的步骤比较复杂,实际要验证的项目也很繁多,大体上 Java 虚拟机需要做以下检查,如图所示。

          image-20210430221736546

整体说明:

验证的内容则涵盖了类数据信息的格式验证、语义检查、字节码验证,以及符号引用验证等。

  • 其中格式验证会和加载阶段一起执行。验证通过之后,类加载器才会成功将类的二进制数据信息加载到方法区中。

  • 格式验证之外的验证操作将会在方法区中进行

链接阶段的验证虽然拖慢了加载速度,但是它避免了在字节码运行时还需要进行各种检查。

具体说明:

  1. 格式验证:是否以魔数 0XCAFEBABE 开头,主版本和副版本号是否在当前 Java 虚拟机的支持范围内,数据中每一个项是否都拥有正确的长度等。

  2. 语义检查:Java 虚拟机会进行字节码的语义检查,但凡在语义上不符合规范的,虚拟机也不会给予验证通过。比如:

    • 是否所有的类都有父类的存在(在 Java 里,除了 object 外,其他类都应该有父类)

    • 是否一些被定义为 final 的方法或者类被重写或继承了

    • 非抽象类是否实现了所有抽象方法或者接口方法

    • 是否存在不兼容的方法(比如方法的签名除了返回值不同,其它都一样,这种方法会让虚拟机无从下手调度;abstract情况下的方法,就不能是final)

  3. 字节码验证:Java 虚拟机还会进行字节码验证,字节码验证也是验证过程中最为复杂的一个过程。它试图通过对字节码流的分析,判断字节码是否可以被正确地执行。比如:

    • 在字节码的执行过程中,是否会跳转到一条不存在的指令

    • 函数的调用是否传递了正确类型的参数

    • 变量的赋值是不是给了正确的数据类型等

    栈映射帧(StackMapTable)就是在这个阶段,用于检测在特定的字节码处,其局部变量表和操作数栈是否有着正确的数据类型。但遗憾的是,100%准确地判断一段字节码是否可以被安全执行是无法实现的,因此,该过程只是尽可能地检查出可以预知的明显的问题。如果在这个阶段无法通过检查,虚拟机也不会正确装载这个类。但是,如果通过了这个阶段的检查,也不能说明这个类是完全没有问题的。

    在前面3次检查中,已经排除了文件格式错误、语义错误以及字节码的不正确性。但是依然不能确保类是没有问题的。

  4. 符号引用的验证:校验器还将进行符号引用的验证。Class 文件在其常量池会通过字符串记录自己将要使用的其他类或者方法。因此,在验证阶段,虚拟机就会检查这些类或者方法确实是存在的,并且当前类是否有权限访问这些数据,如果一个需要使用类无法在系统中找到,则会抛出 NoClassDefFoundError,如果一个方法无法被找到,则会抛出 NoSuchMethodError。此阶段在解析环节才会执行。

2.2. 环节 2:链接阶段之 Preparation(准备)

准备阶段(Preparation),简言之,为类的静态变量分配内存,并将其初始化为默认值。

当一个类验证通过时,虚拟机就会进入准备阶段。在这个阶段,虚拟机就会为这个类分配相应的内存空间,并设置默认初始值。Java 虚拟机为各类型变量默认的初始值如表所示。

类型默认初始值
byte(byte)0
short(short)0
int0
long0L
float0.0f
double0.0
char\u0000
booleanfalse
referencenull

Java 并不支持 boolean 类型,对于 boolean 类型,内部实现是 int,由于 int 的默认值是 0,故对应的,boolean 的默认值就是 false。

注意

  • 这里不包含基本数据类型的字段用static final修饰的情况,因为final在编译的时候就会分配了,准备阶段会显式赋值。

    // 一般情况:static final修饰的基本数据类型、字符串类型字面量会在准备阶段赋值
    private static final String str = "Hello world";
    // 特殊情况:static final修饰的引用类型不会在准备阶段赋值,而是在初始化阶段赋值
    private static final String str = new String("Hello world");
  • 注意这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到 Java 堆中。

  • 在这个阶段并不会像初始化阶段中那样会有初始化或者代码被执行。

2.3. 环节 3:链接阶段之 Resolution(解析)

在准备阶段完成后,就进入了解析阶段。解析阶段(Resolution),简言之,将类、接口、字段和方法的符号引用转为直接引用

具体描述

符号引用就是一些字面量的引用,和虚拟机的内部数据结构和和内存布局无关。比较容易理解的就是在 Class 类文件中,通过常量池进行了大量的符号引用。但是在程序实际运行时,只有符号引用是不够的,比如当如下 println()方法被调用时,系统需要明确知道该方法的位置。

举例

输出操作 System.out.println()对应的字节码:

invokevirtual #24 <java/io/PrintStream.println>

image-20210430225015932

以方法为例,Java 虚拟机为每个类都准备了一张方法表,将其所有的方法都列在表中,当需要调用一个类的方法的时候,只要知道这个方法在方法表中的偏移量就可以直接调用该方法。通过解析操作,符号引用就可以转变为目标方法在类中方法表中的位置,从而使得方法被成功调用。

3. 过程三:Initialization(初始化)阶段

初始化过程主要就是执行<clinit>方法,即执行类中的静态代码块,以及给静态变量显式赋值。

只有类中包含静态代码块或者静态变量的显式赋值代码时,类才会有<clinit>方法。

3.1. static 与 final 的搭配问题

说明:使用 static+ final 修饰的字段的显式赋值的操作,到底是在哪个阶段进行的赋值?

  • 情况 1:在链接阶段的准备环节赋值

  • 情况 2:在初始化阶段<clinit>()中赋值

结论: 在链接阶段的准备环节赋值的情况:

  • 对于基本数据类型的字段来说,如果使用 static final 修饰,则显式赋值(直接赋值常量,而非调用方法通常是在链接阶段的准备环节进行

  • 对于 String 来说,如果使用字面量的方式赋值,使用 static final 修饰的话,则显式赋值通常是在链接阶段的准备环节进行

  • 在初始化阶段<clinit>()中赋值的情况: 排除上述的在准备环节赋值的情况之外的情况。

最终结论:使用 static+final 修饰,且显示赋值中不涉及到方法或构造器调用的基本数据类到或 String 类型的显式赋值,是在链接阶段的准备环节进行。

public static final int INT_CONSTANT = 10;// 在链接阶段的准备环节赋值
public static final int NUM1 = new Random().nextInt(10);// 在初始化阶段clinit>()中赋值
public static int a = 1;// 在初始化阶段<clinit>()中赋值
public static final Integer INTEGER_CONSTANT1 = Integer.valueOf(100);// 在初始化阶段<clinit>()中赋值
public static Integer INTEGER_CONSTANT2 = Integer.valueOf(100);// 在初始化阶段<clinit>()中概值
public static final String s0 = "helloworld0";// 在链接阶段的准备环节赋值
public static final String s1 = new String("helloworld1");// 在初始化阶段<clinit>()中赋值
public static String s2 = "hellowrold2";// 在初始化阶<clinit>()中赋值

3.2. <clinit>()的线程安全性

对于<clinit>()方法的调用,也就是类的初始化,虚拟机会在内部确保其多线程环境中的安全性。

虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。

正是因为函数<clinit>()带锁线程安全的,因此,如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个线程阻塞,引发死锁。并且这种死锁是很难发现的,因为看起来它们并没有可用的锁信息。

如果之前的线程成功加载了类,则等在队列中的线程就没有机会再执行<clinit>()方法了。那么,当需要使用这个类时,虚拟机会直接返回给它已经准备好的信息。

3.3. 类的初始化情况:主动使用 vs 被动使用

Java 程序对类的使用分为两种:主动使用和被动使用。

主动使用

Class 只有在必须要首次使用的时候才会被装载,Java 虚拟机不会无条件地装载 Class 类型。Java 虚拟机规定,一个类或接口在初次使用前,必须要进行初始化。这里指的“使用”,是指主动使用,主动使用只有下列几种情况:(即:如果出现如下的情况,则会对类进行初始化操作。而初始化操作之前的加载、验证、准备已经完成。

  1. 实例化:当创建一个类的实例时,比如使用 new 关键字,或者通过反射、克隆、反序列化。

    /**
     * 反序列化
     */
    Class Order implements Serializable {
        static {
            System.out.println("Order类的初始化");
        }
    }
    ​
    public void test() {
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            // 序列化
            oos = new ObjectOutputStream(new FileOutputStream("order.dat"));
            oos.writeObject(new Order());
            // 反序列化
            ois = new ObjectInputStream(new FileOutputStream("order.dat"));
            Order order = ois.readObject();
        }
        catch (IOException e){
            e.printStackTrace();
        }
        catch (ClassNotFoundException e){
            e.printStackTrace();
        }
        finally {
            try {
                if (oos != null) {
                    oos.close();
                }
                if (ois != null) {
                    ois.close();
                }
            }
            catch (IOException e){
                e.printStackTrace();
            }
        }
    }
  2. 静态方法:当调用类的静态方法时,即当使用了字节码 invokestatic 指令。

  3. 静态字段:当使用类、接口的静态字段时(final 修饰特殊考虑),比如,使用 getstatic 或者 putstatic 指令。(对应访问变量、赋值变量操作)

    public class ActiveUse {
        @Test
        public void test() {
            System.out.println(User.num);
        }
    }
    ​
    class User {
        static {
            System.out.println("User类的初始化");
        }
        public static final int num = 1;
    }
  4. 反射:当使用 java.lang.reflect 包中的方法反射类的方法时。比如:Class.forName("com.atguigu.java.Test")

  5. 继承:当初始化子类时,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。

    当 Java 虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但是这条规则并不适用于接口。

    • 在初始化一个类时,并不会先初始化它所实现的接口

    • 在初始化一个接口时,并不会先初始化它的父接口

    • 因此,一个父接口并不会因为它的子接口或者实现类的初始化而初始化。只有当程序首次使用特定接口的静态字段时,才会导致该接口的初始化。

  6. default 方法:如果一个接口定义了 default 方法,那么直接实现或者间接实现该接口的类的初始化,该接口要在其之前被初始化。

    interface Compare {
        public static final Thread t = new Thread() {
            {
                System.out.println("Compare接口的初始化");
            }
        }
    }
  7. main 方法:当虚拟机启动时,用户需要指定一个要执行的主类(包含 main()方法的那个类),虚拟机会先初始化这个主类。

    VM 启动的时候通过引导类加载器加载一个初始类。这个类在调用 public static void main(String[])方法之前被链接和初始化。这个方法的执行将依次导致所需的类的加载,链接和初始化。

  8. MethodHandle:当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类。(涉及解析 REF getStatic、REF_putStatic、REF invokeStatic 方法句柄对应的类)

被动使用

除了以上的情况属于主动使用,其他的情况均属于被动使用。被动使用不会引起类的初始化。

也就是说:并不是在代码中出现的类,就一定会被加载或者初始化。如果不符合主动使用的条件,类就不会初始化。

  1. 静态字段:当通过子类引用父类的静态变量,不会导致子类初始化,只有真正声明这个字段的类才会被初始化。

    public class PassiveUse {
        @Test
        public void test() {
            System.out.println(Child.num);
        }
    }
    ​
    class Child extends Parent {
        static {
            System.out.println("Child类的初始化");
        }
    }
    ​
    class Parent {
        static {
            System.out.println("Parent类的初始化");
        }
    ​
        public static int num = 1;
    }
  2. 数组定义:通过数组定义类引用,不会触发此类的初始化

    Parent[] parents= new Parent[10];
    System.out.println(parents.getClass());
    // new的话才会初始化
    parents[0] = new Parent();
  3. 引用常量:引用常量不会触发此类或接口的初始化。因为常量在链接阶段就已经被显式赋值了。

    public class PassiveUse {
        public static void main(String[] args) {
            System.out.println(Serival.num);
            // 但引用其他类的话还是会初始化
            System.out.println(Serival.num2);
        }
    }
    ​
    interface Serival {
        public static final Thread t = new Thread() {
            {
                System.out.println("Serival初始化");
            }
        };
    ​
        public static int num = 10;
        public static final int num2 = new Random().nextInt(10);
    }
  4. loadClass 方法:调用 ClassLoader 类的 loadClass()方法加载一个类,并不是对类的主动使用,不会导致类的初始化。

    Class clazz = ClassLoader.getSystemClassLoader().loadClass("com.test.java.Person");

扩展

-XX:+TraceClassLoading:追踪打印类的加载信息

4. 过程四:类的 Using(使用)

任何一个类型在使用之前都必须经历过完整的加载、链接和初始化 3 个类加载步骤。一旦一个类型成功经历过这 3 个步骤之后,便“厉事俱备只欠东风”,就等着开发者使用了。

开发人员可以在程序中访问和调用它的静态类成员信息(比如:静态字段、静态方法),或者使用 new 关键字为其创建对象实例。

5. 过程五:类的 Unloading(卸载)

5.1. 类、类的加载器、类的实例之间的引用关系

       在类加载器的内部实现中,用一个 Java 集合来存放所加载类的引用。另一方面,一个 Class 对象总是会引用它的类加载器,调用 Class 对象的 getClassLoader()方法,就能获得它的类加载器。由此可见,代表某个类的 Class 实例与其类的加载器之间为双向关联关系。

       一个类的实例总是引用代表这个类的 Class 对象。在 Object 类中定义了 getClass()方法,这个方法返回代表对象所属类的 Class 对象的引用。此外,所有的 java 类都有一个静态属性 class,它引用代表这个类的 Class 对象。

5.2.类的生命周期

       当 Sample 类被加载、链接和初始化后,它的生命周期就开始了。当代表 Sample 类的 Class 对象不再被引用,即不可触及时,Class 对象就会结束生命周期,Sample 类在方法区内的数据也会被卸载,从而结束 Sample 类的生命周期。

一个类何时结束生命周期,取决于代表它的Class对象何时结束生命周期。

5.3. 具体例子

image-20210430235455086

loader1 变量和 obj 变量间接引用代表 Sample 类的 Class 对象,而 objClass 变量则直接引用它。

如果程序运行过程中,将上图左侧三个引用变量都置为 null,此时 Sample 对象结束生命周期,MyClassLoader 对象结束生命周期,代表 Sample 类的 Class 对象也结束生命周期,Sample 类在方法区内的二进制数据被卸载。

当再次有需要时,会检查 Sample 类的 Class 对象是否存在,如果存在会直接使用,不再重新加载;如果不存在 Sample 类会被重新加载,在 Java 虚拟机的堆区会生成一个新的代表 Sample 类的 Class 实例(可以通过哈希码查看是否是同一个实例)

5.4. 类的卸载

(1)启动类加载器加载的类型在整个运行期间是不可能被卸载的(jvm 和 jls 规范)

(2)被系统类加载器和扩展类加载器加载的类型在运行期间不太可能被卸载,因为系统类加载器实例或者扩展类的实例基本上在整个运行期间总能直接或者间接的访问的到,其达到 unreachable 的可能性极小。

(3)被开发者自定义的类加载器实例加载的类型只有在很简单的上下文环境中才能被卸载,而且一般还要借助于强制调用虚拟机的垃圾收集功能才可以做到。可以预想,稍微复杂点的应用场景中(比如:很多时候用户在开发自定义类加载器实例的时候采用缓存的策略以提高系统性能),被加载的类型在运行期间也是几乎不太可能被卸载的(至少卸载的时间是不确定的)。

综合以上三点,一个已经加载的类型被卸载的几率很小至少被卸载的时间是不确定的。同时我们可以看的出来,开发者在开发代码时候,不应该对虚拟机的类型卸载做任何假设的前提下,来实现系统中的特定功能。

5.5. 方法区的垃圾回收

方法区的垃圾收集主要回收两部分内容:常量池中废弃的常量和不再使用的类型。

HotSpot 虚拟机对常量池的回收策略是很明确的,只要常量池中的常量没有被任何地方引用,就可以被回收。

判定一个常量是否“废弃”还是相对简单,而要判定一个类型是否属于“不再使用的类”的条件就比较苛刻了。需要同时满足下面三个条件:

  • 该类所有的实例都已经被回收。也就是Java堆中不存在该类及其任何派生子类的实例。

  • 加载该类的类加载器已经被回收。这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP的重加载等,否则通常是很难达成的。

  • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

Java 虚拟机被允许对满足上述三个条件的无用类进行回收,这里说的仅仅是“被允许”,而并不是和对象一样,没有引用了就必然会回收。

6. 静态变量以及静态常量赋值问题

6.1 静态变量

只有static修饰的类变量,不管涉及不涉及其他类的调用,都在Linking(链接)阶段的Preparation(准备)环节赋默认值,在Initialization(初始化)阶段显式赋值。

public static int a = 1;// 在初始化阶段<clinit>()中赋值

public static Integer INTEGER_CONSTANT2 = Integer.valueOf(100);// 在初始化阶段<clinit>()中赋值

public static String s2 = "hellowrold2";// 在初始化阶<clinit>()中赋值

6.2 静态常量

情况一:static + final修饰,而且赋值不涉及其他类的调用,直接赋值为字面量。

static + final修饰的类变量,在编译期赋默认值,在Linking(链接)阶段的Preparation(准备)环节显式赋值。

public static final int INT_CONSTANT = 10;// 在链接阶段的准备环节赋值

public static final String s0 = "helloworld0";// 在链接阶段的准备环节赋值

情况二:static + final修饰,赋值涉及其他类的调用。

static + final修饰的类变量,在编译期赋默认值,Initialization(初始化)阶段显式赋值。

public static final int NUM1 = new Random().nextInt(10);// 在初始化阶段clinit>()中赋值

public static final Integer INTEGER_CONSTANT1 = Integer.valueOf(100);// 在初始化阶段<clinit>()中赋值

public static Integer INTEGER_CONSTANT2 = Integer.valueOf(100);// 在初始化阶段<clinit>()中概值

public static final String s0 = "helloworld0";// 在链接阶段的准备环节赋值

public static final String s1 = new String("helloworld1");// 在初始化阶段<clinit>()中赋值

  • 12
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第1篇 Java编程基础   第1章 Java开发环境的搭建(教学视频:9分钟) 2   1.1 理解Java 2   1.2 搭建Java所需环境 3   1.2.1 下载JDK 3   1.2.2 安装JDK 4   1.2.3 配置环境 5   1.2.4 测试JDK配置是否成功 7   实例1 开发第一个Java程序 7   第2章 Java基础类型与运算符(教学视频:39分钟) 9   2.1 基础类型 9   实例2 自动提升 9   实例3 自动转换 10   实例4 常用基础类型之强制转换 11   2.2 运算符 12   实例5 算术运算符 12   实例6 关系运算符 13   实例7 逻辑运算符 14   实例8 位运算符 15   实例9 移位运算符 16   实例10 转型运算符 17   2.3 其他形式 18   实例11 常量与变量 18   实例12 各种进制的转换 19   实例13 Java中的进制与移位运算符 22   第3章 条件控制语句(教学视频:75分钟) 26   3.1 if控制语句 26   实例14 判断输入的年份是否为闰年 26   实例15 抽奖活动 27   3.2 for语句 28   实例16 小九九乘法表 28   实例17 如何列出素数 29   实例18 Java中的递归 31   实例19 男生女生各多少人 32   实例20 求水仙花数 34   实例21 求任意一个正数的阶乘 35   实例22 求n的n次方 35   实例23 利用for循环输出几何图形 36   实例24 杨辉三角 38   3.3 while语句 39   实例25 求1到100之间的和 39   实例26 存上100元需要多少天 40   实例27 输出100之间的所有偶数 41   实例28 如何判断回文数字 42   3.4 do…while语句 43   实例29 输出100之间的所有奇数 44   实例30 求最大的随机数 44   3.5 switch语句 45   实例31 判断字母分类 46   实例32 优良及差 47   实例33 打印任意一年日历 48   实例34 一年四季的划分 51   第2篇 Java数据处理   第4章 异常处理(教学视频:62分钟) 54   4.1 编译时异常 54   实例35 除0发生的算术异常(ArithmeticException) 54   实例36 数组下标越界异常(ArrayIndexOutOfBoundsException) 55   实例37 数组元素类型不匹配异常(ArrayStoreException) 56   实例38 强制类型转换异常(ClassCastException) 56   实例39 索引越界异常(IndexOutOfBoundsException) 57   实例40 空指针异常(NullPointerException) 58   实例41 数字格式转换异常(NumberFornatException) 59   实例42 字符串索引越界异常(StringIndexOutBounds) 60   实例43 操作错误(UnsupportedOperationException) 60   4.2 运行时异常 61   实例44 找不到指定类时发生的异常(ClassNotFoundException) 62   实例45 请求的方法不存在(NoSuchMethodException) 63   4.3 try…catch捕获异常 65   实例46 try…catch捕获异常的实例 66   实例47 try…catch…finally捕获异常的实例 67   实例48 try…catch嵌套捕获异常的实例 68   4.4 throws声明异常 69   实例49 throws声明异常实例一 69   实例50 throws声明异常实例二 70   4.5 throw抛出异常 72   实例51 throw抛出异常实例一 72   实例52 throw抛出异常实例二 73   4.6 自定义异常 74   实例53 自定义异常实例一 74   实例54 自定义异常实例二 75   第5章 数组(教学视频:98分钟) 78   5.1 一维数组 78   实例55 一
第1篇 Java编程基础   第1章 Java开发环境的搭建(教学视频:9分钟) 2   1.1 理解Java 2   1.2 搭建Java所需环境 3   1.2.1 下载JDK 3   1.2.2 安装JDK 4   1.2.3 配置环境 5   1.2.4 测试JDK配置是否成功 7   实例1 开发第一个Java程序 7   第2章 Java基础类型与运算符(教学视频:39分钟) 9   2.1 基础类型 9   实例2 自动提升 9   实例3 自动转换 10   实例4 常用基础类型之强制转换 11   2.2 运算符 12   实例5 算术运算符 12   实例6 关系运算符 13   实例7 逻辑运算符 14   实例8 位运算符 15   实例9 移位运算符 16   实例10 转型运算符 17   2.3 其他形式 18   实例11 常量与变量 18   实例12 各种进制的转换 19   实例13 Java中的进制与移位运算符 22   第3章 条件控制语句(教学视频:75分钟) 26   3.1 if控制语句 26   实例14 判断输入的年份是否为闰年 26   实例15 抽奖活动 27   3.2 for语句 28   实例16 小九九乘法表 28   实例17 如何列出素数 29   实例18 Java中的递归 31   实例19 男生女生各多少人 32   实例20 求水仙花数 34   实例21 求任意一个正数的阶乘 35   实例22 求n的n次方 35   实例23 利用for循环输出几何图形 36   实例24 杨辉三角 38   3.3 while语句 39   实例25 求1到100之间的和 39   实例26 存上100元需要多少天 40   实例27 输出100之间的所有偶数 41   实例28 如何判断回文数字 42   3.4 do…while语句 43   实例29 输出100之间的所有奇数 44   实例30 求最大的随机数 44   3.5 switch语句 45   实例31 判断字母分类 46   实例32 优良及差 47   实例33 打印任意一年日历 48   实例34 一年四季的划分 51   第2篇 Java数据处理   第4章 异常处理(教学视频:62分钟) 54   4.1 编译时异常 54   实例35 除0发生的算术异常(ArithmeticException) 54   实例36 数组下标越界异常(ArrayIndexOutOfBoundsException) 55   实例37 数组元素类型不匹配异常(ArrayStoreException) 56   实例38 强制类型转换异常(ClassCastException) 56   实例39 索引越界异常(IndexOutOfBoundsException) 57   实例40 空指针异常(NullPointerException) 58   实例41 数字格式转换异常(NumberFornatException) 59   实例42 字符串索引越界异常(StringIndexOutBounds) 60   实例43 操作错误(UnsupportedOperationException) 60   4.2 运行时异常 61   实例44 找不到指定类时发生的异常(ClassNotFoundException) 62   实例45 请求的方法不存在(NoSuchMethodException) 63   4.3 try…catch捕获异常 65   实例46 try…catch捕获异常的实例 66   实例47 try…catch…finally捕获异常的实例 67   实例48 try…catch嵌套捕获异常的实例 68   4.4 throws声明异常 69   实例49 throws声明异常实例一 69   实例50 throws声明异常实例二 70   4.5 throw抛出异常 72   实例51 throw抛出异常实例一 72   实例52 throw抛出异常实例二 73   4.6 自定义异常 74   实例53 自定义异常实例一 74   实例54 自定义异常实例二 75   第5章 数组(教学视频:98分钟) 78   5.1 一维数组 78   实例55 一维数组的创建与使用 78   实例56 按相反的顺序输出 79   实例57 奇偶分组 80   实例58 找宝 81   实例59 寻找最小数 82   实例60 我的位置在哪里 83   实例61 复制数组 85   实例62 插入新元素 86   实例63 数组的合并 87   实例64 去除重复元素 88   实例65 数组求和计算 90   实例66 求最大值、最小值和平均值 91   5.2 二维数组 92   实例67 二维数组的创建与使用 92   实例68 矩阵转置 93   实例69 奇数阶幻方 94   实例70 求方阵对角线之和 96   实例71 矩阵的加法 97   实例72 矩阵的减法 98   实例73 快递报价单 99   5.3 数组的排序 101   实例74 冒泡排序法 102   实例75 数组递增排序 103   实例76 部分数组递增排序 103   实例77 选择排序法 104   实例78 快速排序法 106   第6章 字符串(教学视频:138分钟) 108   6.1 字符串类String 108   实例79 创建字符串类 108   实例80 如何使用charAt()方法计算重复字符 109   实例81 按字母顺序比较大小 110   实例82 首尾相连 111   实例83 字符串间的比较 112   实例84 字符集的解码方法 113   实例85 寻找指定字符第一次出现的位置 114   实例86 寻找指定字符最后出现的位置 115   实例87 我究竟有多长 116   实例88 替换指定的字符 117   实例89 分割字符串 117   实例90 如何使用substring()方法截取子串 118   实例91 分解字符串 119   实例92 字母大小写转换 120   实例93 去除多余的空白 120   实例94 原始数组类型的String形式 121   实例95 Java合法标识符 122   实例96 显示一周各星期的名称 123   实例97 构造空心方框 124   实例98 这一天是星期几 125   实例99 大小写互换 127   实例100 输出指定范围的素数 128   实例101 我出现了几次 129   实例102 算术表达式求值器 129   实例103 字符串对齐调整器 137   实例104 字符串的加密 139   实例105 使用正则表达式验证电话号码的格式 141   6.2 字符串缓存类StringBuffer 143   实例106 创建字符串缓存类 143   实例107 提取单个字符 144   实例108 给指定字符赋值 145   实例109 插入新的字符 146   实例110 插入新的字符串 146   实例111 获取字符串的子串 147   实例112 删除指定的字符 148   实例113 倒置字符串 149   实例114 去除重复字符 149   实例115 检查是否是回文 151   第7章 输入/输出流(教学视频:116分钟) 152   7.1 文件和目录 152   实例116 显示文件的基本信息 152   实例117 显示目录的基本信息 153   实例118 在指定的目录下创建单个文件 156   实例119 在指定的目录下创建多个临时文件 158   实例120 删除指定目录下的文件 160   实例121 移动指定目录下的文件 163   实例122 文件搜索引挚 167   7.2 字节流 169   实例123 复制指定目录下的文件 170   实例124 显示文件中的内容 173   实例125 将数据保存到指定的文件中 175   实例126 将由键盘中录入的信息保存到文件中 176   实例127 一个文件变成多个小文件 178   实例128 多个小文件合成一个文件 181   实例129 统计指定文件中的字符个数 183   实例130 对象的序列化与反序列化 185   实例131 同时显示多个文件 187   实例132 生成zip压缩文件 189   实例133 解压缩zip文件 192   实例134 生成Excel文件 194   实例135 读取Excel文件中的内容 198   实例136 生成PDF文件 199   实例137 读取PDF文件中的内容 203   实例138 用iText生成Word文件 205   实例139 利用POI读取Word文件中的内容 208   7.3 字符流 209   实例140 按顺序创建文件 210   实例141 按顺序读取文件 211   实例142 追加文件内容 211   实例143 只显示文件中指定的字符 214   实例144 读取jar包文件 215   实例145 文件的加密/解密操作 217   实例146 复制图片 219   实例147 随机读写Java类文件 221   第3篇 Java面向对象编程   第8章 面向对象(教学视频:72分钟) 226   8.1 类 226   实例148 简单的通讯录类 226   实例149 简单的长度单位转换类 227   实例150 卡车和卡车司机之间的关系 229   实例151 双色球 231   8.2 成员变量和方法 236   实例152 使用类作为成员变量 236   实例153 构造方法 237   实例154 使用静态成员变量计算内存中实例化的对象数目 239   实例155 实现加减乘除的方法 240   8.3 面向对象的设计模式 241   实例156 Singleton单例模式 242   实例157 招聘(简单工厂模式) 243   实例158 同学聚会(工厂方法模式) 244   实例159 图书展(抽象工厂模式) 246   实例160 汽车适配器(Adapter适配器模式) 248   8.4 垃圾回收 250   实例161 垃圾回收的机制 250   第9章 面向对象的四大特征(教学视频:65分钟) 252   9.1 抽象 252   实例162 求自定义几何图形的面积和周长 252   实例163 使用抽象方法实现的支票夹 254   9.2 封装 257   实例164 世界小姐参赛资格 257   实例165 自定义复数类 261   9.3 继承 264   实例166 轿车与本田的关系 264   实例167 继承关系的加载顺序 266   实例168 如何访问同名的方法或变量 268   实例169 super()方法的使用 271   实例170 this方法的使用 274   实例171 一张考试成绩单 275   实例172 银行自动存取一体机 278   9.4 多态 284   实例173 饮食文化 284   实例174 使用构造方法的重载计算课程的GPA值 287   第10章 内部类与接口(教学视频:41分钟) 290   10.1 成员内部类 290   实例175 成员内部类的使用规范 290   实例176 猜谜 292   10.2 方法内部类 294   实例177 局部内部类的使用规范 294   实例178 奖学金的评分标准 295   10.3 匿名内部类 297   实例179 匿名内部类的使用规范 297   实例180 电话与移动电话 299   10.4 静态内部类 300   实例181 静态内部类的使用规范 300   实例182 苹果的来历 302   10.5 接口 303   实例183 求n的幂数与倍数 304   实例184 商品订单 306   实例185 多功能排序 310   第11章 Java常用类(教学视频:66分钟) 315   11.1 数学Math类 315   实例186 求圆周率∏值 315   实例187 求对数值 316   实例188 使用取整函数 317   11.2 Random类的使用 318   实例189 随机数 319   实例190 验证码 322   11.3 Date类和Calendar类 324   实例191 使用Date类获取系统的当前时间 324   实例192 使用DateFormat类获取系统的当前时间 325   实例193 使用GregorianCalendar类获取系统的当前时间 326   实例194 使用SimpleDateFormat类获取系统的当前时间 329   实例195 显示某年某月某一周的信息 330   实例196 显示某年某月的信息 332   实例197 时间的设置与获取 334   实例198 万年历(农历和阳历的互换) 337   11.4 Formatter类的使用 347   实例199 时间格式转换符的使用 347   实例200 数据格式转换符的使用 349   11.5 System类的使用 351   实例201 记录程序执行的时间 351   实例202 程序的退出 352   实例203 获取程序运行环境的信息 353   第4篇 Java高级开发技术   第12章 集合(教学视频:45分钟) 358   12.1 Set 358   实例204 利用HashSet删除学生 358   实例205 不重复的随机数序列 360   实例206 运用映射的相关类(Map) 363   实例207 运用集的相关类(Set) 365   12.2 List 368   实例208 增加所需的元素 368   实例209 Iterator迭代器的使用 370   实例210 谁是幸运儿 371   实例211 自定义Queue队列 373   实例212 List、Set与Array之间的相互转换 375   实例213 二分查找法的实现方法 377   实例214 模拟操作系统的进程调度 379   实例215 利用栈将字符串逆序输出 381   实例216 动态的数组链表 382   实例217 你能猜出鱼是谁的宠物吗? 387   实例218 使用Collections类对List的排序操作 393   实例219 LinkedList的添加删除操作 395   实例220 运用Vector 397   实例221 改变Properties文件中的键值 399   第13章 多线程编程(教学视频:121分钟) 405   13.1 多线程的五种基本状态 405   实例222 启动线程 405   实例223 参赛者的比赛生活(线程休眠唤醒) 407   实例224 资源搜索并下载(线程等待和通报) 410   实例225 模拟淘宝购物买卖双方交易问题 412   实例226 携子之手 与子偕老(join) 415   实例227 线程让步(Yield) 417   实例228 会走动的钟(多线程) 419   实例229 变形金刚中的守护神(守护线程) 424   实例230 查看JVM中所有的线程的活动状况 426   实例231 模仿网络快车下载工具下载文件 428   13.2 多线程的同步与互斥 436   实例232 多线程同步方法的实例 436   实例233 ATM存取一体机(线程同步互斥) 437   实例234 我的钱哪里去了 440   实例235 门锁打不开了(死锁) 444   实例236 门锁终于被打开了(解决死锁) 446   实例237 一个死锁的例子 448   13.3 线程的优先级 451   实例238 排座位(线程优先级) 451   实例239 赛车 454   13.4 定时器 458   实例240 定时器(Timer) 458   实例241 数字定时器 459   13.5 线程连接池 462   实例242 手术任务(线程池) 462   实例243 模拟人工服务台(线程连接池) 466   13.6 线程应用实例 471   实例244 下雪的村庄 472   实例245 小飞侠 474   实例246 飞流直下 477   实例247 多线程断点续传 479   实例248 滚动的珠子 485   实例249 余额查询 489   实例250 滚动的文字 492   实例251 漂浮效果 495   实例252 监视内存的使用情况 499   实例253 璀璨的星空 501   实例254 银行和超市业务的模拟 505   第14章 泛型(教学视频:43分钟) 511   14.1 泛型基础 511   实例255 一个关于泛型的简单例子 511   实例256 带两个类型参数的泛型 513   实例257 有界类型程序示例 514   实例258 通配符使用示例 515   实例259 泛型方法使用示例 516   实例260 泛型接口示例 518   实例261 泛型实现坐标打印 519   14.2 泛型类的继承 521   实例262 继承泛型类示例 521   实例263 继承非泛型类示例 522   实例264 泛型类的类型识别示例 523   实例265 强制类型转换示例 525   14.3 擦拭 526   实例266 无限界的擦拭 526   实例267 有限界的擦拭 527   14.4 集合泛型类 528   实例268 Hashtable的泛型化 528   实例269 多功能画笔 529   第15章 网络编程(教学视频:52分钟) 534   15.1 IP地址 534   实例270 获取计算机名与IP地址 534   实例271 获取网址的IP地址 535   实例272 判断两个网址的主机名是否一样 536   实例273 测试IP的类型 537   实例274 查找主机 538   实例275 主机所支持的协议 539   15.2 URL类的使用 540   实例276 使用URL访问网页 540   实例277 URL的组成部分 541   实例278 通过指定的URL可以获取网页的源代码 542   实例279 一对多通信模式 544   实例280 自制浏览器 549   实例281 扫描TCP端口 551   实例282 TCP协议服务器 552   实例283 TCP协议客户机 553   实例284 Socket连接信息 555   实例285 Echo服务的客户端是如何实现的? 556   实例286 检测本机的服务端口 558   实例287 下载的页面不丢失链接 559   实例288 如何对网页进行重新定向 560   实例289 在Internet上搜索对象 560   实例290 LAN使用代理服务器 562   实例291 BBS论坛服务器端 567   实例292 UDP报文的发送与接收 579   第16章 数据库技术(教学视频:38分钟) 583   实例293 加载JDBC驱动程序 583   实例294 通过JDBC对数据库进行查询 584   实例295 数据库更新 585   实例296 获取数据库的基本信息 586   实例297 获取数据库对SQL支持的信息 589   实例298 处理访问数据库出现的常见异常情况 591   实例299 在Servlet中连接数据库 593   实例300 数据分页显示 595   实例301 批处理 601   实例302 事物处理 604   实例303 调用存储过程 608   实例304 连接ODBC数据库 611   实例305 数据库中图片文件的存取 614   第17章 界面设计--Swing(教学视频:88分钟) 618   17.1 Swing组件介绍及应用 618   实例306 JFrame框架的应用 618   实例307 Border的使用 619   实例308 使用Icon组件显示一张图片 620   实例309 Icon接口的应用 621   实例310 JLabel组件 622   实例311 Icon应用到JLabel中 623   实例312 JButton的使用 624   实例313 在JButton上设置快捷键 625   实例314 设置默认按钮 626   实例315 使用JCheckBox组件 627   实例316 JCheckBox事件处理 629   实例317 JRadioButton的使用 630   实例318 JComboBox的使用 632   实例319 利用ComboModel构造JComboBox 633   实例320 DefaultComboBoxModel的使用 635   实例321 建立有图像的JComboBox 636   实例322 建立可自行输入的JComboBox 637   实例323 JComboBox的事件处理 638   实例324 JTextField组件的使用 640   实例325 使用JTextArea组件 641   17.2 页面布局及事件监听 642   实例326 BorderLayout版面布局 643   实例327 FlowLayout布局管理 644   实例328 应用GridLayout设计版面 645   实例329 如何使用BoxLayout布局管理器 647   实例330 使用ActionEvent监听组件 648   实例331 使用WindowAdapter实现鼠标事件 649   实例332 使用MouseMotionListener监听鼠标 650   实例333 使用KeyListener监听键盘 652   17.3 组件的综合应用 654   实例334 计算器 654   实例335 创建树菜单 657   实例336 在节点中显示详细信息 658   第18章 Applet小应用程序(教学视频:39分钟) 662   实例337 不断变大的文字 662   实例338 灯光扫描的效果 664   实例339 字体逐渐展开的效果 666   实例340 飞舞的气球 668   实例341 逐渐浮现的图片 671   实例342 火焰边框的特效 674   实例343 局部放大效果 677   实例344 水波荡漾的效果 679   实例345 漫天飞花 682   实例346 动感影集 685   实例347 彩虹字 688   实例348 多功能按键 690   第19章 多媒体与图像处理(教学视频:51分钟) 694   19.1 多媒体 694   实例349 测试音频播放器 694   实例350 测试视频播放器 696   实例351 视频反色效果 698   实例352 实现一个录音机 703   实例353 建立一个调色板 707   19.2 图像处理 710   实例354 在计算机内存中创建一个图像 710   实例355 会移动的图像 712   实例356 将图片进行180度旋转 714   实例357 处理图像的明暗度 716   实例358 如何将彩色图转化为灰度图 721   实例359 锐化和模糊图像 724   实例360 显示一个图像的轮廓 727   实例361 如何放大及拉伸图像 730   第20章 JSP开发技术(教学视频:62分钟) 735   20.1 JSP基础 735   实例362 网页版的9×9乘法口诀 735   实例363 Java Servlet小程序 737   实例364 Servlet对表单的处理过程 738   实例365 获取/修改JavaBeans中的内容 741   20.2 JSP程序设计 743   实例366 喜连大三元 743   实例367 运气对对碰 748   实例368 文字样式统计器 752   实例369 图形统计器 754   20.3 垃圾信息投诉站 756   实例370 创建投诉表单 756   实例371 保存投诉的内容 760   实例372 实现投诉站的后台操作 762   实例373 取消投诉内容 764   实例374 站内信 767   20.4 网站在线评估系统 769   实例375 创建评估问卷 769   实例376 实现评估的后台操作 771   实例377 查看评估结果 773   第5篇 Java综合案例   第21章 模拟服务器与客户端之间的通信(教学视频:13分钟) 778   21.1 实例介绍 778   21.2 框架设计 779   21.2.1 项目的总体结构 779   21.2.2 搭建开发环境 779   21.2.3 确定项目工程目录 779   21.3 公用模块设计 780   21.4 客户端程序 780   21.4.1 客户端的工作流程 780   21.4.2 编写客户端的源程序 781   21.4.3 客户端源的程序解读 783   21.5 服务器端程序 785   21.5.1 服务器端的工作流程 785   21.5.2 编写服务器端的源程序 785   21.5.3 服务器端的源程序解读 788   21.6 小结 790   第22章 书店管理系统(教学视频:56分钟) 791   22.1 实例分析 791   22.2 系统设计 792   22.2.1 结构分析 792   22.2.2 搭建环境 792   22.2.3 创建目录 793   22.3 数据库设计 793   22.4 公用模块设计 795   22.4.1 data包中的类 795   22.4.2 util包中的类 798   22.5 用户登录模块 800   22.6 系统主界面 802   22.7 基础维护模块 804   22.7.1 图书维护 804   22.7.2 用户维护 811   22.8 借还管理模块 818   22.8.1 借书 818   22.8.2 还书 821   22.9 查询管理模块 824   22.9.1 图书查询 824   22.9.2 用户查询 826   22.10 系统管理模块 829   22.11 小结 831
### 回答1: Java类的加载过程可以分为以下几个步骤: 1. 通过类的全限定名在类路径中查找该类的class文件。 2. 如果class文件存在,Java虚拟机将其读入内存,并对其进行校验。校验的主要目的是验证class文件的内容是否符合Java语言的语法规则。 3. 如果class文件通过了校验,Java虚拟机将其转换为方法区中的数据结构,供程序员在运行时使用。 4. 如果class文件不存在,或者校验失败,Java虚拟机将抛出一个ClassNotFoundException异常。 在Java类加载过程中,Java虚拟机默认使用的是双亲委派模型来管理类的加载。在这种模型中,父类加载器会优先加载类,如果父类加载器无法加载该类,那么子类加载器再加载。这样的好处是可以防止类的重复加载,保证了Java类的单例性。 这个加载过程对于程序员透明,所以无需关心具体的加载过程,只需要关注Java类的使用即可。 ### 回答2: Java类加载过程包括了加载、链接和初始化三个阶段。 1. 加载(Loading):将字节码文件读取到内存中,并创建一个对应的Class对象。这个过程是通过类加载器来实现的。 2. 链接(Linking):链接阶段主要包括了验证、准备和解析三个步骤。 - 验证(Verification):验证阶段会对加载的字节码进行一系列验证,包括语法验证、语义验证和字节码验证等,确保字节码的正确性和安全性。 - 准备(Preparation):准备阶段会为类的静态变量分配内存,并设置默认初始值。同时也会在方法区中创建常量池,并进行一些必要的静态符号的引用。 - 解析(Resolution):解析阶段会将符号引用替换为直接引用,即将类、方法、字段的引用都解析为实际内存地址的引用。 3. 初始化(Initialization):在初始化阶段,会执行类的初始化器<clinit>()方法,该方法由编译器自动生成,包含了类中静态变量赋值静态代码块的执行等。在这个阶段,会按照父类-子类的顺序来初始化类。 总结来说,Java类加载过程就是通过类加载器将字节码文件加载到内存中,然后经过验证、准备和解析等链接阶段,最后进行初始化,执行类中的静态代码块和静态变量赋值等操作。 ### 回答3: Java类加载过程主要分为加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)和初始化(Initialization)五个步骤。 加载阶段是指将类的字节码文件读入内存,并将其转换成相应的数据结构。对于由类加载器所加载的每个类,Java虚拟机都会生成一个唯一的Class对象,用来表示这个类。 验证阶段是为了确保类的字节码符合Java虚拟机的规范,包括检查文件格式、语义一致性、字节码验证等。 准备阶段是为类的静态字段分配内存,并为它们设置初始值。在这个阶段,Java虚拟机会为每个字段分配内存空间,并根据字段的类型设置默认值。注意,这里只分配了内存空间,并没有实际初始化字段的值。 解析阶段是将常量池中的符号引用转换为直接引用,即找到对应的内存地址。这个阶段通常在初始化之前执行,它的目的是为了准备执行初始化阶段所需要的信息。 初始化阶段是类加载的最后一步,主要是对静态变量进行初始化,执行静态代码块中的代码。在这个阶段,Java虚拟机会按照顺序执行静态变量赋值操作和静态代码块中的代码。 需要注意的是,类的加载过程是按需进行的,即在第一次使用这个类之前才会触发其加载过程。同时,类加载是按照委托机制进行的,即先将请求委派给父类加载器,只有在父类加载器找不到对应的类时才会由当前类加载器进行加载。 总的来说,Java类加载过程包括加载、验证、准备、解析和初始化五个阶段,它们按需触发,有严格的顺序,是Java虚拟机保证类加载的正确性和安全性的重要机制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值