浅谈Java-static关键字

36次阅读

共计 1736 个字符,预计需要花费 5 分钟才能阅读完成。

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

static 方法就是没有 this 的方法。在 static 方法内部不能调用非静态方法,反过来是可以的。而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用 static 方法。这实际上正是 static 方法的主要用途。

简而言之,一句话来描述就是:方便在没有创建对象的情况下来进行调用(方法 / 变量)。

static 可以用来修饰类的成员方法、类的成员变量,另外可以编写 static 代码块来优化程序性能。

static 方法

static 方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有 this 的,因为它不依附于任何对象,既然都没有对象,就谈不上 this 了。
并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法 / 变量都是必须依赖具体的对象才能够被调用。

static 变量

static 变量也称作静态变量,静态变量和非静态变量的区别是:

  • 静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。
  • 非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

static 成员变量的初始化顺序按照定义的顺序进行初始化。

static 代码块

static 关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能

static 块可以置于类中的任何地方,类中可以有多个 static 块。在类初次被加载的时候,会按照 static 块的顺序来执行每个 static 块,并且只会执行一次。

为什么说 static 块可以用来优化程序性能,是因为它的特性: 只会在类加载的时候执行一次。下面看个例子:

public boolean isBornBoomer() {Date startDate = Date.valueOf("1946");
        Date endDate = Date.valueOf("1964");
        return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
    }

isBornBoomer 是用来这个人是否是 1946-1964 年出生的,而每次 isBornBoomer 被调用的时候,都会生成 startDate 和 endDate 两个对象,造成了空间浪费。
如果改成这样效率会更好:

private static Date startDate,endDate;
static{startDate = Date.valueOf("1946");
    endDate = Date.valueOf("1964");
}

因此,很多时候会将一些只需要进行一次的初始化操作都放在 static 代码块中进行。

常见面试题

下面这段代码的输出结果是什么?

public class Test extends Base{ 
    static{System.out.println("test static");
    }     
    public Test(){System.out.println("test constructor");
    }     
    public static void main(String[] args) {new Test();
    }
} 
class Base{     
    static{System.out.println("base static");
    }     
    public Base(){System.out.println("base constructor");
    }
}

结果:
base static
test static
base constructor
test constructor

在执行开始,先要寻找到 main 方法,因为 main 方法是程序的入口,但是在执行 main 方法之前,必须先加载 Test 类。
而在加载 Test 类的时候发现 Test 类继承自 Base 类,因此会转去先加载 Base 类,在加载 Base 类的时候,发现有 static 块,便执行了 static 块。
在 Base 类加载完成之后,便继续加载 Test 类,然后发现 Test 类中也有 static 块,便执行 static 块。
在加载完所需的类之后,便开始执行 main 方法。在 main 方法中执行 new Test() 的时候会先调用父类的构造器,然后再调用自身的构造器。

正文完
 0