Java 8 - 快速指南
Java 8 - Overview
JAVA 8是JAVA编程语言开发的主要特性发布。 它的初始版本于2014年3月18日发布。随着Java 8的发布,Java提供了对函数式编程,新的JavaScript引擎,用于日期时间操作的新API,新的流API等的支持。
新功能 (New Features)
Lambda expression - 为Java添加功能处理功能。
Method references - 通过名称引用函数而不是直接调用它们。 使用函数作为参数。
Default method - 具有默认方法实现的接口。
New tools - 添加了新的编译工具和实用程序,如'jdeps',以确定依赖关系。
Stream API - 用于促进流水线处理的新流API。
Date Time API - 改进的日期时间API。
Optional - 强调正确处理空值的最佳实践。
Nashorn, JavaScript Engine - 用于执行JavaScript代码的基于Java的引擎。
请考虑以下代码段。
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
public class Java8Tester {
public static void main(String args[]) {
List<String> names1 = new ArrayList<String>();
names1.add("Mahesh ");
names1.add("Suresh ");
names1.add("Ramesh ");
names1.add("Naresh ");
names1.add("Kalpesh ");
List<String> names2 = new ArrayList<String>();
names2.add("Mahesh ");
names2.add("Suresh ");
names2.add("Ramesh ");
names2.add("Naresh ");
names2.add("Kalpesh ");
Java8Tester tester = new Java8Tester();
System.out.println("Sort using Java 7 syntax: ");
tester.sortUsingJava7(names1);
System.out.println(names1);
System.out.println("Sort using Java 8 syntax: ");
tester.sortUsingJava8(names2);
System.out.println(names2);
}
//<b class="notranslate">sort using java 7</b>
private void sortUsingJava7(List<String> names) {
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
}
//<b class="notranslate">sort using java 8</b>
private void sortUsingJava8(List<String> names) {
Collections.sort(names, (s1, s2) -> s1.compareTo(s2));
}
}
运行该程序以获得以下结果。
Sort using Java 7 syntax:
[ Kalpesh Mahesh Naresh Ramesh Suresh ]
Sort using Java 8 syntax:
[ Kalpesh Mahesh Naresh Ramesh Suresh ]
这里, sortUsingJava8()方法使用带有lambda表达式的sort函数作为参数来获取排序条件。
Java 8 - Environment Setup
本地环境设置 (Local Environment Setup)
如果要为Java编程语言设置自己的环境,那么本节将指导您完成整个过程。 请按照以下步骤设置Java环境。
Java SE可以从以下链接免费下载 -
https://www.oracle.com/technetwork/java/javase/downloads/index-jsp-138363.html
您下载基于您的操作系统的版本。
按照说明下载Java,然后运行.exe在您的计算机上安装Java。 在计算机上安装Java后,需要设置环境变量以指向正确的安装目录。
设置Windows 2000/XP的路径
假设您已在c:\Program Files\java\jdk目录中安装了Java -
右键单击“我的电脑”,然后选择“属性”。
单击“高级”选项卡下的“环境变量”按钮。
现在,更改'Path'变量,使其也包含Java可执行文件的路径。 例如,如果路径当前设置为“C:\WINDOWS\SYSTEM32”,则将路径更改为“C:\WINDOWS\SYSTEM32; c:\Program Files\java\jdk\bin”。
设置Windows 95/98/ME的路径
假设您已在c:\Program Files\java\jdk目录中安装了Java -
编辑'C:\autoexec.bat'文件并在结尾添加以下行 -
SET PATH =%PATH%; C:\Program Files\java\jdk\bin
设置Linux,UNIX,Solaris,FreeBSD的路径
应将环境变量PATH设置为指向已安装Java二进制文件的位置。 如果您在执行此操作时遇到问题,请参阅您的shell文档。
例如,如果您使用bash作为shell,那么您将在'.bashrc的末尾添加以下行:export PATH =/path/to/java:$ PATH'
流行的Java编辑器 (Popular Java Editors)
要编写Java程序,您需要一个文本编辑器。 市场上还有更复杂的IDE。 但就目前而言,您可以考虑以下其中一项 -
Notepad - 在Windows机器上,您可以使用任何简单的文本编辑器,如记事本(本教程推荐)或TextPad。
Netbeans - 这是一个开源且免费的Java IDE。 它可以从https://netbeans.org/index.html下载。
Eclipse - 它也是由Eclipse开源社区开发的Java IDE,可以从https://www.eclipse.org/下载。
Java 8 - Lambda Expressions
Lambda表达式在Java 8中引入,并被吹捧为Java 8的最大特性.Lambda表达式有助于函数式编程,并简化了很多开发。
语法 (Syntax)
lambda表达式的特征在于以下语法。
parameter -> expression body
以下是lambda表达式的重要特征。
Optional type declaration - 无需声明参数类型。 编译器可以从参数的值推断出相同的值。
Optional parenthesis around parameter - 无需在括号中声明单个参数。 对于多个参数,需要括号。
Optional curly braces - 如果主体包含单个语句,则无需在表达式主体中使用花括号。
Optional return keyword - 如果正文具有单个表达式以返回值,则编译器会自动返回该值。 需要使用大括号来表示表达式返回一个值。
Lambda表达式示例
使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。
Java8Tester.java
public class Java8Tester {
public static void main(String args[]) {
Java8Tester tester = new Java8Tester();
//with type declaration
MathOperation addition = (int a, int b) -> a + b;
//with out type declaration
MathOperation subtraction = (a, b) -> a - b;
//with return statement along with curly braces
MathOperation multiplication = (int a, int b) -> { return a * b; };
//without return statement and without curly braces
MathOperation division = (int a, int b) -> a/b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10/5 = " + tester.operate(10, 5, division));
//without parenthesis
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
//with parenthesis
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("Mahesh");
greetService2.sayMessage("Suresh");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation) {
return mathOperation.operation(a, b);
}
}
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下输出 -
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10/5 = 2
Hello Mahesh
Hello Suresh
以下是上述示例中要考虑的重点。
Lambda表达式主要用于定义功能接口的内联实现,即仅具有单个方法的接口。 在上面的例子中,我们使用了各种类型的lambda表达式来定义MathOperation接口的操作方法。 然后我们定义了GreetingService的sayMessage的实现。
Lambda表达式消除了对匿名类的需求,并为Java提供了非常简单但功能强大的函数编程功能。
Scope
使用lambda表达式,您可以引用任何最终变量或有效的最终变量(仅分配一次)。 如果第二次为变量赋值,则Lambda表达式会抛出编译错误。
范围示例
使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。
Java8Tester.java
public class Java8Tester {
final static String salutation = "Hello! ";
public static void main(String args[]) {
GreetingService greetService1 = message ->
System.out.println(salutation + message);
greetService1.sayMessage("Mahesh");
}
interface GreetingService {
void sayMessage(String message);
}
}
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下输出 -
Hello! Mahesh
Java 8 - Method References
方法引用有助于通过名称指向方法。 使用“::”符号描述方法参考。 方法参考可用于指出以下类型的方法 -
- 静态方法
- 实例方法
- 使用new运算符的构造函数(TreeSet :: new)
方法参考示例
使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。
Java8Tester.java
import java.util.List;
import java.util.ArrayList;
public class Java8Tester {
public static void main(String args[]) {
List names = new ArrayList();
names.add("Mahesh");
names.add("Suresh");
names.add("Ramesh");
names.add("Naresh");
names.add("Kalpesh");
names.forEach(System.out::println);
}
}
这里我们已经将System.out :: println方法作为静态方法引用传递。
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下输出 -
Mahesh
Suresh
Ramesh
Naresh
Kalpesh
Java 8 - Functional Interfaces
功能界面具有单一功能。 例如,具有单个方法'compareTo'的Comparable接口用于比较目的。 Java 8定义了许多在lambda表达式中广泛使用的函数接口。 以下是java.util.Function包中定义的功能接口列表。
Sr.No. | 界面和描述 |
---|---|
1 | BiConsumer《T,U》 表示接受两个输入参数的操作,并且不返回任何结果。 |
2 | BiFunction《T,U,R》 表示接受两个参数并生成结果的函数。 |
3 | BinaryOperator《T》 表示对两个相同类型的操作数的操作,产生与操作数相同类型的结果。 |
4 | BiPredicate《T,U》 表示两个参数的谓词(布尔值函数)。 |
5 | BooleanSupplier 表示布尔值结果的供应商。 |
6 | Consumer《T》 表示接受单个输入参数且不返回结果的操作。 |
7 | DoubleBinaryOperator 表示对两个双值操作数的运算并产生双值结果。 |
8 | DoubleConsumer 表示接受单个双值参数且不返回结果的操作。 |
9 | DoubleFunction《R》 表示接受双值参数并生成结果的函数。 |
10 | DoublePredicate 表示一个双值参数的谓词(布尔值函数)。 |
11 | DoubleSupplier 代表双值结果的供应商。 |
12 | DoubleToIntFunction 表示接受双值参数并生成int值结果的函数。 |
13 | DoubleToLongFunction 表示接受双值参数并生成长值结果的函数。 |
14 | DoubleUnaryOperator 表示对生成双值结果的单个双值操作数的操作。 |
15 | Function《T,R》 表示接受一个参数并生成结果的函数。 |
16 | IntBinaryOperator 表示对两个int-值操作数的操作,并生成一个int值结果。 |
17 | IntConsumer 表示接受单个int值参数并且不返回任何结果的操作。 |
18 | IntFunction《R》 表示接受int值参数并生成结果的函数。 |
19 | IntPredicate 表示一个int值参数的谓词(布尔值函数)。 |
20 | IntSupplier 表示int值结果的供应商。 |
21 | IntToDoubleFunction 表示接受int值参数并生成双值结果的函数。 |
22 | IntToLongFunction 表示接受int值参数并生成长值结果的函数。 |
23 | IntUnaryOperator 表示对生成int值结果的单个int值操作数的操作。 |
24 | LongBinaryOperator 表示对两个长值操作数的操作,并产生一个长值结果。 |
25 | LongConsumer 表示接受单个长值参数并且不返回任何结果的操作。 |
26 | LongFunction《R》 表示接受长值参数并生成结果的函数。 |
27 | LongPredicate 表示一个长值参数的谓词(布尔值函数)。 |
28 | LongSupplier 代表长期价值结果的供应商。 |
29 | LongToDoubleFunction 表示接受长值参数并生成双值结果的函数。 |
30 | LongToIntFunction 表示接受长值参数并生成int值结果的函数。 |
31 | LongUnaryOperator 表示对生成长值结果的单个长值操作数的操作。 |
32 | ObjDoubleConsumer《T》 表示接受对象值和双值参数的操作,并且不返回任何结果。 |
33 | ObjIntConsumer《T》 表示接受对象值和int值参数的操作,并且不返回任何结果。 |
34 | ObjLongConsumer《T》 表示接受对象值和长值参数的操作,并且不返回任何结果。 |
35 | Predicate《T》 表示一个参数的谓词(布尔值函数)。 |
36 | Supplier《T》 代表结果的供应商。 |
37 | ToDoubleBiFunction《T,U》 表示接受两个参数并生成双值结果的函数。 |
38 | ToDoubleFunction《T》 表示生成双值结果的函数。 |
39 | ToIntBiFunction《T,U》 表示接受两个参数并生成int值结果的函数。 |
40 | ToIntFunction《T》 表示生成int值结果的函数。 |
41 | ToLongBiFunction《T,U》 表示接受两个参数并生成长值结果的函数。 |
42 | ToLongFunction《T》 表示生成长值结果的函数。 |
43 | UnaryOperator《T》 表示对单个操作数的操作,该操作生成与其操作数相同类型的结果。 |
功能接口示例
Predicate
使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。
Java8Tester.java
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Java8Tester {
public static void main(String args[]) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// Predicate<Integer> predicate = n -> true
// n is passed as parameter to test method of Predicate interface
// test method will always return true no matter what value n has.
System.out.println("Print all numbers:");
//pass n as parameter
eval(list, n->true);
// Predicate<Integer> predicate1 = n -> n%2 == 0
// n is passed as parameter to test method of Predicate interface
// test method will return true if n%2 comes to be zero
System.out.println("Print even numbers:");
eval(list, n-> n%2 == 0 );
// Predicate<Integer> predicate2 = n -> n > 3
// n is passed as parameter to test method of Predicate interface
// test method will return true if n is greater than 3.
System.out.println("Print numbers greater than 3:");
eval(list, n-> n > 3 );
}
public static void eval(List<Integer> list, Predicate<Integer> predicate) {
for(Integer n: list) {
if(predicate.test(n)) {
System.out.println(n + " ");
}
}
}
}
这里我们传递了Predicate接口,它接受一个输入并返回Boolean。
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下输出 -
Print all numbers:
1
2
3
4
5
6
7
8
9
Print even numbers:
2
4
6
8
Print numbers greater than 3:
4
5
6
7
8
9
Java 8 - Default Methods
Java 8在接口中引入了默认方法实现的新概念。 添加此功能是为了向后兼容,以便可以使用旧接口来利用Java 8的lambda表达式功能。
例如,“List”或“Collection”接口没有“forEach”方法声明。 因此,添加此类方法将简单地破坏集合框架实现。 Java 8引入了默认方法,因此List/Collection接口可以具有forEach方法的默认实现,而实现这些接口的类不需要实现相同的方法。
语法 (Syntax)
public interface vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
}
多个默认值
使用接口中的默认函数,类可能会使用相同的默认方法实现两个接口。 以下代码说明了如何解决这种歧义。
public interface vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
}
public interface fourWheeler {
default void print() {
System.out.println("I am a four wheeler!");
}
}
第一种解决方案是创建一个覆盖默认实现的自有方法。
public class car implements vehicle, fourWheeler {
public void print() {
System.out.println("I am a four wheeler car vehicle!");
}
}
第二种解决方案是使用super调用指定接口的默认方法。
public class car implements vehicle, fourWheeler {
default void print() {
vehicle.super.print();
}
}
静态默认方法
接口也可以使用Java 8以上的静态帮助器方法。
public interface vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
static void blowHorn() {
System.out.println("Blowing horn!!!");
}
}
默认方法示例
使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。
Java8Tester.java
public class Java8Tester {
public static void main(String args[]) {
Vehicle vehicle = new Car();
vehicle.print();
}
}
interface Vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
static void blowHorn() {
System.out.println("Blowing horn!!!");
}
}
interface FourWheeler {
default void print() {
System.out.println("I am a four wheeler!");
}
}
class Car implements Vehicle, FourWheeler {
public void print() {
Vehicle.super.print();
FourWheeler.super.print();
Vehicle.blowHorn();
System.out.println("I am a car!");
}
}
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下输出 -
I am a vehicle!
I am a four wheeler!
Blowing horn!!!
I am a car!
Java 8 - Streams
Stream是Java 8中引入的新抽象层。使用stream,您可以以类似于SQL语句的声明方式处理数据。 例如,请考虑以下SQL语句。
SELECT max(salary), employee_id, employee_name FROM Employee
上面的SQL表达式自动返回最大受薪员工的详细信息,而无需在开发人员端进行任何计算。 在Java中使用集合框架,开发人员必须使用循环并进行重复检查。 另一个问题是效率; 由于多核处理器易于使用,因此Java开发人员必须编写可能非常容易出错的并行代码处理。
为了解决这些问题,Java 8引入了流的概念,允许开发人员以声明方式处理数据并利用多核架构,而无需为其编写任何特定代码。
什么是Stream?
Stream表示来自源的一系列对象,它支持聚合操作。 以下是流的特征 -
Sequence of elements - 流以顺序方式提供特定类型的一组元素。 流按需获取/计算元素。 它从不存储元素。
Source - Stream将集合,数组或I/O资源作为输入源。
Aggregate operations - Stream支持聚合操作,如过滤,映射,限制,缩减,查找,匹配等。
Pipelining - 大多数流操作都会返回流本身,以便可以对其结果进行流水线操作。 这些操作称为中间操作,它们的功能是获取输入,处理它们并将输出返回到目标。 collect()方法是一个终端操作,通常在流水线操作结束时出现,以标记流的结尾。
Automatic iterations - 流操作在内部对提供的源元素进行迭代,与需要显式迭代的集合形成对比。
生成流
使用Java 8,Collection接口有两种生成Stream的方法。
stream() - 返回一个将集合作为源的顺序流。
parallelStream() - 以集合为源,返回并行Stream。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
forEach
Stream提供了一种新方法'forEach'来迭代流的每个元素。 以下代码段显示如何使用forEach打印10个随机数。
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
map
'map'方法用于将每个元素映射到其对应的结果。 以下代码段使用map打印唯一的数字方块。
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
//get list of unique squares
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
过滤
“过滤器”方法用于根据标准消除元素。 以下代码段使用过滤器打印空字符串计数。
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
//get count of empty string
int count = strings.stream().filter(string -> string.isEmpty()).count();
limit
'limit'方法用于减小流的大小。 以下代码段显示如何使用limit打印10个随机数。
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
sorted
'sorted'方法用于对流进行排序。 以下代码段显示如何按排序顺序打印10个随机数。
Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);
并行处理
parallelStream是并行处理流的替代方案。 看一下使用parallelStream打印空字符串计数的以下代码段。
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
//get count of empty string
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
在顺序和并行流之间切换非常容易。
Collectors
收集器用于组合流的元素的处理结果。 收集器可用于返回列表或字符串。
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("Filtered List: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("Merged String: " + mergedString);
Statistics
在Java 8中,引入了统计信息收集器来计算流处理时的所有统计信息。
List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest number in List : " + stats.getMax());
System.out.println("Lowest number in List : " + stats.getMin());
System.out.println("Sum of all numbers : " + stats.getSum());
System.out.println("Average of all numbers : " + stats.getAverage());
流示例
使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。
Java8Tester.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.Map;
public class Java8Tester {
public static void main(String args[]) {
System.out.println("Using Java 7: ");
// Count empty strings
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
System.out.println("List: " +strings);
long count = getCountEmptyStringUsingJava7(strings);
System.out.println("Empty Strings: " + count);
count = getCountLength3UsingJava7(strings);
System.out.println("Strings of length 3: " + count);
//Eliminate empty string
List<String> filtered = deleteEmptyStringsUsingJava7(strings);
System.out.println("Filtered List: " + filtered);
//Eliminate empty string and join using comma.
String mergedString = getMergedStringUsingJava7(strings,", ");
System.out.println("Merged String: " + mergedString);
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
//get list of square of distinct numbers
List<Integer> squaresList = getSquares(numbers);
System.out.println("Squares List: " + squaresList);
List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
System.out.println("List: " +integers);
System.out.println("Highest number in List : " + getMax(integers));
System.out.println("Lowest number in List : " + getMin(integers));
System.out.println("Sum of all numbers : " + getSum(integers));
System.out.println("Average of all numbers : " + getAverage(integers));
System.out.println("Random Numbers: ");
//print ten random numbers
Random random = new Random();
for(int i = 0; i < 10; i++) {
System.out.println(random.nextInt());
}
System.out.println("Using Java 8: ");
System.out.println("List: " +strings);
count = strings.stream().filter(string->string.isEmpty()).count();
System.out.println("Empty Strings: " + count);
count = strings.stream().filter(string -> string.length() == 3).count();
System.out.println("Strings of length 3: " + count);
filtered = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.toList());
System.out.println("Filtered List: " + filtered);
mergedString = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("Merged String: " + mergedString);
squaresList = numbers.stream().map( i ->i*i).distinct().collect(Collectors.toList());
System.out.println("Squares List: " + squaresList);
System.out.println("List: " +integers);
IntSummaryStatistics stats = integers.stream().mapToInt((x) ->x).summaryStatistics();
System.out.println("Highest number in List : " + stats.getMax());
System.out.println("Lowest number in List : " + stats.getMin());
System.out.println("Sum of all numbers : " + stats.getSum());
System.out.println("Average of all numbers : " + stats.getAverage());
System.out.println("Random Numbers: ");
random.ints().limit(10).sorted().forEach(System.out::println);
//parallel processing
count = strings.parallelStream().filter(string -> string.isEmpty()).count();
System.out.println("Empty Strings: " + count);
}
private static int getCountEmptyStringUsingJava7(List<String> strings) {
int count = 0;
for(String string: strings) {
if(string.isEmpty()) {
count++;
}
}
return count;
}
private static int getCountLength3UsingJava7(List<String> strings) {
int count = 0;
for(String string: strings) {
if(string.length() == 3) {
count++;
}
}
return count;
}
private static List<String> deleteEmptyStringsUsingJava7(List<String> strings) {
List<String> filteredList = new ArrayList<String>();
for(String string: strings) {
if(!string.isEmpty()) {
filteredList.add(string);
}
}
return filteredList;
}
private static String getMergedStringUsingJava7(List<String> strings, String separator) {
StringBuilder stringBuilder = new StringBuilder();
for(String string: strings) {
if(!string.isEmpty()) {
stringBuilder.append(string);
stringBuilder.append(separator);
}
}
String mergedString = stringBuilder.toString();
return mergedString.substring(0, mergedString.length()-2);
}
private static List<Integer> getSquares(List<Integer> numbers) {
List<Integer> squaresList = new ArrayList<Integer>();
for(Integer number: numbers) {
Integer square = new Integer(number.intValue() * number.intValue());
if(!squaresList.contains(square)) {
squaresList.add(square);
}
}
return squaresList;
}
private static int getMax(List<Integer> numbers) {
int max = numbers.get(0);
for(int i = 1;i < numbers.size();i++) {
Integer number = numbers.get(i);
if(number.intValue() > max) {
max = number.intValue();
}
}
return max;
}
private static int getMin(List<Integer> numbers) {
int min = numbers.get(0);
for(int i= 1;i < numbers.size();i++) {
Integer number = numbers.get(i);
if(number.intValue() < min) {
min = number.intValue();
}
}
return min;
}
private static int getSum(List numbers) {
int sum = (int)(numbers.get(0));
for(int i = 1;i < numbers.size();i++) {
sum += (int)numbers.get(i);
}
return sum;
}
private static int getAverage(List<Integer> numbers) {
return getSum(numbers)/numbers.size();
}
}
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下结果 -
Using Java 7:
List: [abc, , bc, efg, abcd, , jkl]
Empty Strings: 2
Strings of length 3: 3
Filtered List: [abc, bc, efg, abcd, jkl]
Merged String: abc, bc, efg, abcd, jkl
Squares List: [9, 4, 49, 25]
List: [1, 2, 13, 4, 15, 6, 17, 8, 19]
Highest number in List : 19
Lowest number in List : 1
Sum of all numbers : 85
Average of all numbers : 9
Random Numbers:
-1279735475
903418352
-1133928044
-1571118911
628530462
18407523
-881538250
-718932165
270259229
421676854
Using Java 8:
List: [abc, , bc, efg, abcd, , jkl]
Empty Strings: 2
Strings of length 3: 3
Filtered List: [abc, bc, efg, abcd, jkl]
Merged String: abc, bc, efg, abcd, jkl
Squares List: [9, 4, 49, 25]
List: [1, 2, 13, 4, 15, 6, 17, 8, 19]
Highest number in List : 19
Lowest number in List : 1
Sum of all numbers : 85
Average of all numbers : 9.444444444444445
Random Numbers:
-1009474951
-551240647
-2484714
181614550
933444268
1227850416
1579250773
1627454872
1683033687
1798939493
Empty Strings: 2
Java 8 - Optional Class
Optional是用于包含非null对象的容器对象。 可选对象用于表示缺少值的null。 此类具有各种实用程序方法,以便于代码将值处理为“可用”或“不可用”,而不是检查空值。 它在Java 8中引入,类似于Guava中的Optional。
Class 声明 (Class Declaration)
以下是java.util.Optional《T》类的声明 -
public final class Optional<T> extends Object
类方法
Sr.No. | 方法和描述 |
---|---|
1 | static 《T》 Optional《T》 empty() 返回一个空的Optional实例。 |
2 | boolean equals(Object obj) 指示某个其他对象是否“等于”此可选项。 |
3 | Optional《T》 filter(Predicate《? super 《T》 predicate) 如果存在值且值与给定谓词匹配,则返回描述值的Optional,否则返回空Optional。 |
4 | 《U》 Optional《U》 flatMap(Function《? super T,Optional《U》》 mapper) 如果存在值,则将提供的Optional-bearing映射函数应用于该值,返回该结果,否则返回空的Optional。 |
5 | T get() 如果此Optional中存在值,则返回该值,否则抛出NoSuchElementException。 |
6 | int hashCode() 返回当前值的哈希码值(如果有),如果不存在值,则返回0(零)。 |
7 | void ifPresent(Consumer《? super T》 consumer) 如果存在值,则使用该值调用指定的使用者,否则不执行任何操作。 |
8 | boolean isPresent() 如果存在值,则返回true,否则返回false。 |
9 | 《U》Optional《U》 map(Function《? super T,? extends U》 mapper) 如果存在值,则将提供的映射函数应用于该值,如果结果为非null,则返回描述结果的Optional。 |
10 | static 《T》 Optional《T》 of(T value) 返回具有指定的当前非null值的Optional。 |
11 | static 《T》 Optional《T》 ofNullable(T value) 返回描述指定值的Optional,如果为非null,否则返回空Optional。 |
12 | T orElse(T other) 返回值(如果存在),否则返回其他值。 |
13 | T orElseGet(Supplier《? extends T》 other) 返回值(如果存在),否则调用other并返回该调用的结果。 |
14 | 《X extends Throwable》 T orElseThrow(Supplier《? extends X》 exceptionSupplier) 返回包含的值(如果存在),否则抛出由提供的供应商创建的异常。 |
15 | String toString() 返回此Optional的非空字符串表示形式,适用于调试。 |
该类继承以下类中的方法 -
- java.lang.Object
可选示例
使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。
Java8Tester.java
import java.util.Optional;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8Tester = new Java8Tester();
Integer value1 = null;
Integer value2 = new Integer(10);
//Optional.ofNullable - allows passed parameter to be null.
Optional<Integer> a = Optional.ofNullable(value1);
//Optional.of - throws NullPointerException if passed parameter is null
Optional<Integer> b = Optional.of(value2);
System.out.println(java8Tester.sum(a,b));
}
public Integer sum(Optional<Integer> a, Optional<Integer> b) {
//Optional.isPresent - checks the value is present or not
System.out.println("First parameter is present: " + a.isPresent());
System.out.println("Second parameter is present: " + b.isPresent());
//Optional.orElse - returns the value if present otherwise returns
//the default value passed.
Integer value1 = a.orElse(new Integer(0));
//Optional.get - gets the value, value should be present
Integer value2 = b.get();
return value1 + value2;
}
}
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下输出 -
First parameter is present: false
Second parameter is present: true
10
Java 8 - Nashorn JavaScript
使用Java 8,Nashorn,引入了一个大大改进的javascript引擎,以取代现有的Rhino。 Nashorn提供2到10倍的性能,因为它直接编译内存中的代码并将字节码传递给JVM。 Nashorn使用Java 7中引入的调用动态特性来提高性能。
jjs
对于Nashorn引擎,JAVA 8引入了一个新的命令行工具jjs,用于在控制台执行javascript代码。
解释js文件
在c:\“JAVA文件夹中创建并保存文件sample.js 。
sample.js
print('Hello World!');
打开控制台并使用以下命令。
C:\JAVA>jjs sample.js
它将产生以下输出:
Hello World!
交互模式下的jjs
打开控制台并使用以下命令。
C:\JAVA>jjs
jjs> print("Hello, World!")
Hello, World!
jjs> quit()
>>
传递参数
打开控制台并使用以下命令。
C:\JAVA> jjs -- a b c
jjs> print('letters: ' +arguments.join(", "))
letters: a, b, c
jjs>
从Java调用JavaScript
使用ScriptEngineManager,可以在Java中调用和解释JavaScript代码。
例子 (Example)
使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。
Java8Tester.java
import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
public class Java8Tester {
public static void main(String args[]) {
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn");
String name = "Mahesh";
Integer result = null;
try {
nashorn.eval("print('" + name + "')");
result = (Integer) nashorn.eval("10 + 2");
} catch(ScriptException e) {
System.out.println("Error executing script: "+ e.getMessage());
}
System.out.println(result.toString());
}
}
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下结果 -
Mahesh
12
从JavaScript调用Java
以下示例说明如何在java脚本中导入和使用Java类。
在c:\“JAVA文件夹中创建并保存sample.js .
sample.js
var BigDecimal = Java.type('java.math.BigDecimal');
function calculate(amount, percentage) {
var result = new BigDecimal(amount).multiply(new BigDecimal(percentage)).divide(
new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_EVEN);
return result.toPlainString();
}
var result = calculate(568000000000000000023,13.9);
print(result);
打开控制台并使用以下命令。
C:\JAVA>jjs sample.js
它应该产生以下输出 -
78952000000000000003.20
Java 8 - New Date/Time API
在Java 8中,引入了一个新的Date-Time API来弥补旧的日期时间API的以下缺点。
Not thread safe - java.util.Date不是线程安全的,因此开发人员在使用date时必须处理并发问题。 新的日期时间API是不可变的,没有setter方法。
Poor design - 默认日期从1900开始,月从1开始,日从0开始,因此没有统一性。 旧API具有较少的日期操作直接方法。 新API为此类操作提供了许多实用方法。
Difficult time zone handling - 开发人员必须编写大量代码来处理时区问题。 新API的开发考虑了特定于域的设计。
Java 8在java.time包下引入了一个新的日期时间API。 以下是java.time包中引入的一些重要类。
Local - 简化的日期时间API,没有时区处理的复杂性。
Zoned - 专门的日期时间API,用于处理各种时区。
本地日期时间API
LocalDate/LocalTime和LocalDateTime类简化了不需要时区的开发。 让我们看看他们的行动。
使用您选择的任何编辑器创建以下java程序,例如C:\> JAVA。
Java8Tester.java
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.Month;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testLocalDateTime();
}
public void testLocalDateTime() {
// Get the current date and time
LocalDateTime currentTime = LocalDateTime.now();
System.out.println("Current DateTime: " + currentTime);
LocalDate date1 = currentTime.toLocalDate();
System.out.println("date1: " + date1);
Month month = currentTime.getMonth();
int day = currentTime.getDayOfMonth();
int seconds = currentTime.getSecond();
System.out.println("Month: " + month +"day: " + day +"seconds: " + seconds);
LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);
System.out.println("date2: " + date2);
//12 december 2014
LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12);
System.out.println("date3: " + date3);
//22 hour 15 minutes
LocalTime date4 = LocalTime.of(22, 15);
System.out.println("date4: " + date4);
//parse a string
LocalTime date5 = LocalTime.parse("20:15:30");
System.out.println("date5: " + date5);
}
}
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下输出 -
Current DateTime: 2014-12-09T11:00:45.457
date1: 2014-12-09
Month: DECEMBERday: 9seconds: 45
date2: 2012-12-10T11:00:45.457
date3: 2014-12-12
date4: 22:15
date5: 20:15:30
Zoned Date-Time API
当考虑时区时,将使用分区日期时间API。 让我们看看他们的行动。
使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。
Java8Tester.java
import java.time.ZonedDateTime;
import java.time.ZoneId;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testZonedDateTime();
}
public void testZonedDateTime() {
// Get the current date and time
ZonedDateTime date1 = ZonedDateTime.parse("2007-12-03T10:15:30+05:30[Asia/Karachi]");
System.out.println("date1: " + date1);
ZoneId id = ZoneId.of("Europe/Paris");
System.out.println("ZoneId: " + id);
ZoneId currentZone = ZoneId.systemDefault();
System.out.println("CurrentZone: " + currentZone);
}
}
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下输出 -
date1: 2007-12-03T10:15:30+05:00[Asia/Karachi]
ZoneId: Europe/Paris
CurrentZone: Etc/UTC
Chrono Units Enum
在Java 8中添加了java.time.temporal.ChronoUnit枚举,以替换旧API中使用的整数值来表示日,月等。让我们看看它们的运行情况。
使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。
Java8Tester.java
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testChromoUnits();
}
public void testChromoUnits() {
//Get the current date
LocalDate today = LocalDate.now();
System.out.println("Current date: " + today);
//add 1 week to the current date
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
System.out.println("Next week: " + nextWeek);
//add 1 month to the current date
LocalDate nextMonth = today.plus(1, ChronoUnit.MONTHS);
System.out.println("Next month: " + nextMonth);
//add 1 year to the current date
LocalDate nextYear = today.plus(1, ChronoUnit.YEARS);
System.out.println("Next year: " + nextYear);
//add 10 years to the current date
LocalDate nextDecade = today.plus(1, ChronoUnit.DECADES);
System.out.println("Date after ten year: " + nextDecade);
}
}
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下结果 -
Current date: 2014-12-10
Next week: 2014-12-17
Next month: 2015-01-10
Next year: 2015-12-10
Date after ten year: 2024-12-10
期间和期限
在Java 8中,引入了两个专门的类来处理时差。
Period - 它处理基于日期的时间量。
Duration - 它处理基于时间的时间量。
让我们看看他们的行动。
使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。
Java8Tester.java
import java.time.temporal.ChronoUnit;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Duration;
import java.time.Period;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testPeriod();
java8tester.testDuration();
}
public void testPeriod() {
//Get the current date
LocalDate date1 = LocalDate.now();
System.out.println("Current date: " + date1);
//add 1 month to the current date
LocalDate date2 = date1.plus(1, ChronoUnit.MONTHS);
System.out.println("Next month: " + date2);
Period period = Period.between(date2, date1);
System.out.println("Period: " + period);
}
public void testDuration() {
LocalTime time1 = LocalTime.now();
Duration twoHours = Duration.ofHours(2);
LocalTime time2 = time1.plus(twoHours);
Duration duration = Duration.between(time1, time2);
System.out.println("Duration: " + duration);
}
}
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下输出 -
Current date: 2014-12-10
Next month: 2015-01-10
Period: P-1M
Duration: PT2H
时间调整者
TemporalAdjuster用于执行日期数学。 例如,获取“本月第二个星期六”或“下周二”。 让我们看看他们的行动。
使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。
Java8Tester.java
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.time.DayOfWeek;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testAdjusters();
}
public void testAdjusters() {
//Get the current date
LocalDate date1 = LocalDate.now();
System.out.println("Current date: " + date1);
//get the next tuesday
LocalDate nextTuesday = date1.with(TemporalAdjusters.next(DayOfWeek.TUESDAY));
System.out.println("Next Tuesday on : " + nextTuesday);
//get the second saturday of next month
LocalDate firstInYear = LocalDate.of(date1.getYear(),date1.getMonth(), 1);
LocalDate secondSaturday = firstInYear.with(TemporalAdjusters.nextOrSame(
DayOfWeek.SATURDAY)).with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
System.out.println("Second Saturday on : " + secondSaturday);
}
}
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下结果 -
Current date: 2014-12-10
Next Tuesday on : 2014-12-16
Second Saturday on : 2014-12-13
向后兼容性
toInstant()方法被添加到原始Date和Calendar对象,可用于将它们转换为新的Date-Time API。 使用ofInstant(Insant,ZoneId)方法获取LocalDateTime或ZonedDateTime对象。 让我们看看他们的行动。
使用您选择的任何编辑器创建以下Java程序,例如C:\> JAVA。
Java8Tester.java
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.Date;
import java.time.Instant;
import java.time.ZoneId;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testBackwardCompatability();
}
public void testBackwardCompatability() {
//Get the current date
Date currentDate = new Date();
System.out.println("Current date: " + currentDate);
//Get the instant of current date in terms of milliseconds
Instant now = currentDate.toInstant();
ZoneId currentZone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(now, currentZone);
System.out.println("Local date: " + localDateTime);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(now, currentZone);
System.out.println("Zoned date: " + zonedDateTime);
}
}
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下输出 -
Current date: Wed Dec 10 05:44:06 UTC 2014
Local date: 2014-12-10T05:44:06.635
Zoned date: 2014-12-10T05:44:06.635Z[Etc/UTC]
Java 8 - Base64
借助Java 8,Base64终于得到了应有的发展。 Java 8现在具有用于Base64编码的内置编码器和解码器。 在Java 8中,我们可以使用三种类型的Base64编码。
Simple - 输出映射到位于A-Za-z0-9 + /中的一组字符。 编码器不在输出中添加任何换行,并且解码器拒绝除A-Za-z0-9 + /之外的任何字符。
URL - 输出映射到位于A-Za-z0-9 + _中的字符集。 输出是URL和文件名安全。
MIME - 输出映射为MIME友好格式。 输出以每行不超过76个字符的行表示,并使用回车符'\ _'后跟换行符'\ n'作为行分隔符。 编码输出的末尾不存在行分隔符。
嵌套类 (Nested Classes)
Sr.No. | 嵌套类和描述 |
---|---|
1 | static class Base64.Decoder 该类实现了一个解码器,用于使用RFC 4648和RFC 2045中规定的Base64编码方案解码字节数据。 |
2 | static class Base64.Encoder 该类实现了一个编码器,用于使用RFC 4648和RFC 2045中规定的Base64编码方案对字节数据进行编码。 |
方法 (Methods)
Sr.No. | 方法名称和描述 |
---|---|
1 | static Base64.Decoder getDecoder() 返回使用Basic类型base64编码方案解码的Base64.Decoder。 |
2 | static Base64.Encoder getEncoder() 返回使用Basic类型base64编码方案进行编码的Base64.Encoder。 |
3 | static Base64.Decoder getMimeDecoder() 返回使用MIME类型base64解码方案解码的Base64.Decoder。 |
4 | static Base64.Encoder getMimeEncoder() 返回使用MIME类型base64编码方案进行编码的Base64.Encoder。 |
5 | static Base64.Encoder getMimeEncoder(int lineLength, byte[] lineSeparator) 返回使用具有指定行长度和行分隔符的MIME类型base64编码方案进行编码的Base64.Encoder。 |
6 | static Base64.Decoder getUrlDecoder() 返回使用URL和Filename安全类型base64编码方案解码的Base64.Decoder。 |
7 | static Base64.Encoder getUrlEncoder() 返回使用URL和Filename安全类型base64编码方案进行编码的Base64.Encoder。 |
方法继承 (Methods Inherited)
该类继承以下类中的方法 -
- java.lang.Object
Base64示例
使用您选择的任何编辑器在C:/> JAVA中创建以下Java程序。
Java8Tester.java
import java.util.Base64;
import java.util.UUID;
import java.io.UnsupportedEncodingException;
public class HelloWorld {
public static void main(String args[]) {
try {
// Encode using basic encoder
String base64encodedString = Base64.getEncoder().encodeToString(
"IoWiki?java8".getBytes("utf-8"));
System.out.println("Base64 Encoded String (Basic) :" + base64encodedString);
// Decode
byte[] base64decodedBytes = Base64.getDecoder().decode(base64encodedString);
System.out.println("Original String: " + new String(base64decodedBytes, "utf-8"));
base64encodedString = Base64.getUrlEncoder().encodeToString(
"IoWiki?java8".getBytes("utf-8"));
System.out.println("Base64 Encoded String (URL) :" + base64encodedString);
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 10; ++i) {
stringBuilder.append(UUID.randomUUID().toString());
}
byte[] mimeBytes = stringBuilder.toString().getBytes("utf-8");
String mimeEncodedString = Base64.getMimeEncoder().encodeToString(mimeBytes);
System.out.println("Base64 Encoded String (MIME) :" + mimeEncodedString);
} catch(UnsupportedEncodingException e) {
System.out.println("Error :" + e.getMessage());
}
}
}
验证结果 (Verify the Result)
使用javac编译器编译类如下 -
C:\JAVA>javac Java8Tester.java
现在运行Java8Tester如下 -
C:\JAVA>java Java8Tester
它应该产生以下输出 -
Base64 Encoded String (Basic) :VHV0b3JpYWxzUG9pbnQ/amF2YTg=
Original String: IoWiki?java8
Base64 Encoded String (URL) :VHV0b3JpYWxzUG9pbnQ_amF2YTg=
Base64 Encoded String (MIME) :YmU3NWY2ODktNGM5YS00ODlmLWI2MTUtZTVkOTk2YzQ1Njk1Y2EwZTg2OTEtMmRiZC00YTQ1LWJl
NTctMTI1MWUwMTk0ZWQyNDE0NDAwYjgtYTYxOS00NDY5LTllYTctNjc1YzE3YWJhZTk1MTQ2MDQz
NDItOTAyOC00ZWI0LThlOTYtZWU5YzcwNWQyYzVhMTQxMWRjYTMtY2MwNi00MzU0LTg0MTgtNGQ1
MDkwYjdiMzg2ZTY0OWU5MmUtZmNkYS00YWEwLTg0MjQtYThiOTQxNDQ2YzhhNTVhYWExZjItNjU2
Mi00YmM4LTk2ZGYtMDE4YmY5ZDZhMjkwMzM3MWUzNDMtMmQ3MS00MDczLWI0Y2UtMTQxODE0MGU5
YjdmYTVlODUxYzItN2NmOS00N2UyLWIyODQtMThlMWVkYTY4M2Q1YjE3YTMyYmItZjllMS00MTFk
LWJiM2UtM2JhYzUxYzI5OWI4