带你读《Java设计模式及实践》之一:从面向对象到函数式编程

简介: 本书向读者展示Java语言中更加智能化的编码实例。书中首先介绍面向对象编程(OOP)和函数式编程(FP)范式,然后描述常用设计模式的经典使用方法,并解释如何利用函数式编程特性改变经典的设计模式。读者将学习混合使用OOP和FP的实现方式,然后学习响应式编程模型——一种为了编写更好的代码而将OOP和FP结合使用的方法。之后,本书将介绍从MVC架构向微服务和无服务器架构转变的发展趋势,最后介绍Java新版本的功能特性及其实践。通过本书的学习,读者可以有效地解决开发应用程序过程中的常见问题,能够轻松地应对各种规模项目的扩展和维护。

Java核心技术系列
点击查看第二章
点击查看第三章
Java设计模式及实践
Design Patterns and Best Practices in Java

image.png

  [印度] 卡马尔米特·辛格(Kamalmeet Singh)
  [荷兰] 艾德里安·伊恩库列斯库(Adrian Ianculescu) 著
[罗马尼亚] 路西安-保罗·托尔耶(Lucian-Paul Torje)
张小坤 黄 凯 贺 涛 译

第1章

从面向对象到函数式编程
本章介绍如何使用设计模式来写具有健壮性、可维护性、可扩展性的代码,以及Java的最新特性。为此,我们需要讨论以下问题:

  • 什么是编程范式
  • 命令式范式
  • 声明式和函数式范式
  • 面向对象范式
  • 统一建模语言(UML)综述
  • 面向对象原则

1.1 Java简介

1995年,一个新的编程语言发布了,它从广为人知的C++语言以及鲜为人知的Smalltalk语言继承而来。这个新的编程语言就是Java,它尝试着去改善之前大部分语言的局限性。比如,令Java广为流行的一个重要特性是:编写一次就能随处使用。这个特性意味着,你能够在一台Windows机器上开发代码,但是可以让代码运行在Linux上,或者说其他的系统上,你所需要的只是JVM(Java Virtual Machine,Java虚拟机)。Java还提供了一些其他特性,比如:垃圾回收器,让开发人员从繁杂的申请内存和释放内存工作中解放出来;JIT(Just In Time,即时编译技术),让Java更加智能和快速。Java还移除了一些特性,比如指针,这样会让Java更加安全。上面提到的所有特性以及后续增加的网络支持特性使Java成为开发人员的一个普遍选择。自Java诞生以来,隔几年就有一种新语言诞生和消失,而Java 11已经成功发布并被公众接受,这正说明了Java语言的成功。

1.2 Java编程范式

什么是编程范式,自从有软件开发开始,开发人员尝试了不同的方式来设计编程语言。对于不同的编程语言,我们都有一系列的概念、原则和规定。这些概念、原则和规定就被称为编程范式。从理论上来说,我们希望编程语言只遵从一个编程范式。但是实际上,一个语言往往拥有多个编程范式。
在接下来的几节里,我们会重点介绍Java语言所基于的编程范式,包括命令式、面向对象、声明式和函数式编程,以及用来描述这些编程范式的主要概念。

1.2.1 命令式编程

命令式编程是这样一种编程范式:用语句更改程序的状态。这个概念出现在运算的开始,并且与计算机的内部结构紧密相连。程序是处理单元上运行的一组指令,它以命令的方式改变状态(状态即存储器中的变量)。“命令”这个名称,顾名思义,指令的执行即是程序的运行。
今天大多数流行的编程语言或多或少都基于命令式编程发展而来。命令式语言最典型的示例就是C语言。
命令式编程示例
为了更好地理解命令式编程范式的概念,让我们举一个例子:你计划在某城镇与一个朋友会面,但他不知道如何到达那里。我们来试着以“命令式”的方式向他解释如何实现目标:
1)在中央火车站乘坐1号电车;
2)在第三站下车;
3)向右走,朝第六大道行进,直到到达第三个路口。

1.2.2 面向对象编程

