Paul Jiang's Blog

  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

HotSpot实战-3类与对象

发表于 2018-11-15 | 更新于 2020-05-17 | 分类于 Java

源码路径

hotspot/src/share/vm/oops

OOP-Klass

  1. OOP(ordinary object pointer): 普通对象指针,用来描述对象实例信息
  2. Klass: Java类的C++对等体,用来描述Java类。

模块

OOP

模块 模块说明
oop 定义了OOPS共同基类

Klass

模块 模块说明
Klass 用来描述语言层的类型
instanceKlass 在虚拟机层面描述一个Java类

HotSpot实战-2启动

发表于 2018-11-15 | 更新于 2020-05-17 | 分类于 Java

启动

Hotspot提供了两种Launcher类型,分别是通用启动器(java)和调试版启动器(gamma)。

通用启动器

1
java [ option ] class [ argument ... ]

入口:jdk/src/share/bin/main.c

调试版启动器

入口:hostspor/src/share/tools/luncher/java.c

虚拟机生命周期

HotSpot实战-1内核框架

发表于 2018-11-15 | 更新于 2020-05-17 | 分类于 Java

编译

搭建环境

源代码下载

Hotspot源码目录结构概览

目录 用途
agent Serviceability Agent(可维护性代理)
make 编译hotspot的makefile文件
src 主体源代码
cpu 依赖具体处理器架构的代码(ppc,sparc,x86,zero)
os 依赖操作系统的代码(bsd,linux,posix,solaris,windows)
os_cpu 同时依赖操作系统和处理器的代码
share 操作系统和处理器无关的代码(vm,tools)
vm 实现虚拟机各项功能
adlc 平台描述文件
asm 汇编器
c1 C1编译器,client编译器
ci 动态编译器
classfile class文件解析和类的链接等
code 机器码生成
compiler 调用动态编译器
gc_implementation gc实现
gc_interface gc接口
interpreter 解释器
libadt 抽象数据结构
memory 内存管理
oops jvm内部对象表示
opto C2编译器,server编译器
precompiled
prims hotspot对外接口
runtime 运行时
services jmx接口
shark 基于LLVM实现的即时编译器
trace
utilizes 内部工具类和公共函数
tools 独立的虚拟机工具程序
test 测试用例

Prims

prims模块定义了外部接口,允许外部程序访问内部状态信息。

比如,jni模块,jvm模块,jvmti模块,perf模块。

Services

services模块主要包含9个主要子模块(management,memory service,thread service, runtime service, memory manager, heap dumper, classloading service, memory pool,attach listener)。

Runtime

runtime是运行时模块,为其他系统组件提供运行时支持,如线程、安全点、PerfData、Stub例程、反射、VMOperation以及互斥锁等组件。

软件工程导论-实现

发表于 2018-11-12 | 更新于 2020-05-17 | 分类于 管理

通常把编码和测试统称为实现。

编码

1.选择程序设计语言。选用的标准:系统用户的要求;可以使用的编译程序;可以得到的软件工具;工程规模;程序员的知识;软件可移植性要求;软件的应用领域;

2.编码风格。遵循规则:程序内部的文档;数据说明;语句构造;输入输出;效率;

软件测试

软件测试的目标

1.测试是为了发现程序中的错误而执行程序的过程。

2.好的测试方案是极可能发现迄今为止尚未发现的错误的测试方案。

3.成功的测试是发现了至今为止尚未发现的错误的测试。

软件测试准则

1.所有测试都应该能追溯到用户需求。

2.应该远在测试开始之前就制定出测试计划。

3.把Pareto原理应用到软件测试中。

4.应该从“小规模”测试开始,并逐步进行“大规模”测试。

5.穷举测试是不可能的。

6.为了达到最佳的测试效果,应该由独立的第三方从事测试工作。

测试任何产品有两种方法:黑盒测试和白盒测试。

测试步骤

1.模块测试

2.子系统测试

3.系统测试

4.验收测试

5.平行运行

测试阶段的信息流

1.软件配置,包括需求说明书、设计说明书和源程序清单等。

2.测试配置,包括测试计划和测试方案。

如果经常出现要求修改设计的严重错误,那么软件的质量和可靠性是值得怀疑的。

反之应该考虑两种可能:1.软件的可靠性是可以接受的;2.所进行的测试尚不足以发现严重的错误。

