私有最终静态属性与私有最终属性


305

在Java中,两者之间有什么区别?

private final static int NUMBER = 10;

private final int NUMBER = 10;

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

有什么更好的?又为什么呢


62
private final static->仅创建一次此变量。private final->为每个对象创建此变量。首先,它可以节省内存。
user1923551 2015年

4
final static表示此变量是一个常量,并且仅与类本身关联,即“每个类一个常量”,而final“每个实例一个常量”。结果,您不能将final static变量放入类的构造函数中,因为该构造函数包含新的instance。(您可以自己尝试,但会出现错误)
LittleLittleQ

1
通过“不能在类的构造函数中放置最终的静态变量”,我的意思是不能final static在构造函数中初始化变量,唯一的方法是使用静态初始化程序:)
LittleLittleQ 2016年

2
@ user1923551对于在大型应用程序,内存稀缺的应用程序或使用单例时仅需要有限的时间的东西,效果相反。每个类中都有静态内容将为不需要的内容保留(巨大)内存块。声明静态最终对象或集合也可能是内存泄漏。
HopefulHelpful

Answers:


309

通常,static是指“与类型本身相关联,而不是与类型实例相关联”。

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

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

打印出10:,y.instanceVariable并且x.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


10
在Java 5中提供枚举之前,静态final是声明常量的常用方法。
Vineet Reynolds

22
@Vineet:静态final仍然是声明基本常量的方法,除非您有枚举的常量=)
Chii

@Matthew:可能。不是常量,而是逻辑上与实例相关的值。并不是说我很喜欢单身人士。
乔恩·斯基特

1
一个根本性的问题。是否值得用private finalprivate static final挤出/回收从类很少的内存?假设对于calculator内存有限但CPU资源充足的设备。
Win Myo Htet

1
@WinMyoHtet:如果使用静态字段,则总共只有一个。如果使用实例字段,则每个实例都有一个。除非您没有任何实例,否则使用静态字段会更好,在这种情况下,它毫无用处。
乔恩·斯基特

38

对于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;
}

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

90
这将无法编译!必须为最终变量分配一个值,或在其构造函数中分配一个值。如果给出了2个构造函数,每个构造函数都将“ a”分配给不同的值,则此答案将是正确的。
MattC

14
确认,将不会编译。如上所述,最终实例变量必须在构造函数完成之前实例化,而最终类变量必须在创建类之前实例化(可以使用静态块)。为什么会有这么多投票?
鲁迪·克肖

正如MattC指出的那样,创建该对象后您无法分配最终变量-实际上,您甚至不能在不为其最终变量提供值的情况下创建对象……
jamesdeath123

万一有人迷路了,请遵循MattC的回答。
法兹(Faz)2015年

我想这就是OP所要问的,我忘记了如果在声明时未为finals赋值,则可以在实例化时为其赋值。
Salsero69 2015年

34

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

private static final int NUMBER = 10;

为什么?这样可以减少每个实例的内存占用量。这对于缓存命中也可能是有利的。这很有意义:static应该用于在某种类型(aka class)的所有实例(aka对象)之间共享的事物。


在运行时还会创建一个静态变量。因此,可以在创建对象之前使用所述变量或方法。
鲍比

13
根据Java编码约定,静态最终变量的名称应全部为大写。
starblue

@Martijn Courteaux,在应用程序的生命周期内一次使用一个类的情况如何?private final int当实例将进行GC处理时,private static final int它将从内存中删除,而在该应用的整个生命周期中都将保留在内存中。您在上述情况下有何建议?
曼·曼

@MANN:这是高度理论性的。实际上,没有任何有用的用例场景。如果您在一个类中有50000个int var,这可能是有益的。即使在这种情况下,这也会节省200kb的内存。由于我们在谈论Java,所以这似乎完全无关紧要。如果是内存关键型设备,那么不错的C或C ++编译器将始终内联那些整数值,从而无需完全释放内存。
马丁·考特

17

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


每当实例变量的所有引用/对象都死掉时,实例变量就会被发送,对吗?
Ruchir Baronia

实例是gc'd,但是静态与类而不是实例相关联。只要该类保留在内存中,您就可以引用其公共静态实例和方法。那些进入了perm gen(或与之等效的JDK 8),并且不进行gc处理。
duffymo

错误。如果您在内存中引用了实例,则可以访问其可用的方法和实例。其余的是错误的。您无法理解类及其实例之间的区别。
duffymo

13

阅读答案,我发现没有真正的考验真正达到目的。这是我的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变量赋予相同的地址。


2
自动装箱'int'-> Integer在这里引起混乱。您会看到一些(小的)int值的自动装箱会导致相同的Integer对象。
dkneller

@StackHola @dkneller的确,自动装箱是一个非常重要的细节。签名是ObjectUtils.identityToString(Object)。(此外,Java仍然无法通过引用传递)。真正有用的测试是public final int FOO = 10使用Java反射以强制方式分配两个对象并更改变量的值。然后检查另一个对象是否也更改了其值。
马丁·考特

11

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

考虑以下示例:

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

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

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

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

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

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

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

我想不出在不同的实例上具有不同的常量值真正有用的任何情况,但是我希望这有助于指出静态和非静态最终值之间存在明显的区别。


2

正如乔恩所说,静态变量(也称为类变量)是存在于类实例中的变量。

我在这里找到了一个例子:

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

2

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

例如:

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学生,希腊


这不是答案:(
Sanjaya Pandey

2

对于Jon的回答,如果您使用static final,它将表现为一种“定义”。一旦编译了使用它的类,它将被烧入已编译的.class文件中。在这里检查我的话题。

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

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

public class JustFinalAttr {
  public final int Number;

  public JustFinalAttr(int a){
    Number=a;
  }
}

...System.out.println(new JustFinalAttr(4).Number);

我在这里有一个链接,以进行详细的比较。对不起,我想这已经被缓和了。
BlondCode 2015年

链接回来了。一位编辑认为它已经死了。它现在似乎正在直播。
Erick G. Hagstrom

2

这是我的两分钱:

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
        }
    }
}

关键是变量和函数可以返回不同的值,因此最终变量可以分配不同的值。


您能否详细说明哪个更好,为什么呢?
丹尼尔(Daniel)

2

只是一个简单的示例,了解静态,静态最终变量,最终变量的用法。代码注释具有正确的解释。

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);

    }

}

1

很少,而且是静态的

两者都是常数,因此差异不大。对于大多数类数据对象,静态意味着与类本身相关联,无论使用new创建多少个对象,都只有一个副本。

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


1

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


1

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

0

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


0

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

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


1
访问静态变量不需要使用静态方法-我认为您正在考虑“从静态方法访问实例变量”(不允许)。
ataulm

0

可以说,如果该类永远不会有一个以上实例,那么哪个实例会占用更多内存:

私有静态最终int ID = 250;或私有最终int ID = 250;

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


1
请不要重复,其他答案已经涵盖。
用户未知

0

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

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”,我们应在该类中分配一个变量(静态变量属于该类)。



0

最终:分配了最终变量后,它始终包含相同的值。无论变量是静态的还是非静态的:对于在内存中一次初始化的所有实例,它将只是一个变量


-1

这可能有帮助

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.");
    }
}

1
确定对这个问题的答案?
mikus

-2

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.