泛型是Java SE 1.5的新特性,泛型的本质是参数化类型。 一般泛型的定义:

1
2
3
List<People> box = new ArrayList<People>();
box.add(new People());
People people =box.get(0);

泛型的优点很明显第一提高了类型的安全,第二消除强制类型的转换,但是尴尬的地方就是每次定义时都要写明泛型的类型,这样显示指定感觉有些冗长。 Java7的泛型类型推断改进:

1
Map<String, String> map = new HashMap<>(); //注意后面的"<>"

上述Map的定义编译器会根据变量声明时的泛型类型自动推断出实例化HashMap时的泛型类型。注意new HashMap后面的“<>”,只有加上这个“<>”才表示是自动类型推断,否则就是非泛型类型的HashMap,并且在使用编译器编译源代码时会给出一个警告提示。
Java SE 7在创建泛型实例时的类型推断是有限制的:
只有构造器的参数化类型在上下文中被显著的声明了,才可以使用类型推断,否则不行。

1
2
3
4
List<String> list = new ArrayList<>();
list.add("A");
//下面addAll方法无法通过编译,由于addAll期望获得Collection<? extends String>类型的参数
list.addAll(new ArrayList<>());

Java8泛型类型推断改进:

1.支持通过方法上下文推断泛型目标类型。

2.支持在方法调用链路当中,泛型类型推断传递到最后一个方法。 官方例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class List<E> {

   static <Z> List<Z> nil() { ... };
   static <Z> List<Z> cons(Z head, List<Z> tail) { ... };
   E head() { ... }
}



//通过方法赋值的目标参数来自动推断泛型的类型
List<String> l = List.nil();
//而不是显示的指定类型
//List<String> l = List.<String>nil();
//通过前面方法参数类型推断泛型的类型
List.cons(42, List.nil());
//而不是显示的指定类型
//List.cons(42, List.<Integer>nil());

所以上面addAll出错的例子在Java8中依据方法参数类型推断就不会出错了。

1
2
3
4
5
//下面代码Java8通过编译
List<String> list = new ArrayList<>();
list.add("A");
//根据add方法中的"A"参数推断new ArrayList<>()的类型为new ArrayList<String>()
list.addAll(new ArrayList<>());

允许在同一类,属性,或方法多次使用同一个注解。
java8之前也可以重复注解,但代码可读性不好,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public @interface Authority {
     String role();
}

public @interface Authorities {
    Authority[] value();
}

public class RepeatAnnotationUseOldVersion {

    @Authorities({@Authority(role="Admin"),@Authority(role="Manager")})
    public void doSomeThing(){
    }
}

java8实现重复注解方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Repeatable(Authorities.class)
public @interface Authority {
     String role();
}

public @interface Authorities {
    Authority[] value();
}

public class RepeatAnnotationUseNewVersion {
    @Authority(role="Admin")
    @Authority(role="Manager")
    public void doSomeThing(){ }
}

java8创建重复注解Authority时,加上@Repeatable,指向存储注解Authorities,在使用时候,直接可以重复使用Authority注解,java8实现方式可读性比之前强。

默认方法:简单的说接口可以有实现方法,而且不需要实现类去实现其方法。只需在方法名前面加个default关键字即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface DefaultMethod {
  default void foo(){
      System.out.println("调用DefaultMethod接口的默认方法foo");
  }

}

public class DefaultMethodTest implements DefaultMethod {

  public static void main(String[] args) {
      DefaultMethodTest defaultMethodTest = new DefaultMethodTest();
      defaultMethodTest.foo();// 调用DefaultMethod.foo

  }

}

java8抽象类与接口对比

相同点:
      1.都是抽象类型。
      2.都可以有实现方法(java 8 接口有default method)。
      3.都可以不需要实现类或者继承者去实现所有方法。
不同点:
       1.抽象类不可以多重继承,接口可以。
       2.抽象类是is-a的关系,接口表示是like-a关系。
       3.接口中定义的变量默认是public static final 型,且必须给其初值,所以实现类中不能重新定义,也不能改变其值;
         抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。

多重继承

如果一个类实现了两个接口,并且两个接口都用相同的签名定义了默认方法,这该怎么办?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface A {
  default void foo() {
      System.out.println("Calling A.foo()");

  }

}

public interface B {
  default void foo() {
      System.out.println("Calling B.foo()");

  }

}

public class AB implements A, B {

}
//Duplicate default methods named foo with the parameters () and () are inherited from the types B and A。