软件可靠性模型使用错误率数据估计将来出现错误的情况,并进而对软件可靠性进行预测。

单元测试

在单元测试期间着重从下述5个方面对模块进行测试。

1.模块接口

2.局部数据结构

3.重要的执行通路

4.出错处理通路

5.边界条件。

代码审查

人工测试源程序可以由程序的编写者本人非正式地进行,也可以由审查小组正式进行。后者称为代码审查。

组成人员:1.组长,应该是一个很有能力的程序员,而且没有直接参与这项工程。2.程序的设计者。3.程序的编写者。4.程序的测试者。

模块并不是一个独立的程序,因此必须为每个单元测试开发驱动软件和(或)存根软件。通常驱动程序也就是一个“主程序”,它接收测试数据,把这些数据传送给被测试的模块,并且印出有关的结果。存根程序代替被测试的模块所调用的模块。它使用被它代替的模块的接口,可能做最少量的数据操作,印出对入口的检验或操作结果,并且把控制归还给调用它的模块。

集成测试

集成测试是测试和组装软件的系统化技术。

由模块组装成程序时有两种方法。

非渐增式测试方法:先分别测试每个模块,再把所有模块按设计要求放在一起结合成所要的程序。

一下子把所有模块放在一起,并把庞大的程序作为一个整体来测试,测试者面对的情况十分复杂。改正错误更是极端苦难,因为在庞大的程序中想要诊断定位一个错误是非常困难的。

渐增式测试:把下一个要测试的模块同已经测试好的那些模块结合起来进行测试,测试完以后再把下一个应该测试的模块结合进来测试。

它把程序划分成小段来构造和测试,在这个过程中比较容易定位和改正错误;对接口可以进行更彻底的测试;可以使用系统化的测试方法。

目前普遍采用渐增式测试方法。

使用渐增方式时,有自顶向下和自底向上两种集成策略。

1.自顶向下集成

自顶向下集成方式是一个日益为人们广泛采用的测试和组装软件的途径。从主控制模块开始,沿着程序的控制层次向下移动,逐渐把各个模块结合起来。在把附属于主控制模块的那些模块组装待程序结构中去时,或者使用深度优先的策略,或者使用宽度优先的策略。

2.自底向上集成

自底向上测试从“原子”模块(即在软件结构最低层的模块)开始组装和测试。因为是从底部向上结合模块,总能得到所需的下层模块处理功能,所以不需要存根程序。

步骤:

第一步,把低层模块组合成实现某个特定的软件子功能的族。

第二步,写一个驱动程序(用于测试的控制程序),协调测试数据的输入和输出。

第三步,对由模块组成的子功能族进行测试。

第四步,去掉驱动程序,沿软件结构自下向上移动,把子功能族组合起来形成更大的子功能族。

回归测试

在集成测试过程中每当一个新模块结合进来时,程序就发生了变化:建立了新的数据流路径,可能出现了新的I/O操作,激活了新的控制逻辑。重新执行已经做过的测试的某个子集,以保证上述这些变化没有带来非预期的副作用。

回归测试集包括3类不同的测试用例:

1.检测软件全部功能的代表性测试用例。

2.专门针对可能受修改影响的软件功能的附加测试。

3.针对被修改过的软件成分的测试。

确认测试

确认测试也成为验收测试,它的目标是验证软件的有效性。

确认测试必须有用户积极参与,或者以用户为主进行。用户应该参与月设计测试方案。确认测试通常使用黑盒测试法。

测试计划:包括要进行的测试的种类及进度安排。

测试过程:规定了用来检测软件是否与需求一致的测试方案。

有下述两种可能的结果

1.功能和性能与用户要求一致,软件是可以接受的。

2.功能和性能与用户要求有差距。

确认测试的一个重要内容是复查软件配置。保证软件配置的所有成分都齐全,质量符合要求,文档与程序完全一致,具有完成软件维护所必须的细节,而且已经编好目录。

如果一个软件是为许多客户开发的,那么使用Alpha测试和Beta测试。

Alpha测试:由用户在开发者的场所进行,并且在开发者对用户的“指导”下进行测试,Alpha测试是在受控的环境中进行的。

Beta测试:由软件的最终用户们在一个或多个客户场所进行。

白盒测试技术:

设计测试方案的基本目标是,确定一组最可能发现某个错误或某类错误的测试数据。

