1:什么是反射机制?反射机制的应用场景有哪些?

  1. 反射机制介绍JAVA 反射机制是在运行状态中,
  2. 对于任意一个类,都能够知道这个类的所有属性和方法;
  3. 对于任意一个对象,都能够调用它的任意一个方法和属性;
  4. 这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。
  5. 静态编译和动态编译
  6. 静态编译:在编译时确定类型,绑定对象
  7. 动态编译:运行时确定类型,绑定对象
  8. 反射机制优缺点优点:
  9. 运行期类型的判断,动态加载类,提高代码灵活度。
  10. 缺点:
  11. 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的 java 代码要慢很多。
  12. 反射是框架设计的灵魂
  13. 在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,
  14. 但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关;
  15. 1 我们在使用 JDBC 连接数据库时使用 Class.forName()通过反射加载数据库的驱动程序;
  16. 2Spring 框架也用到很多反射机制,最经典的就是 xml 的配置模式。

2:反射的作用

  1. 使用的前提条件:必须先得到代表的字节码的ClassClass类用于表示.class文件(字节码)
  2. 反射就是把java类中的各种成分映射成一个个的Java对象
  3. 例如:一个类有:
  4. 构造方法、成员变量、成员方法、包等等信息,
  5. 利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
  6. (其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
  7. 主要分为以下:
  8. 1:获取Class对象的方法
  9. 2:获取构造方法
  10. 3:获取成员变量
  11. 4:获取成员方法
  12. 5:通过反射运行配置文件内容
  13. 6:通过反射越过泛型检查

3:反射的加载 与 Class类的描述

  1. 如图是类的正常加载过程:反射的原理在与class对象。
  2. 加载的时候:将class文件读入内存,并为之创建一个Class对象。
  3. Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
  4. 也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)
  5. Class 没有公共构造方法。
  6. Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。
  7. 也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。

image.png

image.png

4:获取Class对象的三种方式

  1. 在运行期间,一个类,只有一个Class对象产生。
  2. 三种方式常用第三种:
  3. 第一种:对象都有了还要反射干什么。
  4. 第二种:需要导入类的包,依赖太强,不导包就抛编译错误。
  5. 第三种:一个字符串可以传入也可写在配置文件中等多种方法。
  1. /**
  2. * 获取Class对象的三种方式
  3. * 1 Object ——> getClass();
  4. * 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
  5. * 3 通过Class类的静态方法:forName(String className)(常用)
  6. *
  7. */
  8. public class ClassDemo {
  9. public static void main(String[] args) {
  10. //第一种方式获取Class对象
  11. //这一new 产生一个Student对象,一个Class对象。
  12. Student stu1 = new Student();
  13. //获取Class对象
  14. Class stuClass1 = stu1.getClass();
  15. System.out.println(stuClass1.getName());
  16. //第二种方式获取Class对象
  17. Class stuClass2 = Student.class;
  18. //判断第一种方式获取的Class对象和第二种方式获取的是同一个
  19. System.out.println(stuClass1 == stuClass2);
  20. //第三种方式获取Class对象
  21. try {
  22. //注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
  23. Class stuClass3 = Class.forName("base.model.entity.Student");
  24. //判断三种方式获取的是同一个Class对象
  25. System.out.println(stuClass3 == stuClass2);
  26. } catch (ClassNotFoundException e) {
  27. e.printStackTrace();
  28. }
  29. }
  30. }

