Java反射学习

news2024/11/15 21:34:36

反射的概念

Reflection(反射)是Java被视为动态语言的关键
反射机制允许程序在执行期借助于Reflection API获得任何类的内部信息,
并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象)。
这个对象就包含了整个的类的结构信息,我们可以通过这个对象看到类的结构。
这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为:反射

正常方式:引入需要的“包类名称”–>通过new实例化–>取得实例化对象;

反射方式:实例化对象–>getClass()方法–》得到完整的“包类名称”;

反射的功能

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时构造任意一个类所具有的成员变量和方法

在运行时获取泛型信息

在运行时处理注解

生成动态代理

优点:可以实现动态类 创建对象和编译,体现出很大的灵活性

缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM我们希望做什么并且它满足我们的要求。这类操做总是慢于直接执行的相同操作

反射的使用

public class Reflect1 {
    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获取类的Class对象
        Class c1=Class.forName("Reflect.Reflect1");
        System.out.println(c1);
        Class c2=Class.forName("Reflect.Reflect1");
        Class c3=Class.forName("Reflect.Reflect1");
        Class c4=Class.forName("Reflect.Reflect1");
       //一个类在内存中只有一个Class对象
        // 一个类被加载后,类的整个结构都会被封装在Class对象中
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
        System.out.println(c4.hashCode());
    }
    //实体类: pojo,entity
    class User {
        private String name;
        private int id;
        private int age;
	    public  User() {
	 
	    }
        public String getName() {
            return name;
        }
 
        public void setName(String name) {
            this.name = name;
        }
 
        public int getId() {
            return id;
        }
 
        public void setId(int id) {
            this.id = id;
        }
 
        public int getAge() {
            return age;
        }
 
        public void setAge(int age) {
            this.age = age;
        }
 
        public User(String name, int id, int age) {
            this.name = name;
            this.id = id;
            this.age = age;
        }
    }
 
}

Class类详解

对象照镜子后可以得到的信息:某个类的属性、方法和构造器,某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定的某个结构(class/interface/enum/annotation/primitive type/void[])的有关信息

·Class本身也是一个类

·Class对象只能由系统建立对象

·一个加载的类在JVM中只会有一个Class实例

·一个Class对象对应的是一个加载到JVM中的一个.class文件

·每个类的实例都会记得自己是由那个Class实例所生成

·通过Class可以完整地得到一个类中所有被加载 的结构

·Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

Class类的创建方式

public class Reflect2 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person=new Student();
        System.out.println("这个人是"+ person.name);
        
        //方式一:通过对象获得
        Class c1=person.getClass();
        System.out.println(c1.hashCode());
        
        //方式二:forname 获得
        Class c2=Class.forName("Reflect.Reflect2");
        System.out.println(c2.hashCode());
        
        //方式三:通过类名.class获得
        Class c3=Student.class;
        System.out.println(c3.hashCode());
        
        //方式四: 基本内置类型的包装类都是有一个Type 属性
        Class c4=Integer.TYPE;
        System.out.println(c4);
        
        //获得父类类型
        Class c5=c1.getSuperclass();
        System.out.println(c5);
    }
}
 class Person{
   public String  name;
 
     public Person() {
     }
     public void setName(String name) {
         this.name = name;
     }
     @Override
     public String toString() {
         return "Person{" +
                 "name='" + name + '\'' +
                 '}';
     }
 }
 class Student extends Person {
     public Student() {
         this.name = "学生";
     }
 
     class Teacher extends Person {
         public Teacher() {
             this.name = "老师";
         }
     }
 }

在这里插入图片描述

三种创建方式的比较

一般使用Class.forName 方法去动态加载类 ,且使用forName就不需要导入其他类,可以加载我们任意的类。

而使用 .class属性,则需要导入类的包,依赖性强,且容易抛出编译错误。

而使用实例化对象的 getClass() 方法,需要本身创建一个对象,是静态的,就体现不出反射机制意义了。

所以我们在获取class对象中, 一般都会使用 Class.forName去获取

哪些类型可以有Class对象?

class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类

interface:接口

[ ]:数组

enum:枚举

annotation:注解@interfac

primitive type:基本数据类型

void

