java反射常被面试官问到的四个问题

文章目录

  • 说一下反射机制?
  • 如何使用反射?
  • 反射有什么优缺点?
  • 目前常见的反射场景有哪些?

反射机制是指在运行时,动态地获取类的信息(如类名、属性、方法等),并可以在运行时操作类或对象的属性、方法等。在Java中,反射主要通过 java.lang.reflect 包中的类来实现。以下是反射相关的面试问题总结:

说一下反射机制?

反射机制的主要功能包括:

  1. 获取类的信息:可以通过反射获取类的构造方法、属性、方法等信息。

  2. 创建对象:可以通过反射动态创建类的实例。

  3. 调用方法:可以通过反射调用类的方法。

  4. 访问或修改属性:可以通过反射访问或修改类的属性。

反射机制的优点是可以在运行时动态地操作类,增强了程序的灵活性和扩展性。但是,由于反射涉及到运行时的类型检查和方法调用,可能会降低程序的性能,同时也增加了代码的复杂性和难以调试的可能性,因此在使用反射时需要谨慎考虑。

如何使用反射?

使用反射可以动态地获取类的信息、创建对象、调用方法和访问属性。下面是使用反射的一些常见示例:

  1. 获取类的信息:
Class<?> clazz = MyClass.class; // 获取类的Class对象
String className = clazz.getName(); // 获取类的全限定名
Field[] fields = clazz.getDeclaredFields(); // 获取类的所有字段
Method[] methods = clazz.getDeclaredMethods(); // 获取类的所有方法
Constructor<?>[] constructors = clazz.getDeclaredConstructors(); // 获取类的所有构造方法
  1. 创建对象:
Class<?> clazz = MyClass.class;
Object instance = clazz.newInstance(); // 创建类的实例,调用无参构造方法
  1. 调用方法:
Class<?> clazz = MyClass.class;
Method method = clazz.getDeclaredMethod("methodName", parameterTypes); // 获取方法对象
Object result = method.invoke(instance, args); // 调用方法
  1. 访问或修改属性:
Class<?> clazz = MyClass.class;
Field field = clazz.getDeclaredField("fieldName"); // 获取字段对象
field.setAccessible(true); // 设置字段可访问(私有字段需要设置)
Object value = field.get(instance); // 获取字段的值
field.set(instance, newValue); // 设置字段的值

反射有什么优缺点?

反射机制具有以下优点和缺点:

优点:

  1. 灵活性: 反射允许在运行时动态地获取类的信息、创建对象、调用方法和访问属性,增强了程序的灵活性和扩展性。

  2. 通用性: 反射可以处理未知类型的对象,可以应对不同类的情况,提高了代码的通用性。

  3. 适应性: 反射可以用于框架和库的开发,使其能够处理各种不同的类和对象。

缺点:

  1. 性能问题: 反射操作通常比直接调用代码要慢,因为它需要在运行时进行类型检查和方法调用。

  2. 安全性问题: 反射可以访问和修改类的私有字段和方法,可能会破坏封装性,导致安全漏洞。

  3. 复杂性: 反射使代码更加复杂和难以理解,特别是对于初学者来说可能会增加学习和维护的难度。

目前常见的反射场景有哪些?

反射在很多场景下都有应用,以下是一些常见的反射应用场景:

  1. 框架和库开发: 框架和库通常需要处理各种不同的类和对象,反射使得框架和库能够在不知道具体类的情况下处理这些对象。

  2. 工具类: 一些工具类需要在运行时动态地加载和操作类,比如序列化、反序列化、对象复制等。
    下面是一个示例,演示如何使用反射实现对象的复制:

import java.lang.reflect.Field;

public class ObjectUtils {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        // 假设有一个需要复制的对象
        MyClass original = new MyClass("John", 30);
        
        // 调用复制方法,得到复制后的对象
        MyClass copied = copyObject(original);
        
        // 输出复制后的对象信息
        System.out.println("Original: " + original);
        System.out.println("Copied: " + copied);
    }
    
    // 复制对象的方法
    public static <T> T copyObject(T original) throws IllegalAccessException, InstantiationException {
        Class<?> clazz = original.getClass();
        T copy = (T) clazz.newInstance(); // 创建对象的副本
        
        // 获取对象的所有字段
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true); // 设置字段可访问(私有字段需要设置)
            Object value = field.get(original); // 获取原始对象的字段值
            field.set(copy, value); // 设置副本对象的字段值
        }
        
        return copy;
    }
}

class MyClass {
    private String name;
    private int age;
    