1.逻辑覆盖

语句覆盖;判定覆盖;条件覆盖;判定/条件覆盖;条件组合覆盖;点覆盖;边覆盖;路径覆盖;

2.控制结构测试

基本路径测试;条件测试;循环测试;

黑盒测试

黑盒测试力图发现的错误:1.功能不正或遗漏了功能;2.界面错误;3.数据结构错误或外部数据库访问错误;4.性能错误;5.初始化和终止错误。

应该考虑的问题:

1.怎样测试功能的有效性?

2.哪些类型的输入可构成好测试用例?

3.系统是否对特定的输入值特别敏感?

4.怎样划定数据类的边界?

5.系统能够承受什么样的数据率和数据量?

6.数据的特定组合将对系统运行产生什么影响?

测试用例集:

1.所设计出的测试用例能够减少为达到合理测试所需要设计的测试用例的总数。

2.所设计出的测试用例能够告诉人们,是否存在某些类型的错误,而不是仅仅指出与特定测试相关的错误存在。

有哪些技术?

1.等价划分。

2.边界值分析。

3.错误推测。

调试

调试的目标是寻找软件错误的原因并改正错误。

1.蛮干法;2.回溯法;3.原因排除法。

测试阶段的根本目标是消除错误,保证软件可靠性。

软件可靠性是程序在给定的时间间隔内,按照规格说明书的规定成功地运行的概率。

软件可用性是程序在给定的时间点,按照规格说明书的规定,成功地运行的概率。

引入平均无故障时间和平均维修时间。

软件工程导论-详细设计

发表于 2018-11-12 | 更新于 2020-05-17 | 分类于 管理

详细设计的根本目标是确定应该怎样具体地实现所要求的系统,从而在编码阶段可以把这个描述直接翻译成用某种程序设计语言书写的程序。

程序的”读者”有两个,那就是计算机和人,因此,衡量程序的质量不仅要看它的逻辑是否正确,性能是否满足要求,更主要的是要看它是否容易阅读和理解。结构程序设计使实现上述目标的关键技术,因此是详细设计的逻辑基础。

结构程序设计使尽可能少用GO TO语句的程序设计方法。最好仅在检测出错误时才使用GO TO语句,而且应该总是使用前向GO TO语句。

人机界面设计使接口设计的一个重要的组成部分,对于交互式系统来说,人机界面设计和数据设计、体系结构设计及过程设计一样重要。

在设计人机界面的过程中,几乎总会遇到下述4个问题:系统响应时间、用户帮助设施、出错信息处理和命令交互。

用户界面设计是一个迭代的过程:先创建设计模型,再用原型事项这个设计模型,并有用户试用和评估,然后根据用户意见进行修改。

也可以在创建原型之前就对用户界面的设计质量进行初步评估。如果能及早发现并改正潜在的问题,就可以减少评估周期的执行次数,从而缩短软件的开发时间。可以用下述评估标准对设计进行早期复查。

1.系统及其界面的规格说明书的长度和复杂程度,预示了用户学习使用该系统所需要的工作量。

2.命令或动作的数量、命令的平均参数个数或动作中单个操作的个数,预示了系统的交互时间和总体效率。

3.设计模型中包含的动作、命令和系统状态的数量,预示了用户学习使用该系统时需要记忆的内容的多少。

4.界面风格、帮助设施和出错处理协议,预示了界面的复杂程度及用户接收该界面的程度。

描述程序处理过程的工具称为过程设计工具,它们可以分为图形、表格和语言3类。不论是哪类工具,对它们的基本要求都是能提供对设计的无歧义的描述,也就是应该能指明控制流程、处理功能、数据组织及其他方面的实现细节,从而在编码阶段能把对设计的描述直接翻译成程序代码。可以采用程序流程图、盒图、PAD图、判定表、判定树、过程设计语言PDL。

计算机软件本质上是信息处理系统,因此,可以根据软件所处理的信息的特征来设计软件。面向数据结构的设计方法最终目标是得出对程序处理过程的描述。这种设计方法并不明显地使用软件结构的概念,模块是设计过程的副产品,对于模块独立原理也没有给予应有的重视。因此,这种方法最适合于详细设计阶段使用。Jackson方法和Warnier方法是最著名的两个面向数据结构的设计方法。

程序复杂程度的定量度量

