whenever you use the final modifier infront of a variable you must initialize it. What this basically does is, create a constant value for the variable that can never be modified.\ In your example, you are assigning variable i a CONSTANT value of 100. What this effectively does is that it makes the variable independant of a particular type. so when we say: byte b=i it is the same as saying byte b = 100; if you try this it will also compile fine, AS LONG AS IT IS IN THE RANGE OF A BYTE, and 100 is in range.
In the second case since final is not used, the variable i is identified with a particular type, an int, and assignment of an int type cannot be made to a byte (even if the value may be in range) without explicit casting.
As per JLS, narrowing primitive conversion may be used if all of the following conditions are satisfied: (1) The expression is a constant expression of type byte, short, char or int. (2) The type of the variable is byte, short, or char. (3) The value of the expression (which is known at compile time, because it is a constant expression) is representable in the type of the variable. Also, If the type of the expression cannot be converted to the type of the variable by a conversion permitted in an assignment context, then a compile-time error occurs. This is what dictates the results of the code HTH Sandeep Nachane