深入了解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帅逼

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


相关文章
|
11天前
|
缓存 前端开发 安全
java的双亲委派机制
java的双亲委派机制
22 5
|
5天前
|
Java
【专栏】Java中的反射机制与应用实例
【4月更文挑战第27天】本文探讨了Java反射机制,该机制允许程序在运行时获取类信息、动态创建对象、调用方法和访问属性。反射通过Class、Constructor、Method和Field类实现。文中列举了反射的应用场景,如动态创建对象、调用方法、访问属性和处理注解,并提供了相关实例代码演示。
|
2天前
|
Java 程序员 开发者
深入理解Java并发编程:线程同步与锁机制
【4月更文挑战第30天】 在多线程的世界中,确保数据的一致性和线程间的有效通信是至关重要的。本文将深入探讨Java并发编程中的核心概念——线程同步与锁机制。我们将从基本的synchronized关键字开始,逐步过渡到更复杂的ReentrantLock类,并探讨它们如何帮助我们在多线程环境中保持数据完整性和避免常见的并发问题。文章还将通过示例代码,展示这些同步工具在实际开发中的应用,帮助读者构建对Java并发编程深层次的理解。
|
3天前
|
敏捷开发 机器学习/深度学习 Java
Java中的异常处理机制深入理解与实践:持续集成在软件测试中的应用探索自动化测试在敏捷开发中的关键作用
【4月更文挑战第29天】在Java编程中,异常处理是一个重要的概念。它允许开发者在程序执行过程中遇到错误或异常情况时,能够捕获并处理这些异常,从而保证程序的稳定运行。本文将详细介绍Java中的异常处理机制,包括异常的分类、异常的处理方式以及自定义异常等内容。 【4月更文挑战第29天】 随着敏捷开发和DevOps文化的兴起,持续集成(CI)已成为现代软件开发周期中不可或缺的一环。本文将探讨持续集成在软件测试领域内的关键作用、实施策略以及面临的挑战。通过对自动化构建、测试用例管理、及时反馈等核心要素的详细分析,揭示持续集成如何提高软件质量和加速交付过程。 【4月更文挑战第29天】 在当今快速发
|
3天前
|
安全 Java 测试技术
利用Java反射机制提高Spring Boot的代码质量:概念与实战
【4月更文挑战第29天】Java反射机制提供了一种强大的方法来在运行时检查或修改类和对象的行为。在Spring Boot应用中,合理利用反射可以提高代码的灵活性和可维护性。本篇博客将探讨Java反射的核心概念,并展示如何通过反射提高Spring Boot项目的代码质量。
16 0
|
4天前
|
Java API
Java包机制及JavaDoc
Java包机制及JavaDoc
|
5天前
|
安全 Java
【JAVA】Java并发编程中的锁升级机制
【JAVA】Java并发编程中的锁升级机制
|
5天前
|
设计模式 Java Spring
来聊聊Java的反射机制(下)
来聊聊Java的反射机制(下)
7 0
|
5天前
|
Java 编译器 Spring
来聊聊Java的反射机制(上)
来聊聊Java的反射机制
9 0
|
7天前
|
Java 数据库连接
深入理解Java异常处理机制
【4月更文挑战第24天】本文将探讨Java中的异常处理机制,包括异常的概念、分类、捕获和抛出等方面。通过深入了解异常处理机制,可以帮助我们编写更加健壮的程序,提高代码的可读性和可维护性。