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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/417538.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

打造温馨家居,全屋智能家居解决方案

智能家居全屋解决方案覆盖全屋照明、温度、娱乐影音等各种常见的日常生活需求、可通过一键设置联动场景来控制自己的家、也可通过语音对话来操控家中的照明、电器及各种场景模式任意切换&#xff0c;一键升级自己的智能家。 1.入户解决方案 通过智能指纹锁穿过玄关、进入大厅、…

Android Studio 加载多个FLutter项目

按顺序操作即可 选择工程 选择Modules, 导入 module 选中创建module 选择要导入的目录&#xff0c;只选择主目录&#xff0c;下面的文件不要选 添加完成&#xff0c;点击ok后&#xff0c;会进行导入 最终导入成功

Java数据结构之ArrayList(如果想知道Java中有关ArrayList的知识点,那么只看这一篇就足够了!)

前言&#xff1a;ArrayList是Java中最常用的动态数组实现之一&#xff0c;它提供了便捷的操作接口和灵活的扩展能力&#xff0c;使得在处理动态数据集合时非常方便。本文将深入探讨Java中ArrayList的实现原理、常用操作以及一些使用场景。 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨…

专题一_双指针_算法专题详细总结

目录 双指针 对撞指针&#xff1a;⼀般⽤于顺序结构中&#xff0c;也称左右指针。 快慢指针&#xff1a;⼜称为⻳兔赛跑算法&#xff0c;其基本思想就是使⽤两个移动速度不同的指针在数组或链表等序列 结构上移动。 1. 移动零&#xff08;easy&#xff09; 「数组分两块」…

中间件解析漏洞

一、iis 1、IIS6.X 1.在iis的⽹站根⽬录新建⼀个名为x.asp的⽂件 2.在x.asp中新建⼀个jpg⽂件。内容为 asp代码 3.在外部浏览器中访问windows2003的iis⽹站中的2.jpg 发现asp代码被执⾏ 4.将2.jpg⽂件放到⽹站根⽬录下访问&#xff0c;发现其中的asp代码没有被解析。 由此可…

1.ASRPRO天问--开发板介绍及第一次使用--开发板挖掘系列

1. 前言 时光不问赶路人&#xff0c;一切尽在不言中&#xff0c;大家好&#xff0c;我是繁花&#xff0c;oh&#xff0c;不对&#xff0c;是繁华的地方不一定留下你的脚印。开学季的到来&#xff0c;也让我这个老人是思绪万千&#xff0c;不要问为啥是老人&#xff0c;因为中人…

linux如何查看内存条是ddr几代

在 Linux 系统中&#xff0c;可以通过以下几种方法查看内存条的类型和代数&#xff08;如 DDR3、DDR4 等&#xff09;&#xff1a; 1. 使用 dmidecode 命令 dmidecode 是一个工具&#xff0c;它可以从系统的 DMI 表&#xff08;也称为 SMBIOS 表&#xff09;中提取硬件信息&a…

【Python】搭配 Python 环境(超详细教程)

要想能够进行 Python 开发&#xff0c;就需要搭建好 Python 的环境。 需要安装的环境主要是两个部分&#xff1a; 运行环境&#xff1a;Python 开发环境&#xff1a;PyCharm 一、安装 Python 1、找到官方网站 Welcome to Python.org 在搜索引擎中搜索 python 关键字&…

9-6对java的认识

java的特性和优势 java的三大版本 JDK,JRE,JVM

sealos快速搭建k8s集群

一&#xff0c;环境准备 1&#xff0c;三台&#xff08;搭建一主两从集群&#xff09;或五台&#xff08;三主两从集群&#xff09;虚拟机&#xff0c; 安装alimaLinux系统 &#xff0c;相同的root密码&#xff0c;不要安装docker。 如果是alimaLinux-mini版本操作系统&#xf…

Web安全:SQL注入实战测试.(扫描 + 测试)

Web安全&#xff1a;SQL注入实战测试. SQL注入就是 有些恶意用户在提交查询请求的过程中 将SQL语句插入到请求内容中&#xff0c;同时程序的本身对用户输入的内容过于相信&#xff0c;没有对用户插入的SQL语句进行任何的过滤&#xff0c;从而直接被SQL语句直接被服务端执行&am…

【Godot4自学手册】第四十六节实现闪电攻击、闪电链

本节内容&#xff0c;主要学习闪电攻击魔法。闪电攻击又名为闪电链&#xff0c;就是在几个敌人之间产生不断跳动的闪电攻击效果。本节主要实现的是单击鼠标右键&#xff0c;会在敌人之间产生随机的闪电链条&#xff0c;效果如下&#xff1a; 一、基本闪电的建立 新建2D场景&…

MIT线性代数

本文链接的原创作者为 浊酒南街https://blog.csdn.net/weixin_43597208 第1讲 MIT_线性代数笔记&#xff1a;第 01 讲 行图像和列图像-CSDN博客 第2讲 MIT_线性代数笔记&#xff1a;第 02 讲 矩阵消元_矩阵first pivot-CSDN博客 第3讲 MIT_线性代数笔记&#xff1a;第 03…

C++ 继承(二)

目录 1. 实现一个不能被继承的类 2. 友元与继承 3.继承与静态成员 4.多继承及其菱形继承问题 (1). 继承模型 (2). 虚继承 (2.1)虚继承解决数据冗余和二义性的原理 (3). 多继承中指针偏移问题 (4). IO库中的菱形虚拟继承 5. 继承和组合 1. 实现一个不能被继承的类 方法1…

安卓玩机工具-----通用安卓玩机工具 “搞机助手”界面预览 推荐

在网络中有很多很好玩的工具。方便安卓机型联机使用各种功能。系列博文将详细的演示有些工具的特点与使用方法 搞机助手 作者&#xff1a;流水断崖 目前开发功能有&#xff1a;Twrp recovery全自动刷机&#xff0c;免Root冻结、卸载预装软件&#xff0c;免Root激活&#xff…

谈一谈MVCC

一 MVCC的定义 MVCC&#xff08;Multi-Version Concurrency Control&#xff0c;多版本并发控制&#xff09;是一种用于数据库管理系统&#xff08;DBMS&#xff09;中的并发控制方法&#xff0c;它允许数据库读写操作不加锁地并发执行&#xff0c;从而提高了数据库系统的并发性…

什么是ARM架构?什么是X86架构?两者的区别是什么?

一、什么是ARM架构 &#xff08;一&#xff09;起源于发展 ARM 架构由英国剑桥的 Acorn 计算机公司开发。因市场无合适产品&#xff0c;Acorn 自行设计出第一款微处理器&#xff0c;命名为 ARM。此后 ARM 架构不断发展&#xff0c;1990 年为与苹果合作成立 ARM 公司&#xff0…

鸿蒙轻内核M核源码分析系列三 数据结构-任务排序链表

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 在鸿蒙轻内核源码分析系列一和系列二&#xff0c;我们分析了双向循环链表、优先级就绪队列的源码。本文会继续给读者介绍鸿蒙轻内核源码中重要的数据结构…

运动耳机怎么选购?解密最值得购买的五大品牌!

​现在大家都越来越注重健康&#xff0c;运动成了很多人的日常&#xff0c;不管是轻松跑跑步还是来点高强度训练&#xff0c;都能让人身心都受益。运动时候如果能听点音乐&#xff0c;那感觉就更好了&#xff0c;能让运动效果更上一层楼。但是&#xff0c;那种塞进耳朵里的耳机…

Cmake之2.4版本重要特性及用法实例(十)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…