初识Java 17-2 反射

目录

转型前检查

构建例子:生成层次结构

优化Creator:使用类字面量

优化PetCounter:动态验证类型

更通用的递归计数

注册工厂


本笔记参考自: 《On Java 中文版》


转型前检查

        当我们使用传统的类型转换,例如:

Object x = new Shape();
Shape s = (Shape)x; // 传统的类型转换

此时Java就会执行反射,这种转型通常被称为“类型安全向下转型”。不同于向上转型,编译器不会知道这个Object实际的类型是什么。因此在向下转型时,我们需要声明对象的确切类型

    C++在这种强制类型转换时不会进行反射,而是进行“运行时类型识别”。告诉编译器,这是一个新的类型。

        而除了这种类型转换和Class对象外,Java还提供了其他反射的方式:instanceof关键字。这一关键字可以用于判断对象是否是特定类型的实例,例如:

【例子:instanceof关键字的使用】

public class ClassInstanceof {public static void main(String[] args) {Object obj = new Circle();if (obj instanceof Shape)System.out.println("这个实例是一个Shape");if (obj instanceof Circle)System.out.println("这个实例是一个Circle");if (obj instanceof Square)System.out.println("这个实例是一个Square");elseSystem.out.println("这个实例的类型不是Square");}
}

        程序执行的结果是:

        instanceof的存在使得程序员可以使用Java轻松识别出对象的正确类型。

---

构建例子:生成层次结构

        为了方便后续的示例,这里先创建一个比较复杂的层次结构(人和宠物):

        在这一章的笔记中,只会使用到Pet类及其子类。但为了结构的完整性,这里展示的是整个继承结构。Individual作为所有类的基类,会被用到的部分只有其重写的toString()

@Override
public String toString() {return getClass().getSimpleName() +(name == null ? "" : " " + name);
}

        Pet及其子类的实现都较为简单,这里只选取其中一条分支进行展示(其他均与该分支相同):

        这些类都有一个无参构造器,这样我们就可以调用newInstacnce()来生成实例。下面的例子是一个用于随机生成Pet对象的工具类:

【例子:随机生成Pet对象】

package reflection.pets;import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;public abstract class Creatorimplements Supplier<Pet> {private Random random = new Random(47);// types()方法用于创建不同的Pet对象,// 我们要求这个方法必须在子类中进行实现// (一般,这个方法只需要返回一个静态的List引用即可)public abstract List<Class<? extends Pet>> types();// 创建一个随机的Pet对象:@Overridepublic Pet get() {int n = random.nextInt(types().size());try {return types().get(n).getConstructor().newInstance();// 调用newInstance(),会得到四个异常:} catch (InstantiationException |NoSuchMethodException |InvocationTargetException |IllegalAccessException e) {throw new RuntimeException(e);}}public Stream<Pet> stream() {return Stream.generate(this);}public Pet[] array(int size) { // 调用上面的stream()方法return stream().limit(size).toArray(Pet[]::new);}public List<Pet> list(int size) {return stream().limit(size) // 将元素收集到一个ArrayList中.collect(Collectors.toCollection(ArrayList::new));}
}

        Creator是一个生成器的基类,它的types()是一个抽象方法,这样我们就可以要求Creator的基类去实现这个方法。这是一个模板方法模式的例子。值得一提的是,types()的返回类型被规定成一个包含Class对象的List,这个List可以接受Pet及其的任意子类

        接下来尝试为Creator实现一个子类:

【例子:使用forName()的生成器】

