Go 语言学习笔记01

12 阅读4分钟

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, float00
boolfalsefalse
string"" (空字符串)null (这是大坑!)
指针, 切片, mapnilnull

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 的限制

核心概念

  • & (取地址): 获取变量在内存中的位置。
  • * (解引用): 读取该地址指向的值。

为什么用指针?

  1. 修改外部变量: 函数默认是值传递(拷贝),想改原来的值必须传指针。
  2. 性能优化: 避免拷贝大的结构体,只传一个地址(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, Runnablegoroutine (协程) + channel (还没学,但是大杀器)
访问控制public, private, protected首字母 大写 (公有) vs 小写 (私有)

总结建议

  1. 忘掉数组: 除非你是写底层库,否则一律用切片 ([]Type)。
  2. 拥抱指针: 它是你的朋友,用来避免拷贝大对象。
  3. 习惯组合: 不要总想着继承,用“拼装”的方式构建对象。
  4. 关注 Error: Go 代码里会有很多 if err != nil,这是为了让错误处理更显式。