反射:框架设计的灵魂
- 框架:办成品的软件。可以在框架的基础上进行软件的开发 ,简化代码
- 反射:将类的各个组成部分封装为其他的对象,这就是反射机制
- 好处:可以在运行程序的过程中操作对象,比如在IDEA中的方法提示就是通过反射去实现的,IDEA程序在运行着,去将加载到内存中的方法调用其class 类对象的 获取方法名称的方法就可以把方法列出来
- 可以解耦合,提高程序的可扩展性
分析:
- xxx.java 通过javac编译出行 xxx.class
- xxx.class 文件比较重要的组成部分 成员变量 构造方法 成员方法 (Source源代码阶段)
- jvm 通过类加载器加载(ClassLoader对象)将xxx.class 文件加载到内存中 class 对象 (用来描述xxx.class文件)
- class 类对象的组成部分 成员变量 Field[] fields对象 构造方法 Constructor[] cons 成员方法Method[] methods
- class 类对象可以对类进行初始
获取class对象的方式
Person.java
package entiy;
public class Person {
private String name;
private int age;
public Person() {}
public Person(String name, int age) {this.name = name;this.age = age;}
public String getName() {return name;}
public void setName(String name) {this.name = name; }
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
-
Class.forName(“全类名”) //当在Person.class 还没有加载到内存中的时候使用这种方法获取class 对象
*多使用于将全类名放在配置文件,读取文件加载类
public static void main(String[] args) throws ClassNotFoundException { Class<?> aClass = Class.forName("entiy.Person"); System.out.println(aClass);//class entiy.Person }
-
Person.class //在Person.class 已经加载到内存中的时候使用这种方式获取class 对象
*使用于参数的传递
Class<Person> personClass = Person.class; System.out.println(personClass);//class entiy.Person
-
personObj.getClass() //在有person 对象的时候通过getClass方法获取
用于获取字节码文件
Person person = new Person(); Class<? extends Person> personClass = person.getClass(); System.out.println(personClass);//class entiy.Person
分析
public static void main(String[] args) throws ClassNotFoundException { Class<?> personClass1 = Class.forName("entiy.Person"); System.out.println(personClass1);//class entiy.Person Class<Person> personClass2 = Person.class; System.out.println(personClass2);//class entiy.Person Person person = new Person(); Class<? extends Person> personClass3 = person.getClass(); System.out.println(personClass3);//class entiy.Person System.out.println(personClass1==personClass2);//true System.out.println(personClass1==personClass3);//true } /** * 对于同一个字节码文件.class在一次程序运行的过程中只会被加载一次,不论通过那种方法获取到的class对象都是同一个 **/
使用Class 对象的功能
-
Persion.java
-
public class Person { private String name; private int age; private String a; protected String b; String c; public String d; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
获取成员变量对象Field
-
Field[] getFields() //获取所有pulic修饰的成员变量
-
public static void main(String[] args) throws Exception { Person person = new Person(); //获取person class 对象 Class<? extends Person> personClass = person.getClass(); //获取Person 类对象的所有public修饰的成员变量 Field[] fields = personClass.getFields(); for (Field field : fields) { System.out.println(field);// } }
-
Field getFields(String name) //获取指定 名称的成员变量对象
-
public static void main(String[] args) throws Exception { Person person = new Person(); //获取person class 对象 Class<? extends Person> personClass = person.getClass(); //获取public 修饰的d成员变量 Field dField = personClass.getField("d"); //获取person对象成员变量的属性值 Object dValue = dField.get(person); //null 因为person对象d变量没有初始化 System.out.println(dValue); //给person对象的成员变量设置值 dField.set(person,"d的值"); //输出结果:d的值 因为使用 dField.set(person,"d的值");给person对象的d成员变量设置值了 System.out.println(dField.get(person)); }
-
Field[] getDeclaredFields() throws SecurityException //获取所有的成员变量对象
-
public static void main(String[] args) throws Exception { Person person = new Person(); //获取person class 对象 Class<? extends Person> personClass = person.getClass(); //获取任意修饰符号的成员变量对象 Field[] declaredFields = personClass.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); } /** * private java.lang.String entiy.Person.name * private int entiy.Person.age * private java.lang.String entiy.Person.a * protected java.lang.String entiy.Person.b * java.lang.String entiy.Person.c * public java.lang.String entiy.Person.d */ }
-
Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException //获取任意修饰符的指定名称的成员变量对象
-
public static void main(String[] args) throws Exception { Person person = new Person(); //获取person class 对象 Class<? extends Person> personClass = person.getClass(); //获取name成员变量对象 Field nameField = personClass.getDeclaredField("name"); /** * name属性不是public 没有这个操作的话 会抛出 java.lang.IllegalAccessException * nameField.setAccessible(true) 这种方式叫暴力反射 */ nameField.setAccessible(true); //null System.out.println(nameField.get(person)); //为person 对象name 设置值 nameField.set(person,"小神"); //小神 System.out.println(nameField.get(person)); }
获取构造方法对象Constructor
-
Constructor<?>[] getConstructors() throws SecurityException //获取字节码文件对象的所有public 修饰的构造方法
-
public static void main(String[] args) throws Exception { //获取person class 对象 Class<Person> personClass = Person.class; //获取public 修饰的所有构造方法对象 Constructor<?>[] constructors = personClass.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(constructor); } /** * public entiy.Person() * public entiy.Person(java.lang.String,int) */ }
-
Constructor getConstructor(Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException//获取的指定的public的构造方法
-
public static void main(String[] args) throws Exception { //获取person class 对象 Class<Person> personClass = Person.class; //获取public无参的构造方法对象 Constructor<? extends Person> constructor1 = personClass.getConstructor(); //public entiy.Person() System.out.println(constructor1); //获取public 指定参数列表的 操作方法对象 Constructor<? extends Person> constructor2 = personClass.getConstructor(String.class, int.class); //public entiy.Person(java.lang.String,int) System.out.println(constructor2); }
使用构造函数对象 创建实例对象
//使用constructor1 无参数的构造方法对象进行person对象的创建 Person person1 = constructor1.newInstance(); //Person{name='null', age=0} System.out.println(person1); //使用constructor2 有参数的构造方法对象进行person对象的 Person person2 = constructor2.newInstance("小神", 1); //Person{name='小神', age=1} System.out.println(person2);
-
Constructor<?>[] getDeclaredConstructors() throws SecurityException //获取所有的构造方法对象
-
public static void main(String[] args) throws Exception { //获取person class 对象 Class<Person> personClass = Person.class; //获取所有的构造方法对象 Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } /** * public entiy.Person() * public entiy.Person(java.lang.String,int) */ }
-
Constructor getDeclaredConstructor(Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException
//获取指定参数列表的构造方法对象
-
public static void main(String[] args) throws Exception { //获取person class 对象 Class<Person> personClass = Person.class; //获取指定的参数构造方法对象 Constructor<Person> declaredConstructor = personClass.getDeclaredConstructor(String.class,int.class); //public entiy.Person(java.lang.String,int) System.out.println(declaredConstructor); }
-
注意:
-
当想使用非public的构造方法进行对象的创建的时候 需要调用 ConstructorObj.setAccessible(true) 进行暴力反射
-
在获取字节码文件对象只会 可以直接调用 objClass.newInstance() 使用无参数构造方法对象进行实例的创创建
-
public static void main(String[] args) throws Exception { //获取person class 对象 Class<Person> personClass = Person.class; //使用无参数的构造方法对象进行初始 Person person = personClass.newInstance(); }
-
获取成员方法对象 Method
-
Method[] getMethods() throws SecurityException //获取所有的public的成员方法(包括继承得来的)
-
public static void main(String[] args) throws Exception { Person personObj = new Person(); //获取Person Class对象 Class<? extends Person> personClass = personObj.getClass(); //获取personClass 所有的public 修饰的成员方法对象 Method[] methods = personClass.getMethods(); for (Method method : methods) { System.out.println(method); } /** * public java.lang.String entiy.Person.toString() * public java.lang.String entiy.Person.getName() * public void entiy.Person.setName(java.lang.String) * public int entiy.Person.getAge() * public void entiy.Person.setAge(int) * public final void java.lang.Object.wait() throws java.lang.InterruptedException * public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException * public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException * public boolean java.lang.Object.equals(java.lang.Object) * public native int java.lang.Object.hashCode() * public final native java.lang.Class java.lang.Object.getClass() * public final native void java.lang.Object.notify() * public final native void java.lang.Object.notifyAll() */ }
-
Method getMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException
获取指定的名称和参数列表的public修饰的对象
public static void main(String[] args) throws Exception { Person personObj = new Person(); //获取Person Class对象 Class<? extends Person> personClass = personObj.getClass(); //获取personClass的 getName 方法对象 Method getNameMethod = personClass.getMethod("getName"); //public java.lang.String entiy.Person.getName() System.out.println(getNameMethod); //通过getName方法对象(Method) 来执行personObj 对象的getName方法 Object name = getNameMethod.invoke(personObj); //null System.out.println(name); //获取personClass的 setName 方法对象 Method setNameMethod = personClass.getMethod("setName", String.class); //通过setName方法对象(Method) 来执行personObj 对象的setName方法 setNameMethod.invoke(personObj,"小神"); //小神 System.out.println(getNameMethod.invoke(personObj)); }
- Object invoke(Object obj, Object… args) 用于对成员变量方法的执行
-
Method[] getDeclaredMethods() throws SecurityException //获取所有的成员方法对象
//获取所有的成员方法对象 Method[] declaredMethods = personClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); }
-
Method getDeclaredMethod(String name, Class<?>… parameterTypes) //
-
//获取指定名称和参数列表的成员方法对象 Method getNameDeclaredMethod = personClass.getDeclaredMethod("getName"); Method setNameDeclaredMethod = personClass.getDeclaredMethod("setName", String.class);
注意:
- 在使用 Method 的 invoke 方法调用 对象的方法的时候,当方法不是 public 的时候 需要先调用 setAccessible(true) 进行暴力反射 然后才可以调用
获取类名
-
public static void main(String[] args) throws Exception { Class<Person> personClass = Person.class; System.out.println(personClass.getName()); }
反射的的小dome
说明
- 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
- 在程序中去加载配置文件
- 使用反射技术将类文件加载进内存
- 创建对象
- 调用方法
准备工作
-
在entiy 包下创建 一个Student类
package entiy; public class Student { public void sleep(){ System.out.println("睡觉..."); } }
-
在src 目录下创建一个pro.properties 文件
classPath="entiy.Student" methodName="sleep"
具体实现
public class ReflexTest {
public static void main(String[] args) throws Exception{
//1 加载配置文件
Properties properties = new Properties();
//获取reflexTestClass的类文件对象
Class<ReflexTest> reflexTestClass = ReflexTest.class;
/**
* 获取类加载器 因为在执行程序的时候 ReflexTest.class会被ClassLoader加载到内存中
* 因为 ReflexTest.java 和 pro.properties 文件都是在src 下的所以可以使用加载ReflexTest.class 的ClassLoader 去加载资源文件
*
*/
//获取reflexTestClass的类加载器
ClassLoader classLoader = reflexTestClass.getClassLoader();
//获取 pro.properties 输入流
InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
//加载配置文件
properties.load(resourceAsStream);
String classPath = properties.getProperty("classPath");
String methodName = properties.getProperty("methodName");
System.out.println(classPath);
System.out.println(methodName);
//关闭输入流
resourceAsStream.close();
//加载Student 的类字节码文件生成字节码文件 对象
Class<?> studentClass = Class.forName(classPath);
//获取sleep方法对象
Method sleepMethod = studentClass.getDeclaredMethod(methodName);
//暴力反射 在这里其实不需要因为 sleep 方法是publlic 的
sleepMethod.setAccessible(true);
//创建Student 对象
Student studentObj=new Student();
//执行 studentObj 的sleep 方法
sleepMethod.invoke(studentObj);
}
}