Галантерея | Руководство по новым функциям Java8

Java

Это руководство переведено сGitHub.com/winter be/Спешите…

Для получения дополнительных сухих статей, пожалуйста, обратите внимание на официальный аккаунт автора: Xiaoha Learning Java (ID: xiaoha_java), посвященный обмену сухими статьями в области Java, не ограничиваясь вопросами интервью BAT, алгоритмами, базами данных, Spring Boot, Spring Cloud & SOA, высокий параллелизм, настройка JVM, база данных, контейнер Docker, ELK, DevOps и другие связанные знания, кроме того, обратите внимание на ответ «Ресурсы», вы можете получить самые популярные ресурсы для интервью и изучения архитектуры Java в полном тексте ~

Вы также можете перейти на мой сайт, чтобы просмотреть этот учебник:Учебник по новым возможностям Java8

содержание:

Я также надеюсь, что небольшие партнеры, прошедшие эту серию руководств, смогут освоить и применять различные функции Java8, что сделает его мощным рабочим инструментом. Без лишних слов, давайте вместе начнем путешествие по новым функциям Java8!


★★★Если это руководство поможет вам, перейдите на GitHub Xiaoha, чтобы помочьStarДавай, спасибо! портал★★★


Интерфейсы позволяют добавлять методы с реализациями по умолчанию

Java 8 позволяет нам пройтиdefaultКлючевое слово обеспечивает реализацию по умолчанию для абстрактного метода, определенного в интерфейсе.

См. пример кода ниже:

// 定义一个公式接口
interface Formula {
    // 计算
    double calculate(int a);

    // 求平方根
    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}

В приведенном выше интерфейсе, в дополнение к определению абстрактного методаcalculate, также определяет метод с реализацией по умолчаниюsqrt. Когда мы реализуем этот интерфейс, нам нужно только реализоватьcalculateметод, метод по умолчаниюsqrtЕго можно вызвать напрямую, а это значит, что нам не нужно навязывать егоsqrtметод.

Дополнение: пропускdefaultНовая функция ключевого слова может легко расширить предыдущий интерфейс, и в класс реализации этого интерфейса не нужно вносить никаких изменений.

Formula formula = new Formula() {
    @Override
    public double calculate(int a) {
        return sqrt(a * 100);
    }
};

formula.calculate(100);     // 100.0
formula.sqrt(16);           // 4.0

Вышеупомянутое достигается с помощью анонимных объектовFormulaинтерфейс. Но даже в этом случае для завершенияsqrt(a * 100)Для простых вычислений я написал 6 строчек кода, что очень избыточно.

Лямбда-выражения

изучениеLambdaПеред выражениями давайте посмотрим на старый пример кода, который сортирует набор строк:

List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");

Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});

CollectionsКласс инструмента предоставляет статические методыsortметод, входным параметром являетсяListколлекция, иComparatorкомпаратор для заданногоListустановить, чтобы продолжить Сортировать. Приведенный выше пример кода создает анонимный внутренний класс в качестве входного параметра, и такого рода операции можно увидеть повсюду в нашей повседневной работе.

Эта нотация больше не рекомендуется в Java 8 в пользу лямбда-выражений:

Collections.sort(names, (String a, String b) -> {
    return b.compareTo(a);
});

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

Collections.sort(names, (String a, String b) -> b.compareTo(a));

Для блоков кода, содержащих только одну строку методов, мы можем опустить фигурные скобки и простоreturnключевой код. В погоне за ультимейтом мы также можем сделать его короче:

names.sort((a, b) -> b.compareTo(a));

ListКоллекция добавленаsortметод. И компилятор Java можетМеханизм вывода типаОпределите тип параметра, чтобы вы могли опустить тип входного параметра, как насчет этого, разве это не кажется очень мощным!

Функциональный интерфейс

Возникает вопрос: после того, как мы напишем лямбда-выражение (например, краткую форму лямбда-выражения анонимного внутреннего класса в предыдущей главе), как компилятор Java выполняет вывод типа и как он узнает, какой метод переопределить?

Следует отметить, что не каждый интерфейс можно сократить до лямбда-выражения. Только эти функциональные интерфейсы могут быть сокращены как лямбда-выражения.

Так что же такое функциональный интерфейс (Functional Interface)?

