文档章节

Java版本更新历史(ing)

z
 zhpuxin
发布于 2017/07/28 10:16
字数 7265
阅读 1
收藏 1

历史版本特性

JDK Version 1.0

开发代号为Oak(橡树),于1996-01-23发行.

 

JDK Version 1.1

于1997-02-19发行.

引入的新特性包括:

引入JDBC(Java DataBase Connectivity);

支持内部类;

引入Java Bean;

引入RMI(Remote Method Invocation)

引入反射(仅用于内省)

 

J2SE Version 1.2

开发代号为Playground(操场),于1998-12-08发行.

引入的新特性包括:

引入集合框架;

对字符串常量做内存映射;

引入JIT(Just In Time)编译器

引入对打包的Java文件进行数字签名;

引入控制授权访问系统资源的策略工具;

引入JFC(Java Foundation Classes), 包括Swing 1.0, 拖放和Java2D类库;

引入Java插件;

在JDBC中引入可滚动结果集,BLOB,CLOB,批量更新和用户自定义类型;

在Applet中添加声音支持.

 

J2SE Version 1.3

开发代号为Kestrel(红隼),于2000-05-08发行.

引入的新特性包括:

引入Java Sound API;

jar文件索引;

对Java的各个方面都做了大量优化和增强.

 

J2SE Version 1.4

开发代号Merlin(隼),于2004-02-06(首次在JCP下发行).

引入的新特性包括:

XML处理;

Java打印服务;

引入Logging API;

引入Java Web Start;

引入JDBC 3.0 API;

引入断言;

引入Preferences API;

引入链式异常处理;

支持IPV6;

支持正则表达式;

引入Imgae I/O API.

 

Java Version SE 5.0

开发代号为Tiger(老虎),于2004-09-30发行.

引入的新特性包括:

引入泛型;

增强循环,可以使用迭代方式;

自动装箱与自动拆箱;

类型安全的枚举;

可变参数;

静态引入;

元数据(注解);

引入Instrumentation

 

Java Version SE 6

开发代号为Mustang(野马),于2006-12-11发行.

引入的新特性包括:

支持脚本语言

引入JDBC 4.0API;

引入Java Compiler API;

可插拔注解;

增加对Native PKI(Public Key Infrastructure), Java GSS(Generic Security Service),Kerberos和LDAP(Lightweight Directory Access Protocol)支持

继承Web Services;

 

Java Version SE 7

开发代号是Dolphin(海豚),于2011-07-28发行.

引入的新特性包括:

switch语句块中允许以字符串作为分支条件;

在创建泛型对象时应用类型推断;

在一个语句块中捕获多种异常;

支持动态语言;

支持try-with-resources(在一个语句块中捕获多种异常)();

<参考: http://blog.csdn.net/hengyunabc/article/details/18459463>

引入Java NIO.2开发包;

数值类型可以用二进制字符串表示,并且可以在字符串表示中添加下划线;

钻石型语法(在创建泛型对象时应用类型推断);

null值得自动处理.

 

Java Version SE 8

引入Lambda 表达式;

管道和流;

新的日期和时间 API;

默认的方法;

类型注解;

Nashorn javascript引擎;

并行累加器;

并行操作

内存错误移除

TLS SNI 服务器名称标识(Server Name Identification)

  • Lambda Expressions
  • Pipelines and Streams
  • Date and Time API
  • Default Methods
  • Type Annotations
  • Nashhorn JavaScript Engine
  • Concurrent Accumulators
  • Parallel operations
  • PermGen Error Removed
  • TLS SNI

 

特性详细内容

Java 5

1,泛型(Generics):

  引入泛型之后,允许指定集合里元素的类型,免去了强制类型转换,并且能在编译时刻进行类型检查的好处.

Parameterized Type作为范围和返回值,Generic是vararg,annotation,enumeration,collection的基石.

  A, 类型安全

    抛弃List,Map,使用List<T>,Map<K,V> 给他们添加元素或者使用iterator<T> 遍历时, 编译期间就可以给你检查出类型错误.

  B, 方法参数和返回值加上了Type

    抛弃List,Map,使用List<T>,Map<K,V>

  C, 不需要类型转换

    List<String> list = new ArrayList<String>();

    String str = list.get(1);

  D, 类型通配符"?"

    假设一个打印List<T>中元素的方法printList,我们希望任何类型T的List<T>都可以被打印;

代码:

1 public void printList(List<?> list,PrintStream out)throws IOException{
2 for(Iterator<?> i=list.iterator();i.hasNext();){
3 System.out.println(i.next.toString());
4 }
5 
6 }

