"final static String"和"static String"有什么不同?

9
public static final String Test = "ABC";
public static String Test = "ABC";

请展示Java中"final static String"和"static String"之间的差异。 除了final不能改变之外,还有哪些区别?谢谢。 在更复杂的情况下,例如多线程和继承,是否有所改变?

11
这就是它的意思,没有什么好说的了。结束了。 - Ignacio Vazquez-Abrams
是的,除了增加代码可读性之外,没有其他更多的东西。 - Vamsi Mohan Jayanti
@VamsiMohanJayanti 什么?使用 final 会产生不同的行为。这与可读性有什么关系? - ApproachingDarknessFish
当处于更复杂的情况下,例如多线程和继承时...... - sigh
@ValekHalfHeart。是的,你可以轻松地找出只读引用并专注于其余部分。有一些线程已经讨论过这个问题。https://dev59.com/J2855IYBdhLWcg3wilCD,https://dev59.com/YnRB5IYBdhLWcg3wz6ed,http://programmers.stackexchange.com/questions/108349/why-is-the-final-keyword-used-so-little-in-the-industry - Vamsi Mohan Jayanti
14个回答

9
尽管以下描述来自于Android文档,但可能有助于这个Java标签的问题。
考虑在一个类的顶部声明以下内容:
static int intVal = 42;
static String strVal = "Hello, world!";

编译器会生成一个类初始化方法,称为“”,当类首次使用时执行该方法。该方法将值42存储到intVal中,并从classfile字符串常量表中提取strVal的引用。稍后引用这些值时,将使用字段查找访问它们。
我们可以使用“final”关键字来改进此问题:
static final int intVal = 42;
static final String strVal = "Hello, world!";

因为常量进入dex文件中的静态字段初始化程序,所以该类不再需要方法。引用intVal的代码将直接使用整数值42,而对strVal的访问将使用相对廉价的“字符串常量”指令而不是字段查找。

注意:

此优化仅适用于基本类型和String常量,而不适用于任意引用类型。尽管如此,尽可能声明常量为static final仍是一个好习惯。

希望这能解决您的疑虑。

更多细节请参见UseFinal


8

当涉及到变量时

  • final Static String - 常量,是类变量
  • static String - 类变量,不是常量

3
这里有更多内容。
static final String s1;
static String s2;

第一行无法编译,第二行可以编译。

对于基本类型还有更多内容。

final int x = 1;

void x() {
    int y = x + 1;
}

字节码

ICONST_2
ISTORE 1

这句话的意思是int y = 2,因为x是一个常量(虽然它不是静态的)

如果我们改变

int x = 1;

字节码

ALOAD 0
GETFIELD Test1.x : I
ICONST_1
IADD
ISTORE 1

JVM读取x并计算y


1
s1这行代码无法编译,因为你已经将其声明为常量,但是既没有直接赋值,也没有通过静态初始化块进行赋值。 - Charlie
是的,这旨在展示"final static String"和"static String"之间的区别。 - Evgeniy Dorofeev
在更复杂的情况,如多线程和继承中,是否什么都没有改变? - sigh
final 关键字仅影响 Java 编译器,不会影响运行时。 - Evgeniy Dorofeev

1
根据维基百科的说法:
如果变量是一个引用,这意味着该变量不能重新绑定到另一个对象。但是,如果它最初是可变的,则它所引用的对象仍然是可变的。
然而,由于String最初就是不可变的,因此在这种情况下,这个区别是无关紧要的,它只是一个无法以任何方式修改的变量。

1

你自己说了:最后一个不能被分配。据我所知,final修饰符除此之外没有其他行为。我唯一能想到的是,由于Testpublicstaticfinal的,编译器将自动替换代码中所有出现的Test并用字符串字面量"ABC"替换它,尽管由于字符串将被内部化,它不会对代码产生任何影响。基本上,final只是防止字符串被分配给不同的引用。


在多线程中也一样吗? - sigh
我在多线程方面没有太多经验,但是final就是final(至少在Java中)。我想象不出它会有什么影响;如果我错了,请有经验的人纠正我。 - ApproachingDarknessFish

1

final类不能被子类化。这就像C#中的sealed类。

final方法不能被覆盖。

final字段只能被初始化/赋值一次,就像常量一样,但不一定在编译时就已知。

static成员在类的所有实例之间共享。

因此,static final通常用于永远不再更改的内容,但不一定是在编译时硬编码的。


1

在声明Java变量为static final时,编译后的Java类会产生更快的性能。

查看此内容


在更复杂的情况下,比如多线程和继承,什么都没有改变吗? - sigh
编译时初始化和运行时初始化始终对性能产生影响。 - Sunil Gulabani

0

您无法像下面这样更改final的引用

public static final String Test1 = "ABC";
public static String Test2 = "ABC";
public static void testFinal()
{
    Test2 = Test1; //ok
    Test1 = Test2; //fail
}

我早就知道了...但还是谢谢。 - sigh

0
一个字符串可以同时被声明为静态和最终的。在下面叙述了使用这两个访问修饰符声明字符串的优点。
这些优点包括了final和static的影响。
  1. 声明为final的字符串不能被重新赋值。该字符串作为常量使用。

  2. 声明为static的字符串可以在不需要对象或类名的情况下调用。

  3. 静态变量不维护封装性。将静态变量声明为final后,任何对象都无法更改其值,但可以访问它。

    public class Demo {
    
    static final String str = "Hello";
    
    public static void main(String args[]) {
    // str = "world"; // 报错
    System.out.println(str); // 不需要对象就能调用
    System.out.println(Demo.str);// 使用类名调用
    
    }
    }
    
在main()方法中的第一条语句会出错,因为字符串str被声明为final。
以下语句会引发编译错误,因为字符串str被声明为final。
Demo d1 = new Demo();
d1.str =  "World";

实时例子

静态常量字符串 truth = "太阳从东方升起";


0

这里是:

1)final static String:当你使用final时,它是一个常量,你不能在整个程序中改变它的值。它不是一个变量,你不能改变它的值。在Java中,我们使用final关键字来声明常量。在声明常量时,我们应该遵循所有大写字母和下划线来分隔单词。例如:MAX_INPUT_TIME、CONNECTION_POOL_SIZE、MAX_USER等。

2)static String:你可以为类的成员变量使用static,但不能为局部变量使用。静态变量是静态的,即使没有创建类的实例也可以访问。因此,你可以通过object.<static_variable_name>ClassName.<static_variable_name>来使用它们。它们对于类的所有实例都是可访问的,并且保持相同的值。

例如:

Class A {
  static int counter;  // it will be default to 0
}

现在使用这个类:

System.out.println(A.counter);  // output: 0
A.counter++;
System.out.println(A.counter);  // output: 1
// Declare an instance of class A
A o1 = new A();
o1.counter++;
System.out.println(o1.counter);  // output: 1
// Declare another instance of class A
A o2 = new A();
System.out.println(o1.counter);  // output: 1  
# You still get it one and not 0, if it was non-static you would get it as 0;

例如,在这里,您可以使用静态变量来跟踪对象创建的数量,只需在构造函数本身中放置计数器增量即可。

我还没有编译这段代码,但它应该能够让你对两者的区别和概念有一个相当好的了解。 - deej

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接