深入了解Java反射机制

简介: 深入了解Java反射机制

一、前言

1.什么是反射机制

        Java反射指的是Java语言在运行时动态地获取类信息、构造类对象、调用对象方法以及访问或修改对象属性的能力。Java反射机制允许Java程序在运行期间检查其本身的结构并获取类属性、方法和构造函数的信息,也可以动态地创建类的实例、调用其方法和访问其属性。JDK 中 java.lang.Class 类,就是为了实现反射提供的核心类之一。

2.为什么要使用反射

1.获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器(类类)等

2.获取任意对象的属性,并且能改变对象的属性

3.调用任意对象的方法

4.判断任意一个对象所属的类

5.实例化任意一个类的对象

二、使用反射

接下来的所有操作用Student类来演示

package com.xw;
public class Student {
  private String sid;
  private String sname;
  public Integer age;
  static{
    System.out.println("静态代码块加载中。。。");
  }
  public Student() {
    super();
    System.out.println("调用无参构造方法创建了一个学生对象");
  }
  public Student(String sid) {
    super();
    this.sid = sid;
    System.out.println("调用带一个参数的构造方法创建了一个学生对象");
  }
  public Student(String sid, String sname) {
    super();
    this.sid = sid;
    this.sname = sname;
    System.out.println("调用带二个参数的构造方法创建了一个学生对象");
  }
  @SuppressWarnings("unused")
  private Student(Integer age) {
    System.out.println("调用Student类私有的构造方法创建一个学生对象");
    this.age = age;
  }
  public String getSid() {
    return sid;
  }
  public void setSid(String sid) {
    this.sid = sid;
  }
  public String getSname() {
    return sname;
  }
  public void setSname(String sname) {
    this.sname = sname;
  }
  public void hello() {
    System.out.println("你好!我是" + this.sname);
  }
  public void hello(String name) {
    System.out.println(name + "你好!我是" + this.sname);
  }
  @SuppressWarnings("unused")
  private Integer add(Integer a, Integer b) {
    return new Integer(a.intValue() + b.intValue());
  }
}

1.什么是类类(类加载器)

       在Java中,类加载器是一种重要的机制,它负责把 Java 字节码文件(.class文件) 加载到内存中,并将其转换成 Java 类的运行时表示形式。Java类加载器按照加载的方式和顺序可以分为三类:Bootstrap Class Loader(启动类加载器)、Extention Class Loader(扩展类加载器)和App Class Loader(应用程序类加载器)。

      类加载器是Java虚拟机(JVM)的一部分,主要用于实现Java程序的动态扩展和代码的动态加载。类加载器可以加载本地的class文件,也可以从网络或其他JVM中获取class文件进行加载。通过Java反射和类加载器机制,开发人员可以动态地创建对象、调用类的方法、访问类的属性等等,从而实现软件组件的灵活性和可扩展性。    

注意:类加载器并不等同于类,它只负责将类的字节码文件加载到内存中,虚拟机会根据字节码文件实例化和加载类。类加载器按照加载的顺序和层次关系形成一个树状结构,父类加载器可以委托子类加载器来加载类,在默认情况下,父类加载器可以看到其子类加载器所加载的类,但反过来则不行,这样可以使得类在整个JVM中得到合适的共享。

2.获取类类(类加载器)

Class.forName

        //第一种:Class.forName 参数是类的全路径名
    Student stu=new Student();
    Class forName = Class.forName("com.xw.Student");
    System.out.println(forName);
      //打印结果:class com.xw.Student

主要作用于连接sql,加载驱动。

类名.class

//第二种:类名.class
    Class StudentClass =Student.class;
    System.out.println(StudentClass);
//打印结果:class com.xw.Student

实例化对象.getClass

//第三种:对象.getClass
        Student stu=new Student();
    Class objectClass=stu.getClass();
    System.out.println(objectClass);
//打印结果:class com.xw.Student

注意

①所有反射先从类类(类加载器)开始,我们首先需要通过获取类类(类加载器),来加载目标类的字节码文件,然后解析字节码文件并生成类的元数据信息,最终使我们能够使用Java反射机制来操作该类的实例。

反射机制默认调用无参构造器来创建类的实例,但是并不是说所有的类都必须有无参构造器。如果一个类没有定义无参构造器,那么反射机制就无法直接使用Class.newInstance()方法来创建该类的实例,因为该方法会默认调用无参构造器,如果该类没有定义无参构造器,就会抛出InstantiationException异常。

③如果一个类没有定义无参构造器,我们可以通过使用其他的构造器来创建类的实例。例如,我们可以使用Constructor.newInstance()方法来调用指定的构造器来创建类的实例。

