Классы, интерфейсы и перечисления

Вопрос 1

Что будет, если попытаться откомпилировать следующие классы?

class Company { }


class JointStock implements Serializable extends Company { }

а. Код не откомпилируется.
б. Код откомпилируется без ошибок.

а. Код не откомпилируется.

За именем класса могут следовать ключевые слова extends и implements. Но если присутствуют оба, то класс обязательно сперва расширяет (extends) другой класс и только потом реализует (implements) интерфейсы.

Вопрос 2

Что будет, если откомпилировать и выполнить следующий код?

class A {

public void toStr() {

System.out.println("A");

}

}


class B extends A {

@Override

public void toStr() {

System.out.println("B");

}

}


public class Study {

public static void main(String[] args) {

A a = new B();

a.toStr();

}

}

а. Код не откомпилируется.
б. Код откомпилируется, но во время выполнения выбросит исключение.
в. Код откомпилируется и выведет на консоль "А".
г. Код откомпилируется и выведет на консоль "В".

г. Код откомпилируется и выведет на консоль "В".

В классе В переопределён метод toStr(). Поскольку в строке A a = new B(); реально создаётся объект типа В (наследника), то неважно, что на него ссылается переменная типа А (предка). Во время выполнения произойдёт диспетчеризация методов и для выполнения будет выбран именно переопределённый метод наследника. А в нём, как мы видим, в консоль выводится "В".

Вопрос 3

Что будет, если откомпилировать и выполнить следующий код:

class A {

public void method(Object obj) {

System.out.println("A");

}

}


class B extends A {

@Override

public void method(Integer obj) {

System.out.println("B");

}

}


public class Study {

public static void main(String[] args) {

A a = new B();

a.method(0);

}

}

а. Код не откомпилируется.
б. Код откомпилируется, но во время выполнения выбросит исключение.
в. Код откомпилируется и выведет на консоль "А".
г. Код откомпилируется и выведет на консоль "В".

а. Код не откомпилируется.

Это сложный вопрос, в нём несколько подвохов. Но если отвлечься от всего лишнего, то получится следующее.

Поскольку методы:

public void method(Object obj)

public void method(Integer obj)

имеют разную сигнатуру, то один из них не может считаться переопределением другого. При компиляции строка с аннотацией @Override выбросит ошибку:

method does not override or implement a method from a supertype

Вопрос 4

Что будет, если попытаться откомпилировать и выполнить следующий код?

public class Test {

static String str1;

public static void main(String[] args) {

String str2;

System.out.println(str1); //1

System.out.println(str2); //2

}

}

а. Код откомпилируется и выполнится без проблем. Выведет в консоль две пустые строки.
б. Код откомпилируется, но во время выполнения вылетит в ошибку в строке //1.
в. Код откомпилируется, но во время выполнения вылетит в ошибку в строке //2.
г. Код не откомпилируется. В строке //1 возникнет ошибка компиляции.
д. Код не откомпилируется. В строке //2 возникнет ошибка компиляции.

д. Код не откомпилируется. В строке //2 возникнет ошибка компиляции.

Переменная str1 является полем класса, в данном случае ссылочного типа String, а значит автоматически инициализируется значением null. Строка System.out.println(str1); также выполнится без проблем, так как метод println выводит в консоль слово "null", если получает null в качестве параметра, не выбрасывая при этом никаких исключений.

Переменная str2 является обыкновенной локальной переменной метода. Такие переменные обязательно должны быть инициализированы вручную перед первым использованием. Так как инициализации не происходит, то строка System.out.println(str2); даже не скомпилируется.

Вопрос 5

Что будет, если попытаться откомпилировать и выполнить следующий код?

public class Study {


static {

System.out.println("static");

}


public static void main(String[] args) {


System.out.println("main");

}

}

а. Код не откомпилируется.
б. Код откомпилируется, но во время выполнения вылетит в ошибку.
в. Код откомпилируется, выполнится и выведет в консоль сначала "static", а затем "main".
г. Код откомпилируется, выполнится и выведет в консоль сначала "main", а затем "static".

в. Код откомпилируется, выполнится и выведет в консоль сначала "static", а затем "main".

Код абсолютно валидный, он откомпилируется и отработает без проблем.

Несмотря на то, что выполнение программы начинается с метода main, нужно понимать, что сам метод main находится в конкретном классе (в примере - Study), который обязательно сперва будет загружен classLoader'ом и у которого обязательно предварительно будет выполнен блок статической инициализации static { }, если таковой есть (а в нашем примере он есть). И только после этого начнёт выполнятся main.

Вопрос 6

Что выведет на консоль следующий код?

class Animal {

public Animal() {

System.out.print("animal ");

}

}


class Cat extends Animal {

public Cat() {

System.out.print("cat ");

}

}


public class Study {

public static void main(String[] args) {

Animal cat = new Cat();

}

}

а. animal.
б. cat.
в. animal cat
г. cat animal

в. animal cat

Хотя переменная cat объявлена с родительским типом Animal по факту создаётся (new Cat()) объект типа Cat, а значит конструктор public Cat() будет выполнен.

Однако.

Поскольку Cat наследует Animal, и в его конструкторе нет явного вызова родительского конструктора, то в байт-коде Java автоматически подставит в первую строчку вызов super() родительского конструктора. Таким образом всё-таки сперва выполнится конструктор public Animal(), который выведет слово "animal ", затем продолжится выполнение конструктора дочернего класса Cat, который выведет слово "cat".

В итоге на консоль будет выведено "animal cat".