java 到底要不要收费

随着JDK 11发布, Oracle同时调整了JDK的授权许可证,里面包含了好几个动作。 首先, Oracle从JDK 11起把以前的商业特性全部开源给OpenJDK,这样OpenJDK 11和OracleJDK 11的代码和功能,在本质上就是完全相同的(官方原文是Essentially Identical)。 然后, Oracle宣布以后将会同时发行两个JDK:一个是以GPLV2+CE协议下由Oracle发行的OpenJDK (本书后面章节称其为Oracle OpenJDK) ,另一个是在新的OTN协议下发行的传统的OracleJDK。 这两个JDK共享绝大部分源码,在功能上是几乎一样的,核心差异是前者可以免费在开发、测试或生产环境中使用,但是只有半年时间的更新支持;后者个人依然可以免费使用,但若在生产环境中商用就必须付费,可以有三年时间的更新支持。 如果说由此能得出"Java要收费"的结论,那是纯属标题党,最多只能说Oracle在迫使商业用户要么不断升级JDK的版本,要么就去购买商业支持。 JDK选择建议 JDK8 分情况 8u211和之后的版本商用需付费,JDK 9/10 免费 JDK 11及以上所有版本商用需付费 免费建议:JDK8 使用 8u202 版本,JDK8 以上版本使用 OpenJDK 或大型机构替代版本Zulu JDK 、Amazon Corretto JDK等 说明 Java 的版本发布周期变更为每六个月一次 , 每半年发布一个大版本,每个季度发布一个中间特性版本,Java 9 和 Java 10 这两个被称为“功能性的版本”,两者均只提供半年的技术支持,Java 11 不仅提供了长期支持服务,还将作为 Java 平台的参考实现。Oracle 直到2023年9月都会为 Java 11 提供技术支持,而补丁和安全警告等扩展支持将持续到2026年。所以,Java11 必将是下一代长期使用的版本。

February 24, 2021

Neovide

neovide 是一个简单的 neovim 用户图像工具,相对于 Neovim 会有一些界面的提升的,但它的功能类似于终端用户界面。 neovide 的官网:https://github.com/Kethku/neovide。 gitbhub上有更多关于它的安装及使用教程。

February 16, 2021

1.1 Go1的诺言

1.1 Go1的诺言 Go语言从2007年开始设计,2009年底正式开源,而第一个正式的版本Go1则是在2012年上半年发布。Go1的语法变迁主要从Go第一个正式版本开始,Go1是Go语言官方对整个社区的承诺:Go1之后的版本将保证源代码层面兼容。 从Go1的发布日志可以发现,Go1对语言和标准库做了严谨的梳理和完善,Go1的重大变化主要集中在语言和标准库部分。其中语言部分最大的变化是将原先的os.Error接口用内置的error接口类型替代、内置函数引入了close函数用于管道的关闭操作。此外,还对加强了init、append内置函数;增强了复合类型字面值的支持;针对Unicode字符增加了rune别名;改善了对map的遍历和删除元素操作;改进了影子返回值的报警提示;复制结构体时涵盖未导出的成员;明确了哪些类型可以进行相等性测试。同时对每个标准库的路径和功能进行详细的设计和完善。 经过多年的发展和普及,大家已经对Go1语言和标准库耳熟能详,对变更的细节就不详细展开了。但正是这次梳理工作才奠定了Go1之后高速发展的十年。

January 22, 2021

1.2 Go1到Go1.10

