JVM思考-init和clinit区别

目录:JVM总括:目录



clinit和init的区别其实也就是Class对象初始化对象初始化的区别,详情看我上一篇博客:

JVM总括四-类加载过程、双亲委派模型、对象实例化过程

一、init和clinit方法执行时机不同

  init是对象构造器方法,也就是说在程序执行 new 一个对象调用该对象类的 constructor 方法时才会执行init方法,而clinit是类构造器方法,也就是在jvm进行类加载—-链接—–初始化,中的初始化阶段jvm会调用clinit方法。

二、init和clinit方法执行目的不同

init is the (or one of the) constructor(s) for the instance, and non-static field initialization.
clinit are the static initialization blocks for the class, and static field initialization.
上面这两句是Stack Overflow上的解析,很清楚init是instance实例构造器,对非静态变量解析初始化,而clinit是class类构造器对静态变量,静态代码块进行初始化。看看下面的这段程序就很清楚了。

class X {
    static {
      // <clinit>
   }

   static Log log = LogFactory.getLog(); // <clinit>

   private int x = 1;   // <init>

   X(){
      // <init>
   }

}

三、clinit详解

  在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源,或者可以从另外一个角度来表达:初始化阶段是执行类构造器<clinit>()方法的过程。

1、

  <clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问如下代码:

public class Test{
    static{
        i=0;//给变量赋值可以正常编译通过
        System.out.print(i);//这句编译器会提示"非法向前引用"
    }
    static int i=1;
}        

2、

  虚拟机会保证在子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕。 因此在虚拟机中第一个被执行的<clinit>()方法的类肯定是java.lang.Object。由于父类的<clinit>()方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作,如下代码中,字段B的值将会是2而不是1。

static class Parent{
    public static int A=1;

    static{
    A=2;
    }

    static class Sub extends Parent{
        public static int B=A;
    }
    public static void main(String[]args){
        System.out.println(Sub.B);
    }
}

3、

  接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成<clinit>()方法。 但接口与类不同的是,执行接口的<clinit>()方法不需要先执行父接口的<clinit>()方法。 只有当父接口中定义的变量使用时,父接口才会初始化。 另外,接口的实现类在初始化时也一样不会执行接口的<clinit>()方法。
注意:接口中的属性都是static final类型的常量,因此在准备阶段就已经初始化话

转载:

https://blog.csdn.net/u013309870/article/details/72975536

原文地址:https://www.cnblogs.com/java-zzl/p/9905181.html

时间: 2024-09-30 18:55:15

JVM思考-init和clinit区别的相关文章

JVM思考-ClassLoader.loadClasshe和Class.forName区别

JVM思考-ClassLoader.loadClasshe和Class.forName区别 目录:JVM总括:目录 见博客第四节:JVM总括四-类加载过程.双亲委派模型.对象实例化过程 原文地址:https://www.cnblogs.com/java-zzl/p/9905223.html

git init 与 git init --bare 的区别

git init  和 git init –bare 的区别 使用命令"git init --bare"(bare汉语意思是:裸,裸的)初始化的版本库(暂且称为bare repository)只会生成一类文件:用于记录版本库历史记录的.git目录下面的文件;而不会包含实际项目源文件的拷贝;所以该版本库不能称为工作目录(working tree);如果你进入版本目录,就会发现只有.git目录下的文件,而没有其它文件;就是说,这个版本库里面的文件都是.git目录下面的文件,把原本在.git

&lt;init&gt;与&lt;clinit&gt;的区别

在编译生成class文件时,会自动产生两个方法,一个是类的初始化方法<clinit>, 另一个是实例的初始化方法<init> <clinit>:在jvm第一次加载class文件时调用,包括静态变量初始化语句和静态块的执行 <init>:在实例创建出来的时候调用,包括调用new操作符:调用Class或Java.lang.reflect.Constructor对象的newInstance()方法:调用任何现有对象的clone()方法:通过java.io.Obje

JVM堆和栈的区别

各司其职 最主要的区别就是栈内存用来存储局部变量和方法调用. 而堆内存用来存储Java中的对象.无论是成员变量,局部变量,还是类变量,它们指向的对象都存储在堆内存中. 独有还是共享 栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存. 而堆内存中的对象对所有线程可见.堆内存中的对象可以被所有线程访问. 异常错误 如果栈内存没有可用的空间存储方法调用和局部变量,JVM会抛出java.lang.StackOverFlowError.

init 和 initWithFrame 区别

当我们去自定义一些控件时 可以重写: - (instancetype)init; 也可以去重写: (instancetype)initWithFrame:(CGRect)frame 下面关于这两个的差异: #import "BFView.h" @implementation BFView - (instancetype)init{ self = [super init]; NSLog(@"%@",NSStringFromCGRect(self.frame)); re

cocos2djs ctor init onEnter的区别

cocos2d-html5 onEnter init ctor构造函数 ---js特有特性(和c++有点不一样 ctor 构造函数, new 一个对象的时候调用-----coco2d-js , 默认ctor,为对象的构造函数,其它也可以默认其它函数为构造函数. 说白了就是: ctor构造函数 new 对象后自动调用, init 在cocos2d-x 里面是 静态函数 create() 方式创建对象自动调用(是因为cocos2d-x 提供的宏,会自动调用),但是再 js 里面,需要手工调用. --

/etc/rc.d/init.d和/etc/init.d 联系区别

#/etc/init.d 是 rc.d/init.d/ 的软链接 [[email protected] ~]# ll -d /etc/init.d lrwxrwxrwx. 1 root root 11 5月  13 01:01 /etc/init.d -> rc.d/init.d #For example: [[email protected] ~]# vi /etc/rc.d/rc.local #!/bin/sh # # This script will be executed *after*

JVM JRE JDK三者的区别和联系

一. 详细介绍1.JVM -- java virtual machineJVM就是我们常说的java虚拟机,它是整个java实现跨平台的 最核心的部分,所有的java程序会首先被编译为.class的类文件,这种类文件可 以在虚拟机上执行,也就是说class并不直接与机器的操作系统相对应,而是经过虚拟机间接与操作系统交互,由虚拟机将程序解 释给本地系统执行.JVM 是 Java 平台的基础,和实际的机器一样,它也有自己的指令集,并且在运行 时操作不同的内存区域. JVM 通过抽象操作系统和 CPU

JVM 堆和栈的区别

栈内存: 程序在栈内存中运行 栈中存的是基本数据类型和堆中对象的引用 栈是运行时的单元 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据 一个线程一个独立的线程栈 堆内存: 程序运行所需的大部分数据保存在栈内存中 堆中存的是对象 堆是存储的单元,堆只是一块共享的内存 堆解决的是数据存储的问题,即数据怎么放,放在哪儿 所有线程共享堆内存 Java中的参数传递( 传值呢?还是传引用? ): 程序运行永远都是在栈中进行的,因而参数传递时,只存在传递基本类型和对象引用的问题,不会直接传递对象本身