定量度量程序复杂程度的方法很有价值:把程序的复杂程度乘以适当常数即可估算出软件中错误的数量以及软件开发需要用的工作量,定量度量的结果可以用来比较两个不同的设计或两个不同算法的优劣;程序的定量的复杂程度可以作为模块规模的精确限度。用的比较广泛的有McCabe方法和Halstead方法。

敏捷软件开发Scrum

发表于 2018-11-12 | 更新于 2020-05-17 | 分类于 管理

个体和互动高于流程和工具
工作的软件高于详尽的文档
客户合作高于合同谈判
响应变化高于遵循计划
也就是说,尽管右项有其价值,我们更重视左项的价值。

组织行为学

发表于 2018-11-12 | 更新于 2020-05-17 | 分类于 管理

战略管理

发表于 2018-11-12 | 更新于 2020-05-17 | 分类于 管理

信息系统项目管理师-介绍

发表于 2018-11-12 | 更新于 2020-05-17 | 分类于 管理

项目

项目是用有限的资源、有限的时间为特定客户完成特定目标的一次性工作。

资源:完成项目所需要的人、财、物。
时间:明确的开始和结束时间。
客户:提供资金、确定需求并拥有项目成果的组织或个人。
目标:满足要求的产品、服务和成果。

项目的特点:临时性、独特性和渐进性。

  1. 临时性
    临时性是指每一个项目都有确定的开始和结束。
    此外,临时性一般不适用于项目所产生的产品、服务或成果。大多数项目是为了得到持久的结果。
  2. 独特的产品、服务或成果
    独特是项目可交付成果的一种重要特征,例如,办公楼已经建造了成千上万做,单其中每一座都是独特的-不同的业主、不同的设计、不同的地点、不同的承建人等。
  3. 渐进明细
    渐进明细是项目逐步完成的过程。渐进明细意味着分步、连续的积累。例如,在项目的早期项目范围的说明是粗略的,随着项目团队对目标和可交付成果的理解更完整,更深入时,项目的范围也就更具体。
    项目规格说明书的渐进明细务必要与项目范围的恰当定义谨慎地协调起来。如果项目范围,即需要完成的任务规定的恰如其分,则在规格说明书的渐进明细过程中,项目范围仍应保持控制。

信息项目的特点

信息系统项目是根据用户需求,优选各种技术和产品,进行设计开发,将各个分离的“信息孤岛”连接成为一个完整、可靠、经济和有效的整体,并使之能彼此协调工作,发挥整体效益,达到整体优化的目的。

现在的信息系统已不只是为用户提供信息共享的功能,而是要整合各种资源,在满足用户需求的基础上,提高用户的投资效率、管理效率和经营效率,最终帮助用户获取更大的利润。

信息系统是以信息的集成为目标,功能的集成为结构,平台的集成为基础,人的集成为保证。不仅要在技术上实现客户的需求,还要对客户投资的实用性和有效性进行分析。开发商要向客户提供具有针对性的整合应用解决方案,这就要求开发商除了要有IT方面的技术外,必须要有较丰富的行业经验。

项目管理

项目管理就是把各种知识、技能、手段和技术应用于项目活动之中,以达到项目的要求。

管理一个项目包括:

  • 识别要求。
  • 确定清楚而又能够实现的目标。
  • 权衡质量、范围、时间和成本方面互不相让的要求,使技术规定说明书、计划和方法适用于各种各样利害关系者的不同需求与期望。

有效的管理要求项目管理组至少能理解和使用以下5方面的专门知识领域:

  • 项目管理知识体系。
  • 应用领域的知识、标准和规定。
    • 标准是“一直同意建立并由公认的机构批准的文件,该团队提供通用的和可重复使用的规则、指南、活动或其结果的特征,目的是在特定的背景下达到最佳的秩序。
    • 规则是政府强制的要求,它制定了产品、过程或服务的特征。建筑法规是规则的一个例子。
  • 项目环境知识。
  • 通用的管理知识和技能。
  • 软技能(处理人际关系技能)。

国际化组织以PMBOK(项目管理的知识体系)为框架,制定了ISO10006关于项目管理的标准。
包括,
动态的项目管理5大过程

  • 启动过程组。
  • 规划过程组。
  • 执行过程组。
  • 监控过程组。
  • 收尾过程组。

