博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java中的反射总结
阅读量:4304 次
发布时间:2019-05-27

本文共 8412 字,大约阅读时间需要 28 分钟。

刚开始学习java的时候真的很难理解反射到底是个什么东西

一些书籍,哪怕是很经典的书籍都解释的让人感觉懵懵的,或许的确是我太笨

况且,网上说在将来学习框架的时候需要经常应用到反射机制,这样一来总让人心里有些不安

就方才偶然又把讲解反射的章节和视频看了一点,觉得能理解一些了

现在决定一鼓作气,边看边写,顺便把一些主要的内容和操作都记载到这里

我想,对于我这么一个笨笨的人来说,学习的最好方法也许就是不断重复

遇到不懂的知识就停下来把以往的重新学一遍,虽然浪费了很多时间,但对我也有些效果

 

我的理解是:所谓反射,就是根据一个已经实例化了的对象来还原类的完整信息

至少对我而言,我认为它带给我的好处是,让我从下往上的又了解了一遍面向对象

x_x 在此又痛恨一边那些厚部头们,把我的脑细胞搞死一片

 

Class类

如果要完成反射,那么必须了解Class类

实例1:通过对象取得包名和类名

1
2
3
4
5
6
7
8
9
10
11
12
13
package 
org.siu;
 
class 
Test {
     
}
 
public 
class 
Demo {
    
public 
static 
void 
main(String[] args) {
        
Test t = 
new 
Test();
        
System.out.println(t.getClass());
        
System.out.println(t.getClass().getName());
    
}
}

编译结果如下,注意包的编译方式即可

此处的getClass()方法是默认继承自Object类的

 

在java中,Object类是所有类的父类,同样,所有类的实例化对象也都是Class类的实例

因此,这样一来就会牵扯到向上转型和向下转型的概念

由于向下转型的不安全因素,在这里泛型也会接踵而来

(不过我想说的是,此处的泛型设计很刺眼!尼玛,整个java的语法设计同样刺眼,超恶心!!!)

 

实例2:Class类的实例化

由于Class类没有构造方法,所以实例化Class类的方式有点特殊,有三种方式:

  • 对象.getClass( )
  • 类.Class
  • forName( )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class 
Test {
     
}
 
