码出高效:Java开发手册-第2章(5)

简介: 本章开始讲解面向对象思想,并以Java 为载体讲述面向对象思想在具体编程语言中的运用与实践。当前主流的编程语言有50 种左右,主要分为两大阵营:面向对象编程与面向过程编程。面向对象编程(Object-Oriented Programming,OOP)是划时代的编程思想变革,推动了高级语言的快速发展和工业化进程。OOP 的抽象、封装、继承、多态的理念使软件大规模化成为可能,有效地降低了软件开发成本、维护成本和复用成本。面向对象编程思想完全不同于传统的面向过程编程思想,使大型软件的开发就像搭积木一样隔离可控、高效简单,是当今编程领域的一股势不可......

2.3.5 this 与 super

      对象实例化时,至少有一条从本类出发抵达Object 的通路,而打通这条路的两个主要工兵就是this 和super,逢山开路,遇水搭桥。但是this 和super 往往是默默无闻的,在很多情况下可以省略,比如:

  • 本类方法调用本类属性。
  • 本类方法调用另一个本类方法。
  • 子类构造方法隐含调用 super()。

      任何类在创建之初,都有一个默认的空构造方法,它是super() 的一条默认通路。构造方法的参数列表决定了调用通路的选择;如果子类指定调用父类的某个构造方法,super 就会不断往上溯源;如果没有指定,则默认调用super()。如果父类没有提供默认的构造方法,子类在继承时就会编译错误,如图2-4 所示。

4.jpg

图2-4 父类默认构造方法缺失

      如果父类坚持不提供默认的无参构造方法,必须在本类的无参构造方法中使用super 方式调用父类的有参构造方法,如public Son(){ super(123); }。

      一个实例变量可以通过this. 赋值另一个实例变量;一个实例方法可以通过this.调用另一个实例方法;甚至一个构造方法都可以通过this.调用另一个构造方法。如果this 和super 指代构造方法,则必须位于方法体的第一行。换句话说,在一个构造方法中,this和super 只能出现一个,且只能出现一次,否则在实例化对象时,会因子类调用到多个父类构造方法而造成混乱。

      由于this 和super 都在实例化阶段调用,所以不能在静态方法和静态代码块中使用this 和super 关键字。this 还可以指代当前对象, 比如在同步代码块synchronized(this){...} 中,super 并不具备此能力。但super 也有自己的特异功能,在子类覆写父类方法时,可以使用super 调用父类同名的实例方法。最后总结一下this和super 的异同点,如图2-5 所示。

5.jpg

图2-5 this和super的异同点

2.3.6 类关系

      关系是指事物之间存在单向或相互的作用力或者影响力的状态。类与类之间的关系可分成两种:有关系与没关系,这似乎是一句非常正确的废话,难点在于确定类与类之间是否存在相互作用。证明类之间没关系是一个涉及业务、架构、模块边界的问题,往往由于业务模型的抽象角度不同而不同,是一件非常棘手的事情。如果找到了没有关系的点,就可以如庖丁解牛一样,进行架构隔离、模块解耦等工作。有关系的情况下,包括如下6 种类型:

  • 【继承】extends (is-a)。
  • 【实现】implements (can-do)。
  • 【组合】类是成员变量 (contains-a)。
  • 【聚合】类是成员变量 (has-a)。
  • 【依赖】是除组合与聚合外的单向弱关系。比如使用另一个类的属性、方法,或以其作为方法的参数输入,或以其作为方法的返回值输出(depends-a)。
  • 【关联】是互相平等的依赖关系 (links-a)。

      继承和实现是比较容易理解的两种类关系。在架构设计中,要注意组合、聚合、依赖和关联这四者的区别。

      组合在汉语中的含义是把若干个独立部分组成整体,各个部分都有其独立的使用价值和生命周期。而类关系中的组合是一种完全绑定的关系,所有成员共同完成一件使命,它们的生命周期是一样的,极度容易混淆。组合体现的是非常强的整体与部分的关系,同生共死,部分不能在整体之间共享。

      聚合是一种可以拆分的整体与部分的关系,是非常松散的暂时组合,部分可以被拆出来给另一个整体。比如,汽车与轮子之间的关系就是聚合关系,轮子模块包括钢圈、轮胎、气嘴。轮子拆卸下来用到另一个汽车上是完全没有问题的。

      依赖是除组合和聚合外的类与类之间的单向弱关系,就是一个类A 用到类B,那么就说A依赖B,这种关系是偶然的、松散的、临时的。依赖使用另一个类的属性、方法,或以其作为方法的参数输入,或以其作为方法的返回值输出。本书认为,广义上的有向关联也等同于依赖。依赖往往是模块解耦的最佳点。

      关联即是互相平等的依赖关系,可以在关联点上进行解耦,但是解耦难度略大于依赖关系。

      在类图中,用空心的三角形表示继承,用实心的菱形表示组合,用空心的菱形表示聚合,用一条直线表示关联,这四者都是用实线连接的。用三角形来表示实现,用一个箭头表示依赖,与前面的区别是这两者都是用虚线连接的。在画类图时,菱形、箭头、三角形放在哪一侧呢?在很多类图中,这个处理是非常随意的。如果方向画反了,那么类结构的认知也就反了。有一个规律,有形状的图形符号一律放在权力强的这一侧,如表2-3 所示。

      随着业务和架构的发展,类与类的关系是会发生变化的,必须用发展的眼光看待类图。比如表2-3 中的Body 和Head,如果有一天,动物的脑袋可以随意地移植,那么就从组合关系变成聚合关系了。狗与狗绳之间的约束,虽然很弱,但是如果防疫局在狗绳上标记疫苗记录,那么它们之间的关系就会变强,就变成组合关系了。在业务重构过程中,往往会把原来强组合的关系拆开来,供其他模块调用,这就是类图的一种演变。

