JDK8特性总结

JDK8(一)

1.Grande介绍

.Grandle/Maven:目录结构一样的,约定优于配置

源代码:src/main/java

配置文件:src/main/resources

测试代码:src/text/java

测试配置文件:src/text/resources

页面相关:src/main/webapp

setting.gradle:项目名字

build.gradle:项目的描述,类似于pom.xml,包括项目所依赖的信息

 sourceCompatibility = 1.8//源代码兼容性
 targetCompatibility = 1.8//编译后代码兼容性
 dependencies {
 //    testCompile group: 'junit', name: 'junit', version: '4.12'
     testCompile(
             "junit:junit:4.11"
     )
 }

2.Lambda表达式基本格式

(param1,param2,param3)->{

}

 package com.tang.jdk8;
 ​
 import javax.swing.*;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 ​
 public class SwingTest {
     public static void main(String[] args) {
         JFrame jFrame = new JFrame("My JFrame");
         JButton jButton = new JButton("My JButton");
 ​
         //按按钮之后会自动调用方法
         //java为静态类型语言,e为类型推断后结果,
         jButton.addActionListener(e -> System.out.println("Button Pressed!"));
         jFrame.add(jButton);
         //正好是主键的大小
         jFrame.pack();
         jFrame.setVisible(true);
         //关闭的时候整个程序退出
         jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     }
 }

3.@FunctionalInterface

 package java.util.function;
 @FunctionalInterface
 /*An informative annotation type used to indicate that an interface
  * type declaration is intended to be a functional interface as
  * defined by the Java Language Specification.
  ......
  */

1.函数式接口:如果一个接口只有一个抽象方法,那么他就是函数式接口。

2.如果我们在某个接口上声明了@FunctionalInterface注解,那么编译器就按照函数式接口的定义来要求该接口。

3.如果某个接口只有一个抽象方法,但我们没有给该接口声明,@FunctionalInterface注解,那么编译器依旧会将该接口看做函数式接口。

 /*If an interface declares an abstract method overriding one of the
 * public methods of {@code java.lang.Object}, that also does
 * not count toward the interface's abstract method count
 * since any implementation of the interface will have an
 * implementation from {@code java.lang.Object} or elsewhere.
 */
 @FunctionalInterface
 public interface MyInterface {
     void test();
     
     String isString();
 }

Multiple non-overriding abstract methods found in interface com.tang.jdk8.MyInterface

 @FunctionalInterface
 public interface MyInterface {
     void test(); 
     
     @Override
     String toString();
 }

==toString 为Object类下的public方法,所以注解不认为他会一个新的抽象方法,因为任何借口都要直接或者间接继承Object类,继承里面的所有方法

@FunctionalInterface
interface MyInterface {
    void test();

    @Override
    String toString();
}
public class Test2{
    public void myTest(MyInterface myInterface){
        System.out.println(1);
        myInterface.test();
        System.out.println(2);
    }

    public static void main(String[] args) {
        Test2 test2 = new Test2();

        test2.myTest(()-> System.out.println("mytest"));

        MyInterface myInterface=()-> System.out.println("hello");
        System.out.println(myInterface.getClass());
        System.out.println(myInterface.getClass().getSuperclass());
        System.out.println(myInterface.getClass().getInterfaces().length);
        System.out.println(myInterface.getClass().getInterfaces()[0]);
    }
}
1
mytest
2
class com.tang.jdk8.Test2$$Lambda$2/1078694789
class java.lang.Object 
1
interface com.tang.jdk8.MyInterface

4.forEach

List extends Collection extends Iterable
public interface Iterable {
 