Функциональный интерфейс — это объявление, содержащее только один абстрактный метод. Все лямбда-выражения для этого типа интерфейса будут соответствовать этому абстрактному методу.

Примечание. Вам может быть интересно, не разрешено ли Java 8 добавлять методы по умолчанию к интерфейсам с помощью ключевого слова defualt? Так это абстрактный метод? Ответ - нет. Поэтому вы можете без колебаний добавлять методы по умолчанию, это не нарушает определение функционального интерфейса (Functional Interface).

Подведем итог: пока интерфейс содержит только один абстрактный метод, мы можем переписать его в виде лямбда-выражения. Чтобы гарантировать, что интерфейс явно определен как функциональный интерфейс, нам нужно аннотировать интерфейс:@FunctionalInterface. Таким образом, как только вы добавите второй абстрактный метод, компилятор немедленно выдаст ошибку.

Образец кода:

@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}

Пример кода 2:

Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted);    // 123

Примечание: приведенный выше пример кода, даже если он удален@FunctionalInterfaceЭто тоже полезно, это только ограничение.

Конструкторы и методы для удобных эталонных классов

Друзья, помните этот пример кода из предыдущей главы:

@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted);    // 123

Приведенный выше код благодаря новым функциям Java 8 еще больше упрощает приведенный выше код:

Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted);   // 123

Java 8 позволяет пройти::ключевое слово для ссылки на метод или конструктор класса. Приведенный выше код является простым примером обращения к статическим методам.Конечно, помимо статических методов, мы также можем обращаться к обычным методам:

class Something {
    String startsWith(String s) {
        return String.valueOf(s.charAt(0));
    }
}
Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted);    // "J"

Далее, давайте посмотрим, как пройти::ключевое слово для ссылки на конструктор класса. Во-первых, давайте определим пример класса и объявим два конструктора в классе:

class Person {
    String firstName;
    String lastName;

    Person() {}

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

Затем мы определяем фабричный интерфейс для генерацииPersonсвоего рода:

// Person 工厂
interface PersonFactory<P extends Person> {
    P create(String firstName, String lastName);
}

мы можем пройти::ключевое слово для цитатыPersonконструктор класса вместо ручной реализации фабричного интерфейса:

// 直接引用 Person 构造器
PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");

Person::newНа этот код можно напрямую ссылатьсяPersonконструктор класса. Затем компилятор Java может выбрать правильный конструктор для реализации на основе контекста.PersonFactory.createметод.

Lambda обращается к внешним переменным и методам интерфейса по умолчанию

В этой главе мы обсудим, как получить доступ к внешним переменным (включая: локальные переменные, переменные-члены, статические переменные, методы интерфейсов по умолчанию) в лямбда-выражении, что очень похоже на доступ к внешним переменным в анонимном внутреннем классе.

доступ к локальным переменным

В лямбда-выражении мы можем получить доступ к внешнемуfinalВведите переменные, как в следующем примере кода:

// 转换器
@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}
final int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

В отличие от анонимных внутренних классов, нам не нужно явно объявлятьnumпеременнаяfinalтип, следующий код также действителен:

int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

ноnumпеременная должна быть неявнойfinalтип, что подразумеваетсяfinalШерстяная ткань? То есть до момента компиляцииnumОбъекты не могут быть изменены, например, следующий код не может быть скомпилирован:

int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);
num = 3;

изменение внутри лямбда-выраженияnumЗначение также не может быть скомпилировано, вам нужно обратить внимание, например, на следующий пример кода:

int num = 1;
Converter<Integer, String> converter = (from) -> {
	String value = String.valueOf(from + num);
	num = 3;
	return value;
};

Доступ к переменным-членам и статическим переменным

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

    @FunctionalInterface
    interface Converter<F, T> {
        T convert(F from);
    }
class Lambda4 {
        // 静态变量
        static int outerStaticNum;
        // 成员变量
        int outerNum;

        void testScopes() {
            Converter<Integer, String> stringConverter1 = (from) -> {
                // 对成员变量赋值
                outerNum = 23;
                return String.valueOf(from);
            };

            Converter<Integer, String> stringConverter2 = (from) -> {
                // 对静态变量赋值
                outerStaticNum = 72;
                return String.valueOf(from);
            };
        }
    }

Доступ к методу интерфейса по умолчанию

