从初学者到专家:Java反射的完整指南

这篇具有很好参考价值的文章主要介绍了从初学者到专家:Java反射的完整指南。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

从初学者到专家:Java反射的完整指南,java,开发语言

从初学者到专家:Java反射的完整指南,java,开发语言

一.反射的概念及定义

Java 的反射( reflection )机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息;这种动态获取信 息以及动态调用对象方法的功能称为java语言的反射(reflection)机制。
反射的用途
  1. 在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统 应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法 。
  2. 反射最重要的用途就是开发各种通用框架,比如在spring中,我们将所有的类Bean交给spring容器管理,无 论是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring根据这些信息,需要创建那些Beanspring就动态的创建这些类。

反射不同时期的类型

Java程序中许多对象在运行时会出现两种类型:运行时类型(RTTI)和编译时类型。

例如:Person p = new Student();

  • 编译时类型是在编译时期确定的,它是变量声明时所使用的类型。在上面的示例代码中,变量p的编译时类型是Person,因为它是通过Person p的声明来定义的。
  • 运行时类型是在程序运行时确定的,它是实际分配给对象的类型。在上面的示例代码中,使用new Student()创建了一个Student对象,并将其赋值给变量p,因此在运行时,p的类型是Student

通过反射,可以获取对象的运行时类型。例如,可以使用p.getClass()方法获取p对象的实际类型(运行时类型),并进行相应的操作。

除了获取对象的运行时类型,反射还可以获取类的详细信息,包括类的名称、父类、接口、构造函数、字段和方法等。通过获取类的信息,可以做一些动态的操作,如动态创建对象、调用方法、访问字段等。

注意:Java文件被编译后,生成了.class文件,JVM此时就要去解读.class文件 ,被编译后的Java文件.class也被JVM解析为一个对象,这个对象就是 java.lang.Class .这样当程序在运行时,每个java文件就最终变成了Class类对象的一个实例。我们通过Java的反射机制应用到这个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类 .

二.反射相关的示例

从初学者到专家:Java反射的完整指南,java,开发语言

在Java中,反射通过java.lang.reflect包中的类和接口来实现。

反射的基本信息:

  1. ClassClass是反射的核心类之一,它表示一个类或接口的运行时对象。通过Class类可以获取和操作类的信息,如类的名称、父类、接口、构造函数、字段和方法等。

  2. ConstructorConstructor类表示类的构造函数。通过Constructor类可以创建类的实例,调用构造函数并实例化对象。

  3. Field类:Field类表示类的字段(成员变量)。通过Field类可以获取和修改字段的值,以及访问字段的属性信息。

  4. Method类:Method类表示类的方法。通过Method类可以调用类的方法,传递参数并获取返回值。

  5. 获取Class对象:可以使用多种方式获取Class对象,如使用类名调用Class.forName()方法、通过类的实例调用getClass()方法、或者直接通过类字面常量使用SomeClass.class

  6. 实例化对象:通过Class对象和Constructor类可以实例化类的对象。可以使用newInstance()方法创建无参构造函数的实例,或者使用Constructor类的newInstance()方法传递参数创建有参构造函数的实例。

  7. 调用方法:通过Class对象和Method类可以调用类的方法。可以使用invoke()方法传递对象和参数来调用方法,并获取返回值。

  8. 访问字段:通过Class对象和Field类可以访问类的字段。可以使用get()set()方法获取和修改字段的值,以及使用getField()getDeclaredField()方法获取字段对象。


 

 2.1获取Class对象的三种方式

在使用反射时,我们需要先获取要反射的类的  Class 对象,因为  Class 对象提供了许多有用的方法和操作,用于在运行时检查和操作类的结构、属性和方法。
你可以这样理解:当我们使用反射时,需要先创建类的  Class 对象,这个对象相当于是类的身份证。通过这个身份证,我们可以了解这个类的各种信息。

通过 Class 对象,我们可以做以下事情:

  • 创建对象实例:通过 Class 对象的 newInstance() 方法可以动态地创建类的对象实例。

  • 获取类的信息:通过 Class 对象可以获取类的名称、修饰符、包名、父类、接口、字段和方法等信息。

  • 获取和设置字段值:通过 Class 对象和字段名称,可以获取和设置类的字段的值。

  • 调用方法:通过 Class 对象和方法名称,可以调用类的方法。

  • 动态加载类:通过 Class.forName() 方法可以在运行时动态加载类。

  • 进行注解处理:通过 Class 对象可以获取类上的注解信息,并进行相应的处理。

通过先创建类的  Class 对象,我们可以在运行时对类进行检查和操作,而无需提前知道类的具体信息。这使得代码更加灵活,可以在运行时根据需要动态地处理和操作类。
举例子解释:
1.先创建出Student类:
package demo1;

