第1部分-基础知识

第1章:Java8、9、10、11的变化

简洁:利用Java8,可以书写更加简洁的代码。

多核:当前计算机硬件大多都是多核CPU,而Java并没有充分利用多核的优势,即使是多线程也是在单个CPU上进行。利用synchronized成本会比想象中的更高。

行为参数化:方法引用(::语法)和Lambda表达式。将方法作为参数来传递,实现函数式编程。Lambda表达式其实就是一个匿名函数,但没有传统的匿名函数有那么多繁琐的模板代码,形式上更加简洁。

接口的默认方法:为了解决接口平滑演进,比如如何改变已发布的接口而不破坏已有的实现,可使用这一技术实现。

为什么Java还在变:即使现在很活跃,并不代表以后还会一直活跃。只有一直变化,才能不被时代抛弃。要么改变,要么衰亡。COBOL的前车之鉴。

流处理:Unix或Linux中,可以通过管道(|)将多个命令连接在一起,其实就是一个流处理,这些命令是同时处理的。Java8借鉴的这一思想,推出了Stream API,这样就可以把输入的不相关部分拿到几个CPU上分别执行,这是几乎免费的并行,用不着费劲搞Thread了。Collection主要是为了存储和访问数据,Stream主要用于描述对数据的计算,前者迭代是外部迭代,开发者自己控制,而后者迭代是内部迭代,开发者无需关心如何迭代,内部迭代可并行迭代,这是外部迭代不好处理的地方。

谓词:predicate,一个返回boolean值的函数。

Optional类:避免出现NullPointerException。

(结构化的)模式匹配:Switch的扩展形式,能够同时将一个数据类型分解成元素。

第2章:通过行为参数化传递代码

应对不断变化的需求
第1次尝试:直接定义一个判断绿苹果的方法,无扩展性。

public static List<Apple> filterGreenApples(List<Apple> inventory) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : inventory) {
            if (apple.getColor() == Color.GREEN) {
                result.add(apple);
            }
        }
        return result;
    }

第2次尝试:在参数中增加颜色或重量,可以判断颜色或重量,有一定扩展性。

public static List<Apple> filterApplesByColor(List<Apple> inventory, Color color) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : inventory) {
            if (apple.getColor() == color) {
                result.add(apple);
            }
        }
        return result;
    }
public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : inventory) {
            if (apple.getWeight() > weight) {
                result.add(apple);
            }
        }
        return result;
    }

第3次尝试:在参数中增加能想到的所有属性,这样的代码可读性和可维护性会非常差,非常不推荐。代码省略。

第4次尝试:抽象行为,利用谓词定义一个接口ApplePredicate来对选择标准建模,不同的选择实现内容不同。然后将接口作为参数传入filter方法。但可惜的是filter方法只能接受对象,所以必须把代码包裹在ApplePredicate对象里面,有点像策略模式,我们还想更加简洁。

interface ApplePredicate {
        boolean test(Apple a);
    }

static class AppleWeightPredicate implements ApplePredicate {
        @Override
        public boolean test(Apple apple) {
            return apple.getWeight() > 150;
        }
    }

static class AppleColorPredicate implements ApplePredicate {
        @Override
        public boolean test(Apple apple) {
            return apple.getColor() == Color.GREEN;
        }
    }

static class AppleRedAndHeavyPredicate implements ApplePredicate {
        @Override
        public boolean test(Apple apple) {
            return apple.getColor() == Color.RED && apple.getWeight() > 150;
        }
    }
public static List<Apple> filter(List<Apple> inventory, ApplePredicate p) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : inventory) {
            if (p.test(apple)) {
                result.add(apple);
            }
        }
        return result;
    }

第5次尝试:使用匿名类,避免声明实现ApplePredicate接口的类,还要实例化。虽然比之前少了很多代码,但还是有很多模板代码。

List<Apple> redApples2 = filter(inventory, new ApplePredicate() {
            @Override
            public boolean test(Apple a) {
                return a.getColor() == Color.RED;
            }
        });

