The conversion from byte to char is a widening and narrowing primitive conversion, as described in paragraph 5.1.4 of the Java Language Specification.
As the JLS describes, this is done via an intermediate step; the byte is converted to int via a widening primitive conversion and then the int is converted to char via a narrowing primitive conversion (see 5.1.3).
Paragraph 5.2 explains when a cast is necessary when you do an assignment:
... if the expression is a constant expression (§15.28) of type byte, short, char, or int:
- A narrowing primitive conversion may be used if the type of the variable is
byte, short, or char, and the value of the constant expression is representable in the type of the variable.
Your variable b1 is indeed a constant, but your variable b2 is not, so this rule applies for b1 but not for b2.
So: you can assign b1 to c because b1 is a constant and the value of the constant, 1, fits in a char, but you cannot assign b2 to c without a cast because b2 is not a constant.