ChatGPT解决这个技术问题 Extra ChatGPT

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

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

J
Jon Skeet

通常,static 表示“与 type 本身相关联,而不是与该类型的 instance 相关联。”

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

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 来挤出/回收课堂上的少量内存是否值得?假设内存有限但 CPU 资源充足的 calculator 设备。
@WinMyoHtet:如果您使用静态字段,则总共只有一个。如果您使用实例字段,则每个实例都有一个。除非您没有任何实例,否则使用静态字段会更好,在这种情况下它无论如何都没用。
M
Martijn Courteaux

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++ 编译器将始终内联这些整数值,从而无需完全释放内存。
l
lucas

对于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 的回答。
请删除此答案
d
duffymo

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


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

阅读答案,我发现没有真正的测试真正达到重点。这是我的 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' -> Integer 的自动装箱在这里引起了混乱。您会看到一些(小)int 值的自动装箱会导致相同的 Integer 对象。
@StackHola @dkneller 实际上,自动装箱是这里发生的一个非常重要的细节。签名是 ObjectUtils.identityToString(Object)。 (此外,Java 无论如何都没有通过引用)。一个真正有用的测试是分配两个对象并以强制方式使用 Java 反射更改 public final int FOO = 10 变量的值。然后检查另一个对象是否也改变了它的值。
M
Michiel

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

考虑以下示例:

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 实例现在将打印三个不同的随机值,因为每个实例都有自己随机生成的常量值。

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


D
DigitalRoss

很少,而且是静态的

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

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


N
Novitzky

正如 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

V
Volker Stolz

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

例如:

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


E
Erick G. Hagstrom

除了乔恩的回答,如果你使用静态最终,它将表现为一种“定义”。一旦你编译了使用它的类,它就会在编译好的 .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);

我有一个链接here进行详细比较。我很抱歉,我猜这已经被缓和了。
链接回来了。一位编辑认为它已经死了。现在好像直播了。
A
Adrian

这是我的两分钱:

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

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


您能否详细说明哪个更好,为什么?
V
Vaibs

只是另一个简单的例子来了解静态、静态最终、最终变量的用法。代码注释有正确的解释。

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

    }

}

N
NarekOnLine

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


D
DanielBarbarian

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

O
Omar Al-Ithawi

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


S
Sanjay

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

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


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

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

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

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


请不要只是重复,其他答案已经涵盖了。
T
Tsasaa

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

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


W
Warren Jennings

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


H
Hashem Alhariry

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


Y
YoungHobbit

这可能会有所帮助

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

确定它是这个问题的答案吗?
U
Uzma Nasir

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