Java Class文件结构解析

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: Java代码必须要被编译成class文件后,虚拟机才能够加载运行,要搞清楚Java的类加载机制,首先必须要理解Class文件的内部结构。本文参考了周志明所著《深入理解java虚拟机》一书,并结合自身实践而写。

Java代码必须要被编译成class文件后,虚拟机才能够加载运行,要搞清楚Java的类加载机制,首先必须要理解Class文件的内部结构。
本文参考了周志明所著《深入理解java虚拟机》一书,并结合自身实践而写。

1.Class文件基本结构概述

  • Class文件是一组以8位字节为基础单位的二进制流,当遇到需要8位字节以上空间的数据项时,则会按照高位在前的方式分隔成若干个8位字节进行存储。
  • Class文件由无符号数构成。
  • 无符号数:以u1、u2、u4、u8分别代表1个字节、2个字节、4个字节和8个字节的无符号数,可以用来描述数字、索引引用、数量值、按照UTF-8编码构成的字符串值。
  • :由多个无符号数或其他表作为数据项构成的复杂数据类型,所有表都习惯性地以“_info”结尾。

Class文件格式如下:

类型 描述 备注
u4 magic 魔数:0xCAFEBABE
u2 minor_version 小版本号
u2 major_version 主版本号
u2 constant_pool_count 常量池大小,从1开始
cp_info constant_pool[constant_pool_count - 1] 常量池信息
u2 access_flags 访问标志
u2 this_class 类索引
u2 super_class 父类索引
u2 interfaces_count 接口个数
u2 interfaces[interfaces_count] 接口类索引信息
u2 fields_count 字段数
field_info fields[fields_count] 字段表信息
u2 methods_count 方法数
method_info methods[methods_count] 方法表信息
u2 attributes_count 属性个数
attribute_info attributes[attributes_count] 属性表信息

2.一个典型的Class文件

//接口类
public interface Car {
    void drive();
}

//实现类
public class BMWCar implements Car{

    private String name;
    
    public BMWCar() {
        name = "宝马";
    }
    
    @Override
    public void drive() {
        System.out.println("BMW car drive." + name);
    }
    
}

采用javac命令编译以上代码后,用Sublime编辑器打开BMWCar.class文件,内容如下:

cafe babe 0000 0034 002f 0a00 0c00 1708
0018 0900 0b00 1909 001a 001b 0700 1c0a
0005 0017 0800 1d0a 0005 001e 0a00 0500
1f0a 0020 0021 0700 2207 0023 0700 2401
0004 6e61 6d65 0100 124c 6a61 7661 2f6c
616e 672f 5374 7269 6e67 3b01 0006 3c69
6e69 743e 0100 0328 2956 0100 0443 6f64
6501 000f 4c69 6e65 4e75 6d62 6572 5461
626c 6501 0005 6472 6976 6501 000a 536f
7572 6365 4669 6c65 0100 0b42 4d57 4361
722e 6a61 7661 0c00 1000 1101 0006 e5ae
9de9 a9ac 0c00 0e00 0f07 0025 0c00 2600
2701 0017 6a61 7661 2f6c 616e 672f 5374
7269 6e67 4275 696c 6465 7201 000e 424d
5720 6361 7220 6472 6976 652e 0c00 2800
290c 002a 002b 0700 2c0c 002d 002e 0100
0642 4d57 4361 7201 0010 6a61 7661 2f6c
616e 672f 4f62 6a65 6374 0100 0343 6172
0100 106a 6176 612f 6c61 6e67 2f53 7973
7465 6d01 0003 6f75 7401 0015 4c6a 6176
612f 696f 2f50 7269 6e74 5374 7265 616d
3b01 0006 6170 7065 6e64 0100 2d28 4c6a
6176 612f 6c61 6e67 2f53 7472 696e 673b
294c 6a61 7661 2f6c 616e 672f 5374 7269
6e67 4275 696c 6465 723b 0100 0874 6f53
7472 696e 6701 0014 2829 4c6a 6176 612f
6c61 6e67 2f53 7472 696e 673b 0100 136a
6176 612f 696f 2f50 7269 6e74 5374 7265
616d 0100 0770 7269 6e74 6c6e 0100 1528
4c6a 6176 612f 6c61 6e67 2f53 7472 696e
673b 2956 0021 000b 000c 0001 000d 0001
0002 000e 000f 0000 0002 0001 0010 0011
0001 0012 0000 002b 0002 0001 0000 000b
2ab7 0001 2a12 02b5 0003 b100 0000 0100
1300 0000 0e00 0300 0000 0600 0400 0700
0a00 0800 0100 1400 1100 0100 1200 0000
3900 0300 0100 0000 1db2 0004 bb00 0559
b700 0612 07b6 0008 2ab4 0003 b600 08b6
0009 b600 0ab1 0000 0001 0013 0000 000a
0002 0000 000c 001c 000d 0001 0015 0000
0002 0016 

