Java反射(一)

news2024/9/24 11:31:11

目录

1.了解反射

2.Class类的三种实例化方法

3.反射机制与对象实例化

4.反射与单例设计模式

5.通过反射获取类结构的信息


1.了解反射

什么是反射,反射有什么作用

1.在Java中,反射是一种机制,允许程序在运行时动态地获取、使用和修改类的信息。通过反射,可以在编译时不知道类的具体信息的情况下,操作和查看类的属性、方法和构造函数等。

2.反射有以下几个主要的作用:

  1. 动态加载类:使用反射可以在运行时动态地加载需要使用的类,而不需要在编译时将类写死在代码中。这样可以实现更加灵活的代码结构和功能。

  2. 获取类的信息:通过反射,可以获取类的名称、父类、接口、方法、字段等详细信息。这对于编写通用框架、调试工具和JavaBean的序列化等场景非常有用。

  3. 创建对象和执行方法:使用反射可以动态地创建对象,即使在编译时无法确定具体的类。同时,还可以在运行时调用任意对象的方法,甚至是私有方法。

  4. 修改私有字段:反射允许程序访问和修改类的私有字段的值。这在某些特定的应用场景中可能是必要的,但需要小心使用,遵循权限和安全性的原则。

需要注意的是,反射是一种强大而复杂的机制,在普通的业务代码中并不常用。滥用反射可能会导致性能下降,代码可读性降低,并增加出错的可能性。因此,在使用反射时,需要谨慎权衡利弊,并确保了解其使用方式和限制。 


2.Class类的三种实例化方法

1.通过实例化对象调用getclass()方法实现

2.通过直接调用类名的.Class

3.通过调用Class.forname("包+类名") throws ClassNoFoundException

实例化案例:

package Example1701;
class Member{
    private String name;
    private int age;
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
}
public class javaDemo {
    public static void main(String[] args)throws Exception {
        Member mem = new Member();
//        通过实例化getClass()方法得到类
        Class<?> claszz1= mem.getClass();
        System.out.println(claszz1);
//        通过直接获取类名实例化Class
        Class<?> claszz2 = Member.class;
        System.out.println(claszz2);
//        通过调用Class里的forname方法实现实例化
        Class<?> claszz3 = Class.forName("Example1701.Member");
        System.out.println(claszz3);
    }
}

3.反射机制与对象实例化

通过反射机制进行对象实例化就能替代new的关键词的使用

案例

package Example1702;

class Member{
    Member(){
        System.out.println("自动调用构造函数");
    }
    @Override
    public String toString() {
        return "实现Member对象的创建";
    }
}
public class javaDemo {
    public static void main(String[] args)throws Exception {
        Class<?> claszz = Class.forName("Example1702.Member");
//        创建实例化对象并用Object类进行接收
        Object obj = claszz.getDeclaredConstructor().newInstance();
        System.out.println(obj);
//        对比区别
        Object obj2 = claszz.newInstance();
        System.out.println(obj2);
    }
}

 

问: 可以发现Object接收两个对象输出后都是一样的,那么

Object obj = claszz.getDeclaredConstructor().newInstance();与
Object obj2 = claszz.newInstance();有什么区别吗

答:

在Java 9及之前,我们可以使用Class.newInstance()方法来创建一个类的实例对象。这个方法是通过调用类的默认构造函数来创建对象的。例如,claszz.newInstance()会调用Member类的默认构造函数创建对象。

然而,从Java 9开始,Class.newInstance()方法被标记为过时了,因为它在处理异常和对私有构造函数的访问上存在一些限制。取而代之的是,推荐使用getDeclaredConstructor().newInstance()方法来创建对象。这个方法更为灵活,可以处理带参数的构造函数并且可以处理私有构造函数。

所以,Object obj = claszz.getDeclaredConstructor().newInstance()Object obj2 = claszz.newInstance()的区别在于:

  1. 创建实例对象的方式不同:claszz.getDeclaredConstructor().newInstance()可以处理带参数的构造函数,而claszz.newInstance()只能调用无参构造函数。
  2. 访问权限不同:claszz.getDeclaredConstructor().newInstance()可以处理私有构造函数,而claszz.newInstance()无法处理私有构造函数。
  3. 兼容性不同:claszz.getDeclaredConstructor().newInstance()是在Java 9及之后引入的,而claszz.newInstance()是在Java 8及之前推荐使用的方法。

