среда, 29 октября 2014 г.

Технология Java

Технология Java включает в себя язык программирования и платформу.

Язык программирования Java.


Java - это высокоуровневый объектно-ориентированный язык программирования, который можно охарактеризовать следующими особенностями:
1. Простой
2. Обьектно-ориентированный
3. Кроссплатформенный
4. Безопасный
5. Надежный (Robust в оригинале)
6. Не зависящий от архитектуры
7. Портативный
8. Динамический
9. Интерпретируемый
10. Высоко производительный
11. Многопоточный
12. Распределенный

Простой
Язык Java простой, по тому что:
- синтаксис базируется на С++ (его легко изучать после C++)
- убрано много спорных и редко-используемых особенностей (указатели, перегрузка операторов итп.)
- не нужно заботиться о неиспользуемых объектах, т.к. в java присутствует автоматическая сборка мусора


Объектно-ориентированный
Это значит что мы организуем продукт, как комбинацию различных типов объектов, которые включают в себя данные и поведение.
ООП - это методология, которая упрощает разработку и обслуживание ПО, предоставляя некоторые правила.
Основные понятия ООП:
1. Объект
2. Класс
3. Наследование
4. Полиморфизм
5. Абстракция
6. Инкапсуляция


Кроссплатформенный
Java код может быть запущен на любой платформе (Windows, linux, Solaris, Mac/OS итп). Java код компилируется в платформонезависимый байт-код. Это отвечает принципу Write Once and Run Anywhere(WORA).


Безопасный
Java безопасна по тому что:
- нет указателей
- программы запускаются в песочнице виртуальной машины
- загрузчик классов (classloader)
- верификатор байт-кода - проверяет фрагменты кода, который может нарушить права доступа к объектам.
- менеджер безопасности - определяет, какие ресурсы класс может получить на чтение, запись итп.


Устойчивый
Java использует мощное управление памятью. Это отсутствие указателей, которые избегают проблем с безопасностью, автоматический сборщик мусора, отлавливание исключительных ситуаций и механизм проверки типов. Все это делает Java устойчивым языком.


Независимый от архитектуры
Это независящие от архитектуры системы особенности, например размер примитивных типов.


Портативный
Можно переносить байт-код Java на любую платформу
Высокопроизводительный
Java работает быстрее, чем традиционная интерпретация, по тому-что байт-код ближе к нативному коду, но все-же Java немного медленнее, чем компилируемые языки (С++ итп)


Распределенный
Мы можем создавать распределенные приложения в Java. RMI и EJB используются для создания таких приложений. Мы можем получить доступ к файлам вызывая методы с любой машины в интернете.


Многопоточный
Можно писать программы на Java, которые занимаются многими задачами одновременно определим несколько потоков. Главное преимущество многопоточности это разделение потоками общей памяти.


В языке Java весь исходный код сначала пишется в текстовые файлы с расширением .java. Затем исходные файлы компилируются в .class файлы компилятором javac. Эти .class файлы не содержат инструкций для процессора, вместо них они содержат байт-код - набор инструкций исполняемых виртуальной машиной Java (JVM). Виртуальная машина JVM исполняет файлы .class или .jar (упакованный в архив набор .class файлов), эмулируя инструкции, написанные для JVM, путем интерпретирования или использования just-in-time компилятора (JIT), такого, как HotSpot. Сейчас JIT компиляция используется в большинстве JVM в целях достижения большей скорости. Существуют также ahead-of-time компиляторы, позволяющие разработчикам приложений прекомпилировать файлы классов в родной для конкретной платформы код.
Как и Большинство виртуальных машин, Java Virtual Machine имеет stack-ориентированную архитектуру, свойственную микроконтроллерам и микропроцессорам.
JVM, которая является экземпляром JRE (Java Runtime Environment), вступает в действие при исполнении программ Java. После завершения исполнения, этот экземпляр удаляется сборщиком мусора. JIT является частью виртуальной машины Java, которая используется для ускорения выполнения приложений. JIT одновременно компилирует части байт-кода, которые имеют аналогичную функциональность, и, следовательно, уменьшает количество времени, необходимого для компиляции.

Платформа Java


Платформой является программная или аппаратная среда, в которой программа работает. Популярными платформами являются Microsoft Windows, Linux, Solaris OS, and Mac OS. Большинство платформ могут быть описаны в виде комбинации операционной системы и используемого оборудования. Платформа Java отличается от большинства других платформ в том, что это только программная платформа которая запускается поверх других аппаратных платформ.

Платформа Java состоит из 2х компонент:
- Виртуальная машина Java (JVM)
- Java Application Programming Interface (Java API)

JVM - это основа платформы Java и она портирована на различные аппаратные платформы.

Java API - это большая коллекция готовых программных компонентом, которые обеспечивают множество полезных возможностей. Они сгруппированы в библиотеки смежных классов и интерфейсов. Эти библиотеки известны как пакеты. 
Как платформонезависимая среда, платформа Java может быть немного медленнее, чем машинный код, однако прогресс в компиляторе и технологиях виртуальной машины приносит производительность, близкую к машинному коду не теряя кроссплатформенности.

понедельник, 27 октября 2014 г.

Класс Object в Java

Класс Object, из пакета java.lang, находится на вершине дерева иерархии классов. Каждый класс является потомком, прямым или косвенным, класса Object. Любой использованный или написанный нами класс наследует методы этого класса. Благодаря этому мы можем сослаться на объект, тип которого нам не известен.

Исходный код данного класса из OpenJDK можно посмотреть тут.


Разберемся по порядку с методами этого класса.

