Go && java语言对比

前言

近期在学习go语言,这里简单总结一下它和java语言的不同之处,以便加深印象

1、静态类型 + 短变量自动推导

go语言是静态语言,但在编写时可以不显示指定类型,而根据短变量声明赋的值来决定类型

package main

import "fmt"

func main() {
    // 短变量声明(函数内):自动推导类型
    name := "Go"          // string
    age := 10              // int
    pi := 3.14             // float64
    isOk := true           // bool

    // var 声明也支持类型推导
    var score = 99.5       // float64

    fmt.Printf("name: %T, age: %T, pi: %T\n", name, age, pi)
}

2、类型后置

go的类型(变量、函数返回值、指针、切片)是后置的,跟java python有很大区别,要习惯

package main

import "fmt"

// 函数返回值类型后置
func add(a int, b int) int {
    return a + b
}

// 多返回值类型后置
func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, fmt.Errorf("division by zero")
    }
    return a / b, nil
}

func main() {
    // 变量类型后置
    var num int = 10
    var ptr *int = &num // 指针类型后置
    fmt.Println("num:", num, "ptr:", *ptr)
}

3、没有class,只有struct

go中没有类这个概念,仅有struct结构体的概念,可以用结构体 + 方法 + 组合实现面向对象,而非继承

package main

import "fmt"

// 定义结构体(替代 class)
type Person struct {
    Name string
    Age  int
}

func main() {
    // 初始化结构体
    p := Person{Name: "Alice", Age: 25}
    fmt.Printf("Person: %+v\n", p)
}

4、方法绑定到struct(Receiver)

没有类便没有成员方法这个概念,但可以将方法绑定到struct里,实现类似成员方法的功能

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

// 值接收者:不修改原对象
func (p Person) SayHello() {
    fmt.Printf("Hello, I'm %s\n", p.Name)
}

// 指针接收者:修改原对象
func (p *Person) Birthday() {
    p.Age++
}

func main() {
    p := Person{Name: "Bob", Age: 30}
    p.SayHello()
    p.Birthday()
    fmt.Printf("After birthday: %+v\n", p) // Age: 31
}

5、非侵入式接口(鸭子类型)

go的接口不需要显示(如java的implements关键字)实现,如果一个结构体绑定的方法包含了接口的所有方法,便可认为实现了接口,可以利用接口的多态特性

package main

import "fmt"

// 定义接口
type Speaker interface {
    Speak() string
}

// 定义 Dog 结构体
type Dog struct{}

// Dog 实现 Speak() 方法
func (d Dog) Speak() string {
    return "Woof!"
}

// 定义 Cat 结构体
type Cat struct{}

// Cat 实现 Speak() 方法
func (c Cat) Speak() string {
    return "Meow!"
}

// 多态:接收 Speaker 接口
func MakeSpeak(s Speaker) {
    fmt.Println(s.Speak())
}

func main() {
    dog := Dog{}
    cat := Cat{}
    MakeSpeak(dog) // Woof!
    MakeSpeak(cat) // Meow!
}

6、多返回值 + error错误处理

go的方法是支持多返回值的,一般会另外返回个Error来确定是否执行正常

package main

import (
    "errors"
    "fmt"
)

// 多返回值:(结果, error)
func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

func main() {
    res, err := Divide(10, 2)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println("Result:", res) // 5

    // 测试错误情况
    res, err = Divide(10, 0)
    if err != nil {
        fmt.Println("Error:", err) // Error: division by zero
        return
    }
}

7、goroutine + channel实现并发

go可以很方便的实现并发,利用go语句就能触发并发操作,利用channel可以实现协程间的通信,没有线程池概念,但可通过GMP调度器做线程管理

package main

import (
    "fmt"
    "time"
)

// goroutine 示例
func sayHello(name string) {
    fmt.Printf("Hello, %s!\n", name)
}

func main() {
    // 启动 5 个 goroutine
    for i := 0; i < 5; i++ {
        go sayHello(fmt.Sprintf("Goroutine %d", i))
    }

    // 等待 goroutine 执行(实际项目用 sync.WaitGroup)
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Main done")
}

8、go是只有值传递的,但是go中有指针,可以通过指针实现引用传递

package main

import "fmt"

// 值传递:拷贝参数
func modifyValue(x int) {
    x = 100
}

// 指针传递:修改原对象
func modifyPointer(x *int) {
    *x = 100
}

func main() {
    a := 10
    modifyValue(a)
    fmt.Println("After modifyValue:", a) // 10(未修改)

    modifyPointer(&a)
    fmt.Println("After modifyPointer:", a) // 100(已修改)
}

9、首字母大小写控制可见性

go没有显示的静态修饰符,其方法和变量,首字母大小的可视为导出的,可被外部包使用,首字母小写的,则只能被内部包使用

// 文件名:visibility.go
package main

import "fmt"

// 包级变量(类似 static)
var PackageVar = "I'm visible outside"

// 首字母大写:导出(public)
func ExportedFunc() {
    fmt.Println("ExportedFunc: visible outside")
}

// 首字母小写:私有(private)
func privateFunc() {
    fmt.Println("privateFunc: only visible in this package")
}

func main() {
    ExportedFunc()
    privateFunc()
    fmt.Println(PackageVar)
}

10、用切片替代List

go没有list的概念,其切片便对应java中的list,且可以和数组方便的做转换

package main

import "fmt"

func main() {
    // 1. 数组(固定长度)
    arr := [3]int{1, 2, 3}
    fmt.Println("Array:", arr)

    // 2. 切片(动态长度,类似 List)
    slice := []int{1, 2, 3}
    slice = append(slice, 4, 5) // 追加元素
    fmt.Println("Slice:", slice)

    // 3. 数组转切片
    sliceFromArr := arr[:]
    fmt.Println("Slice from array:", sliceFromArr)

    // 4. 切片截取(类似 subList)
    subSlice := slice[1:3]
    fmt.Println("Sub-slice:", subSlice)
}

11、泛型

package main

import "fmt"

// 泛型函数:支持任意类型
func PrintSlice[T any](s []T) {
    for _, v := range s {
        fmt.Println(v)
    }
}

func main() {
    intSlice := []int{1, 2, 3}
    stringSlice := []string{"a", "b", "c"}
    PrintSlice(intSlice)
    PrintSlice(stringSlice)
}

12、defer

go的defer会在函数退出前自动执行,代码更简洁

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    defer file.Close() // 函数退出前自动关闭文件
    fmt.Println("File opened successfully")
}

13、无需break的switch

go的switch支持多条件、类型switch,且默认不穿透

package main

import "fmt"

func main() {
    // 1. 多条件 switch
    num := 2
    switch num {
    case 1, 3, 5:
        fmt.Println("Odd")
    case 2, 4, 6:
        fmt.Println("Even")
    default:
        fmt.Println("Other")
    }

    // 2. 类型 switch
    var x interface{} = 10
    switch v := x.(type) {
    case int:
        fmt.Printf("Int: %d\n", v)
    case string:
        fmt.Printf("String: %s\n", v)
    default:
        fmt.Println("Unknown type")
    }
}
posted @ 2026-04-27 09:20  清酒令  阅读(13)  评论(0)    收藏  举报