原文地址http://www.cnblogs.com/xrq730 感谢博主分享,谢谢!java
以前讲到final关键字的做用是每次面试的时候我必问求职者的两个问题之一,另一个问题就是文本会写到的static。final和static同样,都是一个小问题能够看到一我的的基础是否扎实以及平时是否有钻研精神。面试
静态变量和静态方法函数
static关键字最基本的用法是:post
一、被static修饰的变量属于类变量,能够经过类名.变量名直接引用,而不须要new出一个类来spa
二、被static修饰的方法属于类方法,能够经过类名.方法名直接引用,而不须要new出一个类来线程
被static修饰的变量、被static修饰的方法统一属于类的静态资源,是类实例之间共享的,换言之,一处变、到处变。JDK把不一样的静态资源放在了不一样的类中而不把全部静态资源放在一个类里面,不少人可能想固然认为固然要这么作,可是是否想过为何要这么作呢?我的认为主要有三个好处:code
一、不一样的类有本身的静态资源,这能够实现静态资源分类。好比和数学相关的静态资源放在java.lang.Math中,和日历相关的静态资源放在java.util.Calendar中,这样就很清晰了blog
二、避免重名。不一样的类之间有重名的静态变量名、静态方法名也是很正常的,若是全部的都放在一块儿不可避免的一个问题就是名字重复,这时候怎么办?分类放置就行了。资源
三、避免静态资源类无限膨胀,这很好理解。编译器
OK,再微微深刻一下,也是有些人容易混淆的一个问题:静态方法能不能引用非静态资源?静态方法里面能不能引用静态资源?非静态方法里面能不能引用静态资源?好比就以这段代码为例,是否有错?
1 public class A 2 { 3 private int i = 1; 4 5 public static void main(String[] args) 6 { 7 i = 1; 8 } 9 }
固然有错,在第7行的地方。不妨这么思考这个问题:
静态资源属于类,可是是独立于类存在的。从JVM的类加载机制的角度讲,静态资源是类初始化的时候加载的,而非静态资源是类new的时候加载的。 类的初始化早于类的new,好比Class.forName(“xxx”)方法,就是初始化了一个类,可是并无new它,只是加载这个类的静态资源罢 了。因此对于静态资源来讲,它是不可能知道一个类中有哪些非静态资源的;可是对于非静态资源来讲就不同了,因为它是new出来以后产生的,所以属于类的 这些东西它都能认识。因此上面的几个问题答案就很明确了:
一、静态方法能不能引用非静态资源?不能,new的时候才会产生的东西,对于初始化后就存在的静态资源来讲,根本不认识它。
二、静态方法里面能不能引用静态资源?能够,由于都是类初始化的时候加载的,你们相互都认识。
三、非静态方法里面能不能引用静态资源?能够,非静态方法就是实例方法,那是new以后才产生的,那么属于类的内容它都认识。
静态块
静态块也是static的重要应用之一。也是用于初始化一个类的时候作操做用的,和静态变量、静态方法同样,静态块里面的代码只执行一次,且只在初始化类的时候执行。静态块很简单,不过提三个小细节:
1 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
这个比较冷门,基本不多看见有地方用,使用JUnit可能会用到,写assert的时候会方便些。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 }
这么写也是没问题的。导入静态变量也是同样,有兴趣的能够本身试一下。对于import static,我的的态度是:
一、简化了一些操做,好比静态导入Math下的全部静态资源,在频繁使用Math类下静态资源的地方,能够少些不少“Math.”
二、下降了代码的可读性
建议在某些场景下导入特定的静态资源,不建议使用“.*”的导入方式。