Помните тот, который определен в первой главеFormula(формула) интерфейс?

@FunctionalInterface
interface Formula {
	// 计算
	double calculate(int a);

	// 求平方根
	default double sqrt(int a) {
		return Math.sqrt(a);
	}
}

В то время мы определили интерфейс с реализацией по умолчаниюsqrtМетод квадратного корня, мы можем легко получить доступ к этому методу в анонимном внутреннем классе:

Formula formula = new Formula() {
	@Override
	public double calculate(int a) {
		return sqrt(a * 100);
	}
};

Но не в лямбда-выражениях:

Formula formula = (a) -> sqrt(a * 100);

Метод интерфейса с реализацией по умолчанию, дане можетПри доступе к лямбда-выражению приведенный выше код не будет компилироваться.

Встроенный функциональный интерфейс

API JDK 1.8 включает множество встроенных функциональных интерфейсов. К ним относятся Comparator и Runnable, которые мы часто видим в старых версиях, а в Java 8 к ним добавлены аннотации @FunctionalInterface для поддержки лямбда-выражений.

Стоит отметить, что помимо Comparator и Runnable есть несколько новых функциональных интерфейсов, многие из которых позаимствованы у известныхGoogle Guavaбиблиотека.

Для них, даже если вы уже очень хорошо с ними знакомы, лучше всего знать о них:

Утверждение предиката

PredicateЭто функциональный интерфейс, который может указывать тип ввода и возвращать логическое значение. Он внутренне предоставляет некоторые методы с реализациями по умолчанию, которые могут используется для составления сложного логического суждения (and, or, negate):

Predicate<String> predicate = (s) -> s.length() > 0;

predicate.test("foo");              // true
predicate.negate().test("foo");     // false

Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;

Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();

Function

FunctionРоль функционального интерфейса заключается в том, что мы можем предоставить ему сырье, а он будет производить конечный продукт. С помощью методов по умолчанию он обеспечивает, компонует, обрабатывает цепочки строк (compose, andThen):

Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);

backToString.apply("123");     // "123"

Поставщик производитель

SupplierиFunctionДругое дело, что он не принимает входные параметры и напрямую выдает нам указанный результат, немного похоже на режим производителя:

class Person {
    String firstName;
    String lastName;

    Person() {}

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}
Supplier<Person> personSupplier = Person::new;
personSupplier.get();   // new Person

Потребитель

заConsumer, нам нужно предоставить входные параметры для использования, как показано в следующем примере кода:

class Person {
    String firstName;
    String lastName;

    Person() {}

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));

Comparator

ComparatorЭто было более распространено до Java 8. Помимо обновления до функционального интерфейса, Java 8 также расширяет для него некоторые методы по умолчанию:

Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);

Person p1 = new Person("John", "Doe");
Person p2 = new Person("Alice", "Wonderland");

comparator.compare(p1, p2);             // > 0
comparator.reversed().compare(p1, p2);  // < 0

Optional

Во-первых,OptionalЭто не функциональный интерфейс, он предназначен для предотвращения исключений нулевого указателя (NullPointerException), знайте, что в программировании на Java Исключения нулевого указателя печально известны.

Давайте быстро посмотримOptionalКак пользоваться! ты можешь поставитьOptionalрассматривается как объект-оболочка (вероятно,null, также возможно, чтоnull) контейнер. когда вы определяете Метод, объект, возвращаемый этим методом, может быть пустым или непустым, вы можете рассмотреть возможность использованияOptionalобернуть его, что также является рекомендуемой практикой в ​​Java 8.

Optional<String> optional = Optional.of("bam");

optional.isPresent();           // true
optional.get();                 // "bam"
optional.orElse("fallback");    // "bam"

optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"

Стрим поток

В этой главе мы начинаем изучатьStreamпоток.

чтоStreamпоток?

Проще говоря, мы можем использоватьjava.util.StreamВыполнять различные операции над набором из одного или нескольких элементов. Эти операции могут бытьПромежуточная операцияилитерминальная операция. Терминальные операции возвращают результат, а промежуточные операции возвращаютStreamпоток.

Следует отметить, что вы можете реализовать толькоjava.util.CollectionКлассы интерфейса выполняют потоковые операции.

Mapне поддерживаетсяStreamпоток.

StreamПотоки поддерживают синхронное выполнение, а также параллельное выполнение.

