当前位置: 首页 > 编程日记 > 正文

函数式编程概述

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

引子

JDK8中引入了lambda函数式编程的概念,那么什么是函数式编程,函数式编程又有什么好处,今天我们就来说说函数式编程

我们先了解一下函数式编程的由来 一个名叫阿隆佐·邱奇的数学家设计了一个名为lambda演算的系统。这个系统实质上是为其中一个超级机器设计的编程语言。在这种语言里面,函数的参数是函数,返回值也是函数。这种函数用希腊字母lambda(λ)表示,这种系统因此得名

? 演算之所以这么重要,用 Benjamin C. Pierce 的话说在于它具有某种 “二象性”:它既可以被看作 一种简单的程序设计语言,用于描述计算过程,也可以被看作一个数学对象,用于推导证明一些命题

除此之外,艾伦·图灵也在进行类似的研究。他设计了一种完全不同的系统(后来被称为图灵机),并用这种系统得出了和阿隆佐相似的答案。到了后来人们证明了图灵机和lambda演算的能力是一样的。

函数式编程是阿隆佐思想的在现实世界中的实现。不过不是全部的lambda演算思想都可以运用到实际中,因lambda演算在设计的时候就不是为了在各种现实世界中的限制下工作的。所以,就像面向对象的编程思想一样,函数式编程只是一系列想法,而不是一套严苛的规定。有很多支持函数式编程的程序语言,它们之间的具体设计都不完全一样。

特点

下面我们来看看纯粹的函数式编程都有哪些特点

函数是"第一等公民"

所谓第一等公民,指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。

声明式与命令式编程

函数式的特点就是用声明来表示程序, 用声明的组合来表达程序的组合. 而非函数式编程则习惯于用命令来表示程序, 用命令的顺序执行来表达程序的组合

声明式编程与命令式编程最大的不同其实在于: 声明式编程关心数据的映射,命令式编程关心解决问题的步骤

  • 命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。
  • 声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。

所以声明式比命令式更容易理解

事例

SQL 最大的特点就是只声明我想要什么(What) , 就是不说怎么做(How)。 这个怎么做的部分是由数据库管理系统来完成的, 当然具体的细节自然需要用命令式的编程风格来干活了:

像下面的语句

SELECT * from dogs
INNER JOIN ownersWHERE dogs.owner_id = owners.id

如果我们要用命令式编程来实现

var dogsWithOwners = []
var dog, ownerfor(var di=0; di < dogs.length; di++) {dog = dogs[di]for(var oi=0; oi < owners.length; oi++) {owner = owners[oi]if (owner && dog.owner_id == owner.id) {dogsWithOwners.push({dog: dog,owner: owner})}}}
}

在很多情况中,命令式编程很好用。当我们写业务逻辑,我们通常必须要写命令式代码,没有可能在我们的专项业务里也存在一个可以归纳抽离的实现。   但是,如果我们花时间去学习(或发现)声明式的可以归纳抽离的部分,它们能为我们的编程带来巨大的便捷。首先,我可以少写代码,这就是通往成功的捷径。而且它们能让我们站在更高的层面是思考,站在云端思考我们想要的是什么,而不是站在泥里思考事情该如何去做。

一般来说计算更容易归纳和抽离,所以函数式编程在实践使用中更适合数据计算聚合等操作,比如Java8的Stream.

不可变

在其他类型的语言中,变量往往用来保存"状态"。 不修改变量,意味着状态不能保存在变量中。函数式编程使用参数保存状态,最好的例子就是递归。下面的代码是一个将字符串逆序排列的函数,它演示了不同的参数如何决定了运算所处的"状态"。

String reverse(String string) {if(string.length() == 0) {return string;} else {return reverse(string.substring(1, string.length())) + string.substring(0, 1);}}

这种方式运行起来会相对慢一些,因为它重复调用自己。同时它也会大量的消耗内存,因为它会不断的分配创建内存对象

无副作用

由不可变这个特点带来的另一个特点就是无副作用 所谓副作用,指当调用函数时,除了返回函数值之外,还对主调用函数产生附加的影响。例如修改全局变量(函数外的变量)或修改参数

例如

如果赋值语句修改了内存原有的值,就会产生副作用。i++,这个就是修改了内存中i的值。 如果赋值语句不在内存原有值的基础上进行修改,而是重新申请一块内存保存运算结果,避免了对内存原有值的修改就是没有副作用

