JAVA基础之接口与内部类
接口与内部类
本文主要整理了一些作者看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启动失败的问题。
最新文章
- GridBagConstraints 参数解释
- 基于MFC的单文档,多文档,对话框应用程序
- 修改页面JS 360浏览器
- CSS自适应布局(包括两边宽度固定中间宽度自适应与中间宽度固定两边宽度自适应)
- linux忘记mysql密码找回方法
- Server.MapPath查询路径那几件事
- 通过python切换hosts文件
- [搜片神器]之DHT网络爬虫的代码实现方法
- linux/shell sort命令
- python SyntaxError: Non-ASCII character &#39;\xd5&#39; in file
- Ztree手风琴效果(第三版)
- PHPstudy端口占用的问题
- 一文快速了解MaxCompute
- 以太坊智能合约 Solidity 的常用数据类型介绍
- XML基础学习
- 如何确定Isilon cluster的网卡类型是40GbE的还是10GbE的
- 虚拟机与Docker有何不同
- Spring Boot + Spring Cloud 构建微服务系统(六):熔断监控集群(Turbine)
- 自定义鼠标右键(层叠式菜单:cascading menu)
- python 基本数据类型 之 字符串
热门文章
- docket镜像
- swoole 客户端和服务端不断通信
- Eclipse JSP +Tomcat 环境搭建 错误记录
- sqlserver安装失败,此计算机上安装了 Microsoft Visual Studio 2008 的早期版本解决方法
- CentOS8 安装
- 干掉hao123的第n+1种方法
- .Net Core 3.0 MVC 中使用 SqlSugar ORM框架
- STL: set和map的区别、联系、使用
- Docker结合.Net Core初步使用
- springmvc maven 入门及页面拿不到controller的值 显示${message} el表达式不起作用