• 1.28

  • +2.61

  • ,

com.javarush.task.task32.task3213 не проходит тестирование

Шифр Цезаря

Привет Амиго. Ты знаешь, за нами следят, просматривают нашу переписку. Поэтому нужно шифровать трафик.

Для тебя не составит труда реализовать шифр Цезаря, напомню что это просто сдвиг вправо по алфавиту на key букв.

В методе main есть хороший пример.

Реализуй логику метода String decode(StringReader reader, int key).

Метод получает данные в закодированном виде.

Он должен вернуть дешифрованную строку, что хранится в StringReader — е.

Возвращаемый объект ни при каких условиях не должен быть null.

Метод main не участвует в тестировании.

package com.javarush.task.task32.task3213;

import java.io.IOException;
import java.io.StringReader;

/* 
Шифр Цезаря
*/

public class Solution {
    public static void main(String[] args) throws IOException {
        StringReader reader = new StringReader("Khoor Dpljr");
        System.out.println(decode(reader, -3));  //Hello Amigo

    }

    public static String decode(StringReader reader, int key) throws IOException {

        StringBuilder sb = new StringBuilder();
        int i = 0;
        try {
            while((i=reader.read())!=-1){
                int k =  i + key;

                char c = (char)k;

                sb.append©;

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        String line = sb.toString();
        if (line == null){
            line = "ничего не пришло";
        }
        return line;
    }

}


Почему не проходит? Вывод в конце Hello Amigo. Вроде все верно
  • ,

Ответы на вопросы к собеседованию Level32

1 Зачем нужен RandomAccessFile?
RandomAccessFile — класс пакета Java IO API, он позволяет перемещаться по файлу, читать из него или писать в него, как вам будет угодно. Вы также сможете заменить существующие части файла, речь идет о обновлении содержимого файла, а точней о обновлении фрагмента файла. Это невозможно сделать с помощью FileInputStream или FileOutputStream, но RandomAccessFile даст вам эту возможность.
Ссылка: devcolibri.com/2989
2 Что будет если файл, откуда читает RandomAccessFile, не существует?
Будет FileNotFoundException
3 Что будет если файл, куда пишет RandomAccessFile, не существует?
Создаст новый и запишет в него.
4 Зачем нужен класс StringReader?
The Java.io.StringReader class is a character stream whose source is a string.
Представляет собой поток символов, чей источник называется строкой
5 Зачем нужен класс StringWriter?
public class StringWriter
extends Writer
A character stream that collects its output in a string buffer, which can then be used to construct a string.
Closing a StringWriter has no effect. The methods in this class can be called after the stream has been closed without generating an IOException.
Поток символов, собирающий свой поток в буфер строк, которые затем могут быть использованы для создания строки.
6 Зачем нужен класс ByteArrayStream?
Итак, ByteArrayInputStream и ByteArrayOutputStream.
Эти классы по сути чем-то похожи на StringReader и StringWriter. Только StringReader читал символы (char) из строки (String), а InputStream читает байты из массива байт (ByteArray).
StringWriter писал символы (char) в строку, а ByteArrayOutputStream пишет байты в массив байт у него внутри. При записи в StringWriter строка внутри него удлинялась, а при записи вByteArrayOutputStream его внутренний массив байт тоже динамически расширяется.

7 Зачем нужен класс PrintStream? Назовите места, где он используется?
Класс PrintStream был придуман для читабельного вывода информации. Он практически весь состоит из методов print и println.
8 Зачем нужен DynamicProxy?
В Java есть специальный класс (java.lang.reflect.Proxy), с помощью которого фактическиможно сконструировать объект во время исполнения программы (динамически), не создавая для него отдельного класса.
9 Как работает RMI?
RMI расшифровывается Remote Method Invokation – удаленный вызов методов. Или другими словами RMI – это механизм, который позволяет объекту в одной Java-машине вызывать методы объекта в другой Java-машине, даже если они находятся на разных компьютерах, в разных странах, на разных сторонах земного шара.
Традиционный подход к выполнению кода на других машинах, разнесенных по сети может смутить из-за своей нудной и склонной к ошибкам реализации. Лучший способ рассмотреть эту проблему состоит в предположении, что некоторые объекты располагаются на другой машине, и что вы можете посылать сообщения этим удаленным объектам и получать результат, как будто они располагаются на вашей локальной машине. Это упрощение в точности является тем, что позволяет делать Удаленный Вызов Методов (RMI) в Java.
Вот статья по созданию собственной реализации RMI:
javatutor.net/books/tiej/rmi
10 Объекты каких типов можно передавать по RMI?
Объекты должны имплементировать интерфейс Serializable
Remote method Invocation — механизм, который позволяет вызывать метод удалённого объекта. Согласно ему, все операции по подготовке и передаче данных инкапсулируются в вызываемом методе клиентского объекта-заглушки (stub). Сам же вызов метода ничем не отличается от вызова метода обычного локального объекта, за небольшим исключением:

локальные объекты передаются по значению (копии);
при передаче удалённого (Remote) объекта, если он экспортирован, передаётся stub этого объекта;
передаваемые объекты должны быть Serializable;
кроме всех прочих исключительных ситуаций, при вызове удалённого метода может возбуждаться исключение RemoteException (ошибки маршализации/демаршализации, передачи данных и другие возможные ошибки протокола);
Так же нужно отметить, что при вызове метода мы работаем с удалённым интерфейсом, а не с удалённым классом.
  • ,

Ответы на вопросы к собеседованию Level31

1 Может ли объект File соответствовать файлу, которого еще нет?
Да, если передать в конструктор значение директории.

String dirPath = "/";
File f = new File(dirPath);
File[] files = f.listFiles();

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

public class MyClass {
    public static void main(String[] args) {
        boolean isObject = false;

        
        File file = new File("/");
        if (file instanceof Object){
            isObject = true;
        }
        boolean isFile = file.isFile(); // Tests whether the file denoted by this abstract pathname is a normal file.
Это из документации
        System.out.println(isObject + " "+ isFile);

    }
}

Вывод: true false
File наследуется от object. Ответ: да
Жду комментариев.
2 Как преобразовать объект File к типу Path?
Метод toPath();
toPath(); //Returns a java.nio.file.Path object constructed from the this abstract path.

3 Зачем нужен класс Files?
Взяли за основу класс File, добавили в него немного нового, переименовывали методы, а в конце еще и разделили на два. Так что теперь есть два новых класса – Path и Files. Path – это, фактически новый аналог класса File, а Files – это утилитный класс (по аналогии с классами Arrays & Collections), в него вынесли все статические методы класса File. Так «правильнее» с точки зрения ООП.
Немного из документов:
public final class Files
extends Object
This class consists exclusively of static methods that operate on files, directories, or other types of files.
In most cases, the methods defined here will delegate to the associated file system provider to perform the file operations.

4 Какие классы для архивации вы знаете?
Неплохая статья на эту тему и выдержка из нее: crypto.pp.ua/2010/06/arxivaciya-v-java/
Для работы с архивами в спецификации Java существуют два пакета – java.util.zip и java.util.jar соответственно для архивов zip и jar. Различие форматов jar и zip заключается только в расширении архива zip. Пакет java.util.jar аналогичен пакету java.util.zip, за исключением реализации конструкторов и метода voidputNextEntry(ZipEntry e) класса JarOutputStream. Ниже будет рассмотрен только пакетjava.util.jar. Чтобы переделать все примеры на использование zip-архива, достаточно всюду
в коде заме¬нить Jar на Zip
5 Как добавить директорию в архив?
Для себя я понял этот вопрос, как добавление пустой директории в готовый архив. Никаких рабочих примеров я не нашел. Вот код: (Он наглядно показывает, что можно в архив положить любой файл, а вот с пустой директорией… я не знаю как ответить, постить на StackOverFlow не стал, за такой вопрос заминусят точно) Если у кого есть предложения, то напишите.

public class Main {
    public static void main(String[] args) {
        String[] myFiles = {"D:\\forJava\\MyArtifactName\\packForTest\\res2.txt",
                "D:\\forJava\\MyArtifactName\\packForTest\\res.txt",
                "D:\\forJava\\MyArtifactName\\packForTest\\res4.txt",
                "D:\\forJava\\MyArtifactName\\packForTest\\testDir\\"
                };
        String zipFile = "D:\\forJava\\MyArtifactName\\packForTest\\res.zip";
        ZipUtility zipUtil = new ZipUtility();
        try {
            zipUtil.zip(myFiles, zipFile);

        } catch (Exception ex) {
            // some errors occurred
            ex.printStackTrace();
        }
    }
}

Вопрос о последней testDir, ее то как раз в получившийся архив JVM не кладет, со всеми остальными txt – файлами норм получается.
ZipUtility.java:

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipUtility {

    private static final int BUFFER_SIZE = 4096;

    public void zip(List<File> listFiles, String destZipFile) throws IOException {
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(destZipFile));
        for (File file : listFiles) {
            if (file.isDirectory()) {
                zipDirectory(file, file.getName(), zos);
            } else {
                zipFile(file, zos);
            }
        }
        zos.flush();
        zos.close();
    }

    public void zip(String[] files, String destZipFile) throws IOException {
        List<File> listFiles = new ArrayList<File>();
        for (int i = 0; i < files.length; i++) {
            listFiles.add(new File(files[i]));
        }
        zip(listFiles, destZipFile);
    }

    private void zipDirectory(File folder, String parentFolder, ZipOutputStream zos) throws  IOException {
        for (File file : folder.listFiles()) {
            if (file.isDirectory()) {
                zipDirectory(file, parentFolder + "/" + file.getName(), zos);
                continue;
            }
            zos.putNextEntry(new ZipEntry(parentFolder + "/" + file.getName()));
            BufferedInputStream bis = new BufferedInputStream(
                    new FileInputStream(file));
            long bytesRead = 0;
            byte[] bytesIn = new byte[BUFFER_SIZE];
            int read = 0;
            while ((read = bis.read(bytesIn)) != -1) {
                zos.write(bytesIn, 0, read);
                bytesRead += read;
            }
            zos.closeEntry();
        }
    }

    private void zipFile(File file, ZipOutputStream zos)
            throws  IOException {
        zos.putNextEntry(new ZipEntry(file.getName()));
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
                file));
        long bytesRead = 0;
        byte[] bytesIn = new byte[BUFFER_SIZE];
        int read = 0;
        while ((read = bis.read(bytesIn)) != -1) {
            zos.write(bytesIn, 0, read);
            bytesRead += read;
        }
        zos.closeEntry();
    }
}

