UUID通用唯一识别码(Universally Unique Identifier),是用于计算机体系中以识别信息数目的一个128位标识符,UUID具有唯一性,重复UUID码概率接近零,可以忽略不计。
public class MyTest3 { public static void main(String[] args) { System.out.println(MyParent3.str); } } class MyParent3{ public static final String str= UUID.randomUUID().toString(); static { System.out.println("MyParent3 static code"); } }每次执行静态代码块中的内容都会打印出来,且每次的UUID不同。
从str的定义上来看,str 是常量,但str的值在编译期不能确定下来,只有运行时才知道。
当一个常量的值并非编译期间可以确定的,那么其值就不会被放到调用类的常量池中,这时在程序运行时,会导致主动使用这个常量所在的类,从而导致这个类被初始化。
public class MyTest4 { public static void main(String[] args) { MyParent4 myTest4=new MyParent4(); } } class MyParent4{ static { System.out.println("MyParent4 static block"); } }静态语句块会打印出来。而且即使new更多的实例,静态代码块只在首次且只执行一次。
new一个实例,表示对这个类的主动使用。
public class MyTest4 { public static void main(String[] args) { MyParent4[] myTest4=new MyParent4[1]; } } class MyParent4{ static { System.out.println("MyParent4 static block"); } }当new一个数组时,静态语句块就不会执行,表示了并没有对这个类主动使用,所以没有初始化。
通过getClass获取实例的类型,得到如下信息
class [Lcom.shengsiyuan.jvm.classloader.MyParent4;表示生成出来的这个实例从属于的类型。这个类型是java虚拟在在运行期帮我们生成出来的。 虽然创建出了实例,但这个实例并不是MyParent4的实例,这个实例的类型是数组类型。
把一维数组换成二维数组,打印如下,类同样没有被初始化。
class [[Lcom.shengsiyuan.jvm.classloader.MyParent4;对于数组实例来说,其类型是在JVM在运行期动态生成的。动态生成的类型其父类型是Object。
对于数组来说,JavaDoc经常将构成数组的元素称为Component,实际上就是将数组降低一个维度后的类型
助记符: anewarray:表示创建一个引用类型(如类,接口,数组)的数组,并将其引用值压入栈顶。 newarray:表示创建一个指定的原始类型(如int,float,char)的数组,并将其引用值压住栈顶。
定一两个接口,接口中定义静态变量a和b(public 和 static是冗余的,因为在接口中这两个是默认的(final也是默认的)),再在主方法中调用MyChild5 中的b,结果打印出来6,因为使用到了MyChild5 中的静态变量b,很明显MyChild5 是被初始化了。
当一个接口在初始化时并不要求其父接口都完成了初始化。这方面是与类不同的。 只有在真正使用到父接口的时候(如引用接口中所定义的常量时),才会初始化。(不被初始化不代表着不被加载)
结果都是1。 首先调用getInstance()方法,返回一个Singleton 实例,这个实例是new出来的,所以会调用构造方法,在调用之前会给这个两个赋值,默认值是0。两个值在进入构造方法后都加一,所以两个的值都是1。
但是当把 public static int counter2=0;这句的位置移动之后
class Singleton{ public static int counter1; private static Singleton singleton=new Singleton(); private Singleton(){ counter1++; counter2++; } public static int counter2=0; public static Singleton getInstance(){ return singleton; } }counter1的值是1,counter2的值是0. 首先程序的逻辑并没有发生改变,改变的只是顺序。 初始化是从上到下执行的。 对Singleton主动时候,类准备阶段分配完内存之后,开始为静态的变量赋初值(默认值),counter2的值并没有被赋上。在构造方法阶段两个的值都是1,但到了下一行,又一次初始化,于是counter2被初始化为0,被初始化为0的阶段是counter2真正的初始化阶段。
如果再改变代码,给counter1设置值为1; public static int counter1=1; 输出的结果是2和0.
按照准备和初始化阶段理解:
准备:为类的静态变量分配内存,并将其初始化为默认值。静态变量是因为静态变量是全局变量,此时实例变量还没有被创建。默认值不是设置好的值,而是按照不同类型的默认值(int是0,boolean是false等)。在达到初始化之前,类变量都没有初始化为真正的初始值
初始化,对于类型中的一些静态变量进行赋值。为类的静态变量赋予正确的初始值。
在这个例子中因为counter2的准备阶段是发生在初始化阶段之前的,所以在初始化时还要重新的真正的赋值。准备阶段的目的是让counter2有值,这个值是默认的初始值,可以让构造方法正常进行进行下去。然后counter2的赋值是我们主观的去给它赋值。