静态的项目管理9大知识领域

  • 范围管理。
  • 时间管理。
  • 成本管理。
  • 质量管理。
  • 人力资源管理。
  • 沟通管理。
  • 采购管理。
  • 风险管理。
  • 整体管理。

项目管理高级话题

在更广泛的背景下,项目管理包括相关的工作如大项目和项目组合管理。它们都应用了一些基本技能以管理项目,只是执行的层面不同。通常,战略计划、项目组合、大项目、项目、子项目是有层次的,大项目包含几个相关的项目,并共同为战略计划的完成做出贡献。

项目管理的产生和发展

产生的原因

  1. 适应现代产品的创新速度。
  2. 适应现代的复杂项目系统。
  3. 适应以用户满意为核心的服务理念。

    发展的方向

  4. 向学科化方向发展。
  5. 向实用化方向发展。

软件工程导论-总体设计

发表于 2018-11-12 | 更新于 2020-05-17 | 分类于 管理

1 总体设计
总体设计通常由两个主要阶段组成:
系统设计阶段,确定系统的具体实现方案;
结构设计阶段,确定软件结构。

总体设计的9个步骤:
1.设想供选择的方案
考虑各种可能的实现方案,一般将需求分析阶段得出的数据流图作为出发点,把数据流图中的处理分组的各种可能性,抛弃在技术上行不通的分组方法,余下的分组方法代表可能的实现策略,并且可以启示供选择的物理系统。
2.选取合理的方案
选取若干个合理的方案,通常至少选取低成本/中成本/高成本的3种方案。
每个方案准备四份资料(系统流程图,组成系统的物理元素清单,成本/效益分析,实现这个系统的进度计划)。
3.推荐最佳方案
从合理方案中,推荐一个最佳的方案,并且为这个方案制定详细的实现计划。
4.功能分解
从实现角度把复杂的功能进一步分解,一般说来,经过分解之后应该使每个功能对大多数程序员而言都是明显易懂的。功能分解导致数据流图的进一步细化。
5.设计软件结构
把模块组织成良好的层次系统,顶层模块调用它的下层模块以实现程序的完整功能。
6.设计数据库
7.制定测试计划
考虑测试问题,在设计的时候注意提高软件的可测试性。
8.书写文档
系统说明:主要内容包括用系统流程图描绘的系统构成方案,组成系统的物理元素清单,成本/效益分析;对最佳方案的概括描述,精化的数据流图,用层次图或结构图描绘的软件结构,用IPO图或其他工具简要描述的各个模块的算法,模块间的接口关系,以及需求、功能和模块三者之间的交叉参照关系等。
用户手册:根据总体设计阶段的结果,修改更正在需求分析阶段产生的初步的用户手册。
测试计划:包括测试策略,测试方案,预期的测试结果,测试进度计划等等。
详细的实现计划:
数据库设计结果:
9.审查和复查
技术审查,然后由客户从管理角度进行复查。

2 设计原理
2.1 模块化
模块是由边界元素限定的相邻程序元素的序列,而且有一个总体标识符代表它。按照模块的定义,过程、函数、子程序和宏等,都可作为模块。面向对象方法学中的对象是模块,对象内的方法也是模块。
每个程序都相应地有一最适当的模块数目M,使得系统的开发成本最小。当模块数目增加时每个模块的规模将减小,开发单个模块需要的成本确实减少了;但是,随着模块数目增加,设计模块间接口所需要的工作量也将增加。
2.2 抽象
抽象就是抽出事物的本质特性而暂时不考虑它们的细节。
由于人类思维能力的限制,如果每次面临的元素太多,是不可能产生精确思维的。处理复杂系统的唯一有效的方法是用层次的方式构造和分析它。
当考虑对任何问题的模块化解法时。可以提出许多抽象的层次。在抽象的最高层次使用问题环境的语言,以概括的方式叙述问题的解法;在较低抽象层次采用更过程化的方法,把面向问题的术语和面向实现的术语结合起来叙述问题的解法;最后,在最低的抽象层次用可以直接实现的方式叙述问题的解法。
2.3 逐步求精
逐步求精是人类解决复杂问题时采用的基本方法。为了能集中精力解决主要问题而尽量延迟对问题细节的考虑。
Miller法则:一个人在任何时候都只能把注意力集中在(7±2)个知识块上。
逐步求精是一种自顶向下的设计策略,按照这种设计策略,程序的体系结构是通过逐步精化处理过程的层次而设计出来的。
抽象与求精是一对互补的概念。抽象使得设计者能够说明过程和数据,同时却忽略了底层细节。求精则帮助设计者在设计过程中桌布揭示出低层细节。
2.4 信息隐藏和局部化
信息隐藏原理指出:应该这样设计和确定模块,使得一个模块内包含的信息(过程和数据)对于不需要这些信息的模块来说,是不能访问的。
局部化是指把一些关系密切的软件元素物理地放得彼此靠近。

