вторник, 19 мая 2009 г.

Ответы на вопросы для собеседования по Java SE (Часть 3)

Предыдущая серия ответов: Ответы на вопросы для собеседования по Java SE (Часть 2)
Теперь помимо ответов на вопросы предоставляю ещё дополнительные вопросики для четкости полученных знаний.

11. Какие модификаторы доступа в Java вы знаете?
Java, как наследник C++ наследует и модификатор доступа с этого языка: public, private, protected и модификатор доступа по умолчанию - это когда модификатор не указывается.

-Можно ли объявить класс с модификатором protected?

12. Какой из модификаторов более строгий: protected или package-private?
Для начала надо разобраться, что такое package-private модификатор. Он ограничивает видимость до пределов пэкэджа, в котором лежит класс. И только до него! То есть, если класс лежит в пэкэдже "ru.myprog", то из пэкэджа "ru.myprog.base" он виден не будет(тоже самое для методов).
Protected модификатор, как все знают(кто хотя бы немного изучал ООП), раскрывает область видимости только для классов-наследников и для классов определенных в том же пэкэдже. Здесь мы видим отличие от C++, в котором только наследники видят protected.
Здесь можно почитать более подробно про модификаторы, а также увидеть сводную табличку http://java.sun.com/docs/books/tutorial/java/javaOO/accesscontrol.html

Из всего этого следует, что package-private модификатор более строгий.

-Объявляем класс с модификатором public, в котором создадим метод с default модификатором. Будет ли виден этот метод из других пэкэджей?
-Скомпилируется ли данная программа:
package ru.test;
class DefaultPackage {
void test()
{
;
}
}
----
package ru.test;

public class Public extends DefaultPackage {

}
----
package ru.test.base;

import ru.test.Public;

public class Main {
public static void main(String[] args) {
Public pub = new Public();
pub.test();
}
}

-Возможно ли перегружать default методы?

13. Если у класса-родителя есть метод, объявленный как private, может ли наследник расширить его видимость? А если protected? А сузить видимость?
private методы никто, кроме самого класса не видит. Поэтому их наличие/отсутствие никак не отражается на классах-наследниках. Они с легкостью могут объявлять методы с такой же сигнатурой и любыми модификаторами. Но это очень плохой тон! Антипаттерн.
Также клас наследник может расширить видимость protected-метода. Такой класс называется "Паблик-Морозов":). Из-за того, что как известный советсткий пионер, раскрывают информацию кому ни попадя! :)
Сузить видимость класс-наследник не может. Это будет ошибка компиляции.

14. Что означает ключевое слово final?
В случае класса - нельзя от него наследоваться.

В случае метода - нельзя его переопределять. Это используется, когда нужно явно указать "здесь не трогайте! оно так не задумано!". Обычно позволяет улучшить архитектуру системы. Но это нужно делать только если есть необходимость. Не надо все методы определять как final! Таких программистов называют "фаталисты". Раньше ещё было такое мнение, что final методы быстрее работают. Сейчас это абсолютная неправда. Работают они с такой же скоростью! Так что используйте их только при явной необходимости.

В члена класса final означает константу, которая после инициализации не поменяет своего значения. Но подумайте сами, что будет, если мы объявим, например, коллекцию как final:
Сработает ли следующий код?
public class Test {
final static List _list = new ArrayList();

public test() {
_list.add("Hello world!");
}
}

А этот ?
public class Test {
final List _list = new ArrayList();

public test() {
_list = new LinkedList();
}
}

15. Имеет ли смысл объявлять метод private final?
Только если вы отъявленный фаталист, финалист и пессимист! :)

16. Какие особенности инициализации final переменных?
Если они объявлена не как static, то они инициализируются в конструкторе, даже если не были объявлены не в нем. В этом случае код сгенерирует сам компилятор. Причем порядок их инициализации соответствует порядку их определения.
Если же она объявлена, как static, то либо при первом обращении к этой переменной, либо при первом создании объекта такого класса. То есть создание статических переменных происходит "по требованию" и только один раз. И это очень логично.

