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 super T> 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 super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default Function 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 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 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 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 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 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没有被序列化,做参数的时候会被警告
转存失败重新上传取消
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 super T, ? extends Stream extends R>> 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.内部迭代和外部迭代的区别
转存失败重新上传取消
集合关注的是数据与数据存储本身,流关注的是数据的计算。
流与迭代器类似的一点是:流是无法重复使用消费的
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