Начнем путь обучения! Идти !

Фильтр

Во-первых, мы создаемListсобирать:

List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");

FilterВходным параметром являетсяPredicate, как указано выше,PredicateЭто промежуточная операция утверждения, которая может помочь нам отфильтровать нужные нам элементы множества. Его возвращаемый параметр тот же ЯвляетсяStreamпоток, мы можем пройтиforeachТерминальная операция для печати отфильтрованных элементов:

stringCollection
    .stream()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);

// "aaa2", "aaa1"

Уведомление:foreachявляется терминальной операцией, ее возвращаемый параметр равенvoid, мы не можем передать его снова.

Сортировка

SortedЭто также промежуточная операция, и ее возвращаемый параметр —Streamпоток. В качестве альтернативы, мы можем пройти черезComparatorИспользуется для настройки сортировки, если не передано, будет использоваться правило сортировки по умолчанию.

stringCollection
    .stream()
    .sorted()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);

// "aaa1", "aaa2"

требует внимания,sortedне правильноstringCollectionвнести любые изменения,stringCollectionИли исходные элементы в том же порядке:

System.out.println(stringCollection);
// ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1

Преобразование карты

Промежуточная операцияMapможет помочь намListКаждый элемент в функции обрабатывается. Например, следующий пример,mapмы будем каждыйstringПреобразовать в верхний регистр:

stringCollection
    .stream()
    .map(String::toUpperCase)
    .sorted((a, b) -> b.compareTo(a))
    .forEach(System.out::println);

// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"

Кроме того, мы также можем выполнять преобразование между объектами, которое чаще используется в бизнесе.DO(объект базы данных) преобразуется вBO(хозяйственный объект).

Совпадение

Как следует из названия,matchИспользуется для операций сопоставления, его возвращаемое значение представляет собойbooleanтип. пройти черезmatch, легко убедиться, чтоlistСуществует ли элемент определенного типа в .

// 验证 list 中 string 是否有以 a 开头的, 匹配到第一个,即返回 true
boolean anyStartsWithA =
    stringCollection
        .stream()
        .anyMatch((s) -> s.startsWith("a"));

System.out.println(anyStartsWithA);      // true

// 验证 list 中 string 是否都是以 a 开头的
boolean allStartsWithA =
    stringCollection
        .stream()
        .allMatch((s) -> s.startsWith("a"));

System.out.println(allStartsWithA);      // false

// 验证 list 中 string 是否都不是以 z 开头的,
boolean noneStartsWithZ =
    stringCollection
        .stream()
        .noneMatch((s) -> s.startsWith("z"));

System.out.println(noneStartsWithZ);      // true

Считать количество

countявляется терминальной операцией, она может считатьstreamОбщее количество элементов в потоке, возвращаемое значение равноlongтип.

// 先对 list 中字符串开头为 b 进行过滤,让后统计数量
long startsWithB =
    stringCollection
        .stream()
        .filter((s) -> s.startsWith("b"))
        .count();

System.out.println(startsWithB);    // 3

Reduce

ReduceКитайский перевод:уменьшать, уменьшать. введяFunction, мы можем положитьlistУменьшить до значения. Его возвращаемый типOptionalтип.

Optional<String> reduced =
    stringCollection
        .stream()
        .sorted()
        .reduce((s1, s2) -> s1 + "#" + s2);

reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"

Параллельные потоки Параллельные потоки

Мы говорили в предыдущей главе,streamпоток поддерживаетсяприказипараллельноиз. Последовательные потоковые операции являются однопоточными операциями, тогда как параллельные потоки обрабатываются несколькими потоками, в полной мере используя физические машины. Преимущества многоядерных процессоров при более быстрой обработке.

Сначала мы создаем коллекцию списков, содержащую 1 000 000 UUID.

int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
    UUID uuid = UUID.randomUUID();
    values.add(uuid.toString());
}

Отсортируйте этот список по последовательному потоку и параллельному потоку соответственно и измерьте потребление времени:

сортировка потока последовательности

// 纳秒
long t0 = System.nanoTime();

long count = values.stream().sorted().count();
System.out.println(count);

long t1 = System.nanoTime();

// 纳秒转微秒
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("顺序流排序耗时: %d ms", millis));

// 顺序流排序耗时: 899 ms

