在Java中,有什么区别:
private final static int NUMBER = 10;
和
private final int NUMBER = 10;
都是 private
和 final
,区别在于 static
属性。
什么更好?为什么?
final static
表示该变量是一个常量,并且只与类本身相关联,即“每个类一个常量变量”,而 final
表示“每个实例一个常量变量”。因此,您不能将 final static
变量放在类的构造函数中,因为构造函数涉及 new an instance。 (你可以自己试一下,会报错)
final static
变量,唯一的方法是使用静态初始化程序:)
通常,static
表示“与 type 本身相关联,而不是与该类型的 instance 相关联。”
这意味着您可以引用静态变量而无需创建该类型的实例,并且引用该变量的任何代码都引用完全相同的数据。将此与实例变量进行比较:在这种情况下,类的每个实例都有一个独立版本的变量。例如:
Test x = new Test();
Test y = new Test();
x.instanceVariable = 10;
y.instanceVariable = 20;
System.out.println(x.instanceVariable);
输出 10:y.instanceVariable
和 x.instanceVariable
是分开的,因为 x
和 y
引用不同的对象。
您可以通过引用来引用静态成员,尽管这样做是个坏主意。如果我们这样做了:
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
值。
static
变量在应用程序的整个生命周期内都保留在内存中,并在类加载期间被初始化。每次构造 new
对象时都会初始化一个非 static
变量。通常最好使用:
private static final int NUMBER = 10;
为什么?这减少了每个实例的内存占用。它也可能有利于缓存命中。这很有意义:static
应该用于在特定类型(又名 class
)的所有实例(又名对象)之间共享的东西。
private final int
将在实例被 GC 时从内存中删除,而 private static final int
将在该应用程序的整个生命周期内保留在内存中。在上述情况下,您有什么建议?
对于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.
static 表示“与类关联”;没有它,变量将与类的每个实例相关联。如果它是静态的,则意味着您的内存中只有一个;如果没有,您创建的每个实例都会有一个。 static 表示只要加载了类,变量就会保留在内存中;没有它,变量可以在其实例存在时被 gc'd。
阅读答案,我发现没有真正的测试真正达到重点。这是我的 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 变量提供相同的地址。
ObjectUtils.identityToString(Object)
。 (此外,Java 无论如何都没有通过引用)。一个真正有用的测试是分配两个对象并以强制方式使用 Java 反射更改 public final int FOO = 10
变量的值。然后检查另一个对象是否也改变了它的值。
虽然其他答案似乎很清楚地表明通常没有理由使用非静态常量,但我找不到任何人指出可以在其常量变量上有不同值的各种实例。
考虑以下示例:
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 实例现在将打印三个不同的随机值,因为每个实例都有自己随机生成的常量值。
我想不出在不同实例上有不同的常量值真的有用的任何情况,但我希望这有助于指出静态和非静态决赛之间存在明显差异。
很少,而且是静态的
没有太大区别,因为它们都是常数。对于大多数类数据对象,静态意味着与类本身相关联的东西,无论用 new 创建了多少对象,都只有一个副本。
由于它是一个常量,它实际上可能不会存储在类或实例中,但编译器仍然不会让您从静态方法访问实例对象,即使它知道它们会是什么。如果您不将其设为静态,则反射 API 的存在也可能需要一些无意义的工作。
正如 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
根据我所做的测试,静态最终变量与最终(非静态)变量不同!最终(非静态)变量可能因对象而异!!!但这只是在构造函数中进行初始化的情况下! (如果它没有从构造函数初始化,那么它只是浪费内存,因为它为每个创建的无法更改的对象创建最终变量。)
例如:
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 学生,希腊
除了乔恩的回答,如果你使用静态最终,它将表现为一种“定义”。一旦你编译了使用它的类,它就会在编译好的 .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);
这是我的两分钱:
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
}
}
}
关键是变量和函数可以返回不同的值。因此最终变量可以分配不同的值。
只是另一个简单的例子来了解静态、静态最终、最终变量的用法。代码注释有正确的解释。
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);
}
}
由于类中的变量在同一命令中声明为 final 并初始化,因此绝对没有理由不将其声明为 static,因为无论实例如何,它都将具有相同的值。因此,所有实例可以共享一个值的相同内存地址,从而通过消除为每个实例创建新变量的需要节省处理时间,并通过共享 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
静态的是所有类实例和类本身的相同成员。非静态对于每个实例(对象)都是一个,因此在您的确切情况下,如果您不放置静态,则会浪费内存。
如果您将此变量标记为静态,那么您将需要静态方法再次访问这些值,如果您已经考虑仅在静态方法中使用这些变量,这将很有用。如果是这样,那么这将是最好的。
但是,您现在可以将变量设为公开,因为没有人可以像“System.out”那样修改它,这再次取决于您的意图和您想要实现的目标。
假设该类永远不会有一个以上的实例,那么哪一个会占用更多内存:
私有静态最终 int ID = 250;或私有最终 int ID = 250;
我知道静态将引用在内存中只有一个副本的类类型,而非静态将在每个实例变量的新内存位置中。但是在内部,如果我们只比较同一类的 1 个实例(即不会创建超过 1 个实例),那么在 1 个静态最终变量使用的空间方面是否有任何开销?
静态变量属于该类(这意味着所有对象共享该变量)。非静态变量属于每个对象。
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”,我们应该在类中分配一个变量(静态变量属于该类)。
如果您使用静态变量的值将在所有实例中都相同,如果在一个实例中更改,其他实例也会更改。
最终:一旦分配了最终变量,它总是包含相同的值。无论变量是静态的还是非静态的:对于在内存中初始化一次的所有实例,它将只是一个变量
这可能会有所帮助
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.");
}
}
“静态”关键字使类的变量属性而不是类的单个实例。该变量的一个副本将在该类的所有实例之间共享。静态变量状态的任何变化都将反映在所有实例中。将 final 添加到 static 中,我们得到一个变量,该变量在类加载时已经一劳永逸地初始化,以后不能被类的任何实例更改。静态最终变量需要在声明时初始化,否则我们会出现编译时错误。就私有实例字段而言,它指的是对象的属性/状态/类的实例。类的每个实例/对象都有自己的实例变量副本。当实例变量被声明为 final 时,这意味着我们不能为这个实例更改它的值。为此,我们需要在声明或构造函数中初始化最终变量。如果在其中任何一个中都没有完成,则会显示编译时错误。初始化后,如果您尝试重新分配一个值,您将收到编译时错误。使用静态最终变量,其中数据将在类的所有实例之间共享,并且您希望数据是只读的。如果您想表示属于类的每个单独实例但一次的某些数据,请使用实例最终变量存储的不能更改。静态和实例关键字的使用取决于您的设计需求以及该数据在域中代表的内容。如果跨类实例使用数据,则不需要为每个对象提供单独的副本/内存引用。
private final
而不是private static final
来挤出/回收课堂上的少量内存是否值得?假设内存有限但 CPU 资源充足的calculator
设备。