<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; 新特性</title>
	<atom:link href="https://blog.gmem.cc/tag/%e6%96%b0%e7%89%b9%e6%80%a7/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Sun, 19 Apr 2026 07:54:29 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>Java8新特性</title>
		<link>https://blog.gmem.cc/java8-new-features</link>
		<comments>https://blog.gmem.cc/java8-new-features#comments</comments>
		<pubDate>Thu, 12 Jun 2014 01:40:32 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[新特性]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=7949</guid>
		<description><![CDATA[<p>接口默认、静态方法 传统的Java接口是一组方法的集合，任何实现者都必须实现所有方法。这意味着库的设计者很难更新接口——这会导致很多实现类无法使用。 Java 8对接口的语法做了很大的改造： 支持提供了实现的静态方法，这让为接口提供一个工具类的做法不再必要，工具方法可以直接放在接口中，例如Java8的Stream包含很多静态方法： [crayon-69e6179363fba510308781/] 支持提供了实现的接口方法：如果实现接口的类不明确提供实现，那么会继承来自接口的实现。这个特性让扩展接口变得很方便，例如Java8为List接口扩展了sort方法： [crayon-69e6179363fc1182482369/]  为Collection接口扩展了stream方法： [crayon-69e6179363fc3346716433/] 在先前，使用抽象类来提供缺省接口实现的模式，现在很大程度上可以由接口来代替。但是带有缺省实现的接口和抽象类还是有很大的不同： 类可以实现多个接口，包括带有默认方法的接口；但是类只能继承一个抽象类 抽象类可以具有状态，即实例变量，接口则不能有实例变量 默认方法的用途 可以用来设计“可选方法（Optional methods）” ，可以有意的让接口继承者不需要实现某些方法，例如在迭代器中，remove就是可选方法： [crayon-69e6179363fc5588566980/] 默认方法也可以用来实现多重行为继承（Multiple inheritance of behavior）。由于Java允许实现多个接口，因此自然就可以从多个接口的默认方法中继承行为。 <a class="read-more" href="https://blog.gmem.cc/java8-new-features">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/java8-new-features">Java8新特性</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">接口默认、静态方法</span></div>
<p>传统的Java接口是一组方法的集合，任何实现者都必须实现所有方法。这意味着库的设计者很难更新接口——这会导致很多实现类无法使用。</p>
<p>Java 8对接口的语法做了很大的改造：</p>
<ol>
<li>支持提供了实现的静态方法，这让为接口提供一个工具类的做法不再必要，工具方法可以直接放在接口中，例如Java8的Stream包含很多静态方法：<br />
<pre class="crayon-plain-tag">public interface Stream&lt;T&gt; extends BaseStream&lt;T, Stream&lt;T&gt;&gt; {
    public static&lt;T&gt; Builder&lt;T&gt; builder() {
        return new Streams.StreamBuilderImpl&lt;&gt;();
    }
    public static&lt;T&gt; Stream&lt;T&gt; empty() {
        return StreamSupport.stream(Spliterators.&lt;T&gt;emptySpliterator(), false);
    }
}</pre>
</li>
<li>支持提供了实现的接口方法：如果实现接口的类不明确提供实现，那么会继承来自接口的实现。这个特性让扩展接口变得很方便，例如Java8为List接口扩展了sort方法：<br />
<pre class="crayon-plain-tag">default void sort(Comparator&lt;? super E&gt; c){
    Collections.sort(this, c);
}</pre></p>
<p> 为Collection接口扩展了stream方法：</p>
<pre class="crayon-plain-tag">default Stream&lt;E&gt; stream() {
    return StreamSupport.stream(spliterator(), false);
}</pre>
</li>
</ol>
<p>在先前，使用抽象类来提供缺省接口实现的模式，现在很大程度上可以由接口来代替。但是带有缺省实现的接口和抽象类还是有很大的不同：
<ol>
<li>类可以实现多个接口，包括带有默认方法的接口；但是类只能继承一个抽象类</li>
<li>抽象类可以具有状态，即实例变量，接口则不能有实例变量</li>
</ol>
<div class="blog_h3"><span class="graybg">默认方法的用途</span></div>
<p>可以用来设计“可选方法（Optional methods）” ，可以有意的让接口继承者不需要实现某些方法，例如在迭代器中，remove就是可选方法：</p>
<pre class="crayon-plain-tag">interface Iterator&lt;T&gt; {
    boolean hasNext();
    T next();
    default void remove() {
        throw new UnsupportedOperationException();
    }
}</pre>
<p>默认方法也可以用来实现<span style="background-color: #c0c0c0;">多重行为继承</span>（Multiple inheritance of behavior）。由于Java允许实现多个接口，因此自然就可以从多个接口的默认方法中继承行为。</p>
<div class="blog_h3"><span class="graybg">默认方法冲突解析</span></div>
<p>Java 8 中一个类可以从父类、接口继承得到签名相同的方法，那么到底哪个生效呢？以下是几个规则：</p>
<ol>
<li>类优先原则：声明在当前类或者父类中的方法，总是比接口默认方法优先</li>
<li>子接口优先原则：如果两个接口A、B，B继承A，那么B中的默认方法比A的优先级高</li>
<li>如果冲突仍然存在，那么当前类必须覆盖冲突的方法，在其中明确调用某个接口的默认方法 ：<br />
<pre class="crayon-plain-tag">interface IHello{
    default void say(){
    }
}

interface IWorld{
    default void say(){
    }
}

class HelloWorld implements IHello, IWorld{
    @Override
    public void say(){
        IWorld.super.say(); //明确调用第二个接口的方法
    }
}</pre>
</li>
</ol>
<p>需要注意的是，Java 8中不会存在<span style="background-color: #c0c0c0;">钻石继承</span>的问题，根本原因在于接口继承不存在对象拷贝的问题。这个可以对比C++钻石问题去理解，在C++中D继承B、C，后两者由继承自A，就意味着D拷贝了一份B、C的字段和函数，以及两份来自A的字段和函数，这就意味着冲突，到底使用来自B的A还是来自C的A？C++使用特殊的语法来去歧义。</p>
<div class="blog_h2"><span class="graybg">Lambda表达式</span></div>
<div class="blog_h3"><span class="graybg">关于行为参数化（Behavior parameterization）</span></div>
<p>在策略模式中，Strategy（策略，即行为）与Context是组合关系，前者作为后者的属性。所谓参数化，就是把这种组合关系改变为参数传递关系，即通过方法参数来传递策略类。</p>
<p>行为参数化的好处不言而喻，它可以让Context之间进一步解耦，关联关系被取消。不会因为共享变量Strategy导致线程安全性问题，每次调用都可以灵活切换Strategy。</p>
<p>行为参数化这种设计思想当然从Java诞生开始就能够支持，但是在Java8中，这种思想被更多的添加到JDK的API中：</p>
<pre class="crayon-plain-tag">public interface Iterable&lt;T&gt; {
    default void forEach(Consumer&lt;? super T&gt; action);
}
public interface List&lt;E&gt; extends Collection&lt;E&gt; {
    default void sort(Comparator&lt;? super E&gt; c) {
}</pre>
<p>Lambda表达式让行为参数化变得简洁：</p>
<pre class="crayon-plain-tag">List&lt;String&gt; strList = new ArrayList&lt;String&gt;();
//使用Lambda表达式：
strList.sort( ( String s1, String s2 ) -&gt; s1.length() - s2.length() );
//使用匿名类
strList.sort( new Comparator&lt;String&gt;() {
    public int compare( String s1, String s2 )
    {
        return s1.length() - s2.length();
    }
} );</pre>
<div class="blog_h3"><span class="graybg">Lambda语法</span></div>
<p>Lambda表达式可以理解为可以被<span style="background-color: #c0c0c0;">作为出入参传递</span>的<span style="background-color: #c0c0c0;">匿名函数</span>的<span style="background-color: #c0c0c0;">简便</span>表示形式，很多语言例如C++、Python都支持Lambda表达式，Java 8首次将其引入的Java语言中，Lambda表达式的语法如下：</p>
<pre class="crayon-plain-tag">(Lambda parameters) -&gt; expression

(Lambda parameters) -&gt; {
    statements;
}</pre>
<p>Lambda表达式可以用于任何<span style="background-color: #c0c0c0;">函数式接口</span>的上下文中，包括作为方法参数或者返回值。</p>
<div class="blog_h3"><span class="graybg">函数式接口（Functional interface）</span></div>
<p>所谓函数式接口，是指<span style="background-color: #c0c0c0;">只有一个抽象方法</span>的接口。Java 8中接口可以有默认实现，有多个方法，但是只有一个没有默认实现的接口，也被认为是函数式接口。</p>
<p>使用注解@FunctionalInterface可以限制一个接口为函数式接口，如果不符合函数式接口的规范，将无法通过编译：</p>
<pre class="crayon-plain-tag">@FunctionalInterface
public interface Functional {
    void operation();
    default void defaultOperation(){}
}</pre>
<p>函数式接口的<span style="background-color: #c0c0c0;">抽象方法的签名</span>潜在的定义了Lambda表达式的结构， 我们称这样的抽象方法为函数描述符（Function Descriptor）。</p>
<p>Java 8 中提供了若干常用的函数式接口：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">函数式接口</td>
<td style="width: 150px; text-align: center;">函数描述符</td>
<td style="text-align: center;">针对基本类型的特化接口 </td>
</tr>
</thead>
<tbody>
<tr>
<td>Predicate&lt;T&gt;</td>
<td>T -&gt; boolean</td>
<td>IntPredicate, LongPredicate, DoublePredicate</td>
</tr>
<tr>
<td>Consumer&lt;T&gt;</td>
<td>T -&gt; void</td>
<td>IntConsumer, LongConsumer, DoubleConsumer</td>
</tr>
<tr>
<td>Function&lt;T, R&gt;</td>
<td>T -&gt; R</td>
<td>IntFunction&lt;R&gt;, IntToDoubleFunction, IntToLongFunction,LongFunction&lt;R&gt;, LongToDoubleFunction,LongToIntFunction, DoubleFunction&lt;R&gt;, ToIntFunction&lt;T&gt;,ToDoubleFunction&lt;T&gt;, ToLongFunction&lt;T&gt;</td>
</tr>
<tr>
<td>Supplier&lt;T&gt;</td>
<td>() -&gt; T</td>
<td>BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier</td>
</tr>
<tr>
<td>UnaryOperator&lt;T&gt;</td>
<td>T -&gt; T</td>
<td>IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator</td>
</tr>
<tr>
<td>BinaryOperator&lt;T&gt;</td>
<td>(T, T) -&gt; T</td>
<td>IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator</td>
</tr>
<tr>
<td>BiPredicate&lt;L, R&gt;</td>
<td>(L, R) -&gt; boolean</td>
<td> </td>
</tr>
<tr>
<td>BiConsumer&lt;T, U&gt;</td>
<td>(T, U) -&gt; void</td>
<td>ObjIntConsumer&lt;T&gt;, ObjLongConsumer&lt;T&gt;,ObjDoubleConsumer&lt;T&gt;</td>
</tr>
<tr>
<td>BiFunction&lt;T, U, R&gt;</td>
<td>(T, U) -&gt; R</td>
<td>ToIntBiFunction&lt;T, U&gt;, ToLongBiFunction&lt;T, U&gt;,ToDoubleBiFunction&lt;T, U&gt;</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">类型检查与推断</span></div>
<p>Lambda的类型（即函数式接口的类型）是根据使用Lambda的上下文来由编译器推断的，上下文期望的Lambda类型被称为目标类型（Target type）。</p>
<p>同一Lambda可能和多个抽象方法签名兼容的函数式接口关联，以一条语句作为其Lambda体的表达式，与返回void 的函数描述符兼容（如果参数类型兼容），例如：</p>
<pre class="crayon-plain-tag">//断言接口要求boolean返回值
Predicate&lt;String&gt; p = s -&gt; list.add(s);
//消费接口要求void返回值，兼容
Consumer&lt;String&gt; b = s -&gt; list.add(s);</pre>
<p>编译器可以根据上下文进行类型推断，因此Lambda表达式的编写可以被简化：</p>
<pre class="crayon-plain-tag">//省略Lambda参数的类型声明
Comparator&lt;String&gt; c = ( s1, s2 ) -&gt; s1.length() - s2.length();</pre>
<div class="blog_h3"><span class="graybg">使用局部变量</span></div>
<p>类似于匿名类，Lambda可以使用在外部作用范围定义的局部变量，但是这些局部变量必须是<span style="background-color: #c0c0c0;">final</span>，或者相当于final（没有二次赋值语句）： </p>
<pre class="crayon-plain-tag">//下面的代码无法通过编译，因为hello被二次赋值
String hello = "Hello";
Runnable r = () -&gt; System.out.println( hello );
hello = "World";</pre>
<p>设置“final”限制的缘由是，局部变量是在线程的栈上分配的，那么作为返回值传递的Lambda可能被另外一个线程调用，这个调用可能发生在分配局部变量线程死亡之后，如果允许Lambda任意访问局部变量就会导致非法访问。Java的做法是允许Lambda访问局部变量的拷贝，而不是局部变量本身，final保证了拷贝与局部变量的一致性。 这一限制实质上也使Java不支持真正的闭包。</p>
<div class="blog_h3"><span class="graybg">方法引用</span></div>
<p>Java 8引入了方法引用，这允许重用已经定义的方法并传递它们，就像Lambda一样：</p>
<pre class="crayon-plain-tag">Comparator&lt;String&gt; c = java.util.Comparator.comparing( String::length );
//comparing需要的参数是函数式接口：Function&lt;? super T, ? extends U&gt;
//传递的是String::length
//编译器推导过程：(T) -&gt; return R
//第一个参数是实例方法的实例：(String s) -&gt; return R
//调用实例方法：(String s) -&gt; s.length()
//假设实例方法有入参，依次声明：(String s, Object arg0, Object arg1) -&gt; s.length(arg0, arg1)</pre>
<p><span style="background-color: #c0c0c0;">方法引用</span>本质上是<span style="background-color: #c0c0c0;">只调用了一个方法的Lambda</span>的缩写。上面的例子等价于：</p>
<pre class="crayon-plain-tag">Comparator&lt;String&gt; c = java.util.Comparator.comparing( ( String s ) -&gt; s.length() );</pre>
<p>方法引用的写法有三大类：</p>
<ol>
<li>对于静态方法的引用，例如<pre class="crayon-plain-tag">Integer::parseInt</pre>  </li>
<li>对于某个类型的实例方法的引用，例如<pre class="crayon-plain-tag">String.length</pre> 。具体调用针对的对象实例由Lambda的第一个参数来提供，该方法引用等价于<pre class="crayon-plain-tag">(String s) -&gt; s.length()</pre> </li>
<li>对于某个对象的实例方法的引用，例如<pre class="crayon-plain-tag">"".length</pre> 。具体调用针对的对象实例由上下文中的变量提供，该方法引用等价于<pre class="crayon-plain-tag">() -&gt; "".length()</pre> </li>
</ol>
<div class="blog_h3"><span class="graybg">构造器引用</span></div>
<p>构造器引用类似于静态方法引用，形式为：<pre class="crayon-plain-tag">Class::new</pre> ，示例如下：</p>
<pre class="crayon-plain-tag">Function&lt;String, String&gt; c = String::new; //函数引用
c = ( s ) -&gt; new String( s ); //等价的Lambda表达式
c.apply( "Hello" ); //调用

Supplier&lt;Object&gt; o = Object::new;
o = () -&gt; new Object();</pre>
<div class="blog_h3"><span class="graybg">简化Lambda编写的API</span></div>
<p>Java 8 中引入了很多简化Lambda编写的API：</p>
<pre class="crayon-plain-tag">public interface java.util.Comparator&lt;T&gt; {
    //返回这样的比较器：通过调用被比较对象的方法来抽取Comparable Key，进而根据Key来比较
    public static &lt;T, U&gt; Comparator&lt;T&gt; comparing();
    //反转当前比较器的顺序
    default Comparator&lt;T&gt; reversed();
    //链式的比较器：多重比较
    default Comparator&lt;T&gt; thenComparing(Comparator&lt;? super T&gt; other);
}

public interface Predicate&lt;T&gt; {
    //逻辑否、非、或
    default Predicate&lt;T&gt; and(Predicate&lt;? super T&gt; other);
    default Predicate&lt;T&gt; or(Predicate&lt;? super T&gt; other);
    default Predicate&lt;T&gt; negate();
}

Function&lt;Integer, Integer&gt; f = x -&gt; x + 1;
Function&lt;Integer, Integer&gt; g = x -&gt; x * 2;
Function&lt;Integer, Integer&gt; h = f.andThen( g ); //高阶函数：g(f(x))
Function&lt;Integer, Integer&gt; i = f.compose( g ); //高阶函数：f(g(x))
h.apply( 1 );</pre>
<div class="blog_h2"><span class="graybg">类型注解</span></div>
<p>在Java8中，注解可以写在任何使用类型的地方：</p>
<pre class="crayon-plain-tag">// 声明，允许该注解被用在使用类型的地方、泛型类型参数上
@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
public @interface Test {
}</pre>
<p> 在类型、泛型类型参数上使用注解的例子：</p>
<pre class="crayon-plain-tag">// 任何类型的前面都可以附加注解（前提是该注解的ElementType允许）
@Encrypted String data;
List&lt;@NonNull String&gt; strings;
graph = (@Immutable Graph) tmpGraph;
Collection&lt;? super @Existing File&gt; c;</pre>
<p>类型注解可以用来在编译期进行代码检查，提高代码质量，对运行时没有影响。</p>
<div class="blog_h2"><span class="graybg">Stream API</span></div>
<p>Java 8引入了java.util.stream.Stream，它允许<span style="background-color: #c0c0c0;">声明式的</span>操控集合。Stream能够<span style="background-color: #c0c0c0;">透明的</span>使用多处理器<span style="background-color: #c0c0c0;">并行处理</span>。 下面是一个简明的例子：</p>
<pre class="crayon-plain-tag">List&lt;Cat&gt; cat = new ArrayList&lt;&gt;();
List&lt;String&gt; littleCats = cat  //链式调用
		.parallelStream()  //集合接口可以获取Stream，stream()返回串行处理的流
		.filter(c-&gt;c.getAge()&lt;2)  //内部迭代
		.sorted(Comparator.comparing(Cat::getWeight))
		.map(Cat::getName)
		.collect(toList());  //直到collect调用之前，不会做任何实际操作</pre>
<p>所谓Stream，是指来自一个源（Source）的元素的序列，这些元素支持数据处理操作：</p>
<ol>
<li>元素序列：Collection也是元素序列，但是Collection关心的是数据，Stream则关心操作</li>
<li>源：可以是Collection、数组、I/O资源</li>
<li>操作：Stream提供类似数据库的操作，例如filter、map、reduce、find、match、sort、limit。这些操作可以串行或者并行的被执行</li>
</ol>
<p>Stream还支持链式调用，这允许你串起多个操作；Stream支持内部迭代，不需要手工编写迭代代码。</p>
<div class="blog_h3"><span class="graybg">Stream提供的操作</span></div>
<p>Stream支持两类操作：中间操作、终端操作。前者构建处理管道（Pipeline），后者导致管道被执行。中间操作包括：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">操作 </td>
<td style="width: 150px; text-align: center;">函数描述符</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>filter</td>
<td>T -&gt; boolean</td>
<td>过滤Stream中的元素</td>
</tr>
<tr>
<td>distinct</td>
<td> </td>
<td>筛除重复元素</td>
</tr>
<tr>
<td>skip</td>
<td> </td>
<td>跳过Stream中开始的N个元素</td>
</tr>
<tr>
<td>limit</td>
<td> </td>
<td>限制Stream中元素的个数</td>
</tr>
<tr>
<td>map</td>
<td>T -&gt; R</td>
<td>把Stream中的每个元素使用map函数转换</td>
</tr>
<tr>
<td>flatMap</td>
<td>T -&gt; Stream&lt;R&gt;</td>
<td>把Stream中的每个元素使用map函数转换，转换结果必须是Stream，所有这些结果Stream被合并为一个Stream</td>
</tr>
<tr>
<td>sorted</td>
<td>(T, T) -&gt; int</td>
<td>对Stream中的元素进行排序</td>
</tr>
<tr>
<td>anyMatch</td>
<td>T -&gt; boolean</td>
<td>是否存在任何一个元素满足断言</td>
</tr>
<tr>
<td>allMatch</td>
<td>T -&gt; boolean</td>
<td>是否所有元素都满足断言</td>
</tr>
<tr>
<td>findAny</td>
<td> </td>
<td>寻找流中的一个元素</td>
</tr>
<tr>
<td>findFirst</td>
<td> </td>
<td>得到流中第一个元素</td>
</tr>
</tbody>
</table>
<p>终端操作包括：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;"> 操作</td>
<td style="width: 150px; text-align: center;">函数描述符</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>forEach</td>
<td>T -&gt; void</td>
<td>消费Stream中的每一个元素</td>
</tr>
<tr>
<td>count</td>
<td> </td>
<td>统计Stream中元素的数量</td>
</tr>
<tr>
<td>collect</td>
<td> </td>
<td>Reduce Stream为集合、映射甚至一个Integer</td>
</tr>
<tr>
<td>reduce</td>
<td>(T, T) -&gt; T</td>
<td>化简，接收两个元素作为输入，把一个元素作为输出。化简操作会不断进行，直到只剩一个元素</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">Stream使用示例</span></div>
<pre class="crayon-plain-tag">import static java.util.stream.Collectors.*;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class StreamExample
{

    private static List&lt;Cat&gt; prepareData()
    {
        List&lt;Cat&gt; cats = new ArrayList&lt;&gt;();
        cats.add( new Cat( "叮当猫", 19, 4000, "Yellow" ) );
        cats.add( new Cat( "多啦A梦", 38, 5800, "Blue" ) );
        cats.add( new Cat( "蓝猫", 9, 3500, "Blue" ) );
        cats.add( new Cat( "汤姆猫", 58, 2900, "Blue" ) );
        cats.add( new Cat( "凯蒂猫", 31, 1800, "White" ) );
        cats.add( new Cat( "加菲猫", 27, 9600, "Yellow" ) );
        cats.add( new Cat( "甜甜私房猫", 6, 1200, "Grey" ) );
        cats.add( new Cat( "加菲猫", 27, 9600, "Yellow" ) );
        cats.add( new Cat( "加菲猫", 27, 9600, "Yellow" ) );
        return cats;
    }

    public static void main( String[] args ) throws IOException
    {
        List&lt;Cat&gt; cats = prepareData();

        /** 过滤和切片操作  **/
        //使用断言（Predicate）过滤数据
        List&lt;Cat&gt; overweight = cats.stream().filter( cat -&gt; cat.getWeight() &gt; 3000 ).collect( toList() );
        System.out.println( overweight ); //[叮当猫, 多啦A梦, 蓝猫, 加菲猫]
        //筛除重复元素
        List&lt;Cat&gt; distinct = cats.stream().distinct().collect( toList() );
        System.out.println( distinct ); //[叮当猫, 多啦A梦, 蓝猫, 汤姆猫, 凯蒂猫, 加菲猫, 甜甜私房猫]
        //截断Stream
        List&lt;Cat&gt; limit = cats.stream().limit( 3 ).collect( toList() );
        System.out.println( limit ); //[叮当猫, 多啦A梦, 蓝猫]
        //跳过Stream中起始的6个元素
        List&lt;Cat&gt; skip = cats.stream().skip( 6 ).collect( toList() );
        System.out.println( skip ); //[甜甜私房猫, 加菲猫, 加菲猫]

        /** 映射（Mapping）操作 **/
        //映射为颜色
        List&lt;String&gt; colors = cats.stream().map( Cat::getColor ).collect( Collectors.toList() );
        System.out.println( colors ); //[Yellow, Blue, Blue, Blue, White, Yellow, Grey, Yellow, Yellow]
        //扁平化映射：把元素映射为新Stream，然后把这个新Stream中的所有元素纳入当前Stream，并关闭新Stream
        List&lt;Character&gt; nameChars = cats
                .stream().map( Cat::getNameAsChars ) //元素映射为Character[]
                .flatMap( Arrays::stream ) //Character[]被映射为Character，这是1:N的映射方式
                .distinct().collect( toList() );
        System.out.println( nameChars );//[叮, 当, 猫, 多, 啦, A, 梦, 蓝, 汤, 姆, 凯, 蒂, 加, 菲, 甜, 私, 房]
        /** 查找与匹配操作 **/
        //判断流中是否存在匹配断言的元素
        System.out.println( cats.stream().anyMatch( cat -&gt; cat.getColor() == "White" ) ); //true
        //判断是否流中所有元素匹配断言
        System.out.println( cats.stream().allMatch( cat -&gt; cat.getAge() &lt; 100 ) ); //true
        //判断是否流中不存在任何元素匹配断言
        System.out.println( cats.stream().noneMatch( cat -&gt; cat.getAge() &gt; 100 ) ); //true
        //查找任何一个黄猫
        Optional&lt;Cat&gt; yellow = cats.stream().filter( cat -&gt; cat.getColor() == "Yellow" ).findAny();
        System.out.println( yellow.get() ); //叮当猫
        //查找第一个元素
        System.out.println( cats.stream().findFirst().get() ); //叮当猫

        /** 化简（Reducing）操作 **/
        //将两个元素化简为一个元素
        //求和：年龄之和，先映射再化简
        System.out.println( cats.stream().map( Cat::getAge ).reduce( ( a1, a2 ) -&gt; a1 + a2 ).get() ); //242
        //求最值：最老的猫
        System.out.println( cats.stream().reduce( ( c1, c2 ) -&gt; ( c1.getAge() &gt; c2.getAge() ) ? c1 : c2 ).get() );//汤姆猫
        /** 数字流 **/
        //避免基本类型装拆箱的开销
        IntStream primitiveStream = cats.stream().mapToInt( Cat::getAge );
        Stream&lt;Integer&gt; objectStream = primitiveStream.boxed(); //变回装箱类型的流
        OptionalInt max = primitiveStream.reduce( ( i, j ) -&gt; i &gt; j ? i : j );
        max.orElse( 0 );//如果没有最大值，设为0
        /** 构建流 **/
        IntStream.range( 0, 200 );//数字范围的流
        Stream&lt;String&gt; strStream = Stream.of( "Hello", "World" ); //字符串流
        IntStream intStream = Arrays.stream( new int[] { 1, 2, 3 } );//从数组生成流
        //NIO包支持Stream API
        Stream&lt;String&gt; fileLineStream = Files.lines( Paths.get( "~/ReadMe.txt" ), Charset.defaultCharset() );//行流
        Stream&lt;Integer&gt; infinite = Stream.iterate( 0, n -&gt; n + 2 ); //由函数提供的迭代流
        infinite.limit( 100 ).forEach( i -&gt; System.out.println( i ) );
        Stream.generate( Math::random ); //随机生成流
    }
}</pre>
<div class="blog_h3"><span class="graybg">Reducing的优势</span></div>
<p>化简操作比起逐步迭代的优势在于，它能够<span style="background-color: #c0c0c0;">很方便的支持并行处理</span>。 由于化简操作限定了依据两个输入导出一个输出，并且输入输出的类型一致，这让JDK能够自由的把巨大的流分配给多个线程进行化简操作。这种Map-Reduce思想在大数据处理框架中很普遍，例如Hadoop。</p>
<div class="blog_h3"><span class="graybg">Collector</span></div>
<p>Collector接口传递给Stream.collect()方法，可以用来构建Stream中元素的摘要信息。Collector也可以用来执行化简操作，对Stream元素进行化简。</p>
<p>Collector接口中的静态工厂方法用于创建预定义的Collector，这些Collector和Stream接口提供的功能有重叠：</p>
<pre class="crayon-plain-tag">List&lt;Cat&gt; cats = StreamExample.prepareData();
//求平均猫龄
cats.stream().collect( averagingInt( Cat::getAge ) );
//收集所有猫为列表，再转为数组
cats.stream().collect( collectingAndThen( toList(), List::toArray ) );
//求猫的总数
cats.stream().collect( counting() );
//按毛色分组，如果需要多级分组，为groupingBy添加第二个参数，类型为Collector
Map&lt;String, List&lt;Cat&gt;&gt; byColor = cats.stream().collect( groupingBy( Cat::getColor ) );
//把名字用逗号连接起来
cats.stream().map( Cat::getName ).collect( joining( "," ) );
//依据猫龄映射，然后取最大值
cats.stream().collect( mapping( Cat::getAge, maxBy( ( a1, a2 ) -&gt; a1 &gt; a2 ? a1 : a2 ) ) );
//依据是否白猫，分区为两个列表
Map&lt;Boolean, List&lt;Cat&gt;&gt; partition = cats.stream().collect( partitioningBy( cat -&gt; cat.getColor() == "White" ) );
//化简，取第一只猫
cats.stream().collect( reducing( ( c1, c2 ) -&gt; c1 ) );
//求总吨位
cats.stream().collect( summingInt( Cat::getWeight ) );</pre>
<p>Collector接口的主要方法如下：</p>
<pre class="crayon-plain-tag">/**
 * 收集器接口
 * @param &lt;T&gt; 需要进行化简操作的元素类型，即目标流的元素类型
 * @param &lt;A&gt; 易变的累计器类型（mutable accumulation type），化简的中间结果存放在其中
 * @param &lt;R&gt; 化简操作的结果类型
 */
public interface Collector&lt;T, A, R&gt;
{

    //该函数创建并返回中间结果容器
    Supplier&lt;A&gt; supplier();

    //该函数把一个元素折入结果容器
    BiConsumer&lt;A, T&gt; accumulator();

    //将中间结果进行最后的转换，得到最终结果
    Function&lt;A, R&gt; finisher();

    /**
     * 返回不变的，表示该收集器特征的集合
     * 特征包括：
     * 　　CONCURRENT 表示此收集器支持并发，即accumulator支持多线程并发访问
     * 　　UNORDERED 表示此收集器不会保持元素的顺序
     * 　　IDENTITY_FINISH 表示finisher是“等同的（identity）”函数，可以被省略，意味着可以直接从A造型为R
     */
    Set&lt;Characteristics&gt; characteristics();

    //支持接收两个中间结果，并合并之（折叠并返回其中一个，或者创建新的容器并返回）
    BinaryOperator&lt;A&gt; combiner();

}</pre>
<p>一个简单的Collector的实现：</p>
<pre class="crayon-plain-tag">import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import static java.util.stream.Collector.Characteristics.*;

public class AsListCollector&lt;T&gt; implements Collector&lt;T, List&lt;T&gt;, List&lt;T&gt;&gt;
{

    @Override
    public Supplier&lt;List&lt;T&gt;&gt; supplier()
    {
        // 提供中间结果容器
        return ArrayList::new;
    }

    @Override
    public BiConsumer&lt;List&lt;T&gt;, T&gt; accumulator()
    {
        // 把元素折叠到结果容器中
        return List::add;
    }

    @Override
    public BinaryOperator&lt;List&lt;T&gt;&gt; combiner()
    {
        return ( l1, l2 ) -&gt; {
            l1.addAll( l2 );
            return l1;
        };
    }

    @Override
    public Function&lt;List&lt;T&gt;, List&lt;T&gt;&gt; finisher()
    {
        //生成一个总是返回输入的函数
        return Function.identity();
    }

    @Override
    public Set&lt;java.util.stream.Collector.Characteristics&gt; characteristics()
    {
        return Collections.unmodifiableSet( EnumSet.of( IDENTITY_FINISH, CONCURRENT ) );
    }
}</pre>
<div class="blog_h3"><span class="graybg">并行处理</span></div>
<p>可以把当前Stream转换为等价的并行处理版本：<pre class="crayon-plain-tag">stream().parallel()</pre> ，反之亦可：<pre class="crayon-plain-tag">stream().sequential()</pre> 。反复调用串并行转换，则只有最后一个调用有意义：</p>
<pre class="crayon-plain-tag">stream.parallel()
    .filter(...)
    .sequential()
    .map(...)
    .parallel()  //Stream会并行处理
    .reduce();</pre>
<p>并行Stream在内部使用默认的ForkJoinPool，该Pool具有与处理器数量相当的线程。默认ForkJoinPool的线程大小可以被修改：</p>
<pre class="crayon-plain-tag">System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "16");</pre>
<div class="blog_h2"><span class="graybg">其它新特性</span></div>
<div class="blog_h3"><span class="graybg">Spliterator</span></div>
<p>这是Java 8 添加的新接口，表示可分割的迭代器（Splitable Iterator），它用于支持并行迭代：</p>
<pre class="crayon-plain-tag">public interface Spliterator&lt;T&gt;
{

    //如果仍有剩余元素，执行指定的函数并返回true；否则返回false
    boolean tryAdvance( Consumer&lt;? super T&gt; action );

    //如果当前迭代器可以分区，那么分区出一个新的迭代器，相关元素将不被当前迭代器覆盖
    Spliterator&lt;T&gt; trySplit();

    //返回一个估算的剩余元素大小
    long estimateSize();

    /**
     * 说明此迭代器的特征
     * ORDERED 元素具有定义好的顺序（例如来自List），因此迭代器在遍历、分割时保持顺序
     * DISTINCT 元素具有唯一性
     * SORTED 元素具有预定义的排序顺序
     * SIZED 迭代器的源具有准确的尺寸，因此estimateSize()返回精确值
     * NONNULL 元素不会为空
     * IMMUTABLE 迭代器的源不会变化，这隐含在遍历期间，元素不会被添加、删除或修改
     * CONCURRENT 迭代器的源可以被安全的并发修改，不需要额外的同步保护
     * SUBSIZED 任何分割出的子迭代器具有准确的尺寸
     */
    int characteristics();
}</pre>
<div class="blog_h3"><span class="graybg">使用Optional代替null</span></div>
<p> Java 8引入的Optional泛型类可以很好的避免一些NullPointerException或者null检查：</p>
<pre class="crayon-plain-tag">Company c = new Company();
c.getCeo().getAddress().getZip();//潜在可能的空指针异常
if ( c.getCeo() != null ) //冗长的空指针判断
{
    Person ceo = c.getCeo();
    if ( ceo.getAddress() != null )
    {
        Address addr = ceo.getAddress();
    }
}
Optional&lt;Person&gt; pnull = Optional.empty(); //持有一个空值
Optional&lt;Person&gt; ceo = Optional.of( new Person() );//持有一个对象，传入null立即抛出NullPointerException
Optional&lt;Person&gt; pnullable = Optional.ofNullable( new Person() );//持有一个对象，可以是null
//使用map函数可以安全的获取到Optional对象的属性，避免异常
Optional&lt;Address&gt; address = pnullable.map( Person::getAddress );</pre>
<p>如果想让链式的getter调用安全的返回，需要改造POJO：</p>
<pre class="crayon-plain-tag">class Address
{
    private String zip;
    public String getZip(){
        return zip;
    }
    public void setZip( String zip ){
        this.zip = zip;
    }
}
class Person{
    private Optional&lt;Address&gt; address;
    public Optional&lt;Address&gt; getAddress(){
        return address;
    }
    public void setAddress( Optional&lt;Address&gt; address ){
        this.address = address;
    }

}
class Company{
    private Optional&lt;Person&gt; ceo;
    public Optional&lt;Person&gt; getCeo(){
        return ceo;
    }
    public void setCeo( Optional&lt;Person&gt; ceo ){
        this.ceo = ceo;
    }
}</pre>
<p>然后使用map函数：</p>
<pre class="crayon-plain-tag">Optional&lt;Company&gt; c = Optional.empty();
String zip = 
    c.flatMap( Company::getCeo ) //flatMap防止optional容器的嵌套
     .flatMap( Person::getAddress )
     .map( Address::getZip )
     .orElse( "100044" );</pre>
<div class="blog_h3"><span class="graybg">新的时间日期API</span></div>
<pre class="crayon-plain-tag">/* 日期API */
LocalDate today = LocalDate.now(); //当前日期
LocalDate date = LocalDate.of( 2014, 6, 12 );//2014年6月12日
int year = date.getYear();//2014
Month month = date.getMonth();//JUNE
month.ordinal(); //5
int day = date.getDayOfMonth();//12
DayOfWeek dow = date.getDayOfWeek();//THURSDAY
dow.ordinal(); //3
int len = date.lengthOfMonth();//30
boolean leap = date.isLeapYear();//false
year = date.get( ChronoField.YEAR ); //2014
int mon = date.get( ChronoField.MONTH_OF_YEAR ); //6
int dowInt = date.get( ChronoField.DAY_OF_WEEK );//4
day = date.get( ChronoField.DAY_OF_MONTH );

/* 时间API */
LocalTime time = LocalTime.of( 23, 6, 30 );
int hour = time.getHour();//23
int minute = time.getMinute();//6
int second = time.getSecond();//30

/* 从文本解析日期和时间 */
date = LocalDate.parse( "2014-06-12" );
time = LocalTime.parse( "23:06:30" );

/* 合并使用日期时间 */
LocalDateTime dt1 = LocalDateTime.of( 2014, Month.JUNE, 12, 23, 6, 30 );
LocalDateTime dt2 = LocalDateTime.of( date, time );
LocalDateTime dt3 = date.atTime( 23, 6, 30 );
LocalDateTime dt4 = date.atTime( time );
LocalDateTime dt5 = time.atDate( date );</pre>
<div class="blog_h3"><span class="graybg">CompletableFuture</span></div>
<p>这是一种新的Future类，它可以手工明确的完成：</p>
<pre class="crayon-plain-tag">CompletableFuture&lt;Integer&gt; intFuture = new CompletableFuture&lt;&gt;();
new Thread( () -&gt; {
    sleep( 3 );
    intFuture.complete( 10 ); //手工完成，该方法只能调用一次，obtrudeValue()方法可以用来修改值
} ).start();
intFuture.get();//阻塞，直到三秒后</pre>
<p>CompletableFuture提供了若干静态工厂来创建实例：</p>
<pre class="crayon-plain-tag">//不提供Executor，则默认使用ForkJoinPool.commonPool()
static &lt;U&gt; CompletableFuture&lt;U&gt; supplyAsync(Supplier&lt;U&gt; supplier);
static &lt;U&gt; CompletableFuture&lt;U&gt; supplyAsync(Supplier&lt;U&gt; supplier, Executor executor);
static CompletableFuture&lt;Void&gt; runAsync(Runnable runnable);
static CompletableFuture&lt;Void&gt; runAsync(Runnable runnable, Executor executor);

//示例：
Cat cat = new Cat( "叮当猫", 19, 4000, "Yellow" );
CompletableFuture&lt;Integer&gt; ageFuture = CompletableFuture.supplyAsync( cat::getAge );</pre>
<p> CompletableFuture还支持在异步操作完毕后，执行若干操作</p>
<pre class="crayon-plain-tag">Cat cat = new Cat( "叮当猫", 19, 4000, "Yellow" );
CompletableFuture
        .supplyAsync( cat::getAge ) //异步获取猫龄
        .thenApply( String::valueOf )//得到猫龄后，转换为字符串，同步操作
        .thenAccept( System.out::println ); //转换成字符串后，打印之，同步操作</pre>
<p>异常处理也被支持：</p>
<pre class="crayon-plain-tag">CompletableFuture
        .supplyAsync( cat::getAge ) //异步获取猫龄
        .exceptionally( ex -&gt; 0 ); //如果获取猫龄出错，返回0</pre>
<p>多个异步操作甚至可以被串联起来，<span style="color: #000000;">thenCompose()方法可以用来构建异步处理管道，完全没有阻塞和等待：</span></p>
<pre class="crayon-plain-tag">CompletableFuture
        //异步获取猫龄
        .supplyAsync( cat::getAge )
        //异步的把猫龄转换为字符串
        .thenCompose( age -&gt; CompletableFuture.supplyAsync( age::toString ) )
        //再异步的打印猫龄的字符串
        .thenCompose( age -&gt; CompletableFuture.runAsync( () -&gt; System.out.println( age ) ) );</pre>
<p>可以让两个Future分别运行，在结果都可用时，执行一个回调：</p>
<pre class="crayon-plain-tag">CompletableFuture
        //异步获取猫龄
        .supplyAsync( cat::getAge )
        .thenCombine( //异步获取猫重
                CompletableFuture.supplyAsync( cat::getWeight ),
                //两个操作都完成后，计算重龄比
                ( age, weight ) -&gt; weight / age );</pre>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/java8-new-features">Java8新特性</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/java8-new-features/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Java7新特性</title>
		<link>https://blog.gmem.cc/java7-new-features</link>
		<comments>https://blog.gmem.cc/java7-new-features#comments</comments>
		<pubDate>Fri, 10 Feb 2012 09:18:59 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[NIO]]></category>
		<category><![CDATA[新特性]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=8010</guid>
		<description><![CDATA[<p>Switch语句支持字符串 [crayon-69e6179364afa584692015/] try()结构 [crayon-69e6179364afe670086117/] 多重捕获 [crayon-69e6179364b00268587651/] 二进制字面值 [crayon-69e6179364b02181142453/] 数字下划线分隔符 [crayon-69e6179364b04004912730/] 泛型类型自动推演 [crayon-69e6179364b07380384835/] Fork/Join框架 Fork/Join模式是处理并行编程的经典模式，Java 7将其集成到JDK中，Java7 的Fork/Join框架可以很好的利用多核CPU来完成复杂的计算任务。Fork/Join的思想是，把计算任务分为两个阶段： 分解阶段：把任务分解为多个不相关的小块，分别有多个线程进行计算 合并阶段：计算完成的小块被合并到最终结果中 Map/Reduce本质上是Fork/Join模式的一种变体，但是它主要用于分布式计算领域。 需要注意的是，Java7的Fork/Join框架支持递归的分解和合并，子任务可以继续分解为更细致的任务并在完成后合并，下图形象的说明了递归分解/合并过程： 下面是一个利用Fork/Join求和的例子，该例子中任务会不断分解子任务，直到需要相加的只剩两个数： [crayon-69e6179364b09418770029/] <a class="read-more" href="https://blog.gmem.cc/java7-new-features">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/java7-new-features">Java7新特性</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h3"><span class="graybg">Switch语句支持字符串</span></div>
<pre class="crayon-plain-tag">switch ( args[0] ){
    case "Hello":
        break;
    case "World":
        break;
    default:
        break;
}</pre>
<div class="blog_h3"><span class="graybg">try()结构</span></div>
<pre class="crayon-plain-tag">// try可以附加资源管理的代码，这些资源如果实现AutoCloseable接口，那么会被自动关闭
try (OutputStream out = System.out;InputStream in = System.in){}
catch ( IOException e ){}
finally{}</pre>
<div class="blog_h3"><span class="graybg">多重捕获</span></div>
<pre class="crayon-plain-tag">try{}
catch ( NullPointerException | IllegalArgumentException e ){}</pre>
<div class="blog_h3"><span class="graybg">二进制字面值</span></div>
<pre class="crayon-plain-tag">int i255 = 0b11111111;</pre>
<div class="blog_h3"><span class="graybg">数字下划线分隔符</span></div>
<pre class="crayon-plain-tag">long lightYear = 9454_254_955_488L;</pre>
<div class="blog_h3"><span class="graybg">泛型类型自动推演</span></div>
<pre class="crayon-plain-tag">//钻石操作符，参数化类型会根据上下文推断
List  = new ArrayList&lt;&gt;();
private static List genList(){
    return new ArrayList&lt;&gt;();
}</pre>
<div class="blog_h3"><span class="graybg">Fork/Join框架</span></div>
<p>Fork/Join模式是处理并行编程的经典模式，Java 7将其集成到JDK中，Java7 的Fork/Join框架可以很好的利用多核CPU来完成复杂的计算任务。Fork/Join的思想是，把计算任务分为两个阶段：</p>
<ol>
<li>分解阶段：把任务分解为多个不相关的小块，分别有多个线程进行计算</li>
<li>合并阶段：计算完成的小块被合并到最终结果中</li>
</ol>
<p>Map/Reduce本质上是Fork/Join模式的一种变体，但是它主要用于分布式计算领域。</p>
<p>需要注意的是，Java7的Fork/Join框架支持递归的分解和合并，子任务可以继续分解为更细致的任务并在完成后合并，下图形象的说明了递归分解/合并过程：<img class="aligncenter size-full wp-image-8016" src="https://blog.gmem.cc/wp-content/uploads/2012/02/join-fork.png" alt="join-fork" width="318" height="334" /></p>
<p>下面是一个利用Fork/Join求和的例子，该例子中任务会不断分解子任务，直到需要相加的只剩两个数：</p>
<pre class="crayon-plain-tag">public class SumTask extends RecursiveTask&lt;Long&gt;
{
    private static final long serialVersionUID = 1L;

    private SumProblem        problem;

    public SumTask( SumProblem problem )
    {
        this.problem = problem;
    }

    @Override
    protected Long compute()
    {
        if ( problem.numCount() &lt;= 2 )
        {
            //只剩下两个数的话，相加并返回
            return problem.solve();
        }
        else
        {
            //任务分割，把绝大部分计算内容切分出去
            SumProblem subProblem = problem.chop();
            //当然，类似于均分的方式来切分任务也是可以的
            SumTask subTask = new SumTask( subProblem );
            subTask.fork();//让子任务分支出去，即另外调度执行
            Long result = problem.solve(); //当前任务执行主任务
            return result + subTask.join(); //当前任务负责合并结果
            //注意，这里的join不要理解为线程的join，实际上，执行join时，当前任务会被执行线程放弃
        }
    }

}
public class ForkJoinTest
{
    public static void main( String[] args )
    {
        int processerCount = Runtime.getRuntime().availableProcessors();
        //ForkJoinPool负责发现并执行任务生成的子任务
        ForkJoinPool pool = new ForkJoinPool( processerCount );
        ForkJoinTask&lt;Long&gt; task = new SumTask( new SumProblem( 1L, 1000L ) );
        System.out.println( pool.invoke( task ) );
    }
}</pre>
<div class="blog_h3"><span class="graybg">NIO.2</span></div>
<p>Java New I/O是JDK1.4引入的新API，支持异步方式处理输入输出，大大改善了I/O性能。Java 7对NIO进行了很多的增强，此称为NIO.2。</p>
<p><a href="/java-network-programming">使用Java进行网络编程</a>一文包含了部分NIO2特性在网络编程方面的应用。</p>
<p>NIO.2引入了全新的软件包java.nio.file，下面是一些使用该包的样例代码：</p>
<pre class="crayon-plain-tag">//Path用来定位文件系统中的文件
Path path = FileSystems.getDefault().getPath( "F:/Temp/ReadMe.txt" );
//Files是一个工具类
Files.exists( path );//判断对应Path的文件是否存在
Files.newBufferedReader( path, Charset.forName( "UTF-8" ) ); //打开字符流

/*
 * WatchService可以用来监听特定类型的文件系统事件：
 * ENTRY_CREATE 目录中创建或者重命名（进入）了一个条目
 * ENTRY_DELETE 目录中删除或者重命名（出去）了一个条目
 * ENTRY_MODIFY 目录中的一个条目被修改
 */
path = FileSystems.getDefault().getPath( "F:/Temp/" );
WatchService watcher = FileSystems.getDefault().newWatchService();
WatchKey watchKey = path.register( watcher, StandardWatchEventKinds.ENTRY_DELETE );
for ( ;; )
{
    WatchKey key = watcher.take(); //阻塞，直到有事件发生
    if ( key.equals( watchKey ) )
        for ( WatchEvent&lt;?&gt; e : key.pollEvents() )
        {
            System.out.println( "File deleted: " + e.context() );
        }
    key.reset(); //重置Key以继续接收事件
}</pre>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/java7-new-features">Java7新特性</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/java7-new-features/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Java5新特性</title>
		<link>https://blog.gmem.cc/java5-new-features</link>
		<comments>https://blog.gmem.cc/java5-new-features#comments</comments>
		<pubDate>Fri, 27 Apr 2007 02:22:24 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[并发编程]]></category>
		<category><![CDATA[新特性]]></category>

		<guid isPermaLink="false">http://blog.gmem.cc/?p=8026</guid>
		<description><![CDATA[<p>泛型（Generics） 简介 泛型，也称为参数化类型，是很多语言支持的编程范式——在定义类型、接口或者方法时将类型作为参数。Java诞生以来最重大的语法改变当属泛型，它是Java5最重要的特性。Java泛型与C++的模板机制在形式上有些类似，但是Java泛型的实现方式——类型擦除导致二者有着根本性的不同。 使用泛型，可以获得以下好处： 在编译期获得更好的类型检查 消除不必要的强制转型代码 允许编程人员实现泛型算法 使用泛型机制设计的类，最常见的是容器类，Java5的集合框架大量使用了泛型机制（类似于C++的STL库）： [crayon-69e617936501d491746031/] 但是，任何字段、方法参数、方法返回值需要参数化的类型，都可以从泛型机制获益，例如下面这个通用的回调接口：  [crayon-69e6179365021867202376/] 类/接口的泛型化 所谓泛型类型（ generic type），是指通过类型参数化的类/接口，这种类型的声明语法为： [crayon-69e6179365023510307700/] 类型形参可以在类定义的任何地方引用。类型参数只是一个占位符，它可以表示任意一种非基本（non-primitive ）类型。根据惯例，类型形参使用单个大写字母表示，一般E表示“元素”、K表示“键”、N表示“数字”、T表示“类型”、V表示“值”，等等。 要在代码中使用一个泛型类型，必须进行泛型类型调用（generic type invocation），此时必须把类型形参（type parameter）替换为类型实参（ <a class="read-more" href="https://blog.gmem.cc/java5-new-features">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/java5-new-features">Java5新特性</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h2"><span class="graybg">泛型（Generics）</span></div>
<div class="blog_h3"><span class="graybg">简介</span></div>
<p>泛型，也称为参数化类型，是很多语言支持的编程范式——在定义类型、接口或者方法时将类型作为参数。Java诞生以来最重大的语法改变当属泛型，它是Java5最重要的特性。Java泛型与C++的模板机制在形式上有些类似，但是Java泛型的实现方式——类型擦除导致二者有着根本性的不同。</p>
<p>使用泛型，可以获得以下好处：</p>
<ol>
<li>在编译期获得更好的类型检查</li>
<li>消除不必要的强制转型代码</li>
<li>允许编程人员实现泛型算法</li>
</ol>
<p>使用泛型机制设计的类，最常见的是容器类，Java5的集合框架大量使用了泛型机制（类似于C++的STL库）：</p>
<pre class="crayon-plain-tag">public interface Collection&lt;E&gt;{};
public interface Set&lt;E&gt;{};
public interface List&lt;E&gt;{};
public class ArrayList&lt;E&gt; extends AbstractList&lt;E&gt; implements List&lt;E&gt;, RandomAccess, Cloneable, java.io.Serializable{};
public interface Map&lt;K,V&gt; {}</pre>
<p>但是，任何字段、方法参数、方法返回值需要参数化的类型，都可以从泛型机制获益，例如下面这个通用的回调接口： </p>
<pre class="crayon-plain-tag">interface Callback&lt;P,R&gt;{
    R invoke(P p);
}</pre>
<div class="blog_h3"><span class="graybg">类/接口的泛型化</span></div>
<p>所谓泛型类型（ generic type），是指通过类型参数化的类/接口，这种类型的声明语法为：</p>
<pre class="crayon-plain-tag">class ClassName&lt;T1,...Tn&gt;{
    // T1...Tn是将ClassName参数化的类型形参（type parameter），或者叫类型变量（type variables），可以有1-N个
}
// 示例：
class Container&lt;I&gt; {
    private I item;
    void put(I i) {
        item = i;
    }
    I get() {
        return item;
    }
}</pre>
<p>类型形参可以<span style="background-color: #c0c0c0;">在<strong><span style="background-color: #ccffcc;">类定义的</span></strong>任何地方引用</span>。类型参数只是一个占位符，它可以表示<span style="background-color: #c0c0c0;">任意一种非基本（non-primitive ）类型</span>。根据惯例，类型形参使用单个大写字母表示，一般E表示“元素”、K表示“键”、N表示“数字”、T表示“类型”、V表示“值”，等等。</p>
<p>要在代码中使用一个泛型类型，必须进行泛型类型调用（generic type invocation），此时必须把类型形参（type parameter）替换为类型实参（ type argument），后者是真实的Java类型：</p>
<pre class="crayon-plain-tag">Container&lt;Number&gt; cn = new Container&lt;Number&gt;();
// Java 7引入了钻石操作符，可以根据cn的变量类型推倒构造器的type argument：
Container&lt;Number&gt; cn = new Container&lt;&gt;();</pre>
<p>作为类型实参的Java类型，其本身也可以是泛型类型：</p>
<pre class="crayon-plain-tag">// 不管嵌套多少层，泛型类型的类型参数都不能是占位符
Container&lt;List&lt;String&gt;&gt; cn = new Container&lt;&gt;();
// 下面的语句非法，E不是可解析的真实类型
Container&lt;List&lt;E&gt;&gt; cn = new Container&lt;&gt;();</pre>
<div class="blog_h3"><span class="graybg">原始类型（Raw Types）</span></div>
<p>所谓原始类型，是指在不指定类型实参的情况下使用泛型，例如：<pre class="crayon-plain-tag">ArrayList list = new ArrayList()</pre> 。原始类型为依赖的Java库提供的向后兼容。</p>
<p>混合使用泛型、原始类型，会在编译时得到未检查错误信息（Unchecked Error Messages），所谓Unchecked表示编译器无法得到足够的信息来完成类型推断以确保安全性。要禁止此类错误信息，可以使用编译选项<pre class="crayon-plain-tag">-Xlint:-unchecked</pre> 或者在代码上添加注解<pre class="crayon-plain-tag">@SuppressWarnings("unchecked")</pre> </p>
<div class="blog_h3"><span class="graybg">方法的泛型化</span></div>
<p>不但类可以设计为泛型，方法也可以。方法可以使用类声明的泛型形参，亦可<span style="background-color: #c0c0c0;">自己声明泛型参数</span>，声明自己的泛型参数的方法称为泛型方法（Generic method），例如：</p>
<pre class="crayon-plain-tag">public class Math {
    // 泛型方法，泛型形参列表必须在返回值之前声明：
    public static &lt;RET, ARG0, ARG1&gt; RET plus( ARG0 arg0, ARG1 arg1 ) {
        return (RET) null;
    }
}</pre>
<p>调用泛型方法的完整语法如下：</p>
<pre class="crayon-plain-tag">Integer result = Math.&lt;Integer, Double, Double&gt;plus( 1.1, 1.2 );</pre>
<p>指定泛型实参列表 不是必须的，通常编译器能够根据上下文推断。编译器的这种能力称为type inference。</p>
<div class="blog_h3"><span class="graybg">受限（Bounded）泛型形参</span></div>
<p>Java泛型支持<span style="background-color: #c0c0c0;">限制可作为泛型实参的类型的范围</span>。例如上面的plus方法，可能仅仅支持数字类型。要声明受限泛型形参，需要使用extends关键字：</p>
<pre class="crayon-plain-tag">public static &lt;RET, ARG0 extends Number, ARG1 extends Number&gt; RET plus( ARG0 arg0, ARG1 arg1 ) {
    return (RET) null;
}</pre>
<p>使用受限形参，方法体可以对形参的类型进行假设，这样就可以执行有意义的方法调用了：</p>
<pre class="crayon-plain-tag">public static &lt;T extends Comparable&lt;T&gt;&gt; int countGreaterThan( T[] anArray, T elem ) {
    int count = 0;
    for ( T e : anArray )
        // 调用比较方法，因为T必须是Comparable的子类型
        if ( e.compareTo( elem ) &gt; 0 )
            ++count;
    return count;
} </pre>
<div class="blog_h3"><span class="graybg">多重限制（Multiple Bounds） </span></div>
<p>可以在限制泛型实参必须是某个类型或者其子类型的同时，限制它必须实现某些接口：</p>
<pre class="crayon-plain-tag">// 多个接口用 &amp; 符号分隔
public static &lt;T extends Entity &amp; Serializable&gt; void save(T t){}</pre>
<div class="blog_h3"><span class="graybg">泛型通配符（Wildcard）</span></div>
<p>可以在<span style="background-color: #c0c0c0;">字段、方法形参、变量</span>的声明中使用<span style="background-color: #c0c0c0;">（不受限的）泛型通配符</span>，使用<pre class="crayon-plain-tag">?</pre> 代替实际类型即为泛型通配符，表示<span style="background-color: #c0c0c0;">泛型实参的类型未知</span>。使用通配符后：</p>
<ol>
<li>泛型类中返回参数类型的方法，类型自动变为Object</li>
<li>泛型类中需要参数类型作为入参的方法，将不能接受任何对象</li>
</ol>
<p>例如泛型类：</p>
<pre class="crayon-plain-tag">public class Value&lt;T&gt; {
    private T t;
    public Value( T t ){
        this.t = t;
    }
    public void set( T t ){
        this.t = t;
    }
    public T get(){
        return t;
    }
}</pre>
<p>如果声明参数类型为通配符：</p>
<pre class="crayon-plain-tag">Value&lt;?&gt; value = new Value&lt;Integer&gt;( 1 );
Object o = value.get(); //可以通过编译，你不知道是什么类型，所以我们给你一个根类型
value.set( 1 ); //无法通过编译，不是我们不限制类型，而是你不知道是什么类型</pre>
<p>就意味着，你<span style="background-color: #c0c0c0;">可以</span>从泛型对<span style="background-color: #c0c0c0;">获取</span>参数类型对象，但是<span style="background-color: #c0c0c0;">却不能提供</span>了。 </p>
<div class="blog_h3"><span class="graybg">受限泛型通配符</span></div>
<p>Java 5还支持<span style="background-color: #c0c0c0;">受限泛型通配符</span>的声明，所谓受限是指，变量/返回值/参数的泛型参数必须满足限制：</p>
<ol>
<li><pre class="crayon-plain-tag">Type&lt;? extends PType&gt;</pre> ：上限通配符（Upper Bounded Wildcard），表示变量的类型参数必须是PType或者PType的子类型</li>
<li><pre class="crayon-plain-tag">Type&lt;? super PType&gt;</pre> ：下限通配符（Lower Bounded Wildcard），表示变量的类型参数必须是PType或者PType的超类型</li>
</ol>
<p>在设计一个接收泛型入参的接口时，如果你需要向入参提供一个对象，可以使用下限通配符，因为这可以确保你提供的对象可以被接受：</p>
<pre class="crayon-plain-tag">public static void addNumbers( List&lt;? super Integer&gt; list ) {
    // list的实参可以是Integer，或者Number，或者Object，但是必然能容纳int类型
    for ( int i = 1; i &lt;= 10; i++ ) {
        list.add( i );
    }
}</pre>
<p>在设计一个接收泛型入参的接口时，如果你需要使用入参提供的对象，可以使用上限通配符，这样你可以假设对象的类型：</p>
<pre class="crayon-plain-tag">public static void process( List&lt;? extends Number&gt; list ) {
    // list的实参可以是Integer，或者Number，或者Double，但是都可以针对其调用Number的接口
    for ( Number n : list ) {
        n.intValue();
    }
}</pre>
<p>上面的两条，就是所谓的PECS原则：当泛型对象作为生产者（对外提供对象）时，将其泛型形参声明为上限通配符（extends）；当泛型对象作为消费者时，将其泛型形参声明为下限通配符（super） ，下面的例子有助于进一步理解PECS：</p>
<pre class="crayon-plain-tag">Value&lt;? extends Number&gt; valueExtend = new Value&lt;Integer&gt;( 1 );
//有了这个变量声明，我知道类型参数必然是Number的子类型：
Number num = valueExtend.get();
//但是，具体是Integer，还是Double，我不知道，因此我无法提供参数类型的实例
valueExtend.set( 1 ); //无法编译

Value&lt;? super Number&gt; valueSuper = new Value&lt;Number&gt;( 1 );
//有了这个变量声明，我知道类型参数必然是Number的超类型：
valueSuper.set( new Integer( 1 ) ); //那么，我当然可以提供Number的子类型给你
//但是，我却不知道类型参数的真实类型
Object obj = valueSuper.get(); //所以此变量提供的参数类型未知</pre>
<div class="blog_h3"><span class="graybg">数组与泛型</span></div>
<pre class="crayon-plain-tag">class BoundedBuffer&lt;T&gt;
{
    private T[]             items;
}</pre>
<p>但是，就像不能实例化参数类型一样，你也不能实例化参数类型的数组：</p>
<pre class="crayon-plain-tag">T t = new T();        //无法通过编译，无法提供数组需要的运行时信息
T[] ts  = new T[10];  //无法通过编译，编译器甚至无法确认T类型有默认构造器</pre>
<p>不能实例化的原因是，Java中的<span style="background-color: #c0c0c0;">数组在运行时必须知道其元素的类型</span>。上述语法无法提供这一类型信息——因为T是什么在编译时根本不知道。因此泛型数组如果需要在泛型内部初始化，必须进行变通：</p>
<pre class="crayon-plain-tag">T[] ts = (T[]) new Object[10];</pre>
<div class="blog_h3"><span class="graybg">类型擦除（Type Erasure）</span></div>
<p>注意绝大部分的参数化类型信息都在编译期间擦除了，<span style="background-color: #c0c0c0;">除了继承自泛型类的子类型，可以通过反射得到父类上的真实参数类型</span>：</p>
<pre class="crayon-plain-tag">//它的泛型父类的类型信息String不会被擦除
class StringList extends ArrayList&lt;String&gt; //和C++的模板特化有些类似
{
    private static final long serialVersionUID = 1L;
}

//可以使用下面的代码获得泛型父类的真实参数类型
ParameterizedType type = (ParameterizedType) new StringList().getClass().getGenericSuperclass();
assert ( type.getActualTypeArguments()[0] == String.class );</pre>
<div class="blog_h3"><span class="graybg">协变</span></div>
<p>需要注意泛型是“非协变”的，<span style="background-color: #c0c0c0;">参数类型不同的泛型变量不能相互转换</span>，即使这些参数类型之间存在继承关系：</p>
<pre class="crayon-plain-tag">//数组是协变的
Object oa[] = new Integer[] {};
//泛型不是，尽管泛型实参Object和Number具有父子类关系。但是List&lt;Object&gt;、List&lt;Number&gt;之类不存在父子类关系
List&lt;Object&gt; ol = new ArrayList&lt;Number&gt;(); //无法编译</pre>
<p>使用上限通配符，可以解决非协变特性导致的无法赋值问题：</p>
<pre class="crayon-plain-tag">List&lt;? extends Object&gt; ol = new ArrayList&lt;Number&gt;();  </pre>
<div class="blog_h3"><span class="graybg">泛型子类化</span></div>
<p>继承泛型类型时，可以将全部或者部分泛型参数“特化”：</p>
<pre class="crayon-plain-tag">// 完全特化
abstract class StringList implements List&lt;String&gt; {}
// 部分特化
abstract class StringKeyMap&lt;V&gt; implements Map&lt;String,V&gt; {}</pre>
<p>注意：上面的例子中，任何StringList是List的子类型，StringKeyMap是Map的子类型。在特化类型参数的同时，你可以获得重写父类逻辑的机会。</p>
<div class="blog_h2"><span class="graybg">其它新特性</span></div>
<div class="blog_h3"><span class="graybg">注解（Annotation）</span></div>
<p>注解在Java5中第一次成为可以影响编程方式的语言元素，注解可以为类、字段、方法、甚至注解本身提供元数据信息。注解的出现将很大程度上改变可配置化对XML高度依赖的现状。</p>
<p>声明注解需要使用特殊的类型说明符：<pre class="crayon-plain-tag">@interface</pre> ，例如：</p>
<pre class="crayon-plain-tag">//某些注解可以用来修饰注解类型本身
@Retention ( RetentionPolicy.RUNTIME )
//提示该注解需要在运行期保留
@Target ( ElementType.METHOD )
//提示该注解只能作用于方法上
@interface AutoExtAjaxResult
{
    //注解中可以声明多个“方法”，用来指示注解实例的属性名
    String value(); //value是一个特殊属性，如果注解只有这一个属性，在实例化注解时，可以不指定属性名
    Class&lt;?&gt; type() default App.class; //注解属性可以提供默认值
}</pre>
<p>需要注意的是，注解属性只支持有限的类型：基本类型、字符串、注解、枚举、Class，或者这些类型的一维数组。</p>
<p>要使用（实例化）注解，只需要在目标类/方法/字段的声明前面添加@注解名，并提供必要的参数，所有没有提供默认值的属性都需要提供值：</p>
<pre class="crayon-plain-tag">@AutoExtAjaxResult ( "Avalue" )
public String userInfo()
{
    return null;
}</pre>
<p>要在运行时获取某个类/方法/字段上被附加的注解，需要使用反射机制，例如：</p>
<pre class="crayon-plain-tag">App.class.getMethod( "userInfo" ).getAnnotation( AutoExtAjaxResult.class ).value(); // Avalue</pre>
<div class="blog_h3"><span class="graybg">For/In循环结构</span></div>
<p>数组以及任何实现了<pre class="crayon-plain-tag">java.lang.Iterable</pre> 接口的对象都可以使用该语法：</p>
<pre class="crayon-plain-tag">for ( char c : new char[] { 'H', 'E', 'L', 'L', 'O' } ) {
    System.out.println( c );
}
for ( String str : new ArrayList() ) {
    System.out.println( str );
}</pre>
<div class="blog_h3"><span class="graybg">自动拆装箱</span></div>
<p>现在基本类型与其包装类型的转换，可以自动进行了：</p>
<pre class="crayon-plain-tag">Number n = 1L;
Double d = 1.1d;
Boolean b = false;
boolean bp = Boolean.TRUE;
double dp = Double.valueOf( 1.1d );
long lp = new Long( 1l );</pre>
<div class="blog_h3"><span class="graybg">类型安全的枚举</span></div>
<p>Java5中，枚举被作为一种特殊的类，可以使用enum关键字来声明，枚举类型不支持创建新实例。</p>
<p>枚举类型和普通类一样可以有字段、方法、构造器等部分，特别的是，枚举实例可以对方法进行覆盖。</p>
<pre class="crayon-plain-tag">public enum Color
{
    RED( "FF0000" ),
    GREEN( "00FF00" ),
    BLUE( "0000FF" )
    {
        //每个枚举值可以覆盖方法
        public String toString()
        {
            return "00F";
        }
    };
    //和普通类一样，枚举可以具有字段、方法、构造器
    private String code;

    private Color( String code )
    {
        this.code = code;
    }

    @Override
    public String toString()
    {
        return code;
    }
}</pre>
<div class="blog_h3"><span class="graybg">可变长参数</span></div>
<p>特殊的语法可以声明方法接受不定长度的参数，变长参数的声明必须位于形参列表的尾部：</p>
<pre class="crayon-plain-tag">public static void main( String[] args )
{
    varargsMethod( 1, 2, "1", "2", "3" );
    varargsMethod0( "1", "2", "3" );
}

private static void varargsMethod0( String... strs )
{
}

private static void varargsMethod( int i, int j, String... strs )
{
}</pre>
<div class="blog_h3"><span class="graybg">静态导入</span></div>
<p>可以使用import static语法导入某些类中的静态方法，在当前文件中不需要提供这些类的名字就可以直接调用静态方法：</p>
<pre class="crayon-plain-tag">import static java.util.Collections.*;

public static void main( String[] args )
{
    sort( list ); //不需要提供Collections类名
}</pre>
<div class="blog_h3"><span class="graybg">java.util.concurrent包</span></div>
<p>这是一个Java5引入的专门用于并发编程的新包，该包主要有以下组件：</p>
<ol>
<li>支持并发的集合类型，位于java.util.concurrent包</li>
<li>原子操作类，这些原子操作类受益于JVM引入了CAS（Compare-And-Set）机制，位于java.util.concurrent.atomic包</li>
<li>新的同步原语，主要是一些锁类，位于java.util.concurrent.locks包</li>
<li>并发相关工具，例如线程池、信号量等</li>
<li>异步相关类，例如Future、Executor</li>
</ol>
<p>concurrent中的集合类图如下（深色表示JDK 1.6追加）：</p>
<p><img class="aligncenter size-full wp-image-8050" src="https://blog.gmem.cc/wp-content/uploads/2007/04/patterns_Concurrent.png" alt="patterns_Concurrent" width="662" height="846" /></p>
<p>可以看到类很多，因此不一一描述，仅将类名中单词的含义列在下表：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;">单词</td>
<td style="text-align: center;">含义 </td>
</tr>
</thead>
<tbody>
<tr>
<td>Queue</td>
<td>提供先进先出的操作接口</td>
</tr>
<tr>
<td>Deque</td>
<td>不但支持在列表的尾部操作，还支持在头部操作</td>
</tr>
<tr>
<td>Concurrent</td>
<td>表示支持线程安全的并发访问</td>
</tr>
<tr>
<td>Linked</td>
<td>表示链表结构</td>
</tr>
<tr>
<td>Array</td>
<td>表示数组结构（连续内存，高速随机访问）</td>
</tr>
<tr>
<td>Blocking</td>
<td>当集合为空，取数据操作将阻塞；当集合为满，存数据操作将阻塞</td>
</tr>
<tr>
<td>Synchronous</td>
<td>一个存操作的完成，必须依赖有一个取操作的执行</td>
</tr>
<tr>
<td>Priority</td>
<td>允许元素被优先取出</td>
</tr>
<tr>
<td>Delay</td>
<td>元素只有在超时后，才能从集合中取出</td>
</tr>
<tr>
<td>CopyOnWrite</td>
<td>一旦执行写操作，就复制底层的数据结构（例如数组），复制的成本较高，但是CopyOnWrite避免了同步开销，适用于大量读、很少写的应用场景</td>
</tr>
<tr>
<td>Navigable</td>
<td>可以导航，即根据给定的元素，寻找其最近的元素</td>
</tr>
</tbody>
</table>
<p>concurrent.atomic包中的原子操作类包括：Boolean、Integer、Long、引用的原子类及其数组类型。</p>
<p>concurrent.locks包引入了一些用于支持锁定的类和接口，改变了Java只能依靠<pre class="crayon-plain-tag">synchronized</pre> 关键字进行粗粒度同步控制的现状：</p>
<p><img class="aligncenter size-full wp-image-8062" src="https://blog.gmem.cc/wp-content/uploads/2007/04/patterns_Concurrent_Lock.png" alt="patterns_Concurrent_Lock" width="444" height="386" /></p>
<p>该包主要有三个接口：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;"> 接口/类</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>Lock</td>
<td>
<p>用于代替<pre class="crayon-plain-tag">synchronized</pre> 关键字的功能，可以关联多个<pre class="crayon-plain-tag">Condition)</pre> ，一般锁都是独占的进行共享资源的访问。该接口提供了比<pre class="crayon-plain-tag">synchronized</pre> 更灵活的功能：</p>
<ol>
<li><pre class="crayon-plain-tag">tryLock()</pre> 允许尝试性的获得锁，如果失败，立即返回而不是必须陷入等待</li>
<li><pre class="crayon-plain-tag">lockInterruptibly()</pre> 进行锁定，如果其它线程持有锁，那么让当前线程陷入等待。但是这种等待是可中断的。其它线程可以调用该线程的<pre class="crayon-plain-tag">interrupt()</pre> 中断该线程的等待</li>
<li><pre class="crayon-plain-tag">tryLock(long, TimeUnit)</pre> 允许尝试性的获得锁，如果在指定时间内还没有得到锁，则返回</li>
</ol>
</td>
</tr>
<tr>
<td>Condition</td>
<td>
<p>用于代替<pre class="crayon-plain-tag">java.lang.Object</pre> 定义的监视器方法：wait、notify和notifyAll。该接口可以把前面几个监视器方法的职责分解为多个完全不同的“条件”对象，条件对象总是和一个锁关联。</p>
<ol>
<li>当前持有锁的线程，可以等待一个条件的达成：<pre class="crayon-plain-tag">Condition.await()</pre> ，从而放弃锁，并陷入等待</li>
<li>当前持有锁的线程，可以声明一个条件已经达成：<pre class="crayon-plain-tag">Condition.signal()</pre> ，从而唤醒一个线程，或者调用<pre class="crayon-plain-tag">Condition.signalAll()</pre> 唤醒所有线程</li>
<li>被唤醒的线程将立即尝试获得锁，因为它放弃锁时，处于同步区中</li>
</ol>
</td>
</tr>
<tr>
<td>ReadWriteLock</td>
<td>
<p>维护一对相关的锁：一个用于只读操作，另外一个用于写入操作，规则如下：</p>
<ol>
<li>如果当前没有线程持有写锁，那么多个线程可以依次获得读锁</li>
<li>如果当前没有线程持有锁，那么某个线程可以持有写锁</li>
<li>如果当前有线程持有写锁，那么其它线程不能获得任何锁</li>
<li>如果当前有线程持有读锁，那么其它线程可以获取读锁，单不能获取写锁</li>
</ol>
</td>
</tr>
</tbody>
</table>
<p>和异步编程有关的接口主要有四个：</p>
<ol>
<li>Future接口表示一个“在未来某个时间点可用的结果”</li>
<li>Callable表示一个可调用对象</li>
<li>Executor接口可以用来执行一个Runnable</li>
<li>CompletionService则可用来调用Callable并（异步）获得结果</li>
</ol>
<p>这些类的层次结构如下：</p>
<p><a href="/wp-content/uploads/2007/04/patterns_Concurrent_Async.png"><img class="aligncenter size-full wp-image-8056" src="https://blog.gmem.cc/wp-content/uploads/2007/04/patterns_Concurrent_Async.png" alt="patterns_Concurrent_Async" width="677" height="1243" /></a></p>
<p>concurrent包包含的其它工具类有：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 150px; text-align: center;"> 类</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>Semaphore</td>
<td>信号量，线程可以释放信号量，亦可尝试获取信号量，信号量相当于维护了一个固定数量的许可</td>
</tr>
<tr>
<td>CountDownLatch</td>
<td>计数器，可以让某个线程在计数完毕前等待。其它线程则可以降低计数值</td>
</tr>
<tr>
<td>CyclicBarrier</td>
<td>屏障，允许多个线程相互等待（await），直到所有线程都调用了await()，所有线程才会继续运行</td>
</tr>
<tr>
<td>TimeUnit</td>
<td>这是一个枚举，可以用来进行时间单位的转换，或者让当前线程睡眠一段时间</td>
</tr>
</tbody>
</table>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/java5-new-features">Java5新特性</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/java5-new-features/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
