JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
如何获取一个字节码文件(编译后得到的.class后缀的文件)对应的Class对象?
有三种方式:
方式1:对象已经存在的情况下获取
方式2:直接通过类名的方式进行获取
方式3:调用Class类中的静态方法获取字节码文件的Class对象
参考代码如下:注意:一个字节码文件,在内存中只会存在一个对应的Class类对象
public class ClassDemo1 {
public static void main(String[] args) throws Exception{
//方式1:对象已经存在的情况下获取
Student s1 = new Student();
Class extends Student> c12 = s1.getClass();
Class extends Student> c22 = s1.getClass();
System.out.println(c12==c22);
//方式2:直接通过类名的方式进行获取
Class c1 = Student.class;
Class c2 = Student.class;
System.out.println(c1==c2);
System.out.println(c12==c2);
//方式3:调用Class类中的静态方法获取字节码文件的Class对象
//TODO:其中com.day21是包名
Class> c1 = Class.forName("com.day21.Student");
Class> c2 = Class.forName("com.day21.Student");
System.out.println(c1==c2);
}
}
获取构造方法
getConstructors:获取所有的公共构造方法
getDeclaredConstructors:获取所有构造方法(私有+公共)
创建对象
newInstance()
con.newInstance(“光头强",13);
获取所有成员:
getFields:获取所有公共的成员变量
getDeclaredFields:
获取单个成员:
getField :获取公共的单个成员变量
getDeclaredField :获取私有的单个成员变量
修改成员的值:set(Object obj,Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
获取所有方法:
getMethods :获取类的所有公共成员方法以及父类的公共成员方法
getDeclaredMethods:获取本类中所有的成员方法(公共+私有的成员方法)
获取单个方法
getMethod:获取类中的某个公共成员方法
getDeclaredMethod:获取类中私有成员方法
暴力访问
method.setAccessible(true):获取私有成员变量或方法的时候,出现IllegalAccessException(拒绝访问的时候,抛出的异常),就可以在访问之前加上这段代码,就能正常访问并获取,如果加上这段代码后运行,还是报同样的IllegalAccessException错误,就去检查参数类型和传参问题
获取:
获取构造方法的时候,要传入定义的参数类型(如String.class),获取成员方法的时候,要传入 "成员方法名"(如果有参数,也加上定义参数的类型),获取成员变量的时候在括号要传入"变量名称"。所有获取的变量和方法都可以直接打印
使用:
1、使用构造方法的时候,可以用上面获取的构造方法所设的对象c1来new一个对象,如Object o = c1.newInstance();可以在这个括号里面传参数名称(如果有参数的话),就可以直接打印对象,得到想要的对象内容。
2、使用成员方法的时候,可以用获取的成员方法所设的对象名调用invke(o)方法使用,有参数要在括号写一个参数名称,o代表构造方法的对象
3.使用成员变量,也是用获取的成员变量所设的对象名,调用set()进行设值,如name.set(o,"新值") ,o也是构造方法的对象名
(使用私有的变量方法可能会出现的问题):
使用私有的构造方法、成员方法、成员变量,出现IllegalAccessException拒绝访问报错,先设置访问权限 :E.setAccessible(true);我们知道要使用什么得先去获取才能使用,这个E代表获取的构造方法/成员方法/成员变量的对象名,然后如果设置后还是报错,就看使用的构造方法和成员方法里面是否传入了参数名称,还需注意特殊的:尤其是获取构造方法的时候参数类型是否完全一样,比如Integer.class和int.class是不同的类型。
详细代码见下:
//反射机制,获取和使用类的构造方法,成员变量和成员方法
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
public class Reflection {
public static void main(String[] args) throws Exception{
Class> studentClass = Class.forName("day22_8_22.Student");
//通过字节码对象获取类的无参公共构造方法
Constructor> c1 = studentClass.getConstructor();
// System.out.println(c1);
//获取有参的私有方法,要传参数类型
Constructor> d1 = studentClass.getDeclaredConstructor(String.class);
// System.out.println(d1);
//获取所有的构造方法(公共+私有)包括有参和无参
Constructor>[] ds = studentClass.getDeclaredConstructors();
for (Constructor> d : ds) {
System.out.println(d);
}
//获取所有的成员变量
Field[] df = studentClass.getDeclaredFields();
for (Field field : df) {
System.out.println(field.toString());
}
//接下来通过获取的构造方法,来创建对象并使用
//通过构造方法来new Student对象
Object o = c1.newInstance();
System.out.println(o);
//通过私有构造构造方法new对象的时候,有参数要传入参数名称,发现访问拒错误
//所以我们要暴力执行
d1.setAccessible(true);
Object o1 = d1.newInstance("帅哥");
System.out.println(o1);
System.out.println("======================================================");
//获取单个公共成员变量,要给出变量名
Field f1 = studentClass.getField("stuNumber");
System.out.println("变量名为:"+f1.getName());
//获取单个私有变量,也要给变量名
Field name = studentClass.getDeclaredField("name");
System.out.println("私有变量名:"+name.getName());
//使用私有成员变量设值,出现IllegalAccessException,也要暴力执行或者检查参数类型
name.setAccessible(true);
name.set(o,"悟空");//在某个对象里面设值,下面就直接打印对象,就能看到设值
System.out.println(o);
//获取所有的成员变量,包括(公共+私有),直接用Arrays.toString转字符串不好看,遍历获取单个变量名称
Field[] dfs = studentClass.getDeclaredFields();
for (Field field : dfs) {
System.out.println("所有变量名为:"+field.getName());
}
System.out.println("======================================================");
//先获取全部参数的私有构造方法,再使用单个成员变量,进行重新设值
Constructor> constructor = studentClass.getDeclaredConstructor(String.class,Double.class,String.class,int.class);
//获取有参构造方法的时候,要传入参数类型,当创建对象,使用构造方法要传入参数名称
//NoSuchMethodException报错,因为Integer.class和int.class不一样,所以参数类型要完全和类里面设定的一样
constructor.setAccessible(true);
Object o2 = constructor.newInstance("123456",177.7,"猪八戒",36);
System.out.println(o2);
//获取私有成员方法,先传方法名,有参数再传参数类型
Method dm = studentClass.getDeclaredMethod("fun", String.class);
//获取这个方法可以打印,但是使用的时候,因为方法里面包含了打印,就无需打印
System.out.println(dm);
//获取所有成员方法(注意:包括了父类的公共构造方法)
Method[] ms = studentClass.getMethods();
System.out.println(Arrays.asList(ms));
//获取本类的所有成员方法,包括(公共+私有)
Method[] declaredMethods = studentClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("所有的成员方法为:"+declaredMethod.getName());
}
//使用私有成员有参方法,获取后用invoke(),括号里面传入对象名
Method dd = studentClass.getDeclaredMethod("fun", String.class);
dd.setAccessible(true);
dd.invoke(o,"你好");//该方法带参数,还需要传参
//使用公共成员无参方法,获取后用invoke(),括号里面传入对象名
Method method = studentClass.getMethod("fun1");
method.invoke(o);
}
}