Подтвердить что ты не робот

Статические конечные значения Java заменяются в коде при компиляции?

В 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, то массив объектов получит новое значение или он был бы hardcoded до 100, потому что это значение, когда оно было изначально построено?

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

4b9b3361

Ответ 1

Да, компилятор 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. Запустите снова, и вы увидите, что старое значение будет напечатано.

Ответ 2

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

class A
{  
    public static final int SIZE;

    static 
    {
        SIZE = 100;
    }
}  

Ответ 3

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

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 documentation "беззнаковый байт, который должен быть действительным index в пул констант времени выполнения текущего класса".

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

Ответ 4

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

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

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

Ответ 5

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

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

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

Ответ 6

На самом деле я столкнулся с этой причудой некоторое время назад.

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

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

Ответ 7

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

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

class b
{
      Object[] temp = new Object[100];
}

Ответ 8

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

  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 получит новое значение введите здесь описание изображения

Ответ 9

Существует исключение: -

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

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

Выход будет null


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

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

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

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

Ответ 10

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