如果通配符?让我们的参数类型过于广泛,我们可以把List<?>、Iterator<?> 修改为

      List<? Extends Number>、Iterator<? Extends Number>限制一下它。

 

2, 枚举类型 Enumeration:

3, 自动装箱拆箱( 自动类型包装和解包 ) autoboxing & unboxing:

  简单地说是类型自动转换.

  自动装包: 基本类型自动转为包装类(int ---Integer)

  自动拆包: 包装类自动转为基本类型(Integer ---int)

4, 可变参数varargs(varargs number of arguments)

  参数类型相同时, 把重载函数合并到一起了.

  如: public void test(object... objs){

    for(Object obj:objs){

      System.out.println(obj);

    }

  }

5, Annotations 它是Java中的metadata(注释)

  A, tiger(代号)终于定义的三种标准annotation

    a, Override

    指出某个method覆盖了superclass的method当你要覆盖的方法名拼写错时编译不通过

    b, Deprecated

    指出某个method或element类型的使用是被阻止的,子类将不能覆盖该方法

    c, SupressWarnings

    关闭class, method, field, variable初始化的编译器警告, 比如: List没有使用Generic, 则@SuppressWarnings("unchecked")去掉编译期警告.

  B, 自定义annotation

    public @interface Marked{}

  C, meta-annotation

    或者说annotation的annotation

    四种标准的meta-annotation全部定义在java.lang.annotation包中:

    a, Target

    指定所定义的annotation可以用在哪些程序单元上

    如果Target没有指定, 则表示该annotation可以使用在任意程序单元上

代码:

 

复制代码

 1  @Retention(RetentionPolicy.RUNTIME)  
 2  @Target({ ElementType.FIELD, ElementType.METHOD })  
 3  public @interface RejectEmpty {  
 4     /** hint title used in error message */  
 5   String value() default "";  
 6  }  
 7   
 8  @Retention(RetentionPolicy.RUNTIME)  
 9  @Target( { ElementType.FIELD, ElementType.METHOD })  
10  public @interface AcceptInt {  
11   int min() default Integer.MIN_VALUE;  
12  int max() default Integer.MAX_VALUE;  
13    String hint() default "";  
14  }

复制代码

 

    b, Retention

    指出Java编译期如何对待annotation

    annotation可以被编译期丢掉,或者在编译过的class文件中

    在annotation被保留时,他也指定是否会在JVM加载class时读取该annotation

代码:

1 @Retention(RetentionPolicy.SOURCE)  // Annotation会被编译期丢弃    
2 public @interface TODO1 {}    
3  @Retention(RetentionPolicy.CLASS)   // Annotation保留在class文件中,但会被JVM忽略    
4  public @interface TODO2 {}    
5 @Retention(RetentionPolicy.RUNTIME) // Annotation保留在class文件中且会被JVM读取    
6  public @interface TODO3 {}

    c,Documented

    指出被定义的annotation被视为所熟悉的程序单元的公开API之一

    被@Documented标注的annotation会在javadoc中显示,这在annotation对它标注的元素被客户端使用时有影响时起作用

    d,Inherited

    该meta-annotation应用于目标为class的annotation类型上,被此annotattion标注的class会自动继承父类的annotation

 

 

  D, Annotation的反射

    我们发现java.lang.Class有许多与Annotation的反射相关的方法,如getAnnotations, isAnnotationpresent 我们可以利用Annotation反射来做许多事情, 比如自定义Annotation来做Model对象验证

代码:

 

复制代码

 1      @Retention(RetentionPolicy.RUNTIME)    
 2      @Target({ ElementType.FIELD, ElementType.METHOD })    
 3     public @interface RejectEmpty {    
 4        /** hint title used in error message */    
 5       String value() default "";    
 6      }    
 7          
 8      @Retention(RetentionPolicy.RUNTIME)    
 9     @Target( { ElementType.FIELD, ElementType.METHOD })    
10      public @interface AcceptInt {    
11          int min() default Integer.MIN_VALUE;    
12         int max() default Integer.MAX_VALUE;    
13      String hint() default "";    
14     }

复制代码

 

使用@RejectEmpty和@AcceptInt标注我们的Model的field, 然后利用反射来做Model验证

 

6,新的迭代语句 (for(int n:numbers))

7, 静态导入( import satic )

8, 新的格式化方法( java.util.Formatter )

formatter.format('"Remaining account balance:$%.2f",balance');

