Java static关键字

0. 静态变量

​ Java类中的静态变量不属于任何一个类实例化出来的对象(即该类所有实例化出来的对象共享使用该静态变量,进而静态变量不具有线程安全性)

//静态变量🌰
private static int count;
public static double temper;
  • 静态变量:在类中,整个类的所有实例化对象共用一个
  • 实例变量:每个实例化对象拥有
  • 静态变量:它的值对所有的实例来说都相同

读取静态变量

静态变量由两种读取方法:

  1. 类名.变量名
  2. 实例化对象名.变量名(前提该类可以实例化)

惯用第一种方法来读取变量,不推荐第二种方法读取。

//代码栗子🌰

class Player {
//设置静态变量playCount,计算实例化对象个数
static int playerCount = 0;
private String name;

public Player(String name) {
this.name = name;
playerCount++;
}
}

public class PlayerTestDrive {
public static void main(String[] args) {
//第一种读取方法:类.变量名
System.out.println(Player.playerCount);
Player one = new Player("at0m");
//第二种读取方法:对象名.变量名
System.out.println(one.playerCount);
}
}

Tips

  • 静态变量会在该类的任何 对象创建之前就完成初始化。
  • 静态变量会在该类的任何静态方法执行之前就初始化。
  • 静态的final变量是常数

1. 静态方法

​ 和静态变量类似的,静态方法属于类而非实例。静态的方法不能调用非静态的变量,静态的方法也不能调用非静态的方法。

​ 静态方法是我们需要公开出来的,无需创建一个实例由实例调用类中的方法。从某种角度而言这种方法减少了对象的创建,为堆节省了创建对象的空间。

​ Java的包装类和工具类包含了大量的静态方法,Java程序的入口点main方法就是一个静态的方法

//代码栗子🌰
public class WrongStaticMethod {
//非静态变量
String name;

//非静态方法
public void printMessage() {
System.out.println("只有实例对象才能输出本条信息");
}

//main方法,静态方法
public static void main(String[] args) {
//下面两行是错误调用方法,无法通过编译
WrongStaticMethod.name = "at0m";
WrongStaticMethod.printMessage();

//实例化类的对象才可以使用非静态的方法和非静态的变量
WrongStaticMethod trueCase = new WrongStaticMethod();
trueCase.name = "at0m";
trueCase.printMessage();

//调用Math中的random静态方法生成随机数
//直接调用,无实例对象
System.out.println(Math.random());
}
}

Tips

  • 静态的方法应该用类的名称来调用,而不是用对象引用变量。
  • 静态的方法可以直接调用而不需要堆上的实例
  • 仅有静态方法的类,可以设置其构造函数为private,防止被初始化。
  • 静态方法可以存取静态变量。

2.静态块

Java静态块中的代码,在Java ClassLoader将其装载到内存中时,所执行的语句。静态块主要用于初始化类的静态变量。大多用来加载创建类时创建静态资源。

在静态块中不能访问非静态变量,在一个类中可以有多个静态块(没有太大意义)。静态块只在装载到内存的时候执行一次。

//代码栗子🌰
class otherClass {
//不加载的类
//静态块
static {
System.out.println("otherClass class is loaded");
}
}

class StaticSuper {
//静态块
static {
System.out.println("super static block");
}
//构造函数
StaticSuper(){
System.out.println("super constructor");
}
}

public class StaticTests extends StaticSuper {
static int rand;
//静态块
static {
rand = (int) (Math.random() * 6);
System.out.println("static block " + rand);
}
//构造函数
StaticTests() {
System.out.println("constructor");
}

public static void main(String[] args) {
System.out.println("in main");
StaticTests st = new StaticTests();
}
}

上述代码执行后根据对象间的关系,输出以下内容

$ java StaticTests 
super static block #先装载父类,执行父类静态块
static block 5 #执行子类静态块
in main #进入main()方法
super constructor #执行父类构造函数
constructor #执行子类构造函数
#没有输出otherClass类中静态块的信息,因为没有被Java ClassLoader装载

Tips

  • 构造函数不能标记为静态的

3.静态类

Java可以嵌套使用静态类,但是静态类不能用于嵌套的顶层。静态嵌套类的使用与其他顶层类一样,嵌套只是为了便于项目打包。

//代码栗子🌰
//StaticExample.java
public class StaticExample {
// 静态块
static {
System.out.println("StaticExample static block");
name = "Test";
setCount(2);
}

// 多个静态块在同一个类中
static {
System.out.println("StaticExample static block2");
}

// 静态变量,使用private关键字,保证只有Setter才能修改该变量值
private static int count;
public static String name;

public int getCount() {
return count;
}

// 静态方法
public static void setCount(int count) {
if (count > 0) {
StaticExample.count = count;
}
}

// static util method
public static int addInts(int i, int... js) {
int sum = i;
for (int x : js)
sum += x;
return sum;
}

// 静态类
public static class MyStaticClass {
public int count;

}
}


//TestStatic.java
public class TestStatic {
public static void main(String[] args) {
//直接调用静态方法
StaticExample.setCount(5);

// 非private的变量可以直接通过 类.变量名 修改
StaticExample.name = "at0m";
StaticExample tmp = new StaticExample();
System.out.println(tmp.getCount());
// 两种访问静态变量的结果是使相同的
System.out.println(StaticExample.name + " is same as " + tmp.name);
System.out.println(StaticExample.name == tmp.name);

// 静态嵌套类 实例化对象
StaticExample.MyStaticClass myStaticClass = new StaticExample.MyStaticClass();
myStaticClass.count = 10;

StaticExample.MyStaticClass myStaticClass1 = new StaticExample.MyStaticClass();
myStaticClass1.count = 20;

System.out.println(myStaticClass.count);
System.out.println(myStaticClass1.count);
}
}

上述代码执行后,输出的结果如下

$ java TestStatic      
StaticExample static block #装载StaticExample类 开始执行静态块
StaticExample static block2 #执行第二个静态块
5 #使用getter获取私有静态变量值
at0m is same as at0m #类.静态变量 == 实例对象.静态变量(静态变量非private)
true #二者结果一致
10 #嵌套类对象的实例变量
20

4.静态import

一般,Java允许用静态成员使用类引用,从Java1.5开始,我们可以使用静态import而不用类引用。

//代码栗子🌰
package com.journaldev.test;

public class A {

public static int MAX = 1000;

public static void foo(){
System.out.println("foo static method");
}
}
//代码栗子🌰
package com.journaldev.test;

import static com.journaldev.test.A.MAX;
import static com.journaldev.test.A.foo;

public class B {

public static void main(String args[]){
System.out.println(MAX); //normally A.MAX
foo(); // normally A.foo()
}
}

第2段代码用了import语句 import static,后面跟着的是静态成员的全称。为了导入一个类中的所有的静态成员,可以这样写“import static com.journadev.test.A.*”,这只有在我们要多次使用一个类的静态变量时写,但这种写法会降低代码的可读性。


参考文章:

https://www.journaldev.com/1365/static-keyword-in-java