请先仔细阅读下面程序,猜测运行结果,
1.类中无静态类时,测试程序:
public class StaticTest extends fatherClass{
//初始化时,可以直接调用静态成员,以及同类的非静态成员
//静态变量初始化时不能引用非静态成员
public static int staticVar=new AssistClass().getNum2();
//静态域与静态变量初始化具有相同的优先级
static{
staticVar++;
System.out.println("static block");
//域中可以定义变量以及类,不能定义方法,并且只是在域中使用
//不能带有public\protected\private标示符
int var2=3;
class NSC2{
int i=2;
}
// System.out.println(new NSC2().i);
//不允许定义静态变量和静态类
//! static int staticVar2;
//! static class SC2{}
}
public int var=new AssistClass().getNum();
public int var2=fun3();
public StaticTest(){
System.out.println("constructor");
}
//非静态域与非静态变量初始化具有相同的优先级
{
//静态域可以访问所属类的静态
var++;
staticVar++;
System.out.println("not static block");
}
public void fun(){//非静态方法可以直接调用静态和非静态成员
staticVar=1;
staticFun();
fun2();
}
public void fun2(){
}
public int fun3(){
return 3;
}
public static void staticFun(){
//静态方法不能直接调用非静态成员
//需要:1.实例化 2.改为静态
//! fun2();
System.out.println("sattic method");
}
public static int staticFun2(){
System.out.println("static field init");
return 2;
}
public static int staticFun3(){
System.out.println("not static field init");
return 3;
}
/**
* @param args
*/
//静态方法在第一次执行时会先执行静态初始化和静态域
//构造方法可以认为是静态方法
//每次实例化都会执行一次非静态初始化和非静态域
public static void main(String[] args) {
// TODO Auto-generated method stub
// staticFun();
// new StaticTest();
new StaticTest();
// System.out.println(st.var+"&"+st.var2+"&"+staticVar);
}
//结论:静态成员可以被直接调用,非静态成员则需经实例化(但可以被同类的非静态成员调用)
}
public class AssistClass{
int i=1;
public int getNum(){
System.out.println("not static field init");
return 4;
}
public int getNum2(){
System.out.println("static field init");
return 5;
}
public int getNum3(){
System.out.println("father static field init");
return 5;
}
public int getNum4(){
System.out.println("father not static field init");
return 5;
}
public static void main(String[] args){
//静态方法在第一次执行时会先执行静态初始化和静态域
//构造方法可以认为是静态方法
//每次实例化都会执行一次非静态初始化和非静态域
new StaticTest();
new StaticTest();
}
}
public class fatherClass{
//同静态时,父类的变量初始化/域先于子类执行
static int fatherVar=new AssistClass().getNum3();
static{
System.out.println("father static block");
}
//同非静态时,父类的变量初始化/域先于子类执行
{
System.out.println("father not static block");
}
int fatherVar2=new AssistClass().getNum4();
}
运行结果:
father static field init
father static block
static field init
static block
father not static block
father not static field init
not static field init
not static block
constructor
2.类中有嵌套类(static内部类),测试程序:
public class Painter {
private int a=0;
public Painter(){
System.out.println("外部类构造器");
}
//内部类
public static class InnerShape{
public InnerShape(){
System.out.println("内部类构造器");
}
Painter p=new Painter();
static{
System.out.println("内部类静态域");
}
}
{
System.out.println("外部类普通域");
}
static{
System.out.println("外部类普通域");
}
//定义两个内部类对象,观察输出
static InnerShape in1 = new InnerShape();
static InnerShape in2 = new InnerShape();
public static void main(String []args){
System.out.println("main");
}
}
首先,我想说一下java程序运行的先后顺序 :
第一步:编译
第二步:Java虚拟机加载类
第三步:执行main函数
现在,仔细探讨一下类的加载过程。对于一个类来说,包括两大成员即属性成员和方法成员。
当Java虚拟机对某一个类进行加载时
第一步:给类中的static成员分配内存,即对static属性成员进行初始化,对static方法成员在内存中分配方法的入口地址。对于static域,也进行初始化。这里有必要提一下初始化与分配入口地址的区别:初始化即执行语句,而分配方法的入口地址并不执行方法体。static属性成员,方法成员和域的加载顺序是按照它们在该类中的先后顺序依次进行的。
第二步:对普通属性成员,方法和域进行加载,属性和域不进行初始化,方法也不分配入口地址,只是加载,相当于对象的声明。
总结第一步和第二步可以知道static方法不能访问类的非静态属性成员和方法成员原因之所在,因为static方法在非静态成员加载之前就已经加载到内存中,而此时非静态成员还不存在!!这也是为什么静态内部类中不能访问非静态成员的原因所在!!
注:在加载某个类时,若该类是某个类的子类,则Java虚拟机会首先加载父类的成员,加载方式仍按照上述步骤进行,然后再加载该子类,加载方式仍按照上述步骤进行。
其实在这里还有一种特殊情况就是,加载某个类时,发现该类有一个内部类,事实上,内部类就可以看作是该类的属性成员,仍按照上述步骤进行加载。若该内部类是一个嵌套类(即static类),则进行初始化,此时的初始化不同以往,只是加载,并不管此嵌套类中的具体情况(里面即使有static属性成员,方法成员和域也不再初始化和分配入口地址了!),当定义该嵌套类对象时,该内部类就会按照上述两个步骤进行。若该内部类是一个普通类,则与普通成员无异。
到这里整个加载过程已经完成,static关键字所定义的属性、方法和域在内存中都有了着落,我们不禁会问,那类中普通的成员呢?
好,在开篇已经说过,Java虚拟机加载完成之后就会执行main函数。在main函数中,当我们定义某个类的对象是,事实上,系统会重新扫描该对象所属的类,将非静态成员和非静态域进行初始化(对成员进行初始化,对域则执行与中的语句),对非静态方法在内存中分配入口地址(当然这些成员只是属于这个对象的)。所以说对象实例的不同之处就在于这些非静态的成员,而static成员是对象所共有的,故可通过类名直接调用,而且某个对象改变static属性和方法时,另一个对象在调用这个属性和方法时也被改变了!
若该对象所属类是某个类的子类,则先按照上述方式初始化父类的非静态成员,然后初始化自己的非静态成员。
当一个对象经过这样的初始化之后,再调用自己的构造器重新构造一次!!