一、static的基本概念
static
是Java中的一个修饰符,可用于修饰类的成员变量
、方法
、代码块
和嵌套类
。被static修饰的成员属于类本身
,而不是类的某个实例对象。这意味着静态成员在类加载时就会被初始化
,并且所有该类的实例共享同一份静态成员。
二、static变量
1、静态变量(类变量)
静态变量属于类
,而不是某个特定的对象实例。所有实例共享同一个静态变量
java
public class Counter {
// 静态变量
private static int count = 0;
public Counter() {
count++; // 每次创建实例时count增加
}
public static int getCount() {
return count;
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter c3 = new Counter();
System.out.println("创建的实例数量: " + Counter.getCount()); // 输出: 3
}
}
2、静态变量的初始化
- JVM加载类时,会执行类初始化(Class Initialization),此时静态变量会被显式赋值(若未显式赋值则使用默认值)
- 默认值规则:数值类型为
0
,布尔类型为false
,引用类型为null
- 显式赋值:在
类
定义中直接赋值,或在静态代码块
中赋值(二者执行顺序按代码编写顺序)
- 默认值规则:数值类型为
java
public class InitializationExample {
// 声明时初始化
static int a = 10;
// 静态代码块初始化
static int b;
static {
b = 20;
}
// 默认赋值
static int c;
public static void main(String[] args) {
System.out.println("a=" + a); // 输出10
System.out.println("b=" + b); // 输出20
System.out.println("c=" + c); // 输出0
}
}
3、静态变量 vs 实例变量
特性 | 静态变量 | 实例变量 |
---|---|---|
内存分配 |
类加载时分配,在方法区 | 对象创建时分配,在堆内存 |
生命周期 |
与类相同 | 与对象实例相同 |
访问方式 |
类名.变量名 或 对象.变量名 | 只能通过对象.变量名 |
共享性 |
所有实例共享 | 每个实例独有 |
4、静态变量的线程安全性
- 静态变量被所有实例共享,多线程环境下修改可能导致数据不一致(需通过
synchronized
、Lock
或AtomicXXX
类保证原子性)
java
public class ThreadSafeCounter {
private static int count = 0;
private static final Object lock = new Object();
// 线程不安全的递增
public static void unsafeIncrement() {
count++;
}
// 线程安全的递增
public static void safeIncrement() {
synchronized (lock) {
count++;
}
}
// 使用AtomicInteger实现线程安全
private static AtomicInteger atomicCount = new AtomicInteger(0);
public static void atomicIncrement() {
atomicCount.incrementAndGet();
}
}
三、static方法
1、静态方法
- 静态方法属于类,而不是类的实例
- 可以通过类名直接调用,无需创建实例
java
public class MathUtils {
// 静态变量
private static final double PI = 3.14159;
// 静态方法
public static double calculateCircleArea(double radius) {
return PI * radius * radius;
}
// 另一个静态方法,可以调用其他静态方法
public static double calculateCircleCircumference(double radius) {
return 2 * PI * radius;
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
double area = MathUtils.calculateCircleArea(5.0);
double circumference = MathUtils.calculateCircleCircumference(5.0);
System.out.println("面积: " + area);
System.out.println("周长: " + circumference);
}
}
2、静态方法的使用限制
不能使用this和super关键字
:因为静态方法不依赖于任何实例不能直接访问实例变量和实例方法
:只能直接访问静态成员不能被重写
:静态方法可以被子类"隐藏",但不能被重写
代码示例:重写 vs. 隐藏
- 隐藏: 针对静态方法、静态变量和实例变量
- 发生在编译时,基于引用的声明类型(
等号左边的类型
)来决定调用哪个方法或字段 - 这就是
静态绑定
或早期绑定
java
class Animal {
// 这是一个静态方法,将被"隐藏"
public static void staticMethod() {
System.out.println("Animal: Static Method");
}
// 这是一个实例方法,将被"重写"
public void instanceMethod() {
System.out.println("Animal: Instance Method");
}
}
class Dog extends Animal {
// 隐藏父类的静态方法 (使用 @Override 注解会报错,因为它不是重写)
public static void staticMethod() {
System.out.println("Dog: Static Method");
}
// 重写父类的实例方法 (使用 @Override 注解是正确且推荐的)
@Override
public void instanceMethod() {
System.out.println("Dog: Instance Method");
}
}
public class Test {
public static void main(String[] args) {
// 关键:声明类型为 Animal,实际对象为 Dog
Animal myAnimal = new Dog();
// 调用静态方法 - 隐藏 (看引用类型 Animal)
myAnimal.staticMethod(); // 输出: Animal: Static Method
// 调用实例方法 - 重写 (看对象类型 Dog)
myAnimal.instanceMethod(); // 输出: Dog: Instance Method
// 通过类名调用静态方法(正确方式)
Animal.staticMethod(); // 输出: Animal: Static Method
Dog.staticMethod(); // 输出: Dog: Static Method
}
}
四、static代码块
- 静态代码块在类加载时执行,且
只执行一次
,常用于初始化静态变量或执行只需进行一次的操作
java
public class DatabaseConnection {
private static Connection connection;
// 静态代码块
static {
try {
// 初始化数据库连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");
System.out.println("数据库连接已建立");
} catch (SQLException e) {
System.out.println("数据库连接失败: " + e.getMessage());
}
}
public static Connection getConnection() {
return connection;
}
}
五、static嵌套类
1、静态内部类
- static嵌套类(静态内部类)是声明为static的内部类
- 不依赖于外部类的实例
- 可以访问外部类的static成员
- 不能直接访问外部类的非static成员
- 可以包含static和非static成员
java
public class OuterClass {
private static String staticField = "静态字段";
private String instanceField = "实例字段";
// 静态嵌套类
public static class StaticNestedClass {
public void print() {
System.out.println(staticField); // 可以访问外部类的静态成员
// System.out.println(instanceField); // 错误:不能访问外部类的实例成员
}
}
// 内部类(非静态)
public class InnerClass {
public void print() {
System.out.println(staticField); // 可以访问静态成员
System.out.println(instanceField); // 也可以访问实例成员
}
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
// 依赖外部类,需要先创建外部类,再创建内部类实例
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
inner.print();
// 不需要外部类实例,直接创建静态嵌套类实例
OuterClass.StaticNestedClass nested = new OuterClass.StaticNestedClass();
nested.print();
}
}
2、常见用途(Builder模式)
示例:自定义一个带有 Builder 模式的类
java
public class User {
// 成员变量
private String name;
private Integer age;
private String email;
// 私有构造方法,只能通过 Builder 构建
private User(String name, Integer age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
public static UserBuilder builder() {
return new UserBuilder();
}
// 静态内部类 Builder
public static class UserBuilder {
private String name;
private Integer age;
private String email;
private UserBuilder() {
}
// 设置参数的方法,返回 Builder 本身,支持链式调用
public UserBuilder name(final String name) {
this.name = name;
return this;
}
public UserBuilder age(final Integer age) {
this.age = age;
return this;
}
public UserBuilder email(final String email) {
this.email = email;
return this;
}
// 构建 User 对象
public User build() {
return new User(this.name, this.age, this.email);
}
}
}
使用方式:
java
public class Main {
public static void main(String[] args) {
User user = User.builder()
.name("xuchang")
.age(18)
.email("xuchang@gmail.com")
.build();
System.out.println(user);
}
}
六、静态导入
- Java 5引入了static导入功能,允许直接使用其他类中定义的static成员而
不需要类名前缀
java
// 导入Math类的所有static成员
import static java.lang.Math.*;
public class StaticImportExample {
public static void main(String[] args) {
// 直接使用Math类的static方法,无需Math.前缀
double result = sin(PI / 2) + cos(0);
System.out.println("计算结果: " + result); // 输出2.0
}
}
- 也可以只导入特定的static成员
java
// 只导入Math类的sqrt和PI
import static java.lang.Math.sqrt;
import static java.lang.Math.PI;
public class SelectiveStaticImport {
public static void main(String[] args) {
double area = PI * sqrt(2) * sqrt(2); // 计算半径为√2的圆的面积
System.out.println("面积: " + area); // 输出约6.283185307179586
}
}