上面代码通不过编译:类AB从A,B接口继承了重复的默认方法foo。 解决此问题AB类必须重写冲突方法:

1
2
3
4
5
6
public class AB implements A, B {
  @Override
  public void foo() {
      
  }
}

如果AB类想调用接口的默认方法,那么可以按照以下方式:

1
2
3
4
@Override
public void foo() {
  A.super.foo();
}

方法引用其实是lambda表达式的一个简化写法,所引用的方法其实是lambda表达式的方法体实现。
语法:

1
2
3
4
5
6
7
 ObjectReference::methodName

如果是静态方法,ClassName::methodName。如 Object ::equals

如果是实例方法,Instance::methodName。如Object obj=new Object();obj::equals;

构造函数,ClassName::new

例子

1
2
3
4
@FunctionalInterface
interface Converter<F, T> {
  T convert(F from);
}

使用常规的Lambda表达式:

1
2
3
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("1024");
System.out.println(converted); // 1024

静态方法引用:

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

对象方法引用:

1
2
3
4
5
6
7
8
9
10
public class Something {
  String startsWith(String s) {
      return String.valueOf(s.charAt(0));
  }
}

      Something something = new Something();
      Converter<String, String> converter = something::startsWith;
      String converted = converter.convert("Java");
      System.out.println(converted); // J

构造函数引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Person {
    String firstName;
    String lastName;

    Person() {}

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
  @Override
  public String toString() {
      return "Person [firstName=" + firstName + ", lastName=" + lastName
              + "]";
  }
}

@FunctionalInterface
interface PersonFactory<P extends Person> {
    P create(String firstName, String lastName);
}

PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");
System.out.println(person);//Person [firstName=Peter, lastName=Parker]

通过Person::new来创建一个Person类构造函数的引用。Java编译器会自动地选择合适的构造函数来匹配PersonFactory.create函数的签名,并选择正确的构造函数形式。

1
2
//自动匹配Person类的 Person(String firstName, String lastName)构造函数。
PersonFactory<Person> personFactory = Person::new;


参考资料:
http://winterbe.com/posts/2014/03/16/java-8-tutorial/

什么是Lambda表达式?

在一般数学计算中lambda表达式就是一个函数,为部分或者所有输入值指定一个输出值,在java中Lambad表达式是以函数的概念引入,可以理解为一种语法更加紧凑的匿名方法,并且允许省略修饰符,返回类型并且某种情况下的参数类型。
语法:
lambda基本语法:

1
2
3
   (parameters) -> expression
   or
   (parameters) -> { statements; }

Examples:

1
2
3
4
5
6
  (int x,int y)->x+y   //取整数x,y并返回它们的和
  (x, y) -> x - y  //取整数x,y并返回它们的差                                
  () -> 42  //不取任何值,直接返回42                                         
  (String s) -> System.out.println(s) //取字符串s,在控制台打印,不返回任何值。             
  x -> 2 * x  //取一个数字,返回它的2倍                                      
  c -> { int s = c.size(); c.clear(); return s; }//取集合c,清空它,然后返回它清空前的大小

语法说明

   1.参数类型可以显式声明(ex.1,4)或者隐式推断(ex.2,5,6),显式声明和隐式推断参数不允许用在同一个lambda表达式。
   2.lambda body可以是一个block({}大括号包含代码块ex.6)或者是一个表达式(ex.1-5)。block body可以返回值(ex.6)
   或者什么都不返回()。
   3.如果lambda body是一个表达式,它也可能返回值(ex.1,2,3,5)或者什么都不返回(ex.4)。
   4.隐式推断可以省略()(ex.5,6)。
   5.ex.6 lambda能操作一个集合,同样的,根据上下文给定的参数,如果一个参数a拥有size和clear方法,
   它也能操作并返回其类型的值。
   

函数式接口
Lambda表达式如何匹配Java的类型系统?每一个lambda都能够通过一个特定的接口,与一个给定的类型进行匹配。
一个所谓的函数式接口必须要有且仅有一个抽象方法声明(java8允许接口有默认方法)。每个与之对应的lambda表达式必须要与抽象方法的声明相匹配。

1、一个可以接受Lambda表达式的接口只能有一个抽象方法,否则在编译时将抛出异常。
2、为了显示的说明某接口是可使用lambda表达式,可以使用 @FunctionalInterface 注解该接口。

