Java基础回顾——反射

文章目录

  • 介绍
  • Class类
    • 与instanceof的区别
  • 访问字段
  • 调用方法
  • 调用构造方法
  • 获取继承关系
  • 动态代理

介绍

反射reflection,是指在程序运行期间可以拿到一个对象的所有信息。

正常情况下获取一个对象信息,需要import该类,反射可以在对某个实例一无所知的情况下,调用其方法。

Class类

除了基本类型外,Java的其他类型全是class(除了interface)

class由JVM执行过程中动态加载,JVM第一次读取到一种class类型时,将其加载进内存。

每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来。(Class类型是一个名叫Class的class)

public final class Class {private Class() {}
}

以String类为例,当JVM加载String类时,它首先读取String.class文件到内存,然后,为String类创建一个Class实例并关联起来:

Class cls = new Class(String);

在这里插入图片描述

由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,就可以通过这个Class实例获取到该实例对应的class的所有信息。

这种通过Class实例获取class信息的方法称为反射(Reflection)

获取class的Class实例:

public class reflection {public static void main(String[] args) {
//        1、直接通过一个class的静态变量class获取Class cls=String.class;//        2、如果有一个实例变量,可以通过该实例提供的getClass()方法获取String s="Hello";Class cls2=s.getClass();//        3、如果知道一个class的完整类名,可以通过静态方法Class.forName()获取Class cls3=null;try{cls3=Class.forName("java.lang.String");}catch (Exception e){System.out.println(e);}System.out.println(cls);System.out.println(cls2);System.out.println(cls3);
//class java.lang.String
//class java.lang.String
//class java.lang.String}
}

与instanceof的区别

Class cls1 = String.class;String s = "Hello";
Class cls2 = s.getClass();boolean sameClass = cls1 == cls2; // true
Integer n = new Integer(123);boolean b1 = n instanceof Integer; // true,因为n是Integer类型
boolean b2 = n instanceof Number; // true,因为n是Number类型的子类boolean b3 = n.getClass() == Integer.class; // true,因为n.getClass()返回Integer.class
boolean b4 = n.getClass() == Number.class; // false,因为Integer.class!=Number.class

用instanceof不但匹配指定类型,还匹配指定类型的子类。而用==判断class实例可以精确地判断数据类型,但不能作子类型比较。

访问字段

Class类提供了以下几个方法来获取字段:

  • Field getField(name):根据字段名获取某个public的field(包括父类)
  • Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
  • Field[] getFields():获取所有public的field(包括父类)
  • Field[] getDeclaredFields():获取当前类的所有field(不包括父类)

一个Field对象包含了一个字段的所有信息:

  • getName():返回字段名称,例如,“name”;

private无法访问,调用Field.setAccessible(true)的意思是,别管这个字段是不是public,一律允许访问。

  • getType():返回字段类型,也是一个Class实例,例如,String.class;
  • getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。

此外,setAccessible(true)可能会失败。如果JVM运行期存在SecurityManager,那么它会根据规则进行检查,有可能阻止setAccessible(true)。

设置字段值
通过Field实例既然可以获取到指定实例的字段值,自然也可以设置字段的值。

设置字段值是通过Field.set(Object, Object)实现的,其中第一个Object参数是指定的实例,第二个Object参数是待修改的值。

调用方法

可以通过Class实例获取所有Method信息。Class类提供了以下几个方法来获取Method:

  • Method getMethod(name, Class…):获取某个public的Method(包括父类)
  • Method getDeclaredMethod(name, Class…):获取当前类的某个Method(不包括父类)
  • Method[] getMethods():获取所有public的Method(包括父类)
  • Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)

一个Method对象包含一个方法的所有信息:

  • getName():返回方法名称,例如:“getScore”;
  • getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
  • getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
  • getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
public class Main {public static void main(String[] args) throws Exception {// String对象:String s = "Hello world";// 获取String substring(int)方法,参数为int:Method m = String.class.getMethod("substring", int.class);// 在s对象上调用该方法并获取结果:String r = (String) m.invoke(s, 6);// 打印调用结果:System.out.println(r);}
}

调用静态方法

如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null。

调用非public方法

通过Method.setAccessible(true)允许其调用

public class Main {public static void main(String[] args) throws Exception {Person p = new Person();Method m = p.getClass().getDeclaredMethod("setName", String.class);m.setAccessible(true);m.invoke(p, "Bob");System.out.println(p.name);}
}class Person {String name;private void setName(String name) {this.name = name;}
}

setAccessible(true)可能会失败。如果JVM运行期存在SecurityManager,那么它会根据规则进行检查,有可能阻止setAccessible(true)。

多态
使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在)。