17. Что будет, если единственный конструктор класса объявлен как final?
Ошибочка компиляции. Почему - предлагаю подумать самим и написать в комментариях.

15 комментариев:

  1. Ответ на #12 явно виден из таблицы, которая приведена здесь.
    Также там применяется термин package-private и объясняется, что protected в java дает доступ помимо наследников еще и из классов пэкеджа.

    ОтветитьУдалить
  2. Ни один нестатический член класса не может иницироваться нигде, кроме как в конструкторе. Поэтому final-переменные (нестатические) будут инициализироваться в начале выполнения конструктора. Код инициализации сгенерирует сама JDK.

    ОтветитьУдалить
  3. Еще 5коп.
    package – "моё и соседское". К полям, методам и классам, объявленным package, имеет доступ не только класс, в котором они объявлены, но и все классы, находящиеся в том же самом пакете. О том, что представляет собой пакет с точки зрения контроля доступа, речь пойдет ниже. Ключевого слова для обозначения этого уровня доступа, как я уже говорил, нет. Достаточно не указать любой другой.

    protected – "моё и всех наследников". К полям, методам и классам, объявленным protected, имеет доступ класс, в котором они объявлены, все классы, находящиеся в том же самом пакете и все классы, унаследованные от того, где сделано объявление. Для обозначения уровня доступа используется ключевое слово protected.

    Как видим - package более строг, чем protected. Вопрос вполне корректный.

    ОтветитьУдалить
  4. Спасибо, за интересные комментарии! К своему стыду не знал(не помнил), что "protected в java дает доступ помимо наследников еще и из классов пэкеджа.".
    Так, что вопрос про строгость package-private вполне корректен.
    Сейчас исправлю сам текст.

    ОтветитьУдалить
  5. Исправил текст, в связи с найденными замечаниями.
    Изменениям подверглись вопросы №12, 14, 16

    ОтветитьУдалить
  6. Спасибо, очень полезная информация!
    Было бы неплохо продолжить список вопросов и ответов.

    ОтветитьУдалить
  7. продолжение когда будет? уж оч понравилось. многое не нужно в работе, но спрашивают на собеседованиях к сожалению, придется выучить(((

    ОтветитьУдалить
  8. Спасибо! Продолжение обязательно будет!

    ОтветитьУдалить
  9. ну где продолжение?)))

    ОтветитьУдалить
  10. 2 и 3 статьи написаны очень невнимательно а местами автор объясняет верные ответы неверными соображениями, которые видимо кроме как предложением "А мне так кажется, да и всю жизнь я так думал" не обоснованы.

    ОтветитьУдалить
  11. Вы указывайте конкретные места, где неверные рассуждения присутствуют. Писалось это год назад, а за год многое успело поменяться в голове.

    ОтветитьУдалить
  12. 13. Если у класса-родителя есть метод, объявленный как private, может ли наследник расширить его видимость?

    может через рефлекшен
    и вот так ещё может

    public class Z {

    protected class A {
    private void test() {
    System.out.println("private A.test");
    }
    }

    public class B extends A {
    public void test() {
    super.test();
    }
    }

    public static void main(String[] args) {
    new Z().new B().test();
    }
    }

    ОтветитьУдалить
  13. пример через рефлекшен
    import java.lang.reflect.Method;

    public class Z {

    public static void main(String[] args) {
    new D().test();
    }
    }

    class C {
    private void test() {
    System.out.println("private C.test()");
    }
    }

    class D extends C {
    public void test() {
    try {
    Class c = this.getClass().getSuperclass();
    Method privateMethod = c.getDeclaredMethod("test", null);
    privateMethod.setAccessible(true);
    privateMethod.invoke(c.newInstance());
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }

    ОтветитьУдалить
  14. 2 Анонимный:
    Насчет рефлекшн понятно.

    А вот насчет первого примера уже интереснее. Это не расширение видимости, а подход из серии метод-"павлик морозов". В классе B на самом деле уже другой метод, который имеет привилегию вызвать метод test() из класса A. Можно это проверить поставив @Override у метода из класса B и получив при этом ошибку компиляции. Это уже другой метод, но его сигнатура, и параметры совпадают.

    ОтветитьУдалить