1
2
3
4
5
6
7
8
9
10
11
12
@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);//抽象方法只能一个

    default double sqrt(int a) {//可以多个接口默认方法
        return Math.sqrt(a);
    }
}

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

Default Methods(默认方法)详情
注意,如果不写 @FunctionalInterface 标注,程序也是正确的。

官网例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   public class Calculator {

    interface IntegerMath {
        int operation(int a, int b);
    }

    public int operateBinary(int a, int b, IntegerMath op) {
        return op.operation(a, b);
    }

    public static void main(String... args) {

        Calculator myApp = new Calculator();
        IntegerMath addition = (a, b) -> a + b;//隐式推断类型
        IntegerMath subtraction = (int a, int b) -> a - b;//显式声明类型
        System.out.println("40 + 2 = " +
            myApp.operateBinary(40, 2, addition));
        System.out.println("20 - 10 = " +
            myApp.operateBinary(20, 10, subtraction));
    }
}

为什么Java要增加lambda表达式?

Lambda表达式,也叫做闭包,是一个在很多现代程序语言中十分流行的特性。在众多不同的原因中当中,Java平台最迫切的原因之一是lambda表达式能简化多线程环境下集合的分布式处理。
1.内部循环和外部循环

常规的外部循环:

1
2
3
4
5
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

for (int number : numbers) {
    System.out.println(number);
}

这种情况简单的存在3个缺点:

1.只能顺序处理List中的元素

2.不能充分利用多核CPU

3.不利于编译器优化
使用Lamdba表达式内部循环:

1
2
3
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

numbers.forEach((Integer value) -> System.out.println(value));

1.不一定需要顺序处理List中的元素,顺序可以不确定

2.可以并行处理,充分利用多核CPU的优势

3.有利于JIT编译器对代码进行优化

2、传递行为,而不仅仅是传值
传值局限性的场景

1
2
3
4
5
6
7
8
9
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

public int sumAll(List<Integer> numbers) {
    int total = 0;
    for (int number : numbers) {
        total += number;
    }
    return total;
}

sumAll算法很简单,完成的是将List中所有元素相加。某一天如果需要增加一个对List中所有偶数求和的方法sumAllEven,如下:

1
2
3
4
5
6
7
8
9
public int sumAllEven(List<Integer> numbers) {
    int total = 0;
    for (int number : numbers) {
        if (number % 2 == 0) {
            total += number;
        }
    }
    return total;
}

又有一天,需要增加第三个方法:对List中所有大于3的元素求和,那是不是继续加下面的方法呢?

1
2
3
4
5
6
7
8
9
public int sumAllEven(List<Integer> numbers) {
    int total = 0;
    for (int number : numbers) {
        if (number > 3) {
            total += number;
        }
    }
    return total;
}

比较这三个方法,发现了一个很明显的“代码臭味”—— 代码重复。 如果使用Lambda表达式,那么需要Lambda表达式中的谓词(Predicate)发挥作用:

1
2
3
4
5
6
7
8
9
10
11
12
13
public int sumAll(List<Integer> numbers, Predicate<Integer> p) {
    int total = 0;
    for (int number : numbers) {
        if (p.test(number)) {
            total += number;
        }
    }
    return total;
}

sumAll(numbers, n -> true);
sumAll(numbers, n -> n % 2 == 0);
sumAll(numbers, n -> n > 3);


3、Consumer与Loan Pattern
比如有一个资源类Resource:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Resource {
  public Resource() {
      System.out.println("Opening resource");
  }

  public void operate() {
      System.out.println("Operating on resource");
  }

  public void dispose() {
      System.out.println("Disposing resource");
  }
}
//调用
Resource resource = new Resource();
try {
    resource.operate();
} finally {
    resource.dispose();
}

但是有一个问题,如果很多地方都要用到这个资源,那么就存在很多段类似这样的代码,这很明显违反了DRY(Don’t Repeat It Yourself)原则。而且如果某位程序员由于某些原因忘了用try/finally处理资源,那么很可能导致内存泄漏。那咋办呢?Java 8提供了一个Consumer接口,代码改写为如下:
因为对资源对象resource执行operate方法时可能抛出RuntimeException,所以需要在finally语句块中释放资源,防止可能的内存泄漏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class Resource {
  public Resource() {
      System.out.println("Opening resource");
  }

  public void operate() {
      System.out.println("Operating on resource");
  }

  public void dispose() {
      System.out.println("Disposing resource");
  }

  public static void withResource(Consumer<Resource> consumer) {
      Resource resource = new Resource();
      try {
          consumer.accept(resource);
      } finally {
          resource.dispose();
      }
  }

  public static void main(String[] args) {
      Resource.withResource(resource -> resource.operate());
  }
}


