Java中static关键字有关的静态域和静态块等解析。

静态域&静态块

看一段java代码:

package jvmorder;

public class TestOrder{                      
    public static void main(String[] args) {
        new TestOrder();                         
    }
    static int num = 4;              
    {
        num += 3;
        System.out.println("b");         
    }
    int a = 5;                   
    { 
        System.out.println("c");          
    }
    TestOrder() { 
        System.out.println("d");           
    }
    static {                              
        System.out.println("a");
    }
    static void run()                   
    {
        System.out.println("e");
    }
}

对于这段代码的输出结果,需要掌握java类中的域初始化的顺序是怎样的。
执行顺序分析:
首先,进入程序的入口—main方法,但是!
在执行面方法之前,必须实现对类的加载,即第一步要先实现对TestOrder类的加载,在加载TestOrder类时,发现类中有静态变量和静态代码块,则先顺序加载静态变量和静态代码块。所以,先对num静态变量赋值为4,然后执行静态代码块,输出“a”,分别完成第二步和第三步。现在完成了对静态变量和静态代码块的加载,接下来要进行实例域和实例块的加载,对于这二者,与它们在程序中出现的先后顺序有关。所以第四步和第五步分别执行 “num+=3;输出b”以及输出c的值。到目前为止,便可以回到主方法new一个TestOrder类了,即最后一步完成对类的构造函数的加载,完成对象的建立,即输出d。
所以程序的输出结果为:

a
b
c
d

注意:
因为静态方法需要调用才会执行,所以最后结果没有e。

构建对象时域的初始化

构建对象,就是用new class()语句建立一个新的类的对象。在这种情况下,类中的域是按照如下顺序进行初始化的:
赋予默认值–>(静态域、静态块)–>(实例域、实例块)–>构造器–>静态方法
假设一个域即变量int a,当建立对象时,首先赋予它一个默认值,int类型的默认值为0;如果a为静态域并且在静态块中被赋值,那么就按照静态域和静态块在程序中出现的顺序先后执行;如果同时还在实例块中被赋值,则再执行实例块中的赋值语句(静态域不可能再是实例域);最后执行构造器中的赋值语句(如果在构造器中有被赋值的话)。如果变量a是实例域,则不会有静态域和在静态块中赋值(不能在静态块中给实例域赋值)的情况,其他同前所述。

关于程序的执行顺序

由上述代码可知,在执行main方法之前一定会先加载类,对于类加载前后的程序执行顺序,有如下结论:
如果类还没有被加载:

1、先执行父类的静态代码块和静态变量初始化,并且静态代码块和静态变量的执行顺序只跟代码中出现的顺序有关。
2、执行子类的静态代码块和静态变量初始化。
3、执行父类的实例变量初始化
4、执行父类的构造函数
5、执行子类的实例变量初始化
6、执行子类的构造函数

如果类已经被加载:

则静态代码块和静态变量就不用重复执行,再创建类对象时,只执行与实例相关的变量初始化和构造方法。

static关键字的用途

在《Java编程思想》P86页有这样一段话:
  “static方法就是没有this的方法。在static方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途。”
  这段话虽然只是说明了static方法的特殊之处,但是可以看出static关键字的基本作用,简而言之,一句话来描述就是:
  方便在没有创建对象的情况下来进行调用(方法/变量)。
  很显然,被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
  static可以用来修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能。
1)static方法
  static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
  但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的

package jvmorder;

public class TestStatic {

     static String str = "coucou";
    public TestStatic(){

    }
    
    public static void m1(){

        System.out.println(str);
        m2();  //报错
    }
    
    public void m2(){

        System.out.println(str);
        System.out.println(str);
        m1();  //允许
    }

}

在上面的代码中,由于m1方法是独立于对象存在的,可以直接用过类名调用,但是m2是静态的方法。而这不能互相调用,具体分许如下:

静态方法是属于类的,即静态方法是随着类的加载而加载的,在加载类时,程序就会为静态方法分配内存,而非静态方法是属于对象的,对象是在类加载之后创建的。
也就是说静态方法先于对象存在,当你创建一个对象时,程序为其在堆中分配内存,一般是通过this指针来指向该对象。
静态方法不依赖于对象的调用,它是通过‘类名.静态方法名’这样的方式来调用的
非静态方法依赖于对象的调用,它是通过‘对象.非静态方法’名的方式来调用的
而对于非静态方法,在对象创建的时候程序才会为其分配内存,然后通过类的对象去访问非静态方法。
因此在对象未存在时非静态方法也不存在,静态方法自然不能调用一个不存在的方法。

延伸
有下面这样的程序:

public class Test{
private static void Print(){
System.out.println("Print()");
}
public static void main(String[] args) {
((Test)null).Print();
}
}

问输出什么?
//输出是Print()
(1)首先,我们可以试一下去掉static,这里不会编译错误,但是运行时会抛出空指针异常,原因是什么呢,原因就是类似于上面说的静态方法不能调用非静态方法的原因了。我们很容易被null转移了视线,这里与null的关系不大(这是因为是静态方法,null没有影响),null是为了告诉我们这里的引用没有指向任何地方或者说还未初始化,也就是说对象未创建,从上面对象的创建过程可以知道,如果对象还未创建,则不会有this指针的引用,因此会报空指针异常。
(2)这里用null的话(即(Test)null)是将Test引用强制转换为Test对象,这样也可以调用静态方法,其实不需要null,也是可以调用静态方法的,即Test.Print()。

另外补充一下我觉得很有必要知道的null的知识:
(1)null可以被强制转换为任何引用类型。
(2)任何含有null值的包装类在自动拆箱成基本数据类型时都会抛出一个空指针异常
(3)不能用一个值为null的引用类型变量来调用非静态方法,这样会抛出空指针异常,但是静态方法可以被一个值为null的引用类型变量调用而不会抛出空指针异常。这和对象的创建和静态方法以及非静态方法之间的关系有关。也就是上面说的那些。

static关键字的误区

static关键字会改变类中成员的访问权限吗?

与C/C++中的static不同,Java中的static关键字不会影响到变量或者方法的作用域。在Java中能够影响到访问权限的只有private、public、protected(包括包访问权限)这几个关键字。看下面的例子就明白了:
在这里插入图片描述
 这说明static关键字并不会改变变量和方法的访问权限。

能通过this访问静态成员变量吗?

虽然对于静态方法来说没有this,那么在非静态方法中能够通过this访问静态成员变量吗?先看下面的一个例子,这段代码输出的结果是什么?

public class Main {  
    static int value = 33;     
    public static void main(String[] args) throws Exception{
            new Main().printValue();  
              }    
     private void printValue(){       
      int value = 3;    
         System.out.println(this.value);  
           }
           }

输出结果: 33
  这里面主要考察队this和static的理解。this代表什么?
  this代表当前对象,那么通过new Main()来调用printValue的话,当前对象就是通过new Main()生成的对象。而static变量是被对象所享有的,因此在printValue中的this.value的值毫无疑问是33。在printValue方法内部的value是局部变量,根本不可能与this关联,所以输出结果是33。
  永远要记住一点:静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要访问权限足够)

static能作用于局部变量吗?

在C/C++中static是可以作用域局部变量的,但是在Java中切记:static是不允许用来修饰局部变量。不要问为什么,这只是Java语法的规定。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值