3.魔数

img_c50a091c101f9a96ecb01e1f9984af18.png
magic

4.版本号

img_828e346b29659c1673feb36d34860426.png
version

次版本号是:0000
主版本号是:0034,十进制是52,表示采用的是jdk1.8

5.常量池

版本号后面是常量池,常量池中常量的数量是不固定的,所以常量池的入口处有一个u2类型的数据,表示常量池中常量的数值大小。
img_0caacd7ecae08afb90564286b0b06a56.png
constant_pool_count

0x002f十进制数值是47,表示常量池常量数为46(注意常量池计数是从1而不是0开始),使用“javap -v BMWCar”命令可以查看Class文件的信息如下:

  Last modified 2017-11-10; size 644 bytes
  MD5 checksum ac6d7477d45479490e4ea3f660b1dcdd
  Compiled from "BMWCar.java"
public class BMWCar implements Car
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #12.#23        // java/lang/Object."<init>":()V
   #2 = String             #24            // 宝马
   #3 = Fieldref           #11.#25        // BMWCar.name:Ljava/lang/String;
   #4 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;
   #5 = Class              #28            // java/lang/StringBuilder
   #6 = Methodref          #5.#23         // java/lang/StringBuilder."<init>":()V
   #7 = String             #29            // BMW car drive.
   #8 = Methodref          #5.#30         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #9 = Methodref          #5.#31         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #10 = Methodref          #32.#33        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #11 = Class              #34            // BMWCar
  #12 = Class              #35            // java/lang/Object
  #13 = Class              #36            // Car
  #14 = Utf8               name
  #15 = Utf8               Ljava/lang/String;
  #16 = Utf8               <init>
  #17 = Utf8               ()V
  #18 = Utf8               Code
  #19 = Utf8               LineNumberTable
  #20 = Utf8               drive
  #21 = Utf8               SourceFile
  #22 = Utf8               BMWCar.java
  #23 = NameAndType        #16:#17        // "<init>":()V
  #24 = Utf8               宝马
  #25 = NameAndType        #14:#15        // name:Ljava/lang/String;
  #26 = Class              #37            // java/lang/System
  #27 = NameAndType        #38:#39        // out:Ljava/io/PrintStream;
  #28 = Utf8               java/lang/StringBuilder
  #29 = Utf8               BMW car drive.
  #30 = NameAndType        #40:#41        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #31 = NameAndType        #42:#43        // toString:()Ljava/lang/String;
  #32 = Class              #44            // java/io/PrintStream
  #33 = NameAndType        #45:#46        // println:(Ljava/lang/String;)V
  #34 = Utf8               BMWCar
  #35 = Utf8               java/lang/Object
  #36 = Utf8               Car
  #37 = Utf8               java/lang/System
  #38 = Utf8               out
  #39 = Utf8               Ljava/io/PrintStream;
  #40 = Utf8               append
  #41 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #42 = Utf8               toString
  #43 = Utf8               ()Ljava/lang/String;
  #44 = Utf8               java/io/PrintStream
  #45 = Utf8               println
  #46 = Utf8               (Ljava/lang/String;)V
{
  public BMWCar();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: ldc           #2                  // String 宝马
         7: putfield      #3                  // Field name:Ljava/lang/String;
        10: return
      LineNumberTable:
        line 6: 0
        line 7: 4
        line 8: 10

  public void drive();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #5                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
        10: ldc           #7                  // String BMW car drive.
        12: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: aload_0
        16: getfield      #3                  // Field name:Ljava/lang/String;
        19: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        22: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        25: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        28: return
      LineNumberTable:
        line 12: 0
        line 13: 28
}
SourceFile: "BMWCar.java"

从这里也可以看到,常量池的常量数为46。常量池中主要存放2大类常量:字面量(Literal)和符号引用(Symbolic References)。
字面量主要指文本字符串、被声明为final的常量值。
符号引用主要包括以下3类:

  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符

常量池中的每个常量都是一个表,共有11种不同的表结构,它们有一个共同的特点,就是表开始的第一位都是一个u1类型的标志位(tag,取值为1到12,缺少标志为2的数据类型)。
tag表示的数据类型如下表所示:

