【问题标题】:initialize static final variable in child class(sub class) in java在java中的子类(子类)中初始化静态最终变量
【发布时间】:2015-11-21 11:58:30
【问题描述】:

我有这样的课:

public abstract class A {
    public static final int FIELD;
    // some methods
}

我想在子类中初始化变量 FIELD。 我的意思是这样的:

public class B extends A {
    FIELD = 5;
}

编辑 实际上我有多个扩展 A 的类,它们都有变量 FIELD 但具有不同的值;所以我发现的方法是重构变量 FIELD 并在超类中声明它。还有其他解决方案吗? 有可能有这样的东西吗? 谢谢你的帮助。

【问题讨论】:

  • 您为什么要这样做?为什么不在 B 类中定义变量呢?您是否意识到静态值只有一个实例?
  • 看看this
  • 实际上我有多个扩展 A 的类,它们都有变量 FIELD 但具有不同的值;所以我发现的方法是重构变量 FIELD 并在超类中声明它。还有其他解决方案吗?
  • 每个子类都不会拥有自己的静态字段副本。它由整个 jvm 中运行的所有东西共享(假设没有花哨的类加载器)。静态字段/方法不是多态性的一部分。
  • 谢谢@LanceJava。这是我没有注意的一个重点。那么对于这个问题,我应该为A的所有子类声明变量FIELD吗?

标签: java variables static subclass final


【解决方案1】:

无论 final 关键字如何,单个静态变量都不适用于多个子类,因为每当实例化新子类时都会重新分配单个静态变量。此外,实例化的顺序仍然会进一步混淆最后静态更新的值。

jshell> public abstract class A { public static String STATIC_SUPER_VAR = "A"; }
|  created class A


jshell> public class B extends A { public B() { STATIC_SUPER_VAR = "B"; } }
|  created class B

jshell> B.STATIC_SUPER_VAR
$3 ==> "A"    

jshell> A.STATIC_SUPER_VAR
$4 ==> "A"

jshell> new B()
$3 ==> B@685cb137

jshell> B.STATIC_SUPER_VAR
$4 ==> "B"

jshell> A.STATIC_SUPER_VAR
$5 ==> "B"

jshell> public class C extends A { public C() { STATIC_SUPER_VAR = "C";} }
|  created class C

jshell> new C()
$7 ==> C@5f2108b5

jshell> A.STATIC_SUPER_VAR
$8 ==> "C"

jshell> B.STATIC_SUPER_VAR
$9 ==> "C"

jshell> C.STATIC_SUPER_VAR
$10 ==> "C"

解决方案

我们可以使用静态 Map 代替单个静态变量。

jshell> public abstract class A {
   ...>
   ...>      private static Map<Class<? extends A>, Integer> CLASS_FIELD_HOLDER = new HashMap<>();
   ...>
   ...>      public A (int classSpecificInteger) {
   ...>
   ...>            CLASS_FIELD_HOLDER.put(getClass(), classSpecificInteger);
   ...>      }
   ...>
   ...>
   ...>      public static int getClassSpecificInteger(Class<? extends A> clazz) {
   ...>
   ...>                  return CLASS_FIELD_HOLDER.get(clazz);
   ...>      }
   ...> }
|  created class A

jshell> public class B extends A {
   ...>
   ...>  public B (int classSpecificInteger) {
   ...>
   ...>         super(classSpecificInteger);
   ...>  }
   ...> }
|  created class B

jshell> public class C extends A {
   ...>
   ...>  public C (int classSpecificInteger) {
   ...>
   ...>         super(classSpecificInteger);
   ...>  }
   ...> }
|  created class C

确保子类被初始化,我的意思是静态地图在访问之前被更新, 其他NPE

jshell> B.getClassSpecificInteger(B.class)
|  Exception java.lang.NullPointerException
|        at A.getClassSpecificInteger (#5:13)
|        at (#7:1)

现在初始化:

jshell> new B(10);
$8 ==> B@610694f1

jshell> new C(20)
$9 ==> C@50b494a6

现在静态访问它,子类中没有 FIELD:

jshell> B.getClassSpecificInteger(B.class)
$10 ==> 10

jshell> A.getClassSpecificInteger(B.class)
$11 ==> 10

jshell> B.getClassSpecificInteger(C.class)
$12 ==> 20

【讨论】:

    【解决方案2】:

    当你创建一个新的B对象时,首先会自动创建一个新的A对象,并在其默认构造函数中初始化其最终变量;所以你不能再修改它们的值了。

    【讨论】:

      【解决方案3】:

      如果要在派生类中更改它们,则不能在最终类中使用静态最终变量。

      1) 删除 FIELD 属性的 final 关键字

      2) 修改代码如下

      import java.io.*;
      import java.util.*;
      
      class A {
          public static int FIELD = 4;
      }
      class B extends A {
          public B(){
              this.FIELD  = 5;
          }
      }
      public class Test {
      
          public static void main(String args[]) throws Exception {
          B b = new B();
              System.out.println("B value:"+b.FIELD);
          }
      
      }
      

      输出是:

      B value:5
      

      【讨论】:

        【解决方案4】:

        关键是超类的子类没有在超类中声明的静态字段的副本,并且超类在它们之间共享它;所以没有办法在不同的子类中拥有一个具有不同值的静态变量。因此,我将在所有子类中声明变量 FIELD。
        我从 cmets 中的 @LenceJava 获得了所有这些。 谢谢@LanceJava。

        【讨论】:

          【解决方案5】:

          这应该可以。

          public class A {
          public final int FIELD;
          A(int a){
          this.FIELD=a;
          }
          }
          public class B extends A {
          B(int a){
          super(a);
          }
          }
          

          【讨论】:

          • 通过这个实现 FIELD 只有在创建 B 的实例并且静态约束不会发挥作用时才会有它的值。我的意思是当我们没有任何 B 实例时,FIELD 的值将不可用。我说得对吗?
          • 现在该字段不再是静态的,这意味着 FIELD 的值与 A ....的实例一样多。
          • 你不能初始化一个也是 static 的 final 变量。
          • 为了初始化一个 final 变量,我们使用了通过类构造函数初始化它的概念。
          【解决方案6】:

          final 变量在声明时初始化,以后不能在子类中重新初始化或修改。 final 在 java 中表示常量。尝试修改它们的值会导致编译错误。

          【讨论】:

            【解决方案7】:

            因为它是staticfinal,所以在初始化时和/或在静态块中,您可以为该变量赋值。只是没有其他地方。

            【讨论】:

              【解决方案8】:

              不,这是不可能的(至少在没有一些非常肮脏的技巧的情况下是不可能的)

              A 被初始化时,该字段必须被初始化。 AB 被初始化之前被初始化。 B 中的任何内容仅在 B 初始化时或之后执行。

              如果有可能,当A.FIELD 被使用时,在 B 被加载之前会发生什么?

              您可以做的是将字段设为私有而不是静态的,并提供一个只允许对其进行一次调用并从B 调用它的设置器。

              在大多数情况下,对于真正的问题,有一个更简洁的解决方案,但由于你没有提到你试图解决的真正问题,我们在这方面无能为力。

              【讨论】:

                猜你喜欢
                • 2016-12-30
                • 2020-05-03
                • 1970-01-01
                • 2020-12-11
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-06-28
                相关资源
                最近更新 更多