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 <i>functional interface</i> 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
* <em>not</em> 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<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> 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<String,String> 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<Integer,Integer> function){
int result = function.apply(a);
return result;
}
public String convert(int a, Function<Integer,String> function){
return function.apply(a);
}
public int method1(int a){
return a*a;
}
}
Integer -->String Integer.toString(a); String.valueOf
結論:Lambda表達式傳遞的是行爲,以前是行爲提前定義好,調用行爲
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));
}
區別:
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<Integer,Integer> function1,Function<Integer,Integer> function2){
return function1.compose(function2).apply(a);
}
public int compute2(int a,Function<Integer,Integer> function1,Function<Integer,Integer> function2){
return function1.andThen(function2).apply(a);
}
}
compute = 4
compute2 = 2
先執行function2.apply()再把得到的結果作爲參數執行function1.apply()
拓展:BiFuction
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> 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<Integer> 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<Integer> list, Predicate<Integer> 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<Student> 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<Integer> binaryOperator){
return binaryOperator.apply(a, b);
}
public String getShort(String a, String b, Comparator<String> comparator){
return BinaryOperator.minBy(comparator).apply(a, b);
}
}
30
-10
200
30
aShort = yao
aShort1 = tang
public static <T> BinaryOperator<T> minBy(Comparator<? super T> 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<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
private static final Optional<?> EMPTY = new Optional<>(); private Optional() {
this.value = null;
}
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
public static <T> Optional<T> 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<String> 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沒有被序列化,做參數的時候會被警告
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
, andtoString
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
andy
that are equal according toequals()
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<String> supplier){
return supplier.get() + "test";
}
public String getString2(String str, Function<String,String> 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<String> 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<String> list=Arrays.asList(MyArray);
Stream<String> stream4 = list.stream();
本質區別:函數式編程傳遞的是行爲,根據行爲對數據進行加工,面向對象編程傳遞的是數據
16.方法簽名:
方法簽名由方法名稱和一個參數列表(方法的參數的順序和類型)組成。
17.stream.collect
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
R result = supplier.get();
for (T element : this stream)
accumulator.accept(result, element);
return result;
List<String> 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<String> list=stream.collect(LinkedList::new,LinkedList::add,LinkedList::addAll);
list.forEach(System.out::println);
hello world helloWorld!
/*
*
* @param <R> 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 <a href="package-summary.html#Associativity">associative</a>,
* <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* function for incorporating an additional element into a result
* @param combiner an <a href="package-summary.html#Associativity">associative</a>,
* <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* 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 <T>
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
默認返回的是ArrayList,如果想返回LinkedList就需要看懂源代碼操作
Collections.toCollection();
public static <T, C extends Collection<T>>
Collector<T, ?, C> toCollection(Supplier<C> collectionFactory) {
return new CollectorImpl<>(collectionFactory, Collection<T>::add,
(r1, r2) -> { r1.addAll(r2); return r1; },
CH_ID);
}
根據自己需要創建集合
TreeSet<String> 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<String> list = Arrays.asList("hello", "world", "helloWorld", "test");
list.stream().map(String::toUpperCase).collect(Collectors.toList()).forEach(System.out::println);
List<Integer> 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()
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
Stream<List<Integer>> 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<T> Stream<T> generate(Supplier<T> s) {
Objects.requireNonNull(s);
return StreamSupport.stream(
new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}
Stream<String> 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
<p>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<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f{
...
}
UnaryOperator繼承Function,得到一個元素返回相同類型元素
public interface UnaryOperator<T> extends Function<T, T> {
...
}
因爲是無限串行流,所以需要限制一下(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.內部迭代和外部迭代的區別
集合關注的是數據與數據存儲本身,流關注的是數據的計算。
流與迭代器類似的一點是:流是無法重複使用消費的
21.習題:去重
List<String> 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