【问题标题】:private final static attribute vs private final attribute私有最终静态属性与私有最终属性
【发布时间】:2010-11-27 19:22:58
【问题描述】:

在Java中,有什么区别:

private final static int NUMBER = 10;

private final int NUMBER = 10;

都是privatefinal,区别在于static属性。

什么更好?为什么?

【问题讨论】:

  • private final static -> 只创建一次这个变量。 private final -> 为每个对象创建这个变量。第一个节省内存,去吧。
  • final static 表示这个变量是一个常量并且只与类本身相关联,即“每个类一个常量变量”,而final 表示“每个实例一个常量变量”。结果,您不能将final static 变量放在类的构造函数中,因为构造函数涉及new an instance。 (你可以自己试试,会报错)
  • “不能在类的构造函数中放入最终的静态变量”我的意思是不能在构造函数中初始化final static 变量,唯一的方法是使用静态初始化器:)
  • @user1923551 对于大型应用程序、内存稀缺应用程序或使用单例时仅在有限时间内需要的东西,效果是相反的。在每个类中都有静态的东西将为不需要的东西保留一个(巨大的)内存块。声明静态最终对象或集合也可能是内存泄漏。

标签: java static attributes private final


【解决方案1】:

一般来说,static 的意思是“与类型本身相关联,而不是类型的实例。”

这意味着您可以引用静态变量而无需创建该类型的实例,并且引用该变量的任何代码都引用完全相同的数据。将此与实例变量进行比较:在这种情况下,每个类实例都有一个独立版本的变量。比如:

Test x = new Test();
Test y = new Test();
x.instanceVariable = 10;
y.instanceVariable = 20;
System.out.println(x.instanceVariable);

打印出 10:y.instanceVariablex.instanceVariable 是分开的,因为 xy 指的是不同的对象。

可以通过引用来引用静态成员,尽管这样做是个坏主意。如果我们这样做了:

Test x = new Test();
Test y = new Test();
x.staticVariable = 10;
y.staticVariable = 20;
System.out.println(x.staticVariable);

然后会打印出 20 - 只有一个变量,而不是每个实例一个。写成这样会更清楚:

Test x = new Test();
Test y = new Test();
Test.staticVariable = 10;
Test.staticVariable = 20;
System.out.println(Test.staticVariable);

这使得行为更加明显。现代 IDE 通常会建议将第二个列表更改为第三个。

没有理由有一个像下面这样初始化值的内联声明,因为每个实例都有自己的NUMBER,但总是具有相同的值(不可变并使用文字初始化)。这与所有实例只有一个 final static 变量相同。

private final int NUMBER = 10;

因此,如果它不能改变,那么每个实例只有一个副本是没有意义的。

但是,如果在这样的构造函数中初始化是有意义的:

// No initialization when is declared
private final int number;

public MyClass(int n) {
   // The variable can be assigned in the constructor, but then
   // not modified later.
   number = n;
}

现在,对于MyClass 的每个实例,我们可以有一个不同但不可变的number 值。

【讨论】:

  • 在 Java 5 中提供枚举之前,静态 final 是声明常量的常用方式。
  • @Vineet:静态决赛仍然是声明原始常量的方式,除非你有它们的枚举数量 =)
  • @Matthew:有可能。不是一个常量,而是一些逻辑上与实例相关的值。反正我不是很喜欢单身人士。
  • 一个激进的问题。是否值得使用private final 而不是private static final 从课堂上挤出/回收那一点点记忆?假设calculator 设备内存有限,但 CPU 资源充足。
  • @WinMyoHtet:如果您使用静态字段,则总共只有 一个。如果您使用实例字段,则每个实例都有一个。除非您没有任何实例,否则使用静态字段会更好,在这种情况下它无论如何都没用。
【解决方案2】:

static 变量在应用程序的整个生命周期内都保留在内存中,并在类加载期间被初始化。每次构造new 对象时,都会初始化一个非static 变量。通常最好使用:

private static final int NUMBER = 10;

为什么?这减少了每个实例的内存占用。它也可能有利于缓存命中。这很有意义:static 应该用于在特定类型(又名class)的所有实例(又名对象)之间共享的东西。

【讨论】:

  • 在运行时也会创建一个静态变量。因此,您可以在创建对象之前使用所述变量或方法。
  • 根据 Java 编码约定,静态最终变量的名称应全部大写。
  • @Martijn Courteaux,在应用程序的生命周期中,一个类将被使用一次的情况怎么样! private final int 将在实例被 GC 时从内存中删除,而 private static final int 将在该应用程序的整个生命周期中保留在内存中。在上述情况下,您有什么建议?
  • @MANN:这是高度理论化的。实际上没有任何有用的用例场景。如果您在一个类中有 50000 个 int 变量,这可能是有益的。即使在这种情况下,这也会节省 200kb 的内存。由于我们在谈论 Java,这似乎完全无关紧要。在内存关键设备的情况下,体面的 C 或 C++ 编译器将始终内联这些整数值,从而无需完全释放内存。
