java反射机制学习

java反射介绍

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。主要类包括以下

1
2
3
4
5
6
7
java.lang.Class;                

java.lang.reflect.Constructor; java.lang.reflect.Field;

java.lang.reflect.Method;

java.lang.reflect.Modifier;

Class作用是通过类加载机制把相关类加载到虚拟机并返回Object对象句柄
Field对Object的所有属性进行修改和访问
Method对Object的所有方法进行访问
Modifier返回类或者其成员的访问修饰符
直接建立例子学习,说明在代码注释上,哈哈

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
71
72
73
74
75
76
77
78
79
package reflect;

public class Student {

private int id = 1;

private int age = 23;

private String name = "Lin";

protected int gade = 80;

private Funning mFun = new Funning("pingpong");

String dec = "good student";

public static Student getInstance() {
return new Student();
}

public Student(int age, String name, Funning mFun) {
super();
this.age = age;
this.name = name;
this.mFun = mFun;
}

public void show() {
System.out.print(" id=" + id + " name = " + name + " age = " + age + " funning = " + mFun.getName() + " gede = "
+ gade + " " + " dec=" + dec);
}

public Student() {
super();

}

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;
}

public Funning getmFun() {
return mFun;
}

public void setmFun(Funning mFun) {
this.mFun = mFun;
}

private class Funning {
private String name;

public Funning(String fun) {
name = fun;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = 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
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
//通过绝对的类名路劲加载类
Class student_class = Class.forName("reflect.Student");
//获取类的指定构造函数,其中int类型的为int.class而不Integer.class,而对于构造函数里面含有内部类,Funning为Student的内部类,所以完整的类名路劲为Student的绝对路径“reflect.Student”+“$”+内部类名
Constructor student_constructor = student_class.getConstructor(int.class, String.class,Class.forName("reflect.Student$Funning"));
//内部类的加载
Class funning_class = Class.forName("reflect.Student$Funning");
//内部类的构造函数默认都有对其外部类的指针,所以构造函数的第一个参数默认值为其寄存的类
Constructor funning_constructor = funning_class.getConstructor(student_class,String.class);
//先实例化内部类还是先实例外部类需要根据实际情况分析,若外部类需要根据内部类的数据提供支撑先就先实例内部类,否则实例外部类
Object funning = funning_constructor.newInstance(null,"basketball");
Object student = student_constructor.newInstance(23,"LZH",funning);
if (student != null) {
//反射Sudent的方法show,getMethod返回的是修饰符为public的方法,getDeclaredMethod不区分修饰符
Method show = Student.class.getDeclaredMethod("show");
//运行Sudent的公有方法show
show.invoke(student);
}
} catch (Exception e) {
e.printStackTrace();
}
}

默认输出结果为

id=1 name = Lin age = 23 funning = pingpong gede = 80 dec=good student

1 访问私有类并改变其属性,把funning的输出由pingpong改成football

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
// Student.getInstance().show()
Class student_class = Class.forName("reflect.Student");
Method method = student_class.getDeclaredMethod("getInstance");
Object student = method.invoke(null);
if(student != null){
Method show = Student.class.getDeclaredMethod("show");
//Begin
Field mFun_field = student_class.getDeclaredField("mFun");
mFun_field.setAccessible(true); //设置私有变量为可读
Object mFun = mFun_field.get(student);
Field name_field = funning_class.getDeclaredField("name");
name_field.setAccessible(true);
name_field.set(mFun,new String("football")); //由默认的pingpong改为football
//End
show.invoke(student);
}
} catch (Exception e) {
e.printStackTrace();
}
}

输出结果为:

id=1 name = LZH age = 23 funning = football gede = 80 dec=good student

注意:
1 静态变量或方法的反射,直接在set(Object,Object)或者get(Object,Object)或者invoke(Object,…parameter)等第一个表示实例对象的参数设置为null就行
2 final变量的修饰由于在代码编译过程会直接被替换,比如

1
2
3
4
5
6
final int i = 1;
int j = i + i;

编译会回直接变成
final int i = 1;
int j = 1 + 1;

android 通过反射修改Toast的显示层级,由WindowManager.LayoutParams.TYPE_TOAST 提升到WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,即使锁屏也可以看到Toast提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
try{
Class toast_class = Class.forName("android.widget.Toast");
Class TN_class = Class.forName("android.widget.Toast$TN");
Method maketext = toast_class.getDeclaredMethod("makeText",Context.class,CharSequence.class,int.class);
Object mToast = maketext.invoke(null,getActivity(),tips,Toast.LENGTH_LONG);
Method show = toast_class.getDeclaredMethod("show");
Field mTN_field = toast_class.getDeclaredField("mTN");
mTN_field.setAccessible(true);
Object mTN = mTN_field.get(mToast);

Field lp_field = TN_class.getDeclaredField("mParams");
lp_field.setAccessible(true);
WindowManager.LayoutParams lp = (WindowManager.LayoutParams )lp_field.get(mTN);
lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
show.invoke(mToast);


}catch (Exception e){
e.printStackTrace();
}

所以final的属性的反射暂时发现是不能修改的,若有更好的方法欢迎大家提出,谢谢

文章目录
  1. 1. java反射介绍
    1. 1.1. 1 访问私有类并改变其属性,把funning的输出由pingpong改成football
    2. 1.2. android 通过反射修改Toast的显示层级,由WindowManager.LayoutParams.TYPE_TOAST 提升到WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,即使锁屏也可以看到Toast提示
,