Java 何时会触发一个类的初始化
Java 何时会触发一个类的初始化?
- 使用
new
关键字创建对象 - 访问类的静态成员变量 或 对类的静态成员变量进行赋值
- 调用类的静态方法
- 反射调用类时,如
Class.forName()
- 初始化子类时,会先初始化其父类(如果父类还没有进行过初始化的话)
- 遇到启动类时,如果一个类被标记为启动类(即包含
main
方法),虚拟机会先初始化这个主类。 - 实现带有默认方法的接口的类被初始化时(拥有被
default
关键字修饰的接口方法的类) - 使用 JDK7 新加入的动态语言支持时
MethodHandle
虚拟机在何时加载类
关于在什么情况下需要开始类加载的第一个阶段,《Java虚拟机规范》中并没有进行强制约束,留给虚拟机自由发挥。但对于初始化阶段,虚拟机规范则严格规定:当且仅当出现以下六种情况时,必须立即对类进行初始化,而加载、验证、准备自然需要在此之前进行。虚拟机规范中对这六种场景中的行为称为对一个类型进行主动引用。除此之外,所有引用类型的方式都不会触发初始化,称为被动引用。
1. 遇到指定指令时
在程序执行过程中,遇到 new、getstatic、putstatic、invokestatic 这4条字节码执行时,如果类型没有初始化,则需要先触发其初始化阶段。
new
这没什么好说的,使用new
关键字创建对象,肯定会触发该类的初始化。
getstatic 与 putstatic
当访问某个类或接口的静态变量,或对该静态变量进行赋值时,会触发类的初始化。首先来看第一个例子:
// 示例1
public class Demo {public static void main(String[] args) {System.out.println(Bird.a);}
}class Bird {static int a = 2;// 在类初始化过程中不仅会执行构造方法,还会执行类的静态代码块// 如果静态代码块里的语句被执行,说明类已开始初始化static {System.out.println("bird init");}
}
执行后会输出:
bird init
2
同样地,如果直接给Bird.a
进行赋值,也会触发Bird
类的初始化:
public class Demo {public static void main(String[] args) {Bird.a = 2;}
}class Bird {static int a;static {System.out.println("bird init");}
}
执行后会输出:
bird init
接着再看下面的例子:
public class Demo {public static void main(String[] args) {Bird.a = 2;}
}class Bird {// 与前面的例子不同的是,这里使用 final 修饰static final int a = 2;static {System.out.println("bird init");}
}
执行后不会有输出。
本例中,a
不再是一个静态变量,而变成了一个常量,运行代码后发现,并没有触发Bird
类的初始化流程。常量在编译阶段会存入到调用这个常量的方法所在类的常量池中。本质上,调用类并没有直接引用定义常量的类,因此并不会触发定义常量的类的初始化。即这里已经将常量a=2
存入到Demo
类的常量池中,这之后,Demo
类与Bird
类已经没有任何关系,甚至可以直接把Bird
类生成的class
文件删除,Demo
仍然可以正常运行。使用javap
命令反编译一下字节码:
// 前面已省略无关部分public static void main(java.lang.String[]);Code:0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: iconst_24: invokevirtual #4 // Method java/io/PrintStream.println:(I)V7: return
}
从反编译后的代码中可以看到:Bird.a
已经变成了助记符iconst_2
(将int
类型2
推送至栈顶),和Bird
类已经没有任何联系,这也从侧面证明,只有访问类的静态变量才会触发该类的初始化流程,而不是其他类型的变量。
关于Java助记符,如果将上面一个示例中的常量修改为不同的值,会生成不同的助记符,比如:
// bipush 20
static int a = 20;
// 3: sipush 130
static int a = 130
// 3: ldc #4 // int 327670
static int a = 327670;
其中:
iconst_n
:将int
类型数字n
推送至栈顶,n
取值0~5
lconst_n
:将long
类型数字n
推送至栈顶,n
取值0,1
,类似的还有fconst_n
、dconst_n
bipush
:将单字节的常量值(-128~127
) 推送至栈顶
sipush
:将一个短整类型常量值(-32768~32767
) 推送至栈顶
ldc
:将int
、float
或String
类型常量值从常量池中推送至栈顶
再看下一个实例:
public class Demo {public static void main(String[] args) {System.out.println(Bird.a);}
}class Bird {static final String a = UUID.randomUUID().toString();static {System.out.println("bird init");}
}
执行后会输出:
bird init
d01308ed-8b35-484c-b440-04ce3ecb7c0e
在本例中,常量a
的值在编译时不能确定,需要进行方法调用,这种情况下,编译后会产生getstatic
指令,同样会触发类的初始化,所以才会输出bird init
。看下反编译字节码后的代码:
// 已省略部分无关代码
public static void main(java.lang.String[]);Code:0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: getstatic #3 // Field com/hicsc/classloader/Bird.a:Ljava/lang/String;6: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V9: return
invokestatic
调用类的静态方法时,也会触发该类的初始化。比如:
public class Demo {public static void main(String[] args) {Bird.fly();}
}class Bird {static {System.out.println("bird init");}static void fly() {System.out.println("bird fly");}
}
执行后会输出:
bird init
bird fly
通过本例可以证明,调用类的静态方法,确实会触发类的初始化。
2. 反射调用时
使用java.lang.reflect
包的方法对类型进行反射调用的时候,如果类型没有进行过初始化,则需要先触发其初始化。来看下面的例子:
ublic class Demo {public static void main(String[] args) throws Exception {ClassLoader loader = ClassLoader.getSystemClassLoader();Class clazz = loader.loadClass("com.hicsc.classloader.Bird");System.out.println(clazz);System.out.println("——————");clazz = Class.forName("com.hicsc.classloader.Bird");System.out.println(clazz);}
}class Bird {static {System.out.println("bird init");}
}
执行后输出结果:
class com.hicsc.classloader.Bird
------------
bird init
class com.hicsc.classloader.Bird
本例中,调用ClassLoader
方法load
一个类,并不会触发该类的初始化,而使用反射包中的forName
方法,则触发了类的初始化。
3. 初始化子类时
当初始化类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。比如:
public class Demo {public static void main(String[] args) throws Exception {Pigeon.fly();}
}class Bird {static {System.out.println("bird init");}
}class Pigeon extends Bird {static {System.out.println("pigeon init");}static void fly() {System.out.println("pigeon fly");}
}
执行后输出:
bird init
pigeon init
pigeon fly
本例中,在main
方法调用Pigeon
类的静态方法,最先初始化的是父类Bird
,然后才是子类Pigeon
。因此,在类初始化时,如果发现其父类并未初始化,则会先触发父类的初始化。
对子类调用父类中存在的静态方法,只会触发父类初始化而不会触发子类的初始化。
看下面的例子,可以先猜猜运行结果:
public class Demo {public static void main(String[] args) {Pigeon.fly();}
}class Bird {static {System.out.println("bird init");}static void fly() {System.out.println("bird fly");}
}class Pigeon extends Bird {static {System.out.println("pigeon init");}
}
输出:
bird init
bird fly
本例中,由于fly
方法是定义在父类中,那么方法的拥有者就是父类,因而,使用Pigeno.fly()
并不是表示对子类的主动引用,而是表示对父类的主动引用,所以,只会触发父类的初始化。
4. 遇到启动类时
当虚拟机启动时,如果一个类被标记为启动类(即:包含main
方法),虚拟机会先初始化这个主类。比如:
public class Demo {static {System.out.println("main init");}public static void main(String[] args) throws Exception {Bird.fly();}
}class Bird {static {System.out.println("bird init");}static void fly() {System.out.println("bird fly");}
}
执行后输出:
main init
bird init
bird fly
5. 实现带有默认方法的接口的类被初始化时
当一个接口中定义了 JDK8 新加入的默认方法(被default
关键字修饰的接口方法) 时,如果有这个接口的实现类发生了初始化,那该接口要在其之前被初始化。
由于接口中没有static{}
代码块,怎么判断一个接口是否初始化?来看下面这个例子:
public class Demo {public static void main(String[] args) throws Exception {Pigeon pigeon = new Pigeon();}
}interface Bird {// 如果接口被初始化,那么这句代码一定会执行// 那么Intf类的静态代码块一定会被执行public static Intf intf = new Intf();default void fly() {System.out.println("bird fly");}
}class Pigeon implements Bird {static {System.out.println("pigeon init");}
}class Intf {{System.out.println("interface init");}
}
执行后输出:
interface init
pigeon init
可知,接口确实已被初始化,如果把接口中的default
方法去掉,那么不会输出interface init
,即接口未被初始化。
6. 使用JDK7新加入的动态语言支持时
当使用JDK7新加入的动态类型语言支持时,如果一个java.lang.invoke.MethodHandle
实例最后的解析结果为REF_getStatic
、REF_putStatic
、REF_invokeStatic
、REF_newInvokeSpecial
四种类型的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需要先触发其初始化。
简单点来说,当初次调用MethodHandle
实例时,如果其指向的方法所在类没有进行过初始化,则需要先触发其初始化。
什么是动态类型语言:
- 动态类型语言的关键特性是它的类型检查的主体过程是在运行期进行的,常见的语言比如:JavaScript、PHP、Python等,相对地,在编译期进行类型检查过程的语言,就是静态类型语言,比如 Java 和 C# 等。
- 简单来说,对于动态类型语言,变量是没有类型的,变量的值才具有类型,在编译时,编译器最多只能确定方法的名称、参数、返回值这些,而不会去确认方法返回的具体类型以及参数类型。
- 而Java等静态类型语言则不同,你定义了一个整型的变量x,那么x的值也只能是整型,而不能是其他的,编译器在编译过程中就会坚持定义变量的类型与值的类型是否一致,不一致编译就不能通过。因此,「变量无类型而变量值才有类型」是动态类型语言的一个核心特征。
关于MethodHandle
与反射的区别,可以参考周志明著「深入理解Java虚拟机」第8.4.3小节,这里引用部分内容,方便理解。
- Reflection 和 MethodHandle 机制本质上都是在模拟方法调用,但是 Reflection 是在模拟 Java 代码层次的方法调用,而 MethodHandle 是在模拟字节码层次的方法调用。
- 反射中的 Method 对象包含了方法签名、描述符以及方法属性列表、执行权限等各种信息,而 MethodHandle 仅包含执行该方法的相关信息,通俗来讲:Reflection 是重量级,而 MethodHandle 是轻量级。
总的来说,反射是为 Java 语言服务的,而 MethodHandle
则可为所有 Java 虚拟机上的语言提供服务。
来看一个简单的示例:
public class Demo {public static void main(String[] args) throws Exception {new Pigeon().fly();}
}class Bird {static {System.out.println("bird init");}static void fly() {System.out.println("bird fly");}
}class Pigeon {void fly() {try {MethodHandles.Lookup lookup = MethodHandles.lookup();// MethodType.methodType 方法的第一个参数是返回值// 然后按照目标方法接收的参数的顺序填写参数类型// Bird.fly() 方法返回值是空, 没有参数MethodType type = MethodType.methodType(void.class);MethodHandle handle = lookup.findStatic(Bird.class, "fly", type);handle.invoke();} catch (Throwable a) {a.printStackTrace();}}
}
在Pigeon
类中,使用MethodHandle
来调用Bird
类中的静态方法fly
,按照前面所述,初次调用MethodHandle
实例时,如果其指向的方法所在类没有进行过初始化,则需要先触发其初始化。所以,这里一定会执行Bird
类中的静态代码块。而最终的运行结果也与我们预计的一致:
bird init
bird fly
虚拟机如何加载类 - 类的加载过程
类的加载全过程包括:加载、验证、准备、解析和初始化 5 个阶段,是一个非常复杂的过程。
加载 Loading
Loading 阶段主要是找到类的class文件,并把文件中的二进制字节流读取到内存,然后在内存中创建一个java.lang.Class
对象。
加载完成后,就进入连接阶段,但需要注意的是,加载阶段与连接阶段的部分动作(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始,但这些夹在加载阶段之中进行的动作,仍然属于连接阶段的一部分,这两个阶段的开始时间仍然保持着固定的先后顺序,也就是只有加载阶段开始后,才有可能进入连接阶段。
验证 Verification
验证是连接阶段的首个步骤,其目的是确保被加载的类的正确性,即要确保加载的字节流信息要符合《Java虚拟机规范》的全部约束要求,确保这些信息被当做代码运行后不会危害虚拟机自身的安全。
其实,Java 代码在编译过程中,已经做了很多安全检查工作,比如,不能将一个对象转型为它未实现的类型、不能使用未初始化的变量(赋值除外)、不能跳转到不存在的代码行等等。但 JVM 仍要对这些操作作验证,这是因为 Class 文件并不一定是由 Java 源码编译而来,甚至你都可以通过键盘自己敲出来。如果 JVM 不作校验的话,很可能就会因为加载了错误或有恶意的字节流而导致整个系统受到攻击或崩溃。所以,验证字节码也是 JVM 保护自身的一项必要措施。
整个验证阶段包含对文件格式、元数据、字节码、符号引用等信息的验证。
准备 Preparation
这一阶段主要是为类的静态变量分配内存,并将其初始化为默认值。这里有两点需要注意:
- 仅为类的静态变量分配内存并初始化,并不包含实例变量
- 初始化为默认值,比如
int
为0
,引用类型初始化为null
需要注意的是,准备阶段的主要目的并不是为了初始化,而是为了为静态变量分配内存,然后再填充一个初始值而已。就比如:
// 在准备阶段是把静态类型初始化为 0,即默认值
// 在初始化阶段才会把 a 的值赋为 1
public static int a = 1;
来看一个实例加深印象,可以先考虑一下运行结果。
public class StaticVariableLoadOrder {public static void main(String[] args) {Singleton singleton = Singleton.getInstance();System.out.println("counter1:" + Singleton.counter1);System.out.println("counter2:" + Singleton.counter2);}
}class Singleton {public static Singleton instance = new Singleton();private Singleton() {counter1++;counter2++;System.out.println("构造方法里:counter1:" + counter1 + ", counter2:" + counter2);}public static int counter1;public static int counter2 = 0;public static Singleton getInstance() {return instance;}
}
其运行结果是:
构造方法里:counter1:1, counter2:1
counter1:1
counter2:0
在准备阶段,counter1
和counter2
都被初始化为默认值0
,因此,在构造方法中自增后,它们的值都变为1
,然后继续执行初始化,仅为counter2
赋值为0
,counter1
的值不变。
如果你理解了这段代码,再看下面这个例子,想想会输出什么?
// main 方法所在类的代码不变
// 修改了 counter1 的位置,并为其初始化为 1
class Singleton {public static int counter1 = 1;public static Singleton instance = new Singleton();private Singleton() {counter1++;counter2++;System.out.println("构造方法里:counter1:" + counter1 + ", counter2:" + counter2);}public static int counter2 = 0;public static Singleton getInstance() {return instance;}
}
运行后输出:
构造方法里:counter1:2, counter2:1
counter1:2
counter2:0
counter2
并没有任何变化,为什么counter1
的值会变成2
?其实是因为类在初始化的时候,是按照代码的顺序来的,就比如上面的示例中,为counter1
赋值以及执行构造方法都是在初始化阶段执行的,但谁先谁后呢?按照顺序来,因此,在执行构造方法时,counter1
已经被赋值为1
,执行自增后,自然就变为2
了。
解析 Resolution
解析阶段是将常量池类的符号引用替换为直接引用的过程。在编译时,Java 类并不知道所引用的类的实际地址,只能使用符号引用来代替。符号引用存储在class
文件的常量池中,比如类和接口的全限定名、类引用、方法引用以及成员变量引用等,如果要使用这些类和方法,就需要把它们转化为 JVM 可以直接获取的内存地址或指针,即直接引用。
因此,解析的动作主要是针对类或接口、字段、类方法、接口方法、方法类型、方法句柄、调用点限定符这 7 类符号引用进行的。
初始化 Initialization
在准备阶段我们只是给静态变量设置了类似0
的初值,在这一阶段,则会根据我们的代码逻辑去初始化类变量和其他资源。
更直观的说初始化过程就是执行类构造器<clinit>
方法的过程。
类的初始化是类加载过程的最后一个步骤,直到这一个步骤,JVM 才真正开始执行类中编写的 Java 代码。初始化完也就差不多是类加载的全过程了,什么时候需要初始化也就是我们最前面讲到的几种情况。
类初始化是懒惰的,不会导致类初始化的情况,也就是前面讲到的被动引用类型,再讲全一点:
- 访问类的
static final
静态常量(基本类型和字符串)不会触发初始化 - 访问类对象
.class
不会触发初始化 - 创建该类的数组不会触发初始化
- 执行类加载器的
loadClass
方法不会触发初始化 Class.forName
(反射)的参数2为false
时(为true
才会初始化)
在编译生成class
文件时,编译器会产生两个方法加于class
文件中,一个是类的初始化方法clinit
, 另一个是实例的初始化方法init
。
1. 类初始化方法:<clinit>()
- Java 编译器在编译过程中,会自动收集类中所有静态变量赋值语句、静态代码块中的语句,将其合并到类构造器
<clinit>()
方法,收集的顺序由源代码文件中出现的顺序决定。类初始化方法一般在类初始化阶段执行。 - 如果两个类存在父子关系,那么在执行子类的
<clinit>()
方法之前,会确保父类的方法已执行完毕,因此,父类的静态代码块会优先于子类的静态代码块。
例子:
public class ClassDemo {static {i = 20;}static int i = 10;static {i = 30;}// init 方法收集后里面的代码就是这个,当然你是看不到该方法的init() {i = 20;i = 10;i = 30;}
}
<clinit>()
方法不需要显示调用,类解析完了会立即调用,且父类的<clinit>()
永远比子类的先执行,因此在jvm中第一个执行的肯定是Object
中的<clinit>()
方法。<clinit>()
方法不是必须的,如果没有静态代码块和变量赋值就没有- 接口也有变量复制操作,因此也会生成
<clinit>()
,但是只有当父接口中定义的变量被使用时才会初始化。
这里有一点需要特别强调,JVM 会保证一个类的<clinit>()
方法在多线程环境中被正确的加锁同步,如果多个线程同时去初始化一个类,那么只会有其中一个线程去执行这个类的<clinit>()
方法,其它线程都需要等待,直到<clinit>()
方法执行完毕。如果在一个类的<clinit>()
方法中有耗时很长的操作,那么可能会造成多个线程阻塞,在实际应用中这种阻塞往往是很隐蔽的。因此,在实际开发过程中,我们都会强调,不要在类的构造方法中加入过多的业务逻辑,甚至是一些非常耗时的操作。
另外,静态代码块中只能访问定义它之前的变量,定义在它之后的变量可以赋值但不能访问:
class Class{static {c = 2; // 赋值操作可以正常编译通过System.out.println(c);//编译器提示 Illegal forward reference,非法向前引用}static int c = 1;
}
2. 对象初始化方法:init()
init()
是实例对象自动生成的方法。编译器会按照从上至下的顺序,收集 「类成员变量」 的赋值语句、普通代码块,最后收集构造函数的代码,最终组成对象初始化方法。对象初始化方法一般在实例化类对象的时候执行。
例子:
public class ClassDemo {int a = 1;{a = 2;System.out.println(2);}{b = "b2";System.out.println("b2");}String b = "b1";public ClassDemo(int a, String b) {System.out.println("构造器赋值前:"+this.a+" "+this.b);this.a = a;this.b = b;}public static void main(String[] args) {ClassDemo demo = new ClassDemo(3, "b3");System.out.println("构造结束后:"+demo.a+" "+demo.b);
// 2
// b2
// 构造器赋值前:2 b1
// 构造结束后:3 b3}
}
上面的代码的init()
方法实际为:
public init(int a, String b){super(); // 不要忘记在底层还会加上父类的构造方法this.a = 1;this.a = 2;System.out.println(2);this.b = "b2";System.out.println("b2");this.b = "b1";System.out.println("构造器赋值前:" + this.a + " " + this.b); // 构造方法在最后this.a = a;this.b = b;
}
类执行过程小结:
- 确定类变量的初始值。在类加载的准备阶段,JVM 会为「类变量」初始化默认值,这时候类变量会有一个初始的零值。如果是被 final 修饰的类变量,则直接会被初始成用户想要的值。
- 初始化入口方法。当进入类加载的初始化阶段后,JVM 会寻找整个 main 方法入口,从而初始化 main 方法所在的整个类。当需要对一个类进行初始化时,会首先初始化类构造器,之后初始化对象构造器。
- 初始化类构造器。JVM 会按顺序收集「类变量」的赋值语句、静态代码块,将它们组成类构造器,最终由 JVM 执行。
- 初始化对象构造器。JVM 会按顺序收集「类成员变量」的赋值语句、普通代码块,最后收集构造方法,将它们组成对象构造器,最终由 JVM 执行。
如果在初始化 「类变量」时,类变量是一个其他类的对象引用,那么就先加载对应的类,然后实例化该类对象,再继续初始化其他类变量。
参考:
- 深入理解JVM类加载机制
- jvm深入理解类加载机制
相关文章:

Java 何时会触发一个类的初始化
Java 何时会触发一个类的初始化? 使用new关键字创建对象访问类的静态成员变量 或 对类的静态成员变量进行赋值调用类的静态方法反射调用类时,如 Class.forName()初始化子类时,会先初始化其父类(如果父类还没有进行过初始化的话&a…...
我的记事本
url uniform resource locator. 统一资源定位符 请求状态码 1XX:信息响应 2XX:成功响应 3XX:重定向消息 4XX:客户端错误响应 5XX:服务器端错误响应 IP地址分类 本机回环IP地址:127.0.0.1 ~ 127.255.255.254 局域网IP(私网IP) 192.168.0.0 &am…...
GO设计模式——4、单例模式(创建型)
目录 单例模式(Singleton Pattern) 优缺点 使用场景 饿汉式和懒汉式单例模式 单例模式(Singleton Pattern) 单例模式(Singleton Pattern)是一个类只允许创建一个对象(或者实例ÿ…...

我对迁移学习的一点理解——领域适应(系列3)
文章目录 1. 领域适应(Domain Adaptation)的基本概念2.领域适应(Domain Adaptation)的目标3.领域适应(Domain Adaptation)的实现方法4.领域适应(Domain Adaptation)的可以解决的问题…...

【openssl】RSA 生成公钥私钥 |通过私钥获取公钥
通过博客:Window系统如何编译openssl 编译出openssl.exe(位于apps文件夹下)。 现在需要使用它获得公钥私钥、通过私钥获取公钥 目录 说明!!! 一.定位openssl.exe目录 二、进入命令cmd 三、生成私钥 …...

MongoDB的删除文档、查询文档语句
本文主要介绍MongoDB的删除文档、查询文档命令语句。 目录 MongoDB删除文档MongoDB查询文档 MongoDB删除文档 MongoDB是一种基于文档的NoSQL数据库,它使用BSON格式存储文档。删除文档是MongoDB数据库中的常见操作之一。 下面是MongoDB删除文档的详细介绍和示例&am…...
Rust编程语言入门教程(三)-trait
文章目录 Rust编程语言入门教程(三)-trait什么是 trait?trait使用举例 Rust编程语言入门教程(三)-trait 什么是 trait? trait 是 Rust 中的接口,它定义了类型使用这个接口的行为。你可以类比到…...
LeetCode-1566. 重复至少 K 次且长度为 M 的模式【数组 枚举】
LeetCode-1566. 重复至少 K 次且长度为 M 的模式【数组 枚举】 题目描述:解题思路一:题意就是找出长度为m且连续重复k次的子数组。解题思路就是暴力枚举加剪枝。解题思路二:思路差不多解题思路三:0 题目描述: 给你一个…...

QT5.4.1无法打开文件
问题描述:起初是在QT代码中运行打开文件代码: QString gFilename QFileDialog::getOpenFileName(this,"open File",path,"*", nullptr,QFileDialog::DontUseNativeDialog);时,出现了堵塞情况,经过多次实验一…...
【1day】金和OA某接口存在未授权访问漏洞
注:该文章来自作者日常学习笔记,请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与作者无关。 目录 一、漏洞描述 二、影响版本 三、资产测绘 四、漏洞复现...

使用Rust 构建C 组件
协议解析,这不就很快了,而且原生的标准库红黑树和avl 树支持,异步tokio 这些库,编写应用组件就很快了 rust 标准库不支持 unix 的消息队列,但是支持 shm 和 uds,后者从多方面考虑都比,消息队列更…...
AI:大模型技术
Prompt Prompt(提示)是一种在人工智能领域,特别是在自然语言处理和聊天机器人中常用的技术。它是一种输入,用于激发人工智能模型生成相应的输出。在聊天机器人中,用户输入的问题或请求就是提示,而聊天机器…...
揭开WPF里面XAML可以通过http引入命名空间的神秘面纱
前言 做WPF开发这么久,其实一直对头部引入命名空间有些疑问,为啥官方提供的库通过xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"引入,而我自己开发的就只能通过 xmlns:local="clr-namespace:Darren.Wpf.MainModule.Views"来引入…...

什么是高防IP,高防IP该如何选择。
高防IP,指的是高防御能力的IP地址。在互联网的世界里,网络安全问题成为一个重要的话题。作为一个用户,你是否曾遇到过被黑客攻击造成的网站瘫痪、信息泄露等问题?如果你是一个企业,你是否考虑过自己公司的网站和业务的…...

Linux 进程
文章目录 进程定义进程的描述查看进程方法进程状态进程优先级进程相关概念补充 进程定义 大多数的说法:进程是计算机中正在运行的程序的实例。它是操作系统对程序的一种抽象,用于管理和调度程序的执行。 个人理解: 从OS(操作系统)开始说起,…...

Docker部署开源分布式任务调度平台DolphinScheduler并实现远程访问办公
文章目录 前言1. 安装部署DolphinScheduler1.1 启动服务 2. 登录DolphinScheduler界面3. 安装内网穿透工具4. 配置Dolphin Scheduler公网地址5. 固定DolphinScheduler公网地址 前言 本篇教程和大家分享一下DolphinScheduler的安装部署及如何实现公网远程访问,结合内…...

SQL语言重温
数据库语言重温 笔记背景SQL教程一些最重要的 SQL 命令SQL WHERE 子句SQL AND & OR 运算符SQL ORDER BY 关键字 笔记背景 由于工作需要,现重温简单SQL语言,笔记记录如下。 SQL教程 SQL(Structured Query Language:结构化查询语言&…...
Java学习手册——第五篇数据类型
数据类型:是数据化的基石,如果没有数据类型怎么表示呢?比如年龄可以用整数:18岁。如果有更好的表示方式大家可以留言哟~ 在举个例子就是姓名,我们需要用字符串的形式来表示。这就是数据类型的魅力,而又有同…...

机器学习算法性能评估常用指标总结
考虑一个二分问题,即将实例分成正类(positive)或负类(negative)。对一个二分问题来说,会出现四种情况。如果一个实例是正类并且也被 预测成正类,即为真正类(True positive࿰…...
java面试题-ArrayList 和 LinkedList 的区别是什么
远离八股文,面试大白话,通俗且易懂 看完后试着用自己的话复述出来。有问题请指出,有需要帮助理解的或者遇到的真实面试题不知道怎么总结的也请评论中写出来,大家一起解决。 java面试题汇总-目录-持续更新中 ArrayLi…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...

Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...

【深度学习新浪潮】什么是credit assignment problem?
Credit Assignment Problem(信用分配问题) 是机器学习,尤其是强化学习(RL)中的核心挑战之一,指的是如何将最终的奖励或惩罚准确地分配给导致该结果的各个中间动作或决策。在序列决策任务中,智能体执行一系列动作后获得一个最终奖励,但每个动作对最终结果的贡献程度往往…...
前端调试HTTP状态码
1xx(信息类状态码) 这类状态码表示临时响应,需要客户端继续处理请求。 100 Continue 服务器已收到请求的初始部分,客户端应继续发送剩余部分。 2xx(成功类状态码) 表示请求已成功被服务器接收、理解并处…...