Console:
Opening resource
Operating on resource
Disposing resource

外部要访问Resource,只能通过withResource方法了,而且也完全杜绝了因人为疏忽而导致的潜在内存泄漏。

Lambda表达式范围

对于lambdab表达式外部的变量,其访问权限的粒度与匿名对象的方式非常类似。

1
2
3
4
5
@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}

1.访问局部变量

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

stringConverter.convert(2);     // 3

与匿名对象不同的是,变量num不一定是final。

1
2
3
4
5
 int num = 1;//去掉final也是正确的
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

但是num在编译的时候隐式的当作final变量来处理。

1
2
3
4
int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);
num = 3;//这样是错误的不能对final变量重新复制,在lambda表达式内部改变num的值也是不允许的

2.访问成员变量和静态变量 与局部变量不同,我们在lambda表达式的内部能获取到对成员变量或静态变量的读写权。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class LambdaTest {
    static int outerStaticNum;
    int outerNum;

    void testScopes() {
        Converter<Integer, String> stringConverter1 = (from) -> {
            outerNum = 23;
            return String.valueOf(from);
        };

        Converter<Integer, String> stringConverter2 = (from) -> {
            outerStaticNum = 72;
            return String.valueOf(from);
        };
    }
}

默认方法无法在lambda表达式内部被访问。因此下面的代码是无法通过编译的:

1
2
3
4
5
6
7
8
interface Formula {
    double calculate(int a);

    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}
Formula formula = (a) -> sqrt( a * 100);

内置函数式接口

Predicates
Predicate是一个布尔类型的函数,该函数只有一个输入参数。Predicate接口包含了多种默认方法,用于处理复杂的逻辑动词(and, or,negate)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    Predicate<String> predicate = (s) -> s.length() > 0;
  predicate.test("Java");

  Predicate<String> predicate1 = String::isEmpty;

  System.out.println(predicate1.test("Java"));// false
  System.out.println(predicate1.negate().test("Java"));// true

  Predicate<String> predicate2 = String::isEmpty;

  System.out.println(predicate2.or(predicate).test("JAVA"));// true
  System.out.println(predicate2.and(predicate).test("JAVA"));// false
    System.out.println(Predicate.isEqual("Java").test("Java"));// true
  System.out.println(Predicate.isEqual("Java").test("Java8"));// false

Predicate源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@FunctionalInterface
public interface Predicate<T> {


    boolean test(T t);
    //and
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
    //取反
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
    //or
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }
    //isEqual
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

Functions
Function接口接收一个参数,并返回单一的结果。默认方法可以将多个函数串在一起。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 Function<String, Integer> toInteger = Integer::valueOf;
  Function<String, String> backToString = toInteger
          .andThen(String::valueOf);
  Integer integer = toInteger.apply("123");
  String string = backToString.apply("123");
  System.out.println(integer);// 123
  System.out.println(string);// "123"


    Function<String, Integer> toInteger = Integer::valueOf;
  Function<String, String> toString = (from) -> from + "123";
    //toString Function的结果作为toInteger Fuction的输入
  Integer result = toInteger.compose(toString).apply("123");//此处的"123"是toString Function的输入
  System.out.println(result);//123123     

Function源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@FunctionalInterface
public interface Function<T, R> {

  R apply(T t);
  default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }
   default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }
 static <T> Function<T, T> identity() {
        return t -> t;
    }
}

Suppliers
Supplier接口产生一个给定类型的结果。与Function不同的是,Supplier没有输入参数。

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

Suppliers源码:

1
2
3
4
5
6
7
8
9
10
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

Consumers

Consumer代表了在一个输入参数上需要进行的操作。 为什么Java增加Lambda表达式章节的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void withResource(Consumer<Resource> consumer) {
  Resource resource = new Resource();
  try {
       //输入一个resource对象
      consumer.accept(resource);
  } finally {
      resource.dispose();
  }
}

public static void main(String[] args) {
     //在输入的resource对象上执行operate()操作
  Resource.withResource(resource -> resource.operate());
}