【解决方案3】:

对于final,它可以在初始化时在运行时分配不同的值。 例如

class Test{
  public final int a;
}

Test t1  = new Test();
t1.a = 10;
Test t2  = new Test();
t2.a = 20; //fixed

因此每个实例都有不同的字段a值。

对于static final,所有实例共享相同的值,并且在第一次初始化后不能更改。

class TestStatic{
      public static final int a = 0;
}

TestStatic t1  = new TestStatic();
t1.a = 10; // ERROR, CAN'T BE ALTERED AFTER THE FIRST 
TestStatic t2  = new TestStatic();
t1.a = 20;   // ERROR, CAN'T BE ALTERED AFTER THE FIRST INITIALIZATION.

【讨论】:

  • 这不会编译! final 变量必须要么被赋值,要么在其构造函数中赋值。如果给出了 2 个构造函数,每个构造函数都将“a”分配给不同的值,那么这个答案将是正确的。
  • 确认,这不会编译。如上所述,最终实例变量必须在构造函数完成之前实例化,并且最终类变量必须在创建类之前实例化(您可以使用静态块)。为什么这有这么多赞成?
  • 正如 MattC 指出的那样,您不能在创建该对象后分配给最终变量 - 事实上,您甚至无法在不为其最终变量赋值的情况下创建一个对象...
  • 以防万一有人偶然发现,请按照 MattC 的回答。
  • 请删除这个答案
【解决方案4】:

static 表示“与类关联”;没有它,变量将与类的每个实例相关联。如果它是静态的,则意味着您的内存中只有一个;如果没有,您创建的每个实例都会有一个。 static 表示只要加载了类,变量就会保留在内存中;没有它,变量可以在其实例为时被 gc'd。

【讨论】:

  • 实例变量在所有引用/对象死亡时都会被 gc'd,对吧?
  • 实例是 gc'd,但静态与类相关联,而不是实例。只要该类保留在内存中,您就可以引用它的公共静态实例和方法。那些进入 perm gen(或任何它的 JDK 8 等价物)并且不是 gc'd。
  • 错了。如果您引用了内存中的实例,则可以访问其可用的方法和实例。其余的都是错误的。您无法理解类与其实例之间的区别。
【解决方案5】:

阅读答案后,我发现没有真正意义上的真正测试。这是我的 2 美分:

public class ConstTest
{

    private final int         value             = 10;
    private static final int  valueStatic       = 20;
    private final File        valueObject       = new File("");
    private static final File valueObjectStatic = new File("");

    public void printAddresses() {


        System.out.println("final int address " +
                ObjectUtils.identityToString(value));
        System.out.println("final static int address " +
                ObjectUtils.identityToString(valueStatic));
        System.out.println("final file address " + 
                ObjectUtils.identityToString(valueObject));
        System.out.println("final static file address " + 
                ObjectUtils.identityToString(valueObjectStatic));
    }


    public static void main(final String args[]) {


        final ConstTest firstObj = new ConstTest();
        final ConstTest sndObj = new ConstTest();

        firstObj.printAdresses();
        sndObj.printAdresses();
    }

}

第一个对象的结果:

final int address java.lang.Integer@6d9efb05
final static int address java.lang.Integer@60723d7c
final file address java.io.File@6c22c95b
final static file address java.io.File@5fd1acd3

第二个对象的结果:

final int address java.lang.Integer@6d9efb05
final static int address java.lang.Integer@60723d7c
final file address java.io.File@3ea981ca
final static file address java.io.File@5fd1acd3

结论:

我认为 java 在原始类型和其他类型之间有所不同。 Java 中的原始类型总是“缓存”的,字符串字面量(不是新的 String 对象)也是如此,因此静态成员和非静态成员之间没有区别。

但是,如果非静态成员不是原始类型的实例,则它们存在内存重复。

将 valueStatic 的值更改为 10 甚至会更进一步,因为 Java 将为两个 int 变量提供相同的地址。

