0

0

Java 怎么更好地管理你的对象

little bottle

little bottle

发布时间:2019-04-30 15:11:54

|

2121人浏览过

|

来源于开源中国

转载

本篇文章以讲故事的形式讲述管理对象的方法,具有一定价值,感兴趣的朋友可以看看一下,希望能够让你对类的认识更加深刻。

有一天晚上我脑海中突然冒出来一个问题:“怎样管理我们代码中的对象”。

小弈是刚工作时的我,他说:通过 new 来创建一个对象然后直接使用就好了啊。

public class HelloWorld {
public void hello() {
System.out.println("hello world!");
}
}
HelloWorld helloWorld = new HelloWorld();
helloWorld.hello();

你们看,我有一个 HelloWorld 类,我用 new 就能直接创建一个对象,然后就能使用这个对象中所有的方法了,多简单啊。

二弈是工作两年的我,他一脸鄙视的对小弈说,你别整天 HelloWorld 好不好,还有啊,除了 new 你就不会其他的了,能不能有点追求啊?

立即学习Java免费学习笔记(深入)”;

小弈对二弈说那你说除了 new 还有什么办法啊?

二弈说可以通过 Class 的 newInstance 或者 Constructor 的 newInstance 来创建对象实例啊。

不过你得记住,Class 的 newInstance 只能对那些拥有可见的(Accessible)无参构造函数的类,才能进行对象的实例化,而 Constructor 就没有这些限制。

大弈是工作三年的我,他说,虽然你们的方法都可以用来创建对象,但都还是手动创建的,太原始了,生产力太低。

工欲善其事,必先利其器,我们也得找个高效的生产力工具。IOC 容器你们了解吧?

以前我们在一个对象中如果要调用另外一个对象的方法时,都是通过 new 或者反射来手动创建该对象,但是每次都这样做太累了,并且类之间的耦合也很高。

通过 IOC 容器,我们可以把所有的对象交给容器来管理,在使用之前只需要定义一下对象,然后再使用到该对象时,IOC 容器就会帮我们把该对象初始化好,这样是不是更方便呢?

大弈说完,举了一个例子:

@Bean
public class RegisterService {
public void register() {
// do register
}
}
@Bean
public class LoginService {
public void login() {
// do login
}
}
@Bean
public class HelloWorld {
@Autowired
private RegisterService registerService;
@Autowired
private LoginService loginService;
public void hello() {
// 注册
registerService.register();
// ...
// 登录
loginService.login();
}
}

IOC 容器通过一种叫 Bean 的注解,在系统启动时扫描所有通过 Bean 标注的类,对这些类进行实例化,然后将所有的对象都保存在容器中。再扫描所有通过 Autowired 标注的属性或者方法,从容器中找到与之匹配(通过名称或者类型等)的对象将具体的对象赋值给这些属性。这样我们就可以直接将这些对象拿来使用了,作为一个伸手党是不是很幸福啊。

老弈是工作五年的我,他听了大弈的话后,提出了一个问题,对于新的项目可以使用这种 IOC 的容器,可是对于那些遗留的老项目来说,要使用 IOC 来改造是不太符合实情的。

我举个例子,在一个遗留的老项目中,有一个核心的接口 Handler:

public interface Handler<REQ, RES> {
    RES handle(REQ request);
}

Handler 接口有很多的实现类,我们需要对不同的请求来调用不同的 Handler 实现类进行处理,如果用 IOC 容器来管理这些实现类,显然不太合适,因为我们处理之前是不知道该用哪个 Handler 实现类的。

大弈想了想,如果 Handler 接口只有几个固定的实现类,并且在使用时只会使用一个来进行处理,那么倒是可以在启动前通过配置的方式来确定具体使用哪种 Handler ,比如可以通过 @Conditional 根据某些条件来确定加载具体的对象,但是这种要在使用时才能确定 Handler 对象的类型确实比较棘手。

老弈看大家都不说话了,就继续说了下去。