调用构造方法

通过Class实例获取Constructor的方法如下:

  • getConstructor(Class…):获取某个public的Constructor;
  • getDeclaredConstructor(Class…):获取某个Constructor;
  • getConstructors():获取所有public的Constructor;
  • getDeclaredConstructors():获取所有Constructor。
public class Main {public static void main(String[] args) throws Exception {// 获取构造方法Integer(int):Constructor cons1 = Integer.class.getConstructor(int.class);// 调用构造方法:Integer n1 = (Integer) cons1.newInstance(123);System.out.println(n1);// 获取构造方法Integer(String)Constructor cons2 = Integer.class.getConstructor(String.class);Integer n2 = (Integer) cons2.newInstance("456");System.out.println(n2);}
}

获取继承关系

  • Class getSuperclass():获取父类类型;
  • Class[] getInterfaces():获取当前类实现的所有接口。

动态代理

不编写实现类,直接在运行期创建某个interface实例

动态代理(dynamic proxy)机制:可以在运行期动态创建某个interface的实例。

正常写法:


public interface Hello {void morning(String name);
}public class HelloWorld implements Hello {public void morning(String name) {System.out.println("Good morning, " + name);}
}Hello hello = new HelloWorld();
hello.morning("Bob");

动态代理写法:


public class Main {public static void main(String[] args) {InvocationHandler handler = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(method);if (method.getName().equals("morning")) {System.out.println("Good morning, " + args[0]);}return null;}};Hello hello = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(), // 传入ClassLoadernew Class[] { Hello.class }, // 传入要实现的接口handler); // 传入处理调用方法的InvocationHandlerhello.morning("Bob");}
}interface Hello {void morning(String name);
}

在运行期动态创建一个interface实例的方法如下:

1、定义一个InvocationHandler实例,它负责实现接口的方法调用;
2、通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
- 使用的ClassLoader,通常就是接口类的ClassLoader;
- 需要实现的接口数组,至少需要传入一个接口进去;
- 用来处理接口方法调用的InvocationHandler实例。
3、将返回的Object强制转型为接口。

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

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

相关文章

hive命令启动出现classnotfound

环境:ambari集群三个节点node104、node105和node106,其中node105上有hiveserver2,并且三个节点均有HIVE CLIENT 注意:“./”指hive安装目录 其中装有hiveserver2的node105节点,由于某种需要向lib目录下上传了某些jar包…

css实用入门

css也精炼了解以下内容即可: 一个网页所呈现出来的画面,你可以理解他们就是由一个又一个的盒子拼凑组成而来。 一个盒子,它有外边距,还有内边距。 黑色的部分是盒子本身的样子,外侧蓝色的部分是外边距,内…

2024年PMP报考条件是什么?

报考PMP(项目管理专业)的条件并不是很困难,只需要满足以下两个条件之一: 1、年龄达到23周岁或本科毕业已满3年或高中毕业已满5年,满足其中任一条件即可; 2、获得由PMI(项目管理学会)…

ADS学习笔记(一)——更新中

在ADS中,信号上升时间为信号从0~100%所用的时间,而实际上定义的上升边均为10%~90%,所以可以认为上升边=0.8*ADS设置上升时间。 一、终端开路及短路的反射信号 1.仿真条…

测试工具Jmeter:设置中文界面

首先我们打开Jmeter所在的文件,进入bin目录,打开Jmeter.properties: 打开后找到languageen: 改为zh_CN: 保存关闭,然后再打开Jmeter: 英文并不会显得高级,能做到高效的性能测试才是高级的。

Wordpress插件WP-Statistics无法识别来访IP国家和城市处理方法

Wordpress插件WP-Statistics,可以识别网站访问者的IP物理地址,统计出城市、国家,但最近发现都显示unknown/未知: 更新GeoIP数据库到最新还是不行: 偶然找到了之前能用的数据库,恢复回去,竟然大…

dell服务器 R740xd安装windows server 2019过程记录

公司有两台dell服务器型号是R740xd,增加了存储,更新系统到windows server 2019标准版。 查找了网上的系统安装方式,都没有实践成功,做一下工作记录,给大家做参考。 网络搜索到的两种方式,进行安装 &#x…

如何通过ssh管道传输文件到ubuntu