【讨论】:

  • 'int' 的自动装箱 -> 整数在这里引起了混乱。您会看到一些(小)int 值的自动装箱会导致相同的 Integer 对象。
  • @StackHola @dkneller 实际上,自动装箱是这里发生的一个非常重要的细节。签名是ObjectUtils.identityToString(Object)。 (此外,Java 无论如何都没有通过引用)。一个真正有用的测试是分配两个对象并以强制方式使用 Java 反射更改 public final int FOO = 10 变量的值。然后检查其他对象是否也改变了它的值。
【解决方案6】:

虽然其他答案似乎很清楚地表明通常没有理由使用非静态常量,但我找不到任何人指出可以在其常量变量上使用具有不同值的各种实例。

考虑以下示例:

public class TestClass {
    private final static double NUMBER = Math.random();

    public TestClass () {
        System.out.println(NUMBER);
    }
}

创建三个 TestClass 实例会打印 3 次相同的随机值,因为只会生成一个值并将其存储到静态常量中。

但是,当尝试以下示例时:

public class TestClass {
    private final double NUMBER = Math.random();

    public TestClass () {
        System.out.println(NUMBER);
    }
}

创建三个 TestClass 实例现在将打印三个不同的随机值,因为每个实例都有自己随机生成的常量值。

我想不出任何情况下在不同的实例上使用不同的常量值会非常有用,但我希望这有助于指出静态和非静态决赛之间存在明显的区别。