因此,在现代的Java版本中,建议使用getDeclaredConstructor().newInstance()方法来创建类的实例对象,它更加通用和灵活。


4.反射与单例设计模式

单例设计模式都不陌生,在以前的文章中有提到过,其实现的方法就是通过私有化构造方法实现外部无法实例化对象,并且在类的内部就定义一个唯一的对象,最后通过函数调用的形式将对象输出出去,并且设计模式有两种一种饿汉模式,一种懒汉模式,分别是饿汉:一开始就定义一个唯一对象,懒汉模式:当需要用到的时候才进行创建对象。

那么懒汉模式下就有可能出现问题,什么问题呢?关于多线程问题,懒汉是通过if判断是否对象为空,但是多线程可能出现并发问题,大家都同时执行了if判断语句发现对象为空则大家都一起创建一个对象,造成对象不唯一,不符合单例设计模式

那么如何解决呢?通过反射就能实现多线程下单例设计模式的唯一性

案例代码:

package Example1703;
class Only {
//    实现单例化设计模式
    private Only(){
    }
    private static volatile Only onlyOne = null;
//    创建对象或者获取对象
    public static  Only getOnly(){
        if (onlyOne == null){
            synchronized (Only.class){
                if (onlyOne == null){
                    onlyOne = new Only();
                    return onlyOne;
                }
            }
        }
        return onlyOne;
    }
    public void print(){
        System.out.println("Hello");
    }
}
public class javaDemo {
    public static void main(String[] args) {
//        创建多线程
        for (int i = 0;i<3;i++){
            new Thread(()->{
            Only.getOnly().print();
                System.out.println(Thread.currentThread().getName());
            },i+"的线程").start();
        }
    }
}

问1:synchronized (Only.class)的作用,为什么要写Only.class而不是this?

synchronized (Only.class)使用类级别的锁来实现同步。

在Java中,每个类都有一个对应的Class对象,可以通过类名后面加上.class来获取该类的Class对象。而this关键字代表当前实例对象,它是指向当前对象的引用。

在单例模式中,我们希望通过synchronized来保证只有一个线程能够创建实例对象。使用synchronized (Only.class),即使用类级别的锁,意味着多个线程在访问这段代码时会竞争同一个锁,即类级别的锁。

而如果使用synchronized (this),则表示使用实例级别的锁。在这个例子中,我们不希望通过实例级别的锁来控制多个线程对实例的访问,因为还没有创建实例对象,所以也不存在实例对象来获得锁。

因此,为了确保在多线程环境下只创建一个实例对象,需要使用类级别的锁,即synchronized (Only.class),而不是实例级别的锁。

问2:为什么要进行两次判断是否为空?

答: 问2:进行两次判断是否为空的目的是为了提高代码执行的效率和性能。在双重检查锁定机制中,第一次判断onlyOne是否为空是为了避免重复进行同步块的加锁和解锁操作,从而提高了代码的执行效率。如果只有一次判断是否为空,那么每次调用getOnly()方法时都会进入同步块,性能开销会增加。

  同时,第二次判断是为了在多个线程同时通过了第一次判断后,只有第一个获得锁的线程才会进入同步块创建实例。其他线程在获取到锁后,再次判断onlyOne是否为空,如果已经不为空,就直接返回已经创建好的实例,避免了重复创建对象。

如果只有一次判断是否为空,那么即使已经有线程创建了实例,其他线程也会通过第一次判断,进入同步块再次创建实例,这样会造成多个实例的创建,不符合单例模式的要求。

因此,为了确保只有一个线程创建实例,并提高代码的执行效率和性能,需要进行两次判断是否为空。


5.通过反射获取类结构的信息

常用通过反射获取类结构的方法有这几个

方法名作用
getName()获取类的完整名称(包括包路径)
getPackage()获取类所在的包信息
getSuperclass()获取类的父类
getInterfaces()获取类实现的接口列表

案例代码:

package Example1704;

interface Interface1 {}
interface Interface2 {}
abstract class Father {}
class Test extends Father implements Interface1, Interface2 {
    private String name;
    private int age;

