读书日记一

前言

每天一小步,进步一大步。java反射机制深入理解剖析

0x01 背景
在java开发中有一个非常重要的概念就是java反射机制,也是java的重要特征之一。反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力,通过反射可以调用私有方法和私有属性,大部分框架也都是运用反射原理的。java通常是先有类再有对象,有对象就可以调用方法或者属性,java中的反射其实是通过Class对象来调用类里面的方法。
0x02 发射简介:
主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
一个类有多个组成部分,例如:成员变量、方法、构造方法等,反射就是加载类,并解剖出类的各个组成部分。
反射机制主要提供以下功能:
①在运行时判断任意一个对象所属的类;
②在运行时构造任意一个类的对象;
③在运行时判断任意一个类所具有的成员变量和方法;
④在运行时调用任意一个对象的方法;
⑤生成动态代理。
0x03 java中的反射及作用:
假如有两个程序员,一个程序员在写程序的时需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码是不能通过编译的。此时,利用Java反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。
Java的反射机制它知道类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。如eclipse中,一按点,编译工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供用户进行选择。这就是利用了Java反射的原理,是对我们创建对象的探知、自审。
0x04 反射机制中有哪些类:
1
2
3
4
java.lang.Class;                
java.lang.reflect.Constructor; java.lang.reflect.Field;
java.lang.reflect.Method;
java.lang.reflect.Modifier;
0x05 反射机制的相关API
通过一个对象获得完整的包名和类名:
1
2
3
4
5
6
7
8
package com.example.demo;

public class Test8 {
public static void main(String[] args) {
Test8 test8=new Test8();
System.out.println(test8.getClass().getName());
}
}
结果如下:
1
com.example.demo.Test8
实例化Class类对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.example.demo;

public class Test8 {
public static void main(String[] args) throws ClassNotFoundException{
Class<?> class1=null;
Class<?> class2=null;
Class<?> class3=null;

// 一般采用这种形式
class1=Class.forName("com.example.demo.Test8");
class2=new Test8().getClass();
class3=Test8.class;

System.out.println("类名称 "+class1.getName());
System.out.println("类名称 "+class2.getName());
System.out.println("类名称 "+class3.getName());
}
}
结果如下:
1
2
3
类名称 com.example.demo.Test8
类名称 com.example.demo.Test8
类名称 com.example.demo.Test8
获取一个对象的父类与实现的接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.example.demo;

import java.io.Serializable;

public class Test8 implements Serializable{
private static final long serialVersionUID = 1L;

public static void main(String[] args) throws ClassNotFoundException {
Class<?> clazz=Class.forName("com.example.demo.Test8");

// 获取父类
Class<?> parentClass=clazz.getSuperclass();
System.out.println("clazz的父类为:"+parentClass.getName());

// 获取所有的接口
Class<?> intes[]=clazz.getInterfaces();
System.out.println("clazz实现的接口有:");
for (int i=0;i<intes.length;i++) {
System.out.println((i+1)+": "+intes[i].getName());
}
}
}
结果如下:
1
2
3
clazz的父类为:java.lang.Object
clazz实现的接口有:
1: java.io.Serializable
通过反射机制实例化一个类对象
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package com.example.demo;

import java.lang.reflect.Constructor;

class User {
private int age;
private String name;
public User() {
super();
}
public User(String name) {
super();
this.name = name;
}
public User(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User [age=" + age + ", name=" + name + "]";
}
}

