Java034——反射(Reflection)

news2024/9/24 1:15:09

一、Java中的反射及作用

Java的反射(reflection)机制是指在程序的运行状态中:

  1. 可以构造任意一个类的对象,
  2. 可以了解任意一个对象所属的类,
  3. 可以了解任意一个类的成员变量和方法,
  4. 可以调用任意一个对象的属性和方法。

一句话:这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。

二 认识.class类对象

这里回忆以下类的对象,然后举例说明.Class 类对象

2.1、类的对象

什么是类对象? 比如Dog类 Dog dog = new Dog();
dog 就是Dog类的对象。

2.2、Class 类型的对象

  1. 因为在Java中万物皆为对象,所以Dog 类也是一个对象,那他是谁的对象呢?
  2. 加载完类之后,在堆内存的方法区中就 产生了一个Class类型的对象(一个类只有一个Class对象),例如Dog类加载完成后,就会产生一个Dog 的Class类型对象
  3. 这个对象就像一面镜子,透过这个镜子看到类的结构,所以,才称这个为: 反射
  4. 这个对象包含了完整的类结构的信息( 类名 包名 实现接口 继承类 属性 方法 构造器 注解.... )

三、获取.class类对象

正常情况下,我们定义了一个类,要获得它的实例,是需要通过new关键字获取的,例如

class Dog{//定义了一个Dog
    //私有属性
    private String name = "Tom";
    //公有属性
    public int age = 18;
    
    //构造方法
    public Dog() {
    
    }
    //私有方法
    private void say(){
        System.out.println("private say()...");
    }
    //公有方法
    public void run(){
        System.out.println("狗急跳墙");
    }
}

class TestDemo{
    public static void main(String[] args) {
        Dog dog = new Dog();//通过关键字new获得Dog的一个对象dog

        dog.run();//dog对象调用run方法
    }
}

//运行结果
狗急跳墙

3.1、反射机制获取

一个类被加载后,类的整个结构都会被封装在Class对象中
在这里插入图片描述
下面创建一个Dod类,类里面只有一个成员方法run()

package MyPackage;

class Dog{//一个类被加载后,类的整个结构都会被封装在Class对象中
    //公有方法
    public void run(){
        System.out.println("狗急跳墙");
    }
}

public class TestDemo{
    public static void main(String[] args) throws ClassNotFoundException {
//      1、方式一:通过对象获得
//      通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object
//      类型的对象,而我不知道你具体是什么类,用这种方法
        Dog dog = new Dog();
        Class c1 = dog.getClass();

        //2、方式二:通过类名 .class 获得
        // 该方法最为安全可靠,程序性能更高
        // 这说明任何一个类都有一个隐含的静态成员变量 class
        Class c2 = Dog.class;

        //3、 方式三:forName获得
        Class c3 = Class.forName("MyPackage.Dog");

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

    }
}

//运行结果
class MyPackage.Dog
class MyPackage.Dog
class MyPackage.Dog

四、反射一般都用来做什么

  1. 可以构造任意一个类的对象,
  2. 可以了解任意一个对象所属的类,
  3. 可以了解任意一个类的成员变量和方法,
  4. 可以调用任意一个对象的属性和方法。

4.1、构造类的对象

package MyPackage;

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

class Dog {//一个类被加载后,类的整个结构都会被封装在Class对象中
    //私有属性
    private String name = "旺财";
    //公有属性
    public int age = 2;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //无参构造方法
    public Dog() {

    }

    //有参构造方法
    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //公有方法
    public void run() {
        System.out.println(this.name+"狗急跳墙,"+this.age+"岁");
    }
}

public class TestDemo {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {

        Class c1 = Class.forName("MyPackage.Dog");
        //1.通过动态调用构造方法,构造对象
        System.out.println("-------------------");
        Dog dog1 = (Dog) c1.newInstance();
        dog1.run();

        //2.通过有参构造创建对象
        System.out.println("-------------------");
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class);
        Dog dog2 = (Dog)declaredConstructor.newInstance("小黑",4);
        dog2.run();

       // 3.调用普通方法
        System.out.println("-------------------");
        Dog dog3 = (Dog)c1.newInstance();
        Method setName = c1.getDeclaredMethod("setName", String.class);
        Method setAge = c1.getDeclaredMethod("setAge", int.class);
        setName.invoke(dog3,"小黄");
        setAge.invoke(dog3,1);
        dog3.run();

        // 4.获得指定成员变量
        System.out.println("--------------------");
        Dog dog4 = (Dog)c1.newInstance();
        Field name = c1.getDeclaredField("name");
        Field age = c1.getDeclaredField("age");
        name.setAccessible(true);

