java - "Mutable"Java 字符串行为不可预测

标签 java string

我正在用函数改变字符串值(我知道这是非常不安全和危险的):

public static void reverse(String s) {
    try {
        Field val = String.class.getDeclaredField("value");
        val.setAccessible(true);
        char[] value = (char[]) val.get(s);
        char[] inverse = s.toCharArray();
        for (int i = 0; i < s.length(); i++)
            value[i] = inverse[s.length()-i-1];
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}

之后我发现,根据字符串的创建,它的行为极其不可预测。我用它创建了一个小型智力游戏(需要很多打印才能达到想要的效果):

public static void main(String[] args) {
    final String a = "abc";
    final String b = new String("abc");
    final String c = "abcd".substring(0, 3);

    System.out.println("Let's start!");
    System.out.print("a - ");
    System.out.println(a);
    System.out.print("b - ");
    System.out.println(b);
    System.out.print("c - ");
    System.out.println(c);

    System.out.print("Are they all equals? - ");
    System.out.println(a.equals(b) && a.equals(c) && b.equals(c));

    System.out.print("But they are different objects, right? - ");
    System.out.println(!(a == b || b == c || a == c));

    System.out.println("Let's reverse only 'a'. But all are final and String is not mutable, so what can go wrong?");

    reverse(a);

    System.out.println("Done. What we've got here?");

    // trick 1
    System.out.print("a = ");
    System.out.print(a);
    System.out.println(" - ok, 'a' is reversed. A bit strange, but it works. Super method");
    System.out.print("b = ");
    System.out.print(b);
    System.out.println(" - wait... We haven't touched this");
    System.out.print("c = ");
    System.out.print(c);
    System.out.println(" - this is untouched, wierd, huh? We've just reversed 'a' so 'b' and 'c' should act the same.");

    // trick 2
    System.out.println("\nOk, so 'c' should equals \"abc\", right?\n");
    System.out.println("\"abc\".equals(c)? = "+"abc".equals(c));
    System.out.println("...\n");

    System.out.print("Do you remeber, that");
    System.out.print(" a = ");
    System.out.print(a);
    System.out.print(" oraz b = ");
    System.out.print(b);
    System.out.println(" ?\n");

    // trick 3
    System.out.println("So let's check that");
    System.out.print("a.equals(b) = ");
    System.out.println(a.equals(b)+"\n");
    System.out.println("Ok, we had expected that.\n");
    System.out.println("But what do you think the result of (\" \"+a).equals(\" \"+b) will be?\n");
    System.out.print("(\" \"+a).equals(\" \"+b) = ");
    System.out.println((" "+a).equals(" "+b)+"\n");

    System.out.print("And do you remeber, that");
    System.out.print(" a = ");
    System.out.print(a);
    System.out.print(" ,a c = ");
    System.out.print(c);
    System.out.println(" ?\n");

    // trick 4
    System.out.println("So let's check if they are different:");
    System.out.print("a.equals(c) = ");
    System.out.println(a.equals(c));
    System.out.println("So they are different... but are they really different?\n");
    System.out.print("(\" \"+a).equals(\" \"+c) = ");
    System.out.println((" "+a).equals(" "+c));
    System.out.println("Booo!!! You could choose the blue pill!\n");

    System.out.println("Our actors were: ");
    System.out.print("a = ");
    System.out.print(a);
    System.out.print(", b = ");
    System.out.print(b);
    System.out.print(", c = ");
    System.out.print(c);
    System.out.print(" oraz abc = ");
    System.out.println("abc");
    System.out.print("\n");

    // trick 5
    System.out.println("Or in other words");
    System.out.println("a = "+a+", b = "+b+", c = "+c+" oraz abc ="+(" "+"abc")+"\n");

    System.out.println("But do you remember what we were revering? Was is rally b?");
    System.out.println("Have a nice day. Z-DNA");
}

但我不明白那场比赛。所有字符串都是不同的对象,但具有相同的值。

那么为什么在技巧 1 中,字符串“c”的行为与“b”不同?

好的,我明白了技巧 2。“abc”不再是“abc”而是“cba”(但为什么?我更改了字符串“a”的值,而不是字符串池的值),所以它不能是等于“abc”,但是当我什至无法让“abc”调用“abc”时,“c”怎么可能是“abc”?

为什么在技巧 3 中添加空格后“a”和“b”不再相等,而为什么在 4 个带空格的“a”和“c”中相等?!?!

技巧 5 告诉我们,“a”、“b”、“c”和“abc”的值正在变化,具体取决于我们如何调用它。 (哦等等。“c”是特殊的。创建字符串的最不合理的方法实际上最不受黑魔法的影响)。

请帮助我理解我实际上做了什么以及函数反转是什么样的黑暗。

最佳答案

您已经知道字符串被保存在字符串池中这一事实。这里还有一些事实供您引用。

  1. 这是 new String("abc") 构造函数的源代码。

    String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
    

    所以这意味着您的 ab 后面有相同的字符数组。当您更改 a 的支持值时,您也会更改 b

  2. 当您更改驻留字符串的值时,驻留字符串当然也会更改。

    这实际上意味着代码中每次出现的“abc”不再是真正的“abc”,而是“cba”。代码说它是“abc”,但内存的说法不同。

  3. 但是,编译器会提前计算常量并分别对它们进行实习。这意味着,如果您有一个常量,例如 ""+ "abc",它会被编译为 "abc" - 暂留池中的不同字符串。

  4. 使用 + 的长字符串连接使用 StringBuilder 进行转换,以避免创建多个将被丢弃的中间对象。 + 的每个操作数都成为对 append 的调用。

因此,c 的行为与 b 不同,因为 ab 共享存储,但 b 不与 c 共享存储 - 因为 c 是从不同的常量派生的(现在子字符串无论如何都会创建一个新的支持数组)。

现在,对于 c 等于 "abc" 的情况,技巧 2 返回 false,因为正如我们所说,常量本身不是原来的样子- 你改变了它。

技巧 3 - 为什么当 a 等于 b 时,在它们之前添加空格会使它们不相等?嗯,因为 ""+ a 是一个常量,并且提前被实习为 "abc",而 ""+ b 不是 a常量在编译时已知,因此它是在运行时计算的。很容易检查是否添加

System.out.println( " " + a == " abc" );

这会打印 true - 它们是相同的字符串,并且只有在基于编译器对以下内容的不变性的信念而提前插入 ""+ a 时才会发生这种情况字符串和 final 的最终结果。

因此,""+ a 现在肯定是"abc"。所以难怪它等于""+ c。虽然 c 不是预驻留常量,但它仍然是 "abc" 并且串联仍然产生相同的结果。

最后,您用不同的打印方式打印的表达式仍然单独使用 "abc" ,因此它将其打印为 "cba" 这是它的新值。但是当你用一个大字体打印它时,其中一些是编译器时常量表达式 - 具体来说,括号中的部分:

System.out.println("a = "+a+", b = "+b+", c = "+c+" oraz abc ="+(" "+"abc")+"\n");

在编译时被保留为“abc” - 并且您已经知道这是一个单独的常量。

Java 将字符串串联转换为带有多个附加的 StringBuilder。该表达式相当于:

StringBuilder sb = new StringBuilder();
sb.append( "a = abc b=" )
  .append( b )
  .append( ", c = " )
  .append( c )
  .append( " oraz abc =" )
  .append( " abc" )
  .append( "\n" );
System.out.println( sb.toString() );

现在,有两组已预先连接的常量,其中一组是放在括号中的 ""+ "abc"

如果删除括号,空格和 "abc" 将分别附加,然后 "abc" 显示为 "cba".

如果你使用你可以看到这个

javap -p -v <class file>

关于java - "Mutable"Java 字符串行为不可预测,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36071290/

相关文章:

java - lwjgl 3、如何获取当前线程中的OpenGL上下文?

java - 如何在 Android DatePicker 中禁用以前的日期?

linux - 在 sh 中对字符串使用运算符

javascript - 如何递归地查找字符串中的一组字符?

c - 在字符串中查找字符

c - 如何找到指针数组指向的元素的大小?

java - 当我输入错误的用户名或密码时,它只是说**请输入用户名和密码**

java - Spring Security 阻止公共(public)休息服务

java - protected "stub"方法仅用于覆盖目的是否被视为良好做法?

php - 从字符串中提取数据