public class Reflect3 {
    public static void main(String[] args) {
        Class c1= Object.class;
        Class c2=Comparable.class;
        Class c3=String[].class;
        Class c4=int [][].class;
        Class c5=Override.class;
        Class c6= ElementType.class;
        Class c7=Integer.class;
        Class c8=void.class;
        Class c9=Class.class;
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
 
        //只要元素类型一样,就是同一个Class.
        int []a=new int[10];
        int []b=new int[100];
        System.out.println(a.getClass().hashCode());
        System.out.println(b.getClass().hashCode());
    }
}

在这里插入图片描述

利用反射获取成员变量Field

//返回 字段对象,该对象反映此 类对象表示的类或接口的指定声明字段。
getDeclaredField(String name)

//返回 字段对象的数组, 字段对象反映由此 类对象表示的类或接口声明的所有字段。
getDeclaredFields()
 
 
//返回 字段对象,该对象反映此 类对象表示的类或接口的指定公共成员字段。
getField(String name)

//返回一个包含 字段对象的数组, 字段对象反映此 类对象所表示的类或接口的所有可访问公共字段。
getFields()

例如:

Class<?> cls = Class.forName("com.person");
System.out.println(cls.getName());

//只能获取public 的属性
Field[] field = cls.getFields();
for (Field field1 : field) {
    System.out.println(" 属性: "+field1.getName()); 
}

System.out.println("========");

//可以获取类全部属性
Field[] declaredFields = cls.getDeclaredFields();    
for (Field all : declaredFields) {
    System.out.println("属性" + all.getName());
}

利用反射获取方法Method

//返回 方法对象,该对象反映此 类对象表示的类或接口的指定声明方法。
getDeclaredMethod(String name,<?>... parameterTypes);
//返回一个包含 方法对象的数组, 方法对象反映此 类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法。
getDeclaredMethods();   //数组
//返回 方法对象,该对象反映此 类对象表示的类或接口的指定公共成员方法。
getMethod(String name,<?>... parameterTypes);
//返回一个包含 方法对象的数组, 方法对象反映此 类对象所表示的类或接口的所有公共方法,包括由类或接口声明的那些以及从超类和超接口继承的那些。
getMethods();

用法 跟Field 类似。

但是要注意后面的 String name, 类<?>… parameterTypes
例如

  Class clazz = Class.forName("java.lang.Runtime");
  clazz.getMethod("exec", String.class);

Runtime 类中的 exec 实现了方法的重载, 因此需要使用forMethod就要带上第二个参数,才能准确的找到指定方法的对象。

利用反射获取构造函数

//返回一个 构造器对象,该对象反映此 类对象所表示的类或接口的指定构造函数。
getDeclaredConstructor(<?>... parameterTypes)
//返回 构造器对象的数组, 构造器对象反映由此 类对象表示的类声明的所有构造函数。
getDeclaredConstructors()
//返回一个 构造器对象,该对象反映此 类对象所表示的类的指定公共构造函数。
getConstructor(<?>... parameterTypes)
//返回一个包含 构造器对象的数组, 构造器对象反映了此 类对象所表示的类的所有公共构造函数。
getConstructors()

用法几乎差不多,可以使用 getConstructors 再进行遍历取出

利用反射实例化对象

得到地址和类名后,使用newInstance 实例化这个类:

Class<?> cls = Class.forName("com.person");
Object o = cls.newInstance();
System.out.println(o);

也可以通过构造器实例化对象

 Constructor<?> cons = 
 cls.getDeclaredConstructor(String.class, double.class);
 
  Object snowy = cons.newInstance("snowy", 1000000000);
  System.out.println(snowy);

利用反射中调用对象中的方法

反射中的调用,是用方法来调用对象,如:

方法.invoke(对象)

例子, 比如要调用 person 类中的 hi() 方法,首先要得到 hi() 方法的 Method对象,其次 还需要得到 person类的对象 作为invoke的参数

Class<?> cls = Class.forName("com.person");//加载类
Constructor<?> cons = 
cls.getDeclaredConstructor(String.class, double.class); //获取构造器

Object snowy = cons.newInstance("snowy", 123);//实例化对象
Method m2 = cls.getMethod("m2");//得到Method 对象
Object name = m2.invoke(snowy); // 调用方法

如果调用的是普通方法,第一个参数就是类对象;

如果调用的这个方法是静态方法,第一个参数是类,或者可以护忽略,设置为null

特殊权限方法的反射访问方式

如果得到的Field 、Method、Constructor 是私有属性的话,受到权限影响,不能直接调用或者读取,需要设置 setAccessible(true) ;