Код отсюда:
www.codejava.net/java-se/file-io/zip-directories

6 Зачем нужны Properties?
Properties – это файл свойств. Структура его: ключ – значение. Для работы с такими файлами в Java есть класс Properties, он унаследован от HashTable<Object, Object>
Есть статья про манипуляции с ним
www.mkyong.com/java/java-properties-file-examples/

7 В каком виде хранятся данные в файле .properties?
Ключ – значение.
8 Можно ли изменять данные в объекте Properties после загрузки их из файла?
Если он унаследован от HashMap, тогда можно, только потом нужно будет изменения в этот файл отписать. Для этого есть метод setProperty.
Вот код:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * Created by Роман on 12.09.2016.
 */
public class LoadAndSetProperties {

    public static void main(String[] args) {

        Properties prop = new Properties();
        InputStream input = null;
        try {

            input = new FileInputStream("D:\\forJava\\MyArtifactName\\packForTest\\config.properties");

            // load a properties file
            prop.load(input);

            // get the property value and print it out


            prop.setProperty("database", "ddfdfdfdfdf");
            System.out.print(prop.getProperty("database"));
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

Вывод: ddfdfdfdfdf
9 Зачем нужен класс FileReader?
Java Docs:
public class FileReader

extends InputStreamReader

Convenience class for reading character files. The constructors of this class assume that the default character encoding and the default byte-buffer size are appropriate. To specify these values yourself, construct an InputStreamReader on a FileInputStream.
FileReader is meant for reading streams of characters.
Класс для чтения символов файлов. Конструкторы этого класса предполагают, что кодировка символов дефолтная и дефолтный размер буфера являются подходящими. Чтобы задать эти значения самостоятельно, следует построить InputStreamReader над FileInputStream. FileReader предназначен для считывания потоков символов.
10 Зачем нужен класс FileWriter?
public class FileWriter
extends OutputStreamWriter

Convenience class for writing character files. The constructors of this class assume that the default character encoding and the default byte-buffer size are acceptable. To specify these values yourself, construct an OutputStreamWriter on a FileOutputStream.
Whether or not a file is available or may be created depends upon the underlying platform. Some platforms, in particular, allow a file to be opened for writing by only one FileWriter (or other file-writing object) at a time. In such situations the constructors in this class will fail if the file involved is already open.
FileWriter is meant for writing streams of characters.
Класс для записи символов файлов. Конструкторы этого класса предполагают, что кодировка символов дефолтная и дефолтный размер буфера являются приемлемым. Чтобы задать эти значения самостоятельно, следует построить OutputStreamWriter над FileOutputStream. Является ли файл доступен для записи, зависит от используемой платформы. Некоторые платформы разрешают держать файл для записи только одним FileWriter (или другого объекта записи файла), в одно время. FileWriter предназначен для записи потоков символов. Для написания потоков необработанных байтов, используйте FileOutputStream.
Эти классы (FileReader и FileWriter) специально ориентированы для работы с текстом и строками.
  • ,

добавление пустой директории в zip Архив

В общем есть такой вопрос в собеседовании к Level31
номер 5: Как добавить директорию в архив?

Для себя я этот вопрос понял, как добавление пустой директории в уже готовый zipАрхив

Вот код.

В нем текстовые файлы добавляются в заданный архив, а вот пустая папка нет, может быть кто-нибудь знает почему?

Main:

public class Main {
    public static void main(String[] args) {
        String[] myFiles = {"D:\\forJava\\MyArtifactName\\packForTest\\res2.txt",
                "D:\\forJava\\MyArtifactName\\packForTest\\res.txt",
                "D:\\forJava\\MyArtifactName\\packForTest\\res4.txt",
                "D:\\forJava\\MyArtifactName\\packForTest\\testDir"
                };
        String zipFile = "D:\\forJava\\MyArtifactName\\packForTest\\res.zip";
        ZipUtility zipUtil = new ZipUtility();
        try {
            zipUtil.zip(myFiles, zipFile);

        } catch (Exception ex) {
            // some errors occurred
            ex.printStackTrace();
        }
    }
}


ZipUtility.java:

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipUtility {

    private static final int BUFFER_SIZE = 4096;

    public void zip(List<File> listFiles, String destZipFile) throws IOException {
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(destZipFile));
        for (File file : listFiles) {
            if (file.isDirectory()) {
                zipDirectory(file, file.getName(), zos);
            } else {
                zipFile(file, zos);
            }
        }
        zos.flush();
        zos.close();
    }

    public void zip(String[] files, String destZipFile) throws IOException {
        List<File> listFiles = new ArrayList<File>();
        for (int i = 0; i < files.length; i++) {
            listFiles.add(new File(files[i]));
        }
        zip(listFiles, destZipFile);
    }

    private void zipDirectory(File folder, String parentFolder, ZipOutputStream zos) throws  IOException {
        for (File file : folder.listFiles()) {
            if (file.isDirectory()) {
                zipDirectory(file, parentFolder + "/" + file.getName(), zos);
                continue;
            }
            zos.putNextEntry(new ZipEntry(parentFolder + "/" + file.getName()));
            BufferedInputStream bis = new BufferedInputStream(
                    new FileInputStream(file));
            long bytesRead = 0;
            byte[] bytesIn = new byte[BUFFER_SIZE];
            int read = 0;
            while ((read = bis.read(bytesIn)) != -1) {
                zos.write(bytesIn, 0, read);
                bytesRead += read;
            }
            zos.closeEntry();
        }
    }

    private void zipFile(File file, ZipOutputStream zos)
            throws  IOException {
        zos.putNextEntry(new ZipEntry(file.getName()));
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
                file));
        long bytesRead = 0;
        byte[] bytesIn = new byte[BUFFER_SIZE];
        int read = 0;
        while ((read = bis.read(bytesIn)) != -1) {
            zos.write(bytesIn, 0, read);
            bytesRead += read;
        }
        zos.closeEntry();
    }
}
  • ,

