Java 来自资源文件的最终变量

Java 来自资源文件的最终变量,java,android,android-activity,android-studio,Java,Android,Android Activity,Android Studio,我有一个带有一些最终变量的活动。我将它们的值(假设它们都是字符串)提取到一个资源文件中 问题是: 如果我在实例化时直接分配它们(如下所示): private final String PREFERENCE\u NAME=getResources().getString(R.String.PREFERENCE\u NAME) 我得到以下错误: 原因:java.lang.NullPointerException:尝试在空对象引用上调用虚拟方法“android.content.res.Resource

我有一个带有一些最终变量的活动。我将它们的值(假设它们都是字符串)提取到一个资源文件中

问题是:

如果我在实例化时直接分配它们(如下所示):

private final String PREFERENCE\u NAME=getResources().getString(R.String.PREFERENCE\u NAME)

我得到以下错误:

原因:java.lang.NullPointerException:尝试在空对象引用上调用虚拟方法“android.content.res.Resources android.content.Context.getResources()”

我理解这个问题;尚未调用onCreate()方法,这意味着我无法访问与上下文相关的方法(
getResources()

如果我想在活动的onCreate()方法中赋值,我会得到错误
无法将值赋值给最终变量'PREFERENCE\u NAME'

问题是:如何从资源文件中获取要分配的最终变量?如果这是不可能的,那么解决方案的最佳实践是什么

提前感谢。

您可以使用解决此问题

生成器是一个类,它聚合创建对象所需的数据,完成后,您只需构建类

在这种方法中,工厂也可以使用生成对象所需的数据,他可以轻松创建对象,并在调用其构造函数时初始化
final
字段

你会有类似

class MyFactory {
   private Resource getResources() { ... }
   public MyObject build() { 
       String perference_name = getResources().getString(R.string.preference_name);
       /...
       return new MyObject(perfence_name ,....);
   }
}

使用应用程序上下文:

  • 创建应用程序类:

    public class MyApplication extends Application {
    
        private static Context mContext;
    
        @Override
        public void onCreate() {
            super.onCreate();
            mContext = getApplicationContext();
        }
    
        public static String getStr(int resId) {
            return mContext.getString(resId);
        }
    
    }
    
  • 在您的清单中使用它:

    <application
        android:name=".MyApplication"
        ...
    

  • 在这种情况下,我认为最好的做法是对资源进行多次调用。您仍然只需在一个位置更改值,对
    getResources()
    的调用并不昂贵。

    简单的答案是您不能

    只有在实例化对象时(即在构造函数中或使用初始化器代码),才能设置声明的变量
    final

    使用
    getResources().getString(R.string.preference_name)所有时间或使用非最终变量

    复杂的答案是你可以,但你不应该

    当您声明一个变量
    final
    时,编译器和VM使用它来进行优化和假设。它可以做到这一点,因为变量保证永远不会改变。在初始化后更改它会导致非常奇怪的错误,所以绝对不应该这样做

    以下是您的操作方法:

    public class FinalMessage {
    
        public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
            FinalMessage f = new FinalMessage("Hello World!");
            System.out.println(f.getMessage());
            f.changeFinalMessage("Hello Mars!");
            System.out.println(f.getMessage());
        }
    
        private final String message;
    
        public FinalMessage(String message) {
            this.message = message;
        }
    
        void changeFinalMessage(String newMessage) throws IllegalAccessException, NoSuchFieldException {
            final Field field = FinalMessage.class.getDeclaredField("message");
            field.setAccessible(true);
    
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    
            field.set(this, newMessage);
        }
    
        String getMessage() {
            return message;
        }
    }
    
    这将输出:

    你好,世界
    你好,火星

    很好,所以我们改变了最后一个变量。没问题吧

    那么,就拿这个例子来说:

    public class FinalMessage {
    
        public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
            FinalMessage f = new FinalMessage();
            System.out.println(f.getMessage());
            f.changeFinalMessage("Hello Mars!");
            System.out.println(f.getMessage());
        }
    
        private final String message = "Hello World!";
    
        void changeFinalMessage(String newMessage) throws IllegalAccessException, NoSuchFieldException {
            final Field field = FinalMessage.class.getDeclaredField("message");
            field.setAccessible(true);
    
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    
            field.set(this, newMessage);
        }
    
        String getMessage() {
            return message;
        }
    }
    
    这将输出:

    你好,世界
    你好,世界

    等等,什么

    问题是编译器可以看到变量
    message
    始终是
    “Hello World!”
    ,因此它内联
    “Hello World!”
    ,而不是调用
    f.getMessage()
    。如果在调试器中运行此命令,您将看到调试器将实例中更新的消息反映到
    “Hello Mars!”
    ,但由于变量实际上从未被访问过,因此不会影响程序的结果

    总结一下:您可以通过反射来更新final字段(假设没有任何礼物阻止您这样做),但您不应该这样做,因为它可能会产生非常非常奇怪的副作用


    如果您真的决定实施此操作,如果您的房子出现白蚁或您的猫着火,我不承担任何责任。

    您只需将其声明为最终方法:

    private final String PREFERENCE_NAME() {
        return getResources().getString(R.string.preference_name);}
    

    你不能。你确定你需要本地化
    首选项\u NAME
    吗?是什么阻止你在需要时从你的资源中获取它?@Blackbelt我需要在多个活动中使用它;如果我出于什么原因需要更改首选项名称,我希望从资源中执行此操作。我真的不认为多次调用资源有什么害处。如果你需要改变值,你仍然只需要在一个地方做,就我所知,这不是一个昂贵的电话不知道为什么-1,这个问题对我来说似乎是合理的。我有+1'dI不实例化我的类,我必须使用onCreate()。我想这对我不管用。不管怎样,谢谢你的建议。我尽量避免创建自定义的应用程序类,因此我将接受Dreagen的建议。不管怎样,谢谢你。我会选择这个解决方案。然而,我想为其他有类似问题的人指出,这不是唯一的解决方案。哇,这本书读得不错。谢谢你解释得很好的例子,为什么不这样做呢+1.
    private final String PREFERENCE_NAME() {
        return getResources().getString(R.string.preference_name);}