Java 函数式编程 详细介绍

article/2025/11/5 21:02:21

在兼顾面向对象特性的基础上,Java语言通过Lambda表达式与方法引用等,为开发者打开了函数式编程的大门。 下面我们做一个初探。

Lambda的延迟执行

有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以 作为解决方案,提升性能。

性能浪费的日志案例

 
注:日志可以帮助我们快速的定位问题,记录程序运行过程中的情况,以便项目的监控和优化。

一种典型的场景就是对参数进行有条件使用,例如对日志消息进行拼接后,在满足条件的情况下进行打印输出:

 
public class Demo01Logger {
public static void main(String[] args) {
String msgA = "Hello ";
String msgB = "World ";
String msgC = "Java";
log(1, msgA + msgB + msgC);
}
private static void log(int level, String mgs) {
if (level == 1) {
System.out.println(mgs);
}
}
}

这段代码存在问题:无论级别 level 是否满足要求,作为 log 方法的第二个参数,三个字符串一定会首先被拼接并传入方法内,然后才会进行级别判断。如果级别不符合要求,那么字符串的拼接操作就白做了,存在性能浪费。

 
备注:SLF4J是应用非常广泛的日志框架,它在记录日志时为了解决这种性能浪费的问题,并不推荐首先进行 字符串的拼接,而是将字符串的若干部分作
为可变参数传入方法中,仅在日志级别满足要求的情况下才会进 行字符串拼接。例如: LOGGER.debug("变量{}的取值为{}。", "os", "macOS") ,
其中的大括号 {} 为占位 符。如果满足日志级别要求,则会将“os”和“macOS”两个字符串依次拼接到大括号的位置;否则不会进行字 符串拼接。这也是
一种可行解决方案,但Lambda可以做到更好。

体验Lambda的更优写法

使用Lambda必然需要一个函数式接口:

 
@FunctionalInterface
public interface MessageBuilder {
/**
* 信息生成器
* @return 生成的信息
*/
public abstract String builderMessage();
}

然后对 log 方法进行改造:

 
public class Demo02Logger {
public static void main(String[] args) {
String msgA = "Hello ";
String msgB = "World ";
String msgC = "Java";
log(1, () -> msgA + msgB + msgC);
}
private static void log(int level, MessageBuilder mb) {
if (level == 1) {
System.out.println(mb.builderMessage());
}
}
}

改造前后的对比:

这样一来,只有当级别满足要求的时候,才会进行三个字符串的拼接;否则三个字符串将不会进行拼接。

证明Lambda的延迟

下面的代码可以通过结果进行验证:

 
public class Demo03Logger {
public static void main(String[] args) {
String msgA = "Hello ";
String msgB = "World ";
String msgC = "Java";
log(2, () -> {
System.out.println("Lambada 执行!");
return msgA + msgB + msgC;
});
}
private static void log(int level, MessageBuilder mb) {
if (level == 1) {
System.out.println(mb.builderMessage());
}
}
}

这里只是在调用 log 方法的时候,将传入的Lambda稍作修改,

当传入的 level = 1 的时候,控制台输出:

 
Lambada 执行!
Hello World Java

当传入的 level != 1 的时候,控制台没有输出。

从结果中可以看出,在不符合级别要求的情况下,Lambda将不会执行。从而达到节省性能的效果。

 
扩展:实际上使用内部类也可以达到同样的效果,只是将代码操作延迟到了另外一个对象当中通过调用方法来完成。而是否调
用其所在方法是在条件判断之后才执行的。
public class Demo04Logger {
public static void main(String[] args) {
String msgA = "Hello ";
String msgB = "World ";
String msgC = "Java";
log(1, new MessageBuilder() {
@Override
public String builderMessage() {
System.out.println("Lambada 执行!");
return msgA + msgB + msgC;
}
});
}
private static void log(int level, MessageBuilder mb) {
if (level == 1) {
System.out.println(mb.builderMessage());
}
}
}

使用Lambda作为参数和返回值

如果抛开实现原理不说,Java中的Lambda表达式可以被当作是匿名内部类的替代品。如果方法的参数是一个函数式接口类型,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式 接口作为方法参数。

Lambda作为参数

例如 java.lang.Runnable 接口就是一个函数式接口,假设有一个 startThread 方法使用该接口作为参数,那么就可以使用Lambda进行传参。这种情况其实和 Thread 类的构造方法参数为 Runnable 没有本质区别。

匿名内部类作为参数,创建新的线程并执行:

 
public class Demo01Runnable {
public static void main(String[] args) {
startThread(new Runnable() {
@Override
public void run() {
System.out.println("线程任务执行!");
}
});
}
/**
* 创建一个新的线程,赋予任务,然后开启线程
* @param runnable 传入Thread类的接口,实现创建新线程
*/
private static void startThread(Runnable runnable) {
new Thread(runnable).start();
}
}

运行程序,控制台输出:

 
线程任务执行!

