Как Java выбирает перегруженный метод?

Метод может быть перегружен различными параметрами – в классе могут существовать несколько разных методов с одинаковым названием. При вызове такого метода выбор конкретного варианта происходит на этапе компиляции (раннее связывание). В деталях алгоритм выбора перегруженного метода описан в спецификации.

Выбор происходит в два шага. На первом выбирается одна из трех фаз – множество подходящих методов.

1. Методы, в которые переданные параметры подходят по типу либо как есть, либо с применением расширения (upcasting) примитивов или ссылочных типов, исключая vararg-параметры.

2. Если в фазе 1 подходящих методов не нашлось, к ее условиям добавляются возможность боксинга/анбоксинга параметров. Обратите внимание, в комбинации работает только боксинг+расширение, но не наоборот.

3. Если и для фазы 2 нет удовлетворительных сигнатур, к условиям поиска подключаются vararg-параметры.

В случае, когда ни один метод не нашелся ни в одной фазе, компиляция завершается ошибкой.
Как Java выбирает перегруженный метод?
Когда в фазе имеется несколько подходящих методов, используется наиболее специфичный среди них (но только в рамках данной фазы!).

Метод A считается более специфичным чем B, когда типы параметров одного метода – подтипы типов параметров другого. То есть любые возможные значения аргументов A подошли бы и для B, но не наоборот.

В условии специфичности говорится о типах параметров метода, а не о типах передаваемых значений. Так что боксинг/анбоксинг не учитывается, и метод с параметром int не считается более специфичным, чем с параметром Object (в отличие от Integer). Хотя, целое число можно передавать и как Object, и как Integer. Подробное объяснение.

Когда среди методов невозможно выделить один наиболее специфичный, происходит ошибка компиляции.