因为的每个符号都是final的,于是没有什么函数会有副作用。谁也不能在运行时修改任何东西,也没有函数可以修改在它的作用域之外修改什么值给其他函数继续使用(在命令式编程中可以用类成员或是全局变量做到)。这意味着决定函数执行结果的唯一因素就是它的返回值,而影响其返回值的唯一因素就是它的参数。

引用透明

引用透明的概念与函数的副作用相关,且受其影响, 引用透明,指的是函数的运行不依赖于外部变量或"状态",只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的。 其他类型的语言,函数的返回值往往与系统状态有关,不同的状态之下,返回值是不一样的。 这就叫"引用不透明",很不利于观察和理解程序的行为。 纯函数式语言没有变量,所以它们都具有引用透明性

只用"表达式",不用"语句"

函数式编程要求,只使用表达式,不使用语句

表达式是简单的、可以计算并产生结果的代码块,因此,方法调用、任何布尔值的使用,或者整数运算,都是表达式; 而语句是能够影响程序的状态,但没有任何结果的代码块。 不返回任何值的方法调用就是语句,因为它会影响程序的状态,而不管方法做了什么;赋值也会更改状态。

程序1:

int a, b, r;
void add_abs() {r = abs(a) + abs(b)print(r);
}

程序2:

int add_abs(int a, int b) {return abs(a) + abs(b);
}

比如程序1就是一个语句,因为它会通过赋值操作影响程序的状态,并且没有返回值

而程序2就是一个表达式,通过函数组合来返回一个值,是常见的函数式写法

高阶函数

对于Java这样的命令式语言来说,如果所有的变量都是必须是final的,那么确实很束手束脚。然而对函数式语言来说,情况就不一样了。函数式语言提供了一种特别的抽象工具,这种工具将帮助使用者编写函数式代码,让他们甚至都没想到要修改变量的值。高阶函数就是这种工具之一

我们把可以接收、创建或返回函数的函数或方法被视为高阶函数

你写了一大堆程序而不考虑什么类结构设计,然后发现有一部分代码重复了几次,于是你就会把这部分代码独立出来作为一个函数以便多次调用。如果你发现这个函数里有一部分逻辑需要在不同的情况下实现不同的行为,那么你可以把这部分逻辑独立出来作为一个高阶函数

例子

假设有一段Java的客户端程序用来接收消息,用各种方式对消息做转换,然后发给一个服务器。

class MessageHandler {void handleMessage(Message msg) {msg.setClientCode("ABCD_123");sendMessage(msg);}}

再进一步假设,整个系统改变了:额外的那个服务器需要用另外一种格式发送消息。应该如何处理这种情况呢?我们可以先检查一下消息要发送到哪里,然后选择相应的格式把这个消息发出去:

