static关键字
概述
static 关键字在 Java 中用于定义静态成员。它有以下几个关键点:
1、静态变量:属于类而不是实例,所有实例共享同一个静态变量。例如,public static int count 只会有一个 count 变量,所有对象都访问这个变量。
2、静态方法:同样属于类而不是实例,可以直接通过类调用,而无需创建对象。例如,public static void printMessage() 可以用 ClassName.printMessage() 访问。
3、静态代码块:用于初始化静态变量,且在类加载时运行一次。例如:
static {
// 初始化代码
}
4、静态内部类:只能在嵌套类中定义,作为外部类的静态成员。例如,static class InnerClass 可以被外部类直接访问。
静态成员的主要优势在于,它们在内存中只存在一份,适合存储全局共享数据或方法。并且按照顺序执行。
以下逐一讲解。
一、静态变量
静态变量在 Java 中是类级别的,而不是对象级别的。这意味着所有的实例共享同一个静态变量,而不是每个实例都有自己独立的副本。静态变量的主要特点包括:
共享:所有实例都访问同一个静态变量。
类名访问:可以通过类名直接访问,而无需创建对象。
内存管理:只在内存中存在一份,节省空间。
示例
public class Counter {
// 静态变量
public static int count = 0;
// 构造方法
public Counter() {
count++; // 每次创建对象时,静态变量 count 增加
}
public static void displayCount() {
System.out.println("Count: " + count);
}
public static void main(String[] args) {
// 创建对象
Counter c1 = new Counter();
Counter c2 = new Counter();
// 静态变量通过类名访问
System.out.println("Static count via class name: " + Counter.count);
// 静态方法通过类名访问
Counter.displayCount();
}
}
解释
count 是一个静态变量,所有 Counter 类的实例共享这个变量。
每创建一个 Counter 对象,count 增加 1。
displayCount 是一个静态方法,可以通过 Counter.displayCount() 调用,显示当前 count 的值。
通过 Counter.count 可以访问静态变量,而无需实例化 Counter 类。
在这个例子中,输出将是:
Static count via class name: 2
Count: 2
这表明 count 在两个对象间共享,并且静态方法和变量的修改对所有对象可见。
二、静态方法
静态方法在 Java 中与静态变量类似,具有以下几个重要特点:
类级别:静态方法属于类本身,而不是类的实例。这意味着静态方法可以直接通过类名调用,而不需要创建类的对象。
无法访问实例变量和实例方法:静态方法只能直接访问其他静态变量和静态方法,无法访问实例变量和实例方法,因为实例变量和实例方法依赖于类的实例。
共享:静态方法是共享的,所有对象访问的是同一个静态方法。
示例
public class MathUtils {
// 静态变量
public static final double PI = 3.14159;
// 静态方法
public static int add(int a, int b) {
return a + b;
}
public static void main(String[] args) {
// 通过类名调用静态方法
int sum = MathUtils.add(5, 7);
System.out.println("Sum: " + sum); // 输出: Sum: 12
// 通过类名访问静态变量
System.out.println("Value of PI: " + MathUtils.PI); // 输出: Value of PI: 3.14159
}
}
解释
静态方法 add:public static int add(int a, int b) 是一个静态方法,它接受两个整数参数并返回它们的和。这个方法是类级别的,可以通过 MathUtils.add() 调用,而无需创建 MathUtils 类的实例。
静态变量 PI:public static final double PI = 3.14159; 是一个静态常量,表示圆周率的值。它也是类级别的,可以通过 MathUtils.PI 访问。
调用静态方法
静态方法可以通过类名直接调用,例如 MathUtils.add(5, 7)。这是因为静态方法不依赖于任何特定的对象实例。
静态方法也可以通过类的实例调用,但是这种做法是不推荐的,因为静态方法的调用方式应明确表示其属于类而非某个实例。
三、静态代码块
在 Java 中,静态代码块(static block)是一种特殊的代码块,用于初始化类级别的资源或执行类级别的初始化操作。静态代码块是在类被加载时执行的,只会执行一次。这使得它们非常适合用于执行一次性的初始化操作。
静态代码块的特点
执行时机:静态代码块在类被加载到 JVM 时执行,通常在 main 方法之前。
执行次数:每个类只有一个静态代码块,且只会被执行一次。
用途:用于初始化静态变量、执行一次性的操作等。
静态代码块的语法
静态代码块使用 static 关键字定义,语法如下:
static {
// 静态初始化代码
}
示例
以下是一个使用静态代码块的示例:
public class StaticBlockExample {
// 静态变量
public static int staticValue;
// 静态代码块
static {
System.out.println("静态代码块被执行");
staticValue = 10; // 初始化静态变量
}
public static void main(String[] args) {
System.out.println("静态变量的值: " + StaticBlockExample.staticValue);
}
}
解释
静态代码块的执行:在 StaticBlockExample 类被加载时,静态代码块会被执行,输出 “静态代码块被执行”。然后,静态变量 staticValue 会被初始化为 10。
main 方法的执行:在 main 方法执行时,静态变量 staticValue 的值已经被静态代码块初始化为 10,因此输出 “静态变量的值: 10”。
静态代码块的应用场景
静态变量初始化:当静态变量需要复杂的初始化时,可以使用静态代码块来完成。
类级别的初始化:用于执行类级别的一次性初始化操作,如加载配置文件、设置系统属性等。
注意事项
顺序:如果一个类中有多个静态代码块,它们会按出现的顺序依次执行。
四、静态内部类
静态内部类是 Java 中的一种特殊内部类,它被声明为 static。与普通的内部类不同,静态内部类可以独立于外部类的实例存在,不依赖于外部类的实例。以下是静态内部类的主要特点和用途:
静态内部类的特点
与外部类独立:静态内部类不持有对外部类实例的隐式引用。因此,它不能直接访问外部类的实例变量和实例方法,只能访问外部类的静态成员。
可以独立实例化:静态内部类可以像普通类一样实例化,无需外部类的实例。例如:OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();
访问权限:静态内部类可以访问外部类的静态成员(变量和方法)。
示例
以下是一个静态内部类的示例:
public class OuterClass {
// 外部类的静态成员
private static String staticOuterField = "外部类的静态字段";
// 静态内部类
static class StaticInnerClass {
void display() {
// 访问外部类的静态成员
System.out.println("访问外部类的静态字段: " + staticOuterField);
}
}
public static void main(String[] args) {
// 创建静态内部类的实例
StaticInnerClass inner = new StaticInnerClass();
inner.display();
}
}
解释
静态内部类的定义:StaticInnerClass 被定义为静态内部类,它可以直接访问 OuterClass 的静态字段 staticOuterField。
实例化静态内部类:在 main 方法中,我们可以直接创建 StaticInnerClass 的实例,而无需先创建 OuterClass 的实例。
访问外部类的静态成员:静态内部类的 display 方法可以访问外部类的静态成员 staticOuterField。
五、小记:运行分析实战
代码
public class Demo {
public Demo(String aa) {
System.out.println("====" + aa);
}
static {
System.out.println("11");
}
public static Demo demo = new Demo("+++");
static {
System.out.println("22");
}
}
class Test{
public static void main(String[] args){
Demo demo = new Demo( "----");
}
}
输出结果
流程分析
首先扫描到main方法,定位到Test类,之后扫描Test类,发现除Object外无父类,执行main方法;
执行到语句Demo demo = new Demo(“----”);调用到Demo构造器,扫描Demo类;
发现Demo类除Object外无父类,将Demo.class载入到内存类信息中,并将static静态部分载入到静态方法区/静态变量池;
由于static顺序加载性,在将Demo.class载入到内存时输出前三行,最后一行由main函数输出;