面向对象编程经常与命令式编程联系在一起,在实践当中,两者是可以共存的。Java就是这种协作的生动证明。
接下来,我们将简要介绍面向对象的基本概念,代码都会以Java语言实现。
1.对象和类
对象是面向对象编程(OOP)语言的主要元素,它包括状态和行为。
如果我们将类视为模板,则对象是模板的实现。例如,如果“人类”是一个定义了人类所拥有的属性和行为的类,那么你我都是这个“人类”类的对象,因为我们已经满足了作为“人类”所有的要求。或者,我们把“汽车”视为一个类,那么一辆特定的本田思域汽车就是“汽车”类的一个对象。这辆本田思域汽车可以满足汽车类所具备的所有属性和行为,比如有引擎、方向盘、车灯等,还能前进、倒退等。我们可以看到面向对象范式如何与现实世界相关联。几乎现实世界中的所有东西都可以从类和对象的角度来考虑,因此OOP能够毫不费力地流行起来。
面向对象基于四个基本原则:

  • 封装
  • 抽象
  • 继承
  • 多态

2.封装
封装主要是指属性和行为的绑定。封装的思路是将对象的属性和行为保存在一个地方,以便于维护和扩展。封装还提供了一种隐藏用户所不需要的细节的机制。在Java当中,我们可以为方法和属性提供访问说明符来管理类使用者的可见内容以及隐藏内容。
封装是面向对象语言的基本原则之一。封装有助于不同模块的分离,使得开发人员可以或多或少地独立开发和维护解耦模块。在内部更改解耦模块/类/代码而不影响其外部暴露行为的技术称为代码重构。
3.抽象
抽象与封装密切相关,并且在某种程度上它与封装重叠。简而言之,抽象提供了一种机制,这种机制使得对象可以公开它所做的事,而隐藏它是如何做到这些事的。
我们拿现实世界中的“汽车”作为例子来说明抽象。为了驾驶一辆汽车,我们并不需要知道汽车引擎盖下是什么样的,我们只需要知道它给我们暴露的数据和行为。数据显示在汽车的仪表盘上,行为就是我们可以用控制设备来驾驶汽车。
4.继承
继承是指对象或类基于另一个对象或类的能力。有一个父类或者基类,它为实体提供顶级行为。每一个满足“父类的属性和方法是子类的一部分”条件的子类实体或者子类都可以从父类中继承,并根据需要添加其他行为。
让我们来看一个现实世界的例子。如果我们将Vehicle视为父类,我们知道Vehicle
类可以具有某些属性和行为。例如,Vehicle类有一个引擎、好几个门等等,并且它拥有移动这个行为。现在满足这些条件的所有实体(例如Car、Truck、Bike等),都可以从Vehicle类继承并添加给定的属性和行为。换句话说,我们可以说Car是一种Vehicle。
让我们来看代码如何实现。我们首先创建一个名为Vehicle的基类,此类拥有一个构造函数,这个函数能够接受一个String(字符串)类型的参数(车辆名称):
image.png

现在我们创建一个拥有构造函数的Car类。Car类继承自Vehicle类,因此它继承并可以访问在基类中声明为protected(保护)或public(公共)的所有成员和方法:
image.png

5.多态
从广义上讲,多态为我们提供了让不同类型的实体使用相同接口的选项。主要有两种类型的多态:编译时多态和运行时多态。有一个Shape类,拥有两个计算面积的方法。一个方法计算一个圆的面积,它接受一个整数,也就是说,输入半径并返回这个圆的面积。另一个方法计算矩形的面积,它需要两个输入—长度和宽度。编译器可以根据调用参数的数量来决定调用哪个面积方法。这是编译时多态。
有些技术人员认为,只有运行时多态才是真正的多态。运行时多态(有时也称为子类型多态)在子类继承父类并覆盖其方法时起作用。在这种情况下,编译器无法决定最终是执行子类的实现还是父类的实现,只能在运行时决定。
为了详细说明,我们采用前面的示例并向车辆类型添加新方法以打印对象的类型和名称:
image.png

我们在派生的Car类中重写相同的方法:
image.png

现在我们可以看到行为中的子类型多态。我们创建一个Vehicle对象和一个Car对象,将每个对象声明为Vehicle变量类型(因为Car也是Vehicle)。然后我们为每个对象调用toString方法。vehicle1是Vehicle类的一个实例,它将调用Vehicle.toString()方法。vehicle2是Car类的一个实例,它将调用Car类的toString方法:
image.png