9,新的线程模型和并发库Thread Framework

 HashMap的替代者ConcurrentHashMap和ArrayList的替代者CopyOnWriteArrayList 在大并发量读取时采用java.util.concurrent包里的一些类会让大家满意BlockingQueue、Callable、Executor、Semaphore...

 

Java 6

1、引入了一个支持脚本引擎的新框架

2、UI的增强

3、对WebService支持的增强(JAX-WS2.0和JAXB2.0)

4、一系列新的安全相关的增强

5、JDBC4.0

6、Compiler API

7、通用的Annotations支持

 

Java 7

 1,switch中可以使用字串了

 

复制代码

 1     String s = "test";   
 2     switch (s) {   
 3     case "test" :   
 4     System.out.println("test");   
 5     case "test1" :   
 6     System.out.println("test1");   
 7     break ;   
 8     default :   
 9     System.out.println("break");   
10     break ;   
11     }

复制代码

 

2.运用List<String> tempList = new ArrayList<>(); 即泛型实例化类型自动推断

3.语法上支持集合,而不一定是数组

final List<Integer> piDigits = [1,2.3,4,5];

4, 新增一些获取环境信息的工具方法

1 File System.getJavaIoTempDir() // IO临时文件夹
2 File System.getJavaHomeDir() // JRE的安装目录
3 File System.getUserHomeDie() // 当前用户目录
4 File System.getUserDie() // 启动java进程时所在的目录5

 

5,Boolean类型反转, 空指针安全, 参与位运算

复制代码

1     Boolean Booleans.negate(Boolean booleanObj)  
2     True => False , False => True, Null => Null  
3     boolean Booleans.and(boolean[] array)  
4     boolean Booleans.or(boolean[] array)  
5     boolean Booleans.xor(boolean[] array)  
6     boolean Booleans.and(Boolean[] array)  
7     boolean Booleans.or(Boolean[] array)  
8     boolean Booleans.xor(Boolean[] array)

复制代码

6, 两个char间的equals

1 boolean Character.equalsIgnoreCase(char ch1, char ch2)

7,安全的加减乘除

复制代码

 1 int Math.safeToInt(long value)  
 2 int Math.safeNegate(int value)  
 3 long Math.safeSubtract(long value1, int value2)  
 4 long Math.safeSubtract(long value1, long value2)  
 5 int Math.safeMultiply(int value1, int value2)  
 6 long Math.safeMultiply(long value1, int value2)  
 7 long Math.safeMultiply(long value1, long value2)  
 8 long Math.safeNegate(long value)  
 9 int Math.safeAdd(int value1, int value2)  
10 long Math.safeAdd(long value1, int value2)  
11 long Math.safeAdd(long value1, long value2)  
12 int Math.safeSubtract(int value1, int value2)

复制代码

 

8,map集合支持并发请求, 且可以写成Map map = {name:"xxx",age:18};

 

 

Java 8

java8的新特性逐一列出, 并将使用简单的代码示例来知道你如何使用默认接口方法, lambda表达式, 方法引用以及多重Annotation, 之后你将会学到最新的API上的改进,比如流, 函数式接口 Map以及全新的日期API

一, 接口的默认方法

Java 8允许我们给接口添加一个非抽象的方法实现, 只需要使用default关键字即可, 这个特征又叫做扩展方法, 示例如下:

1 interface Formula{
2  double calculate(int a);
3  default double sqrt(int a){
4   return Math.sqrt(a);
5 }      
6 }

Formula接口在拥有calculate方法之外同时还定义了sqrt方法,实现了Formula接口的子类只需要实现一个calculate方法,默认方法sqrt将在子类上可以直接使用

复制代码

1 Formula formula = new Formula() {  
2     @Override  
3     public double calculate(int a) {  
4         return sqrt(a * 100);  
5     }  
6 };    
7  formula.calculate(100);     // 100.0  
8 formula.sqrt(16);           // 4.0 

复制代码

文中的formula被实现为一个匿名类的实例,该代码非常容易理解,6行代码实现了计算 sqrt(a * 100)。在下一节中,我们将会看到实现单方法接口的更简单的做法。译者注: 在Java中只有单继承,如果要让一个类赋予新的特性,通常是使用接口来实现,在C++中支持多继承,允许一个子类同时具有多个父类的接口与功能,在其他 语言中,让一个类同时具有其他的可复用代码的方法叫做mixin。新的Java 8 的这个特新在编译器实现的角度上来说更加接近Scala的trait。 在C#中也有名为扩展方法的概念,允许给已存在的类型扩展方法,和Java 8的这个在语义上有差别。

 