Consumer也可以将多个操作穿串一起

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 public class Person {
   private String firstName;
   private String lastName;

   Person() {

   }

   public Person(String firstName, String lastName) {
  super();
  this.firstName = firstName;
  this.lastName = lastName;
   }
   }

   //先执行打印firstName操作   
   Consumer<Person> greeter = (p) -> System.out.println("Hello, "
          + p.getFirstName());
   //后执行打印lastName操作       
  greeter.andThen(p -> System.out.println("Hello, " + p.getLastName()))
          .accept(new Person("Luke", "Skywalker"));

   Console:
   Hello, Luke
   Hello, Skywalker

Consumer源码:

1
2
3
4
5
6
7
8
@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

Comparators

Java 8 为这个接口添加了不同的默认方法。

1
2
3
4
5
6
7
8
 Comparator<Person> comparator = (p1, p2) -> p1.getFirstName()
          .compareTo(p2.getFirstName());

  Person p1 = new Person("John", "Doe");
  Person p2 = new Person("Alice", "Wonderland");

  comparator.compare(p1, p2); //> 0
  comparator.reversed().compare(p1, p2); // < 0

Optionals
Optional不是一个函数式接口,而是一个精巧的工具接口,用来防止NullPointerEception产生。
Optional类的Javadoc描述如下:
这是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

of:调用工厂方法创建Optional实例
1
2
3
Optional<String> name = Optional.of("Sanaulla");
//传入参数为null,抛出NullPointerException.
Optional<String> someNull = Optional.of(null);

ofNullable:为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional。

ofNullable与of方法相似,唯一的区别是可以接受参数为null的情况。

1
Optional empty = Optional.ofNullable(null);

isPresent:如果值存在返回true,否则返回false。
1
2
 Optional<String> name = Optional.of("Java");
 System.out.println(name.isPresent());//true

get:如果Optional有值则将其返回,否则抛出NoSuchElementException。
1
2
3
4
5
6
7
8
Optional empty = Optional.ofNullable(null);
//执行下面的代码会输出:No value present 
try {
  //在空的Optional实例上调用get(),抛出NoSuchElementException
  System.out.println(empty.get());
} catch (NoSuchElementException ex) {
  System.out.println(ex.getMessage());
}

ifPresent:如果Optional实例有值则为其调用Consumer,否则不做处理
1
2
3
4
5
 Optional<String> name = Optional.of("Java");
  name.ifPresent((value) -> {
      System.out.println("The length of the value is: " + value.length());
    //The length of the value is: 4
  });

orElse:如果有值则将其返回,否则返回指定的其它值。
1
2
3
4
5
6
7
8
    Optional<Object> empty = Optional.ofNullable(null);
  Optional<String> name = Optional.of("not null");
  System.out.println(empty.orElse("There is no value present!"));
  System.out.println(name.orElse("There is some value!"));

    Console:
    There is no value present!
    not null

orElseGet:orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值。
1
2
3
4
5
6
7
8
 //orElseGet与orElse方法类似,区别在于orElse传入的是默认值,
  //orElseGet可以接受一个lambda表达式生成默认值。
  System.out.println(empty.orElseGet(() -> "Default Value"));
  System.out.println(name.orElseGet(() -> "Default Value"));

   Console:
   Default Value
   not null

orElseThrow:如果有值则将其返回,否则抛出Supplier接口创建的异常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
try {
  //orElseThrow与orElse方法类似。与返回默认值不同,
  //orElseThrow会抛出lambda表达式或方法生成的异常 

  empty.orElseThrow(ValueAbsentException::new);
} catch (Throwable ex) {
  //输出: No value present in the Optional instance
  System.out.println(ex.getMessage());
}

class ValueAbsentException extends Throwable {

  public ValueAbsentException() {
    super();
  }

  public ValueAbsentException(String msg) {
    super(msg);
  }

  @Override
  public String getMessage() {
    return "No value present in the Optional instance";
  }
}

map:如果有值,则对其执行调用mapping函数得到返回值。如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional。
1
2
3
4
 Optional<String> name = Optional.of("not null");
  Optional<String> upperName = name.map((value) -> value.toUpperCase());
    //输出:NOT NULL
  System.out.println(upperName.orElse("No value found"));

flatMap:如果有值,为其执行mapping函数返回Optional类型返回值,否则返回空Optional。flatMap与map(Funtion)方法类似,区别在于flatMap中的mapper返回值必须是Optional。调用结束时,flatMap不会对结果用Optional封装。
1
2
3
4
 Optional<String> name = Optional.of("not null");
  name = name.flatMap((value) -> Optional.of(value.toUpperCase()));
    //输出:NOT NULL
  System.out.println(name.orElse("No value found"));

