Adding on to what Scott said.... to make things abundantly clear.....
Conceptually, an "overloaded operator" exists in a language where I can change the meaning of the operator for a particular object (only objects not primitives AFAIK).
If you look at examples in c++ you can instantly tell how this works:
https://www.programiz.com/cpp-programming/operator-overloading
This link gives a really good definition of why what Java is doing is not "operator overloading":
https://www.tutorialspoint.com/why-is-operator-overloading-not-supported-by-java
If we want to talk about technicalities and interesting nuances.....
The spec refers to what we call the token that is the plus sign in several contexts, and defines concretely what they do in each of those contexts:
Unary plus operator:
https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.15.3
Additive operators (both primitive addition and String concatenation operators):
https://docs.oracle.com/javase/specs/jls/se11/html/jls-15.html#jls-15.18
The token itself is a different operator based on the context in which it occurs (and has a different name, ex unary plus operator vs concatenation operator). The compiler knows what the context is, and generates different instructions based on that information.
We can see evidence of what's going on when we examine the instructions generated by the compiler.
Example:
When we compile and run javap -c on the class file we can identify that these are, in fact, two completely different instructions with similar human readable syntax:
9: invokedynamic #3, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
...
24: iadd
Cool, huh?