5:获取Class对象的构造方法 并 调用

  1. package base.model.entity ;
  2. public class Student {
  3. //---------------构造方法-------------------
  4. //无参构造方法
  5. public Student(){
  6. System.out.println("调用了公有、无参构造方法执行了。。。");
  7. }
  8. //私有构造方法
  9. private Student(int age){
  10. System.out.println("私有的构造方法 年龄:"+ age);
  11. }
  12. //(默认的构造方法)
  13. Student(String str){
  14. System.out.println("(默认)的构造方法 s = " + str);
  15. }
  16. //受保护的构造方法
  17. protected Student(boolean n){
  18. System.out.println("受保护的构造方法 n = " + n);
  19. }
  20. //有一个参数的构造方法
  21. public Student(char name){
  22. System.out.println("姓名:" + name);
  23. }
  24. //有多个参数的构造方法
  25. public Student(String name ,Integer age){
  26. System.out.println("姓名:"+name+"年龄:"+ age);
  27. }
  28. }
  1. import java.lang.reflect.Constructor;
  2. /*
  3. * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
  4. *
  5. * 1.获取构造方法:
  6. * 1).批量的方法:
  7. * 所有"公有的"构造方法
  8. * public Constructor[] getConstructors()
  9. * 获取所有的构造方法(包括私有、受保护、默认、公有)
  10. * public Constructor[] getDeclaredConstructors()
  11. *
  12. * 2).获取单个的方法,并调用:
  13. * 获取单个的"公有的"构造方法:
  14. * public Constructor getConstructor(Class... parameterTypes)
  15. * 获取某个构造方法(私有的,或受保护、默认、公有)
  16. * public Constructor getDeclaredConstructor(Class... parameterTypes):
  17. *
  18. * 调用构造方法:
  19. * Constructor-->newInstance(Object... initargs)
  20. */
  21. public class Constructors {
  22. public static void main(String[] args) throws Exception {
  23. //1.加载Class对象
  24. Class clazz = Class.forName("base.model.entity.Student");
  25. //2.获取所有公有构造方法
  26. Constructor[] pubConArray = clazz.getConstructors();
  27. for(Constructor c : pubConArray){
  28. System.out.println(c);
  29. }
  30. //3.获取所有的构造方法(包括私有、受保护、默认、公有)
  31. Constructor[] allConArray = clazz.getDeclaredConstructors();
  32. for(Constructor c : allConArray){
  33. System.out.println(c);
  34. }
  35. //获取公有、无参的构造方法
  36. //1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
  37. //2>、返回的是描述这个无参构造函数的类对象。
  38. Constructor con = clazz.getConstructor(null);
  39. System.out.println("con = " + con);
  40. //调用构造方法
  41. Object obj = con.newInstance();
  42. System.out.println("******************获取 某个构造方法 并调用*******************************");
  43. con = clazz.getDeclaredConstructor(char.class);
  44. System.out.println("con = " +con);
  45. //调用构造方法
  46. //暴力访问(忽略掉访问修饰符)
  47. con.setAccessible(true);
  48. obj = con.newInstance('hesuijin');
  49. }
  50. }
  51. //控制台打印结果
  52. **********************所有公有构造方法*********************************
  53. public base.model.entity.Student()
  54. public base.model.entity.Student(char)
  55. public base.model.entity.Student(java.lang.String,java.lang.Integer)
  56. ************所有的构造方法(包括:私有、默认、受保护、公有)***************
  57. public base.model.entity.Student()
  58. private base.model.entity.Student(int)
  59. base.model.entity.Student(java.lang.String)
  60. protected base.model.entity.Student(boolean
  61. public base.model.entity.Student(char))
  62. public base.model.entity.Student(java.lang.String,int)
  63. *****************获取公有、无参的构造方法*******************************
  64. con = public base.model.entity.Student()
  65. 调用了公有、无参构造方法执行了。。。
  66. ******************获取私有构造方法,并调用*******************************
  67. con = public base.model.entity.Student(char)
  68. 姓名:hesuijin