    Test(String name, int age) {
        this.age = age;
        this.name = name;
    }

    public void print() {
        System.out.println("输出信息Test类");
    }
}

public class javaDemo {
    public static void main(String[] args) {
//        获取指定类的class对象
        Class<?> clazz = Test.class;

//        获取指定类的包的名称
        Package pag = clazz.getPackage();
        System.out.println(pag.getName());

//        获取类的父类的信息
        Class<?> superClass = clazz.getSuperclass();
        System.out.println(superClass.getName());
//        获取类的接口的信息
        Class<?>[] interfaces = clazz.getInterfaces();
        for (Class<?> temp : interfaces) {
            System.out.println(temp.getName());
        }

    }
}

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

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

相关文章

ASL芯片CS5261 替代瑞昱RTD2171替代AG9310芯片 Type-C转HDMI音频单转 CS5261搭配VL171母座正反插原理图性价比方案

在2021年末尾&#xff0c;瑞昱RTD2171已经停产&#xff0c;ASL集睿致远的单转Type-C转HDMI方案芯片&#xff0c;ASL集睿致远 CS5261却可以完 全替代兼容RTD2171和AG9310, CS5261芯片还可以实现对Type-C接口信号转换的同时实现投屏的慢充功能。另外如果使用芯片CS5261VL171支持T…

Nodejs 第四章(Npm install 原理)

在执行npm install 的时候发生了什么&#xff1f; 首先安装的依赖都会存放在根目录的node_modules,默认采用扁平化的方式安装&#xff0c;并且排序规则.bin第一个然后系列&#xff0c;再然后按照首字母排序abcd等&#xff0c;并且使用的算法是广度优先遍历&#xff0c;在遍历依…

【Python】Web学习笔记_flask(3)——上传文件

用GET、POST请求上传图片并呈现出来 首先还是创建文件上传的模板 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>上传图片</title> </head> <body> <form action""…

XGBoost的参数

目录 1. 迭代过程 1.1 迭代次数/学习率/初始&#x1d43b;最大迭代值 1.1.1 参数num_boost_round & 参数eta 1.1.2 参数base_score 1.1.3 参数max_delta_step 1.2 xgboost的目标函数 1.2.1 gamma对模型的影响 1.2.2 lambda对模型的影响 2. XGBoost的弱评估器 2.…

android app控制ros机器人四(调整界面布局)

半吊子改安卓&#xff0c;记录页面布局调整&#xff1a; 在ros-mobile基础上顶端增加一行&#xff0c;用于显示app名称和logo图像&#xff1b;修改标签页。 添加文字简单&#xff0c;但是替换图标长知识了&#xff0c;开始只是简单的把mipmap各个文件夹下的图片进行替换&…

Web-7-深入理解Cookie与Session:实现用户跟踪和数据存储

深入理解Cookie与Session&#xff1a;实现用户跟踪和数据存储 今日目标 1.掌握客户端会话跟踪技术Cookie 2.掌握服务端会话跟踪技术Sesssion 1.会话跟踪技术介绍 会话&#xff1a;用户打开浏览器&#xff0c;访问web服务器的资源&#xff0c;会话建立&#xff0c;直到有一方断…

Spring boot开发实用篇

一、热部署 1.启动热部署 1.导入坐标 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId> </dependency> 2.使用构建项目操作启动热部署 3.关于热部署 重启&#xff1a;自定义开发…

【软件测试学习】—软件测试的基本认识(一)

【软件测试学习】—软件测试的基本认识&#xff08;一&#xff09; 文章目录 【软件测试学习】—软件测试的基本认识&#xff08;一&#xff09;一、什么是软件测试二、软件测试的目的三、测试的原则四、测试的标准五、测试的基本要求六、bug的由来七、测试的流程八、开发模式九…

消息中间件应用场景介绍

提高系统性能首先考虑的是数据库的优化&#xff0c;但是数据库因为历史原因&#xff0c;横向扩展是一件非常复杂的工程&#xff0c;所有我们一般会尽量把流量都挡在数据库之前。 不管是无限的横向扩展服务器&#xff0c;还是纵向阻隔到达数据库的流量&#xff0c;都是这个思路。…

Web后端基本设计思想

