The relevant parts of the specification are:
[…]
At run time, if evaluation of the operand expression completes abruptly, then the prefix increment expression completes abruptly for the same reason and no incrementation occurs. Otherwise, the value 1 is added to the value of the variable and the sum is stored back into the variable. […]
The value of the prefix increment expression is the value of the variable after the new value is stored.
[…]
At run time, if evaluation of the operand expression completes abruptly, then the postfix increment expression completes abruptly for the same reason and no incrementation occurs. Otherwise, the value 1 is added to the value of the variable and the sum is stored back into the variable. […]
The value of the postfix increment expression is the value of the variable before the new value is stored.
So, the only difference is the result value when used in an expression context. When using it in the context of a for
loop’s update clause, there relevant part is:
[…]
→ First, if the ForUpdate part is present, the expressions are evaluated in sequence from left to right; their values, if any, are discarded. If evaluation of any expression completes abruptly for some reason, the for statement completes abruptly for the same reason; any ForUpdate statement expressions to the right of the one that completed abruptly are not evaluated.
Taking it literally there would be a difference in the code as these expressions produce different results which are then discarded. However, this difference lies in non-observable behavior and as a consequence, the code is usually compiled to not produce the value in the first place and hence does not differ.
So the answer is, the code may have differences, e.g. when compiled naively, however the observable behavior is guaranteed to be the same.