1.反射
1
定义
Java
的反射(
reflflection
)机制是在
运行
状态中,对于任意一个类,都能够知道这个类的
所有属性和方法
;对于任 意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息;这种动态获取信 息以及动态调用对象方法的功能称为java
语言的反射(reflflection)机制
2
用途
(
了解
)
1.
在日常的第三方应用开发过程中,经常会遇到
某个类的某个成员变量、方法或是属性是私有的或是只对系统
应用开放
,这时候就可以利用
Java
的反射机制通过反射来获取所需的私有成员或是方法。
2.
反射最重要的用途就是
开发各种通用框架
,比如在
spring
中,我们将所有的类
Bean
交给
spring
容器管理,无 论是XML
配置
Bean
还是注解配置,当我们从容器中获取
Bean
来依赖注入时,容器会读取配置,而配置中给的 就是类的信息,spring
根据这些信息,需要创建那些
Bean
,
spring
就动态的创建这些类。
3
反射基本信息
Java
程序中许多对象在运行时会出现两种类型:
运行时类型
(RTTI)
和编译时类型
,例如
Person p = new Student();这句代码中
p
在编译时类型为
Person
,运行时类型为
Student
。程序需要在运行时发现对象和类的真实 信息。而通过使用反射程序就能判断出该对象和类属于哪些类。
4
反射相关的类(重要)
![](https://i-blog.csdnimg.cn/direct/60f40b7e3b894239a743158ccb440022.png)
4.1 Class
类
(
反射机制的起源
)
Java
文件被编译后,生成了
.class
文件,
JVM
此时就要去解读
.class
文件
,
被编译后的
Java
文件
.class
也被
JVM
解析为 一个对象,这个对象就是 java.lang.Class
.
这样当程序在运行时,每个
java
文件就最终变成了
Class
类对象的一个实 例。我们通过Java
的反射机制应用到这个实例,就可以去
获得甚至去添加改变这个类的属性和动作
,使得这个类成 为一个动态的类 .
4.1.1 Class
类中的相关方法
(
重要
)
常用获得类相关的方法
![](https://i-blog.csdnimg.cn/direct/61191295c2904320a5e99e5f24b9148a.png)
(
重要
)
常用获得类中属性相关的方法
(
以下方法返回值为Field相关)
![](https://i-blog.csdnimg.cn/direct/985c2b86a00f4de1b2b73baf0f980693.png)
(
了解
)
获得类中注解相关的方法
![](https://i-blog.csdnimg.cn/direct/ef5567d6317145838d45ddad1fe7b1a2.png)
(
重要
)
获得类中构造器相关的方法(以下方法返回值为Constuctor相关
)
![](https://i-blog.csdnimg.cn/direct/beb6e822e2394974a56c4174dd7227c6.png)
(
重要
)
获得类中方法相关的方法(以下方法返回值为Method相关
)
![](https://i-blog.csdnimg.cn/direct/cc1044d8dcdf4b868705c2f93c69a3ce.png)
4.2
反射示例
4.2.1
获得
Class
对象的三种方式
在反射之前,我们需要做的第一步就是先拿到当前需要反射的类的
Class
对象,然后通过
Class
对象的核心方法,达 到反射的目的,即:在运行
状态中,对于任意一个类,都能够知道这个类的
所有属性和方法
;对于任意一个对象, 都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息。
第一种
,使用
Class.forName("
类的全路径名
");
静态方法。
前提:已明确类的全路径名。
第二种
,使用
.class
方法。
说明:仅适合在编译前就已经明确要操作的
Class
第三种
,使用类对象的
getClass()
方法
接下来我们开始使用反射,我们依旧反射上面的
Student
类,把反射的逻辑写到另外的类当中进行理解
注意:所有和反射相关的包都在
import java.lang.reflflect
包下面。
![](https://i-blog.csdnimg.cn/direct/08d0a1f452254c06a900df6225ecf2b3.png)
![](https://i-blog.csdnimg.cn/direct/99033d8f95e54018a21cdd735b9ea159.png)
![](https://i-blog.csdnimg.cn/direct/e2ac68f7e714437488450aab7b0070f9.png)
![](https://i-blog.csdnimg.cn/direct/67918a7816a94b339a384342d4a2df1b.png)
![](https://i-blog.csdnimg.cn/direct/e158749ead324b63ac87484dc4380482.png)
![](https://i-blog.csdnimg.cn/direct/c977f6d35b0543c68394723f4068c6c2.png)
![](https://i-blog.csdnimg.cn/direct/188d1bf843af4fecadec1fedd6a11019.png)
2、反射优点和缺点
优点:
1.
对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
2.
增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
3.
反射已经运用在了很多流行框架如:
Struts
、
Hibernate
、
Spring
等等。
缺点:
1.
使用反射会有效率问题。会导致程序效率降低。具体参考这里:
http://www.imooc.com/article/293679
2.
反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂 。
3.枚举
优点:将常量组织起来统一进行管理
场景:错误状态码,消息类型,颜色的划分,状态机等等
....
本质:是
java.lang.Enum
的子类,也就是说,自己写的枚举类,就算没有显示的继承
Enum
,但是其默认继承了
这个类。
Enum 类的常用方法
![](https://i-blog.csdnimg.cn/direct/ad567dcf5bd54b5e93e1d5bf6a68535e.png)
刚刚说过,在
Java
当中枚举实际上就是一个类。所以我们在定义枚举的时候,还可以这样定义和使用枚举:
重要:枚举的构造方法默认是私有的
![](https://i-blog.csdnimg.cn/direct/f741ff667197432e86fa1bff7984f575.png)
3
枚举优点缺点
优点:
1.
枚举常量更简单安全 。
2.
枚举具有内置方法 ,代码更优雅.
缺点:
1.
不可继承,无法扩展
4 枚举和反射
4.1
枚举是否可以通过反射,拿到实例对象呢?
答案是:不可以!!!
为什么呢?我们直接看源码
![](https://i-blog.csdnimg.cn/direct/2449d918210d4e5bbab883f0f51dddf5.png)
是的,枚举在这里被过滤了,你不能通过反射获取枚举类的实例!这道题是
2017
年阿里巴巴曾经问到的一个问 题,不看不知道,一看吓一跳!同学们记住这个坑。原版问题是:为什么枚举实现单例模式是安全的?
总结
1
、枚举本身就是一个类,其构造方法默认为私有的,且都是默认继承与
java.lang.Enum
2
、枚举可以避免反射和序列化问题
3
、枚举的优点和缺点
5.Lambda表达式
1
背景
Lambda
表达式是
Java SE 8
中一个重要的新特性。
lambda
表达式允许你通过表达式来代替功能接口。
lambda
表达 式就和方法一样,
它提供了一个正常的参数列表和一个使用这些参数的主体
(body,
可以是一个表达式或一个代码 块)
。
Lambda
表达式(
Lambda expression
)
,基于数学中的
λ
演算得名,也可称为闭包(
Closure
) 。
1.1 Lambda
表达式的语法
基本语法
:
(parameters) -> expression
或
(parameters) ->{ statements; }
Lambda
表达式由三部分组成:
1.
paramaters
:类似方法中的形参列表,这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明 也可不声明而由JVM
隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。
2.
->
:可理解为
“
被用于
”
的意思
3.
方法体
:可以是表达式也可以代码块,是函数式接口里方法的实现。代码块可返回一个值或者什么都不反 回,这里的代码块块等同于方法的方法体。如果是表达式,也可以返回一个值或者什么都不反回。
1.2
函数式接口
要了解
Lambda
表达式
,
首先需要了解什么是函数式接口,函数式接口定义:一个接口有且只有一个抽象方法 。
注意:
1.
如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口
2.
如果我们在某个接口上声明了
@FunctionalInterface
注解,那么编译器就会按照函数式接口的定义来要求该接 口,这样如果有两个抽象方法,程序编译就会报错的。所以,从某种意义上来说,只要你保证你的接口中只 有一个抽象方法,你可以不加这个注解。加上就会自动进行检测的。
定义方式:
![](https://i-blog.csdnimg.cn/direct/6527ee8390b249919243298e9718b338.png)
但是这种方式也是可以的:
![](https://i-blog.csdnimg.cn/direct/73d3e18275a7479e9e8a49863002aba4.png)
2 Lambda
表达式的基本使用
首先,我们实现准备好几个接口:
![](https://i-blog.csdnimg.cn/direct/59afbb40f4874486af7f180c50130915.png)
我们在上面提到过,
Lambda
可以理解为:
Lambda
就是匿名内部类的简化,实际上是创建了一个类,实现了接 口,重写了接口的方法 。
没有使用
lambda
表达式的时候的调用方式
![](https://i-blog.csdnimg.cn/direct/ecce83832ad2462c9d459e424948dcba.png)
2.1
语法精简
1.
参数类型可以省略,如果需要省略,每个参数的类型都要省略。
2.
参数的小括号里面只有一个参数,那么小括号可以省略
3.
如果方法体当中只有一句代码,那么大括号可以省略
4.
如果方法体中只有一条语句,且是
return
语句,那么大括号可以省略,且去掉
return
关键字。
![](https://i-blog.csdnimg.cn/direct/1370279131844aa2a9b74d25ef991fa0.png)
3
变量捕获
Lambda
表达式中存在变量捕获 ,了解了变量捕获之后,我们才能更好的理解
Lambda
表达式的作用域 。
Java
当 中的匿名类中,会存在变量捕获
3.1
匿名内部类
匿名内部类就是没有名字的内部类 。我们这里只是为了说明变量捕获,所以,匿名内部类只要会使用就好,那么下 面我们来,简单的看看匿名内部类的使用就好了。
![](https://i-blog.csdnimg.cn/direct/09cfc6ca711a47cfbabebae12f60c9a8.png)
在上述代码当中的
main
函数当中,我们看到的就是一个匿名内部类的简单的使用
3.2
匿名内部类的变量捕获
![](https://i-blog.csdnimg.cn/direct/da28ca6c032c45a7a4dc2c92df699f6c.png)
3.3 Lambda
的变量捕获
在
Lambda
当中也可以进行变量的捕获,具体我们看一下代码。
![](https://i-blog.csdnimg.cn/direct/bf09d84d995d4875b989b3f337462728.png)
4 Lambda
在集合当中的使用
为了能够让
Lambda
和
Java
的集合类集更好的一起使用,集合当中,也新增了部分接口,以便与
Lambda
表达式对接。
![](https://i-blog.csdnimg.cn/direct/73d332922f214deeb0ca8db3297a207f.png)
6 总结
Lambda
表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读。
优点:
1.
代码简洁,开发迅速
2.
方便函数式编程
3.
非常容易进行并行计算
4. Java
引入
Lambda
,改善了集合操作
缺点:
1.
代码可读性变差
2.
在非并行计算中,很多计算未必有传统的
for
性能要高
3.
不容易进行调试
至此本文结束。