接口与内部类

本文主要整理了一些作者看JAVA核心技术卷第六章遇到的难点以及其思考, 欢迎小伙伴及时指出错误!

1. Lambda表达式

1. 关于懒计算

在JAVA8中, 提供了 Supplier这个接口实现懒计算

原理是这样的, 主要依据是以下三个原理

  • 在JAVA8的新特性中, 只要一个接口只有一个抽象方法(不包括default和static), 那么这个接口就会被被认为是一个函数式接口, 可以使用lambda表达式, 而注解 @FunctionalInterface 和我们的 @Override 一样, 用于提示, 不写也可以, 但是建议写

  • lambda表达式在被调用时才执行

  • lambda表达式可以做类型推断(不是太重要的原理)

我们可以观察Objects.requireNoNull 方法, 在参数为 null 会抛出一个异常, 异常的内容与我们传递的第二个参数有关

这个方法有三个重载, 我们主要关注的是有两个方法的重载

  • 首先是传统的重载
public static <T> T requireNonNull(T obj, String message) {
if (obj == null)
throw new NullPointerException(message);
return obj;
}

这里直接传入了一个String类型的message, 这样看虽然没什么不妥, 但是设想一下, 如果我们的 null 不是一个经常出现的结果, 同时我们的String是通过调用某个方法得到的, 这样每次执行非空判断, 都会调用我们对message写的方法, 比如我们传入一个时间

new LocalDate(1970, 1, 1);

这样如果有大量的进程调用这个判断, 同时并没有那么多null, 会造成性能浪费

  • 基于懒计算的优化
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
if (obj == null)
throw new NullPointerException(messageSupplier.get());
return obj;
}

与上面不同, 这里的第二个参数是一个 Supplier 接口, 我们从第一点可以得知, 由于该接口只有一个抽象方法, 因此它是一个函数式接口, 我们可以使用Lambda表达式; 又根据我们第二点, lambda表达式只有在被调用的时候才会执行, 那么如果我们没有那么多的空判断, 这个方法就不会执行, 当我们的第二个参数很复杂(比如要向数据库查询数据), 这样就可以节省了大量的性能, 第二个参数的lambda表达式我们可以这样写

() -> new LocalDate(1970, 1, 1)

类似的, 与懒计算设计思路相似的优化方法还有懒加载, 即页面的元素(比如图片或者视频等)只有在被调用(比如我们往下翻页的时候)才加载, 这样大大缓解了服务器的压力与网络的压力, 毕竟不是所有人都会看到底的

2. Predicate接口

@FunctionalInterface
public interface Predicate<T> { boolean test(T t); 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);
} default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
} static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}

可以看出, 这个接口同样只有一个抽象方法, 因此他也是一个函数式接口, 这个函数式接口很有用, 因为它可以返回一个布尔值, 在我们传入一个方法可以做判断

3. 关于方法引用

  • 方法引用主要有三种情况

    • object :: instanceMethod 等价于向方法传递参数的lambda表达式
    • Class :: instanceMethod 等价于第一个参数作为方法的隐式参数(即this, 表示该参数自己, 方法包括定义自己的属性或者调用自己的一些方法, 最后的结果会返回到这个参数上), 其余的参数会传递到方法
    • Class :: staticMethod 等价于所有的参数都传递到静态方法中, 与上面的区别是有没有隐式参数(即改变了自己的值)
  • 虽然我们可以用lambda表达式来等价方法引用, 但是两者最重要的区别是 方法引用会立即执行, 而lambda表达式只有在调用的时候才会执行

  • 只有当lambda表达式的方法体只调用一个方法而不做其他操作时, 我们才可以将lambda表达式重写为方法引用, 比如下面的就不可以, 因为它除了方法调用, 还进行了比较

s -> s.length() == 0

4. 关于构造器引用

  • 构造器引用的基本结构

    • Class :: new
    • 表示 Class 构造器的一个引用, 引用的构造器取决于上下文, 编译器会自动推导
  • 数组类型的构造器引用

    • Class[] :: new

    • 等价于

      x -> new Class[x]

      即创建了一个指定类型的对象数组

5. 关于变量的作用域

  • lambda可以捕获外围作用域中的变量的值
  • lambda表达式中捕获的值必须实际上是 事实最终变量, 即初始化后就不再为其赋新值, 这是由于lambda表达式在调用后才执行, 如果改变的话会造成不安全
  • lambda表达式的体与嵌套块有相同的作用域, 我们可以理解为, 在lambda表达式左侧传入的变量和上下文的变量的作用域是一致的
    • 在lambda表达式中, 没与参数也要写括号 () -> xxx
    • 在lambda表达式中, 会自动推断变量类型, 可以不写, (String first) -> xxx 和 (first) -> xxx是一样的, 因此如果上文有first这个变量, 这里就会报变量冲突的错误

2. 内部类

1. 局部内部类

  • 在一个方法中局部定义的类叫做局部内部类

  • 声明局部类时不能有访问说明符(即 public private protected), 作用域仅限于声明这个局部类的类中 ==> 可以访问类的全部属性, 包括私有属性

  • 优点: 对外部世界完全屏蔽

