Java基础篇 - 反射机制

简介: Java基础篇 - 反射机制

前言

我们在日常的开发中其实很少写反射,只知道有这么个东西,然而对于为什么用它却知之甚少。实际上,我们的框架中大量用到了这种概念。例如,当我们需要获取某个包下的类,并进行统一处理时,我们就需要知道这些类的名称,通过new 创建实例对象;但是如果我们不知道类有哪些,或者说我们只是在最底层封装的代码,对于用户定义的类名称根本不知道,这个时候,反射就起到作用了。下面让我们一起学习一下Java的动态获取的信息以及动态调用对象的方法的反射机制吧。

一、反射概述

反射: 反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的所有成员(成员变量,成员方法,构造方法)。如果从上面的反射的定义上来看,反射是什么不太容易理解。通俗的讲,我们日常开发中使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。

Apple apple = new Apple(); //直接初始化
apple.setPrice(4);

上面这样子进行类对象的初始化,我们可以理解为“正射”。而反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用 JDK 提供的反射 API 进行反射调用:

Class clz = Class.forName("com.chenshuyi.reflect.Apple");
Method method = clz.getMethod("setPrice", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);

上面两段代码的执行结果,其实是完全一样的。但是其思路完全不一样,第一段代码在未运行时就已经确定了要运行的类(Apple),而第二段代码则是在运行时通过字符串值才得知要运行的类(com.chenshuyi.reflect.Apple)。

所以说什么是反射?反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

1.1 反射过程

* 反射过程描述:
  首先会有一个java文件,然后会编译成.class文件,字节码文件(会里面包含了当前这个类里面所有数据信息)会加载到内存,
JVM负责管理,为了方便管理,它会将每一个加载进来的class文件生成一个对应的Class对象<对应的类型就是生成字节码之前java文件
对应的类名类型>,这个class对象它就代表对应的类(java文件),那么我们可以通过这个类对应的class对象获得这个类中的数据信息
(成员变量、成员方法、构造方法)

网络异常,图片无法展示
|

结论:我们使用反射,其实就是获得某个类的Class对象,然后通过这个Class对象对这个类中的成员数据进行处理(调用执行)!

1.2 反射前提

我们必须得到这类的字节码文件(.class文件) ====>>> Class对象

Class:它是一个类,Class类的实例表示正在运行的Java应用程序中的类和接口。我们的一个类最终在内存中都是字节码,然后JVM会对每一个类对应的字节码生成一个Class对象!然后通过这个Class对象就可以访问类中的所有信息!

* Java类与Class对象的对应关系:
    一个类           =======>>> 一个Class对象
    类中的一个成员变量 ======>>>> 一个Field对象(在程序运行阶段为类中的成员变量赋值或者获取值)  
    类中的一个成员方法 ======>>>> 一个Method对象(在程序运行阶段执行类中的成员方法)
    类中的一个构造方法 ======>>>> 一个Constructor对象(在程序运行阶段创建当前类的一个实例对象)

二、 如何获得类对应的Class对象

2.1 方式一:通过Class类的静态方法

// 参数:某个类的全限定类名(包含包名和类名)
static Class<?> forName(String className) // 返回与给定字符串名称的类或接口相关联的Class对象。
Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student"); // 给定类的全限定类名书写错误,会导致异常!ClassNotFoundException

2.2 方式二:通过类的对象的getClass方法

// 先获得到某个类的对象
Student student = new Student();
// 通过对象调用方法获得Class对象!
Class<? extends Student> clazz1 = student.getClass(); // getClass()方法是从Object继承过来的

2.3 方式三:通过类名的静态属性class

// 通过类名.class获得Class对象!
Class<Student> clazz = Student.class;

2.4 Class对象的常用功能

* String getSimpleName(); // 获得类名字符串:类名
* String getName();  // 获得类全名:包名+类名
* T newInstance() ;  // 创建Class对象关联类的对象(公有构造方法)
public static void main(String[] args) throws Exception{
    // 获得Student类对应的Class对象
    Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
    System.out.println(clazz.getSimpleName()); // Student
    System.out.println(clazz.getName()); // com.itheima.demo01_反射.Student
    Object obj = clazz.newInstance(); // com.itheima.demo01_反射.Student@1647C
}