class MessageHandler {void handleMessage(Message msg) {if(msg.getDestination().equals("server1") {msg.setClientCode("ABCD_123");} else {msg.setClientCode("123_ABC");}sendMessage(msg);}
}

可是这样的实现是不具备扩展性的。如果将来需要增加更多的服务器,上面函数的大小将呈线性增长。面向对象的编程方法告诉我们,可以把MessageHandler变成一个基类,然后将针对不同格式的消息编写相应的子类。

abstract class MessageHandler {void handleMessage(Message msg) {msg.setClientCode(getClientCode());sendMessage(msg);}abstract String getClientCode();
}class MessageHandlerOne extends MessageHandler {String getClientCode() {return "ABCD_123";}
}class MessageHandlerTwo extends MessageHandler {String getClientCode() {return "123_ABCD";}
}

这样一来就可以为每一个接收消息的服务器生成一个相应的类对象,添加服务器就变得更加容易维护了。可是,这一个简单的改动引出了很多的代码。仅仅是为了支持不同的客户端行为代码,就要定义两种新的类型!

如果Java支持高阶函数那么事件就变的简单代码可能像这样:

class MessageHandler {void handleMessage(Message msg, Function getClientCode) {Message msg1 = msg.setClientCode(getClientCode());sendMessage(msg1);}
}String getClientCodeOne() {return "ABCD_123";
}String getClientCodeTwo() {return "123_ABCD";
}MessageHandler handler = new MessageHandler();
handler.handleMessage(someMsg, getClientCodeOne);

上面的例子中我们没有创建任何新的类型或是多层类的结构。仅仅是把相应的函数作为参数进行传递,就做到了和用面向对象编程一样的事情,而且还有额外的好处:一是不再受限于多层类的结构,这样做可以做运行时传递新的函数,可以在任何时候改变这些函数,而且这些改变不仅更加精准而且触碰的代码更少。这种情况下编译器其实就是在替我们编写面向对象的“粘合”代码

加里化

int pow(int i, int j);int square(int i)
{return pow(i, 2);
}

上面的代码中square函数计算一个整数的平方,这个函数的接口被转换成计算一个整数的任意整数次幂。这种简单的技术就被叫做加里化 加里化常常用于转化一个函数的接口以便于其他代码调用。函数的接口就是它的参数,于是加里化通常用于减少函数参数的数量。 函数式语言生来就支持这一技术,于是没有必要为某个函数手工创建另外一个函数去包装并转换它的接口,这些函数式语言已经为你做好了

square = int pow(int i, 2);

在函数式语言中加里化就这么简单:一种可以快速且简单的实现函数封装的捷径。我们可以更专注于自己的设计,编译器则会为你编写正确的代码.

惰性求值

惰性求值是一种有趣的技术,而当我们使用函数式编程的后这种技术就有了得以实现的可能。

String s1 = somewhatLongOperation1();
String s2 = somewhatLongOperation2();
String s3 = concatenate(s1, s2);

在指令式语言中以上代码执行的顺序是显而易见的。由于每个函数都有可能改动或者依赖于其外部的状态,因此必须顺序执行。先是计算somewhatLongOperation1,然后到somewhatLongOperation2,最后执行concatenate。函数式语言就不一样了。 somewhatLongOperation1和somewhatLongOperation2是可以并发执行的,因为函数式语言保证了一点:没有函数会影响或者依赖于全局状态。可是万一我们不想要这两个函数并发执行呢?这种情况下是不是也还是要顺序执行这些函数?答案是否定的。只有到了执行需要s1、s2作为参数的函数的时候,才真正需要执行这两个函数。于是在concatenate这个函数没有执行之前,都没有需要去执行这两个函数:这些函数的执行可以一直推迟到concatenate()中需要用到s1和s2的时候。假如把concatenate换成另外一个函数,这个函数中有条件判断语句而且实际上只会需要两个参数中的其中一个,那么就完全没有必要执行计算另外一个参数的函数了

无穷数据结构

惰性求值技术允许定义无穷数据结构,这要在严格语言中实现将非常复杂 很明显一个列表是无法在有限的时间内计算出这个无穷的数列并存储在内存中的,但是有了惰性求值就可以在真正使用的时候去计算,这样就提供了一个抽象在更高的层次去解决问题

函数式的优点

那么根据函数式编程的几个特点我们可以看出函数式编程具有下面几个优点

  1. 代码简洁,开发快速,易于理解 函数式编程大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快 并且声明式的函数调用更接近自然语言的代码,更容易理解。

  2. 更利用调试 函数式程序中的错误不依赖于之前运行过的不相关的代码。而在一个命令式程序中,一个bug可能有时能重现而有些时候又不能。因为这些函数的运行依赖于某些外部状态, 而这些外部状态又需要由某些与这个bug完全不相关的代码通过某个特别的执行流程才能修改。在函数式编程中这种情况完全不存在:如果一个函数的返回值出错了,它一直都会出错,无论你之前运行了什么代码。 因此,每一个函数都可以被看做独立单元,很有利于进行单元测试和debug,以及模块化组合