Ответы на вопросы к собеседованию Level30

1 Что такое NaN?
NaN (англ. Not-a-Number) — одно из особых состояний числа с плавающей запятой. Используется во многих математических библиотеках и математических сопроцессорах. Данное состояние может возникнуть в различных случаях, например, когда предыдущая математическая операция завершилась с неопределённым результатом, или если в ячейку памяти попало не удовлетворяющее условиям число.
В соответствии с IEEE 754, такое состояние задаётся через установку показателя степени в зарезервированное значение 11…11, а мантиссы — во что угодно, кроме 0 (зарезервированное значение для машинной бесконечности). Знак и мантисса могут нести какую-то дополнительную информацию: многие библиотеки «отрицательный» NaN выводят как -NaN.
К операциям, приводящим к появлению NaN в качестве ответа, относятся:
• все математические операции, содержащие NaN в качестве одного из операндов;
• деление нуля на нуль;
• деление бесконечности на бесконечность;
• умножение нуля на бесконечность;
• сложение бесконечности с бесконечностью противоположного знака;
• вычисление квадратного корня отрицательного числа[1];
• логарифмирование отрицательного числа.
В некоторых языках программирования есть «тихий» и «сигнальный» NaN: первый, попав в любую операцию, возвращает NaN, второй — вызывает аварийную ситуацию. Обычно «тихий» или «сигнальный» определяется старшим битом мантиссы.
NaN не равен ни одному другому значению (даже самому себе[2]); соответственно, самый простой метод проверки результата на NaN — это сравнение полученной величины с самой собой.
Поведение других операций сравнения зависит от языка. Одни языки дают ложь[3] (так что a < b и b > a по-разному ведут себя с NaN), другие — выбрасывают аварию даже для «тихого» NaN.
Любая нетривиальная операция, принимающая «тихий» NaN как аргумент, всегда возвращает NaN вне зависимости от значения других аргументов. Единственными исключениями из этого правила являются функции max и min, которые возвращают значение «второго» аргумента (отличного от NaN).
Тривиальные операции, являющиеся тождеством, обрабатываются особо: так, например, 1NaN равно 1.

2 Как получить бесконечность в Java?
В Java тип double имеет специальные значения для понятий «плюс бесконечность» и «минус бесконечность». Положительное число, разделенное на 0.0, дает «плюс бесконечность», а отрицательное – «минус бесконечность».
Этим понятиям соответствуют специальные константы типа Double:
Код Описание
public static final double POSITIVE_INFINITY = 1.0 / 0.0; плюс бесконечность
public static final double NEGATIVE_INFINITY = -1.0 / 0.0; минус бесконечность
1) Строку конвертируем в число, а в ней есть буквы. Ответ – NaN
2) Бесконечность минус бесконечность. Ответ — NaN
3) Многие другие ситуации, где в ответе ждут число, а получается неизвестно что.
Любая операция, где есть NaN, дает в результате NaN.
Действия с бесконечностью:
Выражение	                Результат
n ÷ ±Infinity	                0
±Infinity × ±Infinity	        ±Infinity
±(не ноль) ÷ 0	                ±Infinity
Infinity + Infinity	        Infinity
±0 ÷ ±0	                        NaN
Infinity - Infinity	        NaN
±Infinity ÷ ±Infinity	        NaN
±Infinity × 0	                NaN


3 Как проверить, что в результате вычисления получилась бесконечность?
Есть ответ на StackOverFlow.
stackoverflow.com/questions/12952024/how-to-implement-infinity-in-java
Все сводится к выводу System.out.println()
4 Что такое битовая маска?
Битовая маска – это когда хранится много различных логических значений (true/false) в виде одного целого числа. При этом каждому boolean-значению соответствует определенный бит.
5 Где применяют битовые маски?
— В основном там, где надо компактно хранить много информации об объектах. Когда хранишь много информации об объекте, всегда наберется пара десятков логических переменных. Вот их всех удобно хранить в одном числе.
Именно хранить. Т.к. пользоваться им в работе не так уж удобно.

6 Как установить бит в единицу в битовой маске?
Опираясь на лекции можно ответить таким кодом:
Здесь использовал метод Integer.toBinaryString(), дабы проверить себя, а вдруг)))
public class BitMask {

   


    public static void main(String[] args) {
        int a = 9;

       a |= (1<<2); // установить в 1 бит 2


        System.out.println(Integer.toBinaryString(a) + " "+ a);
    }
}

Вывод такой:
1101 13


7 Как установить бит в ноль в битовой маске?
public class BitMask {

public static void main(String[] args) {
int a = 15;

a &= ~(1<<2); // установить в 0 бит 2
System.out.println(Integer.toBinaryString(a) + " "+ a);

    }
}
Вывод: 1011 11