JavaWeb应用的后端一般基于MVC和三层架构思想实现。 MVC是一种设计模式&#xff0c;用于开发用户界面和交互式应用程序。M即Model&#xff0c;业务模型&#xff0c;负责处理应用程序的业务逻辑和数据&#xff1b;V即View&#xff0c;视图&#xff0c;负责给用户展示界面和数据&…

【Web】web

dns与域名 网络是基于tcp/ip协议进行通信和连接的 应用层——传输层——网络层——数据链路层——物理层 每一定的台主机都有一个唯一且固定的地址标识——IP地址 IP地址的做用&#xff1a;1.区分用户和计算机&#xff1b;2.进行通信 IP地址由32位二进制数组成&#xff0c;…

<C++> 引用

1.引用的概念 引用&#xff08;Reference&#xff09;是一种别名&#xff0c;用于给变量或对象起另一个名称。引用可以理解为已经存在的变量或对象的别名&#xff0c;通过引用可以访问到原始变量或对象的内容。引用在声明时使用 & 符号来定义。 示例&#xff1a; #inclu…

小程序如何从分类中移除商品

​有时候商家可能需要在商品分类中删除某些商品&#xff0c;无论是因为商品已下架、库存不足还是其他原因。在这篇文章中&#xff0c;我们将介绍如何从分类中移除商品。 方式一&#xff1a;分类管理中删除商品。 进入小程序管理后台&#xff0c;找到分类管理&#xff0c;在分…

记录一次通过iostat命令定位系统数据库CPU飙升的案例

一、背景 我们有个移动考勤的系统&#xff0c;运维监控系统显示&#xff0c;每到上下班时间&#xff0c;考勤数据库的CPU就飙升到100%&#xff0c;磁盘读写请求等待时间变长&#xff0c;最初无法确定是磁盘性能下降导致的CPU飙升&#xff0c;还是CPU飙升导致的磁盘性能下降&…

牛客网Verilog刷题——VL55

牛客网Verilog刷题——VL55 题目答案 题目 请用Verilog实现4位约翰逊计数器&#xff08;扭环形计数器&#xff09;&#xff0c;计数器的循环状态如下&#xff1a;   电路的接口如下图所示&#xff1a; 输入输出描述&#xff1a; 信号类型输入/输出位宽描述clkwireInput1系统…

C5.0决策树建立个人信用风险评估模型

通过构建自动化的信用评分模型&#xff0c;以在线方式进行即时的信贷审批能够为银行节约很多人工成本。本案例&#xff0c;我们将使用C5.0决策树算法建立一个简单的个人信用风险评估模型。 导入类库 读取数据 #创建编码所用的数据字典 col_dicts{} #要编码的属性集 cols [che…

【Spring Cloud一】微服务基本知识

系列文章目录 微服务基本知识 系列文章目录前言一、系统架构的演变1.1单体架构1.2分层架构1.3分布式架构1.4微服务架构1.5分布式、SOA、微服务的异同点 二、CAP原则三、RESTfulRESTful的核心概念&#xff1a; 四、共识算法 前言 在实际项目开发过程中&#xff0c;目前负责开发…

AVX 贴片钽电容的频率特性分析

在介绍 AVX 钽电容的温度特性曲线前&#xff0c;我们必需对以下两个基本概念有所认识&#xff1a; 额定容量(CR) 这是额定 电容。对于钽OxICap?电容器的电容测量是在25 C 时等效串联电路使用测量电桥提供一个0.5V RMS120Hz 的正弦信号&#xff0c;谐波与2.2Vd.c. 电容公差 这是…

测试|性能测试相关理论

测试|性能测试相关理论&#xff08;了解&#xff09; 文章目录 测试|性能测试相关理论&#xff08;了解&#xff09;1.什么是性能测试生活中遇到的软件性能问题&#xff1a;性能测试定义&#xff1a;性能测试和功能测试有什么区别&#xff1a;性能好坏的评价指标影响一个软件性…

Stable Diffusion AI绘画学习指南【插件安装设置】

插件安装的方式 可用列表方式安装&#xff0c;点开Extensions 选项卡&#xff0c;找到如下图&#xff0c;找到Available选项卡&#xff0c;点load from加载可用插件&#xff0c;在可用插件列表中找到要装的插件按install 按扭按装&#xff0c;安装完后(Apply and restart UI)应…