410013796724260
• Webmoney
R335386147728
Z369087728698
Вопросы по Java на собеседовании (5)
Вопросы и ответы для собеседование по Java, Содержание. 1. JRE, JVM и JDKJRE (Java Runtime Environment) представляет минимальную реализацию виртуальной машины, необходимую для исполнения Java-приложений. JRE состоит из виртуальной машины JVM (Java Virtual Machine) и библиотек Java-классов, но не включает компилятор и средства разработки Java приложений. JDK (Java Development Kit) - это комплект разработчика приложений Java, включающий компилятор Java (javac), стандартные библиотеки классов Java, примеры, документацию, различные утилиты и исполнительную систему Java (JRE). JVM (Java Virtual Machine) - это виртуальная машина Java, предназначенная для исполнения Java программ и являющаяся основной частью JRE. JVM интерпретирует байт-коды Java приложений, полученные в результате компиляции исходного кода Java-программы компилятором javac. Т.е. виртуальная Java машина содержит интерпретатор байт-кода. Для повышения производительности во многих JVM дополнительно применяется JIT-компиляция, предназначенная для преобразования часто исполняемых фрагментов байт-кода в машинный код. 2. Загрузчики классов, динамическая загрузка классовJava класс может быть загружен в контекст программы каким-либо загрузчиком classloader. Виртуальные машины JVM включают хотя бы один загрузчик классов, так называемый базовый загрузчик, который загружает все основные классы из rt.jar. Базовый загрузчик никак не связан с программой. Следующий загрузчик - это загрузчик расширений, загружающий классы из $JAVA_HOME/lib/ext. Далее по иерархии идет системный загрузчик, который загружает определенные в classpath классы. Процесс загрузки классов выполняется следующим образом. Сначала системный загрузчик проверяет наличие класса в своем кэше. Если класс найден, то он успешно загружается. В противном случае управление загрузкой передается загрузчику расширений, который также проверяет свой кэш загрузок и, в случае неудачи, передает задачу базовому загрузчику. Базовый загрузчик проверяет кэш и, в случае неудачи, пытается его загрузить. Если загрузка прошла успешно - то процесс закончен, если нет, то управление возвращается загрузчику расширений. Загрузчик расширений пытается загрузить класс и, в случае неудачи, передает задачу системному загрузчику. В завершение процесса, если системный загрузчик не сможет загрузить класс, то возбуждается исключение java.lang.ClassNotFoundException. Так в Java работает загрузка классов, так называемое делегирование загрузки. Если в программе создаются пользовательские загрузчики, то они должны быть унаследованы от класса java.lang.ClassLoader. Статическая и динамическая загрузка классаСтатическая загрузка класса происходит при использовании оператора "new". Динамическая загрузка происходит в режиме "run-time" с помощью статического метода класса Class.forName(className). Подробное описание загрузчиков класса представлено здесь. 3. Области памяти Heap и StackJava Heap (куча) - это динамически распределяемая область оперативной памяти (RAM), создаваемая при старте JVM. Используется данная память для JRE классов и размещения объектов. Создание нового объекта в приложении происходит в куче. Количество выделенной памяти под объект зависит от его структуры. Так, например, для целочисленной переменной в куче будет выделено 32 бита. Любой объект, размещенный/созданный в куче, имеет глобальный доступ, и на него могут ссылаться из любой части приложения. Сборщик мусора периодически освобождает память путем удаления объектов, на которые нет каких-либо ссылок. Java Stack (стек) - это также динамически распределяемая область оперативной памяти (RAM). Однако использование данной памяти имеет существенное отличие. При каждом вызове метода в программе в памяти стека создается новый блок, который содержит примитивы и ссылки на другие объекты в методе. Стековая память работает по принципу LIFO (last-in-first-out). Вместе с окончанием работы метода заканчивается использование блока, освобождая, таким образом, доступ следующему методу. Размер стековой памяти намного меньше объема памяти в куче. Из-за простоты распределения памяти (LIFO), стековая память работает намного быстрее кучи. Размер памяти можно определить использованием соответствующих опций JVM. Так для определения начального и максимального размера памяти в куче необходимо использовать опции -Xms и -Xmx. Для определения стековой памяти следует использовать опцию -XX:MaxPermSize. Если память кучи исчерпана, то вызывается исключение java.lang.OutOfMemoryError: Java Heap Space. При отсутствии свободной памяти стека вызывается исключение java.lang.StackOverflowError. 4. Сборщик мусора Garbage CollectorСборщик мусора имеет несколько алгоритмов очистки памяти. В первую очередь, он может быть вызван, когда объем свободной памяти в области Eden Space становится критичным. В этом случае, Garbage Collector просто переносит объекты, имеющие ссылки, из области Eden Space в область Survivor Space, а объекты без ссылок удаляет. Эта, так называемая minor'ная очистка, выполняется быстро. Если при очистке мусора памяти в области Survivor Space недостаточно, то долгоживущие объекты переносятся в область Tenured Generation, где они могут хранится до конца работы приложения. Вся остальная куча очищается от мусора. Следует отметить, что сборщик мусора периодически вызывается виртуальной машиной, а не только тогда, когда не хватает памяти. Более подробное описание распределения памяти в виртуальной машине JVM и о Garbage Collector'e представлено здесь. 5. Рефлексия кода, reflectionJava Reflection API - это механизм получения информации в режиме run-time приложения. Рефлексия позволяет получать информацию о конструкторах, методах и полях классов, и выполнять следующие операции над полями и методами класса :
Интерфейс Java Reflection API состоит из классов пакетов java.lang и java.lang.reflect. 6. Определение свойств класса в режиме run-timeВ работающем приложении для получения класса используется метод forName (String className) : // Без использования Reflection Foo foo = new Foo(); // С использованием Reflection Class foo = Class.forName("Foo"); Наименование класса, включающего пакет (package), извлекается методом getName() объекта Class : Class aclass = foo.getClass(); System.out.println (aclass.getName()); Для получения значения модификатора класса используется метод getModifiers(). Класс java.lang.reflect.Modifier содержит статические методы, возвращающие логическое значения проверки модификатора класса : Class cls = foo.getClass(); int mods = cls.getModifiers(); if (Modifier.isPublic (mods)) { System.out.println("public"); } if (Modifier.isAbstract(mods)) { System.out.println("abstract");} if (Modifier.isFinal (mods)) { System.out.println("final"); } Для получения суперкласса рефлексированного объекта (класса) необходимо использовать метод getSuperclass () : Class cls = foo.getClass(); Class superCls = cls.getSuperClass(); Поскольку в Java отсутствет множественное наследование, то для получения всех предков следует рекурсивно вызвать метод getSuperclass () в цикле, пока не будет достигнут Object, являющийся родителем всех классов. Object не имеет родителей, поэтому вызов его метода getSuperclass () вернет null. 7. Определение интерфейсов и конструкторов класса в режиме run-timeДля получения в режиме run-time списка интерфейсов, реализующих классом, необходимо получить Class и использовать его метод getInterfaces(). Следующий пример демонстрирует получение списка интерфейсов класса ArrayList : Class<?> cls = ArrayList.class; Class<?>[] ifs = cls.getInterfaces(); System.out.println("List of interfaces\n"); for(Class<?> ifc : ifs) { System.out.println (ifc.getName()); } Чтобы IDE (Eclipse) не предупреждала о необходимости определения типа класса Class is a raw type. References to generic type Class<T> should be parameterized в коде используются generic'и. В консоль будут выведены следующие интерфейсы, реализуемые классом ArrayList : List of interfaces java.util.List java.util.RandomAccess java.lang.Cloneable java.io.Serializable Метод класса getConstructors () позволяет получить массив открытых конструкторов типа java.lang.reflect.Constructor. После этого, можно извлекать информацию о типах параметров конструктора и генерируемых исключениях. Пример : Class<?> cls = obj.getClass(); Constructor[] constructors = cls.getConstructors(); for (Constructor constructor : constructors) { Class<?>[] params = constructor.getParameterTypes(); for (Class<?> param : params) { System.out.println(param.getName()); } } 8. Определение полей класса в режиме run-timeМетод getFields() объекта Class возвращает массив открытых полей типа java.lang.reflect.Field. Эти поля могут быть определены не только в классе, но также и в его родителях (суперклассе), либо интерфейсах, реализованных классом или его родителями. Класс Field позволяет получить имя поля, тип и модификаторы. Class<?> cls = obj.getClass(); Field[] fields = cls.getFields(); for (Field field : fields) { Class<?> fld = field.getType(); System.out.println("Class name : " + field.getName()); System.out.println("Class type : " + fld.getName()); } Если известно наименование поля, то можно получить о нем информацию с помощью метода getField() объекта Class. Class<?> cls = obj.getClass(); Field fld = cls.getField("fieldName"); Методы getField() и getFields() возвращают только открытые члены данных класса. Чтобы получить все поля класса необходимо использовать методы getDeclaredField() и getDeclaredFields(). Данные методы работают точно также, как и их аналоги getField() и getFields(), за исключением того, что они возвращают все поля, включая закрытые и защищенные. Определение значение полей классаКласс Field содержит специализированные методы для получения значений примитивных типов: getInt(), getFloat(), getByte() и др. Для установки значения поля, используется метод set(). Для примитивных типов имеются методы setInt(), setFloat(), setByte() и др. Class<?> cls = obj.getClass(); Field field = cls.getField("fieldName"); String value = (String) field.get(obj); field.set(obj, "New value"); 9. Определение методов класса в режиме run-timeМетод getMethods() объекта Class возвращает массив открытых методов типа java.lang.reflect.Method. Эти методы могут быть определены не только в классе, но также и в его родителях (суперклассе), либо интерфейсах, реализованных классом или его родителями. Класс Method позволяет получить имя метода, тип возвращаемого им значения, типы параметров метода, модификаторы и генерируемые исключения. Class<?> cls = obj.getClass(); Method[] methods = cls.getMethods(); for (Method method : methods) { System.out.println("Method name : " + method.getName()); System.out.println("Return type : " + method.getReturnType().getName()); Class<?>[] params = method.getParameterTypes(); System.out.print("Parameters : "); for (Class<?> paramType : params) { System.out.print(" " + paramType.getName()); } System.out.println(); } Если известно имя метода и типы его параметров, то можно получить отдельный метод класса : Class<?> cls = obj.getClass(); Class[] params = new Class[] {Integer.class, String.class}; Method method = cls.getMethod("methodName", params); 10. Вызов метода класса в режиме run-timeДопустим, что имеется класс Reflect, включающий два закрытых поля id и name и несколько методов для определения значений этих полей. Нас будут интересовать только метод setData для определения значений и метод toString для печати значений. class Reflect { private String name; private int id; ... public void setData(final int id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "Reflect [ id : " + id + ", name : " + name + "]"; } } Сначала получаем метод класса. Для этого формируем массив типов параметров метода и вызываем getMethod с наименованием метода класса и списком его параметров params. После этого формируем массив новых значение полей класса args и вызываем метод invoke объекта Method с указанием объекта класса и аргументами. В заключение получаем ссылку на метод toString и распечатываем значения. Class<?> cls = reflect.getClass(); Class<?>[] params = new Class[] {int.class, String.class}; Method method = cls.getMethod("setData", params); Object[] args = new Object[] {(int) 123, new String("New value")}; method.invoke(reflect, args); System.out.println("invoke method toString()\n"); method = cls.getMethod("toString"); System.out.println(method.invoke(reflect));
Вопросы и ответы для собеседование по Java, Содержание. |