Подтвердить что ты не робот

Требуется объяснение вывода кода Java-кода

Мой код:

class Foo {
  public int a=3;
  public void addFive() {
    a+=5;
    System.out.print("f ");
  }
}

class Bar extends Foo {
  public int a=8;
  public void addFive() {
    this.a += 5;
    System.out.print("b ");
  }
}

public class TestClass {
  public static void main(String[]args) {
    Foo f = new Bar();
    f.addFive();
    System.out.println(f.a);
  }
}

Вывод:

b 3

Пожалуйста, объясните мне, почему вывод для этого вопроса "b 3", а не "b 13", поскольку метод был переопределен?

4b9b3361

Ответ 1

Вы не можете переопределять переменные в Java, поэтому на самом деле у вас есть две переменные a - одна в Foo и одна в Bar. С другой стороны, метод addFive() является полиморфным, поэтому он изменяет Bar.a (Bar.addFive() вызывается, несмотря на то, что статический тип f равен Foo).

Но в конце вы получаете доступ к f.a, и эта ссылка разрешается во время компиляции с использованием известного типа f, который равен Foo. И поэтому Foo.a никогда не касался.

Незначительные переменные BTW в Java должны никогда быть публичными.

Ответ 2

F является ссылкой типа Foo, а переменные не являются полиморфными, поэтому f.a относится к переменной из Foo, которая 3

Как проверить его?

Чтобы проверить это, вы можете удалить переменную a из Foo, это даст вам ошибку времени компиляции

Примечание: Сделайте переменную-член private и используйте accessors для доступа к ним


Также см.

Ответ 3

С таким вопросом экзамен SCJP оценивает ваши знания о том, что известно как сокрытие. Экзаменатор сознательно усложнил ситуацию, пытаясь заставить вас поверить, что поведение программы зависит только от полиморфизма, чего нет.

Попробуйте сделать вещи немного яснее, когда мы удалим метод addFive().

class Foo {
  public int a = 3;
}

class Bar extends Foo {
  public int a = 8;
}

public class TestClass {
  public static void main(String[]args) {
    Foo f = new Bar();
    System.out.println(f.a);
  }
}

Теперь все немного запутанно. Метод main объявляет переменную типа Foo, которому при запуске назначается объект типа Bar. Это возможно, так как Bar наследует от Foo. Затем программа ссылается на общедоступное поле a переменной типа Foo.

Ошибка здесь будет заключаться в том, что такой же тип понятия, который называется переопределением, применяется к полям классов. Но такого понятия для полей нет: публичное поле a класса Bar не перекрывает общедоступное поле a класса Foo, но делает то, что называется скрытием. Как следует из названия, это означает, что в области класса Bar a будет ссылаться на собственное поле Bar, которое не имеет ничего общего с Foo one. (JLS 8.4.8 - Наследование, Перекрытие и скрытие)

Итак, когда мы пишем f.a, к какому a имеем в виду? Напомним, что разрешение поля a выполняется в время компиляции, используя тип объявления объекта f, который равен Foo. Как следствие, программа печатает "3".

Теперь добавьте метод addFive() в класс Foo и переопределите его в классе Bar, как в вопросе экзамена. Здесь применяется полиморфизм, поэтому вызов f.addFive() разрешается, используя не время компиляции, а тип времени выполнения объекта f, который является Bar, и поэтому печатается "b".

Но есть еще что-то, что мы должны понять: почему поле a, которое было увеличено на 5 единиц, все еще придерживается значения "3"? Здесь скрывается. Поскольку это метод класса Bar, который вызывается, а потому, что в классе Bar каждый a относится к Bar общедоступному полю a, это фактически поле Bar, которое увеличивается.

1) Теперь, один вспомогательный вопрос:, как мы можем получить доступ к открытому полю Bar a из метода main? Мы можем сделать это с чем-то вроде:

System.out.println( ((Bar)f).a );

которые заставляют компилятор разрешать член поля a of f как Bar a.

В нашем примере будет напечатан "b 13" .

2) Еще один вопрос:, как мы могли бы работать, скрываясь в методе addFive() класса Bar, чтобы ссылаться не на поле Bar a, а на его многомерное многомерное поле? Просто добавление ключевого слова super перед ссылкой на поле делает трюк:

public void addFive() {
  super.a += 5;
  System.out.print("b ");
}

В нашем примере будет напечатано 'b 8'.

Обратите внимание, что начальная инструкция

public void addFive() {
  this.a += 5;
  System.out.print("b ");
}

можно уточнить до

public void addFive() {
  a += 5;
  System.out.print("b ");
}

потому что когда компилятор разрешает поле a, он начнет искать в ближайшей закрывающей области из метода addFive() и найдет экземпляр класса Bar, исключающий необходимость использования явно this.

Но, ну, this, вероятно, был ключом для испытуемого решить этот вопрос экзамена!

Ответ 4

Поскольку вы выполняете f.a, вы получите значение a из класса Foo. если вы вызвали метод для получения значения, например getA(), то вы получили бы значение из класса Bar.