Java学习之反射
反射
作用:
把一个对象的成员变量、构造方法、成员函数(方法)转换为对象,然后进行动态调用。反射是框架设计的“灵魂”技术!
注意:
代码对象从创建到产生结果的过程中,要经历过编译,加载,运行,三个阶段。
编译:将 .Java 文件转换为字节码 .class 文件。(此时代码还存在于硬盘中)
加载:将 .class 文件加载到内存中,以便编辑器进行操作。
运行:使内存中的代码产生结果。
类加载器:
加载:
指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。
类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。
通过使用不同的类加载器,可以从不同来源加载类的二进制数据,通常有如下几种来源。
- 从本地文件系统加载class文件。
- 从JAR包加载class文件。
- 通过网络加载class文件。
把一个Java源文件动态编译,并执行加载。
类加载器通常无须等到“首次使用”该类时才加载该类,Java虚拟机规范允许系统预先加载某些类。
作用:
- 将硬盘上的Class文件加载到内存(方法区)以便后续的操作。
- 类加载器加载完成后会在堆区创建一个class对象,用来表示加载的“类结构”。
反射的第一步:获取到堆中的class对象
方法:
- Class.forName (全限定类名) 获取Class对象(然后cls.newInstance() 获取实例化对象),这种一般配合配置文件使用
- 类名.Class 获取响应类型的class对象,常用于参数列表用于传参
- 对象名.getClass() 用于方法内部,用于确定传入参数的具体类型
代码案例:
1 package com.reflex.bean; 2 3 public class Person { 4 private String Id; 5 public String name; 6 public String age; 7 protected String gender; 8 9 public String getId() { 10 return Id; 11 } 12 13 public String getName() { 14 return name; 15 } 16 17 public String getAge() { 18 return age; 19 } 20 21 public String getGender() { 22 return gender; 23 } 24 25 public void setId(String id) { 26 27 Id = id; 28 } 29 30 public void setName(String name) { 31 this.name = name; 32 } 33 34 public void setAge(String age) { 35 this.age = age; 36 } 37 38 public void setGender(String gender) { 39 this.gender = gender; 40 } 41 42 public Person(String id, String name, String age, String gender) { 43 44 Id = id; 45 this.name = name; 46 this.age = age; 47 this.gender = gender; 48 } 49 50 public Person() { 51 52 } 53 54 private void reflexDemo() { 55 System.out.println("this is a test!"); 56 } 57 58 @Override 59 public String toString() { 60 return "Person{" + 61 "Id='" + Id + '\'' + 62 ", name='" + name + '\'' + 63 ", age='" + age + '\'' + 64 ", gender='" + gender + '\'' + 65 '}'; 66 } 67 private void eat(){ 68 System.out.println("eat food!"); 69 } 70 }
1 package com.reflex.demo; 2 3 import com.reflex.bean.Person; 4 5 public class demo1 { 6 public static void main(String[] args) throws Exception { 7 // 1、 Class.forName (全限定类名) 获取Class对象(然后cls.newInstance() 获取实例化对象),这种一般配合配置文件使用 8 Class cls1 = Class.forName("com.reflex.bean.Person"); 9 // 2、 类名.Class 获取响应类型的clszz 对象,常用于参数列表用于传参 10 Class<Person> cls2 = Person.class; 11 // 3、 对象名.getClass() 用于方法内部,用于确定传入参数的具体类型 12 Person person = new Person(); 13 Class cls3 = person.getClass(); 14 System.out.println(cls1); 15 System.out.println(cls2); 16 System.out.println(cls3); 17 System.out.println(cls1 == cls2); 18 System.out.println(cls2 == cls3); 19 System.out.println(cls1 == cls3); 20 System.out.println(cls1.getName()); 21 System.out.println(cls1.getClassLoader()); 22 } 23 }
cls1 == cls2 == cls3 说明:无论通过这三种方法的哪一种方法,获取的Class对象有且只有一个!(class对象只创建一次)
反射的第二步:通过Class对象获取对应的构造函数、成员变量、成员方法对象
常用方法:
- Field getField(String name) 返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定公共成员字段。
- Field[] getFields() 返回一个包含 Field对象的数组, Field对象反映由该 Class对象表示的类或接口的所有可访问的公共字段。
- Field getDeclaredField(String name) 返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定声明字段。
- Field[] getDeclaredFields() 返回一个 Field对象的数组,反映了由该 Class对象表示的类或接口声明的所有字段。
- Method getMethod(String name, Class<?>... parameterTypes) 返回一个 方法对象,该对象反映由该 Class对象表示的类或接口的指定公共成员方法。
- Method[] getMethods() 返回一个包含 方法对象的数组, 方法对象反映由该 Class对象表示的类或接口的所有公共方法,包括由类或接口声明的对象以及从超类和超级接口继承的类。
- Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 Class对象。
- Method[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映由 Class对象表示的类或接口的所有声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承方法。
- Constructor<T> getConstructor(Class<?>... parameterTypes) 返回一个 Constructor对象,该对象反映由该 Class对象表示的类的指定公共构造函数。
- Constructor<?>[] getConstructors() 返回一个包含 Constructor对象的数组, Constructor对象反映了由该 Class对象表示的类的所有公共构造函数。
- Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor对象,该对象反映由此 Class对象表示的类或接口的指定构造函数。
- Constructor<?>[] getDeclaredConstructors() 返回反映由该 Class对象表示的类声明的所有构造函数的 Constructor对象的数组。
注意:可获取到public修饰的方法和属性(public),getDeclareXXX获取到对象中所有修饰符修饰的方法和属性(public,protect,private)。
代码案例:
1 package com.reflex.demo; 2 3 import java.lang.reflect.Constructor; 4 import java.lang.reflect.Field; 5 import java.lang.reflect.Method; 6 7 8 public class demo2 { 9 public static void main(String[] args) throws Exception { 10 //获取class对象 11 Class cls = Class.forName("com.reflex.bean.Person"); 12 String name = cls.getName(); 13 System.out.println(name); 14 //获取构造函数对象 15 System.out.println("---------分割线-----------"); 16 Constructor[] constructors = cls.getConstructors(); 17 for (Constructor con : constructors) { 18 System.out.println(con); 19 } 20 System.out.println("---------分割线-----------"); 21 Constructor[] declaredConstructors = cls.getDeclaredConstructors(); 22 for (Constructor Dcon : declaredConstructors) { 23 System.out.println(Dcon); 24 } 25 //获取方法对象 26 System.out.println("---------分割线-----------"); 27 System.out.println("会获取到继承Object类的方法"); 28 Method[] methods = cls.getMethods(); 29 for (Method me : methods) { 30 System.out.println(me); 31 } 32 System.out.println("---------分割线-----------"); 33 Method[] declaredMethods = cls.getDeclaredMethods(); 34 for (Method Dme : declaredMethods) { 35 System.out.println(Dme); 36 } 37 //获取属性对象 38 System.out.println("---------分割线-----------"); 39 Field[] fields = cls.getFields(); 40 for (Field fi : fields) { 41 System.out.println(fi); 42 } 43 System.out.println("---------分割线-----------"); 44 Field[] declaredFields = cls.getDeclaredFields(); 45 for (Field Dfi : declaredFields) { 46 System.out.println(Dfi); 47 } 48 System.out.println("---------分割线-----------"); 49 Object per = cls.newInstance(); 50 Field id = cls.getDeclaredField("Id"); 51 //获取和修改private修饰的方法和属性,需要设置暴力反射 52 id.setAccessible(true); 53 id.set(per,"123456"); 54 System.out.println(per); 55 } 56 }
反射的使用案例:在不改变代码的情况下获取到不同对象和方法
实现:配置文件+反射(框架设计的雏形)
代码案例:
1 package com.reflex.bean; 2 3 public class Person { 4 private String Id; 5 public String name; 6 public String age; 7 protected String gender; 8 9 public String getId() { 10 return Id; 11 } 12 13 public String getName() { 14 return name; 15 } 16 17 public String getAge() { 18 return age; 19 } 20 21 public String getGender() { 22 return gender; 23 } 24 25 public void setId(String id) { 26 27 Id = id; 28 } 29 30 public void setName(String name) { 31 this.name = name; 32 } 33 34 public void setAge(String age) { 35 this.age = age; 36 } 37 38 public void setGender(String gender) { 39 this.gender = gender; 40 } 41 42 public Person(String id, String name, String age, String gender) { 43 44 Id = id; 45 this.name = name; 46 this.age = age; 47 this.gender = gender; 48 } 49 50 public Person() { 51 52 } 53 54 private void reflexDemo() { 55 System.out.println("this is a test!"); 56 } 57 58 @Override 59 public String toString() { 60 return "Person{" + 61 "Id='" + Id + '\'' + 62 ", name='" + name + '\'' + 63 ", age='" + age + '\'' + 64 ", gender='" + gender + '\'' + 65 '}'; 66 } 67 private void eat(){ 68 System.out.println("eat food!"); 69 } 70 }
1 package com.reflex.bean; 2 3 4 public class Student { 5 private String Id; 6 public String name; 7 public String age; 8 protected String gender; 9 10 public String getId() { 11 return Id; 12 } 13 14 public String getName() { 15 return name; 16 } 17 18 public String getAge() { 19 return age; 20 } 21 22 public String getGender() { 23 return gender; 24 } 25 26 public void setId(String id) { 27 28 Id = id; 29 } 30 31 public void setName(String name) { 32 this.name = name; 33 } 34 35 public void setAge(String age) { 36 this.age = age; 37 } 38 39 public void setGender(String gender) { 40 this.gender = gender; 41 } 42 43 public Student(String id, String name, String age, String gender) { 44 45 Id = id; 46 this.name = name; 47 this.age = age; 48 this.gender = gender; 49 } 50 51 public Student() { 52 53 } 54 55 private void reflexDemo() { 56 System.out.println("this is a test!"); 57 } 58 59 @Override 60 public String toString() { 61 return "Person{" + 62 "Id='" + Id + '\'' + 63 ", name='" + name + '\'' + 64 ", age='" + age + '\'' + 65 ", gender='" + gender + '\'' + 66 '}'; 67 } 68 private void study(){ 69 System.out.println("study..."); 70 } 71 }
1 package com.reflex.demo; 2 3 import java.io.InputStream; 4 import java.lang.reflect.Method; 5 import java.util.Properties; 6 7 8 public class demo3 { 9 public static void main(String[] args) throws Exception{ 10 //创建properties对象 11 Properties prop = new Properties(); 12 //获取类加载器 13 ClassLoader classLoader = demo3.class.getClassLoader(); 14 //通过类加载器获取配置文件 15 InputStream in = classLoader.getResourceAsStream("prop.properties"); 16 //加载配置文件 17 prop.load(in); 18 //通过配置文件获取对象的属性 19 String className = prop.getProperty("className"); 20 String methodName = prop.getProperty("methodName"); 21 //通过反射获取对象 22 Class cls = Class.forName(className); 23 Object obj = cls.newInstance(); 24 //获取方法对象 25 Method method = cls.getDeclaredMethod(methodName); 26 //因为方法修饰为private,设置暴力反射(不设置无权访问会报错) 27 method.setAccessible(true); 28 //调用方法 29 method.invoke(obj); 30 } 31 }
配置文件:
名字:
prop.properties
内容:
#className=com.reflex.bean.Person #methodName=eat className=com.reflex.bean.Student methodName=study
获取构造函数、成员属性和成员方法对象的相关操作,建议结合着API文档进行拓展学习!