为了要在调用方法时使用不同的 Handler 来处理不同的而请求,需要确定两种类,一种是请求类,一种是处理类,并且要让请求类和处理类一一对应起来。

假设我们的请求类是一个 Packet 类,每一个具体的请求类都继承自这个基类。

那么想要确定每一个具体的 Packet 是什么类型的,可以有很多种方法,可以为每个 Packet 取一个唯一的名字,例如:

public abstract class Packet {
    public abstract String name();
}

也可以为每一个 Packet 指定一个标志,例如:

public abstract class Packet {
    public abstract int symbol();
}

但是不管哪种方式,每一个 Packet 的实现类都需要实现抽象类中的方法,来“标志”自己是哪种 Packet。

百宝箱
百宝箱

百宝箱是支付宝推出的一站式AI原生应用开发平台,无需任何代码基础,只需三步即可完成AI应用的创建与发布。

下载

我们以第二种方式举例,假设我们有两个具体的 Packet:

public class RegisterPacket extends Packet {
// 注册所需要的其他参数
int symbol() {
return 1;
}
}
public class LoginPacket extends Packet {
// 登录所需要的其他参数
int symbol() {
return 2;
}
}

这样当我们接收到 request 对象时,通过调用 request.symbol() 就知道这个 request 是哪种类型的 Packet 了,这时只要找到具体的 Handler 实现类来处理就可以了。

那请求类已经可以确定了,怎样确定 Handler 处理类呢?我们是否也可以在 Handler 接口中定义一个 symbol 方法呢,像这样:

public interface Handler<REQ, RES> {
    int symbol();
    RES handle(REQ request);
}

这样的话,只要在所有的实现类中实现 symbol 方法来标注该 Handler 是用来处理何种 request 的即可。

public RegisterHandler implements Handler<RegisterPacket, RES> {
    int symbol(){
    return 1;
    }
    RES handle(RegisterPacket request){
    // 具体的处理方法
    }
}
public LoginHandler implements Handler<LoginPacket, RES> {
    int symbol(){
    return 2;
    }
    RES handle(LoginPacket request){
    // 具体的处理方法
    }
}

最后把所有的 Handler 实现类都实例化后保存在一个 HandlerProvider 中,要使用时再到 HandlerProvider 中来获取即可:

public interface HandlerProvider {
    Handler getHandler(int symbol);
}

那怎样获取到所有的 Handler 的实现类呢,有两种方法。

一种是通过 ServiceLoader.load(Handler.class) 的方式来获取,不过这种通过 spi 的方式需要在项目的 resources/META-INF/services/ 目录下创建一个 xxx.Handler 的文件,并在文件中将所有 Handler 的实现类的完全类限定符列出来。

另一种比较简单的方式是通过扫描的方式,获取到所有 Handler 的实现类。

到现在为止,我们的实现还算可以,但是有一个问题,那就是在 Handler 接口中我们增加了一个方法,这样做就对原来的代码进行了侵入。

为了让原来的代码保持不变,我们可以定义一个注解来标注在所有的 Handler 实现类上,比如这样:

@Symbol(1)
public RegisterHandler implements Handler<RegisterPacket, RES> {
    RES handle(RegisterPacket request){
    // 具体的处理方法
    }
}
@Symbol(2)
public LoginHandler implements Handler<LoginPacket, RES> {
    RES handle(LoginPacket request){
    // 具体的处理方法
    }
}

这样就将 Handler 的实现和标注进行了解耦了,也可以通过扫描 @Symbol 注解来获取到所有的 Handler 实现类,不过这样做的缺点就是假如我忘记对某个 Handler 实现类添加 @Symbol 注解,到时候就获取不到该 Handler 了。

大家听完老弈的话之后,都陷入了沉思,我靠,还可以这么玩,真有趣。

这时候现在的我,也就是逅弈,说了一句,如果我有一个接口,他只有几个固定的实现类,我不想搞那一套那么重的实现方式,但是我也需要动态的获取实现类来对请求进行处理,那我该怎么办呢?

