It's easy enough to test it out. Put the following in Temp.java:
class Temp {
public static void main(String[] argv) {
byte b = 27;
System.out.println(b);
}
}
Now compile it with your favorite compiler:
$ javac Temp.java
Now dump the bytecode with javap:
$ javap -c Temp.class
Compiled from "Temp.java"
class Temp {
Temp();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: bipush 27
2: istore_1
3: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
6: iload_1
7: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
10: return
}
Now replace 27
with (byte)27
and run again. You'll see there is no difference. In fact the two classfiles will have the same md5sum.
There is no runtime cast in the bytecode because the compiler figured out it wouldn't be needed, and optimized it away.
I believe you're correct that syntactically the line byte b = 27
differs from the line byte b = (byte) 27
, but they are semantically the same, because all standard compilers are smart enough to optimize the line into a single bytecode.