第6次尝试:使用Lambda表达式,相比匿名类更加简洁,将所有的模板代码都省去。

List<Apple> redApples3 = filter(inventory, (Apple apple) -> Color.RED.equals(apple.getColor()));

第7次尝试:将List类型抽象化,之前的代码只适用于Apple,List类型抽象化,从而超越眼前要处理的问题。在灵活性和简洁性之前找到了最佳平衡点。

public interface Predicate<T>{
        boolean test(T t);
    }

public static <T> List<T> filter(List<T> list, Predicate<T> p){
        List<T> result = new ArrayList<>();
        for (T e : list){
            if (p.test(e)){
                result.add(e);
            }
        }
        return result;
    }
List<Integer> numbers = Arrays.asList(1, 2, 4);
List<Integer> evenNumbers = filter(numbers, (Integer i) -> i % 2 == 0);

第3章:Lambda表达式

Lambda表达式可以看成是匿名函数,也是没有声明名称的方法,也可以像匿名函数一样,作为参数传递给一个方法。Lambda表达式没有名称,但有参数列表、函数主体、返回类型和可能的异常列表。
Lambda表达式的有个两种风格:

  • 表达式风格:(parameters) -> expression
  • 块风格:(parameters) -> { statements;},块中是一个个语句。

函数式接口:只定义一个抽象方法的接口(多个默认实现方法不影响)

可以使用注解@FunctionalInterface来表示这个接口是函数式接口,但不是强制要求一定需要此注解。

函数描述符:函数式接口中的方法签名。

Java8库中常见的函数式接口

  • Predicate接口:T -> boolean,布尔表达式
  • Consumer接口:T -> void,消费一个对象
  • Function接口:T -> R,从一个对象中选择/提取
  • Supplier接口:() -> T,创建对象
  • UnaryOperator接口:T -> T
  • BinaryOperator接口:(T, T) -> T,合并两个值
  • BiPredicate接口:(T, U) -> boolean
  • BiConsumer接口:(T, U) -> void
  • BiFunction接口:(T, U) -> R,比较两个对象

异常处理

  • 自定义函数式接口,并声明受检异常
  • 把Lambda包在一个try/catch块中

类型检查

Lambda的类型是从使用Lambda的上下文推断出来的。因为Lambda表达式将名称都省略掉了,所以只要Lambda表达式的参数列表和出参跟某个函数式接口的方法签名兼容,那么就可以对应传递。

显式二义性问题

两个不同的函数式接口有相同的函数描述符,那么就可能出现一个Lambda表达式对应两个函数式接口。为解决这种问题,可使用显式将Lambda表达式强制转换成对应的接口。

类型推断

Lambda表达式的入参列表可以显式表明类型,也可以不显式表明,后者就是通过类型推断而来。

使用局部变量

Lambda表达式可以没有限制的捕获实例变量和静态变量,但是局部变量必须显式声明为final,或事实上是final。也就是说,Lambda表达式只能捕获指派给它们的局部变量一次。

方法引用

语法::,符号前面是类型,符号后面是方法,针对仅仅涉及单一方法的Lambda的语法糖。
三类方法引用:

  • 指向静态方法的方法引用,如Integer::parseInt
  • 指向任意类型实例方法的方法引用,引用一个对象的方法,这个对象是Lambda表达式的一个参数,如String::length
  • 指向现存对象或表达式实例方法的方法引用,需要传递一个私有辅助方法时特别有用。

构造函数引用,ClassName::new

  • 无参构造函数,适合Supplier接口
  • 一个参数的构造函数,适合Function接口

复合Lambda表达式的有用方法

  • 比较器复合,Comparator接口支持逆序reversed和比较器链thenComparing两个方法
  • 谓词复合,Predicate接口支持negate、and和or三个方法
  • 函数复合,Function接口有andThen和compose两个默认方法,方法返回Function的一个实例
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容