乐趣区

关于go:一名Java开发学习的Go语言学习笔记一

版本 日期 备注
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.go
var 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 上都要做周密的设计。
退出移动版