二, Lambda表达式

首先看看在老版本的Java中是如何排列字符串的:

复制代码

1 List<String> names = Arrays.adList("peter","anna","mike","xenia");
2     Collections.sort(names, new Comparator<String>(){
3     @Override
4     public int compare(String a, String b){
5         return b.compareTo(a);
6     }
7 });

复制代码

只需要给静态方法 Collections.sort 传入一个List对象以及一个比较器来按指定顺序排列。通常做法都是创建一个匿名的比较器对象然后将其传递给sort方法。在Java 8 中你就没必要使用这种传统的匿名对象的方式了,Java 8提供了更简洁的语法,lambda表达式:

1 Collections.sort(names, (String a, String b) -> {
2     reuturn b.compareTo(a);
3 });

对于函数体只有一行代码的,你可以去掉大括号{}以及return关键字, 还可以更短

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

更短

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

Java编译器可以自动推导出参数类型,所以你可以不用再写一次类型。接下来我们看看lambda表达式还能作出什么更方便的东西来:

 

 

三, 函数式接口

Lambda表达式是如何在java的类型系统中表示的呢?每一个lambda表达式都对应一个类型,通常是接口类型。而“函数式接口”是指仅仅只 包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为 默认方法 不算抽象方法,所以你也可以给你的函数式接口添加默认方法。

我们可以将lambda表达式当作任意只包含一个抽象方法的接口类型,确保你的接口一定达到这个要求,你只需要给你的接口添加 @FunctionalInterface 注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的

复制代码

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

复制代码

需要注意如果@FunctionalInterface如果没有指定,上面的代码也是对的。

译者注 将lambda表达式映射到一个单方法的接口上,这种做法在Java 8之前就有别的语言实现,比如Rhino JavaScript解 释器,如果一个函数参数接收一个单方法的接口而你传递的是一个function,Rhino 解释器会自动做一个单接口的实例到function的适配器,典型的应用场景有 org.w3c.dom.events.EventTarget 的addEventListener 第二个参数 EventListener。

 

四, 方法与构造函数引用

前一节中的代码还可以通过静态方法引用来表示:

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

Java 8 允许你使用 :: 关键字来传递方法或者构造函数引用,上面的代码展示了如何引用一个静态方法,我们也可以引用一个对象的方法:

converter = something::startsWith;  
String converted = converter.convert("Java");  
System.out.println(converted);    // "J" 

 

接下来看看构造函数是如何使用::关键字来引用的,首先我们定义一个包含多个构造函数的简单类:

复制代码

1     class Person {  
2         String firstName;  
3         String lastName;    
4          Person() {}   
5          Person(String firstName, String lastName) {  
6             this.firstName = firstName;  
7             this.lastName = lastName;  
8         }  
9     }

复制代码

接下来我们指定一个用来创建Person对象的对象工厂接口:

1     interface PersonFactory<P extends Person> {  
2         P create(String firstName, String lastName);  
3     }

这里我们使用构造函数引用来将他们关联起来,而不是实现一个完整的工厂:

1     PersonFactory<Person> personFactory = Person::new;  
2     Person person = personFactory.create("Peter", "Parker");

我们只需要使用 Person::new 来获取Person类构造函数的引用,Java编译器会自动根据PersonFactory.create方法的签名来选择合适的构造函数。

五, Lambda作用域

在lambda表达式中访问外层作用域和老版本的匿名对象中的方式很相似。你可以直接访问标记了final的外层局部变量,或者实例的字段以及静态变量。

六, 访问局部变量

我们可以直接在lambda表达式中访问外层的局部变量:

1 final int num =1;
2 Coverter<Integer , String> stringConverter = (x) -> String.valueOf(x+num);
3 stringConverter.conver(2);

但是和匿名对象不同的是,这里的变量num可以不用声明为final,该代码同样正确:

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

不过这里的num必须不可被后面的代码修改(即隐性的具有final的语义),例如下面的就无法编译:

错误代码:

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

在lambda表达式中试图修改num同样是不允许的。

 

七, 访问对象字段与静态变量

和本地变量不同的是,lambda内部对于实例的字段以及静态变量是即可读又可写。该行为和匿名对象是一致的:

复制代码

 1     class Lambda4 {  
 2         static int outerStaticNum;  
 3         int outerNum;    
 4          void testScopes() {  
 5             Converter<Integer, String> stringConverter1 = (from) -> {  
 6                 outerNum = 23;  
 7                 return String.valueOf(from);  
 8             };   
 9              Converter<Integer, String> stringConverter2 = (from) -> {  
10                 outerStaticNum = 72;  
11                 return String.valueOf(from);  
12             };  
13         }  
14     }

