11 Java 方法引用、异常处理、Java接口之函数式编程(接口知识补充Function<T,R>、BiFunction<T, U, R>和自定义泛型接口)
文章目录
- 前言
- 一、Java接口之函数式编程 --- 接口知识补充
- 1 Function<T,R>泛型接口
- 2 BiFunction<T, U, R>泛型接口
- 3 自定义泛型函数式编程接口
- 4 使用lambda表达式、方法引用进行函数式编程
- 二、方法引用
- 1 方法引用初体验(以Array.sort()方法为例)
- (1)什么是方法引用?(怎么理解)
- (2)为什么方法引用不用像lambda表达式一样传入参数个数?
- 2 引用静态方法
- 3 引用成员方法
- (1)引用其他类成员方法:其他类对象(实例对象)::方法名
- ---- 示例1:方法引用结合类对象的使用
- ---- 示例2:方法引用结合Function<T,R>泛型接口或者自定义接口对象的使用(强烈推荐这种)
- (2)引用本类成员方法
- (3)引用父类的成员方法
- 4 方法引用(引用成员方法)在Java Swing GUI编程中的使用(套路)
- 5 引用构造方法
- (1)基本使用
- (2)引用数组的构造方法
- 5 特例:使用类名引用成员方法(很好用)
- 6 总结
- 7 练习
- 三、异常处理
- 1 基本概念知识
- (1)什么是异常
- (2)异常的体系结构(异常的分类)
- (3)编译时异常和运行时异常
- (4)异常的作用(Java中异常信息的阅读,要会)
- 2 异常的处理方式
- (1)JVM默认的处理方式
- (2)自己处理(捕获异常):try{ } catch(){ }
- ---- 基本使用
- ---- try中异常类型我不知道应该捕获什么类型该怎么办?catch(Excepttion e) 即可捕获所有种类异常(就是多态的使用嘛)
- ---- try中多种不同类型的异常捕获
- ---- 关于try{}catch(){}的灵魂4问(解决这4个问题你就完全对try...catch...逻辑没有问题了)
- ---- 异常中的常见方法
- (3)抛出处理(抛出异常)
- ----- throws方法签名自动抛出异常(也称方法签名抛出异常)(前面SimpleDateFormat.parse()时间格式化为什么一定要选择抛出异常解释)
- ----- throw方法内手动抛出异常(这个才是我们要重点学习的)
- (4)综合练习
- 3 自定义异常
前言
一、Java接口之函数式编程 — 接口知识补充
前面在学习接口的时候很多都不知道,其中接口的有一项功能就是提供函数式编程的功能。为此Java还内置了两个专门的接口Function<T,R>和BiFunction<T, U, R>泛型接口。本节复习接口的使用和介绍这两个接口,已经到最终的自定义函数式接口(理解了这个接口就真的没有问题了)。
在前面学习stream流里面的map中间方法,里面用到了Function这个泛型接口,后来在学习方法引用,我发现这个接口结合方法引用使用有点有趣。虽然目前我很少看到有人这么干,但是我感觉很有意思,这里就来学习一下这个接口。
1 Function<T,R>泛型接口
很显然,这个函数式接口支持:
- T 输入参数的类型。
- R 是输出结果的类型。

