In fact, prior to Java 8, you could not even declare unsigned integers.
In Java 8 you still can't declare unsigned integers. It just that API have been
extended to provide operations that treat an int or a long as having unsigned
value. This seems to imply that Java leaves the behaviour of signed integer overflow
up to the underlying hardware, but guarantees a two's complement
representation even if the architecture does not. It seems reasonable to
expect that it would simply wrap to 0 (I wasn't able to find a more
conclusive reference for this).
In fact Java Specification does specify what happens in case of overflow very
precisely. For example for multiplication (section 15.17.1): If an integer multiplication overflows, then the result is the low-order bits
of the mathematical product as represented in some sufficiently large
two's-complement format.
And for addition (section 15.18.2): If an integer addition overflows, then the result is the low-order bits of the
mathematical sum as represented in some sufficiently large two's-complement
format.
Java approach without undefined behaviour would seem to be quite convenient for
a programmer, but it does not seem to be the case in practice. Of course you
gain ability to check for overflow after performing the operation, which is
nice, but at the same time loose the straightforward way to detect bugs
provided by undefined behaviour (UB).If an application executes UB, then it is obviously wrong, thus a simple instrumentation of arithmetic operation followed by a check for overflow gives a way to detect such problems without false positives (see for example -fsanitize=undefined and similar options available in modern C/C++ compilers).
IMHO only very small fraction of overflow bugs would have been fixed by just wrapping result around.