首先我们来了解一下Java的反射机制:在运行状态下,我们能够知道一个类有哪些属性和方法,对于一个对象来说我们能够调用他的属性和方法,这种获取一个类的信息和调用一个对象的属性和方法就是Java的反射机制,我们要解刨一个类,就必须先得到他的字节码对象,通过使用class类的方法才能对这个类进行剖析,所以我们要先获得每一个字节码文件对应的class类型的对象。
获得class对象有三种方法:
1.通过object的getclass()方法
public class MyDemo {
public static void main(String[] args) {
Student student = new Student();
Class<? extends Student> aClass = student.getClass();
System.out.println(aClass);
}
}
编译运行可得到
class org.jimmy.demo5.Student
2.通过class静态属性,每个类都有自己的一个class属性
public class MyDemo2 {
public static void main(String[] args) {
Class<Student> aClass = Student.class;
System.out.println(aClass);
}
}
同样的编译运行可得到
class org.jimmy.demo5.Student
3.通过class的静态forname()方法,通过一个类的全路径,就可以获得该类的字节码对象
public class MyDemo3 {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> aClass = Class.forName("org.jimmy.demo5.Student");
System.out.println(aClass);
}
}
一个类的全路径:就是这个类带包名的写法,编译运行得到:
class org.jimmy.demo5.Student
在了解的上述内容后,我们可以试着通过反射来获取一个类的构造器
一个类的构造方法,可以是有参的无参的,可以是公有的也可以是私有的,所以获取不同的构造方法也是不一样的。
1.getConstructors()方法和getDeclaredConstructors()方法
这两个方法可以获得一个类的构造方法,但前者获得的构造方法不是私有的,后者是获取所有包含私有的构造方法,我们新建一个student类,里面包含了私有和公有的构造方法,以及无参和有参的构造方法。
public class Student {
public Student() {
System.out.println("该类的无参构造方法");
}
public Student(String name,int age){
System.out.println("该类的有参构造方法");
}
private Student(String name){
System.out.println("该类的私有构造方法");
}
}
那么获取该类的构造方法
public class MyDemo4 {
public static void main(String[] args) {
Class<Student> aClass = Student.class;
Constructor<?>[] constructors = aClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("-----------------------------------");
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
}
}
编译运行得到:
public org.jimmy.demo5.Student()
public org.jimmy.demo5.Student(java.lang.String,int)
-----------------------------------
public org.jimmy.demo5.Student()
public org.jimmy.demo5.Student(java.lang.String,int)
private org.jimmy.demo5.Student(java.lang.String)
我们也可以获取单个的构造方法
利用getConstructor()或者 getConstructor(Class<?>… parameterTypes)
后者是获取带参数的构造方法
parameterTypes就是指该参数的class类型
同样的getDeclaredConstructor()方法就是获得私有的构造方法,
public class MyDemo5 {
public static void main(String[] args) throws NoSuchMethodException {
Class<Student> aClass = Student.class;
Constructor<Student> constructor = aClass.getConstructor(String.class, int.class);
System.out.println(constructor);
}
}
public org.jimmy.demo5.Student(java.lang.String,int)
这就是如何获得一个类的构造方法。
而创建一个类的实例化对象,就需要调用该类的构造方法,因此,通过刚才获得的构造方法,我们就可以创建该类的实例化对象。
通过调用newInstance()方法类创建实例化对象
public class MyDemo6 {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<Student> aClass = Student.class;
Constructor<Student> constructor = aClass.getConstructor();
Student student = constructor.newInstance();
}
}
通过该类的无参构造方法来获取一个实例对象
该类的无参构造方法
在这里,私有的构造方法来创建对象就需要注意了,因为该构造方法是私有的,外界是不能访问的,因此我们要先取消语法检测,setAccessible(type)方法可以取消语法检测,里面的参数为布尔值,为true即为取消语法检测,我们来用代码体会一下:
public class MyDemo6 {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<Student> aClass = Student.class;
Constructor<Student> declaredConstructor = aClass.getDeclaredConstructor(String.class);
declaredConstructor.setAccessible(true);
declaredConstructor.newInstance("小明");
}
}
该类的私有构造方法
获取了构造方法,创建了该对象,我们来尝试获取变量和设置变量,首先设置student类的变量
public class Student {
private String name;
int age;
public String sex;
}
接下来获取变量和设置变量
public class MyDemo3 {
public static void main(String[] args) throws Exception {
Class<Student> aClass = Student.class;
Field sex = aClass.getField("sex");
Student student = aClass.newInstance();
sex.set(student,"男");//给哪个对象设,第一个参数就是该对象
Object o = sex.get(student);//通过字段对象来获取字段的值
System.out.println(o);
}
}
同样的,获取私有的变量也有getDeclaredField()方法,同样的,在设置变量的时候,我们也要取消语法检测。这里不做代码展示。
获取完变量,我们自然要来试一下获取方法,这和前面两个又是同样的套路;
getMethods()
getDeclaredMethods()
getDeclaredMethod()
getMethod()
获取单个的方法时,括号里面的参数为该方法名,如果该方法有参数,后面为该参数的class类型
那么如何调用该方法呢?
Object invoke (Object obj, Object…args),invoke方法,就可以实现,
如果实现的方法没有参数,invoke方法括号里面即为该对象名,如果有参数,后面紧跟该参数的class类型。我们来用代码来体会。
学生类:
public class Student {
public void show() {
System.out.println("我是一个无参数的公共方法");
}
public String show2() {
System.out.println("我是一个无参数的公共方法,有返回值");
return "一个字符串";
}
public void test(String name) {
System.out.println("我是一个有参数的公共方法" + name);
}
private void myMethod(String name, int age) {
System.out.println("我是一个有参数的私有方法" + name + "==" + age);
}
}
public class MyDemo {
public static void main(String[] args) throws Exception{
Class<?> aClass = Class.forName("org.jimmy.demo4.Student");
Object obj = aClass.newInstance();
Method method = aClass.getMethod("show");
method.invoke(obj);
Method show2 = aClass.getMethod("show2");
Object invoke = show2.invoke(obj);//该方法有返回值,invoke方法返回的就是返回值
System.out.println(invoke);
Method test = aClass.getMethod("test", String.class);
Object show = test.invoke(obj, "测试");
Method declaredMethod = aClass.getDeclaredMethod("myMethod",String.class, int.class);
declaredMethod.setAccessible(true);//取消语法检测
declaredMethod.invoke(obj, "小明", 20);
}
}
编译运行可得到:
我是一个无参数的公共方法
我是一个无参数的公共方法,有返回值
一个字符串
我是一个有参数的公共方法测试
我是一个有参数的私有方法小明==20