2016-03-17 4 views
0

я играл немного с изменением значения типа String (я знаю, что это крайне небезопасно и опасно) с функцией:«Изменчивый» Строка Java действует непредсказуемо

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» (но почему? Я изменил значение String «a», а не значение пула строк), поэтому он не может быть равным «abc», но как «c» может быть «abc», когда я даже не могу получить «abc», вызывающий «abc» ??

Почему в трюке 3 после добавления пространства 'a' и 'b' больше не было равным и почему на земле в 4 'a' и 'c' с пробелами был равен?!?!

Trick 5 показывает нам, что значение 'a', 'b', 'c' и "abc" изменяется в зависимости от того, как мы его называем. (oh wait. 'c' является особенным. Самый нерациональный метод создания строки на самом деле наиболее невосприимчив к этой черной магии).

Пожалуйста, помогите мне понять, что я на самом деле сделал, а какая тьма - функция обратная.

+0

Взгляните на этот ответ http://stackoverflow.com/questions/35899879/and-equals-not-working-in-android-studio-java/35899981 # 35899981 –

+1

Здесь слишком много вопросов, и я не могу беспокоиться о них. Ответ на первый легко. 'new String (" abc ")' создает экземпляр 'new', но он разделяет * тот же * массив поддержки, что и' 'abc" '. –

+2

Конечно, это непредсказуемо. Это _why._ –

ответ

2

Вы уже знаете о том, что строки интернированы в пуле строк. Итак, вот еще несколько фактов для вас.

  1. Это источник конструктора для new String("abc").

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

    Таким образом, это означает, что ваш a и b имеют один и тот же массив символов позади них. Когда вы меняете значение поддержки a, вы меняете b.

  2. При изменении значения интернированной строки, конечно, интернированная строка изменяется.

    Это действительно означает, что каждое появление "abc" в вашем коде на самом деле не «abc», а «cba». В коде говорится, что это «abc», но память говорит по-другому.

  3. Однако компилятор заранее вычисляет константы и ставит их отдельно. Это означает, что если у вас есть константа, такая как " " + "abc", она скомпилирована как " abc" - другая строка в интернированном пуле.

  4. Длинные конкатенации строк с + переведены с использованием StringBuilder, чтобы избежать создания нескольких промежуточных объектов, которые будут отброшены. Каждый операнд + становится вызовом append.

Так, c вели себя иначе, чем b, потому что a акции магазина остроумия b, но b не разделяет магазин с c - потому что c была получена из другой константы (а ныне подстрока создает новый массив подкладочный в любом случае).

Теперь трюк 2 возвращает false для c, равный "abc", потому что, как мы уже говорили, сама константа не такая, какая была - вы ее изменили.

Trick 3 - почему, когда a равно b, добавляет ли пространство перед ними сделать их неравными? Ну, потому что " " + a является константой и получает интернирование заранее как " abc", тогда как " " + b не является константой, известной во время компиляции, поэтому она вычисляется во время выполнения. Легко проверить, если вы добавите

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

Печатается true - они такие же строки, и это может произойти только в том случае " " + a был interened заранее основанный на вере компилятора в неизменности строк и окончательности финала.

Таким образом, " " + 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".

Вы можете увидеть это, если вы используете

+0

Это GENIUS !!! Вау. Для меня ты хозяин. Один, последний вопрос - почему в последней строке 'oraz abc =" + ("" + "abc") 'когда мы удаляем круглые скобки, он снова печатает' abc = cba'? Мне нужно было поместить это в '()' для печати это как 'abc'. Спасибо за вашу помощь. –

+0

@ Z-DNA Я немного изменил конец своего ответа, поскольку он был не совсем точным - только начало конкатенации задерживается вместе. Остальные добавляются с помощью' StringBuilder'. – RealSkeptic

Смежные вопросы