Java 程序员极速上手 go

举报
汪子熙 发表于 2024/04/05 20:40:57 2024/04/05
【摘要】 随着 Go 语言的流行,很多公司的技术栈在往 Go 上转,但很多招进来的后端开发工程师都是 Java 技术栈,然后在工作中边学边上手。那么 Java 程序员要想极速上手 Go,应该从哪些方面入手呢?对于已经有一定基础的 Java 工程师,可以思考自己以前用 Java 编程时,最常使用的语言特性,列一个清单出来。然后按照这个清单,去学习 Go 语言的对应实现方式,这样能够有针对性的的学习,有的...

随着 Go 语言的流行,很多公司的技术栈在往 Go 上转,但很多招进来的后端开发工程师都是 Java 技术栈,然后在工作中边学边上手。

那么 Java 程序员要想极速上手 Go,应该从哪些方面入手呢?

对于已经有一定基础的 Java 工程师,可以思考自己以前用 Java 编程时,最常使用的语言特性,列一个清单出来。然后按照这个清单,去学习 Go 语言的对应实现方式,这样能够有针对性的的学习,有的放矢。

下面是我使用 Java 进行日常工作中经常使用的5个编程要点,我会介绍这些要点对应的 Go 实现,仅供大家参考。

1. 并发编程(Concurrency)

Go语言通过goroutine和channel提供了轻量级的并发编程模型,与Java的线程模型相比更加简洁和高效。下面是一个简单的并发示例:

package main

import (
    "fmt"
    "time"
)

func main() {
    // 创建一个 go routine
    go sayHello("World")
    
    // 主 goroutine 打印 Hello
    sayHello("Gopher")
}

func sayHello(name string) {
    for i := 0; i < 5; i++ {
        fmt.Println("Hello", name)
        time.Sleep(time.Second)
    }
}

执行上述 go 代码,打印输出:

笔者另外一篇文章,曾经详细介绍过 Go 的并发编程,大家可以移步我这篇腾讯云社区文章:

通过三个例子,学习 Go 语言并发编程的利器 - goroutine

2. 内置并发安全的Map(Built-in concurrency-safe Map)

在Go语言中,内置了并发安全的map类型(sync.Map),可以在多个goroutine中安全地读写数据,而无需额外的锁。以下是一个使用sync.Map的示例:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var m sync.Map
    
    // 写入键值对
    m.Store("key", "value")
    
    // 读取键对应的值
    value, ok := m.Load("key")
    if ok {
        fmt.Println(value)
    }
}

执行上述代码,最后打印 value