setAccessible方法是AccessibleObejct类的一个方法,它是Field、Method和Constructor类的公共超类。这个特性是为调式、持久存储和类似机制提供的。

例如,Runtime类的构造函数是私有的,可以这样获取对象

Class<?> cls = Class.forName("java.lang.Runtime");
Constructor<?> m = cls.getDeclaredConstructor();
m.setAccessible(true);
cls.getMethod("exec", String.class).invoke(m.newInstance(),"calc.exe");

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

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

相关文章

【JavaEE】阻塞队列 + 生产者消费者模型

目录 阻塞队列 阻塞队列的使用 生产者消费者模型 模型的两个好处 1. 降低耦合 2. 削峰填谷 简单实现阻塞队列 阻塞队列 阻塞队列是在一般的队列上升级而来的。 对于队列为空时&#xff0c;如果还想取队列中的元素&#xff0c;此时阻塞队列就会进行阻塞。 对于队列为满时…

fpga的SD卡读BMP图片显示实验(SPI模式)

对于 SD 卡的 SPI 模式而言&#xff0c;采用的 SPI 的通信模式为模式 3&#xff0c;即 CPOL1&#xff0c;CPHA1&#xff0c;在 SD 卡 2.0 版本协议中&#xff0c;SPI_CLK 时钟频率可达 50Mhz。SD 卡的 SPI 模式&#xff0c;只用到了 SDIO_D3&#xff08;SPI_CS&#xff09;、SD…

Nacos 部署简单使用

文章目录1、前置相关知识及说明2、官网3、环境4、Nacos 和 Zookeeper、Eureka 的主要区别5、安装部署 & 启动5.1、Windows下载安装包部署单机部署集群部署测试6、使用服务端客户端 - SpringBoot 使用 Nacos Client7、运维健康检查获取配置&#xff0c;验证服务端是否正常异…

【DX-BT24蓝牙模块连接Arduino与手机透传教程】

【DX-BT24蓝牙模块连接Arduino与手机透传教程】1. 前言2. 接线3. 程序设计详解4. 演示效果5. 小结1. 前言 大夏龙雀科技DX-BT24&BT24-S&BT24-PA蓝牙模块,拥有5.1蓝牙协议,模块内置标准串口协议。前期设置蓝牙名称为VOR&#xff0c;采用默认波特率9600&#xff0c;详细…

JavaScript 入门基础 - 对象(五)

JavaScript 入门基础 - 对象 文章目录JavaScript 入门基础 - 对象1. 对象1.1 对象的基本理解1.2 为什么需要变量2. 创建对象的方式2.1 利用字面量创建对象2.2 变量属性函数方法的区别2.3 利用 new Object 创建对象2.4 利用构造函数创建对象3.new关键字4. 遍历对象属性5. JavaSc…

二、Promise

Promise1、回调地狱1.1 如何解决回调地狱的问题1.2 Promise 的基本概念2、基于回调函数按顺序读取文件内容3. 基于 then-fs 读取文件内容3.1 then-fs 的基本使用3.2 .then() 方法的特性3.3 基于 Promise 按顺序读取文件的内容3.4 通过 .catch 捕获错误3.4 通过 .catch 捕获错误…

基于RBAC权限控制模型的管理系统的设计与实现

文章目录一、RBAC 权限设计1.1 模型概述1.2 模型分类二、基于RBAC的后台管理系统2.1 项目概述2.2 技术选型2.3 内部处理流程2.4 功能模块展示2.5 权限控制展示2.6 下载说明一、RBAC 权限设计 1.1 模型概述 基于角色的访问控制 RBAC&#xff0c;是实施面向企业安全策略的一种有…

kafka的介绍和基本使用

文章目录Kafka介绍1.Kafka的使用场景2.Kafka基本概念kafka基本使用1.安装前的环境准备2.启动kafka服务器3.创建主题topic4.发送消息5.消费消息Kafka介绍 Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的 &…

载波相位误差对BPSK解调性能的影响理论推导

在上一篇博客基础上,继续讨论载波相位误差对解调性能的影响! 【通信原理】通信原理书中解调器输入端信噪比a2/(2σ2)与比特信噪比Eb/No有什么关系? 以BPSK为例,从解调原理图可以看出,当本地参考载波信号与原始载波存在相位误差时,假设相位差为 φ \varphi φ,则解调器低…

【Linux】了解磁盘/文件系统/inode