    public MyClass(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "MyClass{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

这个示例中,ObjectUtils 类中的 copyObject 方法接受一个泛型参数,使用反射创建了原始对象的副本,并复制了原始对象的字段值到副本对象中。

  1. 注解处理器: 注解处理器可以使用反射来处理被注解的类和方法,实现特定的功能,比如在编译时生成代码或者进行代码检查。
    注解处理器是用于处理Java注解的工具,通常在编译时或者运行时扫描并处理注解。下面是一个简单的示例,演示如何编写一个简单的注解处理器来处理自定义注解:

首先,定义一个自定义注解 MyAnnotation

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAnnotation {
    String value();
}

然后,编写一个使用了 MyAnnotation 注解的类 MyClass

public class MyClass {
    @MyAnnotation("Hello, world!")
    public void myMethod() {
        System.out.println("Executing myMethod");
    }
}

接下来,编写一个注解处理器 MyAnnotationProcessor 来处理 MyAnnotation 注解:

import java.lang.reflect.Method;

public class MyAnnotationProcessor {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        processAnnotation(myClass);
    }

    public static void processAnnotation(Object object) {
        Class<?> clazz = object.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
            if (annotation != null) {
                String value = annotation.value();
                System.out.println("Found annotation value: " + value);
            }
        }
    }
}

MyAnnotationProcessor 类中,我们使用反射扫描 MyClass 类中的方法,找到使用了 MyAnnotation 注解的方法,并打印出注解的值。

最后,运行 MyAnnotationProcessormain 方法,你会看到输出 Found annotation value: Hello, world!,表示成功处理了 MyAnnotation 注解。

  1. 单元测试: 单元测试框架通常使用反射来调用被测试类的方法,并检查其行为是否符合预期。

  2. 动态代理: 动态代理是反射的一个重要应用场景,可以通过反射动态生成代理类,并在代理类中实现增强逻辑。

  3. 依赖注入: 依赖注入框架可以使用反射来自动装配对象,将对象注入到需要的地方。

  4. 配置文件处理: 一些配置文件处理工具可以使用反射来读取配置文件中的类名,并动态加载这些类。
    在下面这个例子中,我们从配置文件中读取了类名,然后使用反射加载该类并创建实例,最后调用其方法。

假设有一个配置文件 config.properties,内容如下:

handler=com.example.Handler

现在我们希望根据配置文件中的类名动态加载并实例化这个类,然后调用它的方法。代码如下:

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

public class ConfigExample {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        Properties properties = new Properties();
        FileInputStream fis = new FileInputStream("config.properties");
        properties.load(fis);
        fis.close();

        String className = properties.getProperty("handler");
        Class<?> clazz = Class.forName(className);
        Object handler = clazz.newInstance();