表2-3 类关系示例图

6.jpg

2.3.7 序列化

      内存中的数据对象只有转换为二进制流才可以进行数据持久化和网络传输。将数据对象转换为二进制流的过程称为对象的序列化(Serialization)。反之,将二进制流恢复为数据对象的过程称为反序列化(Deserialization)。序列化需要保留充分的信息以恢复数据对象,但是为了节约存储空间和网络带宽,序列化后的二进制流又要尽可能小。序列化常见的使用场景是RPC 框架的数据传输。常见的序列化方式有三种:

      (1) Java原生序列化。Java类通过实现Serializable接口来实现该类对象的序列化,这个接口非常特殊,没有任何方法,只起标识作用。Java 序列化保留了对象类的元数据(如类、成员变量、继承类信息等),以及对象数据等,兼容性最好,但不支持跨语言,而且性能一般。

      实现Serializable 接口的类建议设置serialVersionUID 字段值,如果不设置,那么每次运行时,编译器会根据类的内部实现,包括类名、接口名、方法和属性等来自动生成serialVersionUID。如果类的源代码有修改,那么重新编译后serialVersionUID的取值可能会发生变化。因此实现Serializable 接口的类一定要显式地定义serialVersionUID 属性值。修改类时需要根据兼容性决定是否修改serialVersionUID 值:

  • 如果是兼容升级,请不要修改 serialVersionUID 字段,避免反序列化失败。
  • 如果是不兼容升级,需要修改 serialVersionUID 值,避免反序列化混乱。

      使用Java 原生序列化需注意,Java 反序列化时不会调用类的无参构造方法,而是调用native 方法将成员变量赋值为对应类型的初始值。基于性能及兼容性考虑,不推荐使用Java 原生序列化。

      (2)Hessian 序列化。Hessian 序列化是一种支持动态类型、跨语言、基于对象传输的网络协议。Java 对象序列化的二进制流可以被其他语言(如C++、Python)反序列化。Hessian 协议具有如下特性:

  • 自描述序列化类型。不依赖外部描述文件或接口定义,用一个字节表示常用基础类型,极大缩短二进制流。
  • 语言无关,支持脚本语言。
  • 协议简单,比 Java原生序列化高效。

      相比Hessian 1.0,Hessian 2.0 中增加了压缩编码,其序列化二进制流大小是Java序列化的50%,序列化耗时是Java 序列化的30%,反序列化耗时是Java 反序列化的20%。

      Hessian 会把复杂对象所有属性存储在一个Map 中进行序列化。所以在父类、子类存在同名成员变量的情况下,Hessian 序列化时,先序列化子类,然后序列化父类,因此反序列化结果会导致子类同名成员变量被父类的值覆盖。

     (3)JSON 序列化。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。JSON 序列化就是将数据对象转换为JSON 字符串。在序列化过程中抛弃了类型信息,所以反序列化时只有提供类型信息才能准确地反序列化。相比前两种方式,JSON 可读性比较好,方便调试。

      序列化通常会通过网络传输对象,而对象中往往有敏感数据,所以序列化常常成为黑客的攻击点,攻击者巧妙地利用反序列化过程构造恶意代码,使得程序在反序列化的过程中执行任意代码。Java 工程中广泛使用的Apache Commons Collections、Jackson、fastjson 等都出现过反序列化漏洞。如何防范这种黑客攻击呢?有些对象的敏感属性不需要进行序列化传输,可以加transient 关键字,避免把此属性信息转化为序列化的二进制流。如果一定要传递对象的敏感属性,可以使用对称与非对称加密方式独立传输,再使用某个方法把属性还原到对象中。应用开发者对序列化要有一定的安全防范意识,对传入数据的内容进行校验或权限控制,及时更新安全漏洞,避免受到攻击。

