java static 关键字的作用详解

2018-06-18 02:39:27来源:未知 阅读 ()

新老客户大回馈,云服务器低至5折

 

静态变量和静态方法

static关键字最基本的用法是:

1、被static修饰的变量属于类变量,可以通过类名.变量名直接引用,而不需要new出一个类来

2、被static修饰的方法属于类方法,可以通过类名.方法名直接引用,而不需要new出一个类来

被static修饰的变量、被static修饰的方法统一属于类的静态资源,是类实例之间共享的,换言之,一处变、处处变。JDK把不同的静态资源放在了不同的类中而不把所有静态资源放在一个类里面,很多人可能想当然认为当然要这么做,但是是否想过为什么要这么做呢?个人认为主要有三个好处:

1、不同的类有自己的静态资源,这可以实现静态资源分类。比如和数学相关的静态资源放在java.lang.Math中,和日历相关的静态资源放在java.util.Calendar中,这样就很清晰了

2、避免重名。不同的类之间有重名的静态变量名、静态方法名也是很正常的,如果所有的都放在一起不可避免的一个问题就是名字重复,这时候怎么办?分类放置就好了。

3、避免静态资源类无限膨胀过大。

 

         容易混淆的一个问题:静态方法能不能引用非静态资源?静态方法里面能不能引用静态资源?非静态方法里面能不能引用静态资源?比如就以这段代码为例,是否有错?

 

  public class A
2 {
3     private int i = 1;
4     
5     public static void main(String[] args)
6     {
7         i = 1;
8     }
9 }

       静态资源属于类,但是是独立于类存在的。从JVM的类加载机制的角度讲,静态资源是类初始化的时候加载的,而非静态资源是类new的时候加载的。 类的初始化早于类的实例化,比如Class.forName(“xxx”)方法,就是初始化了一个类,但是并没有实例化它,只是加载这个类的静态资源罢 了。所以对于静态资源来说,它是不可能知道一个类中有哪些非静态资源的;但是对于非静态资源来说就不一样了,由于它是new出来之后产生的,因此属于类的 这些东西它都能认识。所以上面的几个问题答案就很明确了:

1、静态方法能不能引用非静态资源?不能,new的时候才会产生的东西,对于初始化后就存在的静态资源来说,根本不认识它。
2、静态方法里面能不能引用静态资源?可以,因为都是类初始化的时候加载的,大家相互都认识。
3、非静态方法里面能不能引用静态资源?可以,非静态方法就是实例方法,那是new之后才产生的,那么属于类的内容它都认识。

静态块也是static的重要应用之一。也是用于初始化一个类的时候做操作用的,和静态变量、静态方法一样,
静态块里面的代码只执行一次,且只在初始化类的时候执行。静态块很简单,不过提三个小细节:
public class A
2 {
3 private static int a = B();
4
5 static
6 {
7 System.out.println("Enter A.static block");
8 }
9
10 public static void main(String[] args)
11 {
12 new A();
13 }
14
15 public static int B()
16 {
17 System.out.println("Enter A.B()");
18 return 1;
19 }
20 }

打印结果是


Enter A.B()
Enter A.static block

得出第一个结论:静态资源的加载顺序是严格按照静态资源的定义顺序来加载的。这和周志明老师《深入理解Java虚拟机:JVM高级特性与最佳实践》
中类初始化中的说法“<clinit>()方法是由编译器自动收集类中所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,
编译器收集的顺序是由语句在源文件中出现的顺序所决定的
”是一致的。
再看一个例子:
1 public class A
2 {
3 static
4 {
5 c = 3;
6 System.out.println(c);
7 }
8
9 private static int c; 10 }

这段代码的第6行是有错误的“Cannot reference a field before it is defined”。从这个例子得出第二个结论:静态代码块对于定义在它之后的静态变量,
可以赋值,但是不能访问。
再看一个加载顺序的例子
1 public class A
 2 {
 3     static
 4     {
 5         System.out.println("A.static block");
 6     }
 7     
 8     public A()
 9     {
10         System.out.println("A.constructor()");
11     }
12 }

1 public class B extends A
 2 {
 3     static 
 4     {
 5         System.out.println("B.static block");
 6     }
 7     
 8     public B()
 9     {
10         System.out.println("B.constructor()");
11     }
12     
13     public static void main(String[] args)
14     {
15         new B();
16         new B();
17     }
18 }



结果是

A.static block
B.static block
A.constructor()
B.constructor()
A.constructor()
B.constructor()

这个例子得出第三个结论:静态代码块是严格按照父类静态代码块->子类静态代码块的顺序加载的,且只加载一次。

static修饰类

这个用得相对比前面的用法少多了,static一般情况下来说是不可以修饰类的, 如果static要修饰一个类,说明这个类是一个静态内部类(注意static只能修饰一个内部类),
也就是匿名内部类。像线程池 ThreadPoolExecutor中的四种拒绝机制CallerRunsPolicy、AbortPolicy、DiscardPolicy、 DiscardOldestPolicy就是静态内部类

import static导包
import static是JDK1.5之后的新特性,这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名.资源名,可以直接使用资源名。
注意一下,import static必须这么写,而不能写成static import。举个例子来看一下:

1 import static java.lang.Math.*;
2 
3 public class A
4 {
5     public static void main(String[] args)
6     {
7         System.out.println(sin(2.2));
8     }
9 }
这么写意味着我导入了Math下的所有静态资源,main函数里面我就可以直接用sin(2.2)而不需要使用Math.sin(2.2)了。注意一下,要写import static java.lang.Math.*,
最后的“.*”不可少,有了这两个字符才意味着导入的是Math下的所有静态资源,写成import static java.lang.Math是有问题的。当然,我们也可以指定只导入某个静态资源,
比如只导入Math下sin这个方法而不导入Math下的所有静态资 源:

1 import static java.lang.Math.sin;
2 
3 public class A
4 {
5     public static void main(String[] args)
6     {
7         System.out.println(sin(2.2));
8     }
9 }
 

 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:spring 学习总结(一)

下一篇:6.3 lambda 表达式