目录

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 接口是一个带有方法测试(Object)的函数接口,用于返回布尔值。 此接口表示对象被测试为true或false。

使用您选择的任何编辑器创建以下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
↑回到顶部↑
WIKI教程 @2018