程序人生——Java泛型和反射的使用建议
目录
- 引出
- 泛型和反射
- 建议93:Java的泛型是类型擦除的
- 建议94:不能初始化泛型参数和数组
- 建议95:强制声明泛型的实际类型
- 建议96:不同的场景使用不同的泛型通配符
- 建议97:警惕泛型是不能协变和逆变的
- 建议98:建议采用的顺序是List,List,List
- 建议99:严格限定泛型类型采用多重界限
- 建议100:数组的真实类型必须是泛型类型的子类型
- 建议101:注意Class类的特殊性
- 建议102:适时选择getDeclaredXXX和getXXX
- 建议103:反射访问属性或方法是将Accessible设置为true
- 建议104:使用forName动态加载类文件
- 建议105:动态加载不适合数组
- 建议106:动态代理可以使代理模式更加灵活
- 建议107:使用反射增加装饰模式的普适性
- 建议108:反射让模板方法模式更强大
- 建议109:不需要太多关注反射效率
- 深入认识JVM
- JVM内存分配,类加载
- 创建对象的4种方法总结
- 垃圾回收GC
- JVM调优,Arthas使用
- 认识多线程
- 创建多线程方法+了解线程池
- 多线程下-1非原子性问题即解决
- 再论线程,创建、生命周期
- 总结
引出
程序人生——Java泛型和反射的使用建议
泛型和反射
建议93:Java的泛型是类型擦除的
- 加入泛型优点:加强了参数类型的安全性,减少了类型的转换。Java的泛型在编译期有效,在运行期被删除,也就是说所有的泛型参数类型在编译后都会被清除掉。所以:1、泛型的class对象时是相同的;2、泛型数组初始化时不能声明泛型类型;3、instanceof不允许存在泛型参数
建议94:不能初始化泛型参数和数组
- 泛型类型在编译期被擦除,在类初始化时将无法获得泛型的具体参数,所以泛型参数和数组无法初始化,但是ArrayList却可以,因为ArrayList初始化是向上转型变成了Object类型;需要泛型数组解决办法:只声明,不再初始化,由构造函数完成初始化操作
建议95:强制声明泛型的实际类型
- 无法从代码中推断出泛型类型的情况下,即可强制声明泛型类型;方法:List list2 = ArrayUtils.asList();在输入前定义这是一个Integer类型的参数
建议96:不同的场景使用不同的泛型通配符
- Java泛型支持通配符(Wildcard),可以单独使用一个“?”表示任意类,也可以使用extends关键字表示某一个类(接口)的子类型,还可以使用super关键字表示某一个类(接口)的父类型。1、泛型结构只参与“读”操作则限定上界(extends关键字);2、泛型结构只参与“写”操作则限定下界(使用super关键字);3、如果一个泛型结构既用作“读”操作也用作“写”操作则使用确定的泛型类型即可,如List
建议97:警惕泛型是不能协变和逆变的
- Java的泛型是不支持协变和逆变的,只是能够实现协变和逆变)(协变和逆变是指宽类型和窄类型在某种情况下(如参数、泛型、返回值)替换或交换的特性。简单地说,协变是用一个窄类型替换宽类型,而逆变则是用宽类型覆盖窄类型。子类覆写父类返回值类型比父类型变窄,则是协变;子类覆写父类型的参数类型变宽,则是逆变。数组支持协变,泛型不支持协变
建议98:建议采用的顺序是List,List,List
- List是确定的某一个类型,编码者知道它是一个类型,只是在运行期才确定而已;
- List可以进行读写操作,List<?>是只读类型,因为编译器不知道List中容纳的是什么类型的元素,无法增加、修改,但是能删除
建议99:严格限定泛型类型采用多重界限
- 使用“&”符号连接多个泛型界限,如:
建议100:数组的真实类型必须是泛型类型的子类型
- 有可能会抛出ClassCastException异常,toArray方法返回后会进行一次类型转换,Object数组转换成了String数组。由于我们无法在运行期获得泛型类型的参数,因此就需要调用者主动传入T参数类型
建议101:注意Class类的特殊性
- Java处理的基本机制:先把Java源文件编译成后缀为class的字节码文件,然后再通过ClassLoader机制把这些类文件加载到内存中,最后生成实例执行。Java使用一个元类(MetaClass)来描述加载到内存中的类数据,这就是Class类,它是一个描述类的类对象。Class类是“类中类”,具有特殊性:1、无构造函数,不能实例化,Class对象是在加载类时由Java虚拟机通过调用类加载器中的defineClass方法自动构建的;2、可以描述基本类型,8个基本类型在JVM中并不是一个对象,一般存在于栈内存中,但是Class类仍然可以描述它们,例如可以使用int.class表示int类型的类对象;3、其对象都是单例模式,一个Class的实例对象描述一个类,并且只描述一个类,反过来也成立,一个类只有一个Class实例对象。Class类是Java的反射入口,只有在获得了一个类的描述对象后才能动态地加载、调用,一般获得一个Class对象有三种途径:1、类属性方式,如String.class;2、对象的getClass方法,如new String().getClass();3、forName方法重载,如Class.forName(“java.lang.String”)。获得了Class对象后,就可以通过getAnnotation()获得注解,通过个体Methods()获得方法,通过getConstructors()获得构造函数等
建议102:适时选择getDeclaredXXX和getXXX
- getMethod方法获得的是所有public访问级别的方法,包括从父类继承的方法,而getDeclaredMethod获得的是自身类的所有方法,包括公用方法、私有方法等,而且不受限于访问权限。Java之所以这样处理,是因为反射本意只是正常代码逻辑的一种补充,而不是让正常代码逻辑产生翻天覆地的改动,所以public的属性和方法最容易获取,私有属性和方法也可以获取,但要限定本类。如果需要列出所有继承自父类的方法,需要先获得父类,然后调用getDeclaredMethods方法,之后持续递归
建议103:反射访问属性或方法是将Accessible设置为true
- 通过反射方式执行方法时,必须在invoke之前检查Accessible属性。而Accessible属性并不是我们语法层级理解的访问权限,而是指是否更容易获得,是否进行安全检查。Accessible属性只是用来判断是否需要进行安全检查的,如果不需要则直接执行,这就可以大幅度地提升系统性能。经过测试,在大量的反射情况下,设置Accessible为true可以提升性能20倍以上
建议104:使用forName动态加载类文件
- forName只是加载类,并不执行任何代码)(动态加载(Dynamic Loading)是指在程序运行时加载需要的类库文件,一般情况下,一个类文件在启动时或首次初始化时会被加载到内存中,而反射则可以在运行时再决定是否要加载一个类,然后在JVM中加载并初始化。动态加载通常是通过Class.forName(String)实现。一个对象的生成必然会经过一下两个步骤:1、加载到内存中生成Class的实例对象;2、通过new关键字生成实例对象;**动态加载的意义:**加载一个类即表示要初始化该类的static变量,特别是static代码块,在这里我们可以做大量的工作,比如注册自己,初始化环境等,这才是我们重点关注的逻辑
建议105:动态加载不适合数组
- 通过反射操作数组使用Array类,不要采用通用的反射处理API)(如果forName要加载一个类,那它首先必须是一个类—8个基本类型排除在外,不是具体的类;其次,它必须具有可追索的类路径,否则会报ClassNotFoundException异常。在Java中,数组是一个非常特殊的类,虽然是一个类,但没有定义类路径。作为forName参数时会抛出ClassNotFoundException异常,原因是:数组虽然是一个类,在声明时可以定义为String[],但编译器编译后会为不同的数组类型生成不同的类,所以要想动态创建和访问数组,基本的反射是无法实现的
建议106:动态代理可以使代理模式更加灵活
- Java的反射框架提供了动态代理(Dynamic Proxy)机制,允许在运行期对目标类生成代理,避免重复开发。静态代理是通过代理主题角色(Proxy)和具体主题角色(Real Subject)共同实现抽象主题角色(Subject)的逻辑的,只是代理主题角色把相关的执行逻辑委托给了具体主题角色而已。动态代理需要实现InvocationHandler接口,必须要实现invoke方法,该方法完成了对真实方法的调用
建议107:使用反射增加装饰模式的普适性
- 装饰模式(Decorator Pattern)的定义是“动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比于生成子类更为灵活”。比较通用的装饰模式,只需要定义被装饰的类及装饰类即可,装饰行为由动态代理实现,实现了对装饰类和被装饰类的完全解耦,提供了系统的扩展性
建议108:反射让模板方法模式更强大
- 决定使用模板方法模式时,请尝试使用反射方式实现,它会让你的程序更灵活、更强大)(模板方法模式(Template Method Pattern)的定义是:定义一个操作中的算法骨架,将一些步骤延迟到子类中,使子类不改变一个算法的结构即可重定义该算法的某些特定步骤。简单说,就是父类定义抽象模板作为骨架,其中包括基本方法(是由子类实现的方法,并且在模板方法被调用)和模板方法(实现对基本方法的调度,完成固定的逻辑),它使用了简单的继承和覆写机制。使用反射后,不需要定义任何抽象方法,只需定义一个基本方法鉴别器即可加载复合规则的基本方法
建议109:不需要太多关注反射效率
- 反射效率低是个真命题,但因为这一点而不使用它就是个假命题)(反射效率相对于正常的代码执行确实低很多(经测试,相差15倍左右),但是它是一个非常有效的运行期工具类
深入认识JVM
JVM内存分配,类加载
Java进阶(1)——JVM的内存分配 & 反射Class类的类对象 & 创建对象的几种方式 & 类加载(何时进入内存JVM)& 注解 & 反射+注解的案例
创建对象的4种方法总结
Java进阶(4)——结合类加载JVM的过程理解创建对象的几种方式:new,反射Class,克隆clone(拷贝),序列化反序列化
垃圾回收GC
Java进阶(垃圾回收GC)——理论篇:JVM内存模型 & 垃圾回收定位清除算法 & JVM中的垃圾回收器
简介:本篇博客介绍JVM的内存模型,对比了1.7和1.8的内存模型的变化;介绍了垃圾回收的语言发展;阐述了定位垃圾的方法,引用计数法和可达性分析发以及垃圾清除算法;然后介绍了Java中的垃圾回收器,由串行、到并行再到并发,最后到G1的演变;最后给出了垃圾回收器的对比和使用指引。
JVM调优,Arthas使用
- Java进阶(JVM调优)——阿里云的Arthas的使用 & 安装和使用 & 死锁查找案例,重新加载案例,慢调用分析
- Java进阶(JVM调优)——JVM调优参数 & JDK自带工具使用 & 内存溢出和死锁问题案例 & GC垃圾回收
认识多线程
创建多线程方法+了解线程池
Java进阶(5)——创建多线程的方法extends Thread和implements Runnable的对比 & 线程池及常用的线程池
多线程下-1非原子性问题即解决
Java进阶(6)——抢购问题中的数据不安全(非原子性问题)& Java中的synchronize和ReentrantLock锁使用 & 死锁及其产生的条件
再论线程,创建、生命周期
Java进阶(再论线程)——线程的4种创建方式 & 线程的生命周期 & 线程的3大特性 & 集合中的线程安全问题
主要内容:
1.线程创建的方式,继承Thread类,实现Runable接口,实现Callable接口,采用线程池;
2.线程生命周期: join():运行结束再下一个, yield():暂时让出cpu的使用权,deamon():守护线程,最后结束,sleep():如果有锁,不会让出;
3.线程3大特性,原子性,可见性,有序性;
4.list集合中线程安全问题,hash算法问题;
总结
程序人生——Java泛型和反射的使用建议
相关文章:

程序人生——Java泛型和反射的使用建议
目录 引出泛型和反射建议93:Java的泛型是类型擦除的建议94:不能初始化泛型参数和数组建议95:强制声明泛型的实际类型 建议96:不同的场景使用不同的泛型通配符建议97:警惕泛型是不能协变和逆变的 建议98:建议…...

JavaSE-----认识异常【详解】
目录 一.异常的概念与体系结构: 1.1异常的概念: 1.2一些常见的异常: 1.3异常的体系结构: 1.4异常的分类: 二.异常的处理机制: 2.1 抛出异常: 2.2异常的捕获: 2.3try-catch-&…...

【机器学习300问】34、决策树对于数值型特征如果确定阈值?
还是用之前的猫狗二分类任务举例(这个例子出现在【机器学习300问】第33问中),我们新增一个数值型特征(体重),下表是数据集的详情。如果想了解更多决策树的知识可以看看我之前的两篇文章: 【机器…...

计算机二级(Python)真题讲解每日一题:《绘制雪花》
在横线处填写代码,完成如下功能…...

Rust 的 Arc<Mutex<T>> 的用法示例源代码
在 Rust 中,Arc<Mutex<T>> 是一种组合类型,它结合了 Arc(原子引用计数)和 Mutex(互斥锁)。Arc 用于在多个所有者之间共享数据,而 Mutex 用于确保在任意时刻只有一个线程可以访问被保…...

【NR 定位】3GPP NR Positioning 5G定位标准解读(十六)-UL-AoA 定位
前言 3GPP NR Positioning 5G定位标准:3GPP TS 38.305 V18 3GPP 标准网址:Directory Listing /ftp/ 【NR 定位】3GPP NR Positioning 5G定位标准解读(一)-CSDN博客 【NR 定位】3GPP NR Positioning 5G定位标准解读(…...

如何理解闭包
闭包是编程语言中一个重要的概念,特别是在函数式编程中常常会遇到。以下是对闭包的理解: 1. 定义: 闭包是一种函数,它引用了在其定义范围之外的自由变量(非全局变量),并且这些引用的变量在函数被调用时仍然保持活跃状态。2. 构成: 闭包通常由两部分组成:内部函数(函…...

python知识点总结(一)
这里写目录标题 一、什么是WSGI,uwsgi,uWSGI1、WSGI2、uWSGI3、uwsgi 二、python中为什么没有函数重载?三、Python中如何跨模块共享全局变量?四、内存泄露是什么?如何避免?五、谈谈lambda函数作用?六、写一个函数实现字符串反转,尽可能写出你知道的所…...

【Poi-tl Documentation】区块对标签显示隐藏改造
前置说明: <dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.12.1</version> </dependency>模板: 删除行表格测试.docx 改造前测试效果 package run.siyuan…...

第十四届蓝桥杯 三国游戏
一开始的思路就是想着暴力,但是呢,如果真的用暴力一个一个列的话,连30%的数据都搞定不了,所以这里需要考虑别的办法。 这道题的思路就是贪心。 我们这样想:既然要满足至少一个国X>YZ,那么我们何不变成…...

数据结构——通讯录项目
1.通讯录的介绍 顺序表是通讯录的底层结构。 通讯录是将顺序表的类型替换成结构体类型来储存用户数据,通过运用顺序表结构来实现的。 用户数据结构: typedef struct PersonInfo {char name[12];char sex[10];int age;char tel[11];char addr[100]; }…...

学点Java打小工_Day4_数组_冒泡排序
1 数组基本概念 程序算法数据结构 算法:解决程序的流程步骤 数据结构:将数据按照某种特定的结构来存储 设计良好的数据结构会导致良好的算法。 ArrayList、LinkedList 数组是最简单的数据结构。 数组:存放同一种类型数据的集合,在…...

内存分配方式?
内存分配方式主要有三种: 静态存储区分配:这种方式在程序编译的时候就已经分配好内存,并且这块内存在程序的整个运行期间都存在。全局变量和静态变量通常就是在静态存储区分配的。这种分配方式效率高,因为内存在程序开始执行前就已…...

2024/3/17周报
文章目录 摘要Abstract文献阅读题目引言模型架构编码器和解码器堆栈AttentionPosition-wise Feed-Forward NetworksEmbeddings and SoftmaxPositional Encoding 实验数据实验结果 深度学习TransformerEncoderDecoder 总结 摘要 本周阅读了Transformer的开山之作《Attention Is…...

函数连续性和Lipschitz连续性
摘要: 直观上,Lipschitz连续性的含义是函数图像的变化速度有一个全局的上限,即函数的增长速率不会无限增加。这种性质确保了函数在任何地方都不会过于陡峭,有助于分析函数的行为,并且在优化、动力系统理论、机器学习等…...

Qt 鼠标滚轮示例
1.声明 void wheelEvent(QWheelEvent *event) override;2.实现(方便复制、测试起见用静态变量) #include <mutex> void MainWindow::wheelEvent(QWheelEvent *event) {static QLabel *label new QLabel("Zoom Level: 100%", this);st…...

【Unity】进度条和血条的三种做法
前言 在使用Unity开发的时候,进度条和血条是必不可少的,本篇文章将简单介绍一下几种血条的制作方法。 1.使用Slider Slider组件由两部分组成:滑动区域和滑块。滑动区域用于显示滑动条的背景,而滑块则表示当前的数值位置。用户可…...

多人聊天室 (epoll - Linux网络编程)
文章目录 零、效果展示一、服务器代码二、客户端代码三、知识点1.connect()2.socket()3.bind()4.send()5.recv() 四、改进方向五、跟练视频 零、效果展示 一个服务器作为中转站,多个客户端之间可以相互通信。至少需要启动两个客户端。 三个客户端互相通信 一、服务…...

vite配置
"vite": "^5.1.4" resolve.alias:配置别名 1、执行npm install -D types/node 或者 yarn add types/node -D 2、以下配置代表访问src时可以用“”代替 resolve: {alias: {"": path.resolve(__dirname, "./src"),},}, 使…...

服务器生产环境问题解决思路
游戏服务器开发节奏比较快,版本迭代很频繁,有一些项目甚至出现了周更新(每周准时停服更新维护)。由于功能开发时间短,研发人员本身技术能力等原因,线上出现bug很常见。笔者经历过的游戏项目,一年到头没几次更新不出现bug的(当然,配置问题也算bug)。那当出现bug,我们…...

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Column)
沿垂直方向布局的容器。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 子组件 可以包含子组件。 接口 Column(value?: {space?: string | number}) 从API version 9开始,该接口…...

LLM之RAG实战(三十)| 探索RAG语义分块策略
在LLM之RAG实战(二十九)| 探索RAG PDF解析解析文档后,我们可以获得结构化或半结构化的数据。现在的主要任务是将它们分解成更小的块来提取详细的特征,然后嵌入这些特征来表示它们的语义,其在RAG中的位置如图1所示&…...

软件测试-------Web(性能测试 / 界面测试 / 兼容性测试 / 安全性测试)
Web(性能测试 / 界面测试 / 兼容性测试 / 安全性测试) 一、Web性能测试:(压力测试、负载测试、连接速度测试)1、压力测试: 并发测试 (如500人同时登录邮箱) 2、负载测试…...

工欲善其事,必先利其器,Markdown和Mermaid的梦幻联动(2)
该文章Github地址:https://github.com/AntonyCheng/typora-notes/tree/master/chapter03-mermaid 在此介绍一下作者开源的SpringBoot项目初始化模板(Github仓库地址:https://github.com/AntonyCheng/spring-boot-init-template & CSDN文…...

STM32基础--使用寄存器点亮流水灯
GPIO 简介 GPIO 是通用输入输出端口的简称,简单来说就是 STM32 可控制的引脚,STM32 芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。STM32 芯片的 GPIO被分成很多组,每组有 16 个引脚…...

代码随想录训练营Day25:● 216.组合总和III ● 17.电话号码的字母组合
216.组合总和III 题目链接 https://leetcode.cn/problems/combination-sum-iii/description/ 题目描述 思路 自己写的效率会慢一些,而且没有用到剪枝 class Solution {List<List<Integer>> list new ArrayList<>();List<Integer> lis…...

SwiftUI的 特性 - ViewModify
SwiftUI的 特性 - ViewModify 记录一下SwiftUI的 特性 - ViewModify的使用方式 可以通过viewModify来管理视图的样式,结合extension来完成封装达到解偶效果 import SwiftUI/// 我们可以通过viewModify来管理视图的样式,来达到解偶效果 struct DefaultB…...

中间件 | RPC - [Dubbo]
INDEX 1 Dubbo 与 web 容器的关系2 注册发现流程3 服务配置3.1 注册方式 & 订阅方式3.2 服务导出3.3 配置参数 4 底层技术4.1 Dubbo 的 spi 机制4.2 Dubbo 的线程池4.3 Dubbo 的负载均衡策略4.3 Dubbo 的协议 1 Dubbo 与 web 容器的关系 dubbo 本质上是一个 RPC 框架&…...

【中等】保研/考研408机试-二叉树相关
目录 一、基本二叉树 1.1结构 1.2前序遍历(注意三种遍历中Visit所在的位置) 1.2中序遍历 1.3后序遍历 二、真题实战 2.1KY11 二叉树遍历(清华大学复试上机题)【较难】 2.2KY212 二叉树遍历二叉树遍历(华中科技大…...

自动驾驶---Motion Planning之构建SLT Driving Corridor
1 背景 在上篇博客《自动驾驶---Motion Planning之Speed Boundary》中,主要介绍了Apollo中Speed Boundary的一些内容,可以构造ST图得到边界信息,最后结合粗糙的速度曲线和路径曲线,即可使用优化的方法求解得到最终的轨迹信息(s,s,s,l,l,l)。 本篇博客笔者主要介绍近…...