  3. 易于"并发编程" 函数式编程不需要考虑线程安全,因为它不修改变量,所以根本不存在锁线程的问题。 不必担心一个线程的数据,被另一个线程修改,这样就不用关心复杂的同步问题。

面向对象与函数式

面向对象式编程因为引入了类、对象、实例等概念,非常贴合人类对于世间万物的认知方式和思考方式。对于复杂的事物,人类是如何去认识、归纳、总结的?面向对象式编程就是在努力回答这个问题,而答案的核心就是两个字:抽象。所以面向对象式编程特别适合处理业务逻辑,因此被广泛应用于目前的软件开发当中。因为我们开发软件就是为了解决问题,面向对象式编程符合人类对于“问题”的认知方式。

在处理业务逻辑的高度抽象层面,面向对象式编程已经非常符合我们的需要了,但是当进入具体运算的时候,在考虑更低一层的代码实现的时候,我们仍然依赖于数据(比如使用变量存储)和状态(比如全局作用域)的计算处理,这就导致了粒度更细的处理还是复杂

函数式编程最大的特点之一就是摒弃了数据与状态的计算模型,同时也就避免了诸如作用域等细节给我们带来的副作用,函数这个概念并不难理解,我们经常把对于数据和状态的处理封装为函数去隐藏其中的复杂;而函数式编程范式就是把函数提升为基本的编程要素,从语言设计的层面上着眼于函数本身,消除对数据和状态进行处理的过程。

面向对象和函数式哪个更好呐?其实两个都有问题

面向对象的问题在于它对“对象”的定义,它试图将所有事情就纳入到这个概念里。这种做法极端化后,你就得出来一个一切皆为对象思想。但这种思想是错误的,

因为有些东西不是对象,函数就不是对象。 大多数的面向对象语言里都缺乏正确的实现一等函数的机制。Java语言是一个极致,它完全不允许将函数当作数据来传递。你可以将全部的函数都封装进对象,然后称它们为方法,但这是绑架。缺乏一等函数是为什么Java里需要这么多设计模式的主要原因。一旦有了一等函数,你将不再需要大部分的这些设计模式。

相似的,函数式编程走向极端、成为一种纯函数式编程语言后,也是有问题的,纯函数式编程语言也并不合适, 因为副作用是真实存在的,要实现完全的无副作用,就显得相当困难, 其次在工程上想要大规模使用函数式编程仍然有很多待解决的问题,尤其是对于规模比较大的工程而言。如果对函数式编程的理解不够深刻就会导致跟面相对象一样晦涩难懂的局面。

综上所述那么组合使用可能是最好的办法,在拥有复杂的业务模型领域是使用面向对象最合适的地方,用函数式用来编写复杂的计算。

JDK8 中的函数式支持

首先Java 本身是基于对象的并没有函数这样的基本类型

Java 8也没有创建新的类型,而是通过编译器将Lambda表达式自动转换成一个类的实例,这个类由类型推断来决定, lambda的作用跟匿名类没有本质区别。

所以Java 8的Lambda表达式没有神奇地转变成函数式语言,自然对纯函数式语言引用透明,无副作用等特点支持不佳 有的是更好的语法支持Lambda表达式。 值得关注的是Collection类库得到了增强,允许Java开发人员采用更简单的函数式风格来简化代码。

Java 8引入一些新的类来表示函数的基本构造块,如java.util.function中的Predicate、Function和Consumer接口。这些新增的功能使Java 8能够“稍微函数式编程”,但Java为了兼容旧版本实现上也是使用库的方式而不是语言特性,它离纯粹的函数式语言还十分遥远

转载于:https://my.oschina.net/jayqqaa12/blog/1538617

相关文章:

LeetCode实战:字符串相乘

题目英文 Given two non-negative integers num1 and num2 represented as strings, return the product of num1 and num2, also represented as a string. Example 1: Input: num1 "2", num2 "3" Output: "6"Example 2: Input: num1 &q…

UI培训教程之系统图标如何设计?

UI设计在近几年广受大家的关注&#xff0c;学习UI设计的人越来也多&#xff0c;今天小编要介绍的就是其中的系统图标设计方法&#xff0c;系统图标在UI设计中是非常基础的图形化语言&#xff0c;也在页面交互中起到很重要的作用。单个图标的设计并不难&#xff0c;但是系统化、…

看懂SQL Server的查询计划(绝对好文!)

在园子看到一篇SQLServer关于查询计划的好文&#xff0c;激动啊,特转载。原文出自:http://www.cnblogs.com/fish-li/archive/2011/06/06/2073626.html看懂SqlServer查询计划对于SqlServer的优化来说&#xff0c;可能优化查询是很常见的事情。关于数据库的优化&#xff0c;本身也…

LeetCode实战:全排列

题目英文 Given a collection of distinct integers, return all possible permutations. Example: Input: [1,2,3] Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1] ]题目中文 给定一个没有重复数字的序列&#xff0c;返回其所有可能的全排列。 示例: 输入: …

git pull出现There is no tracking information for the current branch

使用git pull 或者 git push 的时候报错 gitThere is no tracking information for the current branch. Please specify which branch you want to merge with. See git-pull(1) for details git pull <remote> <branch> If you wish to set tracking information…

零基础快速学习Java技术的方法整理