比如我有一个序列化的接口,如下所示:

public interface Serializer {
    byte[] serialize(Packet packet);
}

然后只有五种具体的序列化的实现类,如下所示:

public class JdkSerializer implements Serializer {
@Override
    public byte[] serialize(Packet packet) {
    // 具体的序列化操作
    }
}
public class FastJsonSerializer implements Serializer {
@Override
    public byte[] serialize(Packet packet) {
    // 具体的序列化操作
    }
}
public class HessianSerializer implements Serializer {
@Override
    public byte[] serialize(Packet packet) {
    // 具体的序列化操作
    }
}
public class KryoSerializer implements Serializer {
@Override
    public byte[] serialize(Packet packet) {
    // 具体的序列化操作
    }
}
public class ProtoStuffSerializer implements Serializer {
@Override
    public byte[] serialize(Packet packet) {
    // 具体的序列化操作
    }
}

那么我们该怎么确定使用哪种序列化方式对参数 packet 进行序列化呢?

使用老弈刚刚说的那一套也确实能够实现,不过太麻烦了,又得对 Packet 定义 symbol,又得对 Hander 实现类进行标注,还得扫描所有的实现类。

我只有五个实现类,不需要搞那么麻烦的。

其实很简单,只需要定义一个枚举类,表示序列化的算法,然后对 Packet 增加一个 algorithm 方法用来表示,使用何种序列化算法,如下所示:

public enum SerializeAlgorithm {
    JDK((byte) 1),
    FAST_JSON((byte) 2),
    HESSIAN((byte) 3),
    KRYO((byte) 4),
    PROTO_STUFF((byte) 5);
    private byte type;
    SerializeAlgorithm(byte type) {
        this.type = type;
    }
}
public abstract class Packet implements Serializable {
public abstract byte algorithm();
}

然后定义一个 SerializerChooser 根据不同的算法选择不同的 Serializer 实现类即可:

public interface SerializerChooser {
    Serializer choose(byte algorithm);
}

因为根据算法是可以知道对应的序列化接口的,所以就没有必要去扫描了,直接把几种序列化的实现类枚举出来即可,对象的实例可以使用单例模式,如下所示:

public class DefaultSerializerChooser implements SerializerChooser {
    private DefaultSerializerChooser() {
    }
    public static SerializerChooser getInstance() {
        return Singleton.get(DefaultSerializerChooser.class);
    }
    @Override
    public Serializer choose(byte algorithm) {
        SerializeAlgorithm serializeAlgorithm = SerializeAlgorithm.getEnum(algorithm);
        switch (serializeAlgorithm) {
            case JDK: {
                return Singleton.get(JdkSerializer.class);
            }
            case FAST_JSON: {
                return Singleton.get(FastJsonSerializer.class);
            }
            case HESSIAN: {
                return Singleton.get(HessianSerializer.class);
            }
            case KRYO: {
                return Singleton.get(KryoSerializer.class);
            }
            case PROTO_STUFF: {
                return Singleton.get(ProtoStuffSerializer.class);
            }
            default: {
                return null;
            }
        }
    }
}

我说完后,大家又一次陷入了沉思,我知道大家都在思考,他们会在每一次思考中获得进步和成长,正如我在思考后得到成长一样。

小鸟总有一天会成长为老鸟,我还走在成长的路上。

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

作者最新文章

jq是指什么?

2019-05-31 16:04

vue是做什么的

2019-05-31 16:58

mysql能干什么

2019-05-31 17:15

怎么用cmd进入mysql

2019-05-31 17:24

mysql怎么输入

2019-05-31 17:41

mysql免费版好用么

2019-05-31 17:53

mysql慢查询是什么

2019-05-31 18:00

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

38

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

83

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

97

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

223

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

458

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

169

2026.03.04

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

246

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

34

2026.03.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.3万人学习

C# 教程
C# 教程

共94课时 | 11.2万人学习

Java 教程
Java 教程

共578课时 | 81.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号