3.反射实例

反射实例要用到的方法

方法 描述
newInstance() 用于创建一个类的实例。用 newInstance() 方法时,该类必须有可访问的无参构造方法,否则会抛出异常。
getConstructo()

获取指定类中的公共构造器,可以通过这个构造器来创建该类的实例。

注意:该方法需要传入一个Class数组作为参数,用于指定构造器的参数类型列表。

getDeclaredConstructor() 获取指定类中声明的构造器。与getConstructor() 方法不同的是,getDeclaredConstructor() 方法可以获取到指定类中的私有构造器。
 setAccessible() 用于设置一个 AccessibleObject 对象(包括构造器、方法、字段等)的访问标志为 true,使得在访问该对象时被访问的实体(例如:Java 虚拟机)可以忽略 JAVA 语言访问控制检查。

没有无参构造器的错误

Class<Student> c0=Student.class;
  Student newInstance = c0.newInstance();
  System.out.println(newInstance);
  //java.lang.NoSuchMethodException: com.xw.Student.<init>()

无参构造器

  Class<Student> c1=Student.class;
  Student newInstance2 = c1.newInstance();
  System.out.println(newInstance2);
  //com.xw.Student@7852e922

一个参数的构造器

  Class<Student> c2=Student.class;
  Constructor<Student> cr2 = c2.getConstructor(String.class);
  Student newInstance3 = cr2.newInstance("01");
  System.out.println(newInstance3);
  //com.xw.Student@182decdb

两个参数的构造器

Class<Student> c3=Student.class;
  Constructor<Student> cr3 = c3.getConstructor(String.class,String.class);
  Student newInstance4 = cr3.newInstance("01","xw");
  System.out.println(newInstance4);
  //com.xw.Student@182decdb

私有的有参构造器

Class<Student> c4=Student.class;
  Constructor<Student> cr4 = c4.getDeclaredConstructor(Integer.class);
  cr4.setAccessible(true);
  Student newInstance5 = cr4.newInstance(2);
  System.out.println(newInstance5);
  //com.xw.Student@182decdb

4.反射动态方法调用

如果你看懂了反射实例那么反射动态方法只是换个方法那么简单。

反射动态方法要用到的方法

方法 描述
getMethod(name, parameterTypes)

获取某个类中的公共方法。这个方法属于 Class 类的实例方法,因此必须先获取到该类的 Class 对象,然后调用 getMethod() 方法。

name:方法名 parameterTypes:类型

invoke(obj, args)

调用一个对象的方法,并且可以向该方法传递参数。

obj:类实例 args:参数

getDeclaredMethod(name, parameterTypes) 

 获取某个类中声明的方法,不包括父类中声明的方法。这个方法属于 Class 类的实例方法,因此必须先获取到该类的 Class 对象,然后调用 getDeclaredMethod() 方法。

name:方法名 parameterTypes:类型

调用无参方法

// 所有反射先从类类开始
    Class<Student> c = Student.class;
    Student stu = new Student();
        Method m1 = c.getMethod("hello");
        Object invoke = m1.invoke(stu);
     System.out.println(invoke);
    /*
     * 你好!我是null null
     */

调用有参方法

// 所有反射先从类类开始
    Class<Student> c = Student.class;
    Student stu = new Student();
        Method m2 = c.getMethod("hello", String.class);
    Object invoke2 = m2.invoke(stu, "xw");
     System.out.println(invoke2);
    /*
     * xw你好!我是null null
     */

调用私有方法

// 所有反射先从类类开始
    Class<Student> c = Student.class;
    Student stu = new Student();
        Method m3 = c.getDeclaredMethod("add", Integer.class,Integer.class);
    m3.setAccessible(true);
    Object invoke3 = m3.invoke(stu, 1,2);
    System.out.println(invoke3);
    //打印结果:3

5.通过反射进行读写属性

反射读写属性要用的方法

方法 描述
getDeclaredField() 获取类中指定名称的成员变量,不包括父类中的成员变量。
getDeclaredFields() 获取类中全部的成员变量,不包括父类中的成员变量。
get() 获取类的成员变量的值的方法。这个方法属于 Field 类的实例方法,因此必须先获取到该类的 Field 对象,然后调用 get() 方法。
getName() 用于获取成员变量名称的方法,可以获取所有可访问的类成员(包括私有或受保护成员)。
set() 设置类的成员变量值的方法,可以设置公共成员变量和私有成员变量的值。需要注意的是。注意:在设置私有成员变量的值时,需要先使用 setAccessible(true) 方法将可访问性设置为 true。