相关文章
|
14天前
|
监控 JavaScript 前端开发
《理解 WebSocket:Java Web 开发的实时通信技术》
【4月更文挑战第4天】WebSocket是Java Web实时通信的关键技术,提供双向持久连接,实现低延迟、高效率的实时交互。适用于聊天应用、在线游戏、数据监控和即时通知。开发涉及服务器端实现、客户端连接及数据协议定义,注意安全、错误处理、性能和兼容性。随着实时应用需求增加,WebSocket在Java Web开发中的地位将更加重要。
|
2月前
|
NoSQL Java 关系型数据库
凭借Java开发进阶面试秘籍(核心版)逆流而上
最近参加了面试或者身边有朋友在面试的兄弟有没有发现,现在的面试不仅会问八股文,还会考察框架、项目实战、算法数据结构等等,需要准备的越来越多。 其实面试的时候,并不是要求你所有的知识点都会,而是关键的问题答到点子上!这份《Java 开发进阶面试秘籍(核心版)》由 P8 面试官整体把控,目前已经更新了 30 万字! 资料中涵盖了一线大厂、中小厂面试真题,毕竟真题都是技术领域最经典的基础知识和经验沉淀的汇总,非常有必要学习掌握!双重 buff 叠加,offer 接到手软~ 点击此处取,这可能是你到目前为止领取的最具含金量的一份资料! 整套资料涵盖:Spring、Spring
|
1月前
JavaWeb 开发之 ServletContext 的和使用
JavaWeb 开发之 ServletContext 的和使用
21 1
|
7天前
|
运维 NoSQL 算法
Java开发-深入理解Redis Cluster的工作原理
综上所述,Redis Cluster通过数据分片、节点发现、主从复制、数据迁移、故障检测和客户端路由等机制,实现了一个分布式的、高可用的Redis解决方案。它允许数据分布在多个节点上,提供了自动故障转移和读写分离的功能,适用于需要大规模、高性能、高可用性的应用场景。
15 0
|
9天前
|
人工智能 小程序 Java
JAVA开发智慧学校系统源码+人脸电子班牌布局
智慧校园是通过利用物联网,大数据技术来改变师生和校园资源相互交互的方式,以便提高交互的明确性、灵活性和响应速度,从而实现智慧化服务和管理的校园模式。
|
15天前
|
XML JSON JavaScript
使用JSON和XML:数据交换格式在Java Web开发中的应用
【4月更文挑战第3天】本文比较了JSON和XML在Java Web开发中的应用。JSON是一种轻量级、易读的数据交换格式,适合快速解析和节省空间,常用于API和Web服务。XML则提供更强的灵活性和数据描述能力,适合复杂数据结构。Java有Jackson和Gson等库处理JSON,JAXB和DOM/SAX处理XML。选择格式需根据应用场景和需求。
|
15天前
|
前端开发 Java API
构建RESTful API:Java中的RESTful服务开发
【4月更文挑战第3天】本文介绍了在Java环境中构建RESTful API的重要性及方法。遵循REST原则,利用HTTP方法处理资源,实现CRUD操作。在Java中,常用框架如Spring MVC简化了RESTful服务开发,包括定义资源、设计表示层、实现CRUD、考虑安全性、文档和测试。通过Spring MVC示例展示了创建RESTful服务的步骤,强调了其在现代Web服务开发中的关键角色,有助于提升互操作性和用户体验。
构建RESTful API:Java中的RESTful服务开发
|
20天前
|
存储 安全 Java
【Java技术专题】「Guava开发指南」手把手教你如何进行使用Guava工具箱进行开发系统实战指南(不可变集合篇)
【Java技术专题】「Guava开发指南」手把手教你如何进行使用Guava工具箱进行开发系统实战指南(不可变集合篇)
30 1
|
20天前
|
Java API Apache
【Java技术专题】「Guava开发指南」手把手教你如何进行使用Guava工具箱进行开发系统实战指南(基础编程篇)
【Java技术专题】「Guava开发指南」手把手教你如何进行使用Guava工具箱进行开发系统实战指南(基础编程篇)
43 0
|
29天前
|
Java API 计算机视觉
java实现人脸识别V3版本开发
java实现人脸识别V3版本开发
17 0