filter:filter个方法通过传入限定条件对Optional实例的值进行过滤。对于filter函数应该传入实现了Predicate接口的lambda表达式。
1
2
3
4
5
6
7
8
9
10
11
 Optional<String> name = Optional.of("Sanaulla");
  //filter方法检查给定的Option值是否满足某些条件。
  //如果满足则返回同一个Option实例,否则返回空Optional。
  Optional<String> longName = name.filter((value) -> value.length() > 6);
  System.out.println(longName.orElse("The name is less than 6 characters"));//输出Sanaulla
  
  //另一个例子是Optional值不满足filter指定的条件。
  Optional<String> anotherName = Optional.of("Sana");
  Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
  //输出:The name is less than 6 characters
  System.out.println(shortName.orElse("The name is less than 6 characters"));


参考资料:
http://www.lambdafaq.org/
http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#use-case
http://java.dzone.com/articles/why-we-need-lambda-expressions
http://java.dzone.com/articles/why-we-need-lambda-expressions-0
http://www.javacodegeeks.com/2013/09/deep-dive-into-optional-class-api-in-java-8.html

Java Programming Language(Java语言)

1.Lambda Expressions(Lambad表达式)
2.Method References(方法引用)
3.Default Methods(默认方法)
4.Repeating Annotations(重复注解)
5.Type Annotations(类型注解)
6.Improved type inference(改进类型推断)
7.Method parameter reflection(方法参数反射)

Collections(集合)

1.New java.util.stream package(新包java.util.stream)
2.Performance Improvement for HashMaps with Key Collisions(HashMaps Key碰撞的性能改进)
未完待续…
参考资料:
http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html

俗话说”工欲善其事,必先利其器”,Eclipse Kepler已支持Java 8,可以下载官方提供的Java™ 8 Support版本版本来使用此功能。已经下载的Eclipse Kepler SR2也可以通过安装插件的方式来支持 Java 8,具体步骤如下:

下载Java 8并确保你的Eclipse版本是Kepler SR2。

选择菜单:”Help > Eclipse Marketplace…”。
在搜索框中输入”Java 8 Kepler”。 安装Java 8 support for Eclipse Kepler SR2插件。
安装其他可选插件:
Java EE扩展支持:Java 8 Facet for Web Tools for Eclipse Kepler SR2
Maven扩展支持:Java 8 support for m2e for Eclipse Kepler SR2


多版本JDK共存问题
平常开发环境使用JDK7,如果又要使用JDK8做一些学习测试,那么可以在eclipse.ini文件中指定-vm参数,JAVA_HOME还是使用JDK7相互之间不影响。

-vm
C:/Program Files (x86)/Java/jdk1.8.0_05/bin/javaw.exe

参考资料:
http://www.eclipse.org/downloads/java8/

临界区是访问一个共享资源在同一时间不能被超过一个线程执行的代码块。

同步方法

每个方法声明为synchronized是一个临界区。
synchronized关键字来保护访问的代码块,替换在整个方法上使用synchronized关键字。
一般使用this关键字引用执行该方法的对象。

synchronized (this) {
// Java code
}

在同步的类里安排独立属性

当使用synchronized关键字来保护代码块时,必须通过一个对象的引用作为参数。
通常,使用this关键字来引用执行该方法的对象,当有多个共享属性时可以创建多个独立属性,
保证每个共享变量同步访问。

在同步代码中使用条件

Java在Object对象中提供wait(),notify(),和notifyAll() 方法的实现。一个线程可以在
synchronized代码块中调用wait()方法。如果在synchronized代码块外部调用wait()方法,
JVM会抛出IllegalMonitorStateException异常。
当线程调用wait()方法,JVM让这个线程睡眠,并且释放控制synchronized代码块的对象,
这样,虽然它正在执行但允许其他线程执行由该对象保护的其他synchronized代码块。
为了唤醒线程,你必须在由相同对象保护的synchronized代码块中调用notify()或notifyAll()方法。

使用Lock同步代码块

Lock 接口比synchronized关键字提供更多额外的功能。新功能之一是实现的tryLock()方法。
这种方法试图获取锁的控制权并且如果它不能获取该锁,是因为其他线程在使用这个锁,
它将返回这个锁。使用synchronized关键字,当线程A试图执行synchronized代码块,
如果线程B正在执行它,那么线程A将阻塞直到线程B执行完synchronized代码块。
使用锁,你可以执行tryLock()方法,这个方法返回一个 Boolean值表示,
是否有其他线程正在运行这个锁所保护的代码。  