        name.set(dog4,"小白");
        age.set(dog4,3);

        dog4.run();
    }
}

//运行结果
-------------------
旺财狗急跳墙,2-------------------
小黑狗急跳墙,4-------------------
小黄狗急跳墙,1--------------------
小白狗急跳墙,3

4.2、获得类的信息

4.2.1、通过反射可访问的主要描述信息

在这里插入图片描述

4.3、访问构造方法

4.3.1、Constructor 类的常用方法

在这里插入图片描述
通过 java.langreflect.Modifier 类可以解析出 getModifiers0方法的返回值所表示的修饰符信息,在该类中提供了一系列用来解析的静态方法,既能查看是否被指定的修饰符修饰,又能以字符串的形式获得所有修饰符。

Modifier 类中的常用解析方法
在这里插入图片描述
例如,判断对象 constructor 所代表的构造方法是否被 private 修饰,以及以字符串形式获得该构造方法的所有修饰符的典型代码如下:

int modifiers = constructor.getModifiers();
boolean isEmbellishByPrivate = Modifier.isPrivate(modifiers);
String embellishment = Modifier.toString(modifiers);

4.4、访问成员变量

4.4.1、Field 类的常用方法

在这里插入图片描述

4.5、访问成员方法

4.5.1、Method 类的常用方法

在这里插入图片描述

五、java类反射中所必须的类

java的类反射所需要的类并不多,它们分别是:field、constructor、method、class、object,下面将对这些类做一个简单的说明。

5.1、field类

提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。

5.2、constructor类

提供关于类的单个构造方法的信息以及对它的访问权限。这个类和field类不同,field类封装了反射类的属性,而constructor类则封装了反射类的构造方法。

5.3、method类

提供关于类或接口上单独某个方法的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。这个类不难理解,它是用来封装反射类方法的一个类。

5.4、class类

类的实例表示正在运行的 java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 class 对象。

5.5、object类

每个类都使用 object 作为超类。所有对象(包括数组)都实现这个类的方法。

5.6、示例

  1. 获取类对象
    可以使用Class.forName()方法通过类的全限定名获取类对象。

    Class<?> cls = Class.forName("com.example.MyClass");
    
  2. 创建对象
    使用类对象的newInstance()方法可以创建类的实例。

    Object obj = cls.newInstance();
    
  3. 获取方法
    使用类对象的getMethod()方法可以获取指定名称和参数类型的公共方法。

    Method method = cls.getMethod("methodName", parameterTypes);
    
  4. 调用方法
    使用方法对象的invoke()方法可以调用方法。

    Object result = method.invoke(obj, args);
    
  5. 获取字段
    使用类对象的getField()方法可以获取指定名称的公共字段。

    Field field = cls.getField("fieldName");
    
  6. 设置字段的值
    使用字段对象的set()方法可以设置字段的值。

    field.set(obj, value);
    

以上仅是一些基本的反射操作示例,反射还有很多其他强大的功能,比如获取和操作私有方法/字段、获取注解信息、动态代理等。

需要注意的是,反射操作可能会带来性能上的开销,并且因为绕过了编译时的类型检查,容易导致类型错误,在使用反射时需要谨慎操作。

六、基本的反射操作示例

package MyPackage;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Dog {//一个类被加载后,类的整个结构都会被封装在Class对象中
    //私有属性
    private String name = "旺财";
    //公有属性
    public int age = 2;

    protected String color="土黄色";

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //无参构造方法
    public Dog() {

    }

    //有参构造方法
    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //私有方法
    private void say() {
        System.out.println("我叫"+this.name+"今年"+this.age+"岁");
    }


    //公有方法
    public void run() {
        System.out.println(this.name+"狗急跳墙,"+this.age+"岁");
    }
}

public class TestDemo {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {

        //获得名字
        System.out.println("--------------------获得名字");
        Class c1 = Class.forName("MyPackage.Dog");
        System.out.println(c1.getName());//获得类完整的名字,包括包名
        System.out.println(c1.getSimpleName());//只有方法名

        //获得类的public类型的属性(只能获取public修饰的)
        System.out.println("--------------------获得属性(只能获取public修饰的)");
        Field[] fields = c1.getFields();//获得类的public属性
        for (Field field : fields) {
            System.out.println(field);
        }

        //获得类的全部属性。包括私有的
        System.out.println("--------------------获得类的全部属性");
        Field[] declaredFields = c1.getDeclaredFields();//获得类的全部属性
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        //获得类的public类型的方法。这里包括 Object 类的一些方法
        System.out.println("--------------------获得方法");
        Method[] methods = c1.getMethods();//获得本类及其父类的所有public方法
        for (Method method : methods) {
            System.out.println(method);
        }