3 耦合和内聚
耦合衡量不同模块彼此间相互依赖(连接)的紧密程度;内聚衡量一个模块内部各个元素彼此结合的紧密程度。
3.1 耦合
如果两个模块中的每一个都能独立地工作而不需要另一个模块的存在,那么它们彼此完全独立,这意味着模块间无任何连接,耦合程度最低。
数据耦合:低耦合,如果两个模块彼此间通过参数交换信息,而且交换的信息仅仅是数据,那么这种耦合称为数据耦合。系统中必须存在这种耦合,因为只用当某些模块的输出数据作为另一些模块的输入数据时,系统才能完成有价值的功能。
控制耦合:中等耦合,如果传递的信息中有控制信息(尽管有时这种控制信息以数据的形式出现),则称为控制耦合。
特征耦合:如果被调用的模块需要使用作为参数传递进来的数据结构中的所有元素,那么,把整个数据结构作为参数传递就是完全正确的。但是,当把整个数据结构作为参数传递而被调用的模块只需要使用其中一部分数据元素时,就出现了特征耦合。在这种情况下,被调用的模块可以使用的数据多余它确实需要的数据,这将导致对数据的访问失去控制,从而给计算机犯罪提供了机会。
公共环境耦合:当两个或多个模块通过一个公共数据环境相互作用时,它们之间的耦合成为公共环境耦合。公共环境耦合的负责程度随耦合的模块个数而变化,当耦合的模块个数增加时复杂程度显著增加。
内容耦合:最高程度的耦合。一个模块方位另一个模块的内部数据。一个模块不通过正常入口而转到另一个模块的内部。两个模块有一部分程序代码重叠(只可能出现在汇编程序中)。一个模块有多个入口(这意味着一个模块有几种功能)。
尽量使用数据耦合,少用控制耦合和特征耦合,限制公共环境耦合的范围,完全不用内容耦合。

3.2 内聚
内聚标识着一个模块内各个元素彼此结合的紧密程度,它是信息隐藏和局部化概念的自然扩展。
内聚和耦合是密切相关的,模块内的高内聚往往意味着模块间的耦合。内聚和耦合是进行模块化设计的有力工具,但是实践表明内聚更重要,应该把注意力集中到提高模块的内聚程度上。

低内聚有如下几类:
偶然内聚:如果一个模块完成一组任务,这些任务彼此间即时有关系,关系也是很松散,就叫做偶然内聚。
逻辑内聚:如果一个模块完成的任务在逻辑上属于相同或相似的一类。
时间内聚:如果一个模块包含的任务必须在同一段时间内完执行。

中内聚有两类:
过程内聚:如果一个模块内的处理元素是相关的,而且必须以特定次序执行。
通信内聚:如果模块中所有元素都使用同一个输入数据和(或)产生同一个输出数据。

高内聚有两类:
顺序内聚:如果一个模块内的处理元素和同一个功能密切相关,而且这些处理必须顺序执行。
功能内聚:如果模块内所有处理元素属于一个整体,完成一个单一的功能。

|功能内聚|10分|时间内聚|3分|
|顺序内聚|9分 |逻辑内聚|1分|
|通信内聚|7分 |偶然内聚|0分|
|过程内聚|5分 |||

4 启发规则
1.改进软件结果提高模块独立性。
2.模块规模应该适中。
3.深度、宽度、扇出和扇入都应适当。
4.模块的作用域应该在控制域之内。
5.力争降低模块接口的复杂程度。
6.设计单入口单出口的模块。
7.模块功能应该可以预测。

1…141516…22

Paul Jiang

212 日志
26 分类
26 标签
Links
  • 褚霸
  • 章亦春
  • Martin Fowler
© 2020 Paul Jiang
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Gemini v6.5.0