public 
class 
Demo {
    
public 
static 
void 
main(String[] args) {
        
//方式一:
        
Test t = 
new 
Test();
        
Class<? 
extends 
Test> c1 = t.getClass();
        
System.out.println(c1);
         
        
//方式二:
        
//为了避免特殊性,这里不用Test类,而用java库中的String类
        
Class<String> c2 = String.
class
;
        
System.out.println(c2);
         
        
//方式三:
        
//forName()方法会抛出异常
        
Class<?> c3 = 
null
;
        
try 
{
            
c3 = Class.forName(
"Test"
);
        
catch 
(ClassNotFoundException e) {
            
e.printStackTrace();
        
}
        
System.out.println(c3);
    
}
}

 其中,forName( )方法需要重点掌握,因为它可以在类不确定的情况下实例化Class,更具灵活性

 

Class类的应用

Class类中有一个方法叫做newInstance( ),它可以用来创建一个Class类对象的新实例

怎么说呢?Class对象包含的内容就是反射好的那个类,我们要构造那个类的新实例(新对象)

实例3:Class类的无参构造对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public 
class 
Demo {
    
public 
static 
void 
main(String[] args) {
        
//实例化Class对象,forName()方法会抛异常
        
Class<?> c = 
null
;
        
try 
{
            
//这里需要完整的包名和类名
            
c = Class.forName(
"java.lang.String"
);
        
catch 
(ClassNotFoundException e) {
            
e.printStackTrace();
        
}
         
        
//生成一个字符串的引用
        
String s = 
null
;
        
try 
{
            
//将构造好的对象向下转型为String类
            
//newInstance()方法会抛异常
            
s = (String) c.newInstance();
        
catch 
(InstantiationException e) {
            
e.printStackTrace();
        
catch 
(IllegalAccessException e) {
            
e.printStackTrace();
        
}
        
System.out.println(
"字符串长度: " 
+ s.length());
    
}
}

这样就通过无参数的形式构造了一个新的对象,如同正常模式中

通过无参构造方法来构造新对象一样

我们知道,类中除了有无参构造方法,还会存在有参数的构造方法

那在反射中如何通过有参数的形式构造对象呢?接着看

 

实例4:Class类的有参构造对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import 
java.lang.reflect.Constructor;
 
public 
class 
Demo {
    
//下面的几个方法抛出来的异常太多,为了代码的紧凑性,这里就直接抛给虚拟机了
    
public 
static 
void 
main(String[] args) 
throws 
Exception {
        
Class<?> c = 
null
;
        
try 
{
            
c = Class.forName(
"java.lang.String"
);
        
catch 
(ClassNotFoundException e) {
            
e.printStackTrace();
        
}
        
char
[] ch = {
'h'
,
'e'
,
'l'
,
'l'
,
'o'
};
        
String s = 
null
;
        
//获得Class类对象的有参构造方法,括号里面参数的写法是:类型.class
        
Constructor<?> con = c.getConstructor(
char
[].
class
);
        
//用此构造方法构造一个新的字符串对象,参数为一个char数组
        
s = (String) con.newInstance(ch);
        
System.out.println(
"构造的字符串:" 
+ s);
    
}
}

我们还是使用String类做例,因为String类用的比较多,便于理解

这里需要注意的是,构造方法需要使用getConstructor( )方法获得

至于参数类型则是:原有类型.class

还有一点,无论是有参还是无参,这里所使用的构造方法,原本的类里面必须对应存在

那么,如何才能知道原有类里面的构造方法,普通方法,继承的父类等详细信息呢?接着看

 

获取类的结构

要通过反射获取类的结构我们这里要导入一个新的包java.lang.reflect

实例5:取得类的构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import 
java.lang.reflect.Constructor;
import 
java.util.Arrays;
 
public 
class 
Demo {
    
//下面的几个方法抛出来的异常太多,为了代码的紧凑性,这里就直接抛给虚拟机了
    
public 
static 
void 
main(String[] args) 
throws 
Exception {
        
Class<?> c = 
null
;
        
try 
{
            
c = Class.forName(
"java.lang.Boolean"
);
        
catch 
(ClassNotFoundException e) {
            
e.printStackTrace();
        
}
        
//这里的getConstructors()方法返回的是一个Constructor数组
        
Constructor<?>[] cons = c.getConstructors();
        
//打印的方式你可以自己写,为了方便我用Arrays.toString(),凑合着看
        
System.out.println(Arrays.toString(cons));
    
}
}

 我选择了Boolean类来做例,因为Boolean类的构造方法就两个,方便看

 

实例6:取得类所实现的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import 
java.util.Arrays;
 
public 
class 
Demo {
    
public 
static 
void 
main(String[] args) 
throws 
Exception {
        
Class<?> c = 
null
;
        
try 
{
            
c = Class.forName(
"java.lang.Boolean"
);
        
catch 
(ClassNotFoundException e) {
            
e.printStackTrace();
        
}
        
Class<?>[] in = c.getInterfaces();
        
System.out.println(Arrays.toString(in));
    
}
}

 没什么好说的,看结果

 

实例7:取得父类

1
2
3
4
5
6
7
8
9
10
11
12
13
public 
class 
Demo {
    
public 
static 
void 
main(String[] args) 
throws 
Exception {
        
Class<?> c = 
null
;
        
try 
{
            
c = Class.forName(
"java.lang.Boolean"
);
        
catch 
(ClassNotFoundException e) {
            
e.printStackTrace();
        
}
        
//注意了,这里不会是数组,why?
        
Class<?> su = c.getSuperclass();
        
System.out.println(su);
    
}
}

 别忘了,java中是单继承,父类只有一个

 

实例8:取得类的全部方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import 
java.lang.reflect.Method;
 
public 
class 
Demo {
    
public 
static 
void 
main(String[] args) 
throws 
Exception {
        
Class<?> c = 
null
;
        
try 
{
            
c = Class.forName(
"java.lang.Boolean"
);
        
catch 
(ClassNotFoundException e) {
            
e.printStackTrace();
        
}
        
Method[] m = c.getMethods();
        
//好吧,这次我就大发慈悲的写个打印列表出来
        
for 
(
int 
i = 
0
; i < m.length; i++) {
            
System.out.println(m[i]);
        
}
    
}
}

 截取一部分,看看,意思下就行了……这几个例子都比较简单

 

实例9:取得本类的全部属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import 
java.lang.reflect.Field;
 
class 
Person {
    
private 
String name;
    
private 
int 
age;
}
 
public 
class 
Demo {
    
public 
static 
void 
main(String[] args) 
throws 
Exception {
        
Class<?> c = 
null
;
        
try 
{
            
c = Class.forName(
"Person"
);
        
catch 
(ClassNotFoundException e) {
            
e.printStackTrace();
        
}
        
Field[] f = c.getDeclaredFields();
        
for 
(
int 
i = 
0
; i < f.length; i++) {
            
System.out.println(f[i]);
        
}
    
}
}

getDeclaredFielsd()方法可以获取全部属性,getFields()只能获取公共属性

 

实例10:获取本类中属性的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import 
java.lang.reflect.Field;
 
class 
Person {
    
public 
String name;
    
private 
int 
age;
     
    
public 
Person(String name, 
int 
age) {
        
this
.name = name;
        
this
.age = age;
    
}
}
 
public 
class 
Demo {
    
public 
static 
void 
main(String[] args) 
throws 
Exception {
        
Person p = 
new 
Person(
"zhangsan"
,
12
);
 
        
Class<?> c = p.getClass();
         
        
//获取公共属性的值
        
Field f1 = c.getField(
"name"
);
        
//get(p)表明要获取是哪个对象的值
        
String str = (String) f1.get(p);
        
System.out.println(
"姓名: " 
+ str);
         
        
//获取私有属性的值
        
Field f2 = c.getDeclaredField(
"age"
);
        
//age是私有属性,所以要设置安全检查为true
        
f2.setAccessible(
true
);
        
int 
age = (
int
) f2.get(p);
        
System.out.println(
"年龄: " 
+ age);
    
}
}

 要注意的是:setAccessible()方法可以设置是否访问和修改私有属性

 

坦白说,java学到现在我还没发现什么能亮瞎我钛金眼的知识在里边

每次都是写一堆繁琐的语法实现个小玩意儿,不然就是拼命调用API,拼命的抛异常

让本身显得不够紧凑的代码变得愈发累赘

如果我喜欢一门语言,在我利用它做出东西来之前,它本身的特性必须能够打动我

显然,java并不让我快乐,也许很多程序员跟我一样是被迫使用java的

仅以此来安抚我那颗孤独编码的心,下面接着看内容

 

反射的应用

实例11:通过反射修改属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import 
java.lang.reflect.Field;
 
class 
Person {
    
private 
String name;
     
    
public 
Person(String name) {
        
this
.name = name;
    
}
     
    
public 
String toString() {
        
return 
"姓名: " 
this
.name;
    
}
}
 
public 
class 
Demo {
    
public 
static 
void 
main(String[] args) 
throws 
Exception {
        
Person p = 
new 
Person(
"王二狗"
);
        
System.out.println(p);
        
Class<?> c = p.getClass();
     
        
//定义要修改的属性
        
Field f = c.getDeclaredField(
"name"
);
        
f.setAccessible(
true
);
        
//修改属性,传入要设置的对象和值
        
f.set(p, 
"张二蛋"
);
        
System.out.println(p);
    
}
}

 几个方法都是有联系的,如果看不懂就先熟悉上面几个例子

 

实例12:通过反射调用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import 
java.lang.reflect.Method;
 
class 
Person {
    
public 
void 
print(
int 
i) {
        
System.out.println(
"我在写数字: " 
+ i);
    
}
     
    
public 
static 
void 
say(String str) {
        
System.out.println(
"我在说: " 
+ str);
    
}
}
 
public 
class 
Demo {
    
public 
static 
void 
main(String[] args) 
throws 
Exception {
        
Person p = 
new 
Person();
        
Class<?> c = p.getClass();
     
        
//getMethod()方法需要传入方法名,和参数类型
        
Method m1 = c.getMethod(
"print"
int
.
class
);
        
//invoke()表示调用的意思,需要传入对象和参数
        
m1.invoke(p, 
10
);
         
        
Method m2 = c.getMethod(
"say"
, String.
class
);
        
//这里的null表示不由对象调用,也就是静态方法
        
m2.invoke(
null
"你妹"
);
    
}
}

这里演示了一个普通的有参方法和一个静态方法

既然有参数的都写出来了,那么无参的就更简单了,直接传入一个对象即可

 

实例13:通过反射操作数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import 
java.lang.reflect.Array;
 
public 
class 
Demo {
    
public 
static 
void 
main(String[] args) 
throws 
Exception {
        
int
[] arr = {
1
,
2
,
3
,
4
,
5
};
        
Class<?> c = arr.getClass().getComponentType();
         
        
System.out.println(
"数组类型: " 
+ c.getName());
        
int 
len = Array.getLength(arr);
        
System.out.println(
"数组长度: " 
+ len);
        
System.out.print(
"遍历数组: "
);
        
for 
(
int 
i = 
0
; i < len; i++) {
            
System.out.print(Array.get(arr, i) + 
" "
);
        
}
        
System.out.println();
        
//修改数组
        
System.out.println(
"修改前的第一个元素: " 
+ Array.get(arr, 
0
));
        
Array.set(arr, 
0
3
);
        
System.out.println(
"修改后的第一个元素: " 
+ Array.get(arr, 
0
));
    
}
}

 这里要注意一点,getComponentType( )返回的是数组元素的Class

 

暂时就写这么多,我看的书中还有反射在工厂模式中的应用

无非是用forName()方法替换一下,没什么可说的

我是个java初级黑,我恨java那种恶心的语法和设计

这都是为了Android,为了打基础,为了适应以后的工作

Fuck java……

转载地址:http://erhws.baihongyu.com/

你可能感兴趣的文章
Maven:mirror和repository 区别
查看>>
微服务网关 Spring Cloud Gateway
查看>>
SpringCloud Feign的使用方式(一)
查看>>
SpringCloud Feign的使用方式(二)
查看>>
关于Vue-cli+ElementUI项目 打包时排除Vue和ElementUI
查看>>
Vue 路由懒加载根据根路由合并chunk块
查看>>
vue中 不更新视图 四种解决方法
查看>>
MySQL 查看执行计划
查看>>
OpenGL ES 3.0(四)图元、VBO、VAO
查看>>
OpenGL ES 3.0(五)纹理
查看>>
OpenGL ES 3.0(八)实现带水印的相机预览功能
查看>>
OpenGL ES 3.0(九)实现美颜相机功能
查看>>
FFmpeg 的介绍与使用
查看>>
Android 虚拟机简单介绍——ART、Dalvik、启动流程分析
查看>>
原理性地理解 Java 泛型中的 extends、super 及 Kotlin 的协变、逆变
查看>>
FFmpeg 是如何实现多态的?
查看>>
FFmpeg 源码分析 - avcodec_send_packet 和 avcodec_receive_frame
查看>>
FFmpeg 新旧版本编码 API 的区别
查看>>
RecyclerView 源码深入解析——绘制流程、缓存机制、动画等
查看>>
Android 面试题整理总结(一)Java 基础
查看>>