类型 标志 描述
CONSTANT_Utf8_info 1 UTF-8编码的字符串
CONSTANT_Integer_info 3 整形字面量,boolean、byte、char、short等类型都用int存放
CONSTANT_Float_info 4 浮点型字面量
CONSTANT_Long_info 5 长整型字面量
CONSTANT_Double_info 6 双精度浮点型字面量
CONSTANT_Class_info 7 类或接口的符号引用
CONSTANT_String_info 8 字符串类型字面量
CONSTANT_Fieldref_info 9 字段的符号引用
CONSTANT_Methodref_info 10 类中方法的符号引用
CONSTANT_InterfaceMethodref 11 接口中方法的符号引用
CONSTANT_NameAndType_info 12 字段或方法的部分符号引用

从以上表中可以看到,1-12除了类型2之外,共11种表数据类型,以下是一份常量池数据结构总表:

img_dbedc508a562a9f7469bfbba430b2782.png
常量数据结构表

接下来我们分析常量池里的第一个常量:

img_673d242aaa320fece6aa68c47390c5a4.png
第一个常量

这里可以看到,第一个常量的tag = 10,从前面的常量数据结构表中可以看到,10表示该常量为CONSTANT_Methodref_info,表示方法的符号引用,接下来有两个字节class_index = 12,指向常量池中索引值为12的常量CONSTANT_Class_info,再接下来2个字节name_and_type_index = 23,指向常量池中索引值为23的CONSTANT_NameAndType_info,与前面我们采用java命令查看class的信息是一致的。

  #1 = Methodref          #12.#23        // java/lang/Object."<init>":()V
  #12 = Class              #35            // java/lang/Object
  #23 = NameAndType        #16:#17        // "<init>":()V

这里也可以看到第1个常量是一个方法的符号引用,它指向了第12个常量和第23个常量,第12个常量表示类的符号引用,它指向了第35个常量,第23个常量又指向了第16、17个常量。
这样一个一个常量去分析,我们就可以计算出整个常量池在class文件中所占的字节空间,如下图所示:

img_b47d76d9fe618e7ffa2ac2e62c7214c3.png

6.访问标志

常量池之后,是2个字节来表示访问标志,用于识别一些类或者接口层次的访问信息。

标志名称 标志值 含义
ACC_PUBLIC 0x0001 是否为public类型
ACC_FINAL 0x0010 是否被声明为final,只有类可以设置
ACC_SUPER 0x0020 JDK1.0.2以后这个标志都为真
ACC_INTERFACE 0x0200 标识这是一个接口
ACC_ABSTRACT 0x0400 是否为abstract类型,对于接口或抽象类来说,此标志值为真,其他类值为假
ACC_SYNTHETIC 0x1000 标识这个类并非由用户代码产生
ACC_ANNOTATION 0x2000 标识这是一个注解
ACC_ENUM 0x4000 标识这是一个枚举

针对BMWCar这个类来说,其访问标志应该是ACC_PUBLIC、ACC_SUPER这2个标志为真,所以其值为 0x0001 | 0x0020 = 0x0021。

img_1eec40d461977bc15d415047279024e8.png
access_flag

7.类索引、父类索引

img_525adc8f607859f69f3cfce104266544.png

访问标志后的2个字节000b = 11,指向常量池里的第11个常量,接下来的000c = 12,指向常量池里的第12个常量(该类的父类是Object)

8.接口索引

img_df68af6540d0f3e281077119101d848a.png

在本例子中,只有一个接口,0x000d十进制就是13,指向常量池中第13个常量,表示接口信息。

9.字段表

接口索引后面紧跟着的是字段表信息,字段表的入口前2个字节表示字段的个数,在本例子中只定义了个一个字段,所以其值为0x0001,后面紧跟着的是该字段的描述表。字段信息结构表如下:

类型 描述 备注
u2 access_flags 记录字段的访问标志
u2 name_index 常量池中的索引项,指定字段的名称
u2 descriptor_index 常量池中的索引项,指定字段的描述符
u2 attributes_count attributes包含的项目数
attribute_info attributes[attributes_count]

字段访问标志:

权限名称 描述
ACC_PUBLIC 0x0001 public
ACC_PRIVATE 0x0002 private
ACC_PROTECTED 0x0004 protected
ACC_STATIC 0x0008 static,静态
ACC_FINAL 0x0010 final
ACC_VOLATILE 0x0040 volatile,不可和ACC_FIANL一起使用
ACC_TRANSIENT 0x0080 在序列化中被忽略的字段
ACC_SYNTHETIC 0x1000 由编译器产生,不存在于源代码中
ACC_ENUM 0x4000 enum

紧随access_flags标志的是name_index和descriptor_index,他们都是对常量池的引用。name_index代表着字段的简单名称,descriptor_index代表着字段的描述符。描述符的作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。
描述符标识字符含义:

