版本 | 日期 | 备注 | |
---|---|---|---|
1.0 | 2023.5.15 | 文章首发 |
本文首发于泊浮目标掘金:https://juejin.cn/user/1468603264665335
0. 概要
最近因为业务须要在学Go语言,尽管之前也用过别的语言,但主力语言始终是Java。在这里也想次要想用Java做比照,写几篇笔记。
这篇次要是讲语言自身及较为外表的一些比照。
这里的比照用的是Java8,Go的版本是1.20.2。
1. Compile与Runtime
- 在动态、动静链接反对方面,两者雷同。
- Go在Runtime时,程序结构是关闭的。但Java并不是,基于Classloader的动静加载,实现许多灵便的个性,比方Spring,FlinkSQL。但这样做会让Java利用的Startup工夫更长。
- Java的Runtime久经打磨,也是面向长时间利用设计。
- Go间接编译成可执行文件,而Java是先编译成Class文件,而后JVM去解释执行。
有趣味的同学能够看我之前的的一篇笔记:《笔记:追寻云原生的Java》
2. 命名标准
- Go语言在变量命名标准遵循的是C语言格调,越简略越好。
- Java倡议遵循见名知意。
比方:
- 下标:Java倡议index,Go倡议i
- 值:Java倡议value,Go倡议v
- 我认为语言上偏简略的设计,则对工程师的能力要求更强。
3. 变量的零值反对
- 这点其实是Go在设计时,认为C在这下面有所有余——未被显式初始化的对象,其值是不确定的(当然有些编译器反对做初始化)。
Go对于变量默认值的形容是:当通过申明或调用new为变量调配存储空间,或者通过复合文字字面量或调用make创立新值,且不提供显式初始化时,Go会为变量或值提供默认值。
- 所有整型类型:0
- 浮点类型:0.0
- 布尔类型:false
- 字符串类型:""
- 指针、interface、切片(slice)、channel、map、function:nil
- 另外,Go的零值初始是递归的,即数组、构造体等类型的零值初始化就是对其组成元素逐个进行零值初始化。
- 比照Java,我认为这是个仁者见仁智者见智的事。default值并不一定是你想要的值,如果你遗记初始化了,在runtime时间接炸进去也是个好的抉择,而不是把问题藏起来。当然,对Go默认值相熟的人,这对他们来说能够少写一点代码,很难受。
4. 规范库对于工程能力的反对
- 无论是Format还是Test以及模块治理,Go都是开箱即用的,比拟难受。如果规范库确实很好用、社区的迭代能力强,那这是个坏事,当初看来就是。
- Java对于这块都是通过了长期的倒退,相干的工具链比拟成熟,相当于是物竞天择留下来的。
5. Composite litera(复合字面值)
可能没人听过这个说法,举几个例子:
m := map[int]string {1:"hello", 2:"gopher", 3:"!"}
复合字面值由两局部组成:一部分是类型,比方上述示例代码中赋值操作符右侧的map[int]string;另一部分是由大括号{}包裹的字面值。
在申明对象时,也有相似的用法:
// $GOROOT/src/net/http/transport.govar DefaultTransport RoundTripper = &Transport{ Proxy: ProxyFromEnvironment, DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, DualStack: true, }).DialContext, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second,}
Go举荐应用field:value的复合字面值模式对struct类型变量进行值结构,这种值结构形式能够升高构造体类型使用者与构造体类型设计者之间的耦合,这也是Go语言的习用法。
这个是真的很香,Groovy和Kotlin也有相似的反对,很方便使用。尤其是构造函数特地长的时候,你可能说builder也能够做到,但谁不想少写几行代码呢。
6. 对于编程范式的反对
- 在Java中,类是一等公民。反对继承、组合,对函数式编程有肯定的反对。
- 在Go中,函数是一等公民(多返回值),对于函数式编程反对较为齐备。对面向对象齐全通过组合来实现。
- 从传参是否能够传函数上,咱们就能够看出Go的反对比Java好。Java传参中,传一个函数,其实是通过一个匿名对象来传,而Go是真正的一个函数。Kotlin在这块绝对Java好了点,至多在编写时。
- 在Java中,你想写个工具函数,也要先申明一个类再写进去。略Verbose,其实这个类咱们不会把它new进去,只是为了放个函数,所以咱们写了个类。但理论用的时候,
XxxUtils.method
,后面的Xxx其实有肯定的揭示作用,能够作为一个上下文来猜想外面的逻辑。然而如果咱们在method里写分明,当然也能够做到同样的效用,所以这点来说Go是比拟难受的。 - Go的对象办法申明形式比拟非凡:
//申明一个类型type MyInt int//绑定一个办法//func前面的()里,相当于申明了这个办法绑定的类型。在Go语言里叫做recevier,一个函数只能有一个recevier,且不能是指针、接口类型。//不能横跨Go包为其余包内的自定义类型定义办法。func (i MyInt) String() string { return fmt.Sprintf("%d", int(i))}//在编译期,会把办法的第一个参数变成recevier。很简略的实现。有点像Koltin中的Extension Properties。
- Go的Interface是隐式的,只有你实现了对应的办法,就是这个Interface的实现。这个在一开始应用的时候会很不适应,但这个松耦的一种体现——隐式的interface实现会不经意间满足依赖形象、里氏替换、接口隔离等设计准则。
- Go并没有继承。相似的做法叫做类型嵌入(type embedding)的形式。简略来说就是你有一个T1,T2类型,他们有各自的办法,当你申明一个T类型时并蕴含了T1,T2类型的field,那么T就领有了T1,T2的办法。这个实现确实比继承难受多了,继承很容易写出一些坏滋味的代码。这是一种委派思维的实现(delegate)。JS中原型链从表面看起来也有点像这种。
- Go的形式反对多返回值。这点是比拟难受的,如果在Java中要返回多个值,就要思考封装成对象了,比方Tuple2,Tuple3...之类的,这在其余的一些JVM语言中随处可见。
7. 异样流:Error与Exception
- Go外面的error相当于Java的可检异样,Panic相当于Java的RuntimeException和Error。
- 如果你感觉Go外面大量的
if err != nil
让代码嵌套时,能够看看一些优化if else的技巧,比方我博客里有。 - 总的来说,像是在不同的实现做同一件事。也是个仁者见仁智者见智的事。
8. 并发
- Java用的POSIX原语义的线程。而Go是本人实现了一套用户态的线程,或者说叫协程。
- POSIX原语义的线程总体来说易用性没这么好,须要牢记一些知识点才能够防止踩坑。Go在这点上比拟敌对。
- 性能上,因为实际时个别Java会用线程池,所以创立、销毁的代价还好。其实Go也有本人的线程池,用线程去绑多个协程。但在上下文切换上,确实是POSIX原语义的线程代价会大点。
- 为了防止一个协程把线程独占住,在编译期、以及一些规范库API上都要做周密的设计。