static修饰的变量Java中静态(static)成员何时才会初始化
静态成员的初始化的阶段,根据有没有显⽰初始化⽽异,根据是否被final修饰⽽异:
private static int num = 11;
private static int num2;
private static final int num3 = 12
在类加载的准备阶段会给所有static 成员在⽅法区中分配内存,并设置变量初始值【这个初始值就是零值,零值根据类型不同⽽异】,所以可以理解为num2就是默认初始化为了0,因为它没有显⽰初始化,之后不会再初始化num2的值了;
这时【即准备阶段】num值也为0,在类加载的初始化阶段num才会执⾏类变量赋值的操作,即在初始化阶段num值为11;
但在类加载的准备阶段num3的值就会被赋值为12⽽不是0,这是因为num3被final修饰,此时“类字段的字段属性表中就存在ConstantValue 属性,⽽准备阶段变量num3就会被初始化为ConstantValue属性所指定的值”--来⾃《深⼊理解Java虚拟机》第⼆版
总结
所有静态成员在类加载完成之后都已经或显⽰或隐式的完成了初始化赋值的操作。
零值:
int 0
long 0L
short (short) 0
char '\u0000'
byte (byte) 0
boolean false
float 0.0f
double 0.0d
引⽤类型null
我们知道java中类的⽣命周期为装载、连接、初始化、使⽤和卸载五个过程,如下图所⽰:
1.加载
我们编写⼀个java类的代码,经过编译之后⽣成⼀个后缀名为.class的⽂件,java虚拟机就能识别这种⽂件。java的⽣命周期就是class⽂件从加载到消亡的过程。关于加载,其实,就是将源⽂件的class⽂件到类的信息将其加载到⽅法区中,然后在堆区中实例化⼀个java.lang.Class对象,作为⽅法区中这个类的信息的⼊⼝。但是这⼀功能是在JVM之外实现的,主要的原因是⽅便让应⽤程序⾃⼰决定如何获取这个类,在不同的虚拟机实现的⽅式不⼀定相同,hotspot虚拟机是采⽤需要时在加载的⽅式,也有其他是先预先加载的。(可以参考深⼊理解JVM这本书)
2.连接
连接⼀般是加载阶段和初始化阶段交叉进⾏,过程由以下三部分组成:
(1)验证:确定该类是否符合java语⾔的规范,有没有属性和⾏为的重复,继承是否合理,总之,就是保证jvm能够执⾏
(2)准备:主要做的就是为由static修饰的成员变量分配内存,并设置默认的初始值
默认初始值如下:
1.⼋种基本数据类型默认的初始值是0
2.引⽤类型默认的初始值是null
3.有static final修饰的会直接赋值,例如:static final int x=10;则默认就是10.
(3)解析:这⼀阶段的任务就是把常量池中的符号引⽤转换为直接引⽤,说⽩了就是jvm会将所有的类或接⼝名、字段名、⽅法名转换为具体的内存地址。
3.初始化
初始化这个阶段就是将静态变量(类变量)赋值的过程,即只有static修饰的才能被初始化,执⾏的顺序就是:⽗类静态域或着静态代码块,然后是⼦类静态域或者⼦类静态代码块(静态代码块先被加载,然后再是静态属性)
4.使⽤
在类的使⽤过程中依然存在以下三步:
(1)对象实例化:就是执⾏类中构造函数的内容,如果该类存在⽗类JVM会通过显⽰或者隐⽰的⽅式先执⾏⽗类的构造函数,在堆内存中为⽗类的实例变量开辟空间,并赋予默认的初始值,然后在根据构造函数的代码内容将真正的值赋予实例变量本⾝,然后,引⽤变量获取对象的⾸地址,通过操作对象来调⽤实例变量和⽅法
(2)垃圾收集:当对象不再被引⽤的时候,就会被虚拟机标上特别的垃圾记号,在堆中等待GC回收
(3)对象的终结:对象被GC回收后,对象就不再存在,对象的⽣命也就⾛到了尽头
5.类卸载
类卸载即类的⽣命周期⾛到了最后⼀步,程序中不再有该类的引⽤,该类也就会被JVM执⾏垃圾回收,从此⽣命结束…
代码⽰例:
st;
class A{
static int a;//类变量
String name;
int id;
//静态代码块
static{
a=10;
System.out.println("这是⽗类的静态代码块"+a);
}
//构造代码块
{
id=11;
System.out.println("这是⽗类的构造代码块id:"+id);
}
A(){
System.out.println("这是⽗类的⽆参构造函数");
}
A(String name){
System.out.println("这是⽗类的name"+name);
}
}
class B extends A{
String name;
static int b;
static{
b=12;
System.out.println("这是⼦类的静态代码块"+b);
}
B(String name) {
super();
this.name = name;
System.out.println("这是⼦类的name:"+name);
}
}
public class Test666 {
public static void main(String[] args) {
B bb=new B("GG");
}
}
静态代码在类的初始化阶段被初始化。
⾮静态代码则在类的使⽤阶段(也就是实例化⼀个类的时候)才会被初始化。
静态变量
可以将静态变量理解为类变量(与对象⽆关),⽽实例变量则属于⼀个特定的对象。
静态变量有两种情况:
静态变量是基本数据类型,这种情况下在类的外部不必创建该类的实例就可以直接使⽤
静态变量是⼀个引⽤。这种情况⽐较特殊,主要问题是由于静态变量是⼀个对象的引⽤,那么必须初
始化这个对象之后才能将引⽤指向它。因此如果要把⼀个引⽤定义成static的,就必须在定义的时候就对其对象进⾏初始化。
public class TestForStaticObject{
static testObject o = new testObject (); //定义⼀个静态变量并实例化
public static void main(String args[]){
//在main中直接以“类名.静态变量名.⽅法名”的形式使⽤testObject的⽅法
}
}
静态⽅法
与类变量不同,⽅法(静态⽅法与实例⽅法)在内存中只有⼀份,⽆论该类有多少个实例,都共⽤⼀个⽅法。
静态⽅法与实例⽅法的不同主要有:
静态⽅法可以直接使⽤,⽽实例⽅法必须在类实例化之后通过对象来调⽤。
在外部调⽤静态⽅法时,可以使⽤“类名.⽅法名”或者“对象名.⽅法名”的形式。
实例⽅法只能使⽤这种⽅式对象名.⽅法名。
静态⽅法只允许访问静态成员。⽽实例⽅法中可以访问静态成员和实例成员。
静态⽅法中不能使⽤this(因为this是与实例相关的)。
静态代码块
在java类中,可以将某⼀块代码声明为静态的。
static {
//静态代码块中的语句
}
静态代码块主要⽤于类的初始化。它只执⾏⼀次,并且在同属于⼀个类的main函数之前执⾏。
静态代码块的特点主要有:
静态代码块会在类被加载时⾃动执⾏。
静态代码块只能定义在类⾥⾯,不能定义在⽅法⾥⾯。
静态代码块⾥的变量都是局部变量,只在块内有效。
⼀个类中可以定义多个静态代码块,按顺序执⾏。
静态代码块只能访问类的静态成员,⽽不允许访问实例成员。
静态代码块和静态函数的区别
java 静态代码块:
⼀般情况下,如果有些代码必须在项⽬启动前就执⾏的时候,需要使⽤静态代码块,这种代码是主动执⾏的,它只执⾏⼀次,并且在同属于⼀个类的main函数之前执⾏。
静态函数:
需要在项⽬启动的时候就初始化,在不创建对象的情况下,其他程序来调⽤的时候,需要使⽤静态⽅法,这种代码是被动执⾏的.
注意:
(1)静态变量是属于整个类的变量⽽不是属于某个对象的。注意不能把任何⽅法体内的变量声明为静态,例如:
fun()
{
static int i=0;//⾮法。
}
(2)⼀个类可以使⽤不包含在任何⽅法体中的静态代码块,当类被载⼊时,静态代码块被执⾏,且只被执⾏⼀次,静态块常⽤来执⾏类属性的初始化。例如:
static
{
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论