复制代码

八、访问接口的默认方法


Lambda表达式中是无法访问到default修饰的接口方法(java新特性的接口方法)的,以下代码将无法编译:

错误代码:

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

==

点拨:   lambda的使用是建立在函数式接口的基础上, 函数式接口就是只需要实现一个方法的接口,其他的方法可以使用default,或者静态方法实现具体实现

为什么用lambda, 因为书写方便,简洁, 不需要写接口的实现类,然后创建对象,也不需要用匿名类写一堆代码.

==

JDK 1.8 API包含了很多内建的函数式接口,在老Java中常用到的比如Comparator或者Runnable接口,这些接口都增加了@FunctionalInterface注解以便能用在lambda上。
Java 8 API同样还提供了很多全新的函数式接口来让工作更加方便,有一些接口是来自Google Guava库里的,即便你对这些很熟悉了,还是有必要看看这些是如何扩展到lambda上使用的。

Predicate接口

Predicate 接口只有一个参数,返回boolean类型。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非):

 

复制代码

1 Predicate<String> predicate = (s) -> s.length() > 0;    
2  predicate.test("foo");              // true  
3 predicate.negate().test("foo");     // false   
4  Predicate<Boolean> nonNull = Objects::nonNull;  
5 Predicate<Boolean> isNull = Objects::isNull;   
6  Predicate<String> isEmpty = String::isEmpty;  
7 Predicate<String> isNotEmpty = isEmpty.negate();

复制代码

 

Function 接口
Function 接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen):

 

1 Function 接口
2 Function 接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen):

Supplier 接口
Supplier 接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数

1 Supplier<Person> personSupplier = Person::new;  
2 personSupplier.get();   // new Person

Consumer 接口
Consumer 接口表示执行在单个参数上的操作。

1     Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);  
2     greeter.accept(new Person("Luke", "Skywalker"));

Comparator 接口

Comparator 是老Java中的经典接口, Java 8在此之上添加了多种默认方法:

1 Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);    
2  Person p1 = new Person("John", "Doe");  
3 Person p2 = new Person("Alice", "Wonderland");   
4  comparator.compare(p1, p2);             // > 0  
5 comparator.reversed().compare(p1, p2);  // < 0 

Optional 接口

 

Optional 不是函数是接口,这是个用来防止NullPointerException异常的辅助类型,这是下一届中将要用到的重要概念,现在先简单的看看这个接口能干什么:

Optional 被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8中,不推荐你返回null而是返回Optional。

1     Optional<String> optional = Optional.of("bam");    
2      optional.isPresent();           // true  
3     optional.get();                 // "bam"  
4     optional.orElse("fallback");    // "bam"   
5      optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"   

Stream 接口

 

java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。 Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。Stream的操作可以串行执行或者并行执行。

首先看看Stream是怎么用,首先创建实例代码的用到的数据List:

复制代码

1 List<String> stringCollection = new ArrayList<>();  
2 stringCollection.add("ddd2");  
3 stringCollection.add("aaa2");  
4 stringCollection.add("bbb1");  
5 stringCollection.add("aaa1");  
6 stringCollection.add("bbb3");  
7 stringCollection.add("ccc");  
8 stringCollection.add("bbb2");  
9 stringCollection.add("ddd1");

复制代码

Java 8扩展了集合类,可以通过 Collection.stream() 或者 Collection.parallelStream() 来创建一个Stream。下面几节将详细解释常用的Stream操作:

Filter 过滤
过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如 forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他 Stream操作。

1 stringCollection  
2     .stream()  
3     .filter((s) -> s.startsWith("a"))  
4     .forEach(System.out::println);    
5  // "aaa2", "aaa1"

Sort 排序
排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。

1 stringCollection  
2     .stream()  
3     .sorted()  
4     .filter((s) -> s.startsWith("a"))  
5     .forEach(System.out::println);    
6  // "aaa1", "aaa2"  

需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据stringCollection是不会被修改的:

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

Map 映射
中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。

1 stringCollection  
2     .stream()  
3     .map(String::toUpperCase)  
4     .sorted((a, b) -> b.compareTo(a))  
5     .forEach(System.out::println);    
6  // "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"  

Match 匹配
Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。

复制代码

 1 boolean anyStartsWithA =   
 2     stringCollection  
 3         .stream()  
 4         .anyMatch((s) -> s.startsWith("a"));    
 5  System.out.println(anyStartsWithA);      // true   
 6  boolean allStartsWithA =   
 7     stringCollection  
 8         .stream()  
 9         .allMatch((s) -> s.startsWith("a"));   