6:获取成员变量并调用

  1. package base.model.field;
  2. @Data
  3. public class Student {
  4. public Student(){
  5. }
  6. //**********字段*************//
  7. public String name;
  8. protected int age;
  9. char sex;
  10. private String phone;
  11. }
  12. import java.lang.reflect.Field;
  13. /*
  14. * 获取成员变量并调用:
  15. *
  16. * 1.批量的
  17. * 获取所有的"公有字段"
  18. * 1).Field[] getFields()
  19. * 获取所有字段,包括:私有、受保护、默认、公有;
  20. * 2).Field[] getDeclaredFields()
  21. *
  22. * 2.获取单个的:
  23. * 1).public Field getField(String fieldName)
  24. * 2).public Field getDeclaredField(String fieldName)
  25. *
  26. * 设置字段的值:
  27. * Field --> public void set(Object obj,Object value):
  28. * 参数说明:
  29. * 1.obj:要设置的字段所在的对象;
  30. * 2.value:要为字段设置的值;
  31. *
  32. */
  33. public class Fields {
  34. public static void main(String[] args) throws Exception {
  35. //1.获取Class对象
  36. Class stuClass = Class.forName("base.model.field.Student");
  37. //2.获取字段
  38. System.out.println("************获取所有公有的字段********************");
  39. Field[] fieldArray = stuClass.getFields();
  40. for(Field f : fieldArray){
  41. System.out.println(f);
  42. }
  43. System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
  44. fieldArray = stuClass.getDeclaredFields();
  45. for(Field f : fieldArray){
  46. System.out.println(f);
  47. }
  48. //获取一个对象
  49. //产生Student对象--》Student stu = new Student();
  50. Object obj = stuClass.getConstructor().newInstance();
  51. System.out.println("*************获取公有字段**并调用***********************************");
  52. Field f = stuClass.getField("name");
  53. System.out.println(f);
  54. //为字段设置值
  55. //为Student对象中的name属性赋值--》stu.name = "刘德华"
  56. f.set(obj, "刘德华");
  57. //验证
  58. Student stu = (Student)obj;
  59. System.out.println("验证姓名:" + stu.name);
  60. System.out.println("**************获取私有字段****并调用********************************");
  61. f = stuClass.getDeclaredField("phone");
  62. System.out.println(f);
  63. //暴力反射,解除私有限定
  64. f.setAccessible(true);
  65. f.set(obj, "18718747777");
  66. System.out.println("验证电话:" + stu.phone);
  67. }
  68. }
  69. 控制台输出
  70. *************获取公有字段**并调用***********************************
  71. public java.lang.String base.model.field.Student.name
  72. 验证姓名:刘德华
  73. **************获取私有字段****并调用********************************
  74. private java.lang.String base.model.field.Student.phone
  75. 验证电话:18718747777

