java反射详细讲解(2022版)

川长思鸟来 2023-10-06 22:54 149阅读 0赞

目录

什么是反射?

一、三种获取类信息的方式

二、获取类的相关信息

三、通过反射获取构造方法并使用

四、获取成员变量并调用

五: 获取成员方法并调用

六、 将map转成对象


什么是反射?

(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到类对象之后,再通过类对象进行反编译,从而获取对象的各种信息。

(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁

反射:能够分析类信息的能力叫做反射

类的生命周期

2f86da8ec9f94e0fa04dbc5d2ac480d9.png

Java代码在计算机中经历的三个阶段

249f88dd63e94c2da8aebccc467be8dc.png

一、三种获取类信息的方式

  • 类名.class
  • 对象.getClass();
  • Class.forName(“这里面是类的全限定名 包名+类名”)

    public class TestClass {
    // private Map map;

    1. public static void main(String[] args) {
    2. }
    3. public void test() throws ClassNotFoundException {
    4. //获取Cat对象
    5. Class<Cat> catClass = Cat.class;
    6. //通过对象方式获取
    7. Cat cat = new Cat();
    8. Class<? extends Cat> aClass = cat.getClass();
    9. //通过类名字获取类对象
    10. Class<? extends Object> aClass1 = Class.forName("com.fanshe.Cat");
    11. System.out.println(catClass == aClass);
    12. }

    }

结论:

同一个类加载器加载的文件(*.class)在一次程序运行过程中,只会被加载一次,不论你通过那种方式获取的class对象都是同一个

二、获取类的相关信息

  1. @Test
  2. public void testClass() {
  3. //获取 猫类的类对象
  4. Class<Cat> catClass = Cat.class;
  5. //获取该类的加载器
  6. System.out.println("该类的加载器"+catClass.getClassLoader());
  7. //获取 类的 类名
  8. System.out.println(catClass.getName());
  9. System.out.println(catClass.getSimpleName());
  10. System.out.println(catClass.getTypeName());
  11. //获取该类的 父类 (继承是单继承)
  12. Class<? super Cat> superclass = catClass.getSuperclass();
  13. System.out.println(superclass.getName());
  14. //获取 该类 实现的所有接口(Java采用的是多实现)
  15. Class<?>[] interfaces = catClass.getInterfaces();
  16. System.out.println(Arrays.toString(interfaces));
  17. //获取类上的注解
  18. Annotation[] annotations = catClass.getAnnotations();
  19. System.out.println(Arrays.toString(annotations));
  20. }

cc3c124cfd0a4a2f98946d3ca8f77b68.png

三、通过反射获取构造方法并使用

Student测试类

  1. public class Student {
  2. //---------------构造方法-------------------
  3. //(默认的构造方法)
  4. Student(String str){
  5. System.out.println("(默认)的构造方法 s = " + str);
  6. }
  7. //无参构造方法
  8. public Student(){
  9. System.out.println("调用了公有、无参构造方法执行了。。。");
  10. }
  11. //有一个参数的构造方法
  12. public Student(char name){
  13. System.out.println("姓名:" + name);
  14. }
  15. //有多个参数的构造方法
  16. public Student(String name ,int age){
  17. System.out.println("姓名:"+name+"年龄:"+ age);//这的执行效率有问题,以后解决。
  18. }
  19. //受保护的构造方法
  20. protected Student(boolean n){
  21. System.out.println("受保护的构造方法 n = " + n);
  22. }
  23. //私有构造方法
  24. private Student(int age){
  25. System.out.println("私有的构造方法 年龄:"+ age);
  26. }
  27. }

共有6个构造方法

测试类

  1. public class StudentConstructors {
  2. /*
  3. * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
  4. *
  5. * 1.获取构造方法:
  6. * 1).批量的方法:
  7. * public Constructor[] getConstructors():所有"公有的"构造方法
  8. public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
  9. * 2).获取单个的方法,并调用:
  10. * public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
  11. * public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
  12. *
  13. * 调用构造方法:
  14. * Constructor-->newInstance(Object... initargs)
  15. */
  16. public static void main(String[] args) throws Exception {
  17. //1.加载Class对象
  18. Class clazz = Class.forName("com.fanshe.Student");
  19. //2.获取所有公有构造方法
  20. System.out.println("**********************所有公有构造方法*********************************");
  21. Constructor[] conArray = clazz.getConstructors();
  22. for(Constructor c : conArray){
  23. System.out.println(c);
  24. }
  25. System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
  26. conArray = clazz.getDeclaredConstructors();
  27. for(Constructor c : conArray){
  28. System.out.println(c);
  29. }
  30. System.out.println("*****************获取公有、无参的构造方法*******************************");
  31. Constructor con = clazz.getConstructor(null);
  32. //1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
  33. //2>、返回的是描述这个无参构造函数的类对象。
  34. System.out.println("con = " + con);
  35. //调用构造方法
  36. Object obj = con.newInstance();
  37. // System.out.println("obj = " + obj);
  38. // Student stu = (Student)obj;
  39. System.out.println("******************获取私有构造方法,并调用*******************************");
  40. con = clazz.getDeclaredConstructor(char.class);
  41. System.out.println(con);
  42. //调用构造方法
  43. con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
  44. obj = con.newInstance('男');
  45. }
  46. }

输出

  1. **********************所有公有构造方法*********************************
  2. public com.fanshe.Student(java.lang.String,int)
  3. public com.fanshe.Student(char)
  4. public com.fanshe.Student()
  5. ************所有的构造方法(包括:私有、受保护、默认、公有)***************
  6. private com.fanshe.Student(int)
  7. protected com.fanshe.Student(boolean)
  8. public com.fanshe.Student(java.lang.String,int)
  9. public com.fanshe.Student(char)
  10. public com.fanshe.Student()
  11. com.fanshe.Student(java.lang.String)
  12. *****************获取公有、无参的构造方法*******************************
  13. con = public com.fanshe.Student()
  14. 调用了公有、无参构造方法执行了。。。
  15. ******************获取私有构造方法,并调用*******************************
  16. public com.fanshe.Student(char)
  17. 姓名:男

调用方法:
1.获取构造方法:

  • 批量的方法:

public Constructor[] getConstructors():所有”公有的”构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保 护、默认、公有)

  • 获取单个的方法,并调用:

public Constructor getConstructor(Class… parameterTypes):获取单个的”公有的”构造方法:

public Constructor getDeclaredConstructor(Class… parameterTypes):获取”某个构造方法”可以是私有的,或受保护、默认、公有;

调用构造方法:
Constructor—>newInstance(Object… initargs)

2、 newInstance是 Constructor类的方法(管理构造函数的类)
newInstance(Object… initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用

四、获取成员变量并调用

Student类:

  1. public class Student {
  2. public Student(){
  3. }
  4. //**********字段*************//
  5. public String name;
  6. protected int age;
  7. char sex;
  8. private String phoneNum;
  9. @Override
  10. public String toString() {
  11. return "Student [name=" + name + ", age=" + age + ", sex=" + sex
  12. + ", phoneNum=" + phoneNum + "]";
  13. }
  14. }

测试类:

  1. public class Fields {
  2. public static void main(String[] args) throws Exception {
  3. //1.获取Class对象
  4. Class stuClass = Class.forName("com.fanshe.student.Student");
  5. //2.获取字段
  6. System.out.println("************获取所有公有的字段********************");
  7. Field[] fieldArray = stuClass.getFields();
  8. for(Field f : fieldArray){
  9. System.out.println(f);
  10. }
  11. System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
  12. fieldArray = stuClass.getDeclaredFields();
  13. for(Field f : fieldArray){
  14. System.out.println(f);
  15. }
  16. System.out.println("*************获取公有字段**并调用***********************************");
  17. Field f = stuClass.getField("name");
  18. System.out.println(f);
  19. //获取一个对象
  20. Object obj = stuClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();
  21. //为字段设置值
  22. f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华"
  23. //验证
  24. Student stu = (Student)obj;
  25. System.out.println("验证姓名:" + stu.name);
  26. System.out.println("**************获取私有字段****并调用********************************");
  27. f = stuClass.getDeclaredField("phoneNum");
  28. System.out.println(f);
  29. f.setAccessible(true);//暴力反射,解除私有限定
  30. f.set(obj, "18888889999");
  31. System.out.println("验证电话:" + stu);
  32. }
  33. }

结果:

  1. ************获取所有公有的字段********************
  2. public java.lang.String com.fanshe.student.Student.name
  3. ************获取所有的字段(包括私有、受保护、默认的)********************
  4. public java.lang.String com.fanshe.student.Student.name
  5. protected int com.fanshe.student.Student.age
  6. char com.fanshe.student.Student.sex
  7. private java.lang.String com.fanshe.student.Student.phoneNum
  8. *************获取公有字段**并调用***********************************
  9. public java.lang.String com.fanshe.student.Student.name
  10. 验证姓名:刘德华
  11. **************获取私有字段****并调用********************************
  12. private java.lang.String com.fanshe.student.Student.phoneNum
  13. 验证电话:Student [name=刘德华, age=0, sex= , phoneNum=18888889999]

由此可见:

调用字段时:需要传递两个参数:
Object obj = stuClass.getConstructor().newInstance();//产生Student对象—》Student stu = new Student();
//为字段设置值f.set(obj, “刘德华”);

//为Student对象中的name属性赋值—》stu.name = “刘德华”
第一个参数:要传入设置的对象,第二个参数:要传入实参

五: 获取成员方法并调用

Student

  1. public void show1(String s){
  2. System.out.println("调用了:公有的,String参数的show1(): s = " + s);
  3. }
  4. protected void show2(){
  5. System.out.println("调用了:受保护的,无参的show2()");
  6. }
  7. void show3(){
  8. System.out.println("调用了:默认的,无参的show3()");
  9. }
  10. private String show4(int age){
  11. System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
  12. return "abcd";
  13. }

测试类:

  1. public class MethodClass {
  2. public static void main(String[] args) throws Exception {
  3. //1.获取Class对象
  4. Class stuClass = Class.forName("com.fanshe.student.Student");
  5. //2.获取所有公有方法
  6. System.out.println("***************获取所有的”公有“方法*******************");
  7. stuClass.getMethods();
  8. Method[] methodArray = stuClass.getMethods();
  9. for(Method m : methodArray){
  10. System.out.println(m);
  11. }
  12. System.out.println("***************获取所有的方法,包括私有的*******************");
  13. methodArray = stuClass.getDeclaredMethods();
  14. for(Method m : methodArray){
  15. System.out.println(m);
  16. }
  17. System.out.println("***************获取公有的show1()方法*******************");
  18. Method m = stuClass.getMethod("show1", String.class);
  19. System.out.println(m);
  20. //实例化一个Student对象
  21. Object obj = stuClass.getConstructor().newInstance();
  22. m.invoke(obj, "刘德华");
  23. System.out.println("***************获取私有的show4()方法******************");
  24. m = stuClass.getDeclaredMethod("show4", int.class);
  25. System.out.println(m);
  26. m.setAccessible(true);//解除私有限定
  27. Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
  28. System.out.println("返回值:" + result);
  29. }
  30. }

m = stuClass.getDeclaredMethod(“show4”, int.class);//调用制定方法(所有包括私有的),需要传入两个参数,第一个是调用的方法名称,第二个是方法的形参类型,切记是类型。
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println(“返回值:” + result);

六、 将map转成对象

Cat

  1. public class Cat extends Animal {
  2. public String name;
  3. private int age;
  4. @Override
  5. public void call() {
  6. System.out.println("猫在叫~~~~");
  7. }
  8. public String getName() {
  9. return name;
  10. }
  11. public void setName(String name) {
  12. this.name = name;
  13. }
  14. public int getAge() {
  15. return age;
  16. }
  17. public void setAge(int age) {
  18. this.age = age;
  19. }
  20. @Override
  21. public String toString() {
  22. return "Cat{" +
  23. "name='" + name + '\'' +
  24. ", age=" + age +
  25. ", type='" + type + '\'' +
  26. '}';
  27. }
  28. }

测试

  1. @Test
  2. public void testMapToCat() throws Exception{
  3. Map<String, Object> map = new HashMap<>();
  4. map.put("name", "小白胖");
  5. map.put("age", 2);
  6. map.put("type","加菲猫");
  7. //将上述的Map 通过 反射转换成一个 猫对象
  8. //Class<Cat> clz = Cat.class;
  9. Class<?> clz = Class.forName("com.fanshe.Cat");
  10. //获取猫类的所有属性
  11. Field[] fields = clz.getDeclaredFields();
  12. //获取父类定义的所有属性
  13. Field[] parents = clz.getSuperclass().getDeclaredFields();
  14. //合并2个类的所有属性
  15. List<Field> collect = Arrays.stream(fields).collect(Collectors.toList());
  16. List<Field> collect2 = Arrays.stream(parents).collect(Collectors.toList());
  17. collect.addAll(collect2);
  18. // Cat cat = new Cat();
  19. //反射去new Cat 调用他的无参构造
  20. Object obj = clz.getDeclaredConstructor().newInstance();
  21. //遍历所有的Field
  22. for(Field f : collect){
  23. //破坏封装
  24. f.setAccessible(true);
  25. //属性名,是否在Map中
  26. if(map.containsKey(f.getName())){
  27. //将map 中对应的key值,写入到f中
  28. f.set(obj, map.get(f.getName()));
  29. }
  30. }
  31. System.out.println(obj);
  32. }

e14740699d3d40c2863f2773281eef14.png

发表评论

表情:
评论列表 (有 0 条评论,149人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Java反射讲解

    > 文章最前: 我是Octopus,这个名字来源于我的中文名--章鱼;我热爱编程、热爱算法、热爱开源。所有源码在我的个人[github][] ;这博客是记录我学习的点点滴滴,如