Java статические конечные значения заменены в коде при компиляции? - PullRequest
14 голосов
/ 02 марта 2011

В Java, скажем, у меня есть следующее

==fileA.java==
class A
{  
    public static final int SIZE = 100;
}  

Тогда в другом файле я использую это значение

==fileB.java==  
import A;
class b
{
      Object[] temp = new Object[A.SIZE];
}

Когда это скомпилируется, SIZE заменяется значением 100, так что, если бы я шел по дороге, заменил FileA.jar, но не FileB.jar, массив объектов получил бы новое значение или был бы жестко закодировано до 100, потому что это значение, когда оно было изначально построено?

Спасибо
Стефани

Ответы [ 10 ]

27 голосов
/ 03 марта 2011

Да, компилятор Java заменяет значения статических констант, таких как SIZE в вашем примере, их литеральными значениями.

Итак, если вы позже измените SIZE в классе A, но вы не• перекомпилируйте класс b, вы все равно увидите старое значение в классе b.Вы можете легко проверить это:

файл A.java

public class A {
    public static final int VALUE = 200;
}

файл B.java

public class B {
    public static void main(String[] args) {
        System.out.println(A.VALUE);
    }
}

Скомпилируйте A.java и B.java.Теперь выполните: java B

Измените значение в A.java.Перекомпилируйте A.java, но не B.java.Запустите снова, и вы увидите, как печатается старое значение.

8 голосов
/ 03 марта 2011

Вы можете сохранить константу от компиляции в B, выполнив

class A
{  
    public static final int SIZE;

    static 
    {
        SIZE = 100;
    }
}  
4 голосов
/ 03 марта 2011

Еще один способ доказать, что такое поведение - смотреть на сгенерированный байт-код.Когда константа «мала» (предположительно <128): </p>

public B();
  Code:
   0:   aload_0
   1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   bipush  42
   7:   anewarray       #3; //class java/lang/Object
   10:  putfield        #12; //Field temp:[Ljava/lang/Object;
   13:  return

}

(я использовал 42 вместо 100, поэтому он выделяется больше).В этом случае он явно подставляется в байт-код.Но, скажем, константа «больше».Затем вы получите байт-код, который выглядит следующим образом:

public B();
  Code:
   0:   aload_0
   1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   ldc     #12; //int 86753098
   7:   anewarray       #3; //class java/lang/Object
   10:  putfield        #13; //Field temp:[Ljava/lang/Object;
   13:  return

Когда он больше, используется код операции "ldc", который согласно документации JVM"является неподписанным байтом, который долженбыть действительным индексом в пуле констант времени выполнения текущего класса ".

В любом случае константа встроена в B. Я думаю, поскольку в кодах операций вы можете получить доступ только к пулу констант времени выполнения текущих классов,что это решение записать константу в файл класса не зависит от реализации (но я не знаю, что на самом деле).

3 голосов
/ 03 марта 2011

Важной концепцией здесь является то, что поле static final инициализируется постоянной времени компиляции , как определено в JLS. Используйте непостоянный инициализатор (или не static или не final), и он не будет скопирован:

public static final int SIZE = null!=null?0: 100;

(null не является * постоянной времени компиляции`.)

3 голосов
/ 03 марта 2011

Woo - вы узнаете что-то новое каждый день!

Взято из спецификации Java ...

Примечание: если тип примитива или строка определяется как константа и значение известен во время компиляции, компилятор везде заменяет имя константы в коде с его значением. Это называется константой времени компиляции. Если значение константы снаружи мир меняется (например, если это законодательно, что пи на самом деле должно быть 3.975), вам нужно будет перекомпилировать любые классы, которые используют эту константу, чтобы получить текущее значение.

2 голосов
/ 03 марта 2011

На самом деле я столкнулся с этой странностью некоторое время назад.

Это скомпилирует "100" в класс b напрямую. Если вы просто перекомпилируете класс A, это не обновит значение в классе B.

Кроме того, компилятор может не заметить перекомпиляцию класса b (в то время, когда я компилировал отдельные каталоги, а класс B находился в отдельном каталоге, а компиляция каталога a не вызывала компиляцию B)

1 голос
/ 03 марта 2011

В качестве оптимизации компилятор вставит эту переменную final .

Так что во время компиляции это будет выглядеть так.

class b
{
      Object[] temp = new Object[100];
}
0 голосов
/ 10 марта 2017

Есть исключение из этого: -

Если во время компиляции статическое конечное поле является нулевым, оно не заменяется нулевым (что фактически является его значением)

A.java

class A{
     public static final String constantString = null;
}

B.java

class B{
     public static void main(String... aa){
         System.out.println(A.constantString);
     }
}

Скомпилируйте A.java и B.java и выполните java B

Вывод будет ноль


Теперь обновите A.java следующим кодом и скомпилируйте только этот класс.

class A{
     public static final String constantString = "Omg! picking updated value without re-compilation";
}

Теперь запустите Java B

Вывод будет Омг! выбор обновленного значения без перекомпиляции

0 голосов
/ 23 февраля 2017

Следует отметить следующее: статическое конечное значение известно во время компиляции если значение не известно во время компиляции, компилятор не будет заменять имя константы везде в коде своим значением.

  public class TestA {
      // public static final int value = 200;
      public static final int value = getValue();
      public static int getValue() {
        return 100;
      }
  }

public class TestB {
    public static void main(String[] args) {
        System.out.println(TestA.value);
    }
}

первая компиляция TestA и TestB, запуск TestB

затем измените TestA.getValue (), чтобы вернуть 200, скомпилируйте TestA, запустите TestB , TestB получит новое значение введите описание изображения здесь

0 голосов
/ 03 марта 2011

Java оптимизирует эти виды значений, но только если они находятся в одном классе.В этом случае JVM смотрит в A.SIZE, а не оптимизирует его из-за рассматриваемого вами варианта использования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...