三、通过Class对象操作构造方法

3.1 获得Constructor对象

Constructor<?>[] getConstructors() // 通过Class对象获得类中所有的公有的构造方法对象  
Constructor<?>[] getDeclaredConstructors() // 通过Class对象获得类中所有的构造方法对象(公有私有,全都要)  
Constructor<T> getConstructor(Class<?>... parameterTypes) // 通过Class对象获得类中指定公有的构造方法对象!  【★★★★★】
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) // 通过Class对象获得类中指定的构造方法!(公有私有,全都要)  
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
    // 获得Student类对应的Class对象
    Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
    /////////////////////通过Class对象获得Student类中构造方法对应的Constructor对象!///////////////////
    // 获得类中所有的公共的构造方法对应的对象数组
    Constructor<?>[] constructors = clazz.getConstructors();
    System.out.println(Arrays.toString(constructors)); //
    // 获得类中所有的构造方法对应的对象数组(包括私有的!)
    Constructor<?>[] dc = clazz.getDeclaredConstructors();
    System.out.println(Arrays.toString(dc));
    // 获得类中指定构造方法对应的对象
    //Constructor<?> c1 = clazz.getConstructor();
    Constructor<?> c1 = clazz.getDeclaredConstructor();
    System.out.println(c1);
    Constructor<?> c2 = clazz.getDeclaredConstructor(String.class, int.class);
    System.out.println(c2);
}

3.2 通过构造方法对象创建类的实例对象

T newInstance(Object... initargs) 使用由此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。  
public static void main(String[] args) throws Exception{
    // 获得Student类对应的Class对象
    Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
    // 获得Student类中指定的构造方法对象!
    Constructor<?> constructor = clazz.getConstructor();
    // 根据构造方法对象创建Student类的实例对象!
    Object o = constructor.newInstance();
    System.out.println(o); // com.itheima.demo01_反射.Student@14ae5a5
    Student s = (Student) o;
}

注意:一旦类中的构造方法使用private修饰,那么通过构造方法对象创建了的实例对象就要发生变化!

public static void main(String[] args) throws Exception{
    // 获得Student类对应的Class对象
    Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
    // 获得Student类中指定的构造方法对象!
    Constructor<?> constructor = clazz.getDeclaredConstructor(); // 有变动!
    // 暴力访问!
    constructor.setAccessible(true); // 有变动!
    // 根据构造方法对象创建Student类的实例对象!
    Object o = constructor.newInstance();
    System.out.println(o); // com.itheima.demo01_反射.Student@14ae5a5
    Student s = (Student) o;
}

四、 通过Class对象操作成员方法对象

4.1 获得成员方法的Method对象

Method getMethod(String name, Class<?>... parameterTypes) // 返回指定公有的方法对象! 【★★★★★】
Method[] getMethods() // 返回所有公有的方法对象构成的数组,包括由类或接口声明的对象以及从超类和超级接口继承的类。
Method getDeclaredMethod(String name, Class<?>... parameterTypes)// 返回指定的方法对象!(私有的也可以玩)
Method[] getDeclaredMethods() // 返回所有的方法对象构成的数组,包括public,protected,default(package)访问和私有方法,但不包括继承方法。
public static void main(String[] args) throws Exception{
    // 获得Student类对应的Class对象
    Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
    // 获得Student类以及父类(接口)中所有的公有成员方法
    Method[] methods1 = clazz.getMethods();
    for (Method method : methods1) {
        System.out.println(method);
    }
    System.out.println("====================================");
    // 获得Student类中所有的成员方法(包含私有)
    Method[] methods2 = clazz.getDeclaredMethods();
    for (Method method : methods2) {
        System.out.println(method);
    }
    System.out.println("====================================");
    // 获得指定方法(公有无参)
    Method show1 = clazz.getMethod("show1");
    // 获得指定方法(私有无参)
    Method show2 = clazz.getDeclaredMethod("show2");
    // 获得指定方法(公有带参数)
    Method method1 = clazz.getMethod("method1", String.class);
    // 创建类的对象
    Object obj = clazz.getConstructor().newInstance();
    //  获得成员方法对象,期目的是为了执行这个成员方法
    Object result = show1.invoke(obj);// 参数1:当前方法所属对象!   参数2:show1方法的参数值(若定义show1方法没有参数,这里就不写内容)
    System.out.println(result); // null  [show1方法定义的时候没有返回值]
    System.out.println("======================");
    // 执行带有参数的公有成员方法
    Object o = method1.invoke(obj, "666");
    System.out.println(o);
}