/**
 * @Author 12629
 * @Description:
 */
class Student {
    //私有属性name
    private String name = "Classmates";
    //公有属性age
    public int age = 18;
    //不带参数的构造方法
    public Student(){
        System.out.println("Student()");
    }

    private Student(String name,int age) {
        this.name = name;
        this.age = age;
        System.out.println("Student(String,name)");
    }

    private void eat(){
        System.out.println("i am eat");
    }

    public void sleep(){
        System.out.println("i am pig");
    }

    private void function(String str) {
        System.out.println(str);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

第一种,使用 Class.forName("类的全路径名"); 静态方法。(前提:已明确类的全路径名。)

public class Test {

    public static void main(String[] args) {
        //获取Class对象有三种方式之一
        //第一种
        Class<Student> c1 = null;
        try {
            c1 = Class.forName("demo1.Student");
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        
    }
}

这段代码的作用是将字符串 "demo1.Student" 作为参数传递给 Class.forName() 方法,并将返回的 Class 对象赋值给变量 c1

为什么有调异常呢?

这是因为 Class.forName() 方法尝试根据提供的类名加载对应的类,但如果在类路径中找不到该类,就会抛出 ClassNotFoundException 异常。

注意:使用 Class.forName() 方法时,需要提供类的全限定名,即包括包名和类名。

从初学者到专家:Java反射的完整指南,java,开发语言

 

第二种:使用 .class 方法。(仅适合在编译前就已经明确要操作的 Class)

public class Test {

    public static void main(String[] args) {
        //第二种
        Class<Student> c2 = Student.class;

    }
}

段代码的作用是通过类字面常量 Student.class 来获取 Student 类的 Class 对象,并将其赋值给变量 c2

使用类字面常量的方式非常简单,只需要在类名后面加上 .class 就可以直接访问该类的 Class 对象。


第三种 ,使用类对象的 getClass() 方法
public class Test {

        //第三种
        Student student = new Student();
Class<? extends Student> c3 = student.getClass();

    }

我们创建了一个 Student 类的对象 student,然后通过 student.getClass() 方法获取该对象的运行时类的 Class 对象,并将其赋值给类型为 Class<? extends Student> 的变量 c3

使用对象的 getClass() 方法可以在运行时获取对象所属类的 Class 对象。这种方式适用于当我们有一个对象实例,想要获取其对应的类信息时。

 


注意:一个类在 JVM 中只会有一个 Class 实例

public class Test {

    /*
    Class对象 只有一个
     */
    public static void main(String[] args) {
        //获取Class对象有三种方式
        //生成的对象只有一个
        //第一种
        Class<Student> c1 = null;
        try {
            c1 = Class.forName("demo1.Student");
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //第二种
        Class<Student> c2 = Student.class;

        //第三种
        Student student = new Student();
Class<? extends Student> c3 = student.getClass();

        System.out.println(c1 == c2);
        System.out.println(c1 == c3);
    }
}

运行例图如下: 

从初学者到专家:Java反射的完整指南,java,开发语言


2.2 Class对象的使用 

上一步我们已经创建好 Class 对象了,可以使用它来进行各种操作。

注意:所有和反射相关的包都在 import java.lang.reflect 包下面。

 

使用前我们先了解Class类中的相关的方法有哪些
1.常用获得类相关的方法:
从初学者到专家:Java反射的完整指南,java,开发语言

 2.常用获得类中属性相关的方法(以下方法返回值为Field相关):

从初学者到专家:Java反射的完整指南,java,开发语言

3. 获得类中构造器相关的方法(以下方法返回值为Constructor相关

4.从初学者到专家:Java反射的完整指南,java,开发语言

4.获得类中方法相关的方法(以下方法返回值为Method相关 

从初学者到专家:Java反射的完整指南,java,开发语言

5.获得类中注解相关的方法 (了解即可)

从初学者到专家:Java反射的完整指南,java,开发语言

代码案例如下:

package demo1;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectClassDemo {

/*reflectNewInstance() 方法使用反射创建一个类的实例,然后输出该实例。

它通过 Class.forName() 方法获取类的 Class 对象,然后使用 newInstance() 方法创建实例。
*/

    public static void reflectNewInstance() {
        Class<?> classStudent = null;
        try {
            // 获取类的Class对象
            classStudent = Class.forName("demo1.Student");
            // 使用newInstance()方法创建类的实例
            Student student = (Student) classStudent.newInstance();
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
/*
reflectPrivateConstructor() 方法演示了如何使用反射调用私有的构造方法。
它通过 Class.forName() 方法获取类的 Class 对象,然后使用 getDeclaredConstructor() 方法获取私有构造方法,并通过 setAccessible(true) 设置访问权限。
最后,使用 newInstance() 方法创建实例并输出。
*/

    public static void reflectPrivateConstructor() {
        Class<?> classStudent = null;
        try {
            classStudent = Class.forName("demo1.Student");
            // 获取私有构造方法
            Constructor<?> constructor = classStudent.getDeclaredConstructor(String.class, int.class);
            constructor.setAccessible(true); // 设置私有构造方法可访问
            // 调用私有构造方法创建实例
            Student student = (Student) constructor.newInstance("xiaoming", 15);
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
/*
reflectPrivateField() 方法展示了如何使用反射操作私有字段。
它通过 Class.forName() 方法获取类的 Class 对象,然后使用 getDeclaredField() 方法获取私有字段,并通过 setAccessible(true) 设置访问权限。
接下来,使用 set() 方法给字段设置新的值,并输出结果。
*/
    public static void reflectPrivateField() {
        Class<?> classStudent = null;
        try {
            classStudent = Class.forName("demo1.Student");
            // 获取私有字段
            Field field = classStudent.getDeclaredField("name");
            field.setAccessible(true); // 设置私有字段可访问(这个特别要注意,不然会报异常)
            // 创建类的实例
            Student student = (Student) classStudent.newInstance();
            // 设置私有字段的值
            field.set(student, "caocao");
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
/*
reflectPrivateMethod() 方法演示了如何使用反射调用私有方法。
它通过 Class.forName() 方法获取类的 Class 对象,然后使用 getDeclaredMethod() 方法获取私有方法,并通过 setAccessible(true) 设置访问权限。
最后,使用 invoke() 方法调用方法并输出结果。
*/
    public static void reflectPrivateMethod() {
        Class<?> classStudent = null;
        try {
            classStudent = Class.forName("demo1.Student");
            // 获取私有方法
            Method method = classStudent.getDeclaredMethod("function", String.class);
            method.setAccessible(true); // 设置私有方法可访问
            // 创建类的实例
            Student student = (Student) classStudent.newInstance();
            // 调用私有方法
            method.invoke(student, "我是一个反射的参数!");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
    // 反射实例化类
     reflectNewInstance();

    // 反射调用私有构造方法
     reflectPrivateConstructor();

    // 反射操作私有字段
     reflectPrivateField();

    //反射调用私有方法
    reflectPrivateMethod();

   }
}

 运行例图如下:

从初学者到专家:Java反射的完整指南,java,开发语言

三.总结

反射的优缺点如下:

优点:

  1. 动态性和灵活性:反射允许在运行时动态地获取和操作类的信息,使程序能够根据需要适应不同的情况和需求。它提供了灵活的实例化、字段访问和方法调用,以及动态代理和处理注解等功能,增强了程序的灵活性和可扩展性。

  2. 泛型操作:反射使得可以在运行时获取泛型类型的信息,并进行相应的操作。这对于编写通用代码和框架非常有用,可以在不知道具体类型的情况下进行更多的操作和处理。

  3. 框架和库的开发:反射广泛应用于框架和库的开发中。通过反射,可以在运行时动态地加载和使用类,根据配置文件或用户输入进行相应的操作,使框架和库具有更强的扩展性和适应性。

缺点:

  1. 性能影响:反射的操作通常比直接调用方法或访问字段的性能要低。使用反射会引入额外的开销,包括方法调用和类型检查等。因此,频繁使用反射可能导致程序的性能下降。

  2. 安全性问题:反射可以绕过访问权限的限制,可以访问和修改私有成员,并执行敏感操作。这可能导致安全性问题,特别是在处理不受信任的代码或用户输入时需要格外小心。

  3. 编码复杂性和可读性降低:反射的使用可能会增加代码的复杂性和可读性降低。由于反射是在运行时动态进行的,因此一些问题只能在运行时才能被发现,而不是在编译时。这可能导致调试和维护过程中的困难。

  4. 局限性:反射有一些局限性,例如无法操作编译时不存在的类、字段或方法;无法操作原始类型的字段等。此外,由于反射是基于运行时信息的,因此在某些情况下可能无法获得期望的结果。

反射是Java语言中的一项强大特性,它允许程序在运行时动态地获取、操作和修改类、对象、字段和方法的信息。通过反射,我们可以实现灵活的类实例化、字段访问和方法调用,以及处理注解和实现动态代理等功能。然而,反射的使用应谨慎,需要平衡灵活性、性能和安全性,并注意其局限性和注意事项。总而言之,反射为Java开发者提供了强大的工具,使得程序可以在运行时动态地适应不同的需求和场景。

从初学者到专家:Java反射的完整指南,java,开发语言文章来源地址https://www.toymoban.com/news/detail-843402.html

到了这里,关于从初学者到专家:Java反射的完整指南的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 掌握 JavaScript:从初学者到高级开发者的完整指南之JavaScript对象(二)

    可以大体分页3大类: 第一类:基本对象,我们主要学习Array和JSON和String 第二类:BOM对象,主要是和浏览器相关的几个对象 第三类:DOM对象,JavaScript中将html的每一个标签都封装成一个对象 1.1.1 基本对象 1.1.1.1 Array对象 语法格式 Array对象时用来定义数组的。常用语法格式有如下

    2024年02月07日
    浏览(61)
  • 【001-Java基础练习】-适合初学者的练习

    用于巩固java基础知识,初学者多练多敲,熟悉代码,熟悉语法就ok。 练习1、从控制台获取Java、ps、HTML三门课程的成绩,计算总分和平均分(平均分保留2位小数,要求四舍五入),输出总分和平均分 练习2、控制台输入学生信息,学号 姓名 性别 年龄,控制台展示学生信息如

    2024年02月01日
    浏览(50)
  • Java初学者也可以实现的图书系统小练习

           大家好呀,我是小戴🙌🙌🙌        最近大家不是快期末了嘛,有没有泡图书馆呀?今天的学习内容跟图书馆有关,没错,就是图书管理系统,但是目前所学知识水平有限,这是一个很简单的图书系统小练习。不要急,我们的 目的是在于如何使用已知学过的知识进

    2023年04月09日
    浏览(47)
  • 云原生架构:面向初学者的完整概述

    云原生计算基金会 (CNCF)是一个开源软件基金会,位于Linux基金会内,由谷歌,IBM,英特尔,博克斯,思科和VMware等知名公司组成,致力于使云原生计算无处不在和可持续。 云原生技术使企业能够在现代动态环境中设计和部署可扩展的应用程序,包括公共云、私有云和混合

    2024年01月15日
    浏览(47)
  • 爬虫,初学者指南

    1.想目标地址发起请求,携带heards和不携带heards的区别 request模块用于测速发送数据的连通性,通过回复可以看出418,Connection:close表示未获取到服务器的返回值,需要添加heards信息,此服务器拒绝非浏览器发送的请求。 上图可以看出添加了头信息headers之后成功获取了返回值

    2024年02月07日
    浏览(61)
  • Groovy初学者指南

    本文已收录至Github,推荐阅读 👉 Java随想录 微信公众号:Java随想录 目录 摘要 Groovy与Java的联系和区别 Groovy的语法 动态类型 元编程 处理集合的便捷方法 闭包 运算符重载 控制流 条件语句 循环语句 字符串处理 字符串插值 多行字符串 集合与迭代 列表(List) 映射(Map) 迭代器

    2024年02月05日
    浏览(62)
  • 算法初学者指南:理解排序算法

            排序是计算机科学中的基本问题之一,也是数据处理的核心步骤。从最简单的个人项目到复杂的工业级应用,排序都扮演着关键角色。本文将介绍四种常见的排序算法:冒泡排序、插入排序、快速排序和堆排序,旨在帮助算法初学者理解这些基本概念。         冒泡

    2024年01月23日
    浏览(59)
  • 大语言模型初学者指南 (2023)

    大语言模型 (LLM) 是深度学习的一个子集,它正在彻底改变自然语言处理领域。它们是功能强大的通用语言模型,可以针对大量数据进行预训练,然后针对特定任务进行微调。这使得LLM能够拥有大量的一般数据。如果一个人想将LLM用于特定目的,他们可以简单地根据各自的目的

    2024年02月11日
    浏览(67)
  • Spark初学者指南:使用指南和示例

    本文介绍了如何使用Spark处理大规模数据集,并提供了一个Scala编写的Word Count示例,指导您从安装和配置到编写和运行Spark应用程序。无需担心,即使您是Spark初学者,也可以按照本文的步骤来学习和使用Spark。 Spark是一个流行的分布式计算框架,用于处理大规模数据集。它使

    2024年02月06日
    浏览(63)
  • Linux configure命令精通:一个完整的初学者教程

    Linux中的configure命令用于配置和准备软件包以进行编译和安装。它是一个常见的脚本,由软件开发者提供,用于检查系统的环境和依赖关系,并相应地生成Makefile,从而可以在特定的Linux系统上编译和安装软件包。 开源软件中一般都有一个名为\\\"configure\\\"的脚本文件,需要运行这

    2024年02月03日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包