Я взял число 15, так как на нем более наглядно видно, куда устанавливается 0.
8 Как получить значение определенного бита в битовой маске?
public class BitMask {




public static void main(String[] args) {
     int a = 15;

     a &= ~(1<<2); // установить в 0 бит 2

     int c = a & (1<<2); // узнаем про 2 бит
     int d = a & (1<<3); // узнаем про 3 бит
    System.out.println(Integer.toBinaryString(a) + " "+ a + " " + c +" "+ d);

    }
}
Вывод: 1011 11 0 8


C 0 все понятно, на том месте и вправду 0. А переменная d возвращает значение запрашиваемого бита (в 10-ой системе).
9 Что такое ленивое вычисление выражения?
Статья: www.ibm.com/developerworks/ru/library/l-lazyprog/
Это ленивые вычисления (lazy evaluation). В ленивых вычислениях ни один параметр не вычисляется, пока в нем нет необходимости. Программы фактически начинаются с конца и работают от конца к началу. Программа вычисляет, что должно быть возвращено, и продолжает движение назад, чтобы определить, какое значение для этого требуется. В сущности каждая функция вызывается с promise'ами для каждого параметра. Когда для вычисления необходимо значение, тогда выполняется promise. Поскольку код выполняется только тогда, когда необходимо значение, это называется вызов по необходимости (call-by-need). В традиционных языках программирования вместо promise'ов передаются значения, это называется вызов по значению(call-by-value).
Технология программирования «вызов по необходимости» имеет ряд преимуществ. Потоки имплементируются автоматически. Ненужные значения никогда не вычисляются. Однако, поведение ленивых программ часто трудно предсказать. В программах типа «вызов по значению» порядок вычисления довольно предсказуем, поэтому любые time- или sequence-based вычисления относительно легко имплемнтировать. В ленивых языках, где специальные конструкции, например, monads, необходимы для описания явно упорядоченных событий, это намного труднее. Все это также делает связь с другими языками более трудной.
Существуют языки программирования, например, Haskell и Clean, использующие ленивое программирование по умолчанию. Кроме того, для некоторых языков, таких как Scheme, ML и другие, существуют ленивые версии.
Иногда, откладывая вычисления до тех пор, пока не понадобится их значение, вы можете оптимизировать скорость выполнения программы или реструктурировать программу в более понятную форму. Несмотря на свою ценность, методы ленивого программирования не слишком широко используются или даже не очень известны. Подумайте о том, чтобы добавить их в ваш арсенал.

10 Чем отличается использование && и & для типа boolean?
&& — это логическое «и». (В этом случае имеют место ленивые вычисления: некоторые вычисления опускаются, когда результат и так ясен)
& — это побитовое «и» (Если применить этот оператор к переменным типа Boolean, то ленивых вычислений происходить не будет)
  • ,

Ответы на вопросы к собеседованию Level29

1 Что такое autoboxing?
Автоупаковка это механизм неявной инициализации объектов классов-оберток (Byte, Short, Character, Integer, Long, Float, Double) значениями соответствующих им исходных примитивных типов (соотв. byte, short, char, int, long, float, double), без явного использования конструктора класса.
Автоупаковка происходит при прямом присвоении примитива — классу-обертке (с помощью оператора"="), либо при передаче примитива в параметры метода (типа «класса-обертки»). Автоупаковке в «классы-обертки» могут быть подвергнуты как переменные примитивных типов, так и константы времени компиляции(литералы и final-примитивы). При этом литералы должны быть синтаксически корректными для инициализации переменной исходного примитивного типа.

Автоупаковка переменных примитивных типов требует точного соответствия типа исходного примитива — типу «класса-обертки».
Например, попытка автоупаковать переменную типа byte в Short, без предварительного явного приведения byte->short вызовет ошибку компиляции.

Автоупаковка констант примитивных типов допускает более широкие границы соответствия. В этом случае компилятор способен предварительно осуществлять неявное расширение/сужение типа примитивов. Преобразование происходит в два этапа:
1) неявное расширение(сужение) исходного типа примитива до типа примитива соответствующего классу-обертке (для преобразования int->Byte, сначала компилятор неявно сужает int в byte)
2) автоупаковку примитива в соотвествующий «класс-обертку» (компилятор автоупаковывает byte->Byte). однако в этом случае существуют два дополнительных ограничения:
a) присвоение примитива — «обертке» может производится только оператором "=" (нельзя передать такой примитив в параметры метода, без явного приведения типов)
b) тип левого операнда не должен быть старше чем Character, тип правого не дожен старше чем int, (допустимо расширение/сужение byte <-> short, byte <-> char, short <-> char
и только сужение byte < — int, short < — int, char < — int, все остальные варианты требуют явного приведения типов)

Дополнительная особенность целочисленных «классов-оберток» созданных автоупаковкой констант в диапазоне -128 +127, в то что они кэшируются JVM. Потому такие обертки с одинаковыми значениями будут являтся ссылками на один объект.
2 Зачем используется autoboxing?
Я процитирую лекцию:
Насколько ты помнишь, в Java есть как типы, унаследованные от класса Object, так и примитивные типы. Но, как оказалось, такая удобная вещь как коллекции и generic’и могут работать только с типами, унаследованными от Object.
3 Альтернативы autoboxing?
Не нашел ответа, но запостил на StackOverFlow.
stackoverflow.com/questions/39212744/what-is-the-alternative-to-autoboxing-in-java
Исходя из этой дискуссии, получается, что альтернатива autoboxing`у это использование примитивных типов, так как использовние autoboxing`a снижает производительность. Вывод: использовать autoboxing только там где это необходимо.
Написана статья про Autoboxing:
effective-java.com/2010/05/the-advantages-and-traps-of-autoboxing/
4 Типы-обертки для примитивных типов mutable или immutable?
Immutable, так как примитивные объекты тоже immutable. Чтобы работать как с Mutable типом есть класс MutableInteger, и.т.д.
5 Как примитивные типы приводятся к непримитивным аналогам?
На этот и последующий вопросы хорошо отвечает вот эта статья: info.javarush.ru/translation/2013/08/22/%D0%90%D0%B2%D1%82%D0%BE%D1%83%D0%BF%D0%B0%D0%BA%D0%BE%D0%B2%D0%BA%D0%B0-%D0%B8-%D1%80%D0%B0%D1%81%D0%BF%D0%B0%D0%BA%D0%BE%D0%B2%D0%BA%D0%B0-%D0%B2-Java-.html
Это заключение из нее:
Автоупаковка является механизмом для скрытого преобразования примитивных типов данных в соответствующие классы-оболочки(объекты). Компилятор использует метод valueOf() чтобы преобразовать примитивные типы в объекты, а методы IntValue (), doubleValue () и т.д., чтобы получить примитивные типы из объекта(То есть обратное преобразование). Автоупаковка преобразует логический тип boolean в Boolean, byte в Byte, char в Character, float в Float, int в Integer, long в Long, short в Short. Распаковка происходит в обратном направлении.
6 Как непримитивные типы приводятся к примитивным?
Выше ответил
7 Как сравниваются примитивные и непримитивные типы?
В лекции это подробно рассматривается, но я нашел так скажем тоже самое, но другими словами.
В Java есть два способа сравнивать объекты на равенство, == и метод equals().
== используется для примитивных типов.
Для объектов «==» это исключительно сравнение ссылок.
Для остальных случаев нужно использовать метод equals(). Кроме того метод hashCode() служит (в теории) для той же цели. Хорошим тоном считается, если вы переопределили equals() и hashCode(). После инициализации неких объектов a и b должно выполняться правило:
Если выражение a.equals(b) вернет true, то a.hashCode() должен быть равен b.hashCode().
8 Всегда ли создается новый объект при операции autoboxing?
Это в лекциях есть:

