静态代码块 构造代码块 构造函数 深入分析
文章目录
- 静态代码块 构造代码块 构造函数 的执行顺序
- 概念
- 静态代码块
- 构造代码块
- 构造函数
- 普通代码块
- 执行顺序
- 简单例子
- 解释
- 父子类之间的加载顺序(重要)
- 解释
- 参考链接
静态代码块 构造代码块 构造函数 的执行顺序
概念
静态代码块
//在java类中(方法中不能存在静态代码块)使用static关键字和{}声明的代码块
public class CodeBlock {
static {
System.out.println("静态代码块");
}
}
- 执行时机
静态代码块在类被加载的时候就运行了,而且只运行一次,并且优先于各种代码块以及构造函数。如果一个类中有多个静态代码块,会按照书写顺序依次执行。 作用
一般情况下,如果有些代码需要在项目启动的时候就执行,这时候就需要静态代码块。比如一个项目启动需要加载的很多配置文件等资源,我们就可以都放入静态代码块中。
构造代码块
//在java类中使用{}声明的代码块(和静态代码块的区别是少了static关键字)
public class CodeBlock {
{
System.out.println("构造代码块");
}
}
执行时机
构造代码块在创建对象时被调用,每次创建对象都会调用一次,但是优先于构造函数执行。
这里对构造代码块跟构造函数做个测试:
//Java源代码
public class Opt_class {
{
System.out.println("构造代码块");
}
public Opt_class() {
System.out.println("无参构造函数");
}
public Opt_class(String str) {
System.out.println("有参构造函数" + str);
}
}
//对生成的class文件进行反编译
public class Opt_class {
public Opt_class() {
System.out.println("构造代码块");
System.out.println("无参构造函数");
}
public Opt_class(String str) {
System.out.println("构造代码块");
System.out.println((new StringBuilder()).append("有参构造函数").append(str).toString());
}
}
会发现,会将构造代码块加在每一个构造函数的函数体之前。
作用
和构造函数的作用类似,都能对对象进行初始化,并且只要创建一个对象,构造代码块都会执行一次。但是反过来,构造函数则不一定每个对象建立时都执行(多个构造函数情况下,建立对象时传入的参数不同则初始化使用对应的构造函数)。
利用每次创建对象的时候都会提前调用一次构造代码块特性,我们可以做诸如统计创建对象的次数等功能。
构造函数
public class CodeBlock {
public CodeBlock(){
System.out.println("构造函数");
}
}
- 构造函数的命名必须和类名完全相同。
- 构造函数的功能主要用于在类的对象创建时定义初始化的状态。它没有返回值,也不能用void来修饰。
- 构造函数不能被直接调用,必须通过new运算符在创建对象时才会自动调用;
- 当定义一个类的时候,通常情况下都会显示该类的构造函数,如果没有自定义构造函数,Java编译器会提供一个默认的无参构造函数.
普通代码块
//普通代码块和构造代码块的区别是,构造代码块是在类中定义的,而普通代码块是在方法体中定义的。且普通代码块的执行顺序和书写顺序一致。
public void sayHello(){
{
System.out.println("普通代码块");
}
}
执行顺序
简单例子
public class CodeBlock {
static {
System.out.println("静态代码块");
}
{
System.out.println("构造代码块");
}
public CodeBlock(){
System.out.println("构造函数");
}
public void sayHello(){
{
System.out.println("普通代码块");
}
}
public static void main(String[] args) {
System.out.println("main方法————————————————");
new CodeBlock().sayHello();
System.out.println("---------------");
new CodeBlock().sayHello();
}
}
output:
静态代码块
main方法————————————————
构造代码块
构造函数
普通代码块
---------------
构造代码块
构造函数
普通代码块
解释
静态代码块先于主函数执行(这里不够准确,后文会写出我的看法),之后进入主函数,打印第一句,new出一个对象调用sayHello(),这里新建对象的时候,会依次调用构造代码块,构造函数,之后调用方法会进入普通代码块。
因此执行到这里的顺序为: 静态代码块,主函数第一句,构造代码块,构造函数,普通代码块。
之后又新建一个对象,调用sayHello(),所以也会依次调用构造代码块,构造函数,之后调用方法会进入普通代码块。这里不会再调用静态代码块,因为它在类加载的时候已经执行,且只执行一次,应该说它与类有关,与对象是没有关系的
父子类之间的加载顺序(重要)
class SuperClass{
static {
System.out.println("父类静态代码块");
}
{
System.out.println("父类构造代码块");
}
public SuperClass(){
System.out.println("父类构造函数");
}
public void SayHello(){
System.out.println("父类普通方法");
}
}
class SubClass extends SuperClass{
static {
System.out.println("子类静态代码块");
}
{
System.out.println("子类构造代码块");
}
public SubClass(){
System.out.println("子类构造函数");
}
public void SayHello(){
System.out.println("子类普通方法");
}
}
public class exe_seq {
static{
System.out.println("测试主类的静态代码块优先于main函数执行");
}
public static void main(String[] args) {
System.out.println("main方法------------------");
new SubClass().SayHello();
System.out.println("------------------------");
new SubClass().SayHello();
}
}
/* 测试主类的静态代码块优先于main函数执行 main方法------------------ 父类静态代码块 子类静态代码块 父类构造代码块 父类构造函数 子类构造代码块 子类构造函数 子类普通方法 ------------------------ 父类构造代码块 父类构造函数 子类构造代码块 子类构造函数 子类普通方法*/
//执行顺序: 静态代码块内容先执行,接着执行父类构造代码块和构造方法,然后执行子类构造代码块和构造方法。
解释
这里关于static块在main函数之前执行,我说一下我的看法,不知是否正确。
一个文件里只能有一个public类,但是main函数可以在public类中,也可以不在。main函数也算一个类中定义的一个静态方法吧,它作为函数的入口点,要被执行,应该是先加载一下main函数所在的类,然后再根据类去调用这个main方法。
- 这样就不难分析上述程序的执行结果,我在父类,子类和测试类中都定义了静态代码块,但是只有包含main函数的测试类先被加载,也就是只执行了测试类的静态代码块。进入main函数后,打印一句话,接着新建一个子类对象,调用它的一个sayHello()。
这里说明一下子类初始化的加载顺序
父类静态代码块->子类静态代码块->父类构造代码块、父类构造函数->子类构造代码块、子类构造函数->子类的普通方法(先静态,后动态,先父类,后子类)
参考链接
Java中静态代码块、构造代码块、构造函数、普通代码块
final、static、代码块、静态代码块、内部类、代码执行顺序
还没有评论,来说两句吧...