Go基础语法 - 指南

数据类型

基本类型:

  • bool:布尔值(true/false),与 Java 的 boolean 类似。
  • string:字符串,不可修改,比 Java 的 String 更轻量。
  • int / int8 / int16 / int32 / int64:整数类型,类似 Java 的 int、long 等。
  • uint / uint8 / uint16 / uint32 / uint64:无符号整数,Java 中没有无符号整型。
  • byte:uint8 的别名,常用于处理字节,类似 Java 的 byte,但 Java 的 byte 是有符号的。
  • rune:int32 的别名,用于 Unicode 字符,类似 Java 的 char(但 Java 的 char 仅 16 位)。

浮点与复数:

  • float32 / float64:浮点数,对应 Java 的 float/double。
  • complex64 / complex128:复数类型,Java 没有内置复数。

变量定义和声明

用int举例

1. 声明:
var a int;自动赋零值(类似java默认值)
2. 声明并赋值:
var a int = 10
3. 多变量声明并赋值:
var a, b int = 3, 5
4. 类型推断:(常用)
a := 10          // 自动推断为 int,Go 的 int 随平台变化(32 或 64 位)

常量定义

与 Java 对比:

  • Java 用 final int A = 10;
  • Go 的 const 更强:必须是编译时常量,不能依赖运行时函数。
    如:
const a = 10

输入输出(打印到控制台)

推荐:
- fmt.Println:功能最全、可读性最好,常规开发首选。
- fmt.Printf("%s", "hello world")格式化输出
- fmt.Println("aa", "bb")用空格拼接并换行
- fmt.Print("aa", "bb")直接拼接
性能:
- os.Stdout.WriteString:底层高性能输出,没有格式化。
不建议:
- println:仅用于调试,不推荐在正式代码中使用。

条件控制

if

和java的差不多,就是去掉了if后面的括号

func main() {
a := 1
if a == 1 {
fmt.Print(1)
} else if a == 2 {
fmt.Print(2)
} else {
fmt.Print(3)
}
fmt.Print("aa", "bb")
}

switch

和java的差不多,就是去掉了switch后面的括号,但是这个默认只执行一个
注意:

  1. Go 的 switch 默认不会继续执行下面的,比如命中了case 1,后面的case2,等不会执行
  2. 如果像Java一样往下执行,加一个fallthrough关键字,这样命中case1,会执行后面的case2,但是case3不会执行
switch a {
case 1:
fmt.Print(1)
fallthrough
case 2:
fmt.Print(2)
case 3:
fmt.Print(3)
default:
fmt.Print("default")
}

循环控制

for

在Go中,有且仅有一种循环语句:for,Go抛弃了while语句,for语句可以被当作while来使用。
正常用法和java差不多,就是去掉了for后面的括号
也是break跳出循环,continue结束当前循环

for i := 0; i < 10; i++ {
fmt.Println(i)
}

while用法:

i := 0
for i < 10 {
fmt.Println(i)
i++
}
// 死循环写法
for {
fmt.Println(1)
}

for range

和java中的foreach一样可以遍历数组,切片等

str := "hello world"
for i, s := range str {
fmt.Println(i, string(s))
}
i是下标,s是具体字符

派生类型

o 的“派生类型”(Derived Types)指由基本类型组合出来的类型

数组

数组是定长的,大小一旦确定无法更改,和java中的数组一样

func test4() {
arr := [5]int{1, 2, 3}
fmt.Print(arr)
}
//输出 [1 2 3 0 0],所以可以看到未赋值的区域用类型默认取补全

Go中的数组是值类型,也就是说数组是一个单独的类型,并不是指向头部元素的指针

也就是说赋值操作时完全拷贝
func test4() {
arr1 := [5]int{1, 2, 3}
arr2 := arr1
arr1[0] = 9
fmt.Println(arr1)
fmt.Println(arr2)
}
输出是:
[9 2 3 0 0]
[1 2 3 0 0]

获取长度和容量:数组的长度等于容量

len(arr1)
cap(arr1)

切割数组的格式为arr[startIndex:endIndex],切割的区间为左闭右开,例子如下:

nums := [5]int{1, 2, 3, 4, 5}
nums[1:] // 子数组范围[1,5) ->1 2 3 4
nums[:5] // 子数组范围[0,5) -> 0 1 2 3 4
nums[2:3] // 子数组范围[2,3) -> 2
nums[1:3] // 子数组范围[1,3) -> 1 2

切片

切片是不定长的,可以理解为java中的List,会自动扩容
说白了就是没有指定长度的数组
切片的底层实现依旧是数组,是引用类型,可以简单理解为是指向底层数组的指针。
定义切片:

var nums []int // 值
nums := []int{1, 2, 3} // 值
nums := make([]int, 0, 0) // 值
nums := new([]int) // 指针
//添加一个元素
nums = append(nums , 3)

推荐使用make,参数解释:
参数类型
长度: 切片中元素数量大小
容量:切片的实际长度
比如:

func test5() {
sl := []int{1, 2}
sl = append(sl, 3)
fmt.Println(len(sl))// 3
fmt.Println(cap(sl))// 4
}

