一、static 关键字的用途

 在《Java编程思想》P86 页有这样一段话:

  • “static方法就是没有this的方法。

  • 在static方法内部不能调用非静态方法,反过来是可以的。

  • 而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。这实际上正是static方法的主要用途。“

static 方法

static 方法被称为静态方法,由于静态方法不依赖于任何对象就可以进行访问。因此对于静态方法而言,是没有 this 的,因为它不依附于任何对象。因此在静态方法中不能访问类的非静态成员和非静态方法,因为非静态的方法和属性必须依赖于特定的对象。

但是要注意,反过来是可以的,也就是在非静态方法中可以调用静态方法和访问静态成员。

从下图可以看出,非静态可以访问静态,而静态不能访问非静态;

Static关键字_static

static 变量

static 变量也叫静态变量,静态变量和非静态变量的区别就是:静态变量被所有变量所共享,在方法区中只有一个副本,它当且仅在类加载的时候会被初始化。而非今天变量是对象所拥有,在创建对象的时候会被初始化,存在于多个副本那种,各个副本之间的非静态变量互相不干扰;

public class ChineseTest {

    public static void main(String[] args) {
        Chinese.nation = "中国";
        
        Chinese c1 = new Chinese("张三", 12);
        System.out.println("c1 :" + c1);
        System.out.println("c1.nation : " + c1.nation);
        
        Chinese c2 = new Chinese("李四", 15); 
        System.out.println("c2" + c2);
        System.out.println("c2.nation : " + c2.nation);
    }

}

class Chinese {
     String name;
     int age;
    static String nation;

    public Chinese(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Chinese{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

/**
从结果可以看出:静态变量全局就一份,一处修改,所有的实例对象引用同一个静态成员。
但是非静态成员就依赖于具体的类。
c1 :Chinese{name='张三', age=12}
c1.nation : 中国
c2Chinese{name='李四', age=15}
c2.nation : 中国

*/

静态变量和非静态变量的内存图:

Static关键字_static _02

static 代码块

形如:表示一个类中的静态代码块;

static {
    // do something
}

静态代码块在类加载的最后一个阶段-初始化阶段,会有一个 <clinit> 方法,然后这个方法就会收集所有的静态成员的赋值和静态代码块来进行初始化,因此可以看出,静态代码块在类加载的时候就会完成初始化,因此一定是在类实例创建之前就会执行完成。

public class Static_blockDemo {
    public static void main(String[] args) {
        new StaticBlock();
    }
}
class StaticBlock{

    static { System.out.println("静态代码块..."); }
	
    {  System.out.println("非静态代码块"); }
    
    public StaticBlock () {
        System.out.println("类加载器...");
    }
}
// 结果:
静态代码块...
非静态代码块
类加载器...

在来看一下存在父类的情况:

public class Static_extends_demo {
    public static void main(String[] args) {
        new Kid();
    }
}
// 父类
class Father {
    static int a = 1;
    int b = 1;

    static {
        System.out.println("父类静态代码块..");
        // System.out.println("父-静态代码块:father.a = " + a);
    }

    {
        b = 2;
        System.out.println("父类非静态代码块...");
        // System.out.println("父-非静态代码块father.b = " + b);
    }

    public Father() {
        System.out.println("父类构造器...");
        this.b = 3;
        // System.out.println("父-构造方法:father.b = " + b);
    }
}

// 子类
class Kid extends Father {
    static int a = 1;
    int b = 1;

    static {
        System.out.println("子类静态代码块..");
        // System.out.println("子-静态代码块:father.a = " + a);

    }
    {
        b = 2;
        System.out.println("子类非静态代码块...");
        // System.out.println("子-非静态代码块:kid.b = " + b);
    }

    public Kid() {
        System.out.println("子类构造器...");
        this.b = 3;
        // System.out.println("子-构造方法:kid.b = " + b);
    }
}

父类静态代码块..
子类静态代码块..
父类非静态代码块...
父类构造器...
子类非静态代码块...
子类构造器...

最后总结一下类实例化的顺序:

  • 父类静态代码块 和 静态成员

  • 子类静态代码块 和 静态成员

  • 父类非静态属性 和 非静态代码块

  • 父类构造器

  • 子类非静态属性 和 非静态代码块

  • 子类构造器