package reflection.pets;import java.util.ArrayList;
import java.util.List;public class ForNamePetCreator extends Creator {// 应该确保:在类被初始化时,就向manyTypes列表中加载数据// (也就是说,在类被加载时就应该调用loader()方法)private static List<Class<? extends Pet>> manyTypes =new ArrayList<>();// 设置我们需要生成的类型:private static String[] typeNames = {// Dog的子类:"reflection.pets.Mutt","reflection.pets.Pug",// Cat的子类:"reflection.pets.Chero","reflection.pets.Manx","reflection.pets.Cymric",// Rodent的子类:"reflection.pets.Rat","reflection.pets.Mouse","reflection.pets.Hamster"};@SuppressWarnings("unchecked")private static void loader() {try {for (String name : typeNames)manyTypes.add((Class<? extends Pet>) Class.forName(name));} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}static {loader();}// types()方法只需要用于返回静态的List引用即可@Overridepublic List<Class<? extends Pet>> types() {return manyTypes;}
}

        通过Class.forName(),我们就设置好的类的完全限定名全部录入到manyTypes列表中。因为我们放入forName()中的是在编译时无法被验证的字符串,因此它可以会报错。

        需要提一下这里的静态初始化块:

static {loader();
}

由于@SuppressWarnings()不允许被直接用于静态初始化块,因此我们需要将loader()放入一个静态初始化块中。这样当类第一次被使用时,loader()就会被唯一一次调用,保证manyTypes()被正确初始化。

        instanceof关键字可用于检测对象的类型,下面的例子会通过这一关键字完成对各种Pet类型的数量监控。因为需要反映各个类型与其数量之间的对应关系,所以我们可以使用Map

【例子:监控各个Pet类型的数量】

package reflection;import java.util.HashMap;import reflection.pets.*;// 用于跟踪各种类的Pet数量
public class PetCounter {static class Counter extends HashMap<String, Integer> {public void count(String type) {// get()返回type映射的值,若type不在Map中,返回nullInteger quantity = get(type);if (quantity == null) {// 将键和值进行绑定put(type, 1);} elseput(type, quantity + 1);}}private Counter counter = new Counter();private void countPet(Pet pet) {System.out.print(pet.getClass().getSimpleName() + " ");// 注意:instanceof只能与命名类型比较if (pet instanceof Pet)counter.count("Pet");// 属于Dog的:if (pet instanceof Dog)counter.count("Dog");if (pet instanceof Mutt)counter.count("Mutt");if (pet instanceof Pug)counter.count("Pug");// 属于Cat的:if (pet instanceof Cat)counter.count("Cat");if (pet instanceof Chero)counter.count("Chero");if (pet instanceof Manx)counter.count("Manx");if (pet instanceof Cymric)counter.count("Cymric");// 属于Rodent的:if (pet instanceof Rodent)counter.count("Rodent");if (pet instanceof Rat)counter.count("Rat");if (pet instanceof Mouse)counter.count("Mouse");if (pet instanceof Hamster)counter.count("Hamster");}public void count(Creator creator) {creator.stream().limit(20).forEach(pet -> countPet(pet));System.out.println();System.out.println(counter);}public static void main(String[] args) {new PetCounter().count(new ForNamePetCreator());}
}

        程序执行的结果是:

        这个例子体现了instanceof的局限性:通过这一关键字,一个对象只能与命名类型进行比较,而不能与一个Class对象进行比较。这意味着,我们不能将我们需要比对的类型放入一个Class数组中,再通过循环等方式实现instanceof的自动化。

    不过,若代码中存在很多instanceof,这个代码可能也是存在问题的。


优化Creator:使用类字面量

        可以使用类字面量的方式优化Creator,通过这种方式实现的Creator会更加清晰:

【例子:重新实现Creator

package reflection.pets;import java.util.Arrays;
import java.util.Collections;
import java.util.List;// 使用字类面量重新实现Creator
public class PetCreator extends Creator {// 因为使用的是字面量,所以不需要生成实例public static finalList<Class<? extends Pet>> ALL_TYPES =Collections.unmodifiableList(Arrays.asList(Pet.class, Dog.class, Cat.class, Rodent.class,Mutt.class, Pug.class,Chero.class, Manx.class, Cymric.class,Rat.class, Mouse.class, Hamster.class));// 进行类型的随机生成(限定:只生成子类)private static finalList<Class<? extends Pet>> TYPES =ALL_TYPES.subList(ALL_TYPES.indexOf(Mutt.class),ALL_TYPES.size());@Overridepublic List<Class<? extends Pet>> types() {return TYPES;}public static void main(String[] args) {System.out.println(TYPES);List<Pet> pets = new PetCreator().list(7);System.out.println(pets);}
}