为什么会这样,笔者这里解释下:
切片在 append 时容量不够 才会扩容,上面的切片初始化只有容量只有,添加了一个,容量扩容到了4,但是里面只有3个元素,所以长度为3
扩容机制大概如下

如果当前容量 < 1024:
扩容为原先的 2 倍
如果容量 ≥ 1024:
每次增长约 1.25 倍(+25%

所以:append可能返回原来的切片地址也可能返回一个新的切片地址,如果没有扩容就返回原来的切片地址,如果扩容了就返回新的切片地址

删除切片中指定下标的元素:

nums = append(nums[:i], nums[i+1:]...)

拷贝切片:

func test5() {
sl1 := []int{1, 2}
sl2 := []int{4}
copy(sl2, sl1)
fmt.Println(sl2)
}
//输出 1
切片在拷贝时需要确保目标切片有足够的长度,否则只会拷贝目标容量大小的内容

注意:如果一个切片是从数组中切出来的,那么这个切片的容量是这个切片的第一个元素在原数组中的位置到原数组最后一个元素的长度如:

s1: [1 2 3 4 5 6 7 8 9]
s2 := s1[3:4]cap=6

拓展表达式:这个就不展开说了,可以问问ai
s[low:high:max] 的 max 用来手动限制切片的 cap。
如果 cap 很小,append 会强制扩容,从而避免对原切片的底层数组造成影响。

字符串

在Go中,字符串本质上是一个不可变的只读的字节数组,也是一片连续的内存空间。
普通字符串:普通字符串由""双引号表示,支持转义,不支持多行书写

"这是一个普通字符串 \n"

原生字符串:原生字符串由反引号表示,不支持转义,支持多行书写,原生字符串里面所有的字符都会原封不动的输出,包括换行和缩进。

`
这是一个原生字符串,换行
tab缩进,\t制表符但是无效,换行
"这是一个普通字符串"
`

拼接:效率比较慢

s1 := "hello"
s2 := s1 + "world"
fmt.Println(s2)

高性能要求使用strings.Builder,类似于java中的StringBuilder

func test7() {
s1 := strings.Builder{}
s1.WriteString("hello")
s1.WriteString("world")
fmt.Println(s1.String())
}

映射表(就是map)

声明和初始化

// 声明并初始化
mp := map[int]string{
0: "a",
1: "a",
}
//声明类型与初始容量
mp := make(map[string]int, 8)
// 访问0对应的value
fmt.Println(mp[0])

注意:如果访问的元素的value如果不存在会返回value类型的默认值,而不是像java一样返回null
put 元素: 有点像javascript了,哈哈哈哈

mp := map[int]string{}
mp[1] = "A"
fmt.Println(mp)

删除键值对:

delete(mp, 1)

注意map是是无序,遍历是无序的

指针(和c的指针差不多仅介绍下go的限制)

一个是取地址符&,另一个是解引用符*

func test8() {
num := 2
// 获取num的地址(指针),对一个变量进行取地址,会返回对应类型的指针
p := &num
fmt.Println(p)
// *就是接引用,获取到指向的具体值
fmt.Println(*p)
}
手动声明指针,使用new来手动分配地址
numPtr := new(bool)
*numPtr 是 fasle,解引用就是该指针类型的默认值

注意: go中不能指针运算
注意:go中数组指针不用解引用就可直接使用
因为 Go 的语言规范明确规定:
指向数组的指针允许直接用下标操作,就像数组本身一样。

func test10() {
//数组
arr := [5]int{0, 1, 2, 3, 4}
p := &arr
//无需解引用即可直接使用,相当于(*p)[1]
fmt.Println(p[1])
//切片
a := arr[0:2:2]
b := &a
//切片需要解引用后在使用
fmt.Println((*b)[1])
}

结构体

声明:

type Person struct {
name string
age  int
}

实例化:可以看到不需要所有属性都赋值,没有赋值的会有默认值

func main() {
person := Person{
name: "zs",
}
fmt.Println(person)
}
type Person struct {
name string
age  int
}

组合和匿名组合(类似于继承)

组合:就是在一个结构体中去声明另一个结构体

func main() {
student := Student{
p: Person{
name: "zs",
age:  18,
},
school: "aabbcc",
}
fmt.Println(student)
}
type Student struct {
p      Person
school string
}
type Person struct {
name string
age  int
}

匿名组合:匿名字段“提升”(promotion)只在访问时起作用,不在字面量初始化时起作用

func main() {
student := Student{
Person: Person{
age:  18,
name: "aaa",
},
school: "aabbcc",
}
fmt.Println(student.name)
}
type Student struct {
Person
school string
}
type Person struct {
name string
age  int
}

指针:结构体的指针和数组一样也是不需要解引用就可以访问,算是一种语法糖

标签

说白了就是结构体对象序列化时json字段的呈现形式,类似于Java的@JsonProperty(“”)注解
列:如果有多个就用空格隔开

type Person struct {
Name string `json:"name" yaml:"name"`
age  int
}

在格式化这个结构体的时候Name在json中就是name

posted on 2025-12-21 12:35  ljbguanli  阅读(0)  评论(0)    收藏  举报