如何在window系统中,通过ssh将指定的文件传输到ubuntu中呢? 比较常用的有以下种方式: 共享文件夹借助工具, FileZillaMobaxtermWinSCPXshell XFTP samba互传PuTTY pscp 今天主要分享一个使用python脚本搭建的一个,…

Oracle定时任务的创建与禁用/删除

在开始操作之前,先从三W开始,即我常说的what 是什么;why 为什么使用;how 如何使用。 一、Oracle定时器是什么 Oracle定时器是一种用于在特定时间执行任务或存储过程的工具,可以根据需求设置不同的时间段和频率来执行…

HarmonyOS后台代理提醒

后台代理提醒 简介 随着生活节奏的加快,我们有时会忘记一些重要的事情或日子,所以提醒功能必不可少。应用可能需要在指定的时刻,向用户发送一些业务提醒通知。例如购物类应用,希望在指定时间点提醒用户有优惠活动。为满足此类业…

本地运行vue脚手架 从下载node开始到创建vue

vue2node 1.安装node vue2版本我node一般使用的是14版本左右 ![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/f886442c20114e62aef8113425e34ad2.pngnode官网 假如我node版本过低 你就找到控制面板 找到node卸载掉 我一般下载两个版本 安装vue cli vuecl…

BurpSuite抓不到本地包

目录 前言 一、方法一 二、方法二 三、方法三 四、方法四 总结 🌈嗨!我是Filotimo__🌈。很高兴与大家相识,希望我的博客能对你有所帮助。 💡本文由Filotimo__✍️原创,首发于CSDN📚。 &#x1f…

基础数据结构(1):链表

在学习算法时,发现用什么数据结构来存储数据是很重要的,所以学习数据结构也是必须的,先从基础数据结构:数组,字符串,链表,栈,队列,树,矩阵,邻接表…

利用虚继承解决菱形继承(钻石继承)的问题 学习笔记

菱形继承概念: 两个派生类继承同一个基类 又有某个类同时继承两个派生类 这种继承又被称为菱形继承,或者钻石继承 定义一个基类Animal,两个派生类Sheep、Camal,SheepCame继承于两个派生类 class Animal { public:int m_Age; }…

Bert-vits2-v2.2新版本本地训练推理整合包(原神八重神子英文模型miko)

近日,Bert-vits2-v2.2如约更新,该新版本v2.2主要把Emotion 模型换用CLAP多模态模型,推理支持输入text prompt提示词和audio prompt提示语音来进行引导风格化合成,让推理音色更具情感特色,并且推出了新的预处理webuI&am…

SpringMVC---详细介绍+使用

文章目录 什么是SpringMVC?使用SpringMVCSpringMVC创建和连接创建连接RequestMapping的基础使用 获取参数返回数据返回静态页面返回非页面的普通数据(text/html)返回JSON对象请求转发或者请求重定向 什么是SpringMVC? SpringMVC它…

【Spring】14 ApplicationEventPublisherAware 接口

文章目录 1. 简介2. 作用3. 使用3.1 创建并实现接口3.2 配置 Bean 信息3.3 创建启动类3.4 启动3.5 工作流程图 4. 应用场景总结 Spring 框架为开发者提供了丰富的扩展点,其中之一是 Bean 生命周期中的回调接口。本文将专注介绍一个与事件发布相关的接口 Applicatio…

【SQLite】SQLite数据库简单使用与Navicat安装-加密

Sqlite为免安装数据库,安装步骤总结: 官网下载Sqlit数据库,官网下载地址:https://www.sqlite.org/download.html 下载: sqlite-dll-win64-x64-3390400.zip或者32位sqlite-dll-win32 sqlite-tools-win-x64-3440200.zip或者32位sqlite-tools-wi…

Opencv入门五 (显示图片灰度值)

源码如下&#xff1a; #include <opencv2/opencv.hpp> int main(int argc, char** argv) { cv::Mat img_rgb, img_gry, img_cny; cv::namedWindow("Example Gray",cv::WINDOW_AUTOSIZE); cv::namedWindow("Example Canny", cv::WINDOW_…

若依vue前端 报错error:0308010C:digital envelope routines::unsupported一步到胃的解决方法

第一步: 直接打开package.json 第二步: 直接替换scripts的内容为下面的内容 "scripts": {"dev": "SET NODE_OPTIONS--openssl-legacy-provider && vue-cli-service serve","build:prod": "SET NODE_OPTIONS--openssl-leg…