杰瑞科技汇

java static 什么时候用

当你希望某个成员(变量或方法)不属于任何一个具体的对象实例,而是属于这个类本身时,就应该使用 static

java static 什么时候用-图1
(图片来源网络,侵删)

下面我们从几个方面来详细解释 static 的使用场景和注意事项。


静态变量

静态变量(也称为类变量)是被所有该类的对象实例共享的。

什么时候用?

  1. 共享状态:当一个类的所有实例都需要共享同一个数据时。

    • 经典例子:在一个游戏中,玩家类 Player 有一个属性是 totalPlayerCount(总玩家数),每当创建一个新玩家,这个总数就应该加一,这个 totalPlayerCount 就应该被所有 Player 对象共享,而不是每个玩家都有自己的“总玩家数”,所以它应该被声明为 static

      java static 什么时候用-图2
      (图片来源网络,侵删)
    • 代码示例

      public class Player {
          // 每个玩家自己的名字(非静态,属于对象)
          private String name;
          // 所有玩家共享的总数(静态,属于类)
          private static int totalPlayerCount = 0;
          public Player(String name) {
              this.name = name;
              totalPlayerCount++; // 创建新玩家时,总数增加
          }
          public static int getTotalPlayerCount() {
              return totalPlayerCount;
          }
      }
      // 使用
      Player p1 = new Player("Alice");
      Player p2 = new Player("Bob");
      System.out.println(Player.getTotalPlayerCount()); // 输出: 2
  2. 常量定义:Java 中定义常量的标准方式就是使用 public static final

    • 例子Math.PISystem.out
    • 代码示例
      public class MathConstants {
          // PI 是一个固定不变的值,所有实例共享,所以用 static final
          public static final double PI = 3.14159;
      }

如何访问?

  • 通过类名访问(推荐)Player.totalPlayerCount
  • 通过对象名访问(不推荐,容易引起混淆)p1.totalPlayerCount

静态方法

静态方法(也称为类方法)在不创建任何对象实例的情况下就可以被调用

什么时候用?

  1. 工具方法:当一个方法不依赖于任何对象的状态(即不访问任何非静态的成员变量)时,它非常适合被声明为静态。

    • 例子Math.sqrt()Collections.sort()Arrays.toString(),这些方法只是执行一些计算或操作,它们不关心是哪个 Math 对象调用了它们,因为 Math 根本没有可改变的状态。

    • 代码示例

      public class ArrayUtils {
          // 这个方法不依赖于任何对象实例的状态,所以是静态的
          public static int findMax(int[] array) {
              if (array == null || array.length == 0) {
                  throw new IllegalArgumentException("Array cannot be empty");
              }
              int max = array[0];
              for (int i = 1; i < array.length; i++) {
                  if (array[i] > max) {
                      max = array[i];
                  }
              }
              return max;
          }
      }
      // 使用,无需创建 ArrayUtils 对象
      int[] numbers = {1, 5, 3, 9, 2};
      int maxNumber = ArrayUtils.findMax(numbers);
      System.out.println("Max number is: " + maxNumber); // 输出: Max number is: 9
  2. 工厂方法:用于创建和返回该类的一个新实例。

    • 例子BigInteger.valueOf(long val)

    • 代码示例

      public class User {
          private String username;
          private User(String username) {
              this.username = username;
          }
          // 静态工厂方法,创建 User 实例
          public static User create(String username) {
              return new User(username);
          }
      }
      // 使用
      User user = User.create("john_doe");
  3. 访问静态变量:当一个方法只需要操作静态变量时,它也应该是静态的。

静态方法的限制?

静态方法不能直接访问:

  • 非静态成员变量(实例变量)
  • 非静态成员方法(实例方法)

因为它没有 this 引用。this 代表当前对象的引用,而静态方法不属于任何一个对象。


静态代码块

静态代码块在类被加载到 JVM 时执行,并且只执行一次,通常用于静态变量的初始化

什么时候用?

当初始化一个静态变量需要执行复杂的逻辑(如读取配置文件、建立数据库连接池等)时,不能直接在声明时赋值,这时就需要静态代码块。

  • 例子:加载驱动程序、初始化日志系统。

  • 代码示例

    public class DatabaseConfig {
        private static String dbUrl;
        private static String username;
        private static String password;
        // 静态代码块,在类加载时执行
        static {
            try {
                // 模拟从配置文件中读取数据
                Properties props = new Properties();
                props.load(DatabaseConfig.class.getResourceAsStream("/db.properties"));
                dbUrl = props.getProperty("db.url");
                username = props.getProperty("db.username");
                password = props.getProperty("db.password");
                System.out.println("Database configuration loaded successfully.");
            } catch (IOException e) {
                System.err.println("Failed to load database configuration.");
                e.printStackTrace();
            }
        }
        // 私有构造函数,防止外部实例化
        private DatabaseConfig() {}
        public static String getDbUrl() {
            return dbUrl;
        }
    }

静态内部类

静态内部类与外部类关联,但不持有外部类的引用,它更像是一个普通的嵌套类。

什么时候用?

当一个内部类不需要访问外部类的实例成员(非静态成员)时,就应该使用静态内部类,这可以避免内存泄漏和不必要的依赖。

  • 例子HashMap 中的 Node 类就是一个静态内部类。

  • 代码示例

    public class OuterClass {
        private int outerInstanceVar = 10;
        // 静态内部类
        static class StaticInnerClass {
            // 可以访问外部类的静态成员
            private static int outerStaticVar = 20;
            public void display() {
                // System.out.println(outerInstanceVar); // 编译错误!不能访问非静态成员
                System.out.println("Outer static var: " + outerStaticVar);
            }
        }
    }

总结表格

成员类型 何时使用 关键特点 访问方式
静态变量 数据需要被所有实例共享,或定义为常量 (public static final) 属于类,不属于对象,所有实例共享一份,修改会影响所有实例。 ClassName.variableName
静态方法 方法逻辑不依赖于任何对象的状态(不访问实例成员),或作为工具方法、工厂方法。 不需要创建对象即可调用,没有 this 引用。 ClassName.methodName()
静态代码块 在类加载时执行一次,用于初始化静态变量。 只在类首次加载到 JVM 时执行。 自动执行
静态内部类 内部类不需要访问外部类的实例成员。 不持有外部类的 this 引用,可以独立存在。 OuterClass.InnerClass

一个重要的反面教材:单例模式

很多人会误用 static 来实现单例模式,虽然它能工作,但这不是最佳实践。

  • 不好的方式(饿汉式)

    public class Singleton {
        // 静态变量,在类加载时就初始化了
        private static final Singleton INSTANCE = new Singleton();
        // 私有构造函数,防止外部 new
        private Singleton() {}
        public static Singleton getInstance() {
            return INSTANCE;
        }
    }
    • 缺点:如果这个单例对象很大,而且程序可能一直用不到它,那么它会在类加载时就占用内存,造成浪费。
  • 更好的方式(懒汉式,双重检查锁): 这种方式延迟了对象的创建,只有在第一次调用 getInstance() 时才创建,更符合现代应用的需求。

核心思想

记住这个核心原则:static 将成员从“对象级别的”提升到了“类级别的”,当你问自己“这个东西是属于每一个具体的对象,还是属于这个概念性的‘类’?”时,答案就能帮你决定是否使用 static

分享:
扫描分享到社交APP
上一篇
下一篇