文章目录一.磁盘1.磁盘的结构2.磁盘的定位&#xff08;寻找方案&#xff09;3.磁盘的分区与格式化介绍二.理解inode三.ext2文件系统的存储方案一.磁盘 1.磁盘的结构 问题1&#xff1a;什么是磁盘&#xff1f; 磁盘是在冯诺依曼体系结构中几乎唯一的机械设备&#xff0c;机械设…

AntDB数据库助力中国移动结算中心建设

为响应中国移动集团公司IT集中化的要求&#xff1a;全面落实“十三五”十大战略工程&#xff0c;加快“推动公司IT资源一体化整合“重点专项工作。以IT系统为载体&#xff0c;构建高效运营支撑体系&#xff0c;形成集中化支撑和协同业务支撑模式&#xff0c;打造极致体验、高效…

列表初始化(内置类型、自定义类型)

列表初始化的特性来源于单参数的隐式类型转换。以下面这个赋值为例&#xff0c;我们可以理解成 先创建一个匿名对象Point(2)&#xff0c;这个时候就变成了 Point p Point(2);然后会调用拷贝构造。 虽然隐式转换的可以这样理解&#xff0c;但是最后会被编译器优化成直接调用有…

[Android]Bitmap Drawable

在实际开发中&#xff0c;我们可以直接引用原始的图片&#xff0c;但是也可以通过xml的方式来描述它&#xff0c;通过xml来描述的BitmapDrawable可以设置更多效果。 <?xml version"1.0" encoding"utf-8"?> <bitmap xmlns:android"http://…

java spring IOC Bean管理操作讲解 并代码演示xml的实现方式

查看本文 需要您使用spring创建过对象管理 如果之前没有接触过 可以先查看我的文章 java 手把手带你创建一个spring入门案例 IOC 操作中 Bean管理主要有两个部分 分别是创建对象和注入属性 他们都有两种实现方式 分别是xml和注解方式实现 本文只演示xml 后续我会出注解方式的文…

第十三届蓝桥杯省赛 JAVA A组 - 蜂巢

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;蓝桥杯题解集合 &#x1f4dd;原题地址&#xff1a;蜂巢 &#x1f4e3;专栏定位&#xff1a;为想参加蓝桥别的小伙伴整理常考算法题解&#xff0c;祝大家都能…

背包问题——“0-1背包”,“完全背包”(这样讲,还能不会?)

目录 一、0-1背包 1.1、0-1背包解决的问题 1.2、dp数组定义 1.3、转移方程 1.3.1、二维dp数组 1.3.2、一维dp数组 1.4、遍历顺序 1.5、测试代码 1.6、练习 二、完全背包 2.1、完全背包解决问题 2.2、与0-1背包的区别 2.3、测试代码 2.4、拓展问题&#xff1a;装满…

【2022】13 年终总结

新年Flag 2023年&#xff0c;为了各方面能有所进步&#xff0c;列一些希望达成的目标和想做的事&#xff0c;到年底看看效果。 撰写一篇英文论文 申请到CSC 和xl去外地玩两次 想到了再加 去年Flag倒了几个&#xff1f; 一维河网水动力学模型导师说不用自己编&#xff0c;看懂…

Numpy文件交互:.npy和.npz有什么区别?

文章目录saveloadsavezsavez_compressedNumpy提供了以.npy为后缀的文件存储方案&#xff0c;与这种文件格式密切相关的读、写函数分别是np.load和np.save。通过savez可以一次性存储多个数组&#xff0c;并可通过load以键值对的形式读取出来&#xff1b;如果觉得文件太大&#x…

Mybatis缓存

内存中的一块存储空间&#xff0c;服务于某个应用程序&#xff0c;旨在将频繁读取的数据临时保存在内存中&#xff0c;便于二次快速访问。 一级缓存 SqlSession级别的缓存&#xff0c;同一个SqlSession的发起多次同构查询&#xff0c;会将数据保存在一级缓存中。 注意&#x…

【NI Multisim 14.0虚拟仪器设计——放置虚拟仪器仪表(频率特性测试仪)】

目录 序言 &#x1f34d;放置虚拟仪器仪表 &#x1f349;频率特性测试仪 &#x1f34a;&#x1f34a;1.“模式”选项组 &#x1f34a;&#x1f34a;2.“水平”选项组 &#x1f34a;&#x1f34a;3.“垂直”选项组 &#x1f34a;&#x1f34a;4.“控件”选项组 序言 N…