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

У меня есть следующее:

double timeInMinutes = (double) timeInMilliseconds / (1000 * 60);

Выполняется ли операция (1000 * 60) во время компиляции или во время выполнения? Другими словами, существуют ли различия производительности во время выполнения между фрагментом кода выше и:

double timeInMinutes = (double) timeInMilliseconds / 60000;

EDIT: мой вопрос отличается от Будет ли компилятор Java предварительно вычислять суммы литералов?, поскольку я смешиваю использование переменных и литералов в арифметических операциях. Это небольшая разница, но как отметил @TagirValeev в комментариях (Являются ли арифметические операции над литералами, рассчитанными во время компиляции или времени выполнения?), бывают случаи, когда некоторые литералы aren 'pre-compiled, даже если они могут быть.

Ответ 1

Согласно JLS §15.2 - Формы выражений

Некоторые выражения имеют значение, которое можно определить во время компиляции. Это постоянные выражения (§15.28).

Мультипликативные операторы, такие как *, /, and %, попадают под постоянные выражения, поэтому они будут определены во время компиляции.

@SergeyMorozov был быстрее, чем мне писать и получать байт-код (#2 = Integer 60000), но вот практическое доказательство и выше - теоретическое/официальное утверждение:

Попробуйте также генерировать байт-код в конце, используя 1000 * 60 и 60000, и вы увидите такие же инструкции кода байта, и, следовательно, будет такая же производительность выполнения.

Класс Java:

public class Test {
    public static void main(String[] args) {
        int compileTest = 1000 * 60;
    }
}

Байт-код:

Classfile /E:/Test.class
  Last modified Oct 9, 2015; size 265 bytes
  MD5 checksum fd115be769ec6ef7995e4c84f7597d67
  Compiled from "Test.java"
public class Test
  SourceFile: "Test.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#13         //  java/lang/Object."<init>":()V
   #2 = Integer            60000
   #3 = Class              #14            //  Test
   #4 = Class              #15            //  java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Utf8               LineNumberTable
   #9 = Utf8               main
  #10 = Utf8               ([Ljava/lang/String;)V
  #11 = Utf8               SourceFile
  #12 = Utf8               Test.java
  #13 = NameAndType        #5:#6          //  "<init>":()V
  #14 = Utf8               Test
  #15 = Utf8               java/lang/Object
{
  public Test();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=2, args_size=1
         0: ldc           #2                  // int 60000
         2: istore_1
         3: return
      LineNumberTable:
        line 3: 0
        line 4: 3
}

Ответ 2

Во время компиляции. Это одна из тех самых основных оптимизаций компилятора, которая называется Constant Folding.

Ответ 3

Просто создайте тест класса

public class Test {
    public static void main(String [] args) {
        long timeInMilliseconds = System.currentTimeMillis();
        double timeInMinutes = (double) timeInMilliseconds / (1000 * 60);
        System.out.println(timeInMinutes);
    }
}

и декомпилируйте его с помощью команды: javap -v Test

Вы можете увидеть результат декомпилированного класса:

  public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
  stack=4, locals=5, args_size=1
     0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
     3: lstore_1
     4: lload_1
     5: l2d
     6: ldc2_w        #3                  // double 60000.0d
     9: ddiv
    10: dstore_3
    11: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
    14: dload_3
    15: invokevirtual #6                  // Method java/io/PrintStream.println:(D)V
    18: return
  LineNumberTable:
    line 3: 0
    line 4: 4
    line 5: 11
    line 6: 18

Посмотрите на строку 6: ldc2_w # 3//double 60000.0d