Это руководство переведено сGitHub.com/winter be/Спешите…
Для получения дополнительных сухих статей, пожалуйста, обратите внимание на официальный аккаунт автора: Xiaoha Learning Java (ID: xiaoha_java), посвященный обмену сухими статьями в области Java, не ограничиваясь вопросами интервью BAT, алгоритмами, базами данных, Spring Boot, Spring Cloud & SOA, высокий параллелизм, настройка JVM, база данных, контейнер Docker, ELK, DevOps и другие связанные знания, кроме того, обратите внимание на ответ «Ресурсы», вы можете получить самые популярные ресурсы для интервью и изучения архитектуры Java в полном тексте ~
Вы также можете перейти на мой сайт, чтобы просмотреть этот учебник:Учебник по новым возможностям Java8
содержание:
- 1. Методы, позволяющие добавлять реализации по умолчанию в интерфейс
- 2. Лямбда-выражения
- 3. Функциональный интерфейс
- Четыре удобных конструктора и метода ссылочного класса
- 5. Lambda обращается к внешним переменным и методам интерфейса по умолчанию
- 6. Встроенный функциональный интерфейс
- 7. Дополнительно
- Восемь, Streams поток
- Девять, параллельные потоки, параллельный поток
- 10. Коллекция карт
- 11. Новый API дат
- 12. Аннотации
- Тринадцать, блог расширения 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
- Как кокетливо избегать исключений нулевого указателя в Java 8
- Параллелизм новых возможностей Java8 (1) | Потоки и исполнители
Бесплатный обмен | Интервью и учебные ресурсы по социальному обеспечению
Недавно я нашел в Интернете хороший ресурс в формате PDF «Java Core Knowledge & Interview.pdf», чтобы поделиться с вами не только интервью, но и изучением, вы все стоит того! ! !
Как получить: Подпишитесь на официальный аккаунт:Сяоха изучает Java, закулисный ответресурс, либоБесплатный нерегулярный доступ к ссылкам ресурсов, вот каталог и несколько скриншотов:
Важные вещи, которые нужно сказать дважды, обратите внимание на общедоступный номер:Сяоха изучает Java, закулисный ответресурс, либоБесплатный нерегулярный доступ к ссылкам ресурсов! ! !