史上最全讲解:JAVA中的反射
反射概述
反射机制:将类中的各个部分部分封装成其他的对象(构造方法对象们,方法对象们,属性们)
JAVA代码在计算机中经历的三个阶段:
反射的过程:
1.source源代码阶段:
java文件包括三大部分:成员属性,构造方法,成员方法.被编译成class字节码文件,此时.java文件和.class文件还是存在于硬盘上,没有被加载进内存
2.Class类对象阶段:
类加载器,ClassLoader将.class文件加载到内存上.Class类对象专门描述.class字节码文件的(java中一切皆对象),可以将Class类理解为一般的普通类,比如String类,自定义的Person类,它也具有一般类该具有的特性.
三大部分:成员属性封装成为Field对象,成员方法封装成为Method对象,构造方法封装成为Constructor对象
3.runtime运行时阶段:
new对象 就会在堆内存中生成相应的对象.
同一个字节码文件.class文件在一次程序运行过程中只能够被加载一次,一个类对应的Class类对象只有一个
不论哪一种方式获取的类对象都是同一个
**
实例对象就通过Class对象来创建的
如果说类是对对象的抽象和集合的话,那么Class类就是对类的抽象和集合。
反射发生在程序运行时期!
简单案例介绍: 想要实现一个类中的方法,但是目前不确定**是哪个类,比如:有一个Teacher类里面有一个teach方法,还有一个Student类,里面有一个study方法,想调用Teacher类里面的teach方法,再调用Student类里面的study方法,那么在一个main方法中必须先创建一个Teacher类的对象teacher调用teach方法,再将其注释掉,再创建一个Student类的对象,调用study方法 .
package com.shsxt.reflector;
public class ReflectDemo03 {
public static void main(String[] args) {
Teacher02 teacher = new Teacher02();
teacher.teach();
//Student02 student = new Student02();
//student.study();
}
}
class Student02 {
public static void study(){
System.out.println("学习");
}
}
class Teacher02 {
public static void teach(){
System.out.println("教书");
}
}
反射的好处:
1.可以在程序运行过程中操作这些对象
2.解耦
利用反射解决上述问题:可以将类名和方法名放在properties配置文件中
- 使用反射的步骤:
1.获取Class类对象
2.根据Class类对象获取构造方法创建对象
3.根据创建的对象获取方法(私有和公有)
4.根据创建的对象获取属性(私有和公有)
获取Class类对象
一旦加载类,就会存在一个当前类的Class对象,Class只有一个,存着这个类的所有内容如果一旦获取到一个类的Class对象,就能够对这个进行任何操作
- Class.forName(包名+类名);在源文件阶段将class文件加载进内存
- 类名.class Class类对象阶段
- 对象.getClass(); 运行时阶段 new完对象后获取对象对应的类的Class类对象
package com.shsxt.reflector;
public class ReflectDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
//类名.class
Class cls1 = String.class;
//对象.getclass
Class cls2 = "哈哈哈哈".getClass();
//Class.forname("报名+类名")
Class cls3 = Class.forName("java.lang.String");
Class cls4 = int.class;
System.out.println(cls4);//int
Class cls5 = Integer.class;
System.out.println(cls1==cls2);//true
System.out.println(cls1==cls3);//true
System.out.println(cls4==cls5);//false
System.out.println(cls1.getSuperclass());//父类的包名+类名 class java.lang.Object
System.out.println(int.class==Integer.class);//false
System.out.println(int.class==Integer.TYPE);//true
}
}
获取构造方法
Constructor getConstructor(Class<?>… parameterTypes) 获取指定公共的构造器
Constructor<?>[] getConstructors() 获取所有的公共的构造器
Constructor getDeclaredConstructor(Class<?>… parameterTypes) 任意权限的其中一个
Constructor<?>[] getDeclaredConstructors() 任意权限的所有的构造器
创建对象的两种方法:
1.没有参数的对象(即空构造方法),可以有两种方式,Class中的newInstance()方法,默认根据表示的类型中的空构造创建对象.或者使用构造器中的newInstance()方法
2.创建带参数的对象,使用constructor构造器中的newInstance(参数)方法
[注意]私有构造器需要开放权限setAccessible()
获取方法
- 如何获取类中的方法
Method getMethod(String name, Class<?>… parameterTypes) 公共的方法中某一个
Method[] getMethods() 公共的方法中所有的
Method getDeclaredMethod(String name, Class<?>… parameterTypes) 所有方法中的某一个
Method[] getDeclaredMethods() 所有方法中的所有的 - 调用方法
Object invoke(Object obj, Object… args)
获取属性
- 反射操作属性
Field getDeclaredField(String name)
Field[] getDeclaredFields()
Field getField(String name)
Field[] getFields() - 为属性赋值
set(对象名,参数值)
field.set(user,“王五”) - 获取属性的值
field.get(user)
package com.shsxt.reflector;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
public class ReflectDemo02 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException, IllegalArgumentException {
testConstructor(Student.class);
testMethod(Student.class);
testField(Student.class);
}
//操作属性
public static void testField(Class cls) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException{
Field field = cls.getDeclaredField("name");
Student stu = (Student)cls.newInstance();
field.set(stu, "王五");
System.out.println(field.get(stu));
}
//操作方法
public static void testMethod(Class cls) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
Method method = cls.getMethod("study");
//使用成员方法 必须创建对象
Student stu = (Student)cls.newInstance();//根据class类对象创建无参对象,需要向下转型,强制类型转换,如果带有泛型即可不用转换
method.invoke(stu);//调用没有参数的方法
//System.out.println(cls.newInstance());
//method.invoke(stu, 1);//不可以这样操作 因为成员方法还有从 父类继承过来的,即有多个方法的参数为一个
Method[] methods = cls.getMethods();
System.out.println(Arrays.toString(methods));
}
//操作构造器
public static void testConstructor(Class cls) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException {
Constructor cons = cls.getConstructor(String.class);
Constructor[] arr = cls.getDeclaredConstructors();
for(Constructor c:arr){
System.out.println(c);
}
Object obj = cls.newInstance();//通过class类对象直接创建空参对象
System.out.println(obj);
Student stu = (Student)cons.newInstance("hhhh");//通过构造器创建一参对象 并且需要向下转型,强制类型转换 如果创建构造器的时候给定了泛型不用类型转换
Student stu3 = (Student)arr[0].newInstance();//通过构造器创建空参对象 需要向下转型,强制类型转换
System.out.println(stu3);
arr[2].setAccessible(true);
Student stu2 = (Student)arr[2].newInstance("sss","111",123);//通过多参构造器创建多参对象 需要向下转型,强制类型转换
arr[2].setAccessible(false);
System.out.println(stu2);
}
}
class Student {
public String name;
public String sex;
public int age;
private Student(String name, String sex, int age) {
super();
this.name = name;
this.sex = sex;
this.age = age;
}
public Student(String name) {
super();
this.name = name;
this.sex = sex;
this.age = age;
}
public Student() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", sex=" + sex + ", age=" + age + "]";
}
public static void study() {
System.out.println("学习");
}
public static void sleep(int i) {
System.out.println("我会睡觉"+i);
}
}