目录
一、反射
1.定义
Java
的反射(reflection)机制是在
运行
状态中,对于任意一个类,都能够知道这个类的
所有属性和方法
;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息;这种动态获取信息以及动态调用对象方法的功能称java
语言的反射(reflection)机制。
2.用途
(1)在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java
的反射机制通过反射来获取所需的私有成员或是方法 。
(2)反射最重要的用途就是开发各种通用框架,比如在
spring
中,我们将所有的类
Bean
交给
spring
容器管理,无论是XML
配置
Bean
还是注解配置,当我们从容器中获取
Bean
来依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring
根据这些信息,需要创建那些
Bean
,
spring
就动态的创建这些类。
3.反射基本信息
Java
程序中许多对象在运行时会出现两种类型:
运行时类型
(RTTI)
和编译时类型
,例如
Person p = new Student();这句代码中
p
在编译时类型为
Person
,运行时类型为
Student
。程序需要在运行时发现对象和类的真实信心。而通过用反射程序就能判断出该对象和类属于哪些类。
4.反射相关的类
类名
|
用途
|
Class
类
|
代表类的实体,在运行的
Java
应用程序中表示类和接口
|
Field
类
|
代表类的成员变量
/
类的属性
|
Method类
|
代表类的方法
|
Constructor类 |
代表类的构造方法
|
(1)Class类(反射机制的起源)
①Class类代表类的实体,在运行的
Java
应用程序中表示类和接口
。Java文件被编译后,生成了
.class
文件,
JVM
此时就要去解读
.class
文件
,
被编译后的
Java
文件
.class
也被
JVM
解析为一个对象,这个对象就是 java.lang.Class
.
这样当程序在运行时,每个类
文件就最终变成了
Class
类对象的一个实例。我们通过Java
的反射机制应用到这个实例,就可以去
获得甚至去添加改变这个类的属性和动作
,使得这个类成为一个动态的类 。
②Class类的相关方法
方法
|
用途
|
getClassLoader()
|
获得类的加载器
|
getDeclaredClasses()
|
返回一个数组,数组中包含该类中所有类和接口类的对象
(
包括私有的
)
|
forName(String className)
|
根据类名返回类的对象
|
newInstance()
|
创建类的实例
|
getName()
|
获得类的完整路径名字
|
(2)Field类的相关方法
方法 | 用途 |
getField(String name)
|
获得某个公有的属性对象
|
getFields()
|
获得所有公有的属性对象
|
getDeclaredField(String name)
|
获得某个属性对象
|
getDeclaredFields()
|
获得所有属性对象
|
(3)Method类的相关方法
方法 | 用途 |
getMethod(String name, Class...<?> parameterTypes)
|
获得该类某个公有的方法
|
getMethods()
|
获得该类所有公有的方法
|
getDeclaredMethod(String name, Class...<?> parameterTypes)
|
获得该类某个方法
|
getDeclaredMethods() |
获得该类所有方法
|
(4)Constructor类的相关方法
方法 | 用途 |
getConstructor(Class...<?> parameterTypes)
|
获得该类中与参数类型匹配的公有构造方法
|
getConstructors()
|
获得该类的所有公有构造方法
|
getDeclaredConstructor(Class...<?> parameterTypes)
|
获得该类中与参数类型匹配的构造方法
|
getDeclaredConstructors()
|
获得该类所有构造方法
|
(5)获得类中注解相关的方法
方法 | 用途 |
getAnnotation(Class
annotationClass)
|
返回该类中与参数类型匹配的公有注解对象
|
getAnnotations()
|
返回该类所有的公有注解对象
|
getDeclaredAnnotation(Class
annotationClass)
|
返回该类中与参数类型匹配的所有注解对象
|
getDeclaredAnnotations()
|
返回该类所有的注解对象
|
(6)获得Class对象的三种方式
在反射之前,我们需要做的第一步就是先拿到当前需要反射的类的
Class
对象,然后通过
Class
对象的核心方法,达到反射的目的,即:在运行
状态中,对于任意一个类,都能够知道这个类的
所有属性和方法
;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息。
第一种
,使用
Class.forName("
类的全路径名
");
静态方法。 前提:已明确类的全路径名。
第二种
,使用
.class
方法。 说明:仅适合在编译前就已经明确要操作的 Class
第三种
,使用类对象的
getClass()
方法
package reflect;
class Student{
//私有属性name
private String name = "bit";
//公有属性age
public int age = 18;
//不带参数的构造方法
public Student(){
System.out.println("Student()");
}
private Student(String name,int age) {
this.name = name;
this.age = age;
System.out.println("Student(String,name)");
}
private void eat(){
System.out.println("i am eat");
}
public void sleep(){
System.out.println("i am a pig");
}
private void function(String str) {
System.out.println(str);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class TestDemo2 {
public static void main(String[] args) throws ClassNotFoundException{
Class<?> c1 = Class.forName("reflect.Student");
Class<?> c2 = Student.class;
Student student = new Student();
Class<?> c3 = student.getClass();
System.out.println(c1 == c2);
System.out.println(c1 == c3);
System.out.println(c2 == c3);
//全部都是true,说明不管使用哪种方式获取Class对象,该对象只有一个
}
}
(7)反射的使用
(以下代码与上面的代码在同一个包底下)
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectClassDemo {
//通过Class类的newInstance方法获取学生的一个实例
public static void reflectNewInstance() {
try{
Class<?> c1 = Class.forName("reflect.Student");
Student student = (Student) c1.newInstance();//newInstance()的返回值是T类型
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 反射私有或公有的构造方法 屏蔽内容为获得公有的构造方法
public static void reflectPrivateConstructor() {
try{
Class<?> c1 = Class.forName("reflect.Student");
Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
Student student = (Student)constructor.newInstance("abc",18);
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
//反射私有属性(获取私有或公开的)
public static void reflectPrivateField() {
try {
Class<?> c1 = Class.forName("reflect.Student");
Student student = (Student) c1.newInstance();
Field field = c1.getDeclaredField("name");
field.setAccessible(true);
field.set(student,"zhangssan");
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
//反射私有的方法
public static void reflectPrivateMethod() {
try {
Class<?> c1 = Class.forName("reflect.Student");
Student student = (Student) c1.newInstance();
Method method = c1.getDeclaredMethod("function", String.class);
method.setAccessible(true);
method.invoke(student,"我是私有方法的参数");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//reflectNewInstance();
//reflectPrivateConstructor();
//reflectPrivateField();
reflectPrivateMethod();
}
}
五、反射优点和缺点
优点:
1.
对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
2.
增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
3.
反射已经运用在了很多流行框架如:
Struts
、
Hibernate
、
Spring
等等。
缺点:
1.
使用反射会有效率问题。会导致程序效率降低。
2.
反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂 。
二、枚举
1.背景及定义
枚举是在
JDK1.5
以后引入的。主要用途是:将一组常量组织起来,在这之前表示一组常量通常使用定义常量的方式:
public static int final RED = 1;
public static int final GREEN = 2;
public static int final BLACK = 3;
但是常量举例有不好的地方,例如:可能碰巧有个数字
1
,但是他有可能误会为是
RED
,现在我们可以直接用枚举来进行组织,这样一来,就拥有了类型,枚举类型。而不是普通的整形1.
public enum TestEnum {
RED,BLACK,GREEN;
}
优点:将常量组织起来统一进行管理
场景:错误状态码,消息类型,颜色的划分,状态机等等
....
本质:是
java.lang.Enum
的子类,也就是说,自己写的枚举类,就算没有显示的继承
Enum
,但是其默认继承了
这个类
2.枚举的使用
(1)switch语句里面:
public enum TestEnum {
RED,BLACK,GREEN,WHITE;
public static void main(String[] args) {
TestEnum testEnum2 = TestEnum.BLACK;
switch (testEnum2) {
case RED:
System.out.println("red");
break;
case BLACK:
System.out.println("black");
break;
case WHITE:
System.out.println("WHITE");
break;
case GREEN:
System.out.println("black");
break;
default:
break;
}
}
}
(2)常用方法
方法名称 | 描述 |
values()
|
以数组形式返回枚举类型的所有成员
|
ordinal()
|
获取枚举成员的索引位置
|
valueOf()
|
将普通字符串转换为枚举实例
|
compareTo()
|
比较两个枚举成员在定义时的顺序
|
public enum TestEnum {
RED,BLACK,GREEN,WHITE;
public static void main(String[] args) {
TestEnum[] testEnums = TestEnum.values();
for (int i = 0; i < testEnums.length; i++) {
System.out.println(testEnums[i] + "-->" + testEnums[i].ordinal());
}
}
}
public enum TestEnum {
RED,BLACK,GREEN,WHITE;
public static void main(String[] args) {
TestEnum testEnum = TestEnum.valueOf("RED");//把字符串变成对应的枚举对象
System.out.println(testEnum);//RED
System.out.println(RED.compareTo(GREEN));//-2
System.out.println(BLACK.compareTo(RED));//1
}
}
(3)枚举的构造方法默认是私有的
public enum TestEnum {
RED("red",1),BLACK(),GREEN,WHITE;//它属于对象,当我们在它们后面加括号后相当于调用构造方法了
public String color;
public int ordinal;
//构造方法默认是私有的
TestEnum(String color,int ordinal){
this.color = color;
this.ordinal = ordinal;
}
TestEnum(){
}
}
3.枚举的优缺点
优点:
1.
枚举常量更简单安全 。
2.
枚举具有内置方法 ,代码更优雅
缺点:
1.
不可继承,无法扩展
4.枚举和反射
既然枚举的构造方法是私有的,那么是否可以通过反射来获取到枚举对象的实例呢?
package enumdemo;
public enum TestEnum {
RED("red",1),BLACK("black",3),GREEN("green",8), WHITE("white",2);
public String color;
public int ordinal;
//构造方法默认是私有的
TestEnum(String color,int ordinal){
//super();
this.color = color;
this.ordinal = ordinal;
}
}
package enumdemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class TestEnumReflect {
public static void reflectPrivateConstructor(){
try{
Class<?> classEnum = Class.forName("enumdemo.TestEnum");
Constructor<?> declaredConstructorStudent =
classEnum.getDeclaredConstructor(String.class,int.class);
declaredConstructorStudent.setAccessible(true);
Object objectStudent = declaredConstructorStudent.newInstance("green",666);
TestEnum testEnum = (TestEnum) objectStudent;
System.out.println("获得枚举的私有构造函数:"+testEnum);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
reflectPrivateConstructor();
}
}
编译并运行该代码,输出如下:
java.lang.NoSuchMethodException: TestEnum.<init>(java.lang.String, int)的意思
就是没有对应的构造方法,但是我们提供的枚举的构造方法就是两个参数分别是String 和
int
啊!问题出现在哪里呢?我们说过的,我们所有的枚举类,都是默认继承与 java.lang.Enum ,
继承了父类除构造函数外的所有东西,并且子类要帮助父类进行构造! 而我们写的类,并没有帮助父类构造!所以我们要在自己的枚举类里面,提供super
吗?不是的,枚举比较特殊,虽然我们写的是两个,但是默认他还添加了两个参数,哪两个参数呢?我们看一下Enum
类的源码:
那么现在我们来修改一下它吧!
package enumdemo;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class TestEnumReflect {
public static void reflectPrivateConstructor(){
try{
Class<?> classEnum = Class.forName("enumdemo.TestEnum");
Constructor<?> declaredConstructorStudent =
classEnum.getDeclaredConstructor(String.class,int.class,String.class,int.class);
declaredConstructorStudent.setAccessible(true);
Object objectStudent = declaredConstructorStudent.newInstance("green",666,"abc",999);
TestEnum testEnum = (TestEnum) objectStudent;
System.out.println("获得枚举的私有构造函数:"+testEnum);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
reflectPrivateConstructor();
}
}
编译并运行该代码,输出如下:
它还在报错,这是为什么呢?我们来看一下部分底层的源码:
这说明:枚举十分安全,不能通过反射来获取枚举的实例对象
总结:
①枚举本身就是一个类,其构造方法默认为私有的,且都是默认继承与 java.lang.Enum
②枚举可以避免反射和序列化问题
5.一条重要的题
写一个单例模式
(1)普通实现
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class){
if(uniqueInstance == null){//进入区域后,再检查一次,如果仍是null,才创建实例
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
(2)用静态内部类实现一个单例模式
class Singleton { /** 私有化构造器 */
private Singleton() { }/** 对外提供公共的访问方法 */
public static Singleton getInstance() {
return UserSingletonHolder.INSTANCE;
}/** 写一个静态内部类,里面实例化外部类 */
private static class UserSingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
public class Main {
public static void main(String[] args) {
Singleton u1 = Singleton.getInstance();
Singleton u2 = Singleton.getInstance();
System.out.println("两个实例是否相同:"+ (u1==u2));
}
}
(3)用枚举实现一个单例模式
public enum TestEnum {
INSTANCE; public TestEnum getInstance(){
return INSTANCE;
}
public static void main(String[] args) {
TestEnum singleton1=TestEnum.INSTANCE;
TestEnum singleton2=TestEnum.INSTANCE;
System.out.println("两个实例是否相同:"+(singleton1==singleton2));
}
}
三、lambda表达式
1.背景
Lambda
表达式是
Java SE 8
中一个重要的新特性。
lambda
表达式允许你通过表达式来代替功能接口。
lambda
表达式就和方法一样,
它提供了一个正常的参数列表和一个使用这些参数的主体
(body,
可以是一个表达式或一个代码块)
。
Lambda
表达式(
Lambda expression
)可以看作是一个匿名函数
,基于数学中的
λ
演算得名,也可称为闭包(Closure
)
2.基本语法
(i)基本语法
:
(parameters) -> expression
或
(parameters) ->{ statements; }
Lambda
表达式由三部分组成:
(1)
paramaters
:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM
隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。
(2)->:可理解为
“
被用于
”
的意思
(3)方法体
:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不返回,这里的代码块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不返回。
举几个例子:
①
不需要参数,返回值为 2
() -> 2
②
接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
③
接受2个参数(数字),并返回他们的和
(x, y) -> x + y
④
接收2个int型整数,返回他们的乘积
(int x, int y) -> x * y
⑤
接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)
(ii)函数式接口:
一个接口有且只有一个抽象方法
注意:
①如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口
②如果我们在某个接口上声明了
@FunctionalInterface
注解,那么编译器就会按照函数式接口的定义来要求该接口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的。
package lambdademo;
//函数式接口(只有一个抽象方法)
@FunctionalInterface//这个注释修饰这个接口,说明它是函数式接口
interface NoParameterNoReturn{
void test();
}
public class TestDemo {
public static void main(String[] args) {
//简化的写法
NoParameterNoReturn noParameterNoReturn = () -> System.out.println("重写方法!");
noParameterNoReturn.test();
}
public static void main1(String[] args) {
NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){
@Override
public void test(){
System.out.println("重写方法!");
}
};
noParameterNoReturn.test();
}
}
3.基本使用
//没有返回值,没有参数
@FunctionalInterface//这个注释修饰这个接口,说明它是函数式接口
interface NoParameterNoReturn{
void test();
}
//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
void test(int a);
}
//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {
void test(int a,int b);
}
//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
int test();
}
//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
int test(int a);
}
//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {
int test(int a,int b);
}
public class TestDemo {
public static void main(String[] args) {
OneParameterNoReturn oneParameterNoReturn = (a) ->{System.out.println(a);};
oneParameterNoReturn.test(10);
//优化
OneParameterNoReturn oneParameterNoReturn2 = a ->System.out.println(a);
oneParameterNoReturn.test(10);
//继续优化
OneParameterNoReturn oneParameterNoReturn3 = System.out::println;
oneParameterNoReturn.test(10);
MoreParameterNoReturn moreParameterNoReturn = (a,b) -> {//理论上来说要写(int a,int b)但是因为a,b同类型,所以两个int都可以省略
System.out.println(a+b);
};
moreParameterNoReturn.test(10,20);
NoParameterReturn noParameterReturn = () -> {return 10;};
int ret = noParameterReturn.test();
System.out.println(ret);
//简写:
NoParameterReturn noParameterReturn2 = () -> 10;
int ret2 = noParameterReturn2.test();
System.out.println(ret2);
OneParameterReturn oneParameterReturn = a -> a + 11;
System.out.println(oneParameterReturn.test(10));
MoreParameterReturn moreParameterReturn = (a,b) -> a+b;
System.out.println(moreParameterReturn.test(1,2));
}
}
Lambda
表达式本质是一个匿名函数,函数的方法是:返回值 方法名 参数列表 方法体。在, Lambda表达式中我们只需要关心参数列表方法体。
语法精简:
①参数类型可以省略,如果需要省略,每个参数的类型都要省略。
②
参数的小括号里面只有一个参数,那么小括号可以省略
③
如果方法体当中只有一句代码,那么大括号可以省略
④
如果方法体中只有一条语句,其是
return
语句,那么大括号可以省略,且去掉
return
关键字。
4.变量捕获
package lambdademo;
class Test {
public void func(){
System.out.println("func()");
}
}
public class TestDemo2 {
public static void main(String[] args) {
int a = 100;
//a = 99;//error
new Test() {
@Override
public void func() {
System.out.println("我是内部类,且重写了func这个方法!");
System.out.println("我是捕获到变量 a == " + a + " 我要么是一个常量,要么是一个没有改变过值的变量!");
}
}.func();
}
}
5.lambda在集合当中的使用
为了能够让
Lambda
和
Java
的集合类集更好的一起使用,集合当中,也新增了部分接口,以便与
Lambda
表达式对接。
对应的接
口
|
新增的方法
|
Collection
|
removeIf() spliterator()
stream()
parallelStream()
forEach()
|
List
|
replaceAll()
sort()
|
Map
|
getOrDefault() forEach()
replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge()
|
(1)Collection接口
forEach() 方法演示:
该方法在接口 Iterable 当中,原型如下:
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
该方法表示:对容器中的每个元素执行action指定的动作 。
演示如下:
package lambdademo;
import java.util.ArrayList;
import java.util.function.Consumer;
public class TestDemo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
list.add("bit");
list.add("hello");
list.add("lambda");
/*list.forEach(new Consumer<String>(){
@Override
public void accept(String str){ //简单遍历集合中的元素。
System.out.print(str+" ");
}
});*/
//等价于
list.forEach(str -> System.out.print(str + " "));
}
}
(2)List接口
sort()
方法的演示:
sort
方法源码:该方法根据
c
指定的比较规则对容器元素进行排序。
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
演示如下:
package lambdademo;
import java.util.ArrayList;
import java.util.Comparator;
public class TestDemo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("zello");
list.add("bit");
list.add("hello");
list.add("lambda");
list.forEach(str -> System.out.print(str + " "));
System.out.println();
System.out.println("====================");
//list.sort((o1,o2) -> o1.compareTo(o2));
//等价于:
list.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
list.forEach(str -> System.out.print(str + " "));
}
}
编译并运行该代码,输出如下:
(3)Map接口
HashMap
的
forEach(),该方法原型如下:
演示如下:
package lambdademo;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.function.BiConsumer;
class Test {
public void func(){
System.out.println("func()");
}
}
public class TestDemo2 {
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "hello");
map.put(2, "bit");
map.put(3, "hello");
map.put(4, "lambda");
map.forEach(new BiConsumer<Integer, String>(){
@Override
public void accept(Integer key, String value){
System.out.print(key + "=" + value + " ");
}
});
//简化(使用lambda表达式)
System.out.println();
System.out.println("====================");
map.forEach((k,v)-> System.out.print(k + "=" + v+" "));
}
}
编译并运行该代码,输出如下:
(6)lambda的优缺点
优点:
①
代码简洁,开发迅速
②方便函数式编程
③非常容易进行并行计算
④Java
引入
Lambda
,改善了集合操作
缺点:
①代码可读性变差
②在非并行计算中,很多计算未必有传统的
for
性能要高
③不容易进行调试