Сортировка параллельного потока

// 纳秒
long t0 = System.nanoTime();

long count = values.parallelStream().sorted().count();
System.out.println(count);

long t1 = System.nanoTime();

// 纳秒转微秒
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("并行流排序耗时: %d ms", millis));

// 并行流排序耗时: 472 ms

Как видите, при той же логической обработке с параллельными потоками наша производительность увеличивается почти на50%. Чтобы все это осуществить, нам нужно всего лишьstreamизменился наparallelStream.

Коллекция карт

упомянутый ранееMapне поддерживаетсяStreamтечь, потому чтоMapинтерфейс не такойCollectionинтерфейс, определяющийstream()метод. Однако мы можемkey, values, entryиспользовать потоковые операции, такие какmap.keySet().stream(), map.values().stream()иmap.entrySet().stream().

Кроме того, в JDK 8mapПредусмотрены некоторые другие новые функции:

Map<Integer, String> map = new HashMap<>();

for (int i = 0; i < 10; i++) {
    // 与老版不同的是,putIfAbent() 方法在 put 之前,
    // 会判断 key 是否已经存在,存在则直接返回 value, 否则 put, 再返回 value
    map.putIfAbsent(i, "val" + i);
}

// forEach 可以很方便地对 map 进行遍历操作
map.forEach((key, value) -> System.out.println(value));

кроме вышеперечисленногоputIfAbsent()иforEach()Кроме того, мы также можем легкоkeyВыполните связанные операции со значением:

// computeIfPresent(), 当 key 存在时,才会做相关处理
// 如下:对 key 为 3 的值,内部会先判断值是否存在,存在,则做 value + key 的拼接操作
map.computeIfPresent(3, (num, val) -> val + num);
map.get(3);             // val33

// 先判断 key 为 9 的元素是否存在,存在,则做删除操作
map.computeIfPresent(9, (num, val) -> null);
map.containsKey(9);     // false

// computeIfAbsent(), 当 key 不存在时,才会做相关处理
// 如下:先判断 key 为 23 的元素是否存在,不存在,则添加
map.computeIfAbsent(23, num -> "val" + num);
map.containsKey(23);    // true

// 先判断 key 为 3 的元素是否存在,存在,则不做任何处理
map.computeIfAbsent(3, num -> "bam");
map.get(3);             // val33

Что касается операций удаления, JDK 8 предоставляет новую возможность:remove() API:

map.remove(3, "val3");
map.get(3);             // val33

map.remove(3, "val33");
map.get(3);             // null

Как и в приведенном выше коде, только когда данныйkeyиvalueОперация удаления будет выполняться только при наличии точного совпадения.

Что касается метода добавления, JDK 8 предоставляет метод со значениями по умолчанию.getOrDefault()метод:

// 若 key 42 不存在,则返回 not found
map.getOrDefault(42, "not found");  // not found

заvalueОперация слияния также становится проще:

// merge 方法,会先判断进行合并的 key 是否存在,不存在,则会添加元素
map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9

// 若 key 的元素存在,则对 value 执行拼接操作
map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9concat

новый API даты

Пакеты в Java 8java.timeДобавлен новый API даты в разделе .It иJoda-TimeБиблиотеки похожи, но не идентичны. Далее я представлю новый API с примерами кода. Наиболее важные характеристики:

Clock

ClockПредоставляет доступ к текущей дате и времени. мы можем использовать его вместоSystem.currentTimeMillis()метод. Кроме того, поclock.instant()в состоянии получитьinstantпример, Этот экземпляр можно легко преобразовать в старую версиюjava.util.Dateобъект.

Clock clock = Clock.systemDefaultZone();
long millis = clock.millis();

Instant instant = clock.instant();
Date legacyDate = Date.from(instant);   // 老版本 java.util.Date

Часовые пояса

ZoneIdПредставляет класс часового пояса. Его легко получить с помощью статического фабричного метода, и мы можем передать код определенного часового пояса. Кроме того, класс часового пояса также определяет смещение, которое будет использоваться в текущий момент или в определенное время. Преобразование в и из времени целевого часового пояса.

System.out.println(ZoneId.getAvailableZoneIds());
// prints all available timezone ids