    Iterator iterator();
    default void forEach(Consumer action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
    default Spliterator spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

所以list继承了forEach

默认方法保证了Lambda,函数式接口加入,又保证了向下兼容

 

在将函数作为一等公民的语言中,Lambda表达式是函数(Python)。但在java中,Lambda表达式是对象,他们必须依附于一类特别的对象类型——函数式接口。

5.方法引用(粗略)

通过方法引用的形式创建Lambda接口的实例

list.stream().map(String::toUpperCase).forEach(System.out::println);
public String toUpperCase() {
    return toUpperCase(Locale.getDefault());
}

调用toUpperCase方法的当前对象作为输入

输出为执行完toUpperCase方法后的对象

6.Function

Function function=String::toUpperCase;

不能写成String.toupperCase,因为toUpperCase不是静态方法

**如5所讲,一定会存在一个String的实例对象(假设为str),去调用toUpperCase**

总结:如果一个类类型,直接通过 :: 引用实例方法,对应Lambda表达式的第一个参数,就是调用这个方法的对象(this)

 

public class FunctionTest {
    public static void main(String[] args) {
        FunctionTest functionTest=new FunctionTest();
        System.out.println(functionTest.compute(10, val->2*val));
        System.out.println(functionTest.convert( 10, val->String.valueOf(val+"你好")));
        System.out.println(functionTest.convert( 10, val->Integer.toString(val)+"hello"));
     	 System.out.println(functionTest.method1(10));
        
    }
    public int compute(int a, Function function){
        int result = function.apply(a);
        return result;
    }
    public String convert(int a, Function function){
        return function.apply(a);
    }
    public int method1(int a){
        return a*a;
    }
}

Integer -->String Integer.toString(a); String.valueOf

结论:Lambda表达式传递的是行为,以前是行为提前定义好,调用行为

default  Function compose(Function before) {
    Objects.requireNonNull(before);
    return (V v) -> apply(before.apply(v));
}
default  Function andThen(Function after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

区别:

compose :先调用参数接口执行apply()方法,得到的结果作为参数再执行apply()方法

andThen:先调用本接口内的apply()方法,得到的接口再调用参数的apply接口的apply()方法

public class FunctionTest2 {
    public static void main(String[] args) {
        FunctionTest2 functionTest2 = new FunctionTest2();
        //4
        int compute = functionTest2.compute(1, val -> val * val, val -> val + 1);
        System.out.println("compute = " + compute);
        //2
        int compute2 = functionTest2.compute2(1, val -> val * val, val -> val + 1);
        System.out.println("compute2 = " + compute2);
    }
    public int compute(int a,Function function1,Function function2){
        return function1.compose(function2).apply(a);
    }
    public int compute2(int a,Function function1,Function function2){
        return function1.andThen(function2).apply(a);
    }
}
compute = 4
compute2 = 2

先执行function2.apply()再把得到的结果作为参数执行function1.apply()

 

拓展:BiFuction

@FunctionalInterface
public interface BiFunction {

    R apply(T t, U u);

    default  BiFunction andThen(Function after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}

7.高阶函数

如果一个函数接收一个函数作为参数,或者返回一个函数作为返回值,那么该函数称为高阶函数。

8.面向对象和面向函数

面向对象传递一个参数(对象),方法体里面定义行为,处理业务逻辑

函数式编程传递两个参数(对象+函数式接口),具体行为被调用着定义,在方法体里面没有具体表现(提供了更高层次的抽象化)

public class PredicateTest2 {
    public static void main(String[] args) {
        List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        PredicateTest2 predicateTest2=new PredicateTest2();
        predicateTest2.conditionalFileter(list, num->num%2!=0);
        System.out.println();
        predicateTest2.conditionalFileter(list, num->num%2==0);

    }

    public void conditionalFileter(List list, Predicate predicate){
        for (Integer i : list) {
            if (predicate.test(i)){
                System.out.println(i);
            }
        }
    }
}
1 3 5 7 9 
2 4 6 8 10  

没有判断条件的时候,打印所有

predicateTest2.conditionalFileter(list, num->true);

9.Supplier

public class Student {
    private String name="张三";
    private int age=20;
    //构造方法略
}
public class StudentTest {
    public static void main(String[] args) {
        Supplier supplier=()->new Student();
        System.out.println(supplier.get().getName());
    }
}

用于没有输入参数的工厂

public class BinaryOperateorTest {
    public static void main(String[] args) {
        BinaryOperateorTest operateorTest = new BinaryOperateorTest();
        System.out.println( operateorTest.opearte(10, 20, (c,d)->c+d));
        System.out.println( operateorTest.opearte(10, 20, (c,d)->c-d));
        System.out.println( operateorTest.opearte(10, 20, (c,d)->c*d));
        System.out.println( operateorTest.opearte(10, 20, (c,d)->c+d));

        String aShort = operateorTest.getShort("tang", "yao", (a, b) -> a.length() -b.length());
        System.out.println("aShort = " + aShort);
        String aShort1 = operateorTest.getShort("tang", "yao", (a, b) -> a.getBytes()[0] - b.getBytes()[0]);
        System.out.println("aShort1 = " + aShort1);
    }
    public int opearte(int a,int b, BinaryOperator binaryOperator){
       return binaryOperator.apply(a, b);
    }
    public String getShort(String a, String b, Comparator comparator){
        return BinaryOperator.minBy(comparator).apply(a, b);
    }

}
30
-10
200
30
aShort = yao
aShort1 = tang
public static  BinaryOperator minBy(Comparator comparator) {
    Objects.requireNonNull(comparator);
    return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}

10.Optional

NPE NullPointerException

if(null!=person){ person...

}

创建Optional对象方法

public static Optional empty() {
    @SuppressWarnings("unchecked")
    Optional t = (Optional) EMPTY;
    return t;
}
private static final Optional EMPTY = new Optional<>(); private Optional() {
        this.value = null;
    }

public static  Optional of(T value) {
    return new Optional<>(value);
}
private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }
    
    public static  T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }
public static  Optional ofNullable(T value) {
    return value == null ? empty() : of(value);
}

重要方法

public boolean isPresent() {
    return value != null;
}
 public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
 //从数据库查出一个对象,不确定是否为空
Optional optional=Optional.ofNullable("hello");
//        if (optional.isPresent()){
//            System.out.println(optional.get());
//        }
        optional.ifPresent(p-> System.out.println(p));
        System.out.println("------------");
        System.out.println(optional.orElse("world"));
        System.out.println("------------");
        System.out.println(optional.orElseGet(()->"你好"));

Optional没有被序列化,做参数的时候会被警告

uploading.4e448015.gif转存失败重新上传取消

11.Value-based Classes

Some classes, such as

java.util.Optional

and

java.time.LocalDateTime

, are

value-based

. Instances of a value-based class:

  • are final and immutable (though may contain references to mutable objects);

  • have implementations of equals, hashCode, and toString which are computed solely from the instance's state and not from its identity or the state of any other object or variable;

  • make no use of identity-sensitive operations such as reference equality (==) between instances, identity hash code of instances, or synchronization on an instances's intrinsic lock;

  • are considered equal solely based on equals(), not based on reference equality (==);

  • do not have accessible constructors, but are instead instantiated through factory methods which make no committment as to the identity of returned instances;

  • are freely substitutable when equal, meaning that interchanging any two instances x and y that are equal according to equals() in any computation or method invocation should produce no visible change in behavior.

A program may produce unpredictable results if it attempts to distinguish two references to equal values of a value-based class, whether directly via reference equality or indirectly via an appeal to synchronization, identity hashing, serialization, or any other identity-sensitive mechanism. Use of such identity-sensitive operations on instances of value-based classes may have unpredictable effects and should be avoided.

12.方法引用

方法引用实际上是Lambda表达式的语法糖

我们可以将方法引用看做【函数指针】

方法引用共分为四类

1.类名::静态方法名

2.引用名(对象名)::实例方法名

3.类名::实例方法名

public int compareByName(Student stu1){
        return this.getName().compareToIgnoreCase(stu1.getName());
}

students.sort((stu1,stu2)->stu1.compareByName(stu2));
students.forEach(System.out::println);
students.sort(Student::compareByName);

类的名字引用方法的时候,一定有对象调用compareByName方法,这里调用的compareByName实际上是第一个student对象(Lambda表达式第一个参数),也就是当前对象stu1(this),后面传入的对象作为参数

4.构造方法引用:类名::new

public String getString(Supplier supplier){
    return supplier.get() + "test";
}

public String getString2(String str, Function function){
    return function.apply(str);
}
MethodRefencedTest methodRefencedTest=new MethodRefencedTest();
System.out.println(methodRefencedTest.getString(String::new));
System.out.println(methodRefencedTest.getString2("hello",String::new));
public String() {
    this.value = "".value;
}
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

test hello

13.默认方法

public interface MyInterface1 {
    default void mythod(){
        System.out.println("MyInterface1");
    }
}
public interface MyInterface2 {
    default void mythod(){
        System.out.println("MyInterface2");
    }
}
public class MyClass implements MyInterface1,MyInterface2 {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        myClass.mythod();
    }
    @Override
    public void mythod() {
//        System.out.println("MyClass");
        MyInterface2.super.mythod();
    }
}

实现了多个接口,如果默认方法名相同,则必须重写方法,如果想用其中一个方法,可以使用接口名+super+默认方法名的方法

java认为实现类比接口更为具体,所以如果MyClass继承了MyInterface1Impl和实现了Interface2,那么对于具有相同的默认方法,则类MyClass优先调用继承类里面的方法。

14.为什么要有默认方法

在 java 8 之前,接口与其实现类之间的 耦合度 太高了(tightly coupled),当需要为一个接口添加方法时,所有的实现类都必须随之修改。默认方法解决了这个问题,它可以为接口添加新的方法,而不会破坏已有的接口的实现。这在 lambda 表达式作为 java 8 语言的重要特性而出现之际,为升级旧接口且保持向后兼容(backward compatibility)提供了途径。

String[] array = new String[] {
        "hello",
        ", ",
        "world",
};
List list = Arrays.asList(array);
list.forEach(System.out::println); // 这是 jdk 1.8 新增的接口默认方法

这个 forEach 方法是 jdk 1.8 新增的接口默认方法,正是因为有了默认方法的引入,才不会因为 Iterable 接口中添加了 forEach 方法就需要修改所有 Iterable 接口的实现类。

15.Stream

三部分构成

1.source(源)

2.零个或多个中间操作

3.终止操作

流操作的分类

1.惰性求值(中间操作)

2.及早求值(终止求值

构造stream的方法

Stream stream1=Stream.of("hello","world","helloWorld!");
String[] MyArray=new String[]{"hello","world","helloWorld!"};
Stream stream2=Stream.of(MyArray);
Stream stream3= Arrays.stream(MyArray);
List list=Arrays.asList(MyArray);
Stream stream4 = list.stream();

本质区别:函数式编程传递的是行为,根据行为对数据进行加工,面向对象编程传递的是数据

 

16.方法签名:

方法签名由方法名称和一个参数列表(方法的参数的顺序和类型)组成。

17.stream.collect

 R collect(Supplier supplier,
              BiConsumer accumulator,
              BiConsumer combiner);
R result = supplier.get();
   for (T element : this stream)
       accumulator.accept(result, element);
    return result;
List list =
        stream.collect(()->new ArrayList<>(),(list1,item)->list1.add(item),(list1,list2)->list1.addAll(list2));

collect需要三个参数,第一个参数是调用supplier的get方法,也就是相当于得到一个list集合,例如:

()->new ArrayList<>()

第二个参数是调用BiConSumer函数式接口的accept方法,该方法传入两个参数,不返回值,例如:

(list1,item)->list1.add(item) item为stream流的每一个元素,添加到每一个ArrayList中

第三个参数也是调用BiConSumer函数式接口的accept方法,目的是将每次产生的Arraylist合并到一个新的ArrayList集合中

(list1,list2)->list1.addAll(list2)

转换为对应的方法引用为:

List list=stream.collect(LinkedList::new,LinkedList::add,LinkedList::addAll);
list.forEach(System.out::println);

hello world helloWorld!

/*
*
* @param  type of the result
* @param supplier a function that creates a new result container. For a
*                 parallel execution, this function may be called
*                 multiple times and must return a fresh value each time.
* @param accumulator an associative,
*                    non-interfering,
*                    stateless
*                    function for incorporating an additional element into a result
* @param combiner an associative,
*                    non-interfering,
*                    stateless
*                    function for combining two values, which must be
*                    compatible with the accumulator function
* @return the result of the reduction
*/

==========华丽的分割线

stream.collect(Collectors.toList());
public static 
Collector> toList() {
    return new CollectorImpl<>((Supplier>) ArrayList::new, List::add,
                               (left, right) -> { left.addAll(right); return left; },
                               CH_ID);
}

默认返回的是ArrayList,如果想返回LinkedList就需要看懂源代码操作

Collections.toCollection();

public static >
Collector toCollection(Supplier collectionFactory) {
    return new CollectorImpl<>(collectionFactory, Collection::add,
                               (r1, r2) -> { r1.addAll(r2); return r1; },
                               CH_ID);
}

根据自己需要创建集合

TreeSet set = stream.collect(Collectors.toCollection(TreeSet::new));
System.out.println(set.getClass());
set.forEach(System.out::println);

hello helloWorld! world

Collectors.joining()拼接stream中内容

String string = stream.collect(Collectors.joining(" ")).toString();
System.out.println(string);


hello world helloWorld!

18.map()

List list = Arrays.asList("hello", "world", "helloWorld", "test");
list.stream().map(String::toUpperCase).collect(Collectors.toList()).forEach(System.out::println);
List list1=Arrays.asList(1,2,34,5,6);
list1.stream().map(i->i*2).collect(Collectors.toList()).forEach(System.out::println);

TEST 2 4 68 10 12

flatMap()

 Stream flatMap(Function> mapper);
Stream> stream = Stream.of(Arrays.asList(0), Arrays.asList(1, 2), Arrays.asList(3, 4, 5, 6));
stream.flatMap(theList->theList.stream()).map(i->i*i).forEach(System.out::println);

0 1 4 9 16 25 36

19.generate()

generate根据Supplier获得对象得到带流

public static Stream generate(Supplier s) {
    Objects.requireNonNull(s);
    return StreamSupport.stream(
            new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}
Stream stream = Stream.generate(UUID.randomUUID()::toString);
stream.findFirst().ifPresent(System.out::println);

01a7d356-448d-47f3-80b9-9c91e6eda6a7

iterate通过种子(seed)得到流

Returns an infinite sequential ordered {@code Stream} produced by iterative
application of a function {@code f} to an initial element {@code seed},
producing a {@code Stream} consisting of {@code seed}, {@code f(seed)},
{@code f(f(seed))}, etc

The first element (position {@code 0}) in the {@code Stream} will be the provided {@code seed}. For {@code n > 0}, the element at position {@code n}, will be the result of applying the function {@code f} to the element at position {@code n - 1}. 简单来说就是得到一个无限串顺序流,给定一个seed,然后f作用于seed生成新的f(seed)无限循环下去 f(f(seed))...

public static Stream iterate(final T seed, final UnaryOperator f{
...
}

UnaryOperator继承Function,得到一个元素返回相同类型元素

public interface UnaryOperator extends Function {
    ...
}

因为是无限串行流,所以需要限制一下(limit)次数

Stream.iterate(2, item->item*2).limit(10).forEach(System.out::println);

2 4 8 16 32 64 128 256 512 1024

int sum = Stream.iterate(1, item -> item + 2).limit(6).filter(i -> i > 2).mapToInt(i -> i * 2).skip(2).limit(2).sum();
System.out.println("sum = " + sum);

sum = 32

Stream.iterate(0, i->(i+1)%2).distinct().limit(6).forEach(System.out::println);

0 1

distinct在前 程序没有终止

Stream.iterate(0, i->(i+1)%2).limit(6).distinct().forEach(System.out::println);

0 1

Process finished with exit code 0

20.内部迭代和外部迭代的区别

uploading.4e448015.gif转存失败重新上传取消

集合关注的是数据与数据存储本身,流关注的是数据的计算。

流与迭代器类似的一点是:流是无法重复使用消费的

21.习题:去重

List list = Arrays.asList("hello welcome", "world hello", "hello world hello", "hello welcome");
//        list.stream().map(item->item.split(" ")).distinct().collect(Collectors.toList())
//                .forEach(System.out::println);
        list.stream().map(item->item.split(" ")).flatMap(s->Arrays.stream(s))
                .distinct().collect(Collectors.toList()).forEach(System.out::println);

hello welcome world

你可能感兴趣的:(JDK8)