JAVA8给我带了什么——lambda表达
这此年来我一直从事.NET的开发。对于JAVA我内心深处还是很向往的。当然这并不是说我不喜欢.NET。只是觉得JAVA也许才是笔者最后的归处。
MK公司是以.NET起家的。而笔者也因为兄弟的原因转行.NET。虽然有时候还是会拿起JAVA相关的知识回味一下。尽可能的不让自己忘记。但是时代的进步却把我狠狠甩到了后面去。
现在笔者终于离开了M公司。我想回去做JAVA,却发现笔者已经跟不上JAVA时候。在笔者转行.NET的时候,JAVA的版本才到 1.6。现在都1.8了。主要的是这个段时间发现很大的变化。所以就想看看JAVA8底能带给我什么。
笔者回来做JAVA就是想知道的第一件事——JAVA8里面有什么。不知道Oracle公司收了Sun公司之后为什么一直没有动作,在加上笔者忙着搞.NET开发。JAVA的事情就失去了信息,对于JAVA7笔者不是没有感知,主要是JAVA8听说变化很大。可以说是一个大版本的变化。所以笔者回来的时候就想知道——JAVA8里面有什么。同时笔者在这里声明这一系列主要是记得笔者自身从JAVA8得到了什么,如果有要学JAVA8同学,本系列只能作参考。
笔者是从事.NET的开发,相对JAVA以前而言。.NET有一些功能真的不错。lambda表达示可以说成为.NET开发人员不可能离开的一部分。以前的JAVA可是没有这个功能的。.NET可以把一个方法当做一个参数和变量来赋值。JAVA在这一块就弱了很多了。所以JAVA很多时候在设计模式上面做很大的体现。
把一个方法函数当前一个参数和变量来用的行为我们称为行为参数化。那么他有什么好处呢?策略模式相信大家可能都听过。不如笔者就以《JAVA实战》这本书的例子为例吧。假设我是一个农户,家里种苹果的。今年大丰收,好多苹果。我把每一个苹果都打标签。并把相关苹果的颜色,重量,大小,品种都记录到数据库中。
为了方便日后的查看,笔者自己想一款软件。在写的过其中,笔者希望有这样子功能——能以颜色来查看相关的苹果。所以笔者设计一个农民类,他有一个功能——根据颜色查看苹果。
Apple类:
- package com.aomi;
- public class Apple {
- private String color;
- private double weight;
- private String typeName;
- private int size;
- public String getColor() {
- return color;
- }
- public void setColor(String color) {
- this.color = color;
- }
- public double getWeight() {
- return weight;
- }
- public void setWeight(double weight) {
- this.weight = weight;
- }
- public String getTypeName() {
- return typeName;
- }
- public void setTypeName(String typeName) {
- this.typeName = typeName;
- }
- public int getSize() {
- return size;
- }
- public void setSize(int size) {
- this.size = size;
- }
- @Override
- public String toString() {
- return "Apple [color=" + color + ", weight=" + weight + ", typeName=" + typeName + ", size=" + size + "]";
- }
- }
Peasant类
- package com.aomi;
- import java.util.ArrayList;
- import java.util.List;
- public class Peasant {
- public List<Apple> GetApplesByColor(List<Apple> sources, String color) {
- List<Apple> suiteApples = new ArrayList<>();
- for (Apple apple : sources) {
- if (apple.getColor().equals(color))
- suiteApples.add(apple);
- }
- return suiteApples;
- }
- }
Main:
- package com.aomi;
- import java.util.ArrayList;
- import java.util.List;
- public class Main {
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- // 查找红色的苹果
- Peasant peasant = new Peasant();
- List<Apple> rApples = peasant.GetApplesByColor(getSources(), "red");
- for (Apple apple : rApples) {
- System.out.println(apple.toString());
- }
- }
- public static List<Apple> getSources() {
- List<Apple> sources = new ArrayList<>();
- Apple apple1 = new Apple();
- apple1.setColor("red");
- apple1.setTypeName("hot");
- apple1.setSize(12);
- apple1.setWeight(34.2);
- Apple apple2 = new Apple();
- apple2.setColor("grayred");
- apple2.setTypeName("hot");
- apple2.setSize(12);
- apple2.setWeight(34.2);
- Apple apple3 = new Apple();
- apple3.setColor("green");
- apple3.setTypeName("hot");
- apple3.setSize(12);
- apple3.setWeight(34.2);
- sources.add(apple1);
- sources.add(apple2);
- sources.add(apple3);
- return sources;
- }
- }
运行结果:
写完之后,感觉得很完美。过一段时间,突然发现好像不行。这个功能不好,我需要大小来查看苹果。于是修改一下,在Peasant类增加一个新的方法。根据大小来查看:
- public List<Apple> GetApplesBySize(List<Apple> sources, int size) {
- List<Apple> suiteApples = new ArrayList<>();
- for (Apple apple : sources) {
- if (apple.getSize() > size)
- suiteApples.add(apple);
- }
- return suiteApples;
- }
好吧。看起来也不错,那么有没有想过后面还有可能会以重量来查看苹果。只能在加一个方法了。那么问题来了。一但功能多。整类会看起来一个点复杂。理解有一点难度。在没有lambda表达的时候,JAVA会用一下有一点策略模式的方式实现。把相关的比较操作变成一个类。如下
Lookup接口类:
- package com.aomi;
- public interface Lookup {
- boolean handle(Apple apple);
- }
ColorRedLookup类:
- package com.aomi;
- public class ColorRedLookup implements Lookup {
- @Override
- public boolean handle(Apple apple) {
- // TODO Auto-generated method stub
- return apple.equals("red");
- }
- }
SizeLookup类:
- package com.aomi;
- public class SizeLookup implements Lookup {
- @Override
- public boolean handle(Apple apple) {
- // TODO Auto-generated method stub
- return apple.getSize() > 120;
- }
- }
Peasant类:
- package com.aomi;
- import java.util.ArrayList;
- import java.util.List;
- public class Peasant {
- public List<Apple> LookupApple(List<Apple> sources, Lookup lookup) {
- List<Apple> suiteApples = new ArrayList<>();
- for (Apple apple : sources) {
- if (lookup.handle(apple))
- suiteApples.add(apple);
- }
- return suiteApples;
- }
- }
Main:
- package com.aomi;
- import java.util.ArrayList;
- import java.util.List;
- public class Main {
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- // 查找红色的苹果
- Peasant peasant = new Peasant();
- List<Apple> rApples = peasant.LookupApple(getSources(), new ColorRedLookup());
- for (Apple apple : rApples) {
- System.out.println(apple.toString());
- }
- }
- public static List<Apple> getSources() {
- List<Apple> sources = new ArrayList<>();
- Apple apple1 = new Apple();
- apple1.setColor("red");
- apple1.setTypeName("hot");
- apple1.setSize(12);
- apple1.setWeight(34.2);
- Apple apple2 = new Apple();
- apple2.setColor("grayred");
- apple2.setTypeName("hot");
- apple2.setSize(12);
- apple2.setWeight(34.2);
- Apple apple3 = new Apple();
- apple3.setColor("green");
- apple3.setTypeName("hot");
- apple3.setSize(12);
- apple3.setWeight(34.2);
- sources.add(apple1);
- sources.add(apple2);
- sources.add(apple3);
- return sources;
- }
- }
上面的这种从某些方面来讲笔者不是很喜欢。虽然这种方式看起来会比较人性化。但是相比笔者还是喜欢前面那一种增加方法的。这是个人的想法。
- List<Apple> rApples = peasant.LookupApple(getSources(), new ColorRedLookup());
看完这段代码之后我们就可以发现一个问题。是不是每一个条件查找我都要建一个类呢?好像不是很好玩了。所以还是试一下我们试一下lambda表达。上面的代码不用修改太多。只要main方法里面就可以了。
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- // 查找红色的苹果
- Peasant peasant = new Peasant();
- List<Apple> rApples = peasant.LookupApple(getSources(), (Apple apple) -> apple.getColor().equals("red"));
- for (Apple apple : rApples) {
- System.out.println(apple.toString());
- }
- rApples = peasant.LookupApple(getSources(), (Apple apple) -> apple.getSize() > 120);
- for (Apple apple : rApples) {
- System.out.println(apple.toString());
- }
- }
是不是非常简单呢。不用在建什么类了。所以lambda表达的好处很明显的。笔者想要什么查找规则只要改变一下规则就行了。如下用大小来查找。
- List<Apple> rApples = peasant.LookupApple(getSources(), (Apple apple) -> apple.getSize() > 12);
lambda表达给人感觉就是一个缩小版本的方法。往后面看的话,这种感觉你们会变的更加。但是在学习ambda表达的时候,有一些细节点还是要注意的。
- 语法点:(parameters)->expression或是(parameters)->{statements;}
- 学习会查看lambda表达的签名。即称函数描述符
从语法点我们可以知道
左边parameters:是表示参数,就好比如方法函数的参数是一样子的。
中间->:是固定的。
右边expression或是{statements;}:是表示只能接受表达式,或是加大括号的语句。称为主体
举一些例子来加强一下吧
- 1.()-> {}//有效
- 2.()->"aomi"//有效
- 3.()->{return "aomi";}//有效
- 4.()->return "aomi "+ 1;//无效,主体是语句,要加上{}
- 5.()->{"aomi";}//跟上面的相反,主体是表达式,去掉{}
说到lambda表达的签名,这边就不得不提到一个概念函数式接口。他的定义是这样子,只要接口里面只有一个抽象方法都是可以算是函数式接口。举一个JAVA是里面的函数式接口
- package java.lang;
- @FunctionalInterface
- public interface Runnable {
- /**
- * When an object implementing interface <code>Runnable</code> is used
- * to create a thread, starting the thread causes the object's
- * <code>run</code> method to be called in that separately executing
- * thread.
- * <p>
- * The general contract of the method <code>run</code> is that it may
- * take any action whatsoever.
- *
- * @see java.lang.Thread#run()
- */
- public abstract void run();
- }
Runnable就是一个函数式接口。他只有一个run抽象方法。上面有一个注解类@FunctionalInterface他就是用于说明当前类是一个函数式接口。不过好像事实上你可以不用加上他。
AomiRunnable类:
- package com.aomi;
- public interface AomiRunnable {
- void run();
- }
Main:
- public class Main {
- public static void main(String[] args) {
- Runnable run = () -> {
- System.out.println("i am runnble");
- };
- AomiRunnable aRun = () -> {
- System.out.println("i am aomirunnble");
- };
- run.run();
- aRun.run();
- }
- }
运行结果:
看到上面的代码不要奇怪。这个正好可以说明lambda表达的神奇之处。我们可以看到笔者定义了俩个函数式接口的变量。一个是JAVA里面自带的,一个是笔者自己写的。俩个都可以正常的运行。可是笔者自已写的好像没有加入@FunctionalInterface。那是不是@FunctionalInterface没有用呢?那还是有的。看下面就知道了。当你写错了就会提示你写的不是函数式接口。
所以还是加上吧。这样子显得也专业一点吗?
有了函数式接口,就必须说一下函数描述符。他事实上就是lambda表达的签名。他是从哪里来的呢?很简单的,看接口的唯一方法就行了。就好例如上面Runnable类,他的方法就是无参数,无返回值。你可写才这样子表示一下:()->{}。为什么要有函数描述符呢?你们可以这样子理解。JAVA里面有很多自己写好的函数式接口。如果你没有函数描述符的话,你又何如明白什么时候用到哪一个呢?如下
- Predicate类:T -> boolean
- Function类:T ->R
看到上面函数描述符的话,你是不是就可以知道他们的用法呢。所以了解函数描述符的话,你就可以很清楚的明白自己要用JAVA里面的哪个函数式接口。同时还可以提高你在写代码过程的速度。目前JAVA里面有哪一些函数式接呢?自己去看吧。在rt.jar里面的
还是让笔者再举个例子吧。笔者希望按苹果的大小来非序。所以我们可一定要用到List类的sort方法了。sort方法里面以Comparator类作为参数。Comparator类是一个函数式接口。抽象方法如下
- int compare(T o1, T o2);
所以函数描述符是(T,T)-> int。知道这些之后就好办了。
- public static void main(String[] args) {
- List<Apple> apples = getSources();
- apples.sort((Apple a1, Apple a2) -> a2.getSize() - a1.getSize());
- for (Apple apple : apples) {
- System.out.println(apple);
- }
- }
运行结果:
看起很方便吧。
lambda表达的确不错。使得用JAVA8开发的同学代码更加的人性化。但是JAVA8还加入另一种功能叫方法引用。看一下例子。
- package com.aomi;
- import java.util.ArrayList;
- import java.util.List;
- public class Main {
- public static void main(String[] args) {
- List<Apple> apples = getSources();
- apples.sort(Main::AppleComparator);
- for (Apple apple : apples) {
- System.out.println(apple);
- }
- }
- public static int AppleComparator(Apple a1, Apple a2) {
- return a2.getSize() - a1.getSize();
- }
- public static List<Apple> getSources() {
- List<Apple> sources = new ArrayList<>();
- Apple apple1 = new Apple();
- apple1.setColor("red");
- apple1.setTypeName("hot");
- apple1.setSize(13);
- apple1.setWeight(34.2);
- Apple apple2 = new Apple();
- apple2.setColor("grayred");
- apple2.setTypeName("hot");
- apple2.setSize(12);
- apple2.setWeight(34.2);
- Apple apple3 = new Apple();
- apple3.setColor("green");
- apple3.setTypeName("hot");
- apple3.setSize(14);
- apple3.setWeight(34.2);
- sources.add(apple1);
- sources.add(apple2);
- sources.add(apple3);
- return sources;
- }
- }
主要修改的地方:
- apples.sort(Main::AppleComparator);
增加的地方:
- public static int AppleComparator(Apple a1, Apple a2) {
- return a2.getSize() - a1.getSize();
- }
在使用方法引用的时候,要注要一点,好像要静态方法才行。如果不的话。会报错的。
让笔者好好说明下吧。方法引用并不是可以随便写的。他是有依据的。总共有三种:
- 静态方法,必须要符合(arg)->ClassName.staticMehtod(arg).的格式。
- 任意类型的实例方法,必须符合(object,rest)->object.instanceMethod(rest)的格式。
- 对象实例的实例方法,必须符合(args)->obj.instanceMethod(args)的格式。
我们可以看到方法引用就是针于单一方法的lambda表达的。 我们都知道Function函数接口的lambda表达的用法。好!假设笔者在Apple类中加入这样子的方法
- public int testApple(Apple a) {
- return a.getSize() - 100;
- }
然后在Main的代码中是这样子写的。
- Function<Apple, Integer> fun = Apple::testApple;
不好意思他会报错。
让我们看一下Function的函数描述符吧。(T)-> R.好像跟testApple方法是一样子的话,那为什么不行呢? 让我们把Function<Apple,Integer>变成为他等同的一个lambda表达的写吧。
- (Apple a) -> 123//123可以是任意的数字。
跟笔者上面说的三点都不符合,当然不行了。所以想要可行的话,必须把testApple方法变成静态的。这样子就合适第一种了。关于比较Comparator类,JAVA8提供了一个comparing静态方法。他接受了一个Function参数。并返回一个Comparator类对象。修改一下。
- apples.sort(comparing((Apple a) -> a.getSize()));
记得一个要引入
- import static java.util.Comparator.comparing;
又因为方法引用的关系
- (Apple a) -> a.getSize() 等于 Apple::getSize()
我们就可以把他修改为
- apples.sort(comparing(Apple::getSize));
笔者用一个以前的例子吧。排序苹果Main的类全部代码
- package com.aomi;
- import java.util.ArrayList;
- import java.util.List;
- import static java.util.Comparator.comparing;
- public class Main {
- public static void main(String[] args) {
- List<Apple> apples = getSources();
- apples.sort(comparing(Apple::getSize));
- for (Apple apple : apples) {
- System.out.println(apple);
- }
- }
- public static int AppleComparator(Apple a1, Apple a2) {
- return a2.getSize() - a1.getSize();
- }
- public static List<Apple> getSources() {
- List<Apple> sources = new ArrayList<>();
- Apple apple1 = new Apple();
- apple1.setColor("red");
- apple1.setTypeName("hot");
- apple1.setSize(13);
- apple1.setWeight(34.2);
- Apple apple2 = new Apple();
- apple2.setColor("grayred");
- apple2.setTypeName("hot");
- apple2.setSize(12);
- apple2.setWeight(34.2);
- Apple apple3 = new Apple();
- apple3.setColor("green");
- apple3.setTypeName("hot");
- apple3.setSize(14);
- apple3.setWeight(34.2);
- sources.add(apple1);
- sources.add(apple2);
- sources.add(apple3);
- return sources;
- }
- }
运行结果:
有了方法引用之后,在有一种叫构造引用的话,相信大家都不会有什么吃惊的地方了。
- Supplier<Apple> app = Apple::new;
等于
- Supplier<Apple> app = ()->new Apple();
笔者就不多讲了。
关于lambda表达的知识大部分是这样子。笔者说实话吧。JAVA8加入lambda表达让笔者一定也没有感到兴奋。因为.NET那边都写烂了。至少上面讲到的知识让笔者没有什么新鲜感。到是方法引用有一点味。但是下面的知识点却让笔者提了一点兴趣了。
复合lambda表达。什么意思!就是把多个lambda表达用or或and的概念放到一起使用。好比如上面的排序例子。可以反序的。修改下面的代码
- apples.sort(comparing(Apple::getSize).reversed());
加上.reversed()之后
没有加之前
还有哦,还可以修改为
- apples.sort(comparing(Apple::getSize).reversed().thenComparing(Apple::getWeight));
一个排序条件不够,可以加哦。
让我们换另外一些方式来看看吧。
Main类:
- public static void main(String[] args) {
- Function<Integer, Integer> add = (Integer a) -> a + 2;
- Function<Integer, Integer> multiply = (Integer a) -> a * 4;
- Function<Integer, Integer> andThen = add.andThen(multiply);
- Function<Integer, Integer> compose = add.compose(multiply);
- System.out.println("andThen结果:" + andThen.apply(2));
- System.out.println("compose结果:" + compose.apply(2));
- }
运行结果:
这个结果说明一个问题
- andThen是multiply (add(x))。先执行了add,然后在multiply
- compose是add(multiply(x))。先执行了multiply ,然后在add
对于andThen比较好理解。笔者不喜欢的是compose。为什么?一般开发人员喜欢看其名知其意。compose的英文意思是构成,写作,还有组成的意思。有一点难理解。
让我们在看一个奇神的点吧。
- package com.aomi;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.function.Predicate;
- public class Main {
- public static void main(String[] args) {
- Predicate<Apple> query = (Apple a) -> a.getSize() > 13;
- query = query.and((Apple a) -> a.getWeight() < 20);
- List<Apple> apples = fliter(query);
- for (Apple apple : apples) {
- System.out.println(apple);
- }
- }
- public static List<Apple> fliter(Predicate<Apple> pred) {
- List<Apple> nSources = new ArrayList<>();
- List<Apple> sources = getSources();
- for (Apple apple : sources) {
- if (pred.test(apple))
- nSources.add(apple);
- }
- return nSources;
- }
- public static List<Apple> getSources() {
- List<Apple> sources = new ArrayList<>();
- Apple apple1 = new Apple();
- apple1.setColor("red");
- apple1.setTypeName("hot");
- apple1.setSize(13);
- apple1.setWeight(55.2);
- Apple apple2 = new Apple();
- apple2.setColor("grayred");
- apple2.setTypeName("hot");
- apple2.setSize(12);
- apple2.setWeight(34.2);
- Apple apple3 = new Apple();
- apple3.setColor("green");
- apple3.setTypeName("hot");
- apple3.setSize(14);
- apple3.setWeight(34.2);
- Apple apple4 = new Apple();
- apple4.setColor("green");
- apple4.setTypeName("hot");
- apple4.setSize(19);
- apple4.setWeight(12.2);
- sources.add(apple1);
- sources.add(apple2);
- sources.add(apple3);
- sources.add(apple4);
- return sources;
- }
- }
本来集合时面有四个苹果,大小值大于13的有俩个苹果。所以当我们把条件用and在加上的时候—— 重量小于20.结果只有一个。
JAVA8给我带了什么——lambda表达的更多相关文章
- JAVA8给我带了什么——Optional和CompletableFuture
不管是JAVA,还是.NET.我们常常会看到空异常(NullPointerException).这种异常都是在运行的过程中出现.往往是变量是一个null值.但是你引用这个变量的后继字段或是方法.所以我 ...
- JAVA8给我带了什么——并行流和接口新功能
流,确定是笔者内心很向往的天堂,有他之后JAVA在处理数据就变更加的灵动.加上lambda表达不喜欢都不行.JAVA8也为流在提供另一个功能——并行流.即是有并行流,那么是不是也有顺序流.没有错.我前 ...
- Java8初体验(一)lambda表达式语法
感谢同事[天锦]的投稿.投稿请联系 tengfei@ifeve.com 本文主要记录自己学习Java8的历程,方便大家一起探讨和自己的备忘.因为本人也是刚刚开始学习Java8,所以文中肯定有错误和理解 ...
- Java8 Lambda表达应用 -- 单线程游戏server+异步数据库操作
前段时间我们游戏server升级到开发环境Java8,这些天,我再次server的线程模型再次设计了一下,耗费Lambda表情. LambdaJava代码.特别是丑陋不堪的匿名内部类,这篇文章主要就是 ...
- Java Lambda表达
Java 8 lambda表达式示例 我个人对Java 8发布非常激动,尤其是lambda表达式和流API.越来越多的了解它们,我能写出更干净的代码.虽然一开始并不是这样.第一次看到用lambda表达 ...
- Java8新特性(一)——Lambda表达式与函数式接口
一.Java8新特性概述 1.Lambda 表达式 2. 函数式接口 3. 方法引用与构造器引用 4. Stream API 5. 接口中的默认方法与静态方法 6. 新时间日期 API 7. 其他新特 ...
- Java8新特性第1章(Lambda表达式)
在介绍Lambda表达式之前,我们先来看只有单个方法的Interface(通常我们称之为回调接口): public interface OnClickListener { void onClick(V ...
- Java8学习笔记(一)--Lambda表达式
两个概念 函数式接口 函数式接口就是只显式声明一个抽象方法的接口.为保证方法数量不多不少,java8提供了一个专用注解@FunctionalInterface,这样,当接口中声明的抽象方法多于或少于一 ...
- java8新增特性(一)---Lambda表达式
Lambda表达式也成为闭包,是java语言层次上的改变,Lambda同意把函数作为一个方法的參数(函数作为參数传递进方法中),或者把代码看成数据.函数式程序猿对这一概念非常熟悉. 在JVM平台上有非 ...
随机推荐
- WPF开发汽车采样机上位机软件
由于项目需要,需开发同一套汽车.火车.皮带采样机的上位机软件. 看过之前的上位机软件,老版本都是DelPhi.VB开发,稍微新语言开发的是采用winform开发.要不就是使用组态软件. Delphi语 ...
- JAVA CAS原理浅谈
java.util.concurrent包完全建立在CAS之上的,没有CAS就不会有此包.可见CAS的重要性. CAS CAS:Compare and Swap, 翻译成比较并交换. java.uti ...
- systemctl添加开机启动
我们对service和chkconfig两个命令都不陌生,systemctl 是管制服务的主要工具, 它整合了chkconfig 与 service功能于一体. systemctl is-enable ...
- php的垃圾回收机制
转载请附上本文地址:http://blog.csdn.net/u011957758/article/details/76864400 前言 是的,平时经常听到大牛说到的gc,就是垃圾回收器,全称Gar ...
- Visual Studio 2015的安装及单元测试练习
第一部分:Visual Studio 2015的安装 我电脑系统是win10,所以安装的是Visual Studio 2015,安装步骤部分截图如图所示: 1.安装类型选项界面:可以选择默认安装,可以 ...
- Week 3 结对编程
Group: 杜正远 潘礼鹏 结对编程: 优点: 集体荣誉感.你们已经是一个集体了,一定得为对方着想负责. 1.看对方的代码,彼此会互相学习到一些奇妙的方法. 2.结对编程能把两个事情分开,降低复杂度 ...
- Java源码--Array
1. Arrays.asList() 该方法是将数组转化为List,需要注意以下几点: (1)该方法不适用于基本数据类型(byte,short,int,long,float,double,boolea ...
- Windows samba history
https://blogs.technet.microsoft.com/josebda/2013/10/02/windows-server-2012-r2-which-version-of-the-s ...
- Appium学习笔记4_元素定位方法
Appium之元素定位,如果对Android上如何使用工具获取页面元素有问题的,请转战到这:http://www.cnblogs.com/taoSir/p/4816382.html. 下面主要是针对自 ...
- php 7.1 openssl_decrypt() 代替 mcrypt_module_open() 方法
公司开发微信第三方平台,之前用着一直是没有问题的.后来服务器到期进行项目搬迁就怎么也接收不到微信每10分钟的ticketle. 经过调试发现php版本由原来的7.0升到了7.1(该死....为什么没人 ...