1.2.3 声明式编程

让我们回想之前提到的现实生活中的命令式编程例子,我们指导朋友如何到达一个地方。当我们从声明式编程范式的角度思考,我们并不告诉朋友如何到达特定位置,而是简单地给他地址并让他弄清楚如何到达那里。在这种情况下,我们并不关心他是否使用地图或GPS,或者他是否向别人求助,而是告诉他该做什么:上午9:30到达第五大道和第九大道之间的交界处。
与命令式编程相反,声明式编程是这样一种编程范式:它指定程序应该做什么,而不具体说明怎么做。纯粹的声明式语言包括数据库查询语言(如SQL和XPath)以及正则表达式。与命令式编程语言相比,声明式编程语言更抽象。它们并不模拟硬件结构,因此不会改变程序的状态,而是将它们转换为新状态,并且更接近数学逻辑。
通常,非命令式的编程范式都被认为属于声明式类别。这就是为什么有许多类型的范式属于声明式类别。在我们的探索中,我们会看到与本书内容唯一相关的一个声明式编程范式:函数式编程。

1.2.4 函数式编程

函数式编程是声明式编程的子范式。与命令式编程相反,函数式编程不会改变程序的内部状态。
在命令式编程中,函数更多地被视为指令、例程或过程的序列。它们不仅依赖于存储在存储器中的状态,而且还可以改变该状态。这样,根据当前程序的状态,使用相同参数调用命令式函数可能会产生不同的结果,与此同时,被执行的函数更改了程序的变量。
在函数式编程术语中,函数类似于数学函数,函数的输出仅依赖于其参数,而不管程序的状态如何,完全不受函数是何时执行的影响。
矛盾的是,虽然命令式编程自计算机发明以来就存在,但函数式编程的基本概念却可以追溯到这之前。大多数函数式语言都是基于lambda演算,这是由数学家Alonzo Church于20世纪30年代创建的一种形式化数学逻辑系统。
函数式语言在当时变得如此受欢迎的原因之一是它们可以轻松地在并行环境中运行,这与多线程不太一样。函数式语言支持并行运行的关键在于它们的基本原理:函数仅依赖于输入参数而不依赖于程序的状态。也就是说,它们可以在任何地方运行,然后将多个并行执行的结果连接起来并进一步使用。

1.3 流以及集合的使用

每个使用Java的人都知道集合。我们以命令式方式使用集合:告诉程序如何做它应该做的事情。让我们看以下示例,其中实例化10个整数的集合,从1到10:
image.png

现在,我们将创建另一个集合,在其中过滤掉奇数:
image.png

最后,我们把结果打印出来:
image.png

正如你所看到的,我们编写了相当多的代码来执行三个基本操作:创建数字集合,过滤奇数,打印结果。当然,我们可以在一个循环中完成所有操作,但是如果我们在不使用循环的情况下完成它呢?毕竟,使用循环意味着我们告诉程序如何完成其任务。从Java 8开始,我们已经能够使用流在一行代码中执行相同的操作:
image.png

流在java.util.stream包中定义,用于管理可以对其执行功能式操作的对象流。流是集合的功能对应物,并为映射-归约操作提供支持。
我们将在后面的章节中进一步讨论Java中的流和函数式编程。

1.4 统一建模语言简介

统一建模语言(Unified Modeling Language,UML)可以帮助我们表示软件的结构:不同的模块、类和对象如何相互交互,以及它们之间的关系是什么。
UML经常与面向对象的设计结合使用,但它具有更广泛的适用范围。然而这超出了本书的范围,因此,下面我们将重点介绍与本书相关的UML功能。
在UML中,我们可以定义系统的结构和行为,并且可以通过图表可视化全部或者部分模型。有两种类型的图表:

  • 结构图用于表示系统的结构。有许多类型的结构图,但我们只讨论类图。对象图、包图和组件图均类似于类图。
  • 行为图用于描述系统的行为。交互图是行为图的子集,用于描述系统的不同组件之间的控制流和数据流。在行为图中,顺序图广泛用于面向对象的设计中。

类图是面向对象设计和开发阶段中使用最多的图类型。类图是一种结构图,用于说明类的结构及类之间的关系。如图1-1所示。

image.png