标识字符 含义
B 基本类型byte
C 基本类型char
D 基本类型double
F 基本类型float
I 基本类型int
J 基本类型long
S 基本类型short
Z 基本类型boolean
V 特殊类型void
L 对象类型,如Ljava/lang/Object;
[ 数组类型,多个维度则有多个[

用描述符来描述方法时,按照先参数列表后返回值的顺序描述,参数列表按照参数的严格顺序放在一组“()”之内。
例如方法int getAge()的描述符为“()I”,方法void print(String msg)的描述符为“(Ljava/lang/String;)V”,方法int indexOf(int index, char[] arr)的描述符为“(I[C)I”。

再来看我们这个字节码:

img_3b9a05512697bd5797e71c75ffc86c1a.png
field

我们来具体分析下字节码:

  • 前两个字节0x0001表示字段计数值为1,即该类有1个字段;
  • 0x0002是第一个字段的访问标志,这里为private;
  • 0x000e是name_index,查找常量池可知其值为“name”;
  • 0x000f是descriptor_index,查找常量池可知其值为“Ljava/lang/String;”,至此通过字节码分析,我们可以反推出这个字段的定义为:private String name;
  • 0x0000表示attribute_count = 0,说明本字段没有额外的描述信息。但是如果该字段的声明为“private static final String name = "123"”,那就会存在一项名为ConstantValue的属性,其值指向常量“123”;

10.方法表

方法表的结构与字段表的结构是一样的。

类型 描述 备注
u2 access_flags 记录方法的访问标志
u2 name_index 常量池中的索引项,指定方法的名称
u2 descriptor_index 常量池中的索引项,指定方法的描述符
u2 attributes_count attributes包含的项目数
attribute_info attributes[attributes_count]

再来看看我们这个例子:

img_2b5e78d5cafb7f57c3d1e4a8de1bb1d3.png
method
  • 前2个字节0x0002表示有2个方法,一个是编译器添加的实例构造器<init>方法,一个是源码中的drive()方法;
  • 接下来2个字节是访问标志,0x0001表示第一个方法是public;
  • name_index值为0x0010,查找常量表可得方法名为“<init>”;
  • descriptor_index值为0x0011,对应常量值为“()V”,所以我们可以反推出该方法的定义为:public void <init>(),这其实是编译器自动添加的一个实例构造器方法,我们的源码里并没有该方法;
  • 属性表计数器为0x0001,表示只有一个属性,接下来就是该方法的第一个属性表。第一个属性表对应的属性名称索引值为0x0012,对应常量值为“Code”,说明此属性是方法的字节码描述;
  • 行文至此,方法的定义可以通过access_flags、name_index、descriptor_index来表达清楚,但是方法里的代码去哪里了呢?前面刚提到该方法有一个名为“code”的属性,这个属性就存储了方法里的java代码编译后的字节码指令。

11.属性表

在Class文件、字段表、方法表中都可以携带自己的属性表集合,以用于描述某些场景专有的信息。

属性名称 使用位置 含义
Code 方法表 Java代码编译成的字节码指令
ConstantValue 字段表 final关键字定义的常量值
Deprecated 类、方法表、字段表 被声明为deprecated的方法和字段
Exceptions 方法表 方法抛出的异常
InnerClasses 类文件 内部类列表
LineNumberTable Code属性 Java源码的行号与字节码指令的对应关系
LocalVariableTable Code属性 方法的局部变量描述
SourceFile 类文件 源文件名称
Synthetic 类、方法表、字段表 标识方法或字段为编译器自动生成的

不同的属性有不同的数据结构,在这里我们挑选2个最重要的属性来讲解一下:

11.1 Code属性

Java方法里的代码被编译处理后,变为字节码指令存储在方法表的Code属性里,但并不是所有的方法表里都有Code属性,例如接口或抽象类中的方法就可能没有该属性。

Code属性数据结构:

类型 名称 含义
u2 attribute_name_index 属性名称索引
u4 attribute_length 属性长度
u2 max_stack 操作数栈深度的最大值
u2 max_locals 局部变量表所需的存储空间
u4 code_length 字节码长度
u1 code[code_length] 存储字节码指令的一系列字节流
u2 exception_table_length 异常表长度
exception_info exception_table
u2 attributes_count
attribute_info attributes[attributes_count]

可以看到Code属性数据结构里还包含有其他属性,主要有LineNumberTable、LocalVariableTable。
LineNumberTable属性数据结构为:

LineNumberTable_attribute {
    u2 attribute_name_index;        //属性名称索引
    u4 attribute_length;            //属性长度
    u2 line_number_table_length;
    {   u2 start_pc;            //字节码行号
        u2 line_number;         //java源码行号
    } line_number_table[line_number_table_length];
}

我们接着前面方法表的字节码继续分析,前面分析到<init>方法只有一个名为Code的属性值,参照Code属性的数据结构我们可以看到如下图所示:

img_6c41baebe17539f202dc3e661819fae5.png
Code
11.2 ConstantValue属性

只有当一个字段被声明为static final时,并且该字段是基本数据类型或String类型时,编译器才会在字段的属性表集合中增加一个名为ConstantValue的属性,所以ConstantValue属性只会出现在字段表中,其数据结构为:

类型 名称 含义
u2 attribute_name_index 属性名称索引
u2 attribute_length 属性长度
u2 constantvalue_index 常量池常量的索引

12. 字段不同访问标志的初始化赋值差异

我们先来看个例子,里面定义了3种不同访问标志的字段:

public class Simple {

    public static final String a = "abc";
    
    public static String b = "abc";

    public String c = "abc";
    
}

这个类里面定义了3个不同的变量,都是赋值成“abc”,但是虚拟机对它们的赋值时机是不同的:

  1. 对于非静态(无static修饰)的字段,赋值会在实例构造方法<init>()方法中进行。
  2. 对于static修饰的字段:如果有final修饰并且该字段是基本数据类型或String类型,则会在该字段对应的字段表field_info中增加一个名为ConstantValue的属性,赋值的时候使用该ConstantValue进行赋值;如果有final修饰,但该字段不是基本数据类型及String类型,则会在类构造方法<clinit>
    ()中赋值;如果没有final修饰,则会在类构造方法<clinit>()中赋值。
访问标志 数据类型 赋值策略
static final 基本数据类型或String ConstantValue
static final 除基本数据和String以外 <clinit>()方法
static 任意类型 <clinit>()方法
无static 任意类型 <init>()方法

采用javac Simple.java编译该类之后,同样采用javap -v Simple.class查看class文件的字节码信息,可以看到这3个字段的赋值方式:

img_04dfa34caa025fe88cdb854ea6edcfd4.png

java类加载机制系列文章:

目录
相关文章
|
12天前
|
人工智能 自然语言处理 Java
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
FastExcel 是一款基于 Java 的高性能 Excel 处理工具,专注于优化大规模数据处理,提供简洁易用的 API 和流式操作能力,支持从 EasyExcel 无缝迁移。
70 9
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
|
10天前
|
自然语言处理 文字识别 数据处理
多模态文件信息抽取:技术解析与实践评测!
在大数据和人工智能时代,企业和开发者面临的挑战是如何高效处理多模态数据(文本、图像、音频、视频)以快速提取有价值信息。传统方法效率低下,难以满足现代需求。本文将深度评测阿里云的多模态文件信息抽取解决方案,涵盖部署、应用、功能与性能,揭示其在复杂数据处理中的潜力。通过自然语言处理(NLP)、计算机视觉(CV)、语音识别(ASR)等技术,该方案助力企业挖掘多模态数据的价值,提升数据利用效率。
29 4
多模态文件信息抽取:技术解析与实践评测!
|
9天前
|
文字识别 自然语言处理 算法
从多模态到精准洞察:深度解析多模态文件信息提取解决方案!
阿里云推出《多模态数据信息提取》解决方案,涵盖文本、图像、音频、视频等多种数据形式的自动化处理。本文从部署体验、功能验证到实际应用,全面解析该方案的能力与潜力,帮助开发者高效提取和整合复杂数据,提升工作效率...
31 3
从多模态到精准洞察:深度解析多模态文件信息提取解决方案!
|
19天前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
18天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
18天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
18天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
17天前
|
Java 数据库连接 Spring
反射-----浅解析(Java)
在java中,我们可以通过反射机制,知道任何一个类的成员变量(成员属性)和成员方法,也可以堆任何一个对象,调用这个对象的任何属性和方法,更进一步我们还可以修改部分信息和。
|
1月前
|
存储 算法 Java
Java内存管理深度解析####
本文深入探讨了Java虚拟机(JVM)中的内存分配与垃圾回收机制,揭示了其高效管理内存的奥秘。文章首先概述了JVM内存模型,随后详细阐述了堆、栈、方法区等关键区域的作用及管理策略。在垃圾回收部分,重点介绍了标记-清除、复制算法、标记-整理等多种回收算法的工作原理及其适用场景,并通过实际案例分析了不同GC策略对应用性能的影响。对于开发者而言,理解这些原理有助于编写出更加高效、稳定的Java应用程序。 ####
|
1月前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####

推荐镜像

更多