骆贵 发表于 2025-6-2 21:37:40

Java程序员的Go入门笔记

系列文章目录和关于我
0.背景

3年java开发背景(因此这篇文章的特点是:比较适合java程序员doge),业余时间有了解过一些go,如今主要技术栈是go,此篇主要结合go语言圣经和团队内go项目,总结一些基础知识。
另外文章中没有对if,switch ,for进行说明,看多了文中的例子,自然就会这部分了
1.程序结构

1.1.声明、赋值和类型

Go语言主要有四种类型的声明语句:var、const、type和func,分别对应变量、常量、类型和函数实体对象的声明
以下是一个 Go 语言程序结构的代码示例:
// 结构体声明
type MyStruct struct {
    name   string
    age      int8
    sex      bool
    userType UserType
    sub      *MyStruct
    keys   []string
    featuremapany
}

// 类型
type UserType int64
// 常量声明
const (
    SUPER UserType = 1
)

func main() {

    // 常量声明
    const sexBool bool = false
    // 变量
    var userName string
    // 变量赋值
    userName = "username"

    //简短变量声明并赋值
    userAge := 1
    // map类型
    userFeature := make(mapany)
    // map赋值
    userFeature["key"] = "value"
    // 数组类型
    var keys []string = make([]string, 20)
    //数组类型赋值
    keys = "!"
    // 结构体
    var subStruct = MyStruct{age: 1, userType: 1, sex: false, name: "1"}
    myStruct := MyStruct{age: int8(userAge), userType: SUPER, sex: sexBool,
    name: userName, feature: userFeature, sub: &subStruct, keys: keys}
   
}1.2.包和文件

包的概念和java中的包类似,不同的是导入包路径使用的是“/”进行分割
package main

import (
    "errors"
    "fmt"
)
import "code.byted.org/life_service/chenxinglandingdemo/compare" // 导入自定义的包该包中有一个文件int_comparator.go,定义了一个Compare函数
type IntPriority interface {
    GetPriority() int
}

func Compare(first IntPriority, second IntPriority) (int, error) {
    if first == nil || second == nil {
       return 0, errors.New("null input")
    }
    firstPriority := first.GetPriority()
    secondPriority := second.GetPriority()
    if firstPriority == secondPriority {
       return 0, nil
    }
    if firstPriority > secondPriority {
       return 1, nil
    }
    return -1, nil
}引入包后的使用例子
compare.Compare(&myStruct, &subStruct)其中大写开头的方法才被视为是public的,这点一开始让我觉得有点草率,但是似乎确实比java的private,protected,public用起来更爽一点
使用init方法可以对包文件的初始化,有点类似java类中的static静态代码块,但是无论该文件是否被使用,都会执行init,不像java只有使用到这个类执行类的初始化后才会执行static
package compare

import "fmt"

func init() {
    fmt.Println("init_method_learn.go")
}init方法的执行是依赖文件名称顺序的,因此如果A文件使用B文件中的方法是,也许B文件还没有调用init
2.基本数据类型

2.1 整形

Go语言同时提供了有符号和无符号类型的整数运算。这里有int8、int16、int32和int64四种截然不同大小的有符号整数类型,分别对应8、16、32、64bit大小的有符号整数,与此对应的是uint8、uint16、uint32和uint64四种无符号整数类型。
这里需要注意的是,int 和 uint 类型的大小取决于操作系统的位数。在32位操作系统中,int 和 uint 类型的大小通常为4字节(32位);在64位操作系统中,它们的大小通常为8字节(64位)。我看我们系统中一般是没有直接使用int和unit的。
2.2 浮点数

Go语言提供了两种精度的浮点数,float32和float64。它们的算术规范由IEEE754浮点数国际标准定义,该浮点数规范被所有现代的CPU支持。
和其他语言一样,float32和float64都有一定精度误差
var f float32 = 16777216
// true
fmt.Println(f+1 == f)

var f64 float64 = 16777216
// true
fmt.Println(f64+0.000000000000001 == f64)在 IEEE 754 标准中,float32 类型的浮点数使用 32 位来存储,其中包括 1 位符号位(S)、8 位指数位(E)和 23 位尾数位(M)。对于数字 16777216,将其转换为二进制表示为 1000000000000000000000000。将其转换为 IEEE 754 格式的 float32 类型,具体步骤如下:

[*]符号位 S:16777216 是正数,所以符号位 S 为 0。
[*]指数位 E:将二进制数 1000000000000000000000000 右移 23 位,得到指数为 24。由于指数位的偏移量为 127,所以实际的指数值为 24 - 127 = -103。
[*]尾数位 M:取二进制数 1000000000000000000000000 的后 23 位,即 00000000000000000000000,省略掉开头的 1。
因此,16777216 在 IEEE 754 存储中的表示为:0 10000001 00000000000000000000000。
对于数字 16777217,按照同样的步骤转换为 IEEE 754 格式的 float32 类型:

[*]符号位 S:16777217 是正数,所以符号位 S 为 0。
[*]指数位 E:将二进制数 1000000000000000000000001 右移 23 位,得到指数为 24。由于指数位的偏移量为 127,所以实际的指数值为 24 - 127 = -103。
[*]尾数位 M:取二进制数 1000000000000000000000001 的后 23 位,即 000000000000000000000001,省略掉开头的 1。
所以,16777217 在 IEEE 754 存储中的表示也是:0 10000001 00000000000000000000000。
这就是为什么 16777216 和 16777217 在 IEEE 754 存储中看起来是一样的原因,它们的尾数位相同,而指数位也相同,只是在转换为十进制时,由于精度限制,会出现舍入误差,导致结果略有不同。
和java一样go也提供big来解决这个问题
// 使用 math/big 包进行高精度计算
a := new(big.Float).SetPrec(128).SetFloat64(0.000000000000001)
b := new(big.Float).SetPrec(128).SetFloat64(16777216)
c := new(big.Float).Add(a, b)

// 输出1,表示c大于b,符合预期
fmt.Println(c.Cmp(b))2.3 bool 布尔

类似于java中的boolean,在 Go 语言中,布尔型数据只有true和false两个值,没有像 Java 中那样的Boolean类型。
2.4 字符串

func main() {

    str := "testStr测试字符串"

    // 输出22 ===>7+15(汉子3个字符)
    fmt.Println(len(str))
    // 输出116 utf8编码
    var u uint8 = str
    fmt.Println(u)
    // 输出116 utf8编码
    fmt.Println(str)

    // testStr
    fmt.Println(str)
    // testStr
    fmt.Println(str[:7])
    //测试字符串
    fmt.Println(str)
    // 测试字符串
    fmt.Println(str)
   
    // 字符串循环
    for i := range str {
       fmt.Println(string(str))
    }
   
}如上是字符串常用的一些操作,go中的字符串也是不可变的
让我比较迷惑的是str这种字符串的操作似乎是深拷贝,但是有一些文档也说是浅拷贝
3.复合数据类型

3.1 数组

// 声明,然后赋值
ints := int{}
ints = 1
// 声明并赋值,没有赋值的index使用初值0
var ints2 int = int{1, 2, 3, 4, 5}
fmt.Println(ints2)
fmt.Println(len(ints2))

// 根据元素的个数来自动设置长度
ints3 := [...]int{1, 2, 3, 4, 5}
fmt.Println(len(ints3))

ints4 := [...]int{99: -1}

// 长度为100 = 99+1
fmt.Println(len(ints4))

ints5 := int{1, 2, 3, 4, 5}
// true
fmt.Println(ints3 == ints5)其中我觉得比较有意思的是

[*]数组的比较:
如果一个数组的元素类型是可以相互比较的,那么数组类型也是可以相互比较的,这时候我们可以直接通过==比较运算符来比较两个数组,只有当两个数组的所有元素都是相等的时候数组才是相等的。不相等比较运算符!=遵循同样的规则。

[*]数组类型
int和int是不同的类型
因此两个不太类型的数组是不可以比较的

以及也不能把int类型的变量使用=赋值一个int 类型的变量

<ul>数组作为参数<ul>
数组作为参数是值传递,这意味着下面clearArray1方法是无法清空数组内容的,使用指针的clearArray2是可以清空的(这也启发我们,比较占用内存的变量应该使用指针,因为指针作为参数值拷贝浪费的内存少,拷贝的是指针对象!)

[*]func clearArray1(array int) {
    for i := range array {
       array = 0
    }
}

func clearArray(array *int) {
    for i := range array {
       array = 0
    }
}
因为int和int是不太同的数组类型,因此上面两个函数,都只能穿入长度为5的数组,这一点让我觉得有点恶心
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Java程序员的Go入门笔记