Когда мы присваиваем переменной типа Integer значение типа int, при этом вызывается метод Integer.valueOf:
функция valueOf не всегда создает новый объект типа Integer.
Она кэширует значения от -128 до 127.
Если передаваемое значение выходит за эти пределы, то новый объект создается, а если нет, то нет.
Если мы пишем new Integer(), то гарантированно создается новый объект.
Если мы вызываем Integer.valueOf(), явно или при autoboxing, то этот метод может вернуть для нас как новый объект, так и отдать объект из кэша, если переданное число лежит в диапазоне от -128 до 127.
9 Как работает кэширование при операции autoboxing?
Ответил в вопросе выше, на всякий случай создал вопрос на StackOverFlow, но там отвечают тоже самое:
stackoverflow.com/questions/39314971/how-cache-works-in-autoboxing-operation-java
10 Для каких типов и/или значений работает кэширование?
В восьмом вопросе. Если у кого – то есть соображения на тему трех последних вопросов, то напишите в комментариях.
  • ,

Ответы на вопросы к собеседованию Level28

1 Какие приоритеты нитей бывают?
Ответ на этот вопрос есть в лекциях JavaRush.
Для оптимизации параллельной работы нитей в Java имеется возможность устанавливать приоритеты нитей. Нити с большим приоритетом имеют преимущество в получении времени процессора перед нитями с более низким приоритетом.
Работа с приоритетами обеспечивается следующими методами класса Thread:
public final void setPriority(int newPriority)
Устанавливает приоритет нити.
public final int getPriority()
Позволяет узнать приоритет нити.
Значение параметра в методе setPriority не может произвольным. Оно должно находиться в пределах от MIN_PRIORITY до MAX_PRIORITY. При своем создании нить имеет приоритет NORM_PRIORITY.
MIN_PRIORITY = 1.
NORM_PRIORITY =5.
MAX_PRIORITY = 10.
2 Можно ли остановить нить, снизив ее приоритет до 0?
Ответ в статье: «Топ 50 вопросов на собеседовании. Тема: Многопоточность (Multithreading)»
На форуме нашел.
Есть вариант этой статьи на английском языке:
www.javacodegeeks.com/2014/07/top-50-java-thread-interview-questions-answers-for-freshers-experienced-programmers.html
Java предоставляет богатые API для всего, но, по иронии судьбы, не предоставляет удобных способов остановки нити. В JDK 1.0 было несколько управляющих методов, например stop(), suspend() и resume(), которые были помечены как deprecated в будущих релизах из-за потенциальных угроз взаимной блокировки, с тех пор разработчики Java API не предприняли попыток представить стойкий, ните-безопасный и элегантный способ остановки нитей. Программисты в основном полагаются на факт того, что нить останавливается сама, как только заканчивает выполнять методы run() или call(). Для остановки вручную, программисты пользуются преимуществом volatile boolean переменной и проверяют её значение в каждой итерации, если в методе run() есть циклы, или прерывают нити методом interrupt() для внезапной отмены заданий.
Конкретно по вопросу: Нигде не видел, чтобы кто — то приоритет выставлял в 0.
Если кто знает об этом что-нибудь, то напишите в комментариях.

3 Зачем нужен класс ThreadGroup?

ThreadGroup представляет собой набор нитей, которые так же могут содержать в себе другие группы потоков. Группа нитей образует дерево, в котором каждая другая группа нитей имеет родителя (кроме исходной). Поток имеет право доступа к данным из своей группы нитей, но не имеет такого доступа к другим группам или к родительской группе потоков.
4 В какой группе нитей состоит main-thread?
Нигде не нашел)) Подскажите где это есть))
5 Что такое паттерн ThreadPool?
На это есть выдержка из статьи на википедии:
In computer programming, the thread pool pattern (also replicated workers or worker-crew model) is where a number of threads are created to perform a number of tasks, which are usually organized in a queue. The results from the tasks being executed might also be placed in a queue, or the tasks might return no result (for example, if the task is for animation). Typically, there are many more tasks than threads. As soon as a thread completes its task, it will request the next task from the queue until all tasks have been completed. The thread can then terminate, or sleep until there are new tasks available.
The number of threads used is a parameter that can be tuned to provide the best performance. Additionally, the number of threads can be dynamic based on the number of waiting tasks. For example, a web server can add threads if numerous web page requests come in and can remove threads when those requests taper down. The cost of having a larger thread pool is increased resource usage. The algorithm used to determine when to create or destroy threads will have an impact on the overall performance:
— create too many threads, and resources are wasted and time also wasted creating any unused threads
— destroy too many threads and more time will be spent later creating them again
— creating threads too slowly might result in poor client performance (long wait times)
В компьютерном программировании есть модель пула потоков, где определенное число потоков создается для выполнения целого ряда задач, которые обычно организуются в очереди. Результаты от выполненных задач также могут быть помещены в очередь, либо задачи могут не возвращать никакого результата (например, если задача для анимации).
Как правило, существует гораздо больше задач, чем потоков. Как только поток завершит свою задачу, он будет запрашивать следующую задачу из очереди, пока все задачи не будут завершены. Поток может затем прерваться или заснуть. Количество используемых потоков, это параметр, который может быть настроен, для обеспечения наилучшей производительности. Кроме того, число потоков может быть динамическим на основе количества возникающих задач. Например, веб-сервер может добавлять потоки, если запросы многочисленных веб-страниц приходят и может удалить потоки, когда этих запросов становится меньше. С увеличением размера пула потоков увеличивается использование ресурсов компьютера. Алгоритм, используемый для определения того, когда создавать или уничтожать потоки, будет иметь влияние на общую производительность: — Создать слишком много потоков значит тратить ресурсы и время впустую.
— Уничтожить слишком много потоков и больше времени будет потрачено позже снова для их создания — Создание потоков слишком медленно, может привести к снижению производительности клиента.

6 Зачем нужен класс ThreadPoolExecutor?
public class ThreadPoolExecutor extends AbstractExecutorService
ExecutorService это выполняет каждую представленную задачу, используя один возможно из нескольких объединенных в пул потоков, обычно сконфигурированное использование Executors методы фабрики.
Пулы потоков рассматривают две различных проблемы: они обычно обеспечивают улучшенную производительность, выполняя большие количества асинхронных задач, из-за уменьшенных издержек вызова на задачу, и они обеспечивают средство ограничения и управления ресурсами, включая потоки, использованные, выполняя набор задач. Каждый ThreadPoolExecutor также поддерживает немного основной статистики, такой как число завершенных задач.
Чтобы быть полезным через широкий диапазон контекстов, этот класс обеспечивает много корректируемых параметров и рычагов расширяемости. Однако, программистов убеждают использовать более удобное Executors методы фабрики Executors.newCachedThreadPool() (неограниченный пул потоков, с автоматическим восстановлением потока), Executors.newFixedThreadPool(int) (пул потоков фиксированного размера) и Executors.newSingleThreadExecutor() (единственный фоновый поток), которые предварительно конфигурируют настройки для наиболее распространенных сценариев использования.