10  System.out.println(allStartsWithA);      // false   
11  boolean noneStartsWithZ =   
12     stringCollection  
13         .stream()  
14         .noneMatch((s) -> s.startsWith("z"));   
15  System.out.println(noneStartsWithZ);      // true 

复制代码

Count 计数

 

计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。

1     long startsWithB =   
2         stringCollection  
3             .stream()  
4             .filter((s) -> s.startsWith("b"))  
5             .count();    
6      System.out.println(startsWithB);    // 3   

Reduce 规约
这是一个最终操作,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规越后的结果是通过Optional接口表示的:

复制代码

1 Optional<String> reduced =  
2     stringCollection  
3         .stream()  
4         .sorted()  
5         .reduce((s1, s2) -> s1 + "#" + s2);    
6  reduced.ifPresent(System.out::println);  
7 // "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2" 

复制代码

并行Streams

 

前面提到过Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。

 

下面的例子展示了是如何通过并行Stream来提升性能:

首先我们创建一个没有重复元素的大表:

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

然后我们计算一下排序这个Stream要耗时多久,

 

串行排序:

1 long t0 = System.nanoTime();    
2  long count = values.stream().sorted().count();  
3 System.out.println(count);   
4  long t1 = System.nanoTime();   
5  long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);  
6 System.out.println(String.format("sequential sort took: %d ms", millis));

// 串行耗时: 899 ms
并行排序:

1 long t0 = System.nanoTime();    
2  long count = values.parallelStream().sorted().count();  
3 System.out.println(count);   
4  long t1 = System.nanoTime();   
5  long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);  
6 System.out.println(String.format("parallel sort took: %d ms", millis));

// 并行排序耗时: 472 ms
上面两个代码几乎是一样的,但是并行版的快了50%之多,唯一需要做的改动就是将stream()改为parallelStream()。
Map
前面提到过,Map类型不支持stream,不过Map提供了一些新的有用的方法来处理一些日常任务。

1     Map<Integer, String> map = new HashMap<>();    
2      for (int i = 0; i < 10; i++) {  
3         map.putIfAbsent(i, "val" + i);  
4     }

map.forEach((id, val) -> System.out.println(val));
以上代码很容易理解, putIfAbsent 不需要我们做额外的存在性检查,而forEach则接收一个Consumer接口来对map里的每一个键值对进行操作。

下面的例子展示了map上的其他有用的函数:

复制代码

1     map.computeIfPresent(3, (num, val) -> val + num);  
2     map.get(3);             // val33    
3      map.computeIfPresent(9, (num, val) -> null);  
4     map.containsKey(9);     // false   
5      map.computeIfAbsent(23, num -> "val" + num);  
6     map.containsKey(23);    // true   
7      map.computeIfAbsent(3, num -> "bam");  
8     map.get(3);             // val33   

复制代码

接下来展示如何在Map里删除一个键值全都匹配的项:

1 map.remove(3, "val3");  
2 map.get(3);             // val33    
3  map.remove(3, "val33");  
4 map.get(3);             // null 

另外一个有用的方法:

1     map.getOrDefault(42, "not found");  // not found  

对Map的元素做合并也变得很容易了:

1     map.merge(9, "val9", (value, newValue) -> value.concat(newValue));  
2     map.get(9);             // val9    
3      map.merge(9, "concat", (value, newValue) -> value.concat(newValue));  
4     map.get(9);             // val9concat   

Merge做的事情是如果键名不存在则插入,否则则对原键对应的值做合并操作并重新插入到map中。

 

九、Date API

Java 8 在包java.time下包含了一组全新的时间日期API。新的日期API和开源的Joda-Time库差不多,但又不完全一样,下面的例子展示了这组新API里最重要的一些部分:

Clock 时钟

 

Clock类提供了访问当前日期和时间的方法,Clock是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数。某一个特定的时间点也可以使用Instant类来表示,Instant类也可以用来创建老的java.util.Date对象。

1     Clock clock = Clock.systemDefaultZone();  
2     long millis = clock.millis();    
3      Instant instant = clock.instant();  
4     Date legacyDate = Date.from(instant);   // legacy java.util.Date   

 

Timezones 时区

 

在新API中时区使用ZoneId来表示。时区可以很方便的使用静态方法of来获取到。 时区定义了到UTS时间的时间差,在Instant时间点对象到本地日期对象之间转换的时候是极其重要的。

