1. 静态变量
静态变量在内存中只存在一份,只在类初始化时赋值一次。
- 静态变量:类所有的实例都共享静态变量,可以直接通过类名来访问它;
- 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。
public class A {
private int x; // 实例变量
public static int y; // 静态变量
}
2. 静态方法
静态方法在类加载的时候就存在了,它不依赖于任何实例,所以静态方法必须有实现,也就是说它不能是抽象方法(abstract)。
3. 静态语句块
静态语句块在类初始化时运行一次。
例子:
public class Test {
public static int X = 100;
public final static int Y = 200;
public Test()
{
System.out.println("Test构造函数执行");
}
static {
System.out.println("static语句块执行");
}
public static void display()
{
System.out.println("静态方法被执行");
}
public void display_1()
{
System.out.println("实例方法被执行");
}
}
public class RunTest {
public static void main(String args[]){
try {
Class.forName("demo.ljn.com.demo_20180516.Test");
Class.forName("demo.ljn.com.demo_20180516.Test");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
结果:你会发现虽然执行了两条 Class.forName("demo.ljn.com.demo_20180516.Test")语句,但是,只输出了一条"static语句块执行"语句;其实第二条Class.forName()语句已经无效了,因为在虚拟机的生命周期中一个类只被加载一次;又因为static{}是伴随类加载执行的,所以,不管你new多少次对象实例,static{}都只执行一次。
1、static{}语句块执行的时机,即类被加载准确含义:
(1)用Class.forName()显示加载的时候;
(2)实例化一个类的时候,如将main()函数的内容改为:Test t=new Test();//这种形式其实和1相比,原理是相同的,都是显示的加载这个类,读者可以验证Test t=new Test();和Test t=(Test)Class.forName().newInstance();这两条语句效果相同。
(3)调用类的静态方法的时候,如将main()函数的内容改为:Test.display();
(4)调用类的静态变量的时候,如将main()函数的内容改为:System.out.println(Test.X);
总体来说就这四种情况,但是我们特别需要注意一下两点:
(1)调用类的静态常量的时候,是不会加载类的,即不会执行static{}语句块,读者可以自己验证一下(将main()函数的内容改为System.out.println(Test.Y);),你会发现程序只输出了一个200;(这是java虚拟机的规定,当访问类的静态常量时,如果编译器可以计算出常量的值,则不会加载类,否则会加载类)
(2)用Class.forName()形式的时候,我们也可以自己设定要不要加载类,如将Class.forName("Test")改为 Class.forName("Test",false,StaticBlockTest.class.getClassLoader()),你会发现程序什么都没有输出,即Test没有被加载,static{}没有被执行。
2、static{}语句块的执行次序
(1)当一个类中有多个static{}的时候,按照static{}的定义顺序,从前往后执行;
(2)先执行完static{}语句块的内容,才会执行调用语句;
public class TestStatic {
static {
System.out.println(1);
}
static {
System.out.println(2);
}
static {
System.out.println(3);
}
public static void main(String args[])
{
System.out.println(5);
}
static {
System.out.println(4);
}
}
结果:程序会输出1,2,3,4,5
(3)如果静态变量在定义的时候就赋给了初值(如 static int X=100),那么赋值操作也是在类加载的时候完成的,并且当一个类中既有static{}又有static变量的时候,同样遵循“先定义先执行”的原则;
class Test {
public static int X = 300;
static {
System.out.println(X);
X = 200;
System.out.println(X);
}
}
public class StaticBlockTest {
public static void main(String args[])
{
System.out.println(Test.X);
}
}
结果:程序会依次输出300,200,200,先执行完X=300,再执行static{}语句块。
(4)访问静态常量,如果编译器可以计算出常量的值,则不会加载类。即如果A类的静态常量值是通过B类的静态常量赋值,则不加载,否则需要加载A类。
public class TestA {
public static final int a = TestB.a;
public static final int b = TestB.b;
public static final int c = 90;
static
{
System.out.println("TestA static语句块执行");
}
}
public class TestB {
public static int a = 90;
public static final int b = 90;
static
{
System.out.println("TestB static语句块执行");
}
}
public class StaticTest {
public static void main(String args[])
{
System.out.println(TestA.a);
}
}
System.out.println(TestA.a);的结果:
TestB
static
语句块执行
estA
static
语句块执行
4. 静态内部类
内部类的一种,静态内部类不依赖外部类,且不能访问外部类的非静态的变量和方法。
public class StaticInnerClassTest {
public static void main( String args[] ) {
OuterClass2 outer = new OuterClass2();
OuterClass2.StaticInnerClass.innerMethod();
//调用静态内部类的静态方法
OuterClass2.outerMethod();
//创建静态内部类对象
OuterClass2.StaticInnerClass staticInner = new OuterClass2.StaticInnerClass();
int num = staticInner.innerMethod2(); //调用静态内部类实例方法
}
}
class OuterClass2 { //外部类
private double x = 0.0; //内部静态类不可以访问外部类实 例变量
static private int n = 10; //外部类静态变量
static void outerMethod() { //外部类静态方法
System.out.println("from OuterClass...");
}
void outerMethod2() {
System.out.println("from OuterClass’ instance method2()...");
}
static class StaticInnerClass { //静态内部类
static private int m = 5; //静态内部类静态变量
static void innerMethod() { //静态内部类静态方法
int sum;
n = 20; //只可以访问外部类静态变量
sum = n + m;
System.out.println("from InnerClass sum = " + sum);
outerMethod(); //只可以调用外部类静态方法
}
int innerMethod2() {
n = 100;
outerMethod();
System.out.println("from InnerMethod2() n = " + n);
return n;
}
} //静态内部类结束
} //外部类结束
如同不用创建对象就可调用静态方法一样,上例静态内部类中的静态方法利用:
OuterClass2.StaticInnerClass.innerMethod(); //静态内部类调用其静态方法
注意,可以在静态内部类的方法中,直接访问外部类的静态变量n和调用静态方法outerMethod()。但不允许访问外部类的实例变量x以及实例方法outerMethod2().
静态内部类中也可以提供实例方法,如:
static class StaticInnerClass {
int innerMethod2() {
n = 100; //只可访问外部类静态变量
outerMethod(); //只可调用外部类静态方法
System.out.println("from InnerMethod2() n = " + n);
return n;
}
} //静态内部类结束
静态内部类的实例方法中亦只允许访问外部类的静态成员。
可以使用下列语法格式创建一个静态内部类对象并且调用其实例方法,以及静态方法:
OuterClass2.StaticInnerClass staticInner = new OuterClass2.StaticInner Class(); //创建静态内部类对象
int num = staticInner.innerMethod2(); //调用实例方法
staticInner.innerMethod(); //调用其静态方法
5. 静态导包
import static com.xxx.ClassName.*
在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低。
6. 变量赋值顺序
静态变量的赋值和静态语句块的运行优先于实例变量的赋值和普通语句块的运行,静态变量的赋值和静态语句块的运行哪个先执行取决于它们在代码中的顺序。
public static String staticField = "静态变量";
static {
System.out.println("静态语句块");
}
public String field = "实例变量";
{
System.out.println("普通语句块");
}
最后才运行构造函数
public InitialOrderTest() {
System.out.println("构造函数");
}
存在继承的情况下,初始化顺序为:
- 父类(静态变量、静态语句块)
- 子类(静态变量、静态语句块)
- 父类(实例变量、普通语句块)
- 父类(构造函数)
- 子类(实例变量、普通语句块)
- 子类(构造函数)