7 Сколько способов создать нить вы знаете?

На уровне языка есть два способа создания нити. Объект класса java.lang.Thread представляет собой нить, но ей требуется задача для исполнения, которая является объектом, реализующим интерфейс java.lang.Runnable. Так как класс Thread реализует интерфейс Runnable, вы можете переопределить метод run() унаследовав ваш класс от Thread или реализовав в нём интерфейс Runnable.

8 Для чего используется класс Future?

Future хранит результат асинхронного вычисления. Вы можете запустить вычисление, предоставив кому-либо объект Future, и забыть о нем. Владелец объекта Future может получить результат, когда он будет готов.
9 В чем преимущества Callable над Runnable?
Ссылка: www.ibm.com/developerworks/ru/library/l-java_universe_multithreading_tasks/index.html
Интерфейс Callable гораздо больше подходит для создания задач, предназначенных для параллельного выполнения, нежели интерфейс Runnable или тем более класс Thread. При этом стоит отметить, что возможность добавить подобный интерфейс появилась только начиная с версии Java 5, так как ключевая особенность интерфейса Callable – это использование параметризованных типов (generics), как показано в листинге.


Листинг Создание задачи с помощью интерфейса Callable
10	1 import java.util.concurrent.Callable;
11	2 public class CallableSample implements Callable<String>{
12	3     public String call() throws Exception {
13	4         if(какое-то условие) {
14	5             throw new IOException("error during task processing");
15	6         }
16	7         System.out.println("task is processing");
17	8         return "result ";
18	9     }
19	10 }


Сразу необходимо обратить внимание на строку 2, где указано, что интерфейс Callable является параметризованным, и его конкретная реализация – класс CallableSample, зависит от типа String. На строке 3 приведена сигнатура основного метода call в уже параметризованном варианте, так как в качестве типа возвращаемого значения также указан тип String. Фактически это означает, что была создана задача, результатом выполнения которой будет объект типа String (см. строку 8). Точно также можно создать задачу, в результате работы которой в методе call будет создаваться и возвращаться объект любого требуемого типа. Такое решение значительно удобнее по сравнению с методом run в интерфейсе Runnable, который не возвращает ничего (его возвращаемый тип –void) и поэтому приходится изобретать обходные пути, чтобы извлечь результат работы задачи.
Еще одно преимущество интерфейса Callable – это возможность «выбрасывать» исключительные ситуации, не оказывая влияния на другие выполняющиеся задачи. На строке 3 указано, что из метода может быть «выброшена» исключительная ситуация типа Exception, что фактически означает любую исключительную ситуацию, так как все исключения являются потомками java.lang.Exception. На строке 5 эта возможность используется для создания контролируемой (checked) исключительной ситуации типа IOException. Метод run интерфейса Runnable вообще не допускал выбрасывания контролируемых исключительных ситуаций, а выброс неконтролируемой (runtime) исключительной ситуации приводил к остановке потока и всего приложения.

10 Можно ли отменить выполнение задачи, если использовать класс Future?
Исходя из этой дискуссии, поднятой на хабре, выходит, что нельзя.
habrahabr.ru/post/133413/
У Future есть метод Future.cancel(boolean), который должен отменить выполнение задачи. Но если задача уже начала выполняться, вызов Future.cancel(true) на самом деле не остановит ее. В недрах реализацииFutureTask выполняется код:


if (mayInterruptIfRunning) {
Thread r = runner;
if (r != null)
r.interrupt(); }


Т.е. опять потоку, в котором выполняется задача, всего лишь рекомендуется прекратить выполнение. К тому же, мы не имеем даже возможности узнать выполняется ли задача в данный момент или нет. Есть, метод Future.isDone(), но опять мимо, он возвращает true не только когда задача завершила выполнение, а сразу после вызова Future.cancel(), даже если задача все еще выполняется (ведь Future.cancel(true) не останавливает задачу которая уже начала выполняться).

Хорошо, если мы сами пишем весь код, тогда можно в нужных местах аккуратно обрабатывать Thread.isInterrupted() и все будет ОК. Но если мы запускаем сторонний код? Если у нас есть сервер расширяемый с помощью плагинов? Какой-нибудь криво написанный плагин может запросто привести к неработоспособному состоянию весь сервер ведь мы не можем корректно прервать выполнение зависшего плагина.
  • ,

Ответы на вопросы к собеседованию Level27

1. Что такое дедлок?
Дедлок – это ситуация, когда два и более нитей заблокированы, ждущие друг друга. Дедлоком также называется взаимная блокировка.
Взаимная блокировка – это ситуация в которой, два или более процесса занимая некоторые ресурсы, пытаются заполучить некоторые другие ресурсы, занятые другими процессами и ни один из процессов не может занять необходимый им ресурс, и соответственно освободить занимаемый.
Бывают взаимные блокировки порядка синхронизации (решаются назначением порядка);
Взаимная блокировка между объектами (различные объекты пытаются получить доступ к одним и тем же синхронизированным блокам);
Ресурсная взаимоблокировка (при попытке получить доступ к некоторым ресурсам, которые может использовать одновременно только один поток).

2 Какие вы знаете стратегии, предотвращающие появление дедлоков?
Безусловно, если код написан без каких-либо ошибок, то взаимных блокировок в нем не будет. Но кто может поручиться, что его код написан без ошибок? Безусловно, тестирование помогает выявить значительную часть ошибок, но как мы уже видели ранее, ошибки в многопоточном коде нелегко диагностировать и даже после тестирования нельзя быть уверенным в отсутствии ситуаций взаимных блокировок. Можем ли мы как-то перестраховаться от блокировок? Ответ – да. Подобные техники применяются в движках баз данных, которым нередко необходимо восстанавливаться после взаимных блокировок (связанных с механизмом транзакций в БД). Интерфейс Lock и его реализации доступные в пакете java.util.concurrent.locks позволяют попытаться занять монитор, связанный с экземпляром данного класса методом tryLock (возвращает true, если удалось занять монитор).
Также есть стратегия применения открытых вызовов, то есть вызывать методы других объектов вне синхронизированного блока.
Ссылка на статью www.developersonthe.net/ru/posts/post_id/34-Vzaimnaja-blokirovkadeadlock-v-Java-i-metody-borby-s-nej/

3 Могут ли возникнуть дедлоки при использовании методов wait-notify?
Ответить на этот вопрос сложно лично мне, но прочитав в интернете разные дискуссии на эту тему, можно сказать следующее:
Дедлоков можно избежать за счет разумного использования synchronized, volatile, монитора (wait(), notify(), notifyAll()), а если копать глубже, то используя классы java.utils.concurrent: вместо обычных коллекций — многопоточные варианты (ConcurrentHashMap, например); если нужен более сложный способ синхронизации потоков — различные CyclicBarrier, CountDownLatch.
Если грамотно использовать wait – notify, то дедлоки возникнуть не должны.)))
Вот ссылка: www.quizful.net/interview/java/Deadlock