【讨论】:

    【解决方案7】:

    很少,而且是静态的

    没有太大区别,因为它们都是常数。对于大多数类数据对象,静态意味着与类本身相关联的东西,无论使用 new 创建多少对象,都只有一个副本。

    由于它是一个常量,它实际上可能不会存储在类或实例中,但编译器仍然不会让您从静态方法访问实例对象,即使它知道它们会做什么是。如果不将反射 API 设为静态,则反射 API 的存在可能还需要一些无意义的工作。

    【讨论】:

      【解决方案8】:

      正如 Jon 已经说过的,静态变量,也称为类变量,是跨类实例存在的变量。

      我找到了这个here的例子:

      public class StaticVariable
      {
        static int noOfInstances;
        StaticVariable()
        {
          noOfInstances++;
        }
        public static void main(String[] args)
        {
          StaticVariable sv1 = new StaticVariable();
          System.out.println("No. of instances for sv1 : " + sv1.noOfInstances);
      
          StaticVariable sv2 = new StaticVariable();
          System.out.println("No. of instances for sv1 : "  + sv1.noOfInstances);
          System.out.println("No. of instances for st2 : "  + sv2.noOfInstances);
      
          StaticVariable sv3 = new StaticVariable();
          System.out.println("No. of instances for sv1 : "  + sv1.noOfInstances);
          System.out.println("No. of instances for sv2 : "  + sv2.noOfInstances);
          System.out.println("No. of instances for sv3 : "  + sv3.noOfInstances);
        }
      }
      

      程序的输出如下:

      正如我们在这个例子中看到的,每个对象都有自己的类变量副本。

      C:\java>java StaticVariable
      No. of instances for sv1 : 1
      No. of instances for sv1 : 2
      No. of instances for st2 : 2
      No. of instances for sv1 : 3
      No. of instances for sv2 : 3
      No. of instances for sv3 : 3
      

      【讨论】:

        【解决方案9】:

        根据我所做的测试,静态最终变量与最终(非静态)变量不同!最终(非静态)变量可能因对象而异!!!但这只是在构造函数中进行初始化的情况下! (如果它不是从构造函数初始化的,那么它只是浪费内存,因为它为每个创建的无法更改的对象创建最终变量。)

        例如:

        class A
        {
            final int f;
            static final int sf = 5;
        
            A(int num)
            {
                this.f = num;
            }
        
            void show()
            {
                System.out.printf("About Object: %s\n Final: %d\n Static Final: %d\n\n", this.toString(), this.f, sf);
            }
        
            public static void main(String[] args)
            {
                A ob1 = new A(14);
                ob1.show();
        
                A ob2 = new A(21);
                ob2.show();
        
            }
        }
        

        屏幕上显示的是:

        关于对象:A@addbf1 决赛:14 静态决赛:5

        关于对象:A@530daa 决赛:21 静态决赛:5

        匿名的一年级 IT 学生,希腊

        【讨论】:

          【解决方案10】:

          此外,如果您使用静态 final,Jon 的回答将表现为一种“定义”。一旦你编译了使用它的类,它就会在编译好的.class文件中被烧掉。 检查我的帖子here

          对于您的主要目标:如果您在类的不同实例中不以不同方式使用 NUMBER,我建议您使用 final 和 static。 (你只需要记住不要复制编译的类文件,而不考虑我的案例研究描述的可能的麻烦。大多数情况下不会发生这种情况,不用担心 :))

          要向您展示如何在实例中使用不同的值,请查看以下代码:

          public class JustFinalAttr {
            public final int Number;
          
            public JustFinalAttr(int a){
              Number=a;
            }
          }
          
          ...System.out.println(new JustFinalAttr(4).Number);
          

          【讨论】:

          • 我有一个链接在这里进行详细比较。对不起,伙计们,我猜这已经被缓和了。
          • 链接回来了。一位编辑认为它已经死了。它现在似乎正在直播。
          【解决方案11】:

          这是我的两分钱:

          final           String CENT_1 = new Random().nextInt(2) == 0 ? "HEADS" : "TAILS";
          final   static  String CENT_2 = new Random().nextInt(2) == 0 ? "HEADS" : "TAILS";
          

          例子:

          package test;
          
          public class Test {
          
              final long OBJECT_ID = new Random().nextLong();
              final static long CLASSS_ID = new Random().nextLong();
          
              public static void main(String[] args) {
                  Test[] test = new Test[5];
                  for (int i = 0; i < test.length; i++){
                      test[i] = new Test();
                      System.out.println("Class id: "+test[i].CLASSS_ID);//<- Always the same value
                      System.out.println("Object id: "+test[i].OBJECT_ID);//<- Always different
                  }
              }
          }
          

          关键是变量和函数可以返回不同的值。因此最终变量可以被赋予不同的值。

          【讨论】:

          • 能否详细说明哪个更好,为什么?
          【解决方案12】:

          又一个简单的例子来了解static、static final、final变量的用法。代码 cmets 有正确的解释。

          public class City {
          
              // base price that is always same for all objects[For all cities].
              private static double iphone_base_price = 10000;
          
              // this is total price = iphone_base_price+iphone_diff;
              private double iphone_citi_price;
          
              // extra price added to iphone_base_price. It is constant per city. Every
              // city has its own difference defined,
              private final double iphone_diff;
          
              private String cityName = "";
          
              // static final will be accessible everywhere within the class but cant be
              // changed once initialized.
              private static final String countryName = "India";
          
              public City(String cityName, double iphone_diff) {
                  super();
                  this.iphone_diff = iphone_diff;
                  iphone_citi_price = iphone_base_price + iphone_diff;
                  this.cityName = cityName;
          
              }
          
              /**
               * get phone price
               * 
               * @return
               */
              private double getPrice() {
          
                  return iphone_citi_price;
              }
          
              /**
               * Get city name
               * 
               * @return
               */
              private String getCityName() {
          
                  return cityName;
              }
          
              public static void main(String[] args) {
          
                  // 300 is the
                  City newyork = new City("Newyork", 300);
                  System.out.println(newyork.getPrice() + "  " + newyork.getCityName());
          
                  City california = new City("California", 800);
                  System.out.println(california.getPrice() + "  " + california.getCityName());
          
                  // We cant write below statement as a final variable can not be
                  // reassigned
                  // california.iphone_diff=1000; //************************
          
                  // base price is defined for a class and not per instances.
                  // For any number of object creation, static variable's value would be the same
                  // for all instances until and unless changed.
                  // Also it is accessible anywhere inside a class.
                  iphone_base_price = 9000;
          
                  City delhi = new City("delhi", 400);
                  System.out.println(delhi.getPrice() + "  " + delhi.getCityName());
          
                  City moscow = new City("delhi", 500);
                  System.out.println(moscow.getPrice() + "  " + moscow.getCityName());
          
                  // Here countryName is accessible as it is static but we can not change it as it is final as well. 
                  //Something are meant to be accessible with no permission to modify it. 
                  //Try un-commenting below statements
                  System.out.println(countryName);
          
                  // countryName="INDIA";
                  // System.out.println(countryName);
          
              }
          
          }
          

          【讨论】:

            【解决方案13】:

            由于类中的变量被声明为 final 并在同一命令中初始化, 绝对没有理由不将其声明为静态,因为无论实例如何,它都将具有相同的值。因此,所有实例都可以为一个值共享相同的内存地址,从而无需为每个实例创建一个新变量并通过共享 1 个公共地址来节省内存,从而节省处理时间。

            【讨论】:

              【解决方案14】:

              private static final 将被视为常量,并且只能在此类中访问该常量。由于包含关键字 static,因此该类的所有对象的值都是常量。

              私有最终变量值将像每个对象的常量。

              您可以参考 java.lang.String 或查找下面的示例。

              public final class Foo
              {
              
                  private final int i;
                  private static final int j=20;
              
                  public Foo(int val){
                      this.i=val;
                  }
              
                  public static void main(String[] args) {
                      Foo foo1= new Foo(10);
              
                      Foo foo2= new Foo(40);
              
                      System.out.println(foo1.i);
                      System.out.println(foo2.i);
                      System.out.println(check.j);
                  }
              }
              

              //输出:

              10
              40
              20
              

              【讨论】:

                【解决方案15】:

                静态成员是所有类实例和类本身的同一个成员。
                非静态是针对每个实例(对象)的,因此在您的具体情况中,如果您放置静态,则会浪费内存。

                【讨论】:

                  【解决方案16】:

                  如果您将此变量标记为静态,那么您将需要静态方法再次访问这些值,如果您已经考虑仅在静态方法中使用这些变量,这将很有用。如果是这样,那么这将是最好的。

                  但是,您现在可以将变量设为公开,因为没有人可以像“System.out”那样修改它,这又取决于您的意图和您想要实现的目标。

                  【讨论】:

                  • 访问静态变量不需要静态方法 - 我认为您正在考虑“从静态方法访问实例变量”(不允许)。
                  【解决方案17】:

                  假设类永远不会有多个实例,那么哪个实例占用更多内存:

                  私有静态最终 int ID = 250; 要么 私有最终 int ID = 250;

                  我知道静态将引用在内存中只有一个副本的类类型,而非静态将在每个实例变量的新内存位置中。但是在内部,如果我们只比较同一类的 1 个实例(即不会创建超过 1 个实例),那么 1 个静态最终变量使用的空间是否有任何开销?

                  【讨论】:

                  • 请不要重复,其他答案已经涵盖了。
                  【解决方案18】:

                  静态变量属于类(这意味着所有对象共享该变量)。非静态变量属于每个对象。

                  public class ExperimentFinal {
                  
                  private final int a;
                  private static final int b = 999; 
                  
                  public ExperimentFinal(int a) {
                      super();
                      this.a = a;
                  }
                  public int getA() {
                      return a;
                  }
                  public int getB() {
                      return b;
                  }
                  public void print(int a, int b) {
                      System.out.println("final int: " + a + " \nstatic final int: " + b);
                  }
                  public static void main(String[] args) {
                      ExperimentFinal test = new ExperimentFinal(9);
                      test.print(test.getA(), test.getB());
                  } }
                  

                  正如您在上面的示例中看到的,对于“final int”,我们可以为类的每个实例(对象)分配我们的变量,但是对于“static final int”,我们应该在类中分配一个变量(静态变量属于到班级)。

                  【讨论】:

                    【解决方案19】:

                    如果您使用静态,则变量的值在您的所有实例中都是相同的,如果在一个实例中更改,其他实例也会更改。

                    【讨论】:

                      【解决方案20】:

                      Final:一旦分配了最终变量,它总是包含相同的值。 无论变量是否为静态 静态:对于所有在内存中初始化一次的实例,它只会是一个变量

                      【讨论】:

                        【解决方案21】:

                        这可能会有所帮助

                        public class LengthDemo {
                        public static void main(String[] args) {
                            Rectangle box = new Rectangle();
                            System.out.println("Sending the value 10.0 "
                                    + "to the setLength method.");
                            box.setLength(10.0);
                            System.out.println("Done.");
                            }
                        }
                        

                        【讨论】:

                        • 确定它是这个问题的答案吗?
                        【解决方案22】:

                        “Static”关键字使得类的变量属性而不是类的单个实例。该变量的一个副本将在该类的所有实例之间共享。静态变量状态的任何变化都将反映在所有实例中。将 final 添加到 static 中,我们得到一个变量,该变量在类加载时一劳永逸地初始化,以后不能被类的任何实例更改。静态最终变量需要在声明时初始化,否则会出现编译时错误。 就私有实例字段而言,它指的是对象的属性/状态/类的实例。类的每个实例/对象都有自己的实例变量副本。当实例变量被声明为 final 时,这意味着我们不能为这个实例更改它的值。为此,我们需要在声明或构造函数中初始化最终变量。如果在其中任何一个中都没有完成,则会显示编译时错误。初始化后,如果您尝试重新分配值,您将收到编译时错误。使用静态最终变量,其中数据将在类的所有实例之间共享,并且您希望数据是只读的。如果您想表示属于类的每个单独实例但一次的某些数据,请使用实例最终变量存储的不能更改。静态和实例关键字的使用取决于您的设计需求以及该数据在域中代表的内容。如果跨类实例使用数据,则不需要为每个对象单独复制/内存引用。

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2011-03-04
                          • 1970-01-01
                          • 2012-09-29
                          • 2012-04-20
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多