4.2 通过Method对象执行指定方法

/*
    参数obj:当前Method对象所属的类的对象!【实际的对象值!】
    参数args:为方法对象的形参赋值(若方法对象对应的方法定义没有参数,这里可以空着不写<这里是一个可变参数>) 
    返回值Object:执行完当前方法的返回结果!
*/
Object invoke(Object obj, Object... args)  // 在具有指定参数的指定对象上调用此 方法对象表示的基础方法。  【★★★★★】
public static void main(String[] args) throws Exception{
    // 获得Student类对应的Class对象
    Class<?> clazz = Class.forName("com.itheima.demo01_反射.Student");
    // 创建类的对象
    Object obj = clazz.getConstructor().newInstance();
    // 获得指定方法(公有无参)
    Method show1 = clazz.getMethod("show1");
    // 获得指定方法(公有带参数)
    Method method1 = clazz.getMethod("method1", String.class);
    //  获得成员方法对象,期目的是为了执行这个成员方法
    Object result = show1.invoke(obj);// 参数1:当前方法所属对象!   参数2:show1方法的参数值(若定义show1方法没有参数,这里就不写内容)
    System.out.println(result); // null  [show1方法定义的时候没有返回值]
    System.out.println("======================");
    // 执行带有参数的公有成员方法
    Object o = method1.invoke(obj, "666");
    System.out.println(o);
}

五、 通过Class对象操作成员变量

5.1 获得成员变量的Field对象

public static void main(String[] args) throws Exception{
    // 获得Student类对应的Class对象
    Class<?> clazz = Student.class;
    // 获得类的实例
    //Object obj = clazz.newInstance();
    Student s = (Student) clazz.newInstance();
    // 获得公有成员变量对应的Field对象(很多)
    Field[] fields = clazz.getFields();
    System.out.println(Arrays.toString(fields));
    // 获得指定公有成员变量
    Field name = clazz.getField("name");
    System.out.println(name);
    // 获得所有的成员变量对应的Field对象(公有私有均可)
    Field[] declaredFields = clazz.getDeclaredFields();
    System.out.println(Arrays.toString(declaredFields));
    // 获得指定成员变量(公有私有均可)
    Field age = clazz.getDeclaredField("age");
    System.out.println(age);
}

5.2 通过Field对象获取成员变量的值或者设置值

public static void main(String[] args) throws Exception{
    // 获得Student类对应的Class对象
    Class<?> clazz = Student.class;
    // 获得类的实例
    //Object obj = clazz.newInstance();
    Student s = (Student) clazz.newInstance();
    /////////////////////////////////操作公有的成员变量////////////////////////
    // 获得指定公有成员变量
    Field name = clazz.getField("name");
    // 为公有的成员变量设置值
    System.out.println(s.getName()); // null
    name.set(s,"jack");
    // 获得指定公有成员变量的值
    System.out.println(name.get(s)); // jack
    System.out.println(s.getName()); // jack
    /////////////////////////////////操作私有的成员变量////////////////////////
    // 获得指定成员变量(公有私有均可)
    Field age = clazz.getDeclaredField("age");
    // 暴力访问
    age.setAccessible(true);
    // 获取值
    System.out.println(age.get(s)); // 0
    // 设置值
    age.set(s,38);
    System.out.println(age.get(s)); // 38
}

6 后记

开发过程中 我们不会通过反射的机制来获取对象,主要是因为反射的代码阅读性不好,不易理解。其次,不易于代码维护。我们会在系统架构时将其用于底层代码封装或者自定义工具类,除此之外,反射在开发中基本很少用到!