ZoneId zone1 = ZoneId.of("Europe/Berlin");
ZoneId zone2 = ZoneId.of("Brazil/East");
System.out.println(zone1.getRules());
System.out.println(zone2.getRules());

// ZoneRules[currentStandardOffset=+01:00]
// ZoneRules[currentStandardOffset=-03:00]

LocalTime

LocalTimeПредставляет класс времени без указания часового пояса, например,10 p.m.или17:30:15, в следующем примере кода будет использоваться созданный выше Объект часового пояса создает дваLocalTime. Затем мы сравниваем два времени и вычисляем разницу в часах и минутах между ними.

LocalTime now1 = LocalTime.now(zone1);
LocalTime now2 = LocalTime.now(zone2);

System.out.println(now1.isBefore(now2));  // false

long hoursBetween = ChronoUnit.HOURS.between(now1, now2);
long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);

System.out.println(hoursBetween);       // -3
System.out.println(minutesBetween);     // -239

LocalTimeПредоставляет несколько статических фабричных методов для упрощения создания и работы с экземплярами объекта времени, включая операцию синтаксического анализа строк времени.

LocalTime late = LocalTime.of(23, 59, 59);
System.out.println(late);       // 23:59:59

DateTimeFormatter germanFormatter =
    DateTimeFormatter
        .ofLocalizedTime(FormatStyle.SHORT)
        .withLocale(Locale.GERMAN);

LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);
System.out.println(leetTime);   // 13:37

LocalDate

LocalDateявляется объектом даты, например:2014-03-11. это иLocalTimeто жеfinalтип объекта. В следующем примере показано, как вычислить новую дату путем сложения и вычитания дней, месяцев, лет и т. д.

LocalDate, LocalTime, потому чтоfinalОбъект типа, который возвращает новый объект времени для каждой операции.

LocalDate today = LocalDate.now();
// 今天加一天
LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);
// 明天减两天
LocalDate yesterday = tomorrow.minusDays(2);

// 2014 年七月的第四天
LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);
DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();
System.out.println(dayOfWeek);    // 星期五

Вы также можете проанализировать строку даты напрямую, чтобы сгенерироватьLocalDateпример. (иLocalTimeоперация так же проста, как

DateTimeFormatter germanFormatter =
    DateTimeFormatter
        .ofLocalizedDate(FormatStyle.MEDIUM)
        .withLocale(Locale.GERMAN);

LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);
System.out.println(xmas);   // 2014-12-24

LocalDateTime

LocalDateTimeЯвляетсядата-времяобъект. Вы также можете думать об этом какLocalDateиLocalTimeкомбинация. В эксплуатации примерно так же.

LocalDateTimeтот самыйfinalтип объекта.

LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);

DayOfWeek dayOfWeek = sylvester.getDayOfWeek();
System.out.println(dayOfWeek);      // 星期三

Month month = sylvester.getMonth();
System.out.println(month);          // 十二月

// 获取改时间是该天中的第几分钟
long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);
System.out.println(minuteOfDay);    // 1439

Если информация о часовом поясе добавлена,LocalDateTimeтакже может быть преобразован вInstanceпример.Instanceможно конвертировать в старые версииjava.util.Dateобъект.

Instant instant = sylvester
        .atZone(ZoneId.systemDefault())
        .toInstant();

Date legacyDate = Date.from(instant);
System.out.println(legacyDate);     // Wed Dec 31 23:59:59 CET 2014

форматLocalDateTimeОбъекты форматируются как LocalDate или LocalTime. Помимо использования предопределенных форматов, вы также можете настроить форматированный вывод.

DateTimeFormatter formatter =
    DateTimeFormatter
        .ofPattern("MMM dd, yyyy - HH:mm");

LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);
String string = formatter.format(parsed);
System.out.println(string);     // Nov 03, 2014 - 07:13

Примечание: иjava.text.NumberFormatдругой, новыйDateTimeFormatterклассfinalтип, но и потокобезопасный. Для получения более подробной информации см.здесь

Аннотации

В Java 8 аннотации повторяются. Позвольте мне просмотреть приведенный ниже пример кода, чтобы увидеть, что происходит.

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

@interface Hints {
    Hint[] value();
}

@Repeatable(Hints.class)
@interface Hint {
    String value();
}

В Java 8 через@Repeatable, что позволяет нам использовать несколько аннотаций в одном классе:

