读书笔记-《ON JAVA 中文版》-摘要20[第十九章 类型信息-1]
文章目录
- 第十九章 类型信息
- 1. 为什么需要 RTTI
- 2. Class 对象
- 2.1 Class 对象
- 2.2 类字面常量
- 2.3 泛化的 Class 引用
- 3. 类型转换检测
- 4. 注册工厂
- 5. 类的等价比较
- 6. 反射:运行时类信息
- 7. 自我学习总结
第十九章 类型信息
RTTI(RunTime Type Information,运行时类型信息)能够在程序运行时发现和使用类型信息
RTTI 把我们从只能在编译期进行面向类型操作的禁锢中解脱了出来,并且让我们可以使用某些非常强大的程序。
本章将讨论 Java 是如何在运行时识别对象和类信息的。主要有两种方式:
-
“传统的” RTTI:假定我们在编译时已经知道了所有的类型;
-
“反射”机制:允许我们在运行时发现和使用类的信息。
1. 为什么需要 RTTI
import java.util.stream.Stream;abstract class Shape {void draw() {// PS:基类中包含 draw() 方法,它通过传递 this 参数传递给 System.out.println() ,// 间接地使用 toString() 打印类标识符System.out.println(this + ".draw()");}@Overridepublic abstract String toString();
}class Circle extends Shape {@Overridepublic String toString() {return "Circle";}
}class Square extends Shape {@Overridepublic String toString() {return "Square";}
}class Triangle extends Shape {@Overridepublic String toString() {return "Triangle";}
}public class Shapes {public static void main(String[] args) {// PS:在把 Shape 对象放入 Stream<Shape> 中时就会进行向上转型(隐式)Stream.of(new Circle(), new Square(), new Triangle()).forEach(Shape::draw);}
}
输出:
Circle.draw()
Square.draw()
Triangle.draw()
严格来说, Stream 实际上是把放入其中的所有对象都当做 Object 对象来持有,只是取元素时会自动将其类型转为 Shape 。这也是 RTTI 最基本的使用形式,因为在 Java 中,所有类型转换的正确性检查都是在运行时进行的。这也正是 RTTI 的含义所在:在运行时,识别一个对象的类型
—PS:多态的应用
2. Class 对象
2.1 Class 对象
Java 使用 Class 对象来实现 RTTI,即便是类型转换这样的操作都是用 Class 对象实现的。
类是程序的一部分,每个类都有一个 Class 对象。换言之,每当我们编写并且编译了一个新类,就会产生一个 Class 对象(更恰当的说,是被保存在一个同名的 .class 文件中)。
所有的类都是第一次使用时动态加载到 JVM 中的,当程序创建第一个对类的静态成员的引用时,就会加载这个类。
其实构造器也是类的静态方法,虽然构造器前面并没有 static 关键字。所以,使用 new 操作符创建类的新对象,这个操作也算作对类的静态成员引用。
因此,Java 程序在它开始运行之前并没有被完全加载,很多部分是在需要时才会加载。这一点与许多传统编程语言不同,动态加载使得 Java 具有一些静态加载语言(如 C++)很难或者根本不可能实现的特性。
一旦某个类的 Class 对象被载入内存,它就可以用来创建这个类的所有对象。
2.2 类字面常量
Java 还提供了另一种方法来生成类对象的引用:类字面常量(
例如:FancyToy.class)。类字面常量不仅可以应用于普通类,也可以应用于接口、数组以及基本数据类型。
当使用 .class 来创建对 Class 对象的引用时,不会自动地初始化该 Class 对象。为了使用类而做的准备工作实际包含三个步骤:
-
加载,这是由类加载器执行的
-
链接。在链接阶段将验证类中的字节码,为 static 字段分配存储空间,并且如果需要的话,将解析这个类创建的对其他类的所有引用。
-
初始化。如果该类具有超类,则先初始化超类,执行 static 初始化器和 static 初始化块。
直到第一次引用一个 static 方法(构造器隐式地是 static )或者非常量的 static 字段,才会进行类初始化。
package typeinfo;import java.util.Random;class Initable {static final int STATIC_FINAL = 47;static final int STATIC_FINAL2 =ClassInitialization.rand.nextInt(1000);static {System.out.println("Initializing Initable");}
}class Initable2 {static int staticNonFinal = 147;static {System.out.println("Initializing Initable2");}
}class Initable3 {static int staticNonFinal = 74;static {System.out.println("Initializing Initable3");}
}public class ClassInitialization {public static Random rand = new Random(47);public static void main(String[] args) throws ClassNotFoundException {// 仅使用 .class 语 法来获得对类对象的引用不会引发初始化Class initable = Initable.class;System.out.println("After creating Initable ref");// 如果一个 static final 值是“编译期常量”(如 Initable.staticFinal ),// 那么这个值不需要对 Initable 类进行初始化就可以被读取System.out.println(Initable.STATIC_FINAL);// PS:对 Initable.staticFinal2 的访问将强制进行类的初始化,因为它不是一个编译期常量,// Initable.staticFinal2 虽然也是用 static final 修饰System.out.println(Initable.STATIC_FINAL2);// 如果一个 static 字段不是 final 的,那么在对它访问时,总是要求在它被读取之前,// 要先进行链 接(为这个字段分配存储空间)和初始化(初始化该存储空间)System.out.println(Initable2.staticNonFinal);// Class.forName() 来产生 Class 引 用会立即就进行初始化Class initable3 = Class.forName("typeinfo.Initable3");System.out.println("After creating Initable3 ref");System.out.println(Initable3.staticNonFinal);}
}
输出:
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74
2.3 泛化的 Class 引用
Java 引入泛型语法之后,我们可以使用泛型对 Class 引用所指向的 Class 对象的类型进行限定。在下面的实例中,两种语法都是正确的:
public class GenericClassReferences {public static void main(String[] args) {Class intClass = int.class;Class<Integer> genericIntClass = int.class;genericIntClass = Integer.class; // 同一个东西// 普通的类引用可以重新赋值指向任何其他的 Class 对象intClass = double.class;// 使用泛型限定的类引用只能指向其声明的类型// genericIntClass = double.class;}
}
为了在使用 Class 引用时放松限制,我们使用了通配符,它是 Java 泛型中的一部分。通配符就是 ? ,表示“任何事物”。
public class WildcardClassReferences {public static void main(String[] args) {Class<?> intClass = int.class;intClass = double.class;// 为了创建一个限定指向某种类型或其子类的 Class 引用,// 我们需要将通配符与 extends 关键字配 合使用,创建一个范围限定。Class<? extends Number> bounded = int.class;bounded = double.class;bounded = Number.class;}
}
3. 类型转换检测
直到现在,我们已知的 RTTI 类型包括:
-
传统的类型转换,由 RTTI 确保转换的正确性,如果执行了一个错误的类型转换,就会抛出一个 ClassCastException 异常。
-
代表对象类型的 Class 对象. 通过查询 Class 对象可以获取运行时所需的信息。
RTTI 在 Java 中还有第三种形式,那就是关键字 instanceof 。它返回一个布尔值,告诉我们对象是不是某个特定类型的实例,可以用提问的方式使用它,就像这个样子:
if(x instanceof Dog) ((Dog)x).bark();
在将 x 的类型转换为 Dog 之前, if 语句会先检查 x 是否是 Dog 类型的对象。进行向下转型前,如果没有其他信息可以告诉你这个对象是什么类型,那么使用 instanceof 是非常重要的,否则会得到一个 ClassCastException 异常。
instanceof 有一个严格的限制:只可以将它与命名类型进行比较,而不能与 Class 对象作比较。
Class.isInstance() 方法提供了一种动态测试对象类型的方法。
Dog.isInstance(x)
4. 注册工厂
工厂方法可以以多态方式调用,并为你创建适当类型的对象。
5. 类的等价比较
当你查询类型信息时,需要注意:instanceof 的形式(即 instanceof 或 isInstance() ,这两者产生的结果相同) 和 与 Class 对象直接比较 这两者间存在重要区别。
class Base {
}class Derived extends Base {
}public class FamilyVsExactType {static void test(Object x) {System.out.println("Testing x of type " + x.getClass());System.out.println("x instanceof Base " + (x instanceof Base));System.out.println("x instanceof Derived " + (x instanceof Derived));System.out.println("Base.isInstance(x) " + Base.class.isInstance(x));System.out.println("Derived.isInstance(x) " + Derived.class.isInstance(x));System.out.println("x.getClass() == Base.class " + (x.getClass() == Base.class));System.out.println("x.getClass() == Derived.class " + (x.getClass() == Derived.class));System.out.println("x.getClass().equals(Base.class)) " + (x.getClass().equals(Base.class)));System.out.println("x.getClass().equals(Derived.class)) " + (x.getClass().equals(Derived.class)));System.out.println("------------");}public static void main(String[] args) {test(new Base());test(new Derived());}
}
输出:
Testing x of type class typeinfo.Base
x instanceof Base true
x instanceof Derived false
Base.isInstance(x) true
Derived.isInstance(x) false
x.getClass() == Base.class true
x.getClass() == Derived.class false
x.getClass().equals(Base.class)) true
x.getClass().equals(Derived.class)) false
------------
Testing x of type class typeinfo.Derived
x instanceof Base true
x instanceof Derived true
Base.isInstance(x) true
Derived.isInstance(x) true
x.getClass() == Base.class false
x.getClass() == Derived.class true
x.getClass().equals(Base.class)) false
x.getClass().equals(Derived.class)) true
------------
instanceof 和 isInstance() 产生的结果相同, equals() 和 == 产生的结果也相同。但测试本身得出了不同的
结论。与类型的概念一致, instanceof 说的是“你是这个类,还是从这个类派生的类?”。而如果使用 == 比较实际的 Class 对象,则与继承无关 —— 它要么是确切的类型,要么不是。
6. 反射:运行时类信息
类 Class 支持反射的概念, java.lang.reflect 库中包含类 Field 、 Method 和 Constructor (每一个都实现了 Member 接口)。这些类型的对象由 JVM 在运行时创建,以表示未知类中的对应成员。然后,可以使用 Constructor 创建新对象, get() 和 set() 方法读取和修改与 Field 对象关联的字段, invoke() 方法调用与 Method 对象关联的方法。此外,还可以调
用便利方法 getFields() 、 getMethods() 、 getConstructors() 等,以返回表示字段、方法和构造函数的对象数组。
RTTI 和反射的真正区别在于,使用 RTTI 时,编译器在编译时会打开并检查 .class 文件。换句话说,你可以 用“正常”的方式调用一个对象的所有方法。通过反射, .class 文件在编译时不可用;它由运行时环境打开并检查。
7. 自我学习总结
-
RTTI(RunTime Type Information,运行时类型信息)能够在程序运行时发现和使用类型信息
-
Java 使用 Class 对象来实现 RTTI,保存在 java 文件编译后 .class 文件中
-
Java 还提供了另一种方法来生成类对象的引用:类字面常量(
例如:FancyToy.class),此时不会自动初始化该 Class 对象
-
使用类前要做到3个步骤:加载(JVM加载类)、链接(为 static 字段分配存储空间)、初始化(初始化超类及 static 修饰的方法和块)
-
可以使用泛型对 Class 引用所指向的 Class 对象的类型进行限定:
Class<Integer> genericIntClass = int.class;
通配符与 extends 关键字配合使用,创建一个范围限定:
Class<? extends Number> bounded = int.class;
- 类型转换检测:
1)instanceof
if(x instanceof Dog) ((Dog)x).bark();
2)isInstance
Dog.isInstance(x)
- instanceof 说的是“你是这个类,还是从这个类派生的类?”(父类或者本类);而如果使用 == 比较实际的 Class 对象,则与继承无关 —— 它要么是确切的类型,要么不是(确切的对象)
相关文章:
读书笔记-《ON JAVA 中文版》-摘要20[第十九章 类型信息-1]
文章目录 第十九章 类型信息1. 为什么需要 RTTI2. Class 对象2.1 Class 对象2.2 类字面常量2.3 泛化的 Class 引用 3. 类型转换检测4. 注册工厂5. 类的等价比较6. 反射:运行时类信息7. 自我学习总结 第十九章 类型信息 RTTI(RunTime Type Information&am…...
3、Linux驱动开发:模块_传递参数
目录 🍅点击这里查看所有博文 随着自己工作的进行,接触到的技术栈也越来越多。给我一个很直观的感受就是,某一项技术/经验在刚开始接触的时候都记得很清楚。往往过了几个月都会忘记的差不多了,只有经常会用到的东西才有可能真正记…...

基于 ThinkPHP 5.1(稳定版本) 开发wms 进销存系统源码
基于ThinkPHP 5.1(LTS版本)开发的WMS进销存系统源码 管理员账号密码:admin 一、项目简介 这个系统是一个基于ThinkPHP框架的WMS进销存系统。 二、实现功能 控制台 – 权限管理(用户管理、角色管理、节点管理) – 订…...
全面解析 SOCKS5 代理和 HTTP 代理在网络安全与爬虫应用中的技术对比与应用指南
一、SOCKS5 代理和 HTTP 代理的基本原理 SOCKS5 代理:SOCKS5 是一种网络协议,可以在传输层代理 TCP 和 UDP 请求。它不解析请求内容,仅在客户端和代理服务器之间建立连接,并转发数据。SOCKS5 代理支持众多网络协议和端口类型&…...
DevOps系列文章 之 docker 制作kafka镜像
Docker制作Kafka镜像教程 概述 本教程将指导你如何使用Docker制作一个Kafka镜像。Kafka是一个高性能、分布式的消息队列系统,用于处理大规模的实时数据流。使用Docker制作Kafka镜像可以方便地部署和管理Kafka集群。 整体流程 下面是制作Kafka镜像的整体流程…...

iPhone 安装 iOS 17公测版(Public Beta)
文章目录 步骤1. 备份iPhone资料步骤2. 申请iOS 17 公测Beta 资格步骤3. 下载iOS 16 Beta 公测描述档步骤4. 选择iOS 17 Beta 公测描述档更新项目步骤5. 升级iOS 17 Public Beta 公开测试版 苹果已经开始向大众释出首个iOS 17 公开测试版/ 公测版( iOS 17 Public Beta)…...

Spingboot yaml 配置文件及数据读取
属性配置在这里插入图片描述 修改服务器端口 → server.port80 修改 banner → spring.main.banner off(关闭)/console(控制台)/log(日志) 日志 → logging.level.rootinfo Common Application Properties 配置文件分类 优先级 如果三种文件共存时,优先级为&am…...

vue中使用axios发送请求时,后端同一个session获取不到值
问题描述: 在登录页面加载完成后通过axios请求后端验证码接口(这时后端会生成一个session用于保存验证码数值),当输入完用户名、密码、验证码后请求登录接口,报错验证码输入错误,打印后端保存验证码的sessi…...

html请求谷歌音频跨域问题(谷歌翻译接口)虚拟机ping不通google(下载谷歌音频、下载百度翻译音频)
文章目录 调用谷歌翻译接口,尝试了几种方案,都提示跨域不行第一种(通过js代码获取音频文件的Blob对象,提示跨域了)代码结果 第二种(尝试新窗打开音频url,404,估计也是跨域了…...
【设计模式|结构型】享元模式(Flyweight Pattern)
概述 享元模式(Flyweight Pattern)是一种结构型设计模式,它旨在通过共享对象来减少系统中的对象数量,以便在有限的内存中节省空间和提高性能。在享元模式中,对象分为两部分:内部状态(Intrinsic…...
最小覆盖子串(JS)
最小覆盖子串 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。 注意: 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量…...

<C语言> 预处理和宏
1.预定义符号 __FILE__ //进行编译的源文件 __LINE__ //文件当前的行号 __DATE__ //文件被编译的日期 __TIME__ //文件被编译的时间 __STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义这些预定义符号都是C语言内置的。 举个例子&…...
代驾公司如何进行运营分析
在这个快节奏的社会中,人们的生活节奏也在不断加快,对于代驾服务的需求也日益增长。然而,如何在这个竞争激烈的市场中,让订单稳稳地握在自己的手中,成为了每一个代驾公司都需要深思的问题。那么,代驾公司如…...

初学HTML:采用CSS绘制一幅夏天的图
下面代码使用了HTML和CSS来绘制一幅炎炎夏日吃西瓜的画面。其中,使用了伪元素和阴影等技巧来实现部分效果。 <!DOCTYPE html> <html> <head><title>炎炎夏日吃西瓜</title><style>body {background-color: #add8e6; /* 背景颜…...

经典文献阅读之--NoPe-NeRF(优化无位姿先验的神经辐射场)
0. 简介 在没有预先计算相机姿态的情况下训练神经辐射场(NeRF)是具有挑战性的。最近在这个方向上的进展表明,在前向场景中可以联合优化NeRF和相机姿态。然而,这些方法在剧烈相机运动时仍然面临困难。我们通过引入无畸变单目深度先…...

在docker中没有vi如何修改docker中的文件
今天在做学成在线的项目,遇到了一个问题,就是死活登不上xxl-job,按照之前遇到的nacos的问题,我怀疑很大概率是和当时的ip设置有关,不知道nacos的ip怎么修改的同学,可以看看这篇文章:关于docker中…...

【Docker】Docker应用部署之Docekr容器安装Nginx
目录 一、搜索镜像 二、拉取镜像 三、创建容器 四、测试使用 一、搜索镜像 docker search nginx 二、拉取镜像 docker pull nginx # 不加冒号版本号 默认拉取最新版 三、创建容器 首先我们需要在宿主机创建数据卷目录 mkdir nginx # 创建目录 cd nginx # 进入目录 mkd…...

flutter开发实战-jsontodart及 生成Dart Model类
flutter开发实战-jsontodart及 生成Dart Model类。 在开发中,经常遇到请求的数据Json需要转换成model类。这里记录一下Jsontodart生成Dart Model类的方案。 一、JSON生成Dart Model类 在开发中经常用到将json转成map或者list。通过json.decode() 可以方便 JSON 字…...

C++复刻:[流光按钮]+[悬浮波纹按钮]
目录 参考效果实现main.cppdialog.hdialog.cppflowingRayButton.h 流动光线按钮flowingRayButton.cpp 流动光线按钮hoveringRippleButton.h 悬浮波纹按钮hoveringRippleButton.cpp 悬浮波纹按钮模糊知识点 源码 参考 Python版本:GitHub地址 B站主页 效果 实现 ma…...

CompletableFuture 详解
目录 简单介绍 常见操作 创建 CompletableFuture new 关键字 静态工厂方法 处理异步结算的结果 简单介绍 CompletableFuture 同时实现了 Future 和 CompletionStage 接口。 public class CompletableFuture<T> implements Future<T>, CompletionStage<T…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...

面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...