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,一经查实,立即删除!

相关文章

【力扣100】146.LRU缓存

添加链接描述 class DLinkedNode:def __init__(self, key0, value0):self.key keyself.value valueself.prev Noneself.next Noneclass LRUCache:def __init__(self, capacity: int):self.cache dict()# 使用伪头部和伪尾部节点 self.head DLinkedNode()self.tail D…

hive命令启动出现classnotfound

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

css实用入门

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

【Python】基于数据库连接实现简单注册功能

说明 针对数据库的操作,一共分三步: 1.建立数据库连接 2.执行SQL语句 3.关闭数据库连接 Python操作数据库(如MySQL),Python发送能够与数据库直接通信的数据包,并获取数据库服务器的相应结果。是一种典型的基于TCP/IP…

导入别的目录下的py文件

要导入别的目录下的py文件,可以使用相对路径或绝对路径来导入。 使用相对路径导入: from ..other_directory import other_module使用绝对路径导入: import sys sys.path.append(/path/to/other_directory) import other_module在以上示例…

2024年PMP报考条件是什么?

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

Android 清除临时文件,清空缓存

python 代码: import os import shutil import tracebackdef delete_folder(path):if os.path.exists(path):print(f"删除文件夹: {path}")shutil.rmtree(path)print("删除完成")def delete_file(path):if os.path.exists(path):print(f"删…

华为数通试题

选择题 华为数通推出的面向企业的云计算平台是? A) FusionSphere B) CloudEngine C) Agile Controller D) eSight 下面哪个不是华为数通的核心交换机系列? A) S12700 B) S5700 C) S9300 D) CloudEngine 华为数通的企业级路由器系列包括哪个&#xff1f…

Python画一个圣诞树

用python画一个圣诞树 可以使用Python中的turtle模块来画一个圣诞树。 下面是一段示例代码: from turtle import * from random import * import math# 绘图方法 def Rightdraw(Range, Fd, Right):for i in range(Range): # Range循环次数fd(Fd) # 向前Fd个距离…

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

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

【Docker】except yum.Errors.RepoError, e:-yum-config-manager --add-repo报错

问题 [rootitfuture ~]# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repoFile "/usr/bin/yum-config-manager", line 135except yum.Errors.RepoError, e:^ SyntaxError: invalid syntax原因: linux 系统中…

Transport endpoint is not connected

问题背景 今早打开服务器一看发现昨天挂上去的数据预处理脚本中断了,报错如下: python3: cant open file preprocess_data.py: [Errno 107] Transport endpoint is not connected之前从来没有遇到过这种问题,于是顺手执行了 cd ..&#xff…

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

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

C语言中关于函数和数组的理解

函数 函数由函数头和函数体组成&#xff0c;函数头包括函数名、返回值类型和参数列表&#xff0c;函数体包括函数执行的语句块。 #include <stdio.h> int sum(int x,int y) //定义函数的作用以及格式 {int z;zxy; //作用是把两个整数相加…

【taro react】---- 解决 taro 编译 H5 姓名脱敏导致的 iOS 部分低版本白屏问题

1. 姓名脱敏方法 判断传入字段是否是字符串;将字符串除第一个字符后的其他字符全部替换为 *。const replaceUserName = (name) => {if(isTypingMathods.isString(name)){return name.replace(/(?<=.)./g,*)}return name }2. 问题 编译后在浏览器和安卓机没有发现任何问…

[THUPC 2023 决赛] 烂柯杯

题目背景 却说庞统迤逦前进&#xff0c;抬头见两山逼窄&#xff0c;树木丛杂&#xff1b;又值夏末秋初&#xff0c;枝叶茂盛。庞统心下甚疑&#xff0c;勒住马问&#xff1a;“此处是何地&#xff1f;”数内有新降军士&#xff0c;指道&#xff1a;“此处地名落凤坡。”庞统惊…

视觉增强RTK论文(1)—— GNSS-Stereo-Inertial SLAM for Arable Farming

文章目录 摘要方法标记ORB-SLAM3GNSS-Stereo-Inertial融合实验结果代码摘要 农业任务自动化速度的加快要求现场机器人采用高精度和鲁棒的定位系统。同时定位和映射(SLAM)方法不可避免地会在探索性轨迹上积累漂移,并且主要依赖于位置重新访问和循环闭合来保持一个有界的全局…

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

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

12.1 知识回顾(过滤器、 模型层)

一、过滤器 1.1 编写步骤 1 注册app2 在某个app下&#xff1a;创建templatetags模块(模块名只能是templatetags&#xff09;3 在包下写一个py文件&#xff0c;随便命名 在py文件中&#xff1a;写入 from django import templateregister template.Library() &#xff08;注&am…

第5章-第2节-Java里的匿名对象和构造方法

1、匿名对象 匿名&#xff1a;没有名字 生活层面&#xff1a;匿名投票、匿名信... 程序层面&#xff1a; 有名对象和匿名对象 有名对象&#xff1a; 理解&#xff1a;有名字的对象 模板&#xff1a; 数据类型 对象名 new 数据类型(...); 匿名对象&#xff1a; 理…