        程序执行的结果是:

        因为使用的是字面量的缘故,不会用到newInstance()进行实例生成。也因此省去了处理异常的功夫。

        ALL_TYPES包含了所有的Pet类型,方便任何继承了这个类的子类进行使用。而types()返回的TYPES则包含了所有确切的宠物类型,因此可用于生成随机的Pet

    Collections.unmodifiableList()会根据传入的原始列表返回一个只读视图,其空间开辟在堆上。Java 9还加入了一个List.of()方法,这个方式生成的视图存在于内存中,因此速度会更快一点。

        因为之前的PetCounter.count()接受的是一个Creator参数,因此我们也可以使用它来测试这个新的PetCreator

【例子:测试PetCreator

package reflection;import reflection.pets.PetCreator;public class PetCounter2 {public static void main(String[] args) {new PetCounter().count(new PetCreator());}
}

        程序执行的结果是:


优化PetCounter:动态验证类型

        很显然,PetCounter中的instanceof过于冗长,而Class.isInstance()提供了一种更好的方式动态验证对象的类型,可以用它替代PetCounter中的instanceof

【例子:使用isInstance()

package reflection;import onjava.Pair;
import reflection.pets.Pet;
import reflection.pets.PetCreator;import java.util.HashMap;
import java.util.stream.Collectors;public class PetCounter3 {static class Counterextends HashMap<Class<? extends Pet>, Integer> {Counter() {super(PetCreator.ALL_TYPES.stream().map(type -> Pair.make(type, 0)) // 将所有的Pet类型的计数初始化为0.collect(Collectors.toMap(Pair::key, Pair::value)));}public void count(Pet pet) {// 使用Cass.isInstance()消除instanceofentrySet().stream() // entrySet():返回由包含在这个Map视图中的元素决定的Set视图.filter(pair -> pair.getKey().isInstance(pet)).forEach(pair ->put(pair.getKey(), pair.getValue() + 1));}@Overridepublic String toString() {String result = entrySet().stream().map(pair -> String.format("%s=%s",pair.getKey().getSimpleName(),pair.getValue())).collect(Collectors.joining(", "));return "{" + result + "}";}}public static void main(String[] args) {Counter petCount = new Counter();new PetCreator().stream().limit(20).peek(petCount::count) // 对每个流元素进行计数.forEach(p -> System.out.print(p.getClass().getSimpleName() + " "));System.out.println("\n" + petCount);}
}

        程序执行的结果是:

        isInstance()的使用使得我们不再需要使用instanceof

        为了能够为所有的Pet类型计数,就需要在创建类的时候预加载所有的Pet类。因此内部类Counter就需要在其创建的时候将所有类型进行加载,这就用到了之前设置的ALL_TYPES。也因此,只需要修改ALL_TYPES就可以增删类型。


更通用的递归计数

        上一个例子需要在使用前加载所有的Pet类型,但我们也可以使用isAssignableFrom()方法来替代这一预加载过程,在方法执行时对每一个将要计数的对象进行类型判断。这样,我们就可以获得一个更加通用的计数方法:

【例子:对某一类型的实例进行计数】

package onjava;import java.util.HashMap;
import java.util.stream.Collectors;public class TypeCounterextends HashMap<Class<?>, Integer> {private Class<?> baseType;public TypeCounter(Class<?> baseType) {this.baseType = baseType;}public void count(Object obj) {Class<?> type = obj.getClass();if (!baseType.isAssignableFrom(type))throw new RuntimeException(obj + "是一个错误的类型:" + type +",应该传入" + baseType + "或其的子类");countClass(type);}// 先对基类进行计数。若存在父类,则递归调用自身,对父类进行计数。private void countClass(Class<?> type) {Integer quantity = get(type);put(type, quantity == null ? 1 : quantity + 1);Class<?> superClass = type.getSuperclass();if (superClass != null &&baseType.isAssignableFrom(superClass)) {countClass(superClass);}}@Overridepublic String toString() {String result = entrySet().stream().map(pair -> String.format("%s=%s",pair.getKey().getSimpleName(),pair.getValue())).collect(Collectors.joining(", "));return "{" + result + "}";}
}

        isAssignableFrom()方法用于验证传递的对象是否存在于要求的层次结构中。

        在上述的类中,countClass()方法就是用于递归计数的,它在完成子类的计数后会进行判断,若存在父类,则对父类进行计数。

        现在,可以使用上面的类了:

【例子:使用TypeCounter

package reflection;import onjava.TypeCounter;
import reflection.pets.Pet;
import reflection.pets.PetCreator;public class PetCounter4 {public static void main(String[] args) {TypeCounter counter = new TypeCounter(Pet.class);new PetCreator().stream().limit(20).peek(counter::count).forEach(pet -> System.out.print(pet.getClass().getSimpleName() + " "));System.out.println("\n" + counter);}
}

        程序执行的结果如下:

注册工厂

        在上述例子实现Creator的时候,还存在一个问题:

    若需要为Pet添加一个新的子类,就需要在Creator的实现PetCreator等)中添加对应的类型。若我们需要较为频繁地添加子类,这就会给我们造成一定的麻烦。

        对于这种情况,一般有两种做法:

  1. 手动创建列表,并将这个列表放在较为核心的代码部分,例如基类之中。
  2. 使用工厂方法设计模式推迟对象的创建,将创建交付给类自己去完成。工厂方法可以被多态地调用,来创建恰当类型的对象。

        java.util.function.SupplierT get()方法就是一个工厂方法的原型。这一方法可以通过协变返回类型(基类方法返回值的子类型)完成对象的返回。

        下面的示例包含一个工厂对象(Supplier<Part>)的静态List

【例子:使用工厂方法进行注册】

package reflection;import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
import java.util.stream.Stream;class Part implements Supplier<Part> {@Overridepublic String toString() {return getClass().getSimpleName();}static List<Supplier<? extends Part>> prototypes =Arrays.asList( // 将类型的工厂类添加到列表中,完成“注册”new FuelFilter(),new AirFilter(),new CabinAirFilter(),new OilFilter(),new FanBelt(),new PowerSteeringBelt(),new GeneratorBelt());private static Random rand = new Random(47);@Overridepublic Part get() {int n = rand.nextInt(prototypes.size());// 第一个get(n)调用自Arrays// 第二个get()才是Supplier的return prototypes.get(n).get();}
}// 分类器,不存在实现
class Filter extends Part {
}class FuelFilter extends Filter {@Overridepublic FuelFilter get() {return new FuelFilter();}
}class AirFilter extends Filter {@Overridepublic AirFilter get() {return new AirFilter();}
}class CabinAirFilter extends Filter {@Overridepublic CabinAirFilter get() {return new CabinAirFilter();}
}class OilFilter extends Filter {@Overridepublic OilFilter get() {return new OilFilter();}
}// 分类器,不存在实现
class Belt extends Part {
}class FanBelt extends Belt {@Overridepublic FanBelt get() {return new FanBelt();}
}class GeneratorBelt extends Belt {@Overridepublic GeneratorBelt get() {return new GeneratorBelt();}
}class PowerSteeringBelt extends Belt {@Overridepublic PowerSteeringBelt get() {return new PowerSteeringBelt();}
}public class RegisteredFactories {public static void main(String[] args) {Stream.generate(new Part()).limit(10).forEach(System.out::println);}
}

        程序执行的结果是:

        先观察列表prototypes

这些子类原本都应该由get()方法进行生成,它们的工厂类都被添加到了列表中,通过这种方式,我们完成了“注册”。这些工厂是对象本身的实例,在实际操作中,它们会作为生成其他对象的模板进行使用。

    这些对象就好比一个一个的工厂,它们会分别生产不同的“零件”。通过调用这些类的方法,我们就可以进行“零件”的生产。

        上述的FilterBelt用于对不同的“零件”进行分类,因此它们不需要具体的实现。

        这种做法的好处可以在main()方法中看见:由于Part类的get()方法可以生成各种各样的零件,我们仅需创建Part对象就可以控制各个“零件”的生产。

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

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

相关文章

支付卡行业(PCI)PIN安全要求和测试程序 7个控制目标、33个要求及规范性附录ABC 密钥注入-PCI认证-安全行业基础篇4

概述 用于在ATM和POS终端进行在线和离线支付卡交易处理期间&#xff0c;对个人身份号码&#xff08;PIN&#xff09;数据进行安全管理、处理和传输。 该标准具体包括 7 个控制目标和 33 个安全要求&#xff0c; 标准的结构分为标准主体部分&#xff0c;标准附录&#xff08;N…

windows查看端口号占用,并杀死进程

1、查看所有运行的端口&#xff1a; netstat -ano 2、查看被占用端口对应的 PID netstat -aon|findstr "8081" 回车执行该命令&#xff0c;最后一串数字就是 PID进程号, 这里是 19232。 3、查看指定 PID 的进程 tasklist|findstr "19232" 回车执行该…

[NLP] 使用Llama.cpp和LangChain在CPU上使用大模型

一 准备工作 下面是构建这个应用程序时将使用的软件工具: 1.Llama-cpp-python 下载llama-cpp, llama-cpp-python [NLP] Llama2模型运行在Mac机器-CSDN博客 2、LangChain LangChain是一个提供了一组广泛的集成和数据连接器&#xff0c;允许我们链接和编排不同的模块。可以常…

【今日文章】:如何用css 实现星空效果

【今日文章】&#xff1a;如何用css 实现星空效果 需求实现tips: 需求 用CSS 实现星空效果的需求&#xff1a; 屏幕上有“星星”&#xff0c;且向上移动。移动的时候&#xff0c;动画效果要连贯&#xff0c;不能出现闪一下的样子。 实现 这里我们需要知道&#xff0c;“星星”是…

阿里云服务器云盘性能IOPS和吞吐量说明SSD、ESSD和高效

阿里云服务器系统盘或数据盘支持多种云盘类型&#xff0c;如高效云盘、ESSD Entry云盘、SSD云盘、ESSD云盘、ESSD PL-X云盘及ESSD AutoPL云盘等&#xff0c;阿里云百科aliyunbaike.com详细介绍不同云盘说明及单盘容量、最大/最小IOPS、最大/最小吞吐量、单路随机写平均时延等性…

电脑发热发烫,具体硬件温度达到多少度才算异常?

环境&#xff1a; 联想E14 问题描述&#xff1a; 电脑发热发烫,具体硬件温度达到多少度才算异常? 解决方案&#xff1a; 电脑硬件的温度正常范围会因设备类型和使用的具体硬件而有所不同。一般来说&#xff0c;以下是各种硬件的正常温度范围&#xff1a; CPU&#xff1a;正…

软件设计模式的意义

软件设计模式的意义 所有开发人员都应该接触过软件设计模式这个概念&#xff0c;看过《设计模式-可复用的对象软件的基础》这本书&#xff0c;在面试中都被问过&#xff1a; 你用过哪些设计模式这种问题。但很大可能也就仅此而已了。 为什么好像只能从框架中找到设计模式的应用…

kubernetes(5) 续4

目录 volumes emptyDir卷 hostpath卷 nfs卷 持久卷 storageclass statefulset控制器 mysql主从部署 volumes emptyDir卷 [rootk8s2 volumes]# vim emptydir.yaml apiVersion: v1 kind: Pod metadata:name: vol1 spec:containers:- image: busyboxplusname: vm1command: ["…

3D视觉引导纸箱拆码垛,助力物流行业转型升级

近年来&#xff0c;自动化和智能化技术在各行业的应用越来越广泛&#xff0c;特别是在物流和仓储领域。纸箱拆码垛是物流仓储中的一个重要环节。 人工分拣效率低、错误率高、成本高&#xff0c;传统的拆码垛设备存在兼容性差&#xff0c;对纸箱的识别率不高、操作不灵活等问题…

jsp多层弹框,让弹框出现在最外层,最顶层

如下图所示&#xff0c;给该弹框添加样式&#xff0c;z-index:5 这个值要比已经弹出的弹框的值大&#xff0c;

Next.js 项目——从入门到入门(Eslint+Prettier)

Next.js官方文档地址 什么是 Next.js 这是一个用于生产环境的 React 框架。 Next.js 为您提供生产环境所需的所有功能以及最佳的开发体验&#xff1a;包括静态及服务器端融合渲染、 支持 TypeScript、智能化打包、 路由预取等功能 无需任何配置。 功能&#xff1a; 功能说…

Ubuntu 18.04.6 LTS安装docker和docker-compose、镜像导入导出

Ubuntu 18.04.6 LTS安装docker和docker-compose、镜像导入导出 文章目录 Ubuntu 18.04.6 LTS安装docker和docker-compose、镜像导入导出安装docker更新数据源&#xff0c;安装必要插件添加GPG密钥设置存储库再更新apt包索引安装docker-ce查看docker状态运行docker hello-world …

Gin学习笔记

Gin学习笔记 Gin文档&#xff1a;https://pkg.go.dev/github.com/gin-gonic/gin 1、快速入门 1.1、安装Gin go get -u github.com/gin-gonic/gin1.2、main.go package mainimport ("github.com/gin-gonic/gin""net/http" )func main() {// 创建路由引…

LeetCode 热题100——链表专题(二)

一、环形链表 141.环形链表&#xff08;题目链接&#xff09; 思路&#xff1a;使用快慢指针&#xff0c;慢指针走一步&#xff0c;快指针走俩步&#xff0c;如果是环形链表&#xff0c;那么快慢指针一定相遇&#xff0c;如果不是环形结构那么快指针或者快指针的next一定先为N…

概率论和数理统计(一)概率的基本概念

前言 生活中对于事件的发生,可以概括为 确定现象&#xff1a;在一定条件下必然发生&#xff0c;如日出随机现象&#xff1a;在个别试验中其结果呈现出不确定性&#xff0c;在大量重复试验中其结果又具有统计规律的现象&#xff0c;称之为随机现象。 随机现象的特点&#xff…

TCP/IP的基础知识

文章目录 TCP/IP的基础知识硬件&#xff08;物理层&#xff09;网络接口层&#xff08;数据链路层&#xff09;互联网层&#xff08;网络层&#xff09;TCP/IP的具体含义传输层应用层&#xff08;会话层以上的分层&#xff09;TCP/IP分层模型与通信示例发送数据包的一个例子接收…

【公益案例展】火山引擎公益电子票据服务——连接善意,共创美好

‍ 火山引擎公益案例 本项目案例由火山引擎投递并参与数据猿与上海大数据联盟联合推出的 #榜样的力量# 《2023中国数据智能产业最具社会责任感企业》榜单/奖项”评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 捐赠票据是慈善组织接受捐赠后给捐赠方开具的重要凭证&…

Mybatis与Mybatis-Plus(注解与Xml)(单表与多表)

准备工作 这里我们准备了两个与数据库表对应的实体类&#xff0c;stu为学生表&#xff0c;cls为班级表 类属性上的注解如 TableId等 为Mybatis-Plus的注解&#xff0c;使用mybatis会无视掉这些注解 在Stu 类的最后一个属性我们定义了Cls实体类的对象&#xff0c;对于单表查询&…

Dcoker学习笔记(一)

Dcoker学习笔记一 一、 初识Docker1.1 简介1.2 虚拟机和docker的区别1.3 Docker架构1.4 安装Docker&#xff08;Linux&#xff09; 二、 Dcoker基本操作2.1 镜像操作2.2 容器操作练习 2.3 数据卷volume&#xff08;容器数据管理&#xff09;简介数据卷语法数据卷挂载 2.4 自定义…

高性能三防工业平板电脑 防摔防爆电容屏工控平板

HT1000是一款高性能工业三防平板&#xff0c;10.1英寸超清大屏&#xff0c;厚度仅14.9mm&#xff0c;超薄机身&#xff0c;可轻松插入袋中&#xff0c;方便携带&#xff0c;搭载8核2.0GHz高性能CPU&#xff0c;行业领先的Android 11.0&#xff0c;设备性能大幅提升&#xff0c;…