static关键字用于修饰类的成员,如成员变量以及代码块等。

1.静态变量

class Student{
    static String schoolName;
}


public class test2 {
    public static void main(String[] args) {
        Student student1 = new Student();
        Student student2 = new Student();

        Student.schoolName="清华大学";  

        System.out.println("我是student1,来自"+student1.schoolName);
        System.out.println("我是student2,来自"+student2.schoolName);
    }
}

alt

由程序可以看出,所有学生对象共享一个schoolName的变量,在Java类中,要实现这种功能可以使用static关键字来修饰成员变量,该变量被称为静态变量。

静态变量的访问方式:

(1)类名.变量名
(2)定义类的对象,使用对象.成员变量名访问。

静态变量具有“记忆”功能,即一个被声明为静态的变量只会被初始化一次,该函数在调用过程中,其值保持不变。一个变量被定义为静态变量就会将其放到静态数据区,而不是栈区。

class student{
    static int num=10;
    int number=10;

    public int getNum() {
        return num;
    }
    public int addNum(){
        return num++;
    }

    public int addNumber(){
        return number++;
    }
    public int getNumber() {
        return number;
    }

}
public class test {
    public static void main(String[] args) {
        student student1 = new student();
        student student2 = new student();
        student1.addNum();
        student2.addNum();
        student1.addNum();
        student2.addNum();

        student1.addNumber();
        student2.addNumber();
        student1.addNumber();
        student2.addNumber();

        System.out.println("student1 :"+"num="+ student1.getNum()+"  number="+student1.getNumber());
        System.out.println("student2 :"+"num=" + student2.getNum()+"  number="+student2.getNumber());
    }
}

alt 从上述代码中我们看出,静态成员变量与非静态成员变量的区别。。

注意:

static关键字只能用于修饰成员变量,不能用于修饰局部变量,否则编译会报错,如下代码时不合法的。。

class Person{
   public void add(){
       static int num=10;
   }
}

alt

2.静态方法

如果想要使用类中的成员方法,就需要先将这个类实例化,而在实际开发时,开发人员希望在不创建对象的情况下就开始调用这个方法,这种情况就可以使用静态方法。

静态方法的访问方式:

(1)类名.方法
(2)实例化对象名.方法(不推荐)
class Person{
    public static void say(){
        System.out.println("hello world");
    }
}

public class test {
    public static void main(String[] args) {
        //用类名.方法 的方式调用静态方法
        Person.say();

        //实例化对象
        Person person = new Person();
        //用实例化对象名.方法的方式调用静态方法
        person.say();
    }
}

alt

在第十行代码中通过Person.say()的形式调用了静态方法,由此可见,静态方法不需要创建对象就可以直接通过类名调用。

注:在一个静态方法中只能访问用static修饰的成员,原因是在于没有被static修饰的成员需要先创建对象才能被访问,而静态方法在被调用时不创建任何对象。

3.静态代码块和初始化块

3.1 静态代码块

在Java中,使用一对大括号包围起来的若干行代码被称为一个代码块,用static关键字修饰打代码块被称为静态代码块,语法如下:

static{
....
}

当类被加载时,静态代码块会执行,由于类只加载一次,因此静态代码块也只执行一次

在程序中,通常使用静态代码块对类的成员变量进行初始化。

class Person{
    String name="张三";
    //静态代码块
    static {
        System.out.println("执行了Person中的静态代码块");
    }
}

public class test {
    static {
        System.out.println("执行测试类中的静态代码块1");
    }

    public static void main(String[] args) {
        Person person1 = new Person();
        Person person2 = new Person();
    }

    static {
        System.out.println("执行测试类中的静态代码块2");
    }
}

alt

可以看出,程序中的三段代码块全部执行了,在运行程序的时候,Java虚拟机首先会加载类test,在加载类的同时就会执行该类的静态代码块,紧接着会调用main()方法,在main()方法中创建了两个Person对象,但是在实例化的时候,静态代码块的内容只输出了一次,这就说明静态代码块在类第一次使用的时候才会被加载,并且只会加载一次。

3.2 实例初始化块

3.2.1 实例初始化块介绍

初始化块就是在类的内部直接用{}括起来的一段代码(注意这段代码必须在方法外,方法内的是代码块)。初始化块用于对象的初始化。