相关文章
|
20天前
|
存储 算法 Java
Java HashSet:底层工作原理与实现机制
本文介绍了Java中HashSet的工作原理,包括其基于HashMap实现的底层机制。通过示例代码展示了HashSet如何添加元素,并解析了add方法的具体过程,包括计算hash值、处理碰撞及扩容机制。
|
11天前
|
XML 安全 Java
Java反射机制:解锁代码的无限可能
Java 反射(Reflection)是Java 的特征之一,它允许程序在运行时动态地访问和操作类的信息,包括类的属性、方法和构造函数。 反射机制能够使程序具备更大的灵活性和扩展性
19 5
Java反射机制:解锁代码的无限可能
|
5天前
|
安全 IDE Java
Java反射Reflect机制详解
Java反射(Reflection)机制是Java语言的重要特性之一,允许程序在运行时动态地获取类的信息,并对类进行操作,如创建实例、调用方法、访问字段等。反射机制极大地提高了Java程序的灵活性和动态性,但也带来了性能和安全方面的挑战。本文将详细介绍Java反射机制的基本概念、常用操作、应用场景以及其优缺点。 ## 基本概念 ### 什么是反射 反射是一种在程序运行时动态获取类的信息,并对类进行操作的机制。通过反射,程序可以在运行时获得类的字段、方法、构造函数等信息,并可以动态调用方法、创建实例和访问字段。 ### 反射的核心类 Java反射机制主要由以下几个类和接口组成,这些类
15 2
|
10天前
|
存储 缓存 安全
🌟Java零基础:深入解析Java序列化机制
【10月更文挑战第20天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
18 3
|
10天前
|
安全 Java UED
深入理解Java中的异常处理机制
【10月更文挑战第25天】在编程世界中,错误和意外是不可避免的。Java作为一种广泛使用的编程语言,其异常处理机制是确保程序健壮性和可靠性的关键。本文通过浅显易懂的语言和实际示例,引导读者了解Java异常处理的基本概念、分类以及如何有效地使用try-catch-finally语句来处理异常情况。我们将从一个简单的例子开始,逐步深入到异常处理的最佳实践,旨在帮助初学者和有经验的开发者更好地掌握这一重要技能。
16 2
|
12天前
|
Java 数据库连接 开发者
Java中的异常处理机制####
本文深入探讨了Java语言中异常处理的核心概念,通过实例解析了try-catch语句的工作原理,并讨论了finally块和throws关键字的使用场景。我们将了解如何在Java程序中有效地管理错误,提高代码的健壮性和可维护性。 ####
|
13天前
|
Java
Java中的反射机制与应用实例
【10月更文挑战第22天】Java作为一门面向对象的编程语言,提供了丰富的特性来支持对象的创建、操作和交互。其中,反射机制是Java的一项核心特性,它允许程序在运行时动态地获取类的信息、创建对象、调用方法、访问属性等。本文将从三个部分探讨Java中的反射机制及其应用实例:一是反射机制的基本概念和原理;二是反射机制在Java中的应用场景;三是通过实例深入理解反射机制的使用方法和技巧。
14 4
|
14天前
|
安全 Java 程序员
深入浅出Java中的异常处理机制
【10月更文挑战第20天】本文将带你一探Java的异常处理世界,通过浅显易懂的语言和生动的比喻,让你在轻松阅读中掌握Java异常处理的核心概念。我们将一起学习如何优雅地处理代码中不可预见的错误,确保程序的健壮性和稳定性。准备好了吗?让我们一起踏上这段旅程吧!
22 6
|
11天前
|
存储 运维 Java
💻Java零基础:深入了解Java内存机制
【10月更文挑战第18天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
23 1
|
15天前
|
存储 Java 程序员
Java面试加分点!一文读懂HashMap底层实现与扩容机制
本文详细解析了Java中经典的HashMap数据结构,包括其底层实现、扩容机制、put和查找过程、哈希函数以及JDK 1.7与1.8的差异。通过数组、链表和红黑树的组合,HashMap实现了高效的键值对存储与检索。文章还介绍了HashMap在不同版本中的优化,帮助读者更好地理解和应用这一重要工具。
35 5