根据属性名称获取指定实例的值

        // 实例化student类
    Student stu = new Student("01", "xw");
    // 获取类类
    Class cc = Student.class;
        Field declaredField = cc.getDeclaredField("sname");
    declaredField.setAccessible(true);
    Object object = declaredField.get(stu);
    String name = declaredField.getName();
    System.out.println(name);
     System.out.println(object);
        //打印结果:xw

获取实例的所有属性名称以及属性值

        // 实例化student类
    Student stu = new Student("01", "xw");
    // 先获取类类
    Class cc = Student.class;
        Field[] field = cc.getDeclaredFields();
    for (Field field2 : field) {
      field2.setAccessible(true);
      System.out.println(field2.getName() + "   " + field2.get(stu));
      /*
       * sid 01 
       * sname xw 
       * age null
       * 
       */
    }

修改实例student的sname的属性值

        // 实例化student类
    Student stu = new Student("01", "xw");
    // 先获取类类
    Class cc = Student.class;
      Field declaredField2 = cc.getDeclaredField("sname");
    declaredField2.setAccessible(true);
    declaredField2.set(stu, "xw帅逼");
    Object object2 = declaredField2.get(stu);
    System.out.println(object2);
        //打印结果:xw帅逼

觉得有用的话记得点个赞哦!!


目录
打赏
0
0
0
0
7
分享
相关文章
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
141 2
探索Java中的异常处理机制
【10月更文挑战第35天】在Java的世界中,异常是程序运行过程中不可避免的一部分。本文将通过通俗易懂的语言和生动的比喻,带你了解Java中的异常处理机制,包括异常的类型、如何捕获和处理异常,以及如何在代码中有效地利用异常处理来提升程序的健壮性。让我们一起走进Java的异常世界,学习如何优雅地面对和解决问题吧!
|
3月前
|
Java中的异常处理机制深度剖析####
本文深入探讨了Java语言中异常处理的重要性、核心机制及其在实际编程中的应用策略,旨在帮助开发者更有效地编写健壮的代码。通过实例分析,揭示了try-catch-finally结构的最佳实践,以及如何利用自定义异常提升程序的可读性和维护性。此外,还简要介绍了Java 7引入的多异常捕获特性,为读者提供了一个全面而实用的异常处理指南。 ####
105 20
深入理解Java异常处理机制
Java的异常处理是编程中的一块基石,它不仅保障了代码的健壮性,还提升了程序的可读性和可维护性。本文将深入浅出地探讨Java异常处理的核心概念、分类、处理策略以及最佳实践,旨在帮助读者建立正确的异常处理观念,提升编程效率和质量。
155 1
深入探索Java中的异常处理机制##
本文将带你深入了解Java语言中的异常处理机制,包括异常的分类、异常的捕获与处理、自定义异常的创建以及最佳实践。通过具体实例和代码演示,帮助你更好地理解和运用Java中的异常处理,提高程序的健壮性和可维护性。 ##
79 2
Java中的异常处理机制:深入解析与最佳实践####
本文旨在为Java开发者提供一份关于异常处理机制的全面指南,从基础概念到高级技巧,涵盖try-catch结构、自定义异常、异常链分析以及最佳实践策略。不同于传统的摘要概述,本文将以一个实际项目案例为线索,逐步揭示如何高效地管理运行时错误,提升代码的健壮性和可维护性。通过对比常见误区与优化方案,读者将获得编写更加健壮Java应用程序的实用知识。 --- ####
Java 异常处理:机制、策略与最佳实践
Java异常处理是确保程序稳定运行的关键。本文介绍Java异常处理的机制,包括异常类层次结构、try-catch-finally语句的使用,并探讨常见策略及最佳实践,帮助开发者有效管理错误和异常情况。
191 6
Java 反射机制:动态编程的强大利器
Java反射机制允许程序在运行时检查类、接口、字段和方法的信息,并能操作对象。它提供了一种动态编程的方式,使得代码更加灵活,能够适应未知的或变化的需求,是开发框架和库的重要工具。
93 5
深入理解Java中的异常处理机制
本文旨在揭示Java异常处理的奥秘,从基础概念到高级应用,逐步引导读者掌握如何优雅地管理程序中的错误。我们将探讨异常类型、捕获流程,以及如何在代码中有效利用try-catch语句。通过实例分析,我们将展示异常处理在提升代码质量方面的关键作用。
63 3
有哪些场景不适合使用Java反射机制
Java反射机制虽强大,但并非万能。在性能要求极高、安全性严格控制、类结构复杂多变或对象创建频繁的场景下,使用反射可能带来性能下降、安全风险增加等问题,应谨慎选择。
108 11

热门文章

最新文章