作为有 Java 基础的开发者,学习 Go 会非常快!因为两者都是静态强类型、编译型语言,但 Go 语法极简、无冗余设计,我会结合 Java 对比,从核心语法到特色特性,一步步详解 Go 所有关键语法,让你快速上手。
一、先搞懂:Java vs Go 核心差异
这是你快速入门的关键,先记住核心区别:
| 特性 | Java | Go |
|---|---|---|
| 面向对象 | 基于类、继承、封装、多态 | 无类、无继承;用结构体 + 接口实现 OOP |
| 异常处理 | try-catch-finally | 无异常,用 error 接口手动处理 |
| 循环 | for/while/do-while | 只有 for 循环(三合一) |
| 并发 | 线程(Thread)、线程池 | 原生协程(goroutine) ,轻量高效 |
| 语法 | 繁琐(分号、括号、类包裹) | 极简(无分号、无多余括号、类型后置) |
| 函数 | 必须依附类(方法) | 函数是一等公民,可独立存在、多返回值 |
二、环境准备 & 第一个程序
1. 环境安装
直接官网下载:Golang 官网:The Go Programming Language,安装后验证:
go version
2. Hello World(对比 Java)
Java 版
// 必须包裹在类中
public class Hello {
public static void main(String[] args) {
System.out.println("Hello Go!");
}
}
Go 版
// 声明包名(main包=可执行程序,非库)
package main
// 导入依赖包(类似Java import)
import "fmt"
// 主函数:程序入口(无参数、无返回值,必须是main)
func main() {
// 打印函数(类似System.out.println)
fmt.Println("Hello Go!")
}
运行
go run main.go
核心语法点
- 包声明:
package main是可执行程序的标识;其他包名是库包。 - 导入:
import导入标准库 / 第三方库,多包导入用()包裹。 - 函数:
func关键字定义函数,main是唯一入口。 - 无分号:Go 自动补充分号,代码更简洁。
三、基础语法:变量 & 常量
Go 核心规则:类型后置(变量名在前,类型在后),零值初始化(声明未赋值会自动赋默认值)。
以下是 Go 中各种类型的零值速查表:
| 类型类别 | 零值 | 示例 |
|---|---|---|
| 数值类型 | 0 / 0.0 | int,float64,int8 等均为 0 或 0.0 |
| 布尔类型 | false | bool 类型的零值为 false |
| 字符串 | "" | 空字符串 |
| 引用类型 | nil | 指针、切片、映射、通道、函数、接口 |
| 复合类型 | 字段递归为零值 | 数组和结构体的每个元素/字段都会被初始化为其类型的零值 |
1. 变量声明(4 种方式)
package main
import "fmt"
func main() {
// 方式1:标准声明(指定类型,零值初始化)
var age int // 默认值:0
var name string // 默认值:""
fmt.Println(age, name)
// 方式2:声明+赋值(自动推导类型)
var score = 99.5
fmt.Println(score)
// 方式3:短变量声明(最常用!:= 自动推导类型,只能在函数内使用)
gender := "男"
fmt.Println(gender)
// 方式4:批量声明
var (
a int = 10
b string = "test"
)
fmt.Println(a, b)
}
对比 Java
- Java:
int age = 18;(类型前置) - Go:
age := 18或var age int(类型后置,自动推导) - Go 无
null,零值:int=0/string=""/bool=false/指针=nil
2. 常量
用 const 定义,值不可修改,支持iota(枚举计数器):
package main
import "fmt"
// 普通常量
const PI = 3.14159
// iota:常量生成器(从0开始自动递增)
const (
SUN = iota // 0
MON // 1
TUE // 2
)
func main() {
fmt.Println(PI, SUN, MON)
}
四、数据类型
Go 类型分基础类型和复合类型,无包装类,一切皆值类型。
1. 基础类型
// 整型
int int8 int16 int32 int64 // 有符号
uint uint8 uint16 uint32 uint64 // 无符号
// 浮点型
float32 float64
// 布尔型
bool // true/false
// 字符串
string // 不可变,用双引号/反引号(反引号支持多行)
// 字节/字符
byte // uint8 别名(代表ASCII字符)
rune // int32 别名(代表Unicode字符,处理中文)
2. 复合类型(重点)
(1)数组(固定长度,不常用)
// 声明:var 数组名 [长度]类型
var arr [3]int = [3]int{1,2,3}
// 短声明
arr2 := [3]int{4,5,6}
(2)切片 Slice(Go 最核心数据结构,替代 Java List)
切片是动态数组,长度可变,底层是数组,开发中 99% 用切片代替数组。
package main
import "fmt"
func main() {
// 1. 声明切片(无长度,[]类型)
var s []int // 零值:nil
// 2. 初始化切片
s = []int{1,2,3}
// 3. make创建(指定长度、容量)
s2 := make([]int, 3, 5) // 长度3,容量5
// 4. 追加元素(append,自动扩容)
s = append(s, 4,5) fmt.Println(s) // [1 2 3 4 5]
// 5. 切片截取(类似Python)
sub := s[1:3] // 索引1~2(左闭右开)
fmt.Println(sub) // [2 3]
}
(3)Map(替代 Java HashMap)
键值对结构,键必须是可比较类型(int/string 等)。
package main
import "fmt"
func main() {
// 1. make创建map
m := make(map[string]int)
// 2. 赋值
m["张三"] = 18 m["李四"] = 20
// 3. 取值
age := m["张三"]
fmt.Println(age)
// 4. 判断键是否存在(ok-idiom,Go特色)
age, ok := m["王五"]
if !ok {
fmt.Println("键不存在")
}
// 5. 删除键
delete(m, "李四")
}
(4)指针(比 Java 引用更简单)
Go 指针无指针运算,仅用于传递引用、修改原值,比 Java 更安全。
package main
import "fmt"
// 指针参数:修改原值(类似Java引用传递)
func changeAge(p *int) {
*p = 20 // *指针:解引用,访问原值
}
func main() {
age := 18
// &变量:取地址
p := &age
fmt.Println(*p) // 18
changeAge(&age)
fmt.Println(age) // 20
}
五、流程控制
Go 极简:if/for/switch 都无括号,for 是唯一循环。
1. if 语句
支持初始化语句 + 条件判断(Go 特色),无括号:
age := 18
// 标准if
if age >= 18 {
fmt.Println("成年")
} else {
fmt.Println("未成年")
}
// 带初始化的if(变量仅在if内有效)
if num := 10; num > 5 {
fmt.Println("大于5")
}
2. for 循环(三合一:替代 for/while/foreach)
(1)经典 for(类似 Java)
for i := 0; i < 5; i++ {
fmt.Println(i)
}
(2)while 循环
i := 0
for i < 5 {
fmt.Println(i)
i++
}
(3)无限循环
for {
// 死循环,用break退出
}
(4)foreach(遍历切片 /map)
// 遍历切片
s := []int{1,2,3}
for index, value := range s {
fmt.Println(index, value)
}
// 遍历map
m := map[string]int{"a":1}
for k, v := range m {
fmt.Println(k, v)
}
3. switch 语句
无需 break(默认自动中断),支持多条件、任意类型、无表达式:
score := 90
switch score {
case 90, 100: // 多条件
fmt.Println("优秀")
case 80:
fmt.Println("良好")
default:
fmt.Println("及格")
}
// 无表达式switch(替代if-else链)
switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B")
}
4. defer 语句(Go 特色:延迟执行)
defer 修饰的代码,会在函数返回前执行,常用于关闭文件、释放锁、关闭连接(替代 Java finally)。
func main() {
defer fmt.Println("最后执行")
fmt.Println("先执行")
}
// 输出:先执行 → 最后执行
六、函数(Go 核心:一等公民)
Go 函数独立于结构体,支持多返回值、可变参数、匿名函数、闭包,这是 Java 没有的特性。
1. 标准函数
语法:func 函数名(参数) 返回值/返回值列表 { 代码 }
// 单返回值
func add(a int, b int) int {
return a + b
}
// 多返回值(Go 特色!)
func calc(a int, b int) (int, int) {
return a + b, a - b
}
2. 调用多返回值函数
sum, sub := calc(10, 5)
fmt.Println(sum, sub) // 15 5
// 忽略某个返回值(用下划线 _)
sum2, _ := calc(10, 5)
3. 可变参数(类似 Java 可变参)
// ...类型 表示可变参数
func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
// 调用
sum(1,2,3) // 6
4. 匿名函数 & 闭包
// 匿名函数:直接赋值给变量
add := func(a, b int) int {
return a + b
}
fmt.Println(add(1,2))
七、面向对象:结构体 & 方法(无类!)
Go 没有 class,用 struct(结构体)自定义类型,用方法绑定结构体,实现封装。
1. 定义结构体
// 定义结构体(类似 Java 类)
type Person struct {
Name string
Age int
}
2. 初始化结构体
// 方式1
p1 := Person{Name: "张三", Age: 18}
// 方式2
var p2 Person
p2.Name = "李四"
p2.Age = 20
3. 方法(绑定结构体)
方法分值接收者(不修改原值)和指针接收者(修改原值)。
// 值接收者:只读,不修改结构体
func (p Person) sayHi() {
fmt.Printf("我是%s,年龄%d\n", p.Name, p.Age)
}
// 指针接收者:修改结构体(必须用指针)
func (p *Person) setAge(age int) {
p.Age = age
}
// 调用
func main() {
p := Person{Name: "张三", Age: 18}
p.sayHi()
p.setAge(20)
fmt.Println(p.Age) // 20
}
对比 Java
- Java:
class Person { void sayHi(){} } - Go:
type Person struct {}+func (p Person) sayHi(){}
八、接口(Go 特色:隐式实现)
Go 接口无需显式声明实现(不用 implements),只要结构体拥有接口的所有方法,就自动实现该接口。
1. 定义接口
// 定义接口
type Animal interface {
speak() string
}
2. 隐式实现接口
// 狗结构体
type Dog struct{}
// 实现接口方法(自动实现Animal接口)
func (d Dog) speak() string {
return "汪汪汪"
}
// 猫结构体
type Cat struct{}
func (c Cat) speak() string {
return "喵喵喵"
}
3. 使用接口
// 接口作为参数(多态)
func makeSound(a Animal) {
fmt.Println(a.speak())
}
func main() {
d := Dog{}
c := Cat{}
makeSound(d) // 汪汪汪
makeSound(c) // 喵喵喵
}
对比 Java
- Java:
class Dog implements Animal - Go:无任何关键字,自动实现,解耦更强。
九、错误处理(无 try-catch)
Go 抛弃异常机制,用 error 接口手动处理错误,代码更清晰。
1. 标准错误处理
import "errors"
// 函数返回错误(多返回值:结果+error)
func divide(a, b int) (int, error) {
if b == 0 {
// 返回错误
return 0, errors.New("除数不能为0")
}
return a / b, nil // nil=无错误
}
// 调用
func main() {
res, err := divide(10, 0)
if err != nil {
// 处理错误
fmt.Println("错误:", err)
return
}
fmt.Println("结果:", res)
}
十、Go 并发:协程(goroutine)
Go 原生支持并发,goroutine 是轻量级协程(比 Java 线程轻 1000 倍),无需线程池,直接用 go 关键字开启。
1. 开启协程
package main
import (
"fmt"
"time"
)
// 普通函数
func task() {
for i := 0; i < 3; i++ {
fmt.Println("任务执行:", i)
time.Sleep(100 * time.Millisecond)
}
}
func main() {
// 开启协程(go + 函数调用)
go task()
// 主协程等待
time.Sleep(1 * time.Second)
fmt.Println("主程序结束")
}
2. Channel(通道:协程通信)
Go 原则:不要以共享内存通信,要以通信共享内存,channel 用于协程间传递数据。
// 创建通道
ch := make(chan int)
// 协程发送数据
go func() {
ch <- 10 // 发送
}()
// 主协程接收数据
num := <-ch
fmt.Println(num) // 10
十一、包管理 & 模块化
Go 1.11+ 用 Go Modules(替代 GOPATH),类似 Java Maven/Gradle。
1. 初始化模块
go mod init 项目名
2. 导入包规则
- 首字母大写:导出(public,外部可访问)
- 首字母小写:私有(private,仅包内可访问)
// 结构体大写:外部可访问
type User struct {
Name string // 大写:导出
age int // 小写:私有
}
总结(Java 转 Go 必背核心)
- 语法极简:类型后置、无分号、无多余括号、无类;
- 核心特色:函数多返回值、切片 slice、隐式接口、defer、goroutine 协程;
- OOP 替代:结构体 + 方法 + 接口,无继承;
- 错误处理:error 接口,无 try-catch;
- 并发:原生协程 + 通道,秒杀 Java 线程。