复制代码

1 System.out.println(ZoneId.getAvailableZoneIds());  
2 // prints all available timezone ids    
3  ZoneId zone1 = ZoneId.of("Europe/Berlin");  
4 ZoneId zone2 = ZoneId.of("Brazil/East");  
5 System.out.println(zone1.getRules());  
6 System.out.println(zone2.getRules());   
7  // ZoneRules[currentStandardOffset=+01:00]  
8 // ZoneRules[currentStandardOffset=-03:00] 

复制代码

LocalTime 本地时间

LocalTime 定义了一个没有时区信息的时间,例如 晚上10点,或者 17:30:15。下面的例子使用前面代码创建的时区创建了两个本地时间。之后比较时间并以小时和分钟为单位计算两个时间的时间差:

复制代码

1 LocalTime now1 = LocalTime.now(zone1);  
2 LocalTime now2 = LocalTime.now(zone2);    
3  System.out.println(now1.isBefore(now2));  // false   
4  long hoursBetween = ChronoUnit.HOURS.between(now1, now2);  
5 long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);   
6  System.out.println(hoursBetween);       // -3  
7 System.out.println(minutesBetween);     // -239 

复制代码

LocalTime 提供了多种工厂方法来简化对象的创建,包括解析时间字符串。

复制代码

1     LocalTime late = LocalTime.of(23, 59, 59);  
2     System.out.println(late);       // 23:59:59    
3      DateTimeFormatter germanFormatter =  
4         DateTimeFormatter  
5             .ofLocalizedTime(FormatStyle.SHORT)  
6             .withLocale(Locale.GERMAN);   
7      LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);  
8     System.out.println(leetTime);   // 13:37   

复制代码

LocalDate 本地日期

LocalDate 表示了一个确切的日期,比如 2014-03-11。该对象值是不可变的,用起来和LocalTime基本一致。下面的例子展示了如何给Date对象加减天/月/年。另外要注意的是这些对象是不可变的,操作返回的总是一个新实例。

1     LocalDate today = LocalDate.now();  
2     LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);  
3     LocalDate yesterday = tomorrow.minusDays(2);    
4      LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);  
5     DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();

System.out.println(dayOfWeek);    // FRIDAY
从字符串解析一个LocalDate类型和解析LocalTime一样简单:

1 DateTimeFormatter germanFormatter =  
2     DateTimeFormatter  
3         .ofLocalizedDate(FormatStyle.MEDIUM)  
4         .withLocale(Locale.GERMAN);    
5  LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);  
6 System.out.println(xmas);   // 2014-12-24 

LocalDateTime 本地日期时间
LocalDateTime 同时表示了时间和日期,相当于前两节内容合并到一个对象上了。LocalDateTime和LocalTime还有LocalDate一样,都是不可变的。LocalDateTime提供了一些能访问具体字段的方法

复制代码

1     LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);    
2      DayOfWeek dayOfWeek = sylvester.getDayOfWeek();  
3     System.out.println(dayOfWeek);      // WEDNESDAY   
4      Month month = sylvester.getMonth();  
5     System.out.println(month);          // DECEMBER   
6      long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);  
7     System.out.println(minuteOfDay);    // 1439   

复制代码

只要附加上时区信息,就可以将其转换为一个时间点Instant对象,Instant时间点对象可以很容易的转换为老式的java.util.Date。

1 Instant instant = sylvester  
2         .atZone(ZoneId.systemDefault())  
3         .toInstant();    
4  Date legacyDate = Date.from(instant);  
5 System.out.println(legacyDate);     // Wed Dec 31 23:59:59 CET 2014 

格式化LocalDateTime和格式化时间和日期一样的,除了使用预定义好的格式外,我们也可以自己定义格式:

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是不可变的,所以它是线程安全的。
关于时间日期格式的详细信息:http://download.java.net/jdk8/docs/api/java/time/format/DateTimeFormatter.html

 

十、Annotation 注解

在Java 8中支持多重注解了,先看个例子来理解一下是什么意思。
首先定义一个包装类Hints注解用来放置一组具体的Hint注解:

复制代码

1     @interface Hints {  
2         Hint[] value();  
3     }    
4      @Repeatable(Hints.class)  
5     @interface Hint {  
6         String value();  
7     }

复制代码

Java 8允许我们把同一个类型的注解使用多次,只需要给该注解标注一下@Repeatable即可。例 1: 使用包装类当容器来存多个注解(老方法)

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

例 2:使用多重注解(新方法)

1     @Hint("hint1")  
2     @Hint("hint2")  
3     class Person {}

