You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

子类静态代码块未执行,如何实现无需实例化的跨平台Input类

静态抽象类实例化问题的解决方案

问题背景

想要创建具备静态功能的Input抽象类,其静态方法通过调用抽象成员方法实现平台无关的接口,同时外部无需实例化即可使用。原实现中,抽象类定义如下:

public abstract class Input {
    
    protected static Input instance;
    
    public static boolean isKeyPressed(int keycode) {
        return instance.isKeyPressedImpl(keycode);
    }

    protected abstract boolean isKeyPressedImpl(int keycode);
}

针对Windows平台的子类WindowsInput尝试通过静态代码块为父类的instance赋值:

public class WindowsInput extends Input {
    
    static {
        Input.instance = new WindowsInput();
    }
    
    @Override
    protected boolean isKeyPressedImpl(int keycode) {
        long window = Application.get().getWindow().getPointer();
        int state = glfwGetKey(window, keycode);
        return state == GLFW_PRESS || state == GLFW_REPEAT;
    }
}

但实际运行时,WindowsInput的静态代码块从未执行,导致instance始终为null。

问题原因

JVM仅在类被主动引用时才会加载该类并执行其静态代码块。主动引用的场景包括:

  • 实例化类(new WindowsInput())
  • 调用类的静态方法/访问静态字段
  • 通过反射加载类(Class.forName())
  • 子类被加载时(若父类未加载则先加载父类,但反之不成立)

原代码中,若程序未直接引用WindowsInput(仅通过父类Input调用静态方法),JVM不会加载WindowsInput,因此静态代码块无法触发。

可行解决方案

方案1:根据操作系统自动加载对应子类

在Input类的静态代码块中,通过系统属性判断当前操作系统,主动加载对应平台的子类,触发其静态代码块:

public abstract class Input {
    protected static Input instance;

    static {
        String osName = System.getProperty("os.name").toLowerCase();
        try {
            if (osName.contains("win")) {
                // 加载Windows平台实现类
                Class.forName("com.yourpackage.WindowsInput");
            } else if (osName.contains("mac")) {
                // 加载Mac平台实现类
                Class.forName("com.yourpackage.MacInput");
            } else if (osName.contains("nix") || osName.contains("nux")) {
                // 加载Linux平台实现类
                Class.forName("com.yourpackage.LinuxInput");
            } else {
                throw new UnsupportedOperationException("Unsupported operating system");
            }
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Failed to initialize platform-specific Input", e);
        }
    }

    public static boolean isKeyPressed(int keycode) {
        if (instance == null) {
            throw new IllegalStateException("Input has not been initialized properly");
        }
        return instance.isKeyPressedImpl(keycode);
    }

    protected abstract boolean isKeyPressedImpl(int keycode);
}

方案2:程序启动时显式加载子类

在应用初始化的入口处,主动加载对应平台的子类,确保静态代码块执行:

// 应用启动类的初始化方法中
public class Application {
    public static void init() {
        // 根据当前环境选择加载对应的Input子类
        try {
            Class.forName("com.yourpackage.WindowsInput");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // 其他初始化逻辑...
    }
}

方案3:使用服务提供者接口(SPI)解耦平台实现

  1. 定义InputProvider接口(或直接复用Input抽象类):
public interface InputProvider {
    boolean isKeyPressed(int keycode);
}
  1. 让WindowsInput实现该接口并继承Input:
public class WindowsInput extends Input implements InputProvider {
    @Override
    protected boolean isKeyPressedImpl(int keycode) {
        // 原有实现...
    }

    @Override
    public boolean isKeyPressed(int keycode) {
        return isKeyPressedImpl(keycode);
    }
}
  1. 在src/main/resources/META-INF/services下创建文件com.yourpackage.InputProvider,内容为对应平台的实现类全限定名:
com.yourpackage.WindowsInput
  1. 在Input类中通过SPI加载实现类:
public abstract class Input {
    protected static Input instance;

    static {
        ServiceLoader<InputProvider> loader = ServiceLoader.load(InputProvider.class);
        for (InputProvider provider : loader) {
            if (provider instanceof Input) {
                instance = (Input) provider;
                break;
            }
        }
        if (instance == null) {
            throw new RuntimeException("No Input provider found");
        }
    }

    // 原有静态方法...
}

方案4:添加显式初始化方法

为Input类添加静态初始化方法,允许外部指定平台实现类:

public abstract class Input {
    protected static Input instance;

    public static void initialize(Class<? extends Input> inputClass) {
        try {
            instance = inputClass.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("Failed to initialize Input", e);
        }
    }

    // 原有静态方法...
}

在应用启动时调用:

Input.initialize(WindowsInput.class);

总结

所有方案的核心都是确保平台子类被JVM加载,从而触发其静态代码块或实例化逻辑。其中方案1和方案3更适合自动适配多平台的场景,方案2和方案4则更适合需要手动指定实现的场景。

内容的提问来源于stack exchange,提问作者Jonathan Zinzan Salisbury Vega

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 19:54:28