Go 语言基础全景图 (Day 1)
1. 基础语法与变量 (Basics)
1.1 变量声明的两种方式
Go 在声明变量时非常灵活,区分“包级变量”和“函数内变量”。
var关键字: 哪里都能用,可以指定类型,也可以让编译器推导。:=短变量声明: 只能在函数内部使用。它兼具了声明和初始化。
Go
package main
var javaStyle int = 10 // 标准写法
var automatic = 20 // 自动推导为 int
func main() {
// 简短声明 (Short Declaration)
// 相当于 Java 的: var k = 3;
k := 3
// 注意::= 左边必须至少有一个新变量
k, j := 4, 5 // k 被重新赋值,j 是新声明的
}
1.2 零值 (Zero Values)
Java 的类成员默认是 null 或 0,但在方法里的局部变量不初始化会报错。
Go 里的所有变量,只要声明了,就一定有值。
| 类型 | 零值 (默认值) | Java 对比 |
|---|---|---|
int, float | 0 | 0 |
bool | false | false |
string | "" (空字符串) | null (这是大坑!) |
| 指针, 切片, map | nil | null |
1.3 导出规则 (可见性)
Go 没有 public, private 关键字。
- 大写字母开头 (e.g.,
Pizza) =public(其他包可见)。 - 小写字母开头 (e.g.,
pizza) =private(只有当前包可见)。
2. 流程控制 (Flow Control)
2.1 For 循环 (唯一的循环)
Go 只有 for,没有 while。
Go
// 1. 标准形式
for i := 0; i < 10; i++ { }
// 2. While 形式 (省略前后)
sum := 1
for sum < 1000 {
sum += sum
}
// 3. 死循环 (省略所有)
for { }
2.2 If 判断 (带初始化)
if 可以包含一段简短的初始化语句,变量只在 if 块内有效。
Go
// 格式:if 初始化; 条件 { ... }
if v := math.Pow(x, n); v < lim {
return v
}
// 这里访问不到 v
2.3 Defer (延迟执行)
- 作用: 函数返回前执行,用于资源清理(关闭文件、解锁)。
- 顺序: 栈式执行(后进先出)。
- 特性: 参数在
defer声明时就计算好了,只是函数执行被推迟。
3. 指针 (Pointers) —— 突破 Java 的限制
核心概念
&(取地址): 获取变量在内存中的位置。*(解引用): 读取该地址指向的值。
为什么用指针?
- 修改外部变量: 函数默认是值传递(拷贝),想改原来的值必须传指针。
- 性能优化: 避免拷贝大的结构体,只传一个地址(8字节)。
Go
func change(val int, ptr *int) {
val = 100 // 没用,改的是副本
*ptr = 100 // 有用,改的是内存里的值
}
4. 复合数据结构 (Data Structures)
4.1 结构体 (Struct)
Go 的面向对象基石,只有字段,没有方法(方法在外部绑定)。
Go
type Vertex struct {
X int
Y int
}
v := Vertex{1, 2}
p := &v
p.X = 19 // Go 允许通过指针直接访问字段 (自动解引用),不用写 (*p).X
4.2 数组 vs 切片 (Array vs Slice) —— 重中之重
| 特性 | 数组 [n]T | 切片 []T |
|---|---|---|
| 定义 | [3]int{1,2,3} | []int{1,2,3} (没有数字) |
| 长度 | 固定,不可变 | 动态,可扩容 |
| 性质 | 值类型 (传递时全拷贝) | 引用类型 (传递时只拷描述符) |
| 底层 | 真正的存储空间 | 只是一个指向数组的窗口 |
4.3 切片的核心操作
创建 (make)
除了字面量,还可以用 make 动态创建切片。
Go
// make([]Type, len, cap)
b := make([]int, 0, 5) // 长度0,容量5 (底层先占好坑)
追加 (append)
切片本身不能存更多数据,必须用 append,它会自动处理扩容。
Go
var s []int
s = append(s, 1) // 追加一个
s = append(s, 2, 3, 4) // 追加多个
截取 (Slicing)
Go
a := []int{1, 2, 3, 4, 5}
s := a[1:4] // [2, 3, 4] (左闭右开)
s2 := a[:] // 全选
遍历 (Range)
这是 Java foreach 的升级版。
Go
pow := []int{1, 2, 4, 8}
// i 是索引,v 是值的副本
for i, v := range pow {
fmt.Printf("Index: %d, Value: %d\n", i, v)
}
// 如果只需要值,用 _ 忽略索引
for _, v := range pow { ... }
5. 函数与方法 (Functions & Methods)
5.1 多返回值
Go 函数可以返回多个值,这在处理错误时是标准范式。
Go
// 返回:结果,是否成功
func process(input int) (int, bool) {
if input < 0 {
return 0, false
}
return input * 2, true
}
// 调用
res, ok := process(-1)
5.2 方法 (Methods)
Go 没有 class 里的方法,它把函数“挂载”到类型上。
Go
type User struct {
Name string
}
// (u User) 叫接收者 (Receiver),类似 Java 的 this
// 如果要修改 u 里面的值,必须用指针接收者:func (u *User)
func (u User) SayHi() {
fmt.Println("Hi", u.Name)
}
6. Java 程序员避坑指南 (Mindset Shift)
| 场景 | Java 思维 | Go 思维 |
|---|---|---|
| 空指针 | 小心 NullPointerException | 小心 nil,但 Go 的 nil 切片和 map 是安全的(可以读) |
| 异常处理 | try-catch | 检查返回值里的 error (if err != nil) |
| 继承 | class B extends A | 组合:Struct B 里嵌入 Struct A |
| 接口 | implements Interface | 隐式实现:只要你实现了方法,就自动实现了接口 |
| 并发 | Thread, Runnable | goroutine (协程) + channel (还没学,但是大杀器) |
| 访问控制 | public, private, protected | 首字母 大写 (公有) vs 小写 (私有) |
总结建议
- 忘掉数组: 除非你是写底层库,否则一律用切片 (
[]Type)。 - 拥抱指针: 它是你的朋友,用来避免拷贝大对象。
- 习惯组合: 不要总想着继承,用“拼装”的方式构建对象。
- 关注 Error: Go 代码里会有很多
if err != nil,这是为了让错误处理更显式。