原则上讲,一个类可以有多个初始化块,按照定义的先后顺序依次执行。但实际上根本没必要定义多个初始化块。

初始化块内部可以放任何可执行性语句,它和构造器都是用于初始化对象。只是构造器是一个方法,根据传入参数的不同,不同对象的构造器执行结果会不同。但初始化块不接受任何参数,所以,初始化块执行的内容相对死板。

实例初始化块会在初始化类的一个实例时执行,而且在构造函数之前就执行。并且每次创建类的对象时它们都会执行

class Person{
    String name="张三";

    //初始化块
    {
        System.out.println("执行了初始化块:" + name);
    }

    //构造方法
    public Person() {
        System.out.println("执行了构造方法"+name);
    }
}

public class test {

    {
        String name="张三";
        System.out.println("name = " + name);
    }

    public static void main(String[] args) {
        Person person1 = new Person();
        Person person2 = new Person();
        {
            String name="张三";
            System.out.println("name = " + name);
        }
    }

}

alt

实例初始化块和类初始化块:

(1)初始化块作为类的第四个成员,它的修饰符只有一个static。

(2)如果在{}之前加上static修饰符,就表明这是类初始化块。

(3)如果没有修饰符,就是实例初始化块。

实例初始化块与对象相关,会在对象初始化阶段执行代码,帮助对象初始化。

而类初始化块与类相关,在类初始化阶段就会执行,帮助类的初始化。

(4)由于类的初始化早于对象初始化,所以类初始化块一定会先于实例初始化块执行。

类初始化块也是类成员,要遵循类成员不能访问实例成员的原则。这很好理解,凡是和类相关的成员,都不能出现和对象相关的代码,否则系统根本不知道选择哪个对象。

3.2.2 父类中的初始化块
class A{
    {
        System.out.println("父类中的实例初始化块");
    }
    public A() {
        System.out.println("父类的构造函数");
    }
}

public class test extends A{
    {
        System.out.println("子类中的实例初始化块");
    }

    public test() {
        System.out.println("子类的构造函数");
    }

    public static void main(String[] args) {
        test test = new test();
    }
}

alt

由上述程序运行结果可知:

在继承体系中,实例初始化块和构造函数的执行顺序如下:

执行父类的实例初始化块
执行父类的构造函数
执行当前类的实例初始化块
执行当前类的构造函数

3.3 静态代码块,实例初始化块,构造函数的执行顺序

class Person{
    String name="张三";
    //静态代码块
    static {
        System.out.println("执行了Person中的静态代码块");
    }
    //初始化块
    {
        System.out.println("执行了初始化块:" + name);
    }

    //构造方法
    public Person() {
        System.out.println("执行了构造方法"+name);
    }
}

public class test {
    static {
        System.out.println("执行测试类中的静态代码块1");
    }

    {
        String name="张三";
        System.out.println("name = " + name);
    }

    public static void main(String[] args) {
        Person person1 = new Person();
        Person person2 = new Person();
        {
            String name="张三";
            System.out.println("name = " + name);
        }
    }
    static {
        System.out.println("执行测试类中的静态代码块2");
    }
}

alt

由上述程序执行情况可以看出:

静态初始化块最先被执行
其次执行,实例初始化块
最后执行,构造方法
静态成员变量 = 静态代码块 > 非静态成员变量 = 非静态代码块 > 构造函数

4.静态类

通常一个普通类不允许声明为静态的,只有一个内部类才可以。这时这个声明为静态的内部类可以直接作为一个普通类来使用,而不需实例一个外部类。

class Person{
    //静态内部类
    public static class Student{
        public void sayStudent(){
            System.out.println("hello student");
        }
        public static void sayHelloStudent(){
            System.out.println("say hello student");
        }
    }

    public static void say(){
        System.out.println("hello world");
    }
}

public class test {
    public static void main(String[] args) {
        //用类名.方法 的方式调用静态方法
        Person.say();

        //调用内部类
        Person.Student student = new Person.Student();
        //调用内部类中的非静态方法
        student.sayStudent();

        //调用内部类中的静态方法
        Person.Student.sayHelloStudent();
    }
}

alt