类图对于描述类在应用程序中的结构非常有用。大多数时候,仅仅查看结构就足以了解类如何交互,但有时这还不够。对于这些情况,我们可以使用行为图和交互图,其中顺序图用于描述类和对象的交互。让我们使用顺序图来显示Car对象和Vehicle对象如何在继承和多态示例中进行交互,如图1-2所示。

image.png

类之间的关系
在面向对象编程中,除了代表基本概念的继承关系之外,还有一些其他类关系可以帮助我们建模和开发复杂的软件系统:

  • 泛化和实现
  • 依赖
  • 关联、聚合和组合

1.泛化
继承也被称为Is-A关系,因为从另一个类继承而来的类能够被当成父类来使用。
当一个类表示多个类的共享特征时,这称为泛化,例如,Vehicle是Bike、Car和Truck的泛化。类似地,当一个类表示一般类的特殊实例时,这称为特化,因此Car是Vehicle的特化,如图1-3所示。

image.png

在UML术语中,描述继承的关系称为泛化。
2.实现
如果说泛化是面向对象中继承概念对应的UML中的术语,则UML中的实现表示面向对象编程中类的接口实现。
我们假设创建了一个名为Lockable的接口,该接口仅由可以锁定的Vehicle实现。在这种情况下,为Car类实现Lockable的图1-3将如图1-4所示。
3.依赖
依赖是最通用的UML关系类型之一。它用于定义一个类以某种方式依赖于另一个类,而另一个类可能依赖于或不依赖于第一个类。依赖关系有时被称为Uses-A关系。
通常,在面向对象的编程语言中,依赖关系用于描述一个类在方法的声明中是否包含第二个类的参数,或者说它只是通过方法将第二个类传递给其他类,而不是使用方法来创建第二个类的实例。如图1-5所示。

image.png

4.关联
关联表示两个实体之间的关系。有两种类型的关联:组合和聚合。通常,关联关系由箭头表示,如图1-6所示。
5.聚合
聚合是一种特殊类型的关联。如果将继承看作Is-A关系,则可以将聚合视为Has-A关系。
聚合用于描述以下情况中两个或多个类之间的关系:一个类在逻辑上包含另一个类,但所包含的类的实例又可以独立于第一个类在其上下文之外生存,或者可以被其他的类所引用。例如,部门与教师间存在Has-A关系,每位教师必须属于部门,但如果部门不再存在,教师仍然可以处于活动状态,如图1-7所示。

image.png

6.组合
顾名思义,一个类是另一个类的组成部分就称两者间存在组合关系。这有点类似于聚合,不同之处在于当主类不再存在时,依赖类不再存在。例如,房子(House)由房间(Room)组成,但如果房子被销毁,房间就不再存在,如图1-8所示。

image.png

实际上,尤其是在具有垃圾回收器的Java等语言中,组合和聚合之间的界限并不是很清晰。对象不必手动销毁,当它们不再被引用时,它们会被垃圾回收器自动销毁。因此,从编码的角度来看,我们不需要真正关心处理的是组合还是聚合关系,但是如果我们想要在UML中有一个定义良好的模型,考虑这一点就很重要了。

1.5 设计模式和原则

无论你是在大型团队中工作还是在单人项目中工作,软件开发都不仅仅是编写代码。构建应用程序的方式对软件应用程序的成败有很大影响。当我们谈论一个成功的软件应用程序时,不仅讨论应用程序如何执行它应该做的事情,还讨论在开发它时付出了多少努力,以及它是否易于测试和维护。如果没有以正确的方式完成,那么暴涨的开发成本将会导致没人能接受这个应用程序。
创建软件应用程序是为了满足不断变化和发展的需求。一个成功的应用程序还应该提供一种简单的方法来扩展它以满足不断变化的期望。
幸运的是,我们不是第一个遇到这些问题的人。有一些问题已经被开发人员所发现并总结了解决方案。如果在设计和开发软件时应用一组面向对象的设计原则和模式,则可以避免或解决这些常见问题。
面向对象的设计原则也被称为SOLID。在设计和开发软件时可以应用这些原则,以便创建易于维护和开发的程序。SOLID最初是由Robert C. Martin所提出的,它们是敏捷软件开发过程的一部分。SOLID原则包括单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。
除了前面提到的设计原则外,还有面向对象的设计模式。设计模式是可以应用于常见问题的通用可重用解决方案。遵循克里斯托弗·亚历山大的概念,设计模式首先被Kent Beck和Ward Cunningham应用于编程,并于1994年因一本名为《Gang Of Four(GOF)》的书而广为流传。下面我们将介绍SOLID设计原则,接下来的几章将介绍设计模式。