7:获取成员方法并调用

  1. package base.model.method ;
  2. public class Student {
  3. //**************成员方法***************//
  4. public void show1(String s){
  5. System.out.println("调用了:公有的,String参数的show1(): s = " + s);
  6. }
  7. protected void show2(){
  8. System.out.println("调用了:受保护的,无参的show2()");
  9. }
  10. void show3(){
  11. System.out.println("调用了:默认的,无参的show3()");
  12. }
  13. private String show4(int age){
  14. System.out.println("调用了:私有的,并且有返回值的,int参数的show4(): age = " + age);
  15. return "我是返回值";
  16. }
  17. }
  18. import java.lang.reflect.Method;
  19. /*
  20. * 获取成员方法并调用:
  21. * 1.批量的:
  22. * 获取所有"公有方法";(包含了父类的方法也包含Object类)
  23. * public Method[] getMethods()
  24. * 获取所有的成员方法,包括私有的(私有、受保护、默认、公有)(不包括继承的)
  25. * public Method[] getDeclaredMethods()
  26. *
  27. * 2.获取单个的:
  28. * public Method getMethod(String name,Class<?>... parameterTypes):
  29. * 参数:
  30. * name : 方法名;
  31. * Class ... : 形参的Class类型对象
  32. * public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
  33. *
  34. * 调用方法:
  35. * Method --> public Object invoke(Object obj,Object... args):
  36. * 参数说明:
  37. * obj : 要调用方法的对象;
  38. * args:调用方式时所传递的实参;
  39. ):
  40. */
  41. public class MethodDemo {
  42. public static void main(String[] args) throws Exception {
  43. //1.获取Class对象
  44. Class stuClass = Class.forName("base.model.method.Student");
  45. //2.获取所有公有方法
  46. System.out.println("***************获取所有的”公有“方法*******************");
  47. stuClass.getMethods();
  48. Method[] methodArray = stuClass.getMethods();
  49. for(Method m : methodArray){
  50. System.out.println(m);
  51. }
  52. System.out.println("***************获取所有的方法,包括私有的*******************");
  53. methodArray = stuClass.getDeclaredMethods();
  54. for(Method m : methodArray){
  55. System.out.println(m);
  56. }
  57. //实例化一个Student对象
  58. Object obj = stuClass.getConstructor().newInstance();
  59. Method mehtod;
  60. System.out.println("***************获取公有的show1()方法*******************");
  61. m = stuClass.getMethod("show1", String.class);
  62. //需要两个参数,一个是要调用的对象(获取有反射),一个是实参
  63. System.out.println(m);
  64. m.invoke(obj, "刘德华");
  65. System.out.println("***************获取私有的show4()方法******************");
  66. m = stuClass.getDeclaredMethod("show4", int.class);
  67. System.out.println(m);
  68. //暴力反射,解除私有限定
  69. m.setAccessible(true);
  70. //需要两个参数,一个是要调用的对象(获取有反射),一个是实参
  71. Object result = m.invoke(obj, 20);
  72. System.out.println("返回值:" + result);
  73. }
  74. }
  75. 控制台输出
  76. ***************获取公有的show1()方法*******************
  77. public void fanshe.method.Student.show1(java.lang.String)
  78. 调用了:公有的,String参数的show1(): s = 刘德华
  79. ***************获取私有的show4()方法******************
  80. private java.lang.String fanshe.method.Student.show4(int)
  81. 调用了,私有的,并且有返回值的,int参数的show4(): age = 20
  82. 返回值:我是返回值

8:反射方法的其它使用之—-通过反射运行配置文件内容

  1. public class Student {
  2. public void show(){
  3. System.out.println("is show()");
  4. }
  5. }
  6. //配置文件以txt文件为例子(properties.txt):
  7. className = base.model.property.Student
  8. methodName = show
  9. /*
  10. * 我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改
  11. * 我们只需要将新类发送给客户端,并修改配置文件即可
  12. */
  13. public class Demo {
  14. public static void main(String[] args) throws Exception {
  15. //1:通过反射获取Class对象
  16. //"base.model.property.Student"
  17. Class stuClass = Class.forName(getValue("className"));
  18. //2:获取show()方法
  19. //show
  20. Method m = stuClass.getMethod(getValue("methodName"));
  21. //3:调用show()方法
  22. m.invoke(stuClass.getConstructor().newInstance());
  23. }
  24. //此方法接收一个key,在配置文件中获取相应的value
  25. public static String getValue(String key) throws IOException{
  26. //获取配置文件的对象
  27. Properties pro = new Properties();
  28. //获取输入流
  29. FileReader fr = new FileReader("properties.txt");
  30. //将流加载到配置文件对象中
  31. pro.load(fr);
  32. fr.close();
  33. //返回根据key获取的value值
  34. return pro.getProperty(key);
  35. }
  36. }

9:反射方法的其它使用之—-通过反射越过泛型检查

  1. /*
  2. * 通过反射越过泛型检查
  3. *
  4. * 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?
  5. */
  6. public class Demo {
  7. public static void main(String[] args) throws Exception{
  8. ArrayList<String> strList = new ArrayList<>();
  9. strList.add("string");
  10. // strList.add(100);
  11. //获取ArrayList的Class对象,反向的调用add()方法,添加数据
  12. Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
  13. //获取add()方法
  14. Method m = listClass.getMethod("add", Object.class);
  15. //调用add()方法
  16. m.invoke(strList, 100);
  17. //遍历集合
  18. for(Object obj : strList){
  19. System.out.println(obj);
  20. }
  21. }
  22. }