反射的核心思想就是利用已经装载到内存的字节码获取类的对象的信息。
反射定义
反射是在运行时而非编译时,才知道要操作的类是什么,动态的获取类型的信息,比如接口信息、成员信息、方法信息、构造方法等,根据这些信息去创建对象、访问方法、访问或者修改成员变量。
Class类
每个已加载的类在内部都有一份类信息,每个对象都有指向他所属的类信息的引用。类信息对应的类就是java.lang.Class。Class是一个泛型类。
可通过三种途径获取Class对象。
- 通过类名获取
Class<Date> cls = Date.getClass;
- 通过类名直接加载获取
Class<?> cls = Class.forName("java.util.HashMap");
- 通过对象获取,getClass并不知道具体的类型, 所以返回的是Class<?>
Date date = new Date();
Class<?> clz = date.getClass();
使用Class对象操作字段信息
- 获取所有的public字段,包括父类的
- 获取本类中所有的字段,包括非public的,但不包括父类的
- 获取本类或者父类中指定名称的public字段
- 获取本类中声明的指定名臣的字段,包括非public的
- 获取字段的名称
- 获取字段的值
- 修改public字段的值
- 修改非public字段的值
public class Student {
private String name;
public String location;
public int age;
public Student(String name,String location,int age){
this.name = name;
this.location = location;
this.age = age;
}
private void run(){
System.out.println("this ist the private run method");
}
public String getName() {
System.out.println("this ist the public getName method");
return name;
}
public void setName(String name) {
System.out.println("this ist the public getName method");
this.name = name;
}
public String getLocation() {
System.out.println("this ist the public getLocation method");
return location;
}
public void setLocation(String location) {
System.out.println("this ist the public setLocation method. location = " + location);
this.location = location;
}
public int getAge() {
System.out.println("this ist the public getAge method");
return age;
}
public void setAge(int age) {
System.out.println("this ist the public setAge method");
this.age = age;
}
}
private static void getField() throws IllegalAccessException, InstantiationException {
Class<Student> clz = Student.class;
Student student = (Student) clz.newInstance();
Field[] fields = clz.getFields();
System.out.println("获取所有的public的字段,包括父类的");
for(Field field : fields){
System.out.println(field.getName());
}
System.out.println("*********************************************");
System.out.println("获取本类中所有的的字段,包括非public的,但不包括父类的");
fields = clz.getDeclaredFields();
for(Field field : fields){
System.out.println(field.getName());
}
System.out.println("*********************************************");
System.out.println("获取本类或者父类中指定名称的public字段,");
Field field = null;
try {
field = clz.getField("age");
System.out.println("字段名称 : " + field.getName());
System.out.println("获取字段的值");
System.out.println(field.get(student));
System.out.println("修改字段的值");
field.set(student,43565);
System.out.println(field.get(student));
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
System.out.println("*********************************************");
System.out.println("获取本类中声明的指定名称的字段");
try {
field = clz.getDeclaredField("name");
field.setAccessible(true);
System.out.println("字段名称 : " + field.getName());
System.out.println("修改私有字段的值");
field.set(student,"修改成功");
System.out.println(field.get(student));
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
输出为:
获取所有的public的字段,包括父类的
location
age
*********************************************
获取本类中所有的的字段,包括非public的,但不包括父类的
name
location
age
*********************************************
获取本类或者父类中指定名称的public字段,
字段名称 : age
获取字段的值
0
修改字段的值
43565
*********************************************
获取本类中声明的指定名称的字段
字段名称 : name
修改私有字段的值
修改成功
使用Class对象操作方法
- 获取本类和父类中所有的public方法
- 获取本类中声明的所有方法,包括非public的,但不包括父类的
- 获取本类和父类中指定名称的public方法
- 获取本类中已声明的指定名称的方法,包括非public的,但是不包括父类的
- public方法的调用
- 非public方法的调用
Class<Student> clz = Student.class;
Student student = null;
try {
student = (Student) clz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
System.out.println("************************");
System.out.println("返回所有的public方法,包括父类的");
Method[] methods = clz.getMethods();
for(Method method:methods){
System.out.println(method.getName());
}
System.out.println("************************");
System.out.println("返回本类中声明的所有方法,包括非public的,但不包括父类的");
methods = clz.getDeclaredMethods();
for(Method method:methods){
System.out.println(method.getName());
}
System.out.println("************************");
System.out.println("返回本类和父类中指定名称和参数类型的public方法");
try {
Method method1 = clz.getMethod("setLocation",String.class);
System.out.println(method1.getName());
method1.invoke(student,"这是地址");
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("************************");
System.out.println("返回本类中声明的指定名称和参数类型的方法,包括非public方法");
try {
Method method1 = clz.getDeclaredMethod("run");
System.out.println(method1.getName());
method1.setAccessible(true);
method1.invoke(student);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
运行结果为:
************************
返回所有的public方法,包括父类的
getName
getLocation
setName
setLocation
getAge
setAge
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
************************
返回本类中声明的所有方法,包括非public的,但不包括父类的
getName
run
getLocation
setName
setLocation
getAge
setAge
************************
返回本类和父类中指定名称和参数类型的public方法
setLocation
this ist the public setLocation method. location = 这是地址
************************
返回本类中声明的指定名称和参数类型的方法,包括非public方法
run
this ist the private run method
创建对象和构造方法
- 获取所有的public构造方法
- 获取所有的构造方法,包括非public的
- 获取指定参数类型的public构造方法
- 获取指定参数类型的构造方法,包括非public 的
- 通过public构造方法创建对象
- 通过非public方法创建对象
Class<Student> clz = Student.class;
System.out.println("*********************************************");
System.out.println("获取所有的public构造方法");
Constructor<Student>[] constructors = (Constructor<Student>[]) clz.getConstructors();
for(Constructor constructor: constructors){
System.out.println(constructor);
}
System.out.println("*********************************************");
System.out.println("获取所有的构造方法,包括非public的");
constructors = (Constructor<Student>[]) clz.getDeclaredConstructors();
for(Constructor constructor: constructors){
System.out.println(constructor);
}
System.out.println("*********************************************");
System.out.println("获取指定参数类型的public构造方法");
Constructor constructor = null;
try {
constructor = clz.getConstructor(String.class,String.class,int.class);
System.out.println(constructor);
Student s = (Student) constructor.newInstance("this is name","this is location",18);
System.out.println(s);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
System.out.println("*********************************************");
System.out.println("获取指定参数类型的构造方法,包括非public的");
constructor = null;
try {
constructor = clz.getDeclaredConstructor(String.class);
System.out.println(constructor);
constructor.setAccessible(true);
Student s = (Student) constructor.newInstance("this is name");
System.out.println(s);
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
运行结果为:
*********************************************
获取所有的public构造方法
public reflect.Student(java.lang.String,java.lang.String,int)
*********************************************
获取所有的构造方法,包括非public的
private reflect.Student(java.lang.String)
public reflect.Student(java.lang.String,java.lang.String,int)
*********************************************
获取指定参数类型的public构造方法
public reflect.Student(java.lang.String,java.lang.String,int)
Student{name='this is name', location='this is location', age=18}
*********************************************
获取指定参数类型的构造方法,包括非public的
private reflect.Student(java.lang.String)
Student{name='this is name', location='null', age=0}
- 通过入口类Class可以访问类的各种信息,比如字段、方法、构造方法、父类、接口、泛型信息等,也可以创建和操作对象、调用方法等。
- 借助反射,可以边写通用的,灵活的程序,但是一般不建议使用,一是因为反射更容易出现运行时错误,二是反射的性能相对低一些,在访问字段、调用方法前,反射先要查找对应的Field/Method,所以要慢一些。
网友评论