1.5.1 单一职责原则

单一职责原则是一种面向对象的设计原则,该原则指出软件模块应该只有一个被修改的理由。在大多数情况下,编写Java代码时都会将单一职责原则应用于类。
单一职责原则可被视为使封装工作达到最佳状态的良好实践。更改的理由是:需要修改代码。如果类需要更改的原因不止一个,那么每个类都可能引入影响其他类的更改。当这些更改单独管理但影响同一模块时,一系列更改可能会破坏与其他更改原因相关的功能。
另一方面,每个更改的职责/理由都会增加新的依赖关系,使代码不那么健壮,更难以修改。
在示例中,我们将使用数据库来持久保存对象。假设对Car类添加方法来处理增、删、改、查的数据库操作,如图1-9所示。

image.png

在这种情况下,Car不仅会封装逻辑,还会封装数据库操作(两个职责是改变的两个原因)。这将使我们的类更难维护和测试,因为代码是紧密耦合的。Car类将取决于数据库,如果将来想要更改数据库系统,我们必须更改Car代码,这可能会在Car逻辑中产生错误。
相反,更改Car逻辑可能会在数据持久性中产生错误。
解决方案是创建两个类:一个用于封装Car逻辑,另一个用于负责持久性。如图1-10所示。

image.png

1.5.2 开闭原则

这个原则如下:
“模块、类和函数应该对扩展开放,对修改关闭。”
应用此原则将有助于我们开发复杂而稳健的软件。我们必须想象:开发的软件正在构建一个复杂的结构,一旦我们完成了它的一部分,不应该再修改它,而是应该在它的基础之上继续建设。软件开发也是一样的。一旦我们开发并测试了一个模块,如果想要改变它,不仅要测试正在改变的功能,还要测试它负责的整个功能。这涉及许多额外的资源,这些资源可能从一开始就没有估算过,也会带来额外的风险。一个模块中的更改可能会影响其他模块或整体上的功能。如图1-11所示。

image.png

因此,最好的办法是尝试在完成后保持模块不变,并通过继承和多态扩展来添加新功能。开闭原则是最重要的设计原则之一,是大多数设计模式的基础。

1.5.3 里氏替换原则

Barbara Liskov指出,派生类型必须完全可替代其基类型。里氏替换原则(LSP)与子类型多态密切相关。基于面向对象语言中的子类型多态,派生对象可以用其父类型替换。例如,如果有一个Car对象,它可以在代码中用作Vehicle。
里氏替换原则声明,在设计模块和类时,必须确保派生类型从行为的角度来看是可替代的。当派生类型被其父类型替换时,其余代码就像它是子类型那样使用它。从这个角度来看,派生类型应该像其父类型那样表现,不应该破坏它的行为。这称为强行为子类型。
为了理解LSP,我们举一个违反原则的例子。在开发汽车服务软件时,我们发现需要对以下场景进行建模。当留下汽车需要服务时,车主离开汽车。服务助理拿走钥匙,当车主离开时,服务助理检查他是否有正确的钥匙以及是否发现了正确的车。他只是去解锁并锁上车,然后将钥匙放在指定的地方并留一张便条,这样机械师就可以在检查汽车时轻易找到钥匙。
我们已经定义了一个Car类,现在创建一个Key类并在汽车类中添加两个方法:lock和unlock。我们添加了一个相应的方法,助理检查钥匙是否匹配汽车:
image.png

类图如图1-12所示。
在开发软件时,我们想到了有时通过汽车服务修理巴吉赛车(译者注:一种没有门的沙漠赛车,使用后轮驱动)。由于巴吉赛车是四轮车,我们创造了一个继承自Car的Buggy类。如图1-13所示。

image.png

巴吉赛车没有门,因此无法锁定或解锁。我们相应地实现了代码:
image.png