Lock接口允许读写操作分离。
Lock接口比synchronized关键字提供更好的性能。

private final Lock lock=new ReentrantLock();
//获取锁
lock.lock();
try {

   } catch (InterruptedException e) {
 }finally { 
//释放锁
lock.unlock();
 }

Lock 接口(和ReentrantLock类)包含其他方法来获取锁的控制权,那就是tryLock()方法。
这个方法与lock()方法的最大区别是,如果一 个线程调用这个方法不能获取Lock接口的控制权
时,将会立即返回并且不会使这个线程进入睡眠。这个方法返回一个boolean值,
true表示这个线程 获取了锁的控制权,false则表示没有。

ReentrantLock锁可重入。

使用读/写锁同步数据访问

ReadWriteLock接口和唯一一个实现它的ReentrantReadWriteLock类。
这个类提供两把锁,一把用于读操作和一把用于写操作。同时可以有多个线程执行读操作,
但只有一个线程可以执行写操作。当一个线程正在执行一个写操作,不可能有任何线程执行读操作。

private ReadWriteLock lock=new ReentrantReadWriteLock();

//读操作锁
private void read(){

lock.readLock().lock();
//read code
lock.readLock().unlock();

}
//写操作锁
private void write(){
lock.writeLock().lock();
//write code
lock.writeLock().unlock();
}

修改Lock的公平性

在ReentrantLock类和 ReentrantReadWriteLock类的构造器中,允许一个名为fair的boolean
类型参数, 默认值为 false。
当多个线程等待一把锁时,这个锁必须选择一个进入临界区,当fair为false时这个选择没有标准
即非公平模式。
当这个值为true时,锁将选择等待时间最长的进入临界区即公平模式。由于tryLock()方法并不会
使线程进入睡眠,因此此参数不影响它。

在Lock中使用多条件

Condition条件变量,等待某种条件的发生,条件发生后,可以唤醒等待在该条件上的一个线程,
或所有线程。条件变量要与锁一起协同工作。

 ReentrantLock lock = new ReentrantLock(); 
 Condition condition = lock.newCondition(); 

 condition.await();//满足某条件时等待  Object.wait();
 condition.signal(); //唤醒等待的其中一个线程  Object.notify(); 
 condition.signalAll(); //唤醒等待的所有线程   Object.notifyAll();

java中创建线程有2种方式:

  1.继承Thread类,覆盖run方法。
  2.构建实现Runnable接口类,然后创建一个Thread类对象传入一个Runnable对象作为构造参数。

获取和设置线程信息:

 ID: 每个线程的独特标识。
 Name: 线程的名称。
 Priority: 线程对象的优先级。优先级别在1-10之间,1是最低级,10是最高级。
 Status: 线程的状态。
 Tips:
 setPriority() 方法会抛出 IllegalArgumentException 异常,如果设置的优先级不是在1-10之间。
 在Java中,线程只能有这6种中的一种状态:  new, runnable, blocked, waiting,
 time waiting, 或 terminated。

线程的中断:

   Thread类有一个boolean类型的属性表明线程是否被中断,当调用线程的interrupt()方法,
   此属性被设置为true,isInterrupted() 方法仅返回属性值。

   isInterrupted()和interrupted()区别:
   isInterrupted()不会改变interrupted属性值,interrupted()会设置为false,
   interrupted() 方法是一个静态方法,建议使用isInterrupted()方法。

操作线程的中断机制

   在Thread类的run方法中捕获InterruptedException异常来控制线程的中断。

    private void do() throws InterruptedException
    { 
     //....
     if (Thread.interrupted()) {
    throw new InterruptedException();
     }
     }
      @Override
      public void run() {
       try {
      do();
      } catch (InterruptedException e) {

    } 
    }

线程的睡眠与恢复

  Thread类的 sleep(long millis) 方法 。表示线程休眠millis毫秒。
  TimeUnit枚举元素的sleep(long millis)方法同样能使当前线程睡眠millis毫秒。
  2种线程休眠的方法都会抛出InterruptedException异常。

等待线程的终结

  当前线程调用某个线程的join()方法,当前线程会暂停直到被调用线程执行完成。
  join (long milliseconds)
  join (long milliseconds, long nanos)

  ex:thread1调用thread2.join(1000),那么thread1暂停运行,
  直到一下其中一个条件发生,join方法返回:
  thread2结束运行
  1000毫秒过去了