public class Test8 {
public static void main(String[] args) throws Exception {
Class<?> class1 = null;
class1 = Class.forName("com.example.demo.User");

// 第一种方法,实例化默认构造方法,调用set赋值
User user = (User) class1.newInstance();
user.setAge(20);
user.setName("liyu");
System.out.println(user);

// 第二种方法 取得全部的构造函数 使用构造函数赋值
Constructor<?> cons[] = class1.getConstructors();
// 查看每个构造方法需要的参数
for (int i = 0; i < cons.length; i++) {
Class<?> clazzs[] = cons[i].getParameterTypes();
System.out.print("cons[" + i + "] (");
for (int j = 0; j < clazzs.length; j++) {
if (j == clazzs.length - 1)
System.out.print(clazzs[j].getName());
else
System.out.print(clazzs[j].getName() + ",");
}
System.out.println(")");
}

user = (User) cons[0].newInstance(20, "liyu");
System.out.println(user);

user = (User) cons[1].newInstance("liyu");
System.out.println(user);
}
}
结果如下:
1
2
3
4
5
6
User [age=20, name=liyu]
cons[0] (int,java.lang.String)
cons[1] (java.lang.String)
cons[2] ()
User [age=20, name=liyu]
User [age=0, name=liyu]
获取某个类的全部属性
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
33
34
35
36
37
package com.example.demo;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class Test8 implements Serializable {
private static final long serialVersionUID = 1L;
private String name;

public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.example.demo.Test8");
System.out.println("===============本类属性===============");
// 取得本类的全部属性
Field[] field = clazz.getDeclaredFields();
for (int i = 0; i < field.length; i++) {
// 权限修饰符
int mo = field[i].getModifiers();
String priv = Modifier.toString(mo);
// 属性类型
Class<?> type = field[i].getType();
System.out.println(priv + " " + type.getName() + " " + field[i].getName() + ";");
}

System.out.println("==========实现的接口或者父类的属性==========");
// 取得实现的接口或者父类的属性
Field[] filed1 = clazz.getFields();
for (int j = 0; j < filed1.length; j++) {
// 权限修饰符
int mo = filed1[j].getModifiers();
String priv = Modifier.toString(mo);
// 属性类型
Class<?> type = filed1[j].getType();
System.out.println(priv + " " + type.getName() + " " + filed1[j].getName() + ";");
}
}
}
结果如下:
1
2
3
4
===============本类属性===============
private static final long serialVersionUID;
private java.lang.String name;
==========实现的接口或者父类的属性==========
获取某个类的全部方法
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
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.example.demo;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Test8 implements Serializable {
private static final long serialVersionUID = 1L;

public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.example.demo.Test8");

Method method[] = clazz.getMethods();

for (int i = 0; i < method.length; ++i) {
Class<?> returnType = method[i].getReturnType();

Class<?> para[] = method[i].getParameterTypes();
int temp = method[i].getModifiers();
System.out.print(Modifier.toString(temp) + " ");
System.out.print(returnType.getName() + " ");
System.out.print(method[i].getName() + " ");
System.out.print("(");
for (int j = 0; j < para.length; ++j) {
System.out.print(para[j].getName() + " " + "arg" + j);
if (j < para.length - 1) {
System.out.print(",");
}
}
Class<?> exce[] = method[i].getExceptionTypes();
if (exce.length > 0) {
System.out.print(") throws ");
for (int k = 0; k < exce.length; ++k) {
System.out.print(exce[k].getName() + " ");
if (k < exce.length - 1) {
System.out.print(",");
}
}
} else {
System.out.print(")");
}
System.out.println();
}
}
}
结果如下:
1
2
3
4
5
6
7
8
9
10
public static void  main ([Ljava.lang.String; arg0) throws java.lang.Exception 
public final native void wait (long arg0) throws java.lang.InterruptedException
public final void wait (long arg0,int arg1) throws java.lang.InterruptedException
public final void wait () throws java.lang.InterruptedException
public boolean equals (java.lang.Object arg0)
public java.lang.String toString ()
public native int hashCode ()
public final native java.lang.Class getClass ()
public final native void notify ()
public final native void notifyAll ()
通过反射机制调用某个类的方法
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.example.demo;

import java.io.IOException;
import java.lang.reflect.Method;

class Eval{
private String command;

public Eval() {
super();
}

public Eval(String command) {
this.command=command;
}

public void toEval() {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}

public void toEval(String command) {
try {
Runtime.getRuntime().exec(command);
} catch (IOException e) {
e.printStackTrace();
}
}
}

public class Test8 {
public static void main(String[] args) throws Exception{
Class<?> clazz=Class.forName("com.example.demo.Eval");

// 调用Evil类中的toEvil()方法
Method method=clazz.getMethod("toEval");
method.invoke(clazz.newInstance());

// 调用Evil类中的toEvil(String command);
method=clazz.getMethod("toEval", String.class);
method.invoke(clazz.newInstance(),"whoami");
}
}
通过反射机制操作某个类的属性
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
33
34
35
36
37
38
39
40
41
42
43
44
package com.example.demo;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

class Eval{
private String command;

public Eval() {
super();
}

public Eval(String command) {
this.command=command;
}

public void toEval(String command) {
try {
Runtime.getRuntime().exec(command);
} catch (IOException e) {
e.printStackTrace();
}
}

public void toSay() {
System.out.println(this.command);
}
}

public class Test8 {
public static void main(String[] args) throws Exception{
Class<?> clazz=Class.forName("com.example.demo.Eval");
Object obj=clazz.newInstance();

Field field=clazz.getDeclaredField("command");
field.setAccessible(true);
field.set(obj, "calc");
System.out.println(field.get(obj));

Method method=clazz.getMethod("toSay");
method.invoke(obj);
}
}
结果如下:
1
2
calc
calc
反射机制的动态代理
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.example.demo;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义项目接口
interface Subject{
public String say(String name,int age);
}

// 定义真实项目
class RealSubject implements Subject{
public String say(String name,int age) {
return name+" "+age;
}
}

class MyInvocationHandler implements InvocationHandler {
private Object obj = null;
public Object bind(Object obj) {
this.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object temp = method.invoke(this.obj, args);
return temp;
}
}

/**
* 在java中有三种类类加载器。
*
* 1)Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。
*
* 2)Extension ClassLoader 用来进行扩展类的加载,一般对应的是jrelibext目录中的类
*
* 3)AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。
*
* 如果想要完成动态代理,首先需要定义一个InvocationHandler接口的子类,已完成代理的具体操作。
*
*
*/

public class Test8 {
public static void main(String[] args) throws Exception{
MyInvocationHandler demo=new MyInvocationHandler();
Subject sub=(Subject)demo.bind(new RealSubject());
String info=sub.say("liyu", 20);
System.out.println(info);
}
}
结果如下:
1
liyu 20
0x06 反射机制的应用实例
在泛型为Integer的ArrayList中存放一个String类型的对象
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.demo;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class Test9 {
public static void main(String[] args) throws Exception{
ArrayList<Integer> list=new ArrayList<Integer>();
Method method=list.getClass().getMethod("add", Object.class);
method.invoke(list, "java反射机制实例。");
System.out.println(list.get(0));
}
}
结果如下:
1
java反射机制实例。
通过反射取得并修改数组的信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.example.demo;

import java.lang.reflect.Array;

public class Test9 {
public static void main(String[] args) throws Exception {
int[] temp = { 1, 2, 3, 4, 5 };
Class<?> demo = temp.getClass().getComponentType();
System.out.println("数组类型: " + demo.getName());
System.out.println("数组长度 " + Array.getLength(temp));
System.out.println("数组的第一个元素: " + Array.get(temp, 0));
Array.set(temp, 0, 100);
System.out.println("修改之后数组第一个元素为: " + Array.get(temp, 0));
}
}
结果如下:
1
2
3
4
数组类型: int
数组长度 5
数组的第一个元素: 1
修改之后数组第一个元素为: 100
通过反射机制修改数组的大小
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
33
34
35
36
package com.example.demo;

import java.lang.reflect.Array;

public class Test9 {
public static void main(String[] args) throws Exception {
int[] temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int[] newTemp = (int[]) arrayInc(temp, 15);
print(newTemp);
String[] atr = { "a", "b", "c" };
String[] str1 = (String[]) arrayInc(atr, 8);
print(str1);
}

// 修改数组大小
public static Object arrayInc(Object obj, int len) {
Class<?> arr = obj.getClass().getComponentType();
Object newArr = Array.newInstance(arr, len);
int co = Array.getLength(obj);
System.arraycopy(obj, 0, newArr, 0, co);
return newArr;
}

// 打印
public static void print(Object obj) {
Class<?> c = obj.getClass();
if (!c.isArray()) {
return;
}
System.out.println("数组长度为: " + Array.getLength(obj));
for (int i = 0; i < Array.getLength(obj); i++) {
System.out.print(Array.get(obj, i) + " ");
}
System.out.println();
}
}
结果如下:
1
2
3
4
数组长度为: 15
1 2 3 4 5 6 7 8 9 0 0 0 0 0 0
数组长度为: 8
a b c null null null null null
将反射机制应用于工程模式
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.example.demo;

interface fruit {
public abstract void eat();
}

class Apple implements fruit {
public void eat() {
System.out.println("Apple");
}
}

class Orange implements fruit {
public void eat() {
System.out.println("Orange");
}
}

class Factory {
public static fruit getInstance(String ClassName) {
fruit f = null;
try {
f = (fruit) Class.forName(ClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
}

/**
* 对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。 当我们添加很多的子类的时候,会很麻烦。
* Java 工厂模式可以参考
* http://baike.xsoftlab.net/view/java-factory-pattern
*
* 现在我们利用反射机制实现工厂模式,可以在不修改工厂类的情况下添加任意多个子类。
*
* 但是有一点仍然很麻烦,就是需要知道完整的包名和类名,这里可以使用properties配置文件来完成。
*
* java 读取 properties 配置文件 的方法可以参考
* http://baike.xsoftlab.net/view/java-read-the-properties-configuration-file
*
* @author xsoftlab.net
*/

public class Test10 {
public static void main(String[] args) {
fruit f = Factory.getInstance("com.example.demo.Apple");
if (f != null) {
f.eat();
}
}
}
结果如下:
1
Apple
0x07 使用java反射的优势与弊端
反射虽然很灵活,能够使得写的代码,变的大幅精简,所以在用的时候,一定要注意具体的应用场景,反射的优缺点如下:
优点:
(1) 能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
(2) 与Java动态编译相结合,可以实现无比强大的功能
缺点:
(1) 使用反射的性能较低
(2) 使用反射相对来说不安全
(3) 破坏了类的封装性,可以通过反射获取这个类的私有方法和属性
任何事物,都有两面性,反射的优点,也同是就是它的缺点,所以,没有好与坏,只有最合适的场景。
0x08 参考链接
https://www.w3cschool.cn/java/java-reflex.html