        // 假设 Handler 类有一个方法叫做 handle()
        clazz.getMethod("handle").invoke(handler);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《QT实用小工具·五十九》随机图形验证码,带有一些可人的交互与动画

1、概述 源码放在文章末尾 该项目实现了可交互的动画验证码控件&#xff0c;趣味性十足&#xff1a; 字符变换动画 噪音动画 可拖动交互 项目demo演示如下所示&#xff1a; 项目部分代码如下所示&#xff1a; #ifndef CAPTCHAMOVABLELABEL_H #define CAPTCHAMOVABLELABEL…

【影片欣赏】【指环王】【魔戒:护戒使者 The Lord of the Rings: The Fellowship of the Ring】

2001年发行&#xff0c;Extended DVD Edition Part One 1. Prologue: One Ring to Rule Them All… 2. Concerning Hobbits 3. The Shire 4. Very Old Friends 5. A Long-expected Party 6. Farewell Dear Bilbo 7. Keep It Secret, Keep It Safe 8. The Account of Isildur 9…

MyBatis入门例子

1、建立与数据库对应的POJO类 2、建立mybatis的配置文件 修改后如下&#xff1a; 3、创建POJO对象和Mysql数据的表之间的映射配置 4、建一个测试方法 实现从数据库中取数一条数据&#xff0c;封装成User对象返回 注意点&#xff1a; 这点&#xff0c;大家应该不陌生了&#x…

28-代码随想录18四数之和

18. 四数之和 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#xff09;&#xff…

小米手机miui14 android chrome如何取消网页自动打开app

搜索媒体打开应用 选择你要阻止打开的app&#xff0c;以github为例 取消勾选打开支持的链接。 参考&#xff1a;https://www.reddit.com/r/chrome/s/JBsGkZDkRZ

【进程终止】退出信号 | 三种退出情况 | 如何进程终止returnexit_exit

目录 退出码 退出信号 进程终止情况3 如何进程终止 return退出 库函数exit 系统调用函数_exit ​exit和_exit的区别缓冲区 exit _exit 退出码 回顾上篇 代码跑完&#xff0c;结果正确&#xff08;退出码为0&#xff09;代码跑完&#xff0c;结果不正确&#xff08;退…

批量将GOID转成GO term名并添加BP,MF,CC分类信息

基因本体论&#xff08;Gene Ontology&#xff0c;GO&#xff0c;https://www.geneontology.org&#xff09;是一个广泛应用于生物信息学领域的知识库&#xff0c;它提供了一套标准化的词汇和分类体系&#xff0c;用于描述基因功能、细胞组分和生物过程。GO旨在统一科研人员对基…

C/C++ BM30 二叉搜索树与双向链表

文章目录 前言题目解决方案一1.1 思路阐述1.2 源码 解决方案二2.1 思路阐述2.2 源码 总结 前言 这道题要明白二叉搜索树的概念&#xff0c;同时还要对链表的知识比较熟悉。 题目 输入一棵二叉搜索树&#xff0c;将该二叉搜索树转换成一个排序的双向链表。如下图所示 数据范…

在QEMU上运行OpenSBI+Linux+Rootfs

在QEMU上运行OpenSBILinuxRootfs 1 编译QEMU2 安装交叉编译工具3 编译OpenSBI4 编译Linux5 创建根文件系统5.1 编译busybox5.2 创建目录结构5.3 制作文件系统镜像5.3.1 创建 ext2 文件5.3.2 将目录结构拷贝进 ext2 文件5.3.3 取消挂载 6 运行OpenSBILinuxRootfs 本文所使用的版…

PVZ2 植物克僵尸【第二期】

众所周知&#xff0c;PVZ2&#xff08;植物大战僵尸2&#xff09;中有许多恶心的僵尸&#xff0c;而我们不得不派出它们的————克星&#xff01;&#xff08;*为建议方法&#xff09; 5.战机小鬼 战机小鬼&#xff0c;恶心会发射子弹&#xff0c;所以&#xff1a; 1&…

(三)JSP教程——JSP动作标签

JSP动作标签 用户可以使用JSP动作标签向当前输出流输出数据&#xff0c;进行页面定向&#xff0c;也可以通过动作标签使用、修改和创建对象。 <jsp:include>标签 <jsp:include>标签将同一个Web应用中静态或动态资源包含到当前页面中。资源可以是HTML、JSP页面和文…

【解决】:git clone项目报错fatal: fetch-pack: invalid index-pack output

象&#xff1a;之前一直使用gitee将个人学习和工作相关记录上传到个人gitee仓库&#xff0c;一直没出现过问题。直到有一天换电脑重新拉取代码发现出了问题&#xff0c;具体如下图&#xff1a; 原因分析&#xff1a; 经过查询发现主要原因是因为git clone的远程仓库的项目过大…

【强化学习】公平性Actor-Critic算法

Bringing Fairness to Actor-Critic Reinforcement Learning for Network Utility Optimization 阅读笔记 Problem FormulationLearning AlgorithmLearning with Multiplicative-Adjusted RewardsSolving Fairness Utility Optimization Evaluations 在网络优化问题中&#xff…

懒人网址导航源码v3.9源码及教程

懒人网址导航源码v3.9源码及教程 效果图使用方法部分源码领取源码下期更新预报 效果图 使用方法 测试环境 宝塔Nginx -Tengine2.2.3的PHP5.6 MySQL5.6.44为防止调试错误&#xff0c;建议使用测试环境运行的php与mysql版本首先用phpMyAdmin导入数据库文件db/db.sql 如果导入不…

QT-TCP通信

网上的资料太过于书面化&#xff0c;所以看起来有的让人云里雾里&#xff0c;看不懂C-tcpsockt和S-tcpsocket的关系 所以我稍微画了一下草图帮助大家理解两个套接字之间的关系。字迹有的飘逸勉强看看 下面是代码 服务端&#xff1a; MainWindow::MainWindow(QWidget *parent) …

Kubernetes 教程:在 Containerd 容器中使用 GPU

原文链接:Kubernetes 教程:在 Containerd 容器中使用 GPU 云原生实验室本文介绍了如何在使用 Containerd 作为运行时的 Kubernetes 集群中使用 GPU 资源。https://fuckcloudnative.io/posts/add-nvidia-gpu-support-to-k8s-with-containerd/ 前两天闹得沸沸扬扬的事件不知道…

Golang | Leetcode Golang题解之第67题二进制求和

题目&#xff1a; 题解&#xff1a; func addBinary(a string, b string) string {ans : ""carry : 0lenA, lenB : len(a), len(b)n : max(lenA, lenB)for i : 0; i < n; i {if i < lenA {carry int(a[lenA-i-1] - 0)}if i < lenB {carry int(b[lenB-i-1…

6W 1.5KVDC. 单、双输出 DC/DC 电源模块——TP2L-6W 系列

TP2L-6W系列是一款高性能、超小型的电源模块&#xff0c;2:1电压输入&#xff0c;输出有稳压和连续短路保护功能&#xff0c;隔离电压为1.5KVDC、作温度范围为–40℃到85℃。特别适合对输出电压的精度有严格要求的地方&#xff0c;外部遥控功能对您的设计又多一项选择&#xff…

Liunx磁盘管理(中)

Liunx磁盘管理(上)-CSDN博客 目录 查看块设备信息 lsblk&#xff08;list block devices&#xff09; fdisk gdisk parted blkid df&#xff08;disk free&#xff09; 虚拟机添加硬盘 步骤&#xff1a; 磁盘分区 MBR格式创建分区 使用方法 替代工具 GPT分区格式…

【C 数据结构-动态内存管理】2. 边界标识法管理动态内存

文章目录 【 1. 边界标识法的结构设计 】【 2. 分配算法 】【 3. 回收算法 】3.1 空闲块两侧是占用块3.2 空闲块左侧是空闲块3.3 空闲块右侧是空闲块3.3 空闲块两侧是空闲块 边界标识法 可以解决系统中内存碎片过多而无法使用的问题。 【 1. 边界标识法的结构设计 】 使用边界…
最新文章