1.2 Go1到Go1.10 因为Go1承诺,Go1后序的版本都保持了向前兼容的目标。不过在从Go1发展到Go1.10的过程中,语言依然是增加了一些新的特性。本节我们简单回顾Go1到Go1.10的变化。 1.2.1 Go1.2(2013年12月) Go1.2最大的语言变化是切片操作时,可以设置新切片的容量。这个需求在Go1之前就被提出了,但是因为Go1修改工作较大而延期到了Go1.2才被实现。 比如下面的代码: var a = make([]int, 10) var b = a[i:j:k] 其中b切片是从a切片的第i个元素开始到第j个元素前结束,b切片的容量为k(k指定的容量不能超出a切片的容量)。 为了配合切片语法的变更,reflect包也增加了相应的方法: func (v Value) SetCap(cap int) func (v Value) Slice3(low, high, max int) Value 其中Value.SetCap只调整切片的容量,和a[::cap]写法效果等价。而Value.Slice3在进行切片操作的同时也指定新切片的容量,和a[low:high:max]写法效果等价。 通过限制子切片的容量,可以将不同子切片进行安全的分割,避免子切片无意越界操作其它切片空间。 1.2.2 Go1.4(2014年12月) Go1.4语言部分对for语法进行了加强。在Go1.3之前for只有下面两种写法: for i, v := range x { // ... } for i := range x { // ... } for range针对要循环变量类型的不同,产生的循环变量也有差异。在第一种写法中,如果要循环的是数组或切片类型则i和v分别表示索引的下标和元素的值,如果循环的类型是map类型时则i和v分别表示键和值,这种写法不能用于管道类型变量的迭代。而第二种循环也可以用管道变量的迭代,直到管道被关闭时结束。如果用第二种方式循环遍历数组或map,则和for i, _ := range x {}的写法相关相同,相当于忽略的要迭代的值。 但是有时候我们仅仅是要循环几次而并不关心循环变量的值,在Go1.3之前可以这样写: var times [5][0]int for i := 0; i < len(times); i++ { // ... } for _ = range times { // ... } 前一种方式采用传统的for循环方式遍历,而后一种方式采用for range遍历,但是获取了每次遍历到的值。 在Go1.4中,后一种方式可以省略掉前面的垃圾桶变量,像这样写: var times [5][0]int for range times { // ... } 其中times对应一个[5][0]int类型的数组,虽然第一维数组有长度,但是数组的元素[0]int大小是0,因此整个数组占用的内存大小依然是0。没有付出额外的内存代价,我们就通过for range方式实现了times次快速迭代。...

January 22, 2021

1.3 Go1.10过渡到Go2

1.3 Go1.10过渡到Go2 回顾前一节中Go1到Go1.10的语法变化可发现,在Go1之后除了Go1.2的切片语法完善和Go1.9的类型别名很少有重量级的语法特性加入。这是因为Go1.2之后Go语言的语言设计基本是被冻结了,涉及到语言变化的Issue原则上必须通过Russ Cox等核心成员的同意才会被考虑。但是从Go1.10开始,官方终于开始启动Go2.0的语言前期设计工作。首先是官方开始提交模块化的草案,其次是很多呼声较高的语法糖特性开始被考虑逐步加入。可以说Go1.10之后进入了后Go1.0时代,它要在Go2.0到来之前解逐步完善细节问题,从而最终能给轻装上阵开始进入Go2.0开发流程。 1.3.1 Go1.11(2018年8月) 在Go语言的发展史中,2018年注定是一个重要的时间点,因为在2018年8月正式发布了Go1.11。Go1.11语言部分虽然没有变化,但是带来了3个重量级的更新:一是amd64平台完全支持AVX512高性能的指令集;二是Go1.11开始支持模块化的特性;三是Go语言开始WebAssembly平台。这几个个改进将成为后Go1时代最大的亮点。 首先AVX512高性能的指令集可以让Go语言榨干CPU的全部性能,为Go2进军运算密集型应用做好准备(如果对Go汇编语言感兴趣,可以参考作者的《Go语言高级编程》中汇编语言相关的章节)。而模块是管理任何大型工程必备的工具,但是Go语言发布十年来一直缺乏官方的模块化工具。模块化的特性将彻底解决大型Go语言工程的管理问题,至此Go1除了缺少泛型等特性已经近乎完美。最后,WebAssembly作为一个Web汇编语言和虚拟机标准,Go语言可能为Web开发打开一个崭新的领域。 关于模块和WebAssembly都是较大的主题,它们目前都还是作为实验特性,希望在后序版本中逐步完成。在本书将有专门的章节讨论模块和WebAssembly相关的技术。 1.3.2 Go1.12(2019年2月) Go1.12并没有增加新的语言特性,但是官方正式决定在下个Go1.13版本中删除对二进制包的支持。二进制包的是在Go1.7版本作为实验性的特性引入的,一个包可以以类似C语言静态库的方式采用二进制包发布,从而避免公布源代码。在Go语言不支持二进制包特性的时候,社区对这个需求呼声甚高,但是当官方真正支持了之后却根本没有人使用。Go语言官方删除二进制包的决定也是Go的“少即是多”的哲学决定的,而正是这种极度的克制的基因才造就了目前的Go语言成功。 1.3.3 Go1.13(2019年8月) 从Go1.13开始,从Go1.11开始酝酿的诸多决定终于开始逐个生效:首先是模块化将成为默认的特性,彻底告别GOPATH时代;其次是不是太重要的二进制特性将被废除;最后是语言的进化将再次启动,一些细微的语言特性将在Go2到来之前被提前实现。 最有可能被优先实现的特性有:数字支持下划线分割的特性,以便于书写更容易阅读的数字,比如100000000可以写为1_0000_0000;其次Go语言将引入二进制的数字字面值,比如0b00001111是以二进制格式书写的整数。这虽然是很小的语法糖改进,但是标志了Go2语言的开发工作正逐步进入开发计划。

January 22, 2021

1.4 Go2诞生

1.4 Go2诞生 在2018年官方已经发布了Go2的设计草案,其中包含了令人惊喜的泛型和错误等诸多改进,在后Go1时代过去之后将是新兴的Go2时代。需要说明的是,Go2的诞生并不表示Go1被抛弃!如何避免Py3k的笑话正是Go2第一要考虑的问题,因此才会有Go1.11到Go2逐步过段的阶段。而Go语言官方也已经通过博文承诺Go2将保持对Go1软件资产的最大兼容,鉴于Go1诺言被忠实地执行的参考,我们有理由相信Go2会处理好Go1资产的兼容性问题。 大约在2012年前后,作者曾乐观估计Go2将在2020年前后到来,并可能带来大家期盼已久的泛型特性。作者在此预测Go2将在2020年正式进入开发流程,并在2022年前后进入工业生产环境使用,而Go1将在2030年前后逐渐退出历史。为了在Go2正式到来时轻装上阵,我们需要提前把握Go语言的发展动向,而本书正是为此目标准备。

January 22, 2021

2.1 Go1的包机制

2.1 Go1的包机制 同⽬录Go源⽂件的集合构成包,而同⽬录下⼦⽬录对应的包的集合构成模块(暂不考虑子模块)。因此,要了解模块之前需要先了解包。 2.1.1 包是最小链接单位 Go语言是一种编译型的语言,链接的最小单位是包,对应go/ast.Package类型,多个包链接为一个可执行程序。在当前的官方实现中,一个包一般对应一个路径目录下的全部的Go语言源文件,而目录的路径包含了表示包的唯一路径名。Go语言的规范中包只是一个抽象的概念,并不要求包一定是以目录的方式展现,在未来包或者是模块也可能以压缩文件的方式展现。 包是Go语言应用编译和链接的基本单位,因此模块最终的目的是为了管理这些包的版本。 2.1.2 包目录布局的演变 在Go语言刚刚开源、还没有GOPATH环境变量之前,Go语言标准库的包全部是放在$(GOROOT)/src目录之下的,比如标准库中的image/png包对应$(GOROOT)/src/image/png目录。而第三方的包也可以放在$(GOROOT)/src目录,这时候Go语言的构建工具是不区分标准库的包和第三方包的。此外第三方或自己的应用对应的包,可以放在$(GOROOT)目录之外的任意目录。 比如可以在任意目录创建一个hello.go文件: package hello import "fmt" func PrintHello() { fmt.Printf("Hello, 世界\n") } 然后在同级的目录创建一个Makefile文件用于管理构建工作: include $(GOROOT)/src/Make.inc TARG=github.com/chai2010/go2-book/ch2/hello GOFILES=./hello.go include $(GOROOT)/src/Make.pkg 第一个语句包含构建环境,最后的语句表示这是一个包。最重要的TARG变量定义了包的路径,而GOFILES则表示了包由哪些Go文件组成。然后执行make nuke就可以编译生成$(GOROOT)/pkg/github.com/chai2010/go2-book/ch2/hello.a文件。当另一个包要导入hello包时,实际上是从$(GOROOT)/pkg/github.com/chai2010/go2-book/ch2/hello.a文件读取包的信息。 在Go1之前的史前时代,一切包都是手工构建安装的,因此包的版本管理也是手工的方式进行。如果需要使用社区开源的第三方包,需要手工下载代码放到合适的目录正确编译并安装之后才可以使用。 为了将标准库的包和第三方的包分开管理,避免第三方包代码污染$(GOROOT)/src和$(GOROOT)/pkg目录,Go语言引入了GOPATH环境变量。GOPATH环境变量对应的目录用于管理非标准库的包,其中src用户存放Go代码,pkg用于存放编译后的.a文件,bin用于存放编译后的可执行程序。此后直到Go1.10,Go语言所有和包版本管理相关的工作都是基于GOPATH特性展开。 2.1.3 GOPATH特性的扩展 扩展GOPATH的目标都是为了更方便管理包。Go语言的包有三种类型:首先是叶子包,此类包最多依赖标准库,不依赖第三方包;其次是main包表示一个应用,它不能被其它包导入(单元测试除外);最后是普通的依赖第三方包的非main包。比较特殊的main包同时也是一个叶子包。 叶子包自身很少会遇到版本管理问题,因为不会遇到因为依赖第三方包产生的各种问题,因此叶子包的开发者很少关注版本管理的问题。稍微复杂一点的是main包,main包是包依赖中的根包。main包不担心被其它包依赖,因此它其实是可以通过一个独占的GOPATH来维护所有依赖的第三方包的。最复杂的是既不是main包,也不是普通的叶子包,因为普通包需要管理其依赖的第三方包,同时一般又不能单独管理GOAPTH。在vendor出现之前的版本管理实践中,普通包的版本比较简陋,很多普通包甚至都没有版本管理,只有master一个最新版本。 GOPATH的扩展主要分为横向和纵向两个方向。横向就是同时并列维护多个GOPATH目录,通过手工方式调整其中某些目录来实现目录中包版本切换的目的。纵向扩展一般在管理main包对应的应用程序中使用,通过在包内部创建临时的GOPATH子目录,在GOPATH子目录中包含全部第三方依赖的拷贝来实现外部依赖包版本的管理。 社区中早期出现的Godeps工具就是通过在当前目录下创建Godeps/_workspace子目录来管理维护依赖的第三方包的版本。Godeps的实践成果最终被吸收到来Go语言中,通过vendor机制实现来main包的依赖管理(不是版本管理)。但是最终vendor机制也带来了各种问题(稍后的章节会讨论),最终官方完全重新设计了模块化的特性。 2.1.4 模块化之后的包目录路径 通过vendor机制来实现版本管理的尝试虽然失败了,但是通过横向扩展GOPATH的来维护同一个包的不同版本的思路却在模块中复活了。模块化通过重新组织目录结构,实现了同时管理同一个包的不同版本需求。 比如之前$(GOPATH)/src/github.com/chai2010/pbgo包的1.0.0版本,在模块化之后将对应$(HOME)/go/pkg/mod/github.com/chai2010/pbgo@1.0.0。模块化通过$(HOME)/go/pkg/mod目录管理第三方的依赖包,同时通过pkg@x.y.z的版本后缀来区分同一个包的不同版本。这其实和多个GOPATH并列存放的思路是类似,不过模块化对多版本支持的更加完美。

January 22, 2021

2.2 基于vendor的版本管理

2.2 基于vendor的版本管理 2.2.1 vendor的工作机制 2.2.1 vendor的优势 TODO 2.2.1 vendor的问题

January 22, 2021

2.3 模块的设计⽬标

2.3 模块的设计⽬标 2.3.1 模块的概念 2.3.2 不同时间可重现构建 2.3.3 不同环境可重现构建 2.3.4 语义化版本号 2.3.5 最⼩化版本选择 TODO

January 22, 2021

2.4 模块快速⼊⻔

2.4 模块快速⼊⻔ 2.4.1 快速入门 2.4.2 go get重新⼊⻔ 2.4.3 go.mod⽂件 2.4.4 go.sum⽂件 2.4.5 版本切换 2.4.3 go mod命令 TODO

January 22, 2021