Lambda作为参数,创建新的线程并执行:

 
public class Demo02Runnable {
public static void main(String[] args) {
startThread(
() -> System.out.println("线程任务执行!")
);
}
/**
* 创建一个新的线程,赋予任务,然后开启线程
* @param runnable 传入Thread类的接口,实现创建新线程
*/
private static void startThread(Runnable runnable) {
new Thread(runnable).start();
}
}

运行程序,控制台输出:

 
线程任务执行!

Lambda作为返回值

类似地,如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。当需要通过一个方法来获取一个 java.util.Comparator 接口类型的对象作为排序器时,就可以调该方法获取。

Lambda作为返回值,字符串的长短比较:

 
import java.util.Arrays;
import java.util.Comparator;
public class DemoComparator {
public static void main(String[] args) {
String[] array = { "abc", "ab", "a" };
System.out.println("使用比较器比较之前:" + Arrays.toString(array));
Arrays.sort(array, newComparator());
System.out.println("使用比较器比较之后:" + Arrays.toString(array));
}
/**
* 字符串a、b的长短比较,自己定义比较器规则,生序排序,字符串长的排在后面。
* @return 布尔值,
* a.length() - b.length() < 0 返回 false,
* a.length() - b.length() > 0 返回 true,
* a.length() = b.length() 返回 0
*/
public static Comparator<String> newComparator() {
return (a, b) -> a.length() - b.length();
}
}

匿名内部类作为返回值,字符串的长短比较:

 
import java.util.Arrays;
import java.util.Comparator;
public class DemoComparator {
public static void main(String[] args) {
String[] array = { "abc", "ab", "a" };
System.out.println("使用比较器比较之前:" + Arrays.toString(array));
Arrays.sort(array, newComparator());
System.out.println("使用比较器比较之后:" + Arrays.toString(array));
}
public static Comparator<String> newComparator1() {
return new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
};
}
}

运行程序,控制台输出一样:

 
使用比较器比较之前:[abc, ab, a]
使用比较器比较之后:[a, ab, abc]

 


http://chatgpt.dhexx.cn/article/prdlehki.shtml

相关文章

Java基础函数式编程