2. 匿名内部类

  • 如果只想创建局部内部类的一个对象而不需要给其指定名字, 可以使用匿名内部类

  • new SuperType(construction parameters)
    {
    inner class methods and data
    }
  • SuperType可以是接口 ==> 匿名内部类实现这个接口

  • SuperType可以使一个类 ==> 匿名内部类拓展这个类

  • 如果参数列表的结束小括号后面跟着一个开始大括号, 就是在定义匿名内部类

  • 与lambda表达式最大的区别

    • lambda编译后不会生成class文件,那么也就略过了类的加载、验证、解析等。相当于是在运行时再进行相应的操作
    • 这里主要体现在对Spring的影响中, 在spring注入过程中,无法注入含确定类型的入参和出参方法的实现类,所以,才会出现无法确定类型,导致注入失败,从而springboot启动失败的问题。

JAVA基础之接口与内部类的更多相关文章

  1. Java基础十--接口

    Java基础十--接口 一.接口的定义和实例 /* abstract class AbsDemo { abstract void show1(); abstract void show2(); } 8 ...

  2. Java基础-面向接口(interface)编程

    Java基础-面向接口(interface)编程 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.接口的概念 接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的“类 ...

  3. 夯实Java基础(十一)——内部类

    1.内部类的概念 内部类顾名思义:将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类.对于很多Java初学者来说,内部类学起来真的是一头雾水,根本理解不清楚是个什么东西,包括我自己(我太菜 ...

  4. Java抽象类、接口、内部类

    抽象类的概念: 1.Java中可以定义没有方法体的方法,还方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类: 2.如,shape类计算周长和面积的方法无法确定,那么就可以将这样 ...

  5. 【Java基础】接口和抽象类之间的对比

    Java 中的接口和抽象类之间的对比 一.接口 Interface,将其翻译成插座可能就更好理解了.我们通常利用接口来定义实现类的行为,当你将插座上连接笔记本的三角插头拔掉,换成微波炉插上去的时候,你 ...

  6. JAVA基础之接口

    接口 学习完框架之后,整合SSM过程中对于接口的认识加深了许多.根据<java核心技术>这本书进一步研究了一下. 1.概念 java核心技术是这样说的:"在Java程序设计中,接 ...

  7. Java基础之接口与抽象类及多态、内部类

    final关键字 被其修饰的类,不能被继承. 被其修饰的方法,不能被覆盖. 被其修饰的变量,是一个常量,不能被修改,所以定义时必须初始化(和C++的const类似). 一般有final,会搭配stat ...

  8. Java 基础 面向对象- 成员内部类/局部内部类/举例Comparable 接口的匿名内部类

    笔记: package 任务135; /**类的 内部类, *1.相当于说, 我们可以在类的内部再定义类, * 2.成员内部类: * a.是外部类的一个成员,4个修饰符:static, final , ...

  9. java基础(十三)-----详解内部类——Java高级开发必须懂的

    可以将一个类的定义放在另一个类的定义内部,这就是内部类. 为什么要使用内部类 为什么要使用内部类?在<Think in java>中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能 ...

随机推荐

  1. Hudi on Flink在顺丰的实践应用

    ​ 获取PDF版本 请关注"实时流式计算" 后台回复 "flink1015"

  2. 增强for循环的用法

    一.增强for循环 增强for循环的作用: 简化迭代器的书写格式.(注意:增强for循环的底层还是使用了迭代器遍历.)增强for循环的适用范围: 如果是实现了Iterable接口的对象或者是数组对象都 ...

  3. empty()和size() == 0有区别吗

    empty()和size() 这里说的empty()和size()都是STL的容器中提供的接口,分别用来判断当前容器是否为空和获取当前包含的元素个数 区别 其实按道理来说两者应该是相等的,而且STL容 ...

  4. 经验分享:计算机 web 浏览器——访问剪切板图片

      有时候,我们希望能访问用户的剪切板,来实现一些方便用户的功能:但是另一方面,剪切板里的数据对用户来说又是非常隐私的,所以浏览器在获取信息方面有安全限制,同时也提供访问接口. 当我们需要实现在富文本 ...

  5. Python操作CSV和Excel

    概述 csv是最通用的文件格式,本质是文本文件,用记事本即可打开.同一行中每个字段间用逗号分隔,在csv中显示的是在不同单元格中,在记事本中显示的是一行中用逗号分隔. xls是excel专用格式,是二 ...

  6. Nacos快速入门

    什么是 Nacos Nacos 是阿里巴巴推出来的一个新开源项目,这是一个更易于构建云原生应用的动态服务发现.配置管理和服务管理平台. Nacos 致力于帮助您发现.配置和管理微服务.Nacos 提供 ...

  7. 从0实现python批量爬取p站插画

    一.本文编写缘由 很久没有写过爬虫,已经忘得差不多了.以爬取p站图片为着手点,进行爬虫复习与实践. 欢迎学习Python的小伙伴可以加我扣群86七06七945,大家一起学习讨论 二.获取网页源码 爬取 ...

  8. python识别视频黑屏或者低清晰度

    第一步:获取视频第一帧图片 https://www.cnblogs.com/pythonywy/p/13749735.html 第二步:进行识别 import os import numpy as n ...

  9. Vue3实战系列:Vue3.0 + Vant3.0 搭建种子项目

    最近在用 Vue3 写一个开源的商城项目,开源后让大家也可以用现成的 Vue3 大型商城项目源码来练练手,目前处于开发阶段,过程中用到了 Vant3.0,于是就整理了这篇文章来讲一下如何使用 Vue3 ...

  10. Api版本控制

    版本控制是计算机软件行业人士使用的术语.但进化是我们所有人都要经历的事情,它适用于这个世界上的每个对象. 在计算机软件行业,可以看到每3至4年,每台计算机软件都会附带不同的发行版/版本,以满足当前/现 ...