Первая форма: использовать контейнер аннотаций (старый метод)

@Hints({@Hint("hint1"), @Hint("hint2")})
class Person {}

Вторая форма: использование повторяющихся аннотаций (новый метод)

@Hint("hint1")
@Hint("hint2")
class Person {}

Используя вторую форму, компилятор Java может автоматически@HintСделайте настройки. Это очень важно, когда вам нужно прочитать информацию аннотации через отражение.

Hint hint = Person.class.getAnnotation(Hint.class);
System.out.println(hint);                   // null

Hints hints1 = Person.class.getAnnotation(Hints.class);
System.out.println(hints1.value().length);  // 2

Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);
System.out.println(hints2.length);          // 2

Хотя мы никогда не будемPersonобъявление класса@HintsАннотация, но ее информация по-прежнему доступна черезgetAnnotation(Hints.class)читать. и,getAnnotationsByTypeметод был бы более удобным, потому что он дает все@HintsПрямой доступ к аннотированным методам.

@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@interface MyAnnotation {}

Эпилог

На этом руководство по программированию для новых функций Java 8 завершено. Конечно, многое еще предстоит изучить и объяснить. Это требует от читателей дальнейшего изучения JDK 8, Например:Arrays.parallelSort, StampedLockиCompletableFutureПодожди, я здесь только для того, чтобы сыграть роль в привлечении Джейд.

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

Записи блога о расширении Java8

Бесплатный обмен | Интервью и учебные ресурсы по социальному обеспечению

Недавно я нашел в Интернете хороший ресурс в формате PDF «Java Core Knowledge & Interview.pdf», чтобы поделиться с вами не только интервью, но и изучением, вы все стоит того! ! !

Как получить: Подпишитесь на официальный аккаунт:Сяоха изучает Java, закулисный ответресурс, либоБесплатный нерегулярный доступ к ссылкам ресурсов, вот каталог и несколько скриншотов:

关注微信公众号【小哈学Java】,回复【资源】,即可免费无套路领取资源链接哦
Подпишитесь на общедоступную учетную запись WeChat [Xiao Ha Learn Java], ответьте на [Ресурсы], вы можете получить ссылку на ресурс бесплатно без каких-либо процедур.

关注微信公众号【小哈学Java】,回复【资源】,即可免费无套路领取资源链接哦
Подпишитесь на общедоступную учетную запись WeChat [Xiao Ha Learn Java], ответьте на [Ресурсы], вы можете получить ссылку на ресурс бесплатно без каких-либо процедур.

关注微信公众号【小哈学Java】,回复【资源】,即可免费无套路领取资源链接哦
Подпишитесь на общедоступную учетную запись WeChat [Xiao Ha Learn Java], ответьте на [Ресурсы], вы можете получить ссылку на ресурс бесплатно без каких-либо процедур.

关注微信公众号【小哈学Java】,回复【资源】,即可免费无套路领取资源链接哦
Подпишитесь на общедоступную учетную запись WeChat [Xiao Ha Learn Java], ответьте на [Ресурсы], вы можете получить ссылку на ресурс бесплатно без каких-либо процедур.

关注微信公众号【小哈学Java】,回复【资源】,即可免费无套路领取资源链接哦
Подпишитесь на общедоступную учетную запись WeChat [Xiao Ha Learn Java], ответьте на [Ресурсы], вы можете получить ссылку на ресурс бесплатно без каких-либо процедур.

关注微信公众号【小哈学Java】,回复【资源】,即可免费无套路领取资源链接哦
Подпишитесь на общедоступную учетную запись WeChat [Xiao Ha Learn Java], ответьте на [Ресурсы], вы можете получить ссылку на ресурс бесплатно без каких-либо процедур.

Важные вещи, которые нужно сказать дважды, обратите внимание на общедоступный номер:Сяоха изучает Java, закулисный ответресурс, либоБесплатный нерегулярный доступ к ссылкам ресурсов! ! !

Добро пожаловать в публичный аккаунт WeChat: Сяоха изучает Java

关注微信公众号【小哈学Java】,回复【资源】,即可免费无套路领取资源链接哦
Подпишитесь на общедоступную учетную запись WeChat [Xiao Ha Learn Java], ответьте на [Ресурсы], вы можете получить ссылку на ресурс бесплатно без каких-либо процедур.