本篇博文目录: 前言1.什么是函数式接口2.函数式编程(1) 使用Lambda表达式(2) Lambda表达式的进一步简化(3) Java内置函数式接口 3.方法引用(1) 方法引用的简单使用(2) 方法引用的分类 4.Stream API(1) 什么是Stream(2) 流式操作的执行流程(3) Stream的创建(4) Stream中间操作(5…

Java8新特性【函数式编程API、新时间日期处理API、Optional容器类】总结

文章目录 1、Lambda表达式1.1什么是Lambda表达式1.2从匿名类到 Lambda 的转换1.3Lambda表达式语法 2、函数式接口2.1什么是函数式接口2.2自定义函数式接口2.3内置核心函数式接口2.4接口中常用的默认方法 3、方法引用与构造器引用3.1 推荐用法3.2 基本格式3.3 语法详解(了解)3.3…

一文带你入门 Java 函数式编程

Java 在最开始是不支持函数式编程的&#xff0c;想来也好理解&#xff0c;因为在 Java 中类 Class 才是第一等公民&#xff0c;这就导致在 Java 中实现编程不是件那么容易的事儿&#xff0c;不过虽然难&#xff0c;但是结果我们也已经知道了&#xff0c;在 Java 8 这个大版本里…

Oracle数据库 存储过程入门

oracle存储过程:简单入门 一、定义 存储过程是一组为了完成特定功能的SQL语句&#xff0c;经编译后存储在数据库中。点击查看优缺点。二、存储过程简单入门 ***第一个存储过程&#xff1a;打印hello word, my name is stored procedure内容*** create or replace procedure m…

数据库储存过程超简单实例

网上看了半天都没找到一个完整储存过程从创建到调用的实例,于是自己写了一个简单的实例. 数据库创建存储过程,定义个函数 格式如下,开头DELIMITER //和结尾/DELIMITER 和BEGIN 和 END 是固定格式 定了一个叫test2()的方法(在mapper.xml中会指定这个函数名),in表示入参,varc…

DM8达梦数据库存储过程函数使用

DM8数据库的过程函数的编写主要分为4个部分&#xff1a;过程头部分&#xff0c;声明定义部分&#xff0c;执行部分和异常处理部分。在编写方面&#xff0c;过程和函数的主要区别还是函数可以返回一个值&#xff0c;但是过程没有。下面就从这4个部分来分别介绍过程的编写和一些常…

数据库:存储过程实验

一、实验目的及要求 目的 掌握存储过程的编写与调用 要求 掌握存储过程的编写&#xff1b;掌握存储过程的调用 二、实验条件 安装有SQL Server2014数据库的计算机 三、实验内容 使用事务、锁和游标&#xff1b;编写和使用存储过程&#xff1b;使用触发器 四、实验结果…

达梦数据库存储过程注意事项

引言&#xff1a;达梦数据库是一款国产数据库&#xff0c;在语法使用和函数方面和MySQL&#xff0c;Oracle有着很多相似的地方。但是也有一 些细微的区别。 1、先看一下达梦数据库的存储过程模板&#xff1a; CREATE OR REPLACE FUNCTION getName() AS OR IS DECLARE ... BEGI…

MySQL数据库-存储过程详解

存储过程简单来说&#xff0c;就是为以后的使用而保存的一条或多条MySQL语句的集合。可将其视为批件&#xff0c;虽然它们的作用不仅限于批处理。在我看来&#xff0c; 存储过程就是有业务逻辑和流程的集合&#xff0c; 可以在存储过程中创建表&#xff0c;更新数据&#xff0c…

EXTJS5 入门指南

EXTJS5带领EXTJS步入了新的时代&#xff0c;Ext JS 5已经不再支持IE6、IE7和其他旧版本的浏览器了&#xff0c;这样可以显著减少跨整个框架的逻辑和样式设置。再加上额外的优化&#xff0c;Ext JS 5已经为企业级的Web应用程序迈出了惊人的一步。 EXTJS5不在和EXTJS4一样&#x…

ExtJS-入门(转载自http://www.blogjava.net/puras/archive)

2010 - 01 - 13 缩略显示 ExtJS-入门&#xff08;转载自http://www.blogjava.net/puras/archive&#xff09; 文章分类:Web前端 在ExtJS里最常用的,应该就是Ext.onReady这个方法了, 而且它也可能是你学习ExtJS所接触的第一个方法,这个方法在当前的DOM加载完毕后自动调用,保证…

Ext JS 6学习文档–第1章–ExtJS入门指南

Ext JS 入门指南 前言 本来我是打算自己写一个系列的 ExtJS 6 学习笔记的&#xff0c;因为 ExtJS 6 目前的中文学习资料还很少。google 搜索资料时找到了一本国外牛人写的关于 ExtJS 6 的电子书 [Ext JS 6 By Example]。这份资料在 PACKT 上卖 35.99 刀的&#xff0c;当然了万…

Extjs——初步学习

最近在系统学习Extjs框架&#xff0c;从刚一开始接触Extjs到现在发现对Extjs越来越喜欢了。刚开始只是想在页面上实现一个展示大量图片的功能&#xff0c;就像在线订餐系统展示菜单的效果那样&#xff0c;每幅图片上都有一些必要的信息、动作、链接等。效果如下图&#xff1a; …

Extjs基础(一)

1.1基础学习 说明&#xff1a; 本示例的所有代码均在extjs6.2版本上测试通过,学习内容来源于官方文档和自己的一些见解。 1.1.1window组件 简单的一个window面板&#xff1a; title: 窗口标题,height: 220, //可以使用百分比width: 220, html: 内容部分,resizable: true, //…

ExtJS基础入门

公司需要用ExtJS搭建系统框架&#xff0c;然后&#xff0c;这个很老了&#xff0c;没有用过 。 开始进行时候一脸懵逼&#xff0c;因为搜索了相关的知识&#xff0c;面临如下问题&#xff1a; 1.版本太多&#xff0c;从一到六&#xff0c;不知从何入手 2.提供的教程和视频都…

extjs初学者教程

layout 1.面板 (1)类结构 Ext.Base Ext.AbstractComponent Ext.Component Ext.container.AbstractContainer Ext.container.Container Ext.panel.AbstractPanel …

ext.js入门

序言&#xff1a;extjs 是一种OOP语言&#xff0c;可以按照学习Java 的过程来进行学习&#xff0c;可以类比 Java中的图像界面JWT来进行学习。 工具 这些是sencha提供的用于Ext JS应用程序开发的工具&#xff0c;主要用于生产级别。Sencha Cmd Sencha CMD是一个提供Ext JS代码…

EXTJS入门教程及其框架搭建

EXTJS是一个兼容AJAX的前台WEB UI的框架&#xff0c;在普通的HTML文件的 BODY 元素中无须写任何HTML代码&#xff0c;就能产生相应的表格等元素。 原创不易&#xff0c;转载请注明出处&#xff1a;EXTJS入门教程及其框架搭建 代码下载地址:http://www.zuidaima.com/share/17244…

EXTJS详细教程

布局和容器 普通布局 Ext.create(Ext.panel.Panel, {renderTo: Ext.getBody(),width: 400,height: 300,title: Container Panel,items: [{xtype: panel,title: Child Panel 1,height: 100,width: 75%}, {xtype: panel,title: Child Panel 2,height: 100,width: 75%}] });列布…

国嵌视频,买了就是坑

国嵌买视频的&#xff0c;更新慢&#xff0c;谁敢抱怨&#xff0c;踢你&#xff0c;锁你账号&#xff0c;真无语&#xff0c;安卓没录完&#xff0c;3月拖到6月&#xff0c;拖到10月&#xff0c;引起公愤了&#xff0c;就说送路由视频&#xff0c;结果路由视频还没录呢&#xf…