4 Что чаще используется: notify или notifyAll?
The java.lang.Object.notify() wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object's monitor by calling one of the wait methods.
This method should only be called by a thread that is the owner of this object's monitor. A thread becomes the owner of the object's monitor in one of three ways:
• By executing a synchronized instance method of that object.
• By executing the body of a synchronized statement that synchronizes on the object.
• For objects of type Class, by executing a synchronized static method of that class.
Only one thread at a time can own an object's monitor.
The java.lang.Object.notifyAll() wakes up all threads that are waiting on this object's monitor. A thread waits on an object's monitor by calling one of the wait methods.
The awakened threads will not be able to proceed until the current thread relinquishes the lock on this object. The awakened threads will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened threads enjoy no reliable privilege or disadvantage in being the next thread to lock this object.
This method should only be called by a thread that is the owner of this object's monitor.
Это отрывки из документации. Вопрос – то по большей части риторический, смотря какое приложение, в зависимости от ситуации))) Я даже не знаю, как бы я ответил. Если у кого-то есть какие-то догадки, то прошу в комментариях оставить, буду очень рад почитать.

5 Метод wait рекомендуется использовать с конструкциями if или while?
Здесь отвечу просто цитатой из сайта: www.skipy.ru/technics/synchronization.html
По поводу вызова метода wait. Это уже из разряда чистой техники. Рекомендуется вызывать wait изнутри цикла while. Т.е., писать не

if (some condition){
    obj.wait()
}


..., а

while (some condition){
    obj.wait()
}


Зачем это надо. Дело в том, что notify может вызвать кто угодно. Просто по ошибке, от которой никто не застрахован. В том случае из опыта, о котором я рассказывал выше, мы взялись за переделку именно для того, чтобы избежать такой возможности. Просто спрятали объект, на котором происходит синхронизация. И доступ к нему имел только наш код. Это хорошая практика, но не всегда возможно, к сожалению. Так вот, если поток ждет выполнения некоторого условия – вариант с while надежнее. Если поток пустили по ошибке – он опять проверит условие и, если надо, будет ждать дальше.
Кроме того, не исключена возможность и простого выхода из ожидания без вызова notify. Я честно признаюсь, что не видел этого в спецификации виртуальной машины, хотя специально искал. Но некоторые «гуру» утверждают, что VM может выйти из состояния ожидания самопроизвольно. И более того, периодически это наблюдается. Если кто-нибудь даст ссылку на соответствующую спецификацию – буду благодарен!

6 Что происходит после вызова метода notifyAll?
The java.lang.Object.notifyAll() wakes up all threads that are waiting on this object's monitor. A thread waits on an object's monitor by calling one of the wait methods.
Пробуждает все нити, которые ждали на этом мониторе.

7 Какие выгоды получает объект, если он immutable?
Нашел комментарий на: ru.stackoverflow.com/questions/432545/
Immutable объект — это объект, состояние которого после создания невозможно изменить. В случае Java это значит что все поля экземпляра у класс отмечены как final и являются примитивами или тоже immutable типами.
Пример:

public class ImmutablePoint {
    private final int x;
    private final int y;
    private final String description;

    public ImmutablePoint(int x, int y, String description) {
        this.x = x;
        this.y = y;
        this.description = description;
    }
}


После создания экземпляра ImmutablePoint его модификация невозможна.
Простейший пример immutable класса из JDK это String. Любые методы, которые вы вызовите на строке (например description.toLowerCase()) вернут новую строку, а не модифицируют исходную.
Пример mutable класс из JDK — Date. Например myDate.setHours(x) модифицирует сам экземпляр myDate!

Есть разница между immutable-объектом (то есть, неизменяемым), и final-ссылкой.
Ключевое слово final для объектных типов гарантирует неизменяемость лишь ссылки, но не самого объекта. Например, если у вас есть final-ссылка на ArrayList, вы тем не менее можете добавлять в него новые элементы или изменять существующие.
В случае же immutable-объекта объект после окончания конструктора не изменяется вообще. Одного лишь модификатора final для этого недостаточно, необходимо, чтобы все подобъекты были тоже неизменяемыми. Вы в принципе можете держать внутри ссылку на изменяемый объект, но обращаться с ним так, чтобы он не менялся.
Использование неизменяемых объектов даёт много выгод. Например, о таком объекте намного легче судить в ситуации, когда во многих частях программы есть ссылка на него (для изменяемого объекта, любая часть программы может вызвать мутирующую функцию в практически любой момент времени и из любого потока).
Но то, что для нас важно в контексте вопроса — неизменяемые объекты не требуют синхронизации при многопоточном доступе. Вот собственно и вся рекомендация: используйте неизменяемые объекты, и вам не придётся думать о том, что нужно, а что не нужно синхронизировать. Единственная возможная проблема — если вы внутри ещё не отработавшего конструктора публикуете ссылку на объект, через которую к нему может получить доступ кто-нибудь ещё, и увидеть объект в изменяющемся состоянии! (Это бывает не так уж и редко. Например, иногда программист хочет добавить объект в конструкторе в коллекцию всех объектов данного типа.)
________________________________________
Следует различать действительно неизменяемые объекты, и объекты, имеющие лишь интерфейс «только для чтения». При чтении объект тем не менее может менять свою внутреннюю структуру (например, кэшировать самый свежий запрос данных). Такие объекты не являются в строгом смысле неизменяемыми, и не могут быть использованы из разных потоков без предосторожностей. (Поэтому, если ваш объект включает другие объекты, убедитесь, что документация гарантирует их неизменяемость!)
________________________________________
Обратите внимание, что для полей неизменяемого объекта вы практически обязаны использовать final! Дело в так называемой безопасной публикации. Смотрите. Инструкции в Java-программе могут быть переставлены как оптимизатором, так и процессором (у Java достаточно слабая модель памяти). Поэтому, если не предпринимать специальных действий, окончание работы конструктора и присвоение значений полям может быть переставлено (но невидимо в рамках текущего потока)! Использование final гарантирует, что такого не произойдёт.

В случае многопоточного программирования преимущества immutable классов очевидны: после создания объекты можно передавать другим потокам и они всегда будут в актуальном состоянии. Т.е. вам не надо проверять не устарело ли состояние вашего экземпляра и не модифицировал ли его другой поток пока вы с ним работаете. Например, у вас есть метод bill(Date endDate), в нём вы наивно проверяете соответствие endDate каким-то предварительным условиям и начинаете с ней работать. В этот момент другой поток может изменить endDate, например установит её глубоко в прошлое. Последствия могут быть самыми удивительными.

8 Что такое «thread-safe»?
Опять же:
stackoverflow.com/questions/6324085/
Thread safe means that a method or class instance can be used by multiple threads at the same time without any problems occuring.
Состояние потоко-безопасности подразумевает, что метод или класс может быть использован множеством нитей без проблем столкновения, то есть дедлоков.
Consider the following method:

    private int myInt = 0;