守护线程的创建和运行

  在Java中有两类线程:用户线程 (User Thread)、守护线程 (Daemon Thread)。

  守护线程,是指在程序运行的时候在后台提供一种通用服务的线程,
  比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。
  因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。
  反过来说,只要任何非守护线程还在运行,程序就不会终止。

  Thread类的 setDaemon() 方法设置线程为守护线程,此方法必须在调用start()方法之前调用。
  isDaemon() 方法来检查线程是否是守护线程(方法返回 true) 或者是用户线程 (方法返回 false)。

处理线程非检测异常

  Java中有2种异常:
  检测异常(Checked Exceptions):这种异常必须强制捕获或者继续抛出异常(throws)。
  非检测异常(Unchecked Exceptions):这种异常无需强制捕获。
  在线程对象run()方法中抛出一个检测异常必须捕获,因为run()方法不接受throws抛出异常。

  在Java处理Thread中的非检测异常类,需要实现UncaughtExceptionHandler接口。
  public class ExceptionHandler implements UncaughtExceptionHandler {

  @Override
  public void uncaughtException(Thread t, Throwable e) {

 }
 }
 在线程启动之前设置setUncaughtExceptionHandler(new ExceptionHandler());

当在一个线程里抛出一个异常,但是这个异常没有被捕获(非检查异常), 
JVM 检查线程的相关方法是否有设置一个未捕捉异常的处理者 。
如果有JVM使用Thread对象和Exception作为参数调用此方法 。
如果线程没有捕捉未捕获异常的处理者, 那么JVM会把异常的 stack trace 写入操控台并结束任务。
静态方法 setDefaultUncaughtExceptionHandler() 为应用里的所有线程对象建立异常handler 。
JVM检测异常3种潜在的Handler:Thread UncaughtExceptionHandler->
Thread Group UncaughtExceptionHandler->Default Thread 
UncaughtExceptionHandler->print stack trace

本地线程变量

ThreadLocal类为线程的变量存储独立的属性值。
ThreadLocal<Object> threadLocal = new ThreadLocal<Object>() {
    //初始值
    protected Object initialValue() {
        return new Object();
    };

    public void set(Object value) {
    };

    public Object get() {
        return new Object();
    };
    //移除此线程局部变量的值。
    public void remove() {
    };

}; 
InheritableThreadLocal 类提供线程创建线程的值的遗传性 。
如果线程A有一个本地线程变量,然后它创建了另一个线程B,
那么线程B将有与A相同的本地线程变量值。
你可以覆盖 childValue() 方法来初始子线程的本地线程变量的值。
它接收父线程的本地线程变量作为参数。
InheritableThreadLocal<Object> inheritableThreadLocal = 
new InheritableThreadLocal<Object>() {
    protected Object childValue(Object parentValue) {
        return parentValue;
    };
};

线程组

Java提供ThreadGroup类来组织线程。 
ThreadGroup对象可以由Thread对象组成和由另外的ThreadGroup对象组成,生成线程树结构。
ThreadGroup threadGroup = new ThreadGroup(group name);
Thread thread=new Thread(threadGroup, runnableimpl);
activeCount() 和 enumerate() 方法来获取线程个数和与ThreadGroup对象关联的线程的列表。

处理线程组内非检测异常

public class MyThreadGroup extends ThreadGroup {
public MyThreadGroup(String name) {
super(name);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
//覆盖此方法
}
}

线程工厂

ThreadFactory线程工厂。
ThreadFactory 接口只有一个方法是newThread。它接收 Runnable 对象作为参数并返回一个 Thread对象。
public class MyThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
    return new Thread(r);
}
}

使用FragmentTabHost时,Fragment之间切换时每次都会调用onCreateView方法,导致每次Fragment的布局都重绘,无法保持Fragment原有状态。

解决办法:在Fragment onCreateView方法中缓存View。

    //缓存Fragment view
    private View rootView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
    Bundle     savedInstanceState) {
    if(rootView==null){
        rootView=inflater.inflate(R.layout.tab_fragment, null);
    }
    //缓存的rootView需要判断是否已经被加过parent, 如果有parent需要从parent删除,
    //要不然会发生这个rootview已经有parent的错误。    
    ViewGroup parent = (ViewGroup) rootView.getParent();
    if (parent != null) {
        parent.removeView(rootView);
    } 
    return rootView;
    }