getMethod(String name, Class…public static void main ( String [ ] args) { try { Class < ? > c = Class . forName ( “Student” ) ; Student student = ( Student ) c. newInstance ( ) ; Method method = c. getDeclaredMethod ( “function” , String . class ) ; method. setAccessible ( true ) ; method. invoke ( student, “传入私有方法参数” ) ; } catch ( ClassNotFoundException e) { e. printStackTrace ( ) ; } catch ( NoSuchMethodException e) { e. printStackTrace ( ) ; } catch ( InstantiationException e) { e. printStackTrace ( ) ; } catch ( IllegalAccessException e) { e. printStackTrace ( ) ; } catch ( InvocationTargetException e) { e. printStackTrace ( ) ; } } 运行结果:
通过反射可以获取到实例对象的私有方法并进行调用。
2.8 获得类中注解相关的方法 方法 用途 getAnnotation(Class annotationClass) 返回该类中与参数类型匹配的公有注解对象 getAnnotations() 返回该类所有的公有注解对象 getDeclaredAnnotation(Class annotationClass) 返回该类中与参数类型匹配的所有注解对象 getDeclaredAnnotations() 返回该类所有的注解对象
3. 反射的优缺点 优点 :
对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力 反射已经运用在了很多流行框架如:Struts、Hibernate、Spring 等等。 缺点 :
使用反射会有效率问题。会导致程序效率降低。 反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂 。 二. 枚举 1. 枚举的概述 枚举是在JDK1.5以后引入的; 关键字enum 可以将一组具名的值的有限集合 创建为一种新的类型 ,而这些具名的值可以作为常规的程序组件使用,这个新的类型就是枚举 。
主要用途是:将一组常量组织起来,在这之前表示一组常量通常使用定义常量的方式:
public static int final RED = 1 ; public static int final GREEN = 2 ; public static int final BLACK = 3 ;
但是常量举例有不好的地方,例如:可能碰巧有个数字1,但是他有可能误会为是RED,现在我们可以直接用枚举来进行组织,这样一来,就拥有了类型,枚举类型。而不是普通的整形1.
下面是创建一个Color枚举类型 :
public enum Color { RED, BLUE, GREEN, YELLOW, BLACK; }
优点:将常量组织起来统一进行管理
场景:错误状态码,消息类型,颜色的划分,状态机等等…
本质 :是 java.lang.Enum 的子类,也就是说,自己写的枚举类,就算没有显示的继承 Enum ,但是其默认继承了这个类 。
2. 枚举的使用 2.1 switch语句中使用枚举 switch语句中可以使用枚举来提高代码的可读性。
其实enum关键字组织的是一个特殊的类,里面包含一个或多个的枚举对象,下面定义的Color,其实里面包含了3个枚举对象,每个对象都是Color类型。
enum Color { BLACK, YELLOW, GREEN; } public class Test { public static void main ( String [ ] args) { Color color = Color . YELLOW; switch ( color) { case BLACK: System . out. println ( "BLACK" ) ; break ; case YELLOW: System . out. println ( "YELLOW" ) ; break ; case GREEN: System . out. println ( "GREEN" ) ; break ; default : break ; } } }
运行结果:
2.2 枚举enum中的常用方法 枚举中常用的方法如下:
方法名称 描述 values() 以数组形式返回枚举类型的所有成员 ordinal() 获取枚举成员的索引位置 valueOf() 将普通字符串转换为枚举实例 compareTo() 比较两个枚举成员在定义时的顺序
关于Enum类源码中找不到values()方法的解释:
values方法,在编译前无法找到,这是因为enum声明实际上定义了一个类,我们可以通过定义的enum调用一些方法,Java编译器会自动在enum类型中插入一些方法,其中就包括values(),valueOf(),所以我们的程序在没编译的时候,就没办法查看到values()方法以及源码,这也是枚举的特殊性。
使用values()得到一个含有所有枚举对象的一个数组 :public enum Color { BLACK, YELLOW, GREEN; public static void main ( String [ ] args) { Color [ ] colors = Color . values ( ) ; for ( Color c : colors) { System . out. println ( c) ; } } }
运行结果:
使用valueOf()通过一个字符串获取同名枚举 :public enum Color { BLACK, YELLOW, GREEN; public static void main ( String [ ] args) { Color color = Color . valueOf ( "BLACK" ) ; System . out. println ( color) ; } }
运行结果:
使用ordinal()获取枚举在枚举类中的位置次序,也就是索引: public enum Color { BLACK, YELLOW, GREEN; public static void main ( String [ ] args) { Color [ ] colors = Color . values ( ) ; for ( Color c : colors) { System . out. println ( c + "的索引:" + c. ordinal ( ) ) ; } } }
运行结果:
使用compareTo() 比较两个枚举成员在定义时的顺序: public enum Color { BLACK, YELLOW, GREEN; public static void main ( String [ ] args) { System . out. println ( Color . GREEN. compareTo ( Color . YELLOW) ) ; System . out. println ( Color . BLACK. compareTo ( Color . YELLOW) ) ; } }
运行结果:
3. 自定义构造枚举对象 上面的例子中enum本质上其实是一个特殊的类,默认继承了抽象类java.lang.Enum,里面包含了一个或多个枚举对象,并且这些枚举对象默认情况下都是通过无参数的构造方法构造的,
其实我们可以在枚举类中自定义属性方法以及构造方法,实现自定义枚举对象.
看下面的写法, 和上面的例子是一样的 , 只不过上面的写法是无参构造省略了 ( )
我们可以自己在枚举类中定义一些属性, 然后去写含有含有参数的构造方法, 实现自定义枚举;
注意 : 枚举中的构造方法必须(默认)是私有的, 且当我们写了含有参数的构造方法时, 编译器不会再提提供无参的构造方法 , 所以此时需要按照我们自己写的构造方法传入参数;
public enum Color { BLACK ( "BLACK" , 11 , 1 ) , YELLOW ( "YELLOW" , 12 , 2 ) , GREEN ( "GREEN" , 13 , 3 ) ; public String colorName; public int colorId; public int ordonal; Color ( String colorName, int colorId, int ordonal) { this . colorName = colorName; this . colorId = colorId; this . ordonal = ordonal; } @Override public String toString ( ) { return "Color{" + "colorName='" + colorName + '\'' + ", colorId=" + colorId + ", ordonal=" + ordonal + '}' ; } public static void main ( String [ ] args) { Color [ ] colors = Color . values ( ) ; for ( Color c : colors) { System . out. println ( c) ; } } }
运行结果:
4. 枚举的安全性 首先看下面的代码, 我们想要从外部通过反射获取到枚举类:
public class Test { public static void main ( String [ ] args) { Class < " />> c = Color . class ; try { Constructor < ? > constructor = c. getDeclaredConstructor ( String . class , int . class , int . class ) ; constructor. setAccessible ( true ) ; Color color = ( Color ) constructor. newInstance ( "蓝色" , 88 , 2 ) ; System . out. println ( color) ; } catch ( NoSuchMethodException e) { e. printStackTrace ( ) ; } catch ( InvocationTargetException e) { e. printStackTrace ( ) ; } catch ( InstantiationException e) { e. printStackTrace ( ) ; } catch ( IllegalAccessException e) { e. printStackTrace ( ) ; } } }
运行结果:
结果中抛出一个java.lang.NoSuchMethodException: Color.(java.lang.String, int, int)
异常,表示没有找到我们给定参数列表的构造方法,但是我们枚举类中是定义过这个构造方法的,那么这里报错的原因是什么呢” />Constructor < ? > constructor = c. getDeclaredConstructor ( String . class , int . class , String . class , int . class , int . class ) ;
再次运行程序结果如下:
可以发现结果还是会抛出异常,但是此时抛的不是构造方法找不到的异常,而是枚举无法进行反射异常Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
;
所以枚举对象是无法通过反射得到的, 这也就保证了枚举的安全性;
其实枚举无法通过反射获取到枚举对象是因为在**newInstance****()**中获取枚举对象时,会过滤掉枚举类型,如果遇到的是枚举类型就会抛出异常。
5. 总结 枚举本身就是一个类,其构造方法默认为私有的,且都是默认继承与 java.lang.Enum 枚举可以避免反射和序列化问题 枚举实现单例模式是安全的 枚举的优点:
枚举的缺点:
三. Lambda表达式 1. 函数式接口 要了解Lambda表达式,首先需要了解什么是函数式接口,函数式接口定义:一个接口有且只有一个抽象方法 。
注意 :
如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口 如果我们在某个接口上声明了 @FunctionalInterface
注解,那么编译器就会按照函数式接口的定义来要求该接 口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的。 定义方式 :
@FunctionalInterface interface NoParameterNoReturn { void test ( ) ; }
基于jdk1.8, 还以有如下定义:
@FunctionalInterface interface NoParameterNoReturn { void test ( ) ; default void test2 ( ) { System . out. println ( "JDK1.8新特性,default默认方法可以有具体的实现" ) ; } }
2. 什么是Lambda表达式? Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达 式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码 块)。 Lambda 表达式(Lambda expression),基于数学中的λ演算得名,也可称为闭包(Closure) 。
Lambda表达式的语法 :
( parameters) -> expression 或 ( parameters) -> { statements; }
Lambda表达式由三部分组成 :
paramaters :类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。-> :可理解为“被用于”的意思方法体 :可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。常用的lambda表达式格式:
( ) -> 2 x -> 2 * x( x, y) -> x + y( int x, int y) -> x * y( String s) -> System . out. print ( s)
3. Lambda表达式的基本使用 参数类型可以省略,如果需要省略,每个参数的类型都要省略。 参数的小括号里面只有一个参数,那么小括号可以省略 如果方法体当中只有一句代码,那么大括号可以省略 如果方法体中只有一条语句,其是return语句,那么大括号可以省略,且去掉return关键字 以下面这些接口为例:
@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) { NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn ( ) { @Override public void test ( ) { System . out. println ( "hello" ) ; } } ; noParameterNoReturn. test ( ) ; } }
那么这里使用lambda表达式, 可以进一步进行简化;
public class TestDemo { public static void main ( String [ ] args) { NoParameterNoReturn noParameterNoReturn = ( ) -> { System . out. println ( "无参数无返回值" ) ; } ; noParameterNoReturn. test ( ) ; OneParameterNoReturn oneParameterNoReturn = ( int a) -> { System . out. println ( "一个参数无返回值:" + a) ; } ; oneParameterNoReturn. test ( 10 ) ; MoreParameterNoReturn moreParameterNoReturn = ( int a, int b) -> { System . out. println ( "多个参数无返回值:" + a+ " " + b) ; } ; moreParameterNoReturn. test ( 20 , 30 ) ; NoParameterReturn noParameterReturn = ( ) -> { System . out. println ( "有返回值无参数!" ) ; return 40 ; } ; int ret = noParameterReturn. test ( ) ; System . out. println ( ret) ; OneParameterReturn oneParameterReturn = ( int a) -> { System . out. println ( "有返回值有一个参数!" ) ; return a; } ; ret = oneParameterReturn. test ( 50 ) ; System . out. println ( ret) ; MoreParameterReturn moreParameterReturn = ( int a, int b) -> { System . out. println ( "有返回值多个参数!" ) ; return a+ b; } ; ret = moreParameterReturn. test ( 60 , 70 ) ; System . out. println ( ret) ; } }
上面的的代码根据开头的省略规则还可以进一步省略, 如下:
public class TestDemo { public static void main ( String [ ] args) { NoParameterNoReturn noParameterNoReturn= ( ) -> System . out. println ( "无参数无返回值" ) ; noParameterNoReturn. test ( ) ; OneParameterNoReturn oneParameterNoReturn= a-> System . out. println ( "一个参数无返回值:" + a) ; oneParameterNoReturn. test ( 10 ) ; MoreParameterNoReturn moreParameterNoReturn= ( a, b) -> System . out. println ( "多个参数无返回值:" + a+ " " + b) ; moreParameterNoReturn. test ( 20 , 30 ) ; NoParameterReturn noParameterReturn = ( ) -> 40 ; int ret = noParameterReturn. test ( ) ; System . out. println ( ret) ; OneParameterReturn oneParameterReturn = a-> a; ret = oneParameterReturn. test ( 50 ) ; System . out. println ( ret) ; MoreParameterReturn moreParameterReturn = ( a, b) -> a+ b; ret = moreParameterReturn. test ( 60 , 70 ) ; System . out. println ( ret) ; } }
还有一种写法更加简洁, 但可读性就… , 比如:
OneParameterNoReturn oneParameterNoReturn = a-> System . out. println ( a) ;
可以简化成下面的样子, 看不太懂了…
OneParameterNoReturn oneParameterNoReturn = System . out:: println ;
4. 变量捕获 Lambda 表达式中存在变量捕获 ,了解了变量捕获之后,我们才能更好的理解Lambda 表达式的作用域 。
在匿名内部类中,只能捕获到常量,或者没有发生修改的变量,因为lambda本质也是实现函数式接口,所以lambda也满足此变量捕获的规则。
下面的代码捕获的变量num未修改, 程序可以正常编译和运行;
当捕获的变量num是修改过的, 则会报错;
5. Lambda在集合当中的使用 5.1 Collection接口中的forEach方法 注意:Collection的forEach()方 法是从接口 java.lang.Iterable 拿过来的。
forEach方法需要传递的参数是Consumer
使用匿名内部类,accept中的t参数表示集合中迭代出的元素,我们可以对该元素设定操作, 这里重写的方法只做输出操作;
public static void main ( String [ ] args) { ArrayList < String > list = new ArrayList < > ( ) ; list. add ( "欣" ) ; list. add ( "欣" ) ; list. add ( "向" ) ; list. add ( "荣" ) ; list. forEach ( new Consumer < String > ( ) { @Override public void accept ( String str) { System . out. print ( str+ " " ) ; } } ) ; }
执行结果:
我们可以将上面的匿名内部类使用lambda表示,它只有一个参数没有返回值,上面的代码变为
public static void main ( String [ ] args) { ArrayList < String > list = new ArrayList < > ( ) ; list. add ( "欣" ) ; list. add ( "欣" ) ; list. add ( "向" ) ; list. add ( "荣" ) ; list. forEach ( s -> System . out. print ( s + " " ) ) ; }
5.2 Map中forEach方法 map中的forEach方法和前面Collection中的forEach方法的使用其实都差不多,换了一个参数而已,这个参数BiConsumer
使用匿名内部类:
public static void main ( String [ ] args) { Map < Integer , String > map = new HashMap < > ( ) ; map. put ( 1 , "欣" ) ; map. put ( 2 , "欣" ) ; map. put ( 3 , "向" ) ; map. put ( 4 , "荣" ) ; map. forEach ( new BiConsumer < Integer , String > ( ) { @Override public void accept ( Integer k, String v) { System . out. println ( k + "=" + v) ; } } ) ; }
运行结果:
同样的对上面代码可以使用lambda表达式来实现,这是一个含有两个参数无返回值的函数式接口,上面的代码改为:
public static void main ( String [ ] args) { Map < Integer , String > map = new HashMap < > ( ) ; map. put ( 1 , "欣" ) ; map. put ( 2 , "欣" ) ; map. put ( 3 , "向" ) ; map. put ( 4 , "荣" ) ; map. forEach ( ( k, v) -> System . out. println ( k + "=" + v) ) ; }
5.3 大部分接口中的sort方法 大部分接口中的sort方法,默认都是按照升序的方式进行排序,如果需要对自定义类进行排序或者实现自定义规则的排序,需要额外传入一个Comparator的实现类对象(比较器) ; 这里以List集合中的sort方法为例 .
public static void main ( String [ ] args) { ArrayList < String > list = new ArrayList < > ( ) ; list. add ( "aaaa" ) ; list. add ( "bbb" ) ; list. add ( "cc" ) ; list. add ( "d" ) ; list. sort ( new Comparator < String > ( ) { @Override public int compare ( String str1, String str2) { return str1. length ( ) - str2. length ( ) ; } } ) ; System . out. println ( list) ; }
运行结果:
同样的对上面代码可以使用lambda表达式来实现,这是一个含有两个参数有返回值的函数式接口,上面的代码改为:
public static void main ( String [ ] args) { ArrayList < String > list = new ArrayList < > ( ) ; list. add ( "aaaa" ) ; list. add ( "bbb" ) ; list. add ( "cc" ) ; list. add ( "d" ) ; list. sort ( ( str1, str2) -> str1. length ( ) - str2. length ( ) ) ; System . out. println ( list) ; }
6. 总结 Lambda表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读。
代码简洁,开发迅速 方便函数式编程 非常容易进行并行计算 Java 引入 Lambda,改善了集合操作 代码可读性变差 在非并行计算中,很多计算未必有传统的 for 性能要高 不容易进行调试