第二个例子里java编译器会隐性的帮你定义好@Hints注解,了解这一点有助于你用反射来获取这些信息:

1 Hint hint = Person.class.getAnnotation(Hint.class);  
2 System.out.println(hint);                   // null    
3  Hints hints1 = Person.class.getAnnotation(Hints.class);  
4 System.out.println(hints1.value().length);  // 2   
5  Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);  
6 System.out.println(hints2.length);          // 2 

即便我们没有在Person类上定义@Hints注解,我们还是可以通过 getAnnotation(Hints.class) 来获取 @Hints注解,更加方便的方法是使用 getAnnotationsByType 可以直接获取到所有的@Hint注解。
另外Java 8的注解还增加到两种新的target上了:

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

 十一, lambda与集合类批处理操作

原来:

1 for(Object o: list) { // 外部迭代
2         System.out.println(o);
3     }

现在:

1 list.forEach(o -> {System.out.println(o);}); //forEach函数实现内部迭代

或者

list.forEach(o -> System.out.println(o)); //forEach函数实现内部迭代

 

本文转载自:http://www.cnblogs.com/qinggege/p/6580788.html

z
粉丝 0
博文 3
码字总数 0
作品 0
大连
私信 提问
Java粗浅认识-java简介

简介 java 是1991年是为了交互式电视设计,sun公司在1996年发布java1.0版本,java的特点是“一次编译,到处执行”,借助于 在浏览器上可执行java apple,渐渐流行起来,在1999年发布java1.2...

zoQ
2018/12/14
0
0
NetBeans 6.8 的第一个补丁包发布

NetBeans 刚刚发布了一个 6.8 版本的补丁包,这是 6.8 的一个更新版本。该版本主要修正了 C/C++、EJB、EAR、GUI 构建、IDE平台、Java和Java调试、JavaFX SDK、Java持久化、Java Web应用开发、...

红薯
2010/02/08
488
1
2019年1月已到,Java 8 要收费了吗?

根据此前开源中国发起的 Java 版本使用调查,国内的 Java 主力版本仍是 Java 8,有近 70% 的用户表示仍在使用 Java 8。所以对于「Java 8 是否要收费」这个问题,十分有必要阐述清楚,以消除不...

局长
01/22
33.6K
73
Java 9 的新特性发布

Oracle 已经在开发 Java 9,Java 9 主要的增强内容之前已经出现在 OpenJDK 之上。而现在 Oracle 发布的是真正的新特性,前一个 JDK 9 的 early access 发行版 被标注了“修复bug和小增强”。...

oschina
2014/08/19
39.5K
62
我该用 Java 12 还是坚持 Java 11?

本文经授权转自开源中国 距离 Java 11 的正式发布已过去一个多月,而 Java 12 也正在赶来的路上。根据此前开源中国发起的一项关于开发者使用的 Java 版本的调查(https://www.oschina.net/q...

CSDN资讯
2018/11/05
0
0

没有更多内容

加载失败,请刷新页面

加载更多

作为一个(IT)程序员!聊天没有话题?试试这十二种技巧

首先呢?我是一名程序员,经常性和同事没话题。 因为每天都会有自己的任务要做,程序员对于其他行业来说;是相对来说比较忙的。你会经常看到程序员在发呆、调试密密麻麻代码、红色报错发呆;...

小英子wep
57分钟前
11
0
【SpringBoot】产生背景及简介

一、SpringBoot介绍 Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程,该框架使用了特定的方式来进行配置,从而使开发人员不再需要...

zw965
今天
4
0
简述并发编程分为三个核心问题:分工、同步、互斥。

总的来说,并发编程可以总结为三个核心问题:分工、同步、互斥。 所谓分工指的是如何高效地拆解任务并分配给线程,而同步指的是线程之间如何协作,互斥则是保证同一时刻只允许一个线程访问共...

dust8080
今天
6
0
OSChina 周四乱弹 —— 当你简历注水但还是找到了工作

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @花间小酌 :#今日歌曲推荐# 分享成龙的单曲《男儿当自强》。 《男儿当自强》- 成龙 手机党少年们想听歌,请使劲儿戳(这里) @hxg2016 :刚在...

小小编辑
今天
3.2K
22
靠写代码赚钱的一些门路

作者 @mezod 译者 @josephchang10 如今,通过自己的代码去赚钱变得越来越简单,不过对很多人来说依然还是很难,因为他们不知道有哪些门路。 今天给大家分享一个精彩的 GitHub 库,这个库整理...

高级农民工
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部