public int AddOne()
{
    int tmp = myInt;
    tmp = tmp + 1;
    myInt = tmp;
    return tmp;
}


Now thread A and thread B both would like to execute AddOne(). but A starts first and reads the value of myInt (0) into tmp. Now for some reason the scheduler decides to halt thread A and defer execution to thread B. Thread B now also reads the value of myInt (still 0) into it's own variable tmp. Thread B finishes the entire method, so in the end myInt = 1. And 1 is returned. Now it's Thread A's turn again. Thread A continues. And adds 1 to tmp (tmp was 0 for thread A). And then saves this value in myInt. myInt is again 1.
Здесь и нить А и нить B хотят выполнить AddOne (). но А начинается первой и считывает значение myInt (0) в TMP. Теперь по некоторым причинам планировщик решает остановить поток А и отложить выполнение нити B. Поток В настоящее время также считывает значение myInt (0) в его собственной переменной TMP. Нить B завершает весь метод так, что в конце концов myInt = 1. И 1 возвращается. Поток А продолжается. И добавляет 1 к TMP (TMP 0 для нити A). А затем сохраняет это значение в myInt. myInt снова 1.
So in this case the method AddOne was called two times, but because the method was not implemented in a thread safe way the value of myInt is not 2, as expected, but 1 because the second thread read the variable myInt before the first thread finished updating it.
Так что в этом случае метод AddOne был вызван два раза, но так как этот метод не был реализован в потоке безопасным способом величина myInt не 2, как ожидалось, а 1, потому что второй поток чтения переменной myInt закончился перед первой нитью до его обновления.
Creating thread safe methods is very hard in non trivial cases. And there are quite a few techniques. In Java you can mark a method as synchronized, this means that only one thread can execute that method at a given time. The other threads wait in line. This makes a method thread safe, but if there is a lot of work to be done in a method, then this wastes a lot of time. Another technique is to 'mark only a small part of a method as synchronized' by creating a lock or semaphore, and locking this small part (usually called the critical section). There are even some methods that are implemented as lockless thread safe, which means that they are built in such a way that multiple threads can race through them at the same time without ever causing problems, this can be the case when a method only executes one atomic call. Atomic calls are calls that can't be interrupted and can only be done by one thread at a time.

Создание потоко — безопасных методов очень трудно. В Java вы можете пометить метод как синхронизированный, это будет означать, что только один поток может выполнить этот метод в данный момент времени. Другие нити, будут ждать в очереди. Это делает метод потоко-безопасным, но если много работы предстоит сделать в методе, то на это будет уходить много времени. Другой метод заключается в разметке лишь малой части метода, как синхронизированного 'путем создания локов(locks) или семафоров, и запирании этой небольшой части (обычно называемый критический раздел (critical section)). Есть даже некоторые методы, которые реализуются как беззамочные потокобезопасные (lockless thread safe), это означает, что они построены таким образом, что несколько потоков могут проходить через них в одно время и никогда не вызывают проблем, это может быть в случае, когда метод выполняет только один атомарный вызов. Атомарные вызовы это вызовы, которые не могут быть прерваны, и может быть реализованы только одним потоком.

9 Что такое «happens-before»?
Есть статья на википедии, она не конкретно про «happens-before», но все – таки.
А так-то:
«Выполняется прежде» (англ. happens before) — отношение строгого частичного порядка (арефлексивное, антисимметричное, транзитивное), введённое между атомарными командами (++ и — не атомарны!), придуманное Лесли Лэмпортом и не означающее «физически прежде». Оно значит, что вторая команда будет «в курсе» изменений, проведённых первой.

ru.wikipedia.org/wiki/%D0%9C%D0%BE%D0%B4%D0%B5%D0%BB%D1%8C_%D0%BF%D0%B0%D0%BC%D1%8F%D1%82%D0%B8_Java
В частности, одно выполняется прежде другого для таких операций (список не исчерпывающий):
• Синхронизация и мониторы:
• Захват монитора (начало synchronized, метод lock) и всё, что после него в том же потоке.
• Возврат монитора (конец synchronized, метод unlock) и всё, что перед ним в том же потоке.
• Таким образом, оптимизатор может заносить строки в синхроблок, но не наружу.
• Возврат монитора и последующий захват другим потоком.
• Запись и чтение:
• Любые зависимости по данным (то есть запись в любую переменную и последующее чтение её же) в одном потоке.
• Всё, что в том же потоке перед записью в volatile-переменную, и сама запись.
• volatile-чтение и всё, что после него в том же потоке.
• Запись в volatile-переменную и последующее считывание её же.[4][2] Таким образом, volatile-запись делает с памятью то же, что возврат монитора, а чтение — то же, что захват.[5] А значит: если один поток записал в volatile-переменную, а второй обнаружил это, всё, что предшествует записи, выполняется раньше всего, что идёт после чтения; см. иллюстрацию.
• Для объектных переменных (например, volatile List x;) столь сильные гарантии выполняются для ссылки на объект, но не для его содержимого.
• Обслуживание объекта:
• Статическая инициализация и любые действия с любыми экземплярами объектов.
• Запись в final-поля в конструкторе[6] и всё, что после конструктора. Как исключение из всеобщей транзитивности, это соотношение happens-before не соединяется транзитивно с другими правилами и поэтому может вызвать межпоточную гонку.[7]
• Любая работа с объектом и finalize().
• Обслуживание потока:
• Запуск потока и любой код в потоке.
• Зануление переменных, относящихся к потоку, и любой код в потоке.
• Код в потоке и join(); код в потоке и isAlive() == false.
• interrupt() потока и обнаружение факта останова.

10 Что такое JMM?
Java Memory Model
Вот ссылка:
docs.oracle.com/javase/specs/jls/se7/html/jls-17.html
И вот выдержка из нее:
A memory model describes, given a program and an execution trace of that program, whether the execution trace is a legal execution of the program. The Java programming language memory model works by examining each read in an execution trace and checking that the write observed by that read is valid according to certain rules.
Я понял на своем уровне что это набор правил:

Правило № 1: однопоточные программы исполняются псевдопоследовательно. Это значит: в реальности процессор может выполнять несколько операций за такт, заодно изменив их порядок, однако все зависимости по данным остаются, так что поведение не отличается от последовательного.
Правило № 2: нет невесть откуда взявшихся значений. Чтение любой переменной (кроме не-volatile long и double, для которых это правило может не выполняться) выдаст либо значение по умолчанию (ноль), либо что-то, записанное туда другой командой.
И правило № 3: остальные события выполняются по порядку, если связаны отношением строгого частичного порядка «выполняется прежде» (англ. happens before).

11 Какое исключение вылетит, если вызвать wait не в блоке synchronized?
Вот ссылка:
jsehelper.blogspot.ru/2016/01/multithreading-2.html
Основная причина вызова wait и notify из статического блока или метода в том, что Java API обязательно требует этого. Если вы вызовете их не из синхронизированного блока, ваш код выбросит IllegalMonitorStateException. Более хитрая причина в том, чтобы избежать состояния гонки между вызовами wait и notify.