看下面代码请参考上一篇《类的加载与反射与反射》
例子一:
class FinalTest{
//编译时候直接将常量yeshiwu放入常量池中,本质上并没有直接引用到定义常量类的初始化
public static final String str="yeshiwu";
//编译的时候就可以确定为2,所以不会对类进行初始化
public static final int x=6/3;
//编译的时候不能确定,运行的时候才能确定,所以会对类进行初始化
public static final int x2=new Random().nextInt(100);
static{
System.out.println("FinalTest static block");
}
}
public class Test2 {
public static void main(String[] args){
System.out.println(FinalTest.x);
System.out.println(FinalTest.str);
System.out.println(FinalTest.x2);
}
}
2
yeshiwu
FinalTest static block
41
当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。
例子二:
class Parent{
static int a=3;
static {
System.out.println("parent static block");
}
}
class Child extends Parent{
static int b=4;
static {
System.out.println("Child static block");
}
}
public class Test4 {
static {
System.out.println("Test4 static block");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(Child.b);
}
}
Test4 static block
parent static block
Child static block
4
这里先加载主类,static静态初始化执行,然后初始化子类的时候会先去加载父类。
例子三:
class Parent2{
static int a=3;
static{
System.out.println("Parent2 static block");
}
}
class Child2 extends Parent2{
static int b=4;
static{
System.out.println("Child2 static block");
}
}
public class Test5 {
static{
System.out.println("Test5 static block");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Parent2 parent2;
System.out.println("-------------");
parent2=new Parent2();
System.out.println(Parent2.a);
System.out.println(Child2.b);
}
}
Test5 static block
-------------
Parent2 static block
3
Child2 static block
4
先加载父类,当再加载子类的时候便不会再去加载子类。
例子四
class Parent3{
static int a=3;
static{
System.out.println("Parent3 static block");
}
static void doSomething(){
System.out.println("doSomething");
}
}
class Child3 extends Parent3{
//Child3没有被初始化,因为Child3.a不是对Child3的主动使用,而是对Parent的主动使用
static{
System.out.println("Child3 static block");
}
}
public class Test6 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Parent3[] parents = new Parent3[10];
Child3.a=4;
System.out.println(Child3.a);
Child3.doSomething();
}
}
通过子类来引用父类的静态字段,不会导致子类初始化。通过数组来定义类,也不会触发此类的初始化。
顺便在这里提一下final修饰的变量:
public static void main(String[] args) {
String a = "hello2";
final String b = "hello";
String d = "hello";
String e = d + 2;
String c = b + 2;
String str1="aaa";
String str2="aaa";
String str3=str1+"bbb";
String str4="aaabbb";
final String str="bbb";
String str5=str1+str;
String str6="aaa"+str;
//true 当str1和str2代表字符串一样时string会指向同一个地方
System.out.println(str1==str2);
//str3和str4是由链接来形成的,指向的根本不是同一个地址
System.out.println(str3==str4);//false
//b被final修饰,被当做编译器常量,使用b的地方会直接将b替换为它的值,和str1==str2道理一样
System.out.println((a == c));//true
System.out.println((a == e));//false
}
final和static的区别
static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变。看下面这个例子:
class MyClass {
public final double i = Math.random();
public static double j = Math.random();
}
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyClass myClass1 = new MyClass();
MyClass myClass2 = new MyClass();
System.out.println(myClass1.i);
System.out.println(myClass1.j);
System.out.println(myClass2.i);
System.out.println(myClass2.j);
}
}
static 修饰的变量j,第一次初始化MyClass的时候就已经给赋值j赋值了,第二次初始化的时候不会再管j了,所以第二次和第一次j的值是一样的