这段代码是一个简单的Go语言程序,它演示了如何使用sync.Map进行并发安全的键值对操作。下面是逐行解释这段代码的含义:

  1. package main:声明这个文件属于main包,表明这是一个可执行程序的入口文件。

  2. import (:引入需要使用的外部包,这里使用了"fmt"和"sync"包。

  3. "fmt":引入了用于格式化输出的标准库包。

  4. "sync":引入了用于同步操作的标准库包。

  5. func main() {:定义了程序的主函数。

  6. var m sync.Map:声明了一个变量m,类型为sync.Map。sync.Map是Go语言提供的一种并发安全的map类型,可以在多个goroutine中安全地读写数据,而无需额外的锁。

  7. m.Store("key", "value"):向map m中存储键值对,键为"key",值为"value"。

  8. value, ok := m.Load("key"):从map m中读取键为"key"的值,如果键存在,则将值赋给变量value,并将ok设置为true;如果键不存在,则value为nil,ok为false。这里使用了多重赋值的方式。

  9. if ok {:判断变量ok是否为true,如果为true,说明键存在。

  10. fmt.Println(value):打印键"key"对应的值value。

  11. }:if语句的结束。

整个程序的逻辑很简单,就是先向sync.Map中存储了一个键值对,然后再从中读取出来并打印出对应的值。由于sync.Map是并发安全的,所以可以在多个goroutine中安全地进行这些操作。

3. 错误处理(Error Handling)

Go语言采用显式的错误处理机制,通过返回error类型来传递错误信息,而不是Java中的异常。下面是一个简单的错误处理示例:

package main

import (
    "errors"
    "fmt"
)

func divide(x, y int) (int, error) {
    if y == 0 {
        return 0, errors.New("division by zero")
    }
    return x / y, nil
}

func main() {
    result, err := divide(10, 0)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println("Result:", result)
}

执行之后的输出:

以下是对代码逐行解释的含义:

import (
    "errors"
    "fmt"
)

这里使用了import关键字引入了两个标准库包。errors包用于创建和处理错误信息,fmt包用于格式化输出。

func divide(x, y int) (int, error) {

这行代码定义了一个名为divide的函数。这个函数接受两个int类型的参数xy,并返回两个值,第一个值是int类型的商,第二个值是error类型的错误信息(如果有错误的话)。

if y == 0 {
    return 0, errors.New("division by zero")
}

在函数内部,首先检查y是否为零。如果y为零,就会调用errors.New函数创建一个新的错误,其中包含字符串"division by zero",然后返回0和该错误。

return x / y, nil

如果y不为零,则返回x / y作为商,并且返回nil表示没有错误发生。

func main() {

这行代码定义了程序的主函数。

result, err := divide(10, 0)

在主函数中,调用了divide函数,传入了参数10和0。result接收到divide函数返回的商,err接收到可能返回的错误。

if err != nil {

检查err是否不为nil,如果不为nil,说明函数调用过程中发生了错误。

fmt.Println("Error:", err)

如果有错误发生,使用fmt.Println打印错误信息。

return

然后通过return语句结束函数执行。

fmt.Println("Result:", result)

如果没有发生错误,则打印商的结果。

整体而言,这段代码展示了一个简单的除法函数,并演示了如何处理可能的错误情况。

4. defer和panic/recover(defer and panic/recover)

Go语言提供了defer关键字用于延迟执行函数调用,以及panic和recover机制用于处理异常。以下是一个使用defer和panic/recover的示例:

package main

import "fmt"

func recoverName() {
    if r := recover(); r != nil {
        fmt.Println("recovered from", r)
    }
}

func fullName(firstName *string, lastName *string) {
    defer recoverName()
    if firstName == nil {
        panic("runtime error: first name cannot be nil")
    }
    if lastName == nil {
        panic("runtime error: last name cannot be nil")
    }
    fmt.Printf("%s %s\n", *firstName, *lastName)
    fmt.Println("returned normally from fullName")
}

func main() {
    defer fmt.Println("deferred call in main")
    firstName := "John"
    fullName(&firstName, nil)
    fmt.Println("returned normally from main")
}

执行之后,打印的输出结果:

这段代码演示了在Go语言中如何使用deferpanicrecover来处理运行时错误。具体来说,它包含了以下几个关键点:

  1. recoverName()函数定义了一个延迟函数,它通过调用recover()函数来恢复从panic状态中返回的错误信息。如果recover()函数返回的值不为nil,则说明有panic发生,并且在该函数中进行了错误恢复处理。

  2. fullName()函数展示了如何在运行时可能出现错误的情况下使用panic来中断程序执行,并在发生错误时抛出错误信息。在这个函数中,首先使用defer关键字来延迟执行recoverName()函数,以确保无论是否发生panic,都能在函数退出时执行恢复处理。然后,通过检查传入的指针是否为nil来判断是否存在运行时错误,如果存在错误,就会触发panic,并且程序会跳转到最近的defer语句执行。

  3. main()函数是程序的入口点。在这个函数中,通过使用defer语句来延迟执行fmt.Println("deferred call in main"),确保该语句会在函数退出时被执行。然后定义了一个firstName变量,并调用了fullName()函数,将firstName的地址和一个nil指针作为参数传递给该函数。由于lastName参数为nil,因此会触发panic,并且程序会跳转到fullName()函数中执行错误处理。最后,fmt.Println("returned normally from main")语句会在main()函数正常返回时执行,但由于在调用fullName()函数时已经发生了panic,因此该语句不会被打印出来。

5. 结构体(Structs)和接口(Interfaces)

Go语言中的结构体和接口是非常灵活和强大的,可以有效地组织代码和实现多态性。下面是一个使用结构体和接口的示例:

package main

import (
    "fmt"
)

type Shape interface {
    Area() float64
}

type Rectangle struct {
    Width  float64
    Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14 * c.Radius * c.Radius
}

func printArea(s Shape) {
    fmt.Println("Area:", s.Area())
}

func main() {
    rect := Rectangle{Width: 5, Height: 3}
    circle := Circle{Radius: 4}
    
    printArea(rect)
    printArea(circle)
}

执行之后的代码输出:

这段代码演示了如何在Go语言中使用接口和结构体来实现多态性。具体来说,它包含了以下几个关键点:

  1. Shape接口:定义了一个Shape接口,该接口包含一个Area()方法,用于计算形状的面积。任何实现了Area()方法的类型都可以被视为Shape接口的实现者。

  2. Rectangle结构体:定义了一个Rectangle结构体,包含了WidthHeight两个字段,分别表示矩形的宽度和高度。此外,还实现了Area()方法,用于计算矩形的面积。

  3. Circle结构体:定义了一个Circle结构体,包含了Radius字段,表示圆的半径。同样,它也实现了Area()方法,用于计算圆的面积。

  4. printArea()函数:定义了一个printArea()函数,接受一个Shape类型的参数,并调用其Area()方法来打印形状的面积。

  5. main()函数:程序的入口点。在这个函数中,创建了一个Rectangle类型的变量rect和一个Circle类型的变量circle,并分别传递给printArea()函数进行面积打印。由于RectangleCircle类型都实现了Shape接口的Area()方法,因此它们都可以作为printArea()函数的参数,展示了多态性的特性。

以上只是五个笔者工作中最经常使用到的 Go 特性分享,祝各位 Java 程序员的 Go 转型之路一切顺利。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。