private static native void registerNatives();

Обычно, для того, чтобы JVM нашла нативные функции, они должны быть названы определенным образом. Например для java.lang.Object.registerNatives, соответствующая функция на С должна называться Java_java_lang_Object_registerNatives. Используя JNI функцию registerNatives можно именовать наши С функции как захочется.

Код на С :

static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
    methods, sizeof(methods)/sizeof(methods[0]));
}

Следует заметить, что Object.getClass нет в этом списке. Это значит что он будет вызываться со стандартным именем Java_java_lang_Object_getClass.
Регистрация нативных функций также полезна если мы встраиваем Java в программу на С и хотим ссылаться на функции своего приложения, а не на функции из общей библиотеки или хотим привязать нативные методы к другим функциям на С.

Далее идет статичный конструктор.
    static {
        registerNatives(); 
    }   


public final native Class<?> getClass();

Этот метод нельзя переопределить.
Возвращает класс объекта, методы которого можно использовать для получения информации об этом классе, например его имя (getSimpleName()), его суперкласс (getSuperClass()), и реализуемых интерфейсах (getInterfaces()).
Например, следующий метод выведет имя класса объекта:

void printClassName(Object obj) {
    System.out.println("The object's" + " class is " +
        obj.getClass().getSimpleName());
}



public native int hashCode();

Возвращает хэш-код объекта. Этот метод поддерживается в интересах хэш-таблиц (таких как HashMap, например).

По умолчанию, если два объекта равны, их хэш-коды должны быть также равны. Если переопределить метод equals(), реализация метода hashCode() у Object перестает быть корректной. Поэтому, если мы переопределяем метод equals(), мы должны переопределить метод hashCode().

В доках Oracle написано: нативный метод реализован так, что возвращает адрес объекта в памяти.
В openJDK 7 для HotSpot JVM он основан на генераторе случайных чисел. Смотреть метод get_next_hash.

Но также можно выбрать другой режим, в том числе и адрес в памяти.

С помощью ключа командной строки -XX:hashCode=n (где n от 0 до 5).
0 – Park-Miller RNG (по умолчанию)
1 – f(адрес, глобальное_состояние)
2 – константа 1
3 – последовательный счетчик
4 – адрес объекта
5 – Thread-local Xorshift


public boolean equals(Object obj) {
    return (this == obj);
}

Метод сравнивает два объекта и возвращает true, если они равны. Метод equals(), реализованный в классе Object использует тождественный оператор (==), чтобы определить, являются ли два объекта равными. Для примитивных типов данных это корректно. Для объектов метод проверяет только то, ссылки указывают на один и тот-же объект.
Всегда надо переопределять метод equals(), если сравнение тождественным оператором не подходит для объекта.
Переопределив метод equals() обязывает разработчика переопределить метод hashCode().


protected native Object clone() throws CloneNotSupportedException;

Если класс или один из его суперклассов реализуют интерфейс Cloneable, то можно использовать метод clone() для создания копии существующего объекта.
Реализация этого метода у Object проверяет, реализует ли объект у которого clone() был вызван интерфейс Cloneable(). Если нет, то выбрасывается  CloneNotSupportedException. Если да, то создается объект того же класса, как исходный и его поля инициализируется теми же значениями.
Для некоторых классов стандартное поведение метода clone() работает отлично. Но если объект содержит ссылку на внешний объект, то возможно потребуется переопределить поведение метода clone(). В противном случае изменение внешнего объекта в одном классе приведет к его изменению в другом (полученном клонированием) классе.


public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

Возвращает строковое представление объекта. При переопределении метода рекомендуется возвращать краткий но информативный результат. Для класса Object метод toString() возвращает строку, содержащую имя класса, экземпляром которого является объект и шестнадцатеричное представление хэщ-кода объекта, разделенные символом "@".


Методы notify(), notifyAll(), wait() объекта участвуют в синхронизации независимо запущенных потоков в программе.
public final native void notify();

public final native void notifyAll();

public final native void wait(long timeout) throws InterruptedException;

public final void wait(long timeout, int nanos) throws InterruptedException {
    if (timeout < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    if (nanos < 0 || nanos > 999999) {
       throw new IllegalArgumentException(
                            "nanosecond timeout value out of range");
    }
    if (nanos >= 500000 || (nanos != 0 && timeout == 0)) {
        timeout++;
    }
    wait(timeout);
}

public final void wait() throws InterruptedException {
    wait(0);
}


protected void finalize() throws Throwable { }

Класс Object предоставляет callback метод finalize(), который может вызываться для объекта когда он становится "мусором". Реализация finalize() у Object не делает ничего. Его можно переопределить для очистки, нарпимер свободных ресурсов.

Метод finalize() может быть вызван системой автоматически, но тот момент когда он будет вызван никак не определен. По этому нельзя пологаться на этот метод, что-бы сделать свою чистку. Следует заметить, что если объект доступен для сборки и в нем переопределен метод finalize(), то он не вызовется сразу, а поместится в очередь, которая обрабатывается специально созданным для этого потоком.

Есть вероятность, что этот метод не будет вызван совсем. Это может произойти в момент, когда объект уже станет доступным для сборщика мусора и программа завершит свою работу.

Краткая выдержка про использование finalize (Джошуа Блох):
1. finalize() можно использовать только в двух случаях:
1.1. Проверка/подчистка ресурсов с логированием
1.2. При работе с нативным кодом, который не критичен к утечке ресурсов
2. finalize() замедляет работу GC по очистке объекта в 430 раз
3. finalize() может быть не вызван