源码里面这是一个函数式接口,在之前学习stream流里面的map中间方法也知道这个接口是用来定义映射关系的,简单来说就是函数关系。如果你用惯了python ,会发现java虽然说支持函数式编程,但也仅仅只是lambda表达式这种套着匿名对象类的假函数式编程。和python里面的def比较起来算个屁的函数式编程。
但是我发现Function这个接口有点 def 那个味道了,看下面例子。
/*
Function<Integer, Integer> square1 = new Function<Integer, Integer>() {@Overridepublic Integer apply(Integer x) {// 泛型中第一个参数:输入的 x 的 数据类型// 泛型中第二个参数:输出的 y 的 数据类型// x : 输入的数据// 返回值: 表示映射后的数据return x * x;}
}; // 实例化一个函数对象
int result1 = square1.apply(5);
System.out.println(result1); // 25
*/// 使用lambda表达式的简洁定义函数对象方式
Function<Integer, Integer> square2 = x -> x * x;
int result = square2.apply(5);
System.out.println(result); // 25
可以看到这样是不是十分接近python中的 def 了
这个接口只支持 单输入,下面的BiFunction<T, U, R>支持两个输入
2 BiFunction<T, U, R>泛型接口
很显然,这个函数式接口支持:
- T 和 U 是输入参数的类型。
- R 是输出结果的类型。
/*
BiFunction<Integer, Integer, Integer> add1 = new BiFunction<Integer, Integer, Integer>() {@Overridepublic Integer apply(Integer x, Integer y) {return x + y;}
};
int result1 = add1.apply(5, 3);
System.out.println(result1); // 8
*/BiFunction<Integer, Integer, Integer> add2 = (x, y) -> x + y;
int result = add2.apply(5, 3);
System.out.println(result); // 8
3 自定义泛型函数式编程接口
一般情况下,输入超过2个就要我们自己定义函数式接口了。
自定义接口
package cn.hjblogs.demo;@FunctionalInterface
interface TriFunction<T, U, V, R> {R apply(T t, U u, V v);
}
TriFunction<Integer,Integer,Integer,Integer> add = new TriFunction<Integer, Integer, Integer, Integer>() {@Overridepublic Integer apply(Integer x, Integer y, Integer z) {return x*y*z;}
};
int res = add.apply(2,3,5);
System.out.println(res); // 30
4 使用lambda表达式、方法引用进行函数式编程
public class Main {public static void main(String[] args) {// 使用Lambda表达式Function<String, Integer> func1 = (String value) -> Integer.parseInt(value);// 使用方法引用Function<String, Integer> func2 = Integer::parseInt;// 两者效果相同,都是将字符串转换为整数int result1 = func1.apply("123");int result2 = func2.apply("123");System.out.println(result1); // 输出: 123System.out.println(result2); // 输出: 123}
}
这个示例充分说明了,lambda表达式、方法引用在Java中就是一个实例对象,创建了对于函数式接口的匿名内部类实例对象。
二、方法引用
- 方法:就是以前我们学习过的一些方法。
- 引用:就是把已经有的方法拿过来用
怎么用?当做函数式接口中抽象方法的方法体
1 方法引用初体验(以Array.sort()方法为例)
(1)什么是方法引用?(怎么理解)

结合上面的图。我们来讲讲方法引用。
图中的Array.sort(数组,排序规则) 这个数组排序方法,关键在于排序规则这个参数是一个接口(准确的来说是一个函数式接口),意味着我们必须要传入这个接口的一个实现类。前面我们学过两种办法
(1)传入一个该接口的匿名内部类对象 (2)使用lambda表达式
本质上都是传进去一个该函数式接口的实现类对象。由于这是一个函数式接口,要我们重写的抽象方法只有一个,我们也可以理解为传入这个函数式接口的目的就是为了单单调用这个要求我们重写的抽象方法而已。
因此我们有了第三中传入方式
(3)方法引用:其实本质上就是lambda表达式的进一步简写形式
我们在上述的排序规则处,直接传入一个各种规则都和接口里面的要求重写的方法的形式一致的方法当做接口中抽象方法的方法体(理解成在接口的原抽象方法的方法体处调用引用的方法这样理解就可以了)
【特别注意】:引用的方法是充当接口里面方法的方法体,简单来说就在抽象方法里面调用这个方法,不是充当抽象方法,是充当抽象方法的方法体的作用
【注】这个方法可以是java或者第三方写好的,也可以是我们自己写的。必须注意,方法格式必须和函数式接口里面的方法输入参数个数、返回值、数据类型等等这些严格对应(只有这样底层才能自动推导出来)。也就是说下面三点必须满足才行:
(1)需要有函数式接口
(2)被引用的方法必须已经存在
(3)被引用方法的形参和返回值需要跟抽象方法保持一致(关于这一点,被引用方法的形参个数可以比抽象方法少甚至没有,反正只要能够在抽象方法中被调用并满足当前需求就可以引用)
(4)被引用方法的功能要满足当前需求
说白了,就是以前传匿名内部类、lambda表达式、现在方法引用就是传一个方法放进接口里面方法的方法体;其实底层还是在传接口的实现类。
为了方便演示方法引用,我们这里先给出引用静态方法的方法引用语法格式:
- 格式: 类名::静态方法
- 范例: Integer : :parseInt
下面演示一下就清楚了:
public class Test {public static void main(String[] args) {Integer[] arr = {3, 5, 4, 1, 6, 2};/*// 匿名内部类Arrays.sort(arr, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o1 - o2;}});*//*// lambda表达式Arrays.sort(arr, (o1, o2) -> o1 - o2);*/// 方法引用// 把这个方法当做函数式接口抽象方法的方法体Arrays.sort(arr, Test::my_compare); // 方法引用 类名::方法名System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 6]}// 自定义比较方法,要被方法引用的public static int my_compare(int num1, int num2){return num1 - num2;}}
- :: 符号是方法引用符
上面的方法引用其实可以等价成下面这样理解就好了(帮助理解)---- 我觉得这样理解是最好的
// ============= 下面是上面方法引用的等价
// 等价于下面 lambda表达式
// Arrays.sort(arr, (o1, o2) -> Test.my_compare(o1, o2));// 等价于下面 匿名内部类
Arrays.sort(arr, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return Test.my_compare(o1, o2); // 等价于 Test::my_compare,在方法体中调用引用方法}
});
关于为什么方法引用不许要像lambda表达式一样写出输入参数个数。
(2)为什么方法引用不用像lambda表达式一样传入参数个数?
当你使用方法引用时,虽然你没有显式地写出参数,但实际上参数是隐式传递的。Java 编译器会根据上下文自动推导出参数,并将它们传递给引用的方法。
具体说明:以下面的 Integer::parseInt 为例,它实际上等价于Lambda表达式 (String value) -> Integer.parseInt(value),这里的 value 是Lambda表达式中的参数。在使用方法引用时,编译器会自动处理参数的传递。
public class Main {public static void main(String[] args) {// 使用Lambda表达式Function<String, Integer> func1 = (String value) -> Integer.parseInt(value);// 使用方法引用Function<String, Integer> func2 = Integer::parseInt;// 两者效果相同,都是将字符串转换为整数int result1 = func1.apply("123");int result2 = func2.apply("123");System.out.println(result1); // 输出: 123System.out.println(result2); // 输出: 123}
}
下面解释为什么上面代码为什么采用方法引用不用传入参数:
- 方法引用之所以不需要显式写出参数,是因为编译器可以推导出这些参数并将它们传递给引用的方法。在上面的例子中:
(1)Function<String, Integer> 是一个函数式接口,定义了一个抽象方法 apply,这个方法接受一个 String 参数并返回一个 Integer。
(2)当使用 Integer::parseInt 作为方法引用时,编译器会自动知道 apply 方法的参数是 String 类型,因此会将这个 String 参数传递给 Integer.parseInt 方法。
(3)所以,在 func2.apply(“123”) 被调用时,实际执行的操作是将 “123” 传递给 Integer.parseInt,这就是为什么你没有显式地写出参数 (String value) 但仍然可以正常工作。
初步了解了方法引用,下面我们就开心学习不同情形下方法引用的格式是什么。
2 引用静态方法
- 格式: 类名::静态方法
- 范例: Integer : :parseInt
练习:集合中有以下数字,要求把他们都变成int类型
“1” “2” “3” “4” “5”
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"1","2","3","4","5","6","7","8","9","10");List<Integer> res = list.stream().map(Integer::parseInt).collect(Collectors.toList());
System.out.println(res); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]/*
// 等价于
List<Integer> res2 = list.stream().map(new Function<String, Integer>() {@Overridepublic Integer apply(String s) {return Integer.parseInt(s);}
}).collect(Collectors.toList());System.out.println(res2);
*/
3 引用成员方法
引用成员方法包含三类:引用其他类成员方法、引用本类成员方法、引用父类成员方法
- 格式:对象::成员方法
(1)其他类:其他类对象(实例对象)::方法名
(2)本类:this::方法名
(3)父类:super::方法名
需要注意的是如果是引用静态方法和在其他方法中引用静态方法,静态方法是没有this和super关键字的,所以要采用类名或者类对象这样引用才行。
下面我们依次演示
(1)引用其他类成员方法:其他类对象(实例对象)::方法名
---- 示例1:方法引用结合类对象的使用
练习:
集合中有一些名字。按照要求过滤数据
数据:”张无忌"“,“周芷若”,赵敏”,”张强”,”“张三非”"要求:只要以张开头。而且名字是3个字的
我们先从lambda表达式和匿名内部类开始,直接上方法引用的确有点反人类啊!
lambda表达式和匿名内部类
// 集合中有一些名字。按照要求过滤数据
// 数据:”张无忌"","周芷若”,赵敏”,”张强",""张三非""要求:只要以张开头。而且名字是3个字的ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三非");// lambda表达式
// list.stream().filter(s -> s.startsWith("张") && s.length() == 3 ).forEach(s -> System.out.println(s));// 匿名内部类
list.stream().filter(new Predicate<String>() {@Overridepublic boolean test(String s) {return s.startsWith("张") && s.length() == 3;}}).forEach(s -> System.out.println(s)); // 张无忌 张三非
方法引用(普通版本):通过上面的匿名内部类我们找不到合适的方法引用,只能自己写一个,由于这里是演示引用其他类成员方法,那我们就创建一个其他类并写成其中的引用方法吧。
public class Test {public static void main(String[] args) {// 集合中有一些名字。按照要求过滤数据// 数据:”张无忌"","周芷若”,赵敏”,”张强",""张三非""要求:只要以张开头。而且名字是3个字的ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三非");list.stream().filter(n相关文章:
11 Java 方法引用、异常处理、Java接口之函数式编程(接口知识补充Function<T,R>、BiFunction<T, U, R>和自定义泛型接口)
文章目录 前言一、Java接口之函数式编程 --- 接口知识补充1 Function<T,R>泛型接口2 BiFunction<T, U, R>泛型接口3 自定义泛型函数式编程接口4 使用lambda表达式、方法引用进行函数式编程二、方法引用1 方法引用初体验(以Array.sort()方法为例)(1)什么是方法引…...
深入探索 Go 语言的编译器与垃圾回收机制
Go 编译器 Go 编译器是通过 go 工具执行的,这个工具的功能不仅仅是生成可执行文件。你可以使用 go tool compile 命令来编译一个 Go 源文件。这个操作将生成一个目标文件,也就是 .o 后缀的文件。以下是在 macOS Mojave 系统上执行的命令和结果展示&…...
2024国赛数学建模-模拟火算法(MATLAB 实现)
模拟退火算法 1.1 算法原理 模拟退火算法的基本思想是从一给定解开始 ,从邻域 中随机产生另一个解 ,接受 Metropolis准则允许目标函数在 有限范围内变坏 ,它由一控制参数 t决定 ,其作用类似于物 理过程中的温度 T,对于控制参数的每一取值 ,算法持续进 行“产生 —判断 —接受…...
YOLOv8 只检测人 只画框不要标签
参考了这个:YOLOv8只检测人(或其他一种或者多种类别)_yolov8只检测指定类别-CSDN博客 1. 只检测人:predict的时候指定参数classes[0] 2. 只画框不要标签:plot的时候传入labelsFalse 3. 标签中去掉置信度:…...
如何将网络安全防范游戏化
组织对威胁的准备和恢复能力跟不上网络犯罪分子的进步。 一些首席执行官仍然认为网络安全需要偶尔干预,而不是持续关注。 但对于许多公司来说,情况并非如此;网络威胁准备需要协调一致的培训工作,因此网络安全团队在攻击发生时已…...
Qt QGraphicsView实现图片放缩、鼠标拖动移动、鼠标点位置放大缩小_图片查看
QtQGraphicsView实现图片放缩、鼠标拖动移动、鼠标点位置放大缩小 头文件: #ifndef TIMGWIDGET_H #define TIMGWIDGET_H#include <QGraphicsItem> #include <QMainWindow> #include <QObject> #include <QWidget>// class TImgWidget : pu…...
Percona Toolkit 神器全攻略(复制类)
Percona Toolkit 神器全攻略(复制类) Percona Toolkit 神器全攻略系列共八篇,前文回顾: 前文回顾Percona Toolkit 神器全攻略Percona Toolkit 神器全攻略(实用类)Percona Toolkit 神器全攻略(配…...
SQLite3 数据类型深入全面讲解
SQLite3,作为一款轻量级的数据库管理系统,在数据存储方面展现出了其独特的魅力。它不仅支持标准的SQL语法,还提供了丰富的数据类型供开发者选择。这些数据类型不仅涵盖了基本的数值和文本类型,还包括了日期时间、二进制数据等复杂…...
Python高效实现Trie(前缀树)及其插入和查找操作
Python高效实现Trie(前缀树)及其插入和查找操作 在Python面试中,考官通常会关注候选人的编程能力、问题解决能力以及对Python语言特性的理解。Trie(前缀树)是一种高效的数据结构,广泛应用于字符串处理、自动补全、拼写检查等场景。本文将详细介绍如何实现一个Trie,并提…...
傅里叶变换家族
禹晶、肖创柏、廖庆敏《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》 禹晶、肖创柏、廖庆敏《数字图像处理》资源二维码...
深度学习——强化学习算法介绍
强化学习算法介绍 强化学习讨论的问题是一个智能体(agent) 怎么在一个复杂不确定的环境(environment)里面去极大化它能获得的奖励。 强化学习和监督学习 强化学习有这个试错探索(trial-and-error exploration),它需要通过探索环境来获取对环境的理解。强化学习 ag…...
轴承知识大全,详细介绍(附3D图纸免费下载)
轴承一般由内圈、外圈、滚动体和保持架组成。对于密封轴承,再加上润滑剂和密封圈(或防尘盖)。这就是轴承的全部组成。 根据轴承使用的工作状况来选用不同类型的轴承,才能更好的发挥轴承的功能,并延长轴承的使用寿命。我…...
【PyTorch】基础环境如何打开
前期安装可以基于这个视频,本文是为了给自己存档如何打开pycharm和jupyter notebookPyTorch深度学习快速入门教程(绝对通俗易懂!)【小土堆】_哔哩哔哩_bilibili Pycharm 配置 新建项目的时候选择解释器pytorch-gpu即可。 Jupyte…...
QT教程:QTime和QTimer的使用场景
QTime类 QTime 是一个用来表示和操作时间的类,它处理一天中的具体时间(例如小时、分钟、秒、毫秒)。通常用于计算时间间隔、记录时间戳、获取当前时间等。 特点和功能 表示时间:QTime 用来表示一天中的某个具体时间(小…...
MySQL 迁移中 explicit_defaults_for_timestamp 参数影响
前言 最近在做数据迁移的时候,使用的是云平台自带的同步工具,在预检查阶段,当时报错 explicit_defaults_for_timestamp 参数在目标端为 off 建议修改 on,有什么风险呢?在此记录下。 测试对比 MySQL 默认情况下 expl…...
树状数组记录
树状数组(Fenwick Tree)是一种用于维护数组前缀和的数据结构,支持高效的单点更新和区间查询操作。它的查询和更新时间复杂度为 O ( log n ) O(\log n) O(logn),适用于需要频繁更新和查询的场景。 树状数组的基本操作 单点更…...
客户端时间和服务器时间的区别
客户端时间: 服务器向客户端拷贝一份前端内容,客户端通过JS获取时间,这样获取的是客户端时间 服务器时间: 服务器通过java代码获取的时间传输给客户端,这样获取的是服务器时间 当有些时候需要使用客户端时间…...
已入职华为!!关于我成功拿下华为大模型算法岗经验总结
方向:大模型算法工程师 整个面试持续了1小时10分钟,能够看出面试官是典型搞技术的,问的很专业又很细,全程感觉压力好大,面完后感觉丝丝凉意,不过幸好还是成功拿下了Offer 一面: 自我介绍 简历项目深度交流 1.项目的背…...
从安卓开发到AI产品经理——我的AI绘画之旅
大家好,我是一名有着多年安卓开发经验的程序员。在日复一日的编码生活中,我对AI行业产生了浓厚的兴趣。于是,我决定转行成为一名AI产品经理。在这个过程中,我通过学习AI绘画工具初步了解了AI行业,下面我将分享我的学习…...
代码随想录八股训练营第三十四天| C++
前言 一、vector和list的区别? 1.1.存储方式: 1.2.随机访问: 1.3.插入和删除操作: 1.4.内存使用: 1.5.容量和大小: 1.6.迭代器类型: 1.7.用途: 二、vector 底层原理和扩容过…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
什么是VR全景技术
VR全景技术,全称为虚拟现实全景技术,是通过计算机图像模拟生成三维空间中的虚拟世界,使用户能够在该虚拟世界中进行全方位、无死角的观察和交互的技术。VR全景技术模拟人在真实空间中的视觉体验,结合图文、3D、音视频等多媒体元素…...