我们设计的软件要用于汽车,无论它们是否是巴吉赛车,所以将来可以将它扩展到其他类型的汽车。汽车能被锁定或解锁可能会产生一些问题。

1.5.4 接口隔离原则

下面这句话从链接https://www.oodesign.com/interface-segregation-principle.html 得来:
“客户端不应该依赖于它所不需要的接口。”
实际应用中,接口隔离原则(Interface Segregation Principle,ISP)减少了代码耦合,使软件更健壮,更易于维护和扩展。接口隔离原则最初是由Robert Martin提出的,他意识到如果接口隔离原则被破坏,客户端被迫依赖它们不使用的接口时,代码就会变得紧密耦合,几乎不可能为其添加新功能。
为了更好地理解这一点,我们再次采用汽车服务示例(参见图1-14)。现在我们需要实现一个名为Mechanic(机修工)的类。机修工修理汽车,所以我们增加了修理汽车的方法。在这个例子中,Mechanic类依赖于ICar类,但是,Car类提供的方法超出了Mechanic需要的。

image.png

这是一个糟糕的设计,因为如果我们想把汽车替换为另一辆汽车,需要在Mechanic类中进行更改,这违反了开闭原则。换个思路,我们可以创建一个仅公开Me-chanic类所需的相关方法的接口。如图1-15所示。

image.png

1.5.5 依赖倒置原则

“高级模块不应该依赖低级模块,两者都应该依赖抽象。”
“抽象不应该依赖于细节,细节应该依赖于抽象。”
为了理解这个原理,我们必须解释耦合和解耦的重要概念。耦合是指软件系统的模块彼此依赖的程度。依赖度越低,维护和扩展系统就越容易。有不同的方法来解耦系统的组件。其中一个办法是将高级逻辑与低级模块分开,如图1-16所示。这样做时,可以尝试让它们都依赖于抽象进而减少二者之间的依赖关系。如此就可以替换或扩展其中任何一个模块而不影响其他模块。

image.png

1.6 总结

在本章中,我们介绍了Java中使用的主要编程范式。我们学习了两种不同的范式(例如命令式编程和函数式编程)可以在同一种语言中共存,也学习了Java如何从纯粹的、命令式的面向对象编程转变为集成函数式编程元素的。
虽然Java从版本8开始引入了新的函数式编程元素,但它的核心仍然是面向对象的语言。为了编写易于扩展和维护的可靠且健壮的代码,我们学习了面向对象编程语言的基本原则。
开发软件的一个重要部分是设计程序组件的结构和所需行为。通过这种方式,我们可以在大型系统、大型团队中工作,在团队内部或团队之间共享面向对象的设计。为了能够做到这一点,我们强调了与面向对象设计和编程相关的主要UML图和概念。我们还在本书中广泛使用UML来描述这些示例。在介绍了类关系并展示如何在图中表示它们之后,我们进入下一部分,在那里我们描述了面向对象的设计模式和原理,提出了主要原则。在下一章中,我们将继续介绍处理对象创建的设计模式,这种模式使代码具有健壮性和扩展性。

相关文章
|
1天前
|
数据采集 存储 Java
高德地图爬虫实践:Java多线程并发处理策略
高德地图爬虫实践:Java多线程并发处理策略
|
1天前
|
安全 Java
Java基础&面向对象&继承&抽象类
Java基础&面向对象&继承&抽象类
|
1天前
|
Java
【Java基础】详解面向对象特性(诸如继承、重载、重写等等)
【Java基础】详解面向对象特性(诸如继承、重载、重写等等)
5 0
|
2天前
|
设计模式 算法 Java
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
|
2天前
|
设计模式 JavaScript Java
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
|
2天前
|
设计模式 存储 JavaScript
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
|
2天前
|
设计模式 Java Go
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
|
2天前
|
设计模式 存储 前端开发
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
|
2天前
|
IDE Java 开发工具
Java从入门到精通:1.3.1实践编程巩固基础知识
Java从入门到精通:1.3.1实践编程巩固基础知识
|
7天前
|
安全 Java 机器人
《Java 简易速速上手小册》第2章:面向对象的 Java(2024 最新版)
《Java 简易速速上手小册》第2章:面向对象的 Java(2024 最新版)
19 0