        //获得本类所有方法
        System.out.println("--------------------获得本类所有方法");
        Method[] declaredMethods = c1.getDeclaredMethods();//获得本类所有方法
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        //获得指定的属性
        System.out.println("--------------------获得指定的属性");
        Field age = c1.getField("age");
        System.out.println(age);

        //获得指定的私有属性
        System.out.println("--------------------获得指定的私有属性");
        Field name= c1.getDeclaredField("name");
		//启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消
        name.setAccessible(true);
        System.out.println(name);

        //获取所有构造方法
        System.out.println("--------------------获取所有构造方法");
        Constructor[] constructors = c1.getConstructors();
        for(Constructor constructor : constructors){
            System.out.println(constructor.toString());//public com.ys.reflex.Person()
        }
    }
}

//运行结果
--------------------获得名字
MyPackage.Dog
Dog
--------------------获得属性(只能获取public修饰的)
public int MyPackage.Dog.age
--------------------获得类的全部属性
private java.lang.String MyPackage.Dog.name
public int MyPackage.Dog.age
protected java.lang.String MyPackage.Dog.color
--------------------获得方法
public void MyPackage.Dog.setAge(int)
public void MyPackage.Dog.run()
public java.lang.String MyPackage.Dog.getName()
public void MyPackage.Dog.setName(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
--------------------获得本类所有方法
public void MyPackage.Dog.setAge(int)
private void MyPackage.Dog.say()
public void MyPackage.Dog.run()
public java.lang.String MyPackage.Dog.getName()
public void MyPackage.Dog.setName(java.lang.String)
--------------------获得指定的属性
public int MyPackage.Dog.age
--------------------获得指定的私有属性
private java.lang.String MyPackage.Dog.name
--------------------获取所有构造方法
public MyPackage.Dog(java.lang.String,int)
public MyPackage.Dog()

Process finished with exit code 0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/758653.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3.类与对象

Java作为一种面向对象语言。支持以下基本概念&#xff1a; 多态继承封装抽象类对象实例方法重载 1.类与对象 对象&#xff1a;对象是类的一个实例&#xff0c;有状态和行为 类&#xff1a;类是一个模板&#xff0c;它描述一类对象的行为和状态 public class Dog {String bree…

编程语言里的转义字符真的叫转义字符吗?

我们先来看看别人是怎么说转义字符的&#xff1f; 这是我随便从两本书上截图来的&#xff0c;总的来说就是反斜杠加几个字符就是转义字符。 就问你晕不晕&#xff0c;斜杠加几个字符至少是两个字符&#xff0c;那还叫字符吗&#xff0c;那叫字符串吧&#xff0c;怎么取名转义字…

ModaHub魔搭社区:AI原生云向量数据库Zilliz Cloud与 LlamaIndex 集成搭建文档问答系统

目录 准备工作 准备数据 主要参数 处理数据 开始提问 本文将演示如何与集成 LlamaIndex 从指定源获取信息。 在 ChatGPT 占领各大头条时,越来越多的企业在考虑如何在它们的产品中使用 ChatGPT。一个比较大的使用场景就是如何使用 ChatGPT 来改造产品文档孱弱的搜索能力。…

【板栗糖GIS】——通过插件调教网页版b站首页

【板栗糖GIS】——通过插件调教网页版b站首页 参考视频&#xff1a;利用adblock关掉热搜和搜索推荐_哔哩哔哩_bilibili 在edge浏览器上使用的插件为Adblockplus&#xff0c;可以在插件中心查询到 拦截元素如下&#xff1a; bilibili.com##.adblock-tipsbilibili.com##.trend…

2023/7/15总结

JWT 在写项目的时候&#xff0c;本来想着把用户的数据全部存入session这样的话就不用每次都需要带用客户端的账号。后面了解到JWT&#xff0c;这个是在服务器无状态的一个方式。 具体是分为三个部分&#xff0c;第一个是头部主要存储用到的算法等信息&#xff0c;第二个是载荷…

51单片机--DS1302时钟

文章目录 DS1302引脚定义和应用电路内部结构框图寄存器的定义时序定义BCD码DS1302时钟代码 DS1302 DS1302是美国DALLAS公司推出的一款实时时钟电路芯片。它具有高性能和低功耗的特点&#xff0c;可以通过SPI三线接口与CPU进行同步通信。DS1302能够提供秒、分、时、日、星期、月…

HTML基础教程

1 什么是HTML HTML 是用来描述网页的一种语言。HTML 是一种在 Web 上使用的通用标记语言。HTML 允许你格式化文本&#xff0c;添加图片&#xff0c;创建链接、输入表单、框架和表格等等&#xff0c;并可将之存为文本文件&#xff0c;浏览器即可读取和显示。 HTML 指的是超文本…

复用cmake代码的其他方法

复用cmake代码的其他方法 函数和宏是代码复用的方法,在cmake3.18版本开始,cmake添加了cmake_language()命令,通过这个命令我们可以直接调用任意的cmake代码,无需将这些可复用的代码使用函数或者宏包起来. 当然cmake_language()命令不是为了代替函数和宏而设计的,而是希望通过…

ROS话题通信自定义msg

从21年至今写过不下10个ROS话题通信的工程&#xff0c;今天系统地记录下自定义msg的过程&#xff0c;让后来者少走弯路。 1、自定义msg简介 在 ROS 通信协议中&#xff0c;数据载体是一个较为重要组成部分&#xff0c;ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:Stri…

redis7单节点、主从、哨兵、集群的安装和配置

redis7安装和配置 2022年4月份正式发布的redis 7.0&#xff0c;是目前历史上变化最大的版本&#xff0c;有超过50个以上的新增命令 官网&#xff1a; https://redis.io中文镜像 http://redis.cn中文学习网&#xff1a; https://redis.com.cn/redis版本&#xff0c;建议升级…

Spring初识(一)

一.Spring 是什么&#xff1f; 首先我们来看看官网的解释 Spring 使每个人都可以更快、更轻松、更安全地进行 Java 编程。Spring 对速度、简单性和生产力的关注使其成为 世界上最受欢迎的 Java框架。 这里我简单的说明一下什么是spring? 我们通常所说的 Spring 指的是 Sprin…

纯CSS实现的卡片切换效果

纯CSS实现的卡片切换效果 无需JS就可以实现限于纯静态页面产品展示不需要轮播,自动切换 示例代码 <template><div class"example-css-tab"><div class"container dwo"><div class"card"><input type"radio"…

(续2)选择屏幕

分页签组件 表单控件 双击空白处----出现右侧编辑框 其中 fixed的意思是固定几列。 可修改 回车会自动复原 回车会自动复原 原因 在pai中检查字段却没有做任何操作。 打算新建一个表单 表单中指定选择行. 按钮扩展. 执行后 修改列名. 创建一个moudle 修改后不会复原. …

前端卷算法系列(七)

前端卷算法系列&#xff08;六&#xff09; 删除有序数组中的重复项 给你一个 升序排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯…

用 pesq 给 soundfile 读取的录音数据打分

音频文件来源 NOIZEUS: Noisy speech corpus - Univ. Texas-Dallas 很多python库都能计算pesq分数&#xff0c;如pypesq跟pesq两个库&#xff0c;这里讲的的是pesq的库 import soundfile as sf from pesq import pesq# 读取音频文件 audio_clean, src sf.read(./audio/NOIZE…

[南京大学]-[软件分析]课程学习笔记(三)-数据流分析

Data Flow Analysis Overview of Data Flow Analysis may analysis: outputs information that may be true(over-approximation) 可能正确&#xff0c;超出近似over-approximationmust analysis: outputs information that must be true (under-approximation) 必须正确&…

WPF RadioButton单选失效

文章目录 前言解决方案 前言 我最近在照着教程敲代码&#xff0c;WPF深入讲解第8集。发现RadioButton按钮点击触发器不是单选的。WPF中单选和复选通过RadioButton和CheckButton来进行区分。我点击另一个RadioButton之后&#xff0c;之前的Radiobutton没有取消选择。 解决方案 …

文件上传下载概述

So Easy系列之文件上传下载教程 文件上传下载概述 什么是文件上传下载 所谓文件上传下载就是将本地文件上传到服务器端&#xff0c;从服务器端下载文件到本地的过程。例如目前网站需要上传头像、上传下载图片或网盘等功能都是利用文件上传下载功能实现的。 文件上传下载实际上…

Shell运行原理以及Linux中的权限问题

目录 一、shell的运行原理 二、Linux权限的概念 2.1 用户账号切换 2.2 仅提升当前指令的权限 2.3 将普通用户添加到信任列表 三、Linux权限管理 3.1 文件访问者的分类 3.2 文件类型和访问权限 3.3 字符权限值的表示方法 3.3.1 字符表示方法 3.3.2 八进制表示法 3.4…

CSS学习04

文章目录 1.精灵图1.1 为什么需要精灵图1.2 精灵图&#xff08;sprites&#xff09;的使用 2.字体图标2.1 字体图标的产生2.2 字体图标的优点**2.3** **字体图标的下载****2.4** **字体图标的引入**2.5 字体图标的追加 3.CSS 三角3.1 介绍 4.CSS 用户界面样式4.1 鼠标样式 curs…