Это потому, что Java допускает переопределение для методов, но не для полей.
Спецификация записывает :
Если класс объявляет поле с определеннымname, тогда говорят, что объявление этого поля скрывает все доступные объявления полей с одинаковыми именами в суперклассах и суперинтерфейсах класса.
В этом отношении сокрытие полей отличается от сокрытия методов (§8.4.8.3), поскольку в скрытии полей не проводится различий между статическими и нестатическими полями, тогда как различие проводится междустатические и нестатические методы в скрытии методов.
Доступ к скрытому полю можно получить с помощью квалифицированного имени (§6.5.6.2), если оно статическое, или с помощью выражения доступа к полю, которое содержитключевое слово super (§15.11.2) или приведение к типу суперкласса.
В этом отношении скрытие полей аналогично сокрытию методов.
То есть каждый экземпляр B
имеет два разных поля, которые имеют одно и то же имя.Когда вы используете имя поля в выражении доступа к полю, таком как test1.number
, имя поля интерпретируется относительно типа времени компиляции test1
.
Напротив, методы экземпляра могут быть переопределены :
Метод экземпляра mC, объявленный (или унаследованный) в классе C, переопределяет из C другой метод mAобъявлен в классе A, если все следующее верно:
(где «подпись» означает, чтоимя метода и параметры совместимы.)
Переопределение разрешено следующим образом :
В противном случае должен быть вызван метод экземпляра, и существует цельссылка.Если целевая ссылка равна нулю, в этой точке генерируется исключение NullPointerException.В противном случае говорят, что целевая ссылка ссылается на целевой объект и будет использоваться в качестве значения ключевого слова this в вызываемом методе.
...
В противном случае, [...] может произойти переопределение.Динамический метод поиска используется.Процесс динамического поиска начинается с класса S, определяемого следующим образом:
Для поиска в динамическом методе используется следующая процедура для поиска в классе S, а затем в суперклассах и суперинтерфейсах класса S,при необходимости для метода m.
То есть, если вы напишите test1.getNumber()
, среда выполнения оценит test1
, чтобы получить целевой объект, определить его класс и затем найти подходящий метод в этом классе.
В вашем примере test1
ссылается на объект класса B
, поэтому вызывается B.getNumber()
.
Затем реализация B.getNumber()
переходит к чтению B.number
, которое никогда не назначалось и, следовательно, все еще содержит значение по умолчанию 0.