在学习java技术这条道路上&#xff0c;有很多都是零基础学员&#xff0c;他们对于java的学习有着很多的不解&#xff0c;不知怎么学习也不知道如何下手&#xff0c;其实Java编程涉及到的知识点还是非常多的&#xff0c;我们需要制定java学习路线图这样才能少走弯路&#xff0c;…

转 深入理解Midlet类

在J2ME编程过程中&#xff0c;MIDlet是最核心的类之一&#xff0c;熟悉该类的使用是J2ME学习过程中必须首先掌握的类&#xff0c;下面就结合实际介绍一下该类的实际使用。 众所周知&#xff0c;J2ME程序都是从MIDlet类开始执行&#xff0c;系统规定了MIDlet的生命周期。规定MID…

LeetCode实战:最大子序和

题目英文 Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum. Example: Input: [-2,1,-3,4,-1,2,1,-5,4], Output: 6 Explanation: [4,-1,2,1] has the largest sum 6.Follow up…

PHP如何更好的利用PHPstorm的自动提示

说明 写了一段时间的java之后&#xff0c;特别不习惯PHP本身的弱类型方式&#xff0c;在写代码的时候总觉得不怎么放心&#xff0c;特别本身PHP又是弱类型的语言&#xff0c;所以在编码的时候&#xff0c;很多时候是没有代码提示的。 一个一般例子 class Data {public $name;pu…

2021年Java面试题目最新总结【90%面试会踩的坑】

学会java技术之后大家面临的最多的问题就是面试这关&#xff0c;求职面试java岗位是否能够成功是直接影响我们的工作机会的&#xff0c;所以对于Java程序员面试你准备好了吗?今天小编汇总了一下关于Java程序员面试&#xff0c;90%会踩到的坑。 2021年Java面试题目最新总结【90…

LeetCode实战:螺旋矩阵

题目英文 Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order. Example 1: Input: [[ 1, 2, 3 ],[ 4, 5, 6 ],[ 7, 8, 9 ] ] Output: [1,2,3,6,9,8,7,4,5]Example 2: Input: [[1, 2, 3, 4],[5, 6, 7, 8],[9,10,11,…

Json的序列化和反序列化

1、引用命名空间: usingSystem.Runtime.Serialization;2、json的序列化和反序列化的方法&#xff1a; publicclassJsonHelper {///<summary>///序列化///</summary>///<typeparam name"T"></typeparam>///<param name"t">&l…

Angular开山篇

1&#xff1a;环境搭建 今天给大家介绍4种环境搭建的方法。一&#xff1a;Angular-cli的安装 官方指导文档&#xff1a;www.angular.cn/guide/quickstart 请使用cnpm来安装&#xff0c;或者配置淘宝镜像。 使用原生npm安装可能会遇到的问题&#xff1a; 需要python的环境可能会…

零基础参加软件测试培训需要学多长时间

软件测试对于零基础学员来说是非常好入门的&#xff0c;软件测试没有很多的限制&#xff0c;那么零基础参加软件测试培训需要学多长时间呢?来看看下面的详细介绍吧。 零基础参加软件测试培训需要学多长时间?软件测试培训时间一般都在四个月左右&#xff0c;四个月时间的课程内…

Windows API函数大全

1. API之网络函数 WNetAddConnection 创建同一个网络资源的永久性连接 WNetAddConnection2 创建同一个网络资源的连接 WNetAddConnection3 创建同一个网络资源的连接 WNetCancelConnection 结束一个网络连接 WNetCancelConnection2 结束一个网络连接 WNetCloseEnum 结束一…

LeetCode实战:螺旋矩阵 II

题目英文 Given a positive integer n, generate a square matrix filled with elements from 1 to n^2 in spiral order. Example: Input: 3 Output: [[ 1, 2, 3 ],[ 8, 9, 4 ],[ 7, 6, 5 ] ]题目中文 给定一个正整数 n&#xff0c;生成一个包含 1 到 n^2 所有元素&#x…

电子文件归档为什么非云不可

本文讲的是电子文件归档为什么非云不可华为云为企业搭建功能强大的电子文件归档系统平台&#xff0c;一站式满足文件存储与管理、协同分享、移动办公等不同的业务需求。打造安全、高效、便捷的文件归档环境&#xff0c;帮助企业节省运营成本&#xff0c;优化管理流程&#xff0…

北京学习Java培训有哪些比较好

北上广算是互联网技术大咖的聚集之地&#xff0c;很多知名互联网企业都在这些城市&#xff0c;随之java培训机构也是非常多的&#xff0c;那么在北京学习java培训有哪些比较好呢?来看看下面的详细介绍吧。 北京学习Java培训有哪些比较好?想要在这些培训机构中选择比较靠谱的J…

C#命名规则、开发习惯和风格

1. 文件命名组织 1-1文件命名 1. 文件名遵从Pascal命名法&#xff0c;无特殊情况&#xff0c;扩展名小写。 2. 使用统一而又通用的文件扩展名&#xff1a; C# 类 .cs 1-2文件注释 1. 在每个文件头必须包含以下注释说明 1 在每个文件头必须包含以下注…

LeetCode实战:不同路径

题目英文 A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below). The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Fini…

前端部分面试题整理,欢迎补充

1.ng中如何配置路由&#xff0c;$scope和$rootscope的原理ng中如何配置路由?1)使用内置路由模块ng-routevar app angular.module(ngRouteExample, [ngRoute]).controller(MainController, function($scope) {}).config(function($routeProvider, $locationProvider) {$routeP…

JS栈结构的简单封装

栈&#xff1a;是一种遵循后进先出(Last In First Out / LIFO) 原则的一种有序集合。 新添加或者要删除的元素都会保存在栈的同一端&#xff0c;我们把它叫做栈顶&#xff0c;另外一端叫做栈底。 在栈中所有的新元素都接近栈顶&#xff0c;而所有的旧元素都接近栈底。 在我们的…

记录CSS3 target伪类简介

CSS3 target伪类是众多实用的CSS3特性中的一个。它用来匹配文档(页面)的URI中某个标志符的目标元素。具体来说&#xff0c;URI中的标志符通常会包含一个”#”字符&#xff0c;然后后面带有一个标志符名称&#xff0c;比如#respond&#xff0c;target就是用来匹配ID为respond的元…

LeetCode实战:合并两个有序数组

题目英文 Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array. Note: The number of elements initialized in nums1 and nums2 are m and n respectively.You may assume that nums1 has enough space (size that is greater o…

Java云托管服务的开支削减策略

\摘要\随着项目不断扩大&#xff0c;你需要将其迁移到更大的虚拟机上。但如果新虚拟机环境超出了你的需求则会产生额外开支。\相比虚拟机&#xff0c;容器具有更小的粒度&#xff0c;并且无需重启运行中的实例即可垂直扩展。\单体应用和历史遗留应用无需更改配置&#xff0c;即…

SpringBoot培训教程--史前文明之Spring简介

一. Spring之起源 1.你知道J2EE吗? 要说到Spring的历史起源&#xff0c;首先咱们要说说J2EE这个玩意儿。 J2EE在1999年和2000年的时候开始得到广泛实现&#xff0c;在J2EE中提出了”事务管理“等核心中间层标准化的概念&#xff0c;但是在实践中出现了各种问题&#xff0c;尤其…

利用外部命令Oralce数据库导入导出

1--数据库导出(exp) 首先进入命令行 导出数据库 在命令行中输入如下命令: exp c2j/c2jc2j filec:/table.dmp tablesjbitaku,jbitakum grantsy 然后按回车键 说明: c2j/c2jc2j 分别表示用户名&#xff0c;密码和服务名 file&#xff1a;输出文件的位置和文…

LeetCode实战:子集

题目英文 Given a set of distinct integers, nums, return all possible subsets (the power set). Note: The solution set must not contain duplicate subsets. Example: Input: nums [1,2,3] Output: [[3],[1],[2],[1,2,3],[1,3],[2,3],[1,2],[] ]题目中文 给定一组…

linux的挂载命令

在linux中所有的存储设备都必须挂载后才能使用&#xff0c;相当于windows的分配盘符 挂载命令 mount #查看系统中已经挂载好的设备 mount -a #根据/etc/fstab中的内容&#xff0c;自动挂载 /etc/fstab是系统开机的自动挂载文件 系统挂载时要自动检车测这个文件&#xff0c;如果…

软件测试需要学习什么技术

软件测试在近几年被很多企业都重视起来&#xff0c;互联网时代&#xff0c;APP种类越来越多&#xff0c;软件测试这一行业的发展前景是非常大的&#xff0c;那么想要学习软件测试需要学习什么技术呢?来看看下面的详细介绍。 软件测试需要学习什么技术? 每个软件在上线之前都离…