Java

Предыдущие части: Часть 1Часть 2

Данные статьи помогут легко и быстро разобраться в концепциях и программировании на Java. Даже при нулевых знаниях в Java трудностей в освоении этих материалов не возникнет. А опытные Java-разработчики смогут освежить свои знания.


Потоки

  • Поток — это небольшая исполняемая программа. В процессе может быть задействовано несколько потоков (облегченный процесс).

Способы работы с потоками:

1. Расширение класса Thread.

2. Реализация интерфейса Runnable.

public class Test extends Thread {

public void run(){

// logic

}

Thread t1 = new Test();

// Thread t1 = new Thread(new Test()); FOR RUNNABLE

t1.Start();

}
  • Лучше использовать Runnable, т.к. в нем заложена цепь множественного наследования.
  • Метод Start() вызывает run(). Но вызвать run() напрямую нельзя — этот метод не создает нового потока. Run() заставляет текущий поток выполнять тот же run(). Получается вызов прямого метода без потока.

 

Object.wait() → Блокировка потока до вызова уведомления.

Object.notify() → Пробуждает поток, ожидающий на мониторе указанного объекта, и переводит его в «работоспособное» состояние (Runnable mode).

Object.notifyAll() → Пробуждает все потоки, ожидающие на мониторе данного объекта.

Thread.yield() → Переводит текущий поток в «работоспособное» состояние и переходит к следующему потоку.

Thread.sleep() → На несколько миллисекунд переводит поток в спящее/приостановленное состояние. Время в методе sleep передается как параметр.

  • Поток-демон (Daemon) — это поток с низким приоритетом. Всегда выполняется в фоновом режиме (например, поток Garbage Collection).
  • Во избежание взаимной блокировки не синхронизируйте код с блокирующим вызовом.

Мьютекс и семафор

  • Оба используются для синхронизации потока. Семафор — это простой тип ограничивающих объектов. Для каждого семафора задаются начальное и максимальное значение. Блокировка выполняет взаимное исключение (мьютекс), но не работает с запросами и не решает проблем с очередностью (например, очередность печати или шаблон «Производитель/Потребитель»). Для этих целей есть семафор.
public class Semaphore {

int value;

// No of users that can use Resource

public Semaphore(int init) {

if (init < 0) {

init = 0;

}

value = init;

}

// Acquiring Resource

public synchronized void down() {

while (value == 0) {

try {

wait();

} catch (InterruptedException e) {

}

value — ;

}

}

// Releasing Resource

public synchronized void up() {

value++;

notify();

}

}
  • Класс Semaphore инициализируется перед методом Start(). Блокировка делается через вызов semaphoreObject.down(); для завершения задачи вызывается semaphoreObject.up().
  • Мьютекс — это ни что иное, как «Семафор взаимного исключения». Он ссылается на тип блокируемого объекта, который может одновременно принадлежать лишь одному потоку. Только поток, осуществляющий захват блокировки, может снять эту блокировку в мьютексе. Когда мьютекс заблокирован, все попытки захвата блокировки приведут к ошибке или блокировке действий, даже если они выполняются тем же самым потоком.

Обработка ошибок

  • Ошибка — это нарушение динамических связей или ручной/машинный сбой, который не должен происходить при нормальных обстоятельствах.
  • Непроверяемые исключения — это исключения в среде выполнения исполняющей программы JVM (к примеру, nullPointerException).
public class MyException extends Exception {

private String errorCode = “Unkown_Exception”;

public MyException(String message, String errorCode){

super(message);

this.errorCode = errorCode;

}

Public String getErrorCode(){

Return this.errorCode;

}

}
  • Ключевое слово throw выбрасывает исключения в среду выполнения для его дальнейшей обработки.
  • Если нужен выброс исключения без обработки, то в сигнатуру метода добавляется ключевое слово throws — так вызывающая программа находит исключения, которые выбрасываются методом.

Класс Observable и интерфейс Observer

  • Существует шаблон проектирования под названием «Наблюдатель». Довольно часто необходимо уведомить некие объекты (Observer) о происходящем в конкретном экземпляре (Observable).
  • Обычно этот шаблон используют в Ajax, чтобы уведомить несколько объектов при возникновении события (onclick по ссылке).

Как это делается:

1. Создается класс наблюдаемых — Observable.

2. Добавляется несколько классов наблюдателей — Observer.

3. Регистрируются классы Observer в объекте Observable (addObserver).

4. Вызывается notifyObserver для уведомления всех наблюдателей о произошедшем событии.

JDBC

  • JDBC — это Java API (набор классов и интерфейсов), используемый для подключения и исполнения запросов в базе данных. Подключение базы данных делается через драйвер JDBC.
  • JDBC Driver — это программный компонент, позволяющий Java-приложению взаимодействовать с базой данных.

Существуют 3 типа запросов в JDBC:

  1. Statement: повторяющаяся компиляция запроса.

2. PreparedStatement: однократная компиляция запроса. Производительность здесь в разы выше.

3. CallableStatement: исполняет процедуры и функции.

  • Класс DriverManager управляет зарегистрированными драйверами.
  • Объект ResultSet — это строка таблицы.
  • Интерфейс ResultSetMetaData возвращает даннные о таблице: общее количество колонок, их тип, название и т.д.

Пример кода:

@Resource(mappedName = “jdbc/DarmAircom”)

private javax.sql.DataSource dataSource;

Connection connection = dataSource.getConnection();

CallableStatement statement = connection.prepareCall(“call ABC.ABC_PKG.PROCEDURE_NAME(?,?,?,?,?,?,?,?,?,?,?,?,?)”);

statement.execute();

statement.close();

connection.close();

Чтение и запись файла

ByteStream — для чтения и записи бинарных данных.

CharacterStreams работает с символами, а не байтами.

FileInputStream содержит входной байт из файла и реализует входной стрим.

FileOutputStream: записывает данные в файл и реализует выходной стрим.

Чтение из файла: Общий способ чтения символов из файла — через класс FileReader. Класс BufferedReader можно обернуть вокруг любого Reader (например, FileReader) для буферизации входных данных и увеличения эффективности.

public static void readFromFile(String fileName) throws IOException {

int total = 0;

BufferedReader in = new BufferedReader( new FileReader(fileName));

for ( String s = in.readLine(); s != null; s = in.readLine() ) {

// GOT EACH LINE

}

in.close();

}

Пример названия файла: “C:/Users/chellapilla_m/Desktop/task.txt”

Запись в файл: Ниже приведен пример записи в файл через FileOutputStream.

File fout = new File(file_location_string);

FileOutputStream fos = new FileOutputStream(fout);

BufferedWriter out = new BufferedWriter(new OutputStreamWriter(fos));

out.write(“something”);

Using FileWriter:

FileWriter fstream = new FileWriter(file_location_string);

BufferedWriter out = new BufferedWriter(fstream);

out.write(“something”);

FileOutputStream нужен для записи стримов сырых байтов (raw byte). К примеру, информация об изображении. Для записи стримов из символов лучше использовать FileWriter.

Шаблоны проектирования

Шаблон «Одиночка»

  • В JVM создается по одному экземпляру класса на каждый загрузчик классов.
  • Конструктор имеет приватный модификатор доступа, поэтому нельзя создавать экземпляры вне класса.
  • Единственный метод создания экземпляров — через getInstance() с публичным модификатором доступа. Это статичный метод.
  • Примеры одиночных классов: SessionFactory и Logger.
public class Singleton {

private static Singleton instance = new Singleton();

private singleton() {}

public static Singleton getInstance(){

return instance;

}

}

Шаблон «Фабрика»

Это самый популярный шаблон проектирования в Java. Здесь мы создаем объект без демонстрации логики создания клиенту и обращаемся к недавно созданным объектам через общий интерфейс. Пример:

interface Dog {

public void speak ();

}

class Poodle implements Dog {

public void speak() {

System.out.println(“The poodle says \”arf\””);

}

}

class Rottweiler implements Dog {

public void speak() {

System.out.println(“The Rottweiler says (in a very deep voice) \”WOOF!\””);

}

}

class DogFactory {

public static Dog getDog(String criteria) {

if ( criteria.equals(“small”) )

return new Poodle();

else if ( criteria.equals(“big”) )

return new Rottweiler();

return null;

}

}

public class JavaFactoryPatternExample {

public static void main(String[] args) {

Dog dog = DogFactory.getDog(“small”);

dog.speak();

dog = DogFactory.getDog(“big”);

dog.speak();

}

}

Шаблон «Адаптер»

Обеспечивает совместную работу двух несвязанных интерфейсов. Пример:

public interface SocketAdapter {

public Volt get120Volt();

public Volt get12Volt();

public Volt get3Volt();

}

Шаблон «Строитель»

Это ответвление шаблона «Фабрика». Класс Builder создает сложный объект в несколько этапов.

Важные моменты

  • java.util.Date отображает время и дату, а java.sql.Date показывает только дату (java.sql.Time является дополнением java.sql.Date; показывает только время и расширяет java.util.Date)
  • Синхронизация — это способность контролировать доступ нескольких потоков к общим ресурсам.
  • Статичные синхронизированные методы блокируют класс. Поэтому, как только поток достигает синхронизированного статичного метода, сам класс блокируется монитором потока, и ни один поток не сможет достичь тех же методов данного класса. Это главное отличие от методов создания экземпляров класса, в которых сразу несколько потоков получают одновременный доступ к одинаковым синхронизированным методам создания экземпляров класса. Пример: public synchronized void synchronizedMethod() {}
  • Thread Dump — список всех активных потоков.
  • Утечка потоков — приложение выдает ссылки на объекты потока с ошибками, из-за чего некоторые потоки не попадают в сборщик мусора, и количество неиспользуемых потоков растает.
  • Пул потоков — это коллекция потоков с разбивкой по задачам. Используется как альтернатива созданию нового потока для каждой задачи.
  • Конструкторы не синхронизируются, т.к. другие потоки не видят новых объектов до того, как какой-либо поток не завершит их создания.
  • Метод run() и класс с Runnable можно синхронизировать. Если синхронизировать метод run(), то перед выполнением этого метода блокировка на объекте Runnable будет занята другим процессом.
  • Кластер — это группа компьютеров, которые обособлено запускают приложение. Кластеризация нужна для достижения максимальной доступности серверных приложений. Основная цель — 100% доступность или нулевое время простоя.
  • Балансировщик нагрузки — простая технология для распределения рабочей нагрузки по нескольким кластерам или машинам.
  • Переключение при отказе — переход на другую машину в случае неисправности первой.
  • JEE приложения основаны на концепции распределенных веб-приложений; обеспечивают переключение сеансов и балансировку нагрузок. Необходимо добавление опознавательного тега <distributable/> в web.xml file.
  • Java — это всегда pass-by-value (передача по значению). Самое трудно здесь — понять, что Java передает объекты как ссылки, и именно эти ссылки передаются по значению.
  • Методы Arrays.sort() используют сортировку слияния или настроенную быструю сортировку, в зависимости от типов данных. Они также переключаются на сортировку вставками, если объектов в массиве меньше семи. Таким образом, эффективность реализации повышается. Классы Collections опосредованно используют Arrays.sort.
  • 0xDEADBEEF («мертвая говядина») часто используется для обозначения аварийных сбоев в программах и взаимной блокировки встроенных систем. Изначально DEADBEEF показывал выделенные участки памяти без инициализации. При сканировании дампов памяти можно тоже увидеть DEADBEEF. Он выступает в роли «магического числа отладки» (magic debug value) в системах IBM RS/6000, Mac OS, 32-разрядных процессорах PowerPC и в Commodore Amiga. В Solaris от Sun Microsystems DEADBEEF отмечает освобожденную память ядра. OpenVMS с процессорами Alpha можно найти DEAD_BEEF, нажав комбинацию CTRL-T. В консоли DEC Alpha SRM есть специальный фоновый процесс для «отлова» ошибок памяти. Они идентифицируются компьютером как «BeefEater waiting on 0xdeadbeef».

Вопрос: Зачем нужна функция main()?

Выполнение программы начинается с метода main().

Вопрос: Почему метод main() публичный?

Так метод доступен для JVM — она начинает выполнение программы вне класса.

Вопрос: Почему main() статичный?

Для его исполнения не нужен объект.

Вопрос: Почему String args[]?

Параметры — это аргументы командной строки. Просто так вышло, что разработчик Java счел их обязательными. Никакой особой нагрузки они не несут.

Вопрос: Для чего нужен out в System.out.println()?

«out» — это объект класса PrintStream и статический член класса System, который вызывает функцию println().

Вопрос: Как в Java реализован Random?

Класс java.util.Random реализует линейный конгруэнтный метод.

Его формула:

number(i+1) = (a * number(i) + c) mod m

где m, c и a — константы, а i — выбранное начальное значение.

Вопрос: Что такое regex в Java?

Регулярные выражения (regex) определяют поисковый образ строк.

private static final String EMAIL_PATTERN =

“^[_A-Za-z0–9-\\+]+(\\.[_A-Za-z0–9-]+)*@”

+ “[A-Za-z0–9-]+(\\.[A-Za-z0–9]+)*(\\.[A-Za-z]{2,})$”;

Pattern pattern = Pattern.compile(EMAIL_PATTERN);

Matcher matcher = pattern.matcher(inputString);

boolean result = matcher.matches();
  • Ключевое слово assert используется для утверждений, т.е. выражений, которые, по мнению программиста, всегда истинны в данном участке программы. Assert помогает в тестировании и отладке.

Вопрос: Чем отличаются вложенные и внутренние классы?

Внутренними называются не статичные вложенные классы.

Вопрос: Что такое вложенный интерфейс?

Это любой интерфейс, объявленный в классе или интерфейсе. Всегда статичный по умолчанию.

Перевод статьи Madhu PathyA Beginner’s Guide to Java: Part 3 of 4