跳到主要内容
零基础入门 Go 语言 | 极客日志
Go / Golang
零基础入门 Go 语言 系统讲解 Go 语言入门知识,包括环境搭建、基础语法(变量、常量、类型、流程控制)、核心特性(函数、结构体、接口)及并发编程(goroutine、channel)。通过 HTTP 服务与 MySQL 操作实战,对比 Go 与 Java 差异,并总结开发最佳实践,适合后端开发者快速上手。
laoliangsh 发布于 2026/3/25 更新于 2026/5/23 23 浏览作为一名长期深耕 Java 生态的开发者,你或许早已习惯了 JVM 的繁琐配置、GC 的调优难题、高并发场景下线程池的复杂管控。而 Go 语言(Golang)自 2009 年由 Google 推出以来,凭借'简单、高效、天生支持并发'的特性,迅速成为云原生、微服务、高并发后端领域的'新宠'。相比于 Java,Go 无需厚重的运行时,编译后直接生成可执行文件,部署仅需一个二进制包;并发模型基于 goroutine(轻量级线程),数万级并发轻松应对,资源消耗远低于 Java 线程。
一、Go 语言入门前的准备:环境搭建
1.1 为什么选择 Go?
先明确 Go 的核心优势(权威依据:Go 官方文档 https://go.dev/doc/):
高性能:编译型语言,接近 C/C++的执行效率,远高于 Java 的解释执行(JVM);
极简语法:比 Java 少 80% 的冗余语法,学习成本低,上手快;
原生并发:goroutine+channel 的 CSP 并发模型,无需手动管理线程池;
跨平台编译:一行命令编译出任意平台的可执行文件(Windows/Linux/Mac);
零依赖部署:编译后生成单一二进制文件,无需安装运行时,部署像复制文件一样简单;
丰富的标准库:内置 net/http、encoding/json、database/sql 等核心库,无需依赖第三方包即可完成大部分开发。
1.2 环境搭建(以 Linux/Mac 为例,Windows 同理)
Go 的最新稳定版本为 1.22.0(权威来源:Go 官方下载页 https://go.dev/dl/),安装步骤如下:
# 1. 下载安装包(Linux 64 位)
wget https://dl.google.com/go/go1.22.0.linux-amd64.tar.gz
# 2. 解压到/usr/local 目录
sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
# 3. 配置环境变量(编辑~/.bashrc 或~/.zshrc)
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
echo 'export GOPATH=$HOME/go' >> ~/.bashrc
echo 'export GO111MODULE=on' >> ~/.bashrc
echo 'export GOPROXY=https://goproxy.cn,direct' >> ~/.bashrc
# 4. 生效环境变量
source ~/.bashrc
# 5. 验证安装
go version
输出 go version go1.22.0 linux/amd64 即为成功。
补充:Windows 用户直接下载 msi 安装包,双击安装即可,安装程序会自动配置环境变量。
1.3 Go 的开发工具
推荐使用 VS Code(安装 Go 插件)或 Goland,VS Code 的 Go 插件提供语法高亮、自动补全、调试等功能,完全满足入门需求。
二、Go 语言核心语法:从 Hello World 到基础语法
2.1 第一个 Go 程序:Hello World
创建文件 hello.go,内容如下:
package main
import
{
fmt.Println( )
}
"fmt"
func main ()
"Hello, Go!"
# 直接运行
go run hello.go
# 编译为二进制文件
go build hello.go
# 运行编译后的文件(Linux/Mac)
./hello
# Windows
hello.exe
package:Go 的包管理机制,每个 Go 文件都属于一个包,main 包是唯一可执行的包;
import:导入依赖的包,fmt 是 Go 标准库中用于格式化 I/O 的包;
func main():程序的入口函数,无参数、无返回值,必须在 main 包中;
fmt.Println:打印字符串并换行,区别于 fmt.Print(不换行)。
2.2 变量与常量
2.2.1 变量声明 Go 是静态类型语言,变量声明后类型不可变,有三种声明方式:
var name string = "ken"
var age int = 30
var height = 1.80
score := 95.5
package main
import "fmt"
func main () {
var username string = "jam"
fmt.Println("用户名:" , username)
var a, b int = 10 , 20
fmt.Println("a =" , a, ", b =" , b)
var c = 3.14
fmt.Printf("c 的类型:%T,值:%v\n" , c, c)
d := "Go 语言"
fmt.Println("d =" , d)
var e int
var f string
var g bool
fmt.Printf("e 的零值:%d,f 的零值:%q,g 的零值:%t\n" , e, f, g)
}
用户名:jam
a = 10 , b = 20
c 的类型:float64,值:3.14
d = Go 语言
e 的零值:0,f 的零值:"",g 的零值:false
2.2.2 常量声明 常量使用 const 关键字,值不可修改,支持字符、字符串、布尔、数值类型:
package main
import "fmt"
const PI = 3.1415926
const (
STATUS_SUCCESS = 0
STATUS_ERROR = 1
)
func main () {
const MAX_SIZE = 100
fmt.Printf("PI = %v\n" , PI)
fmt.Printf("成功状态码:%d,错误状态码:%d\n" , STATUS_SUCCESS, STATUS_ERROR)
fmt.Printf("最大长度:%d\n" , MAX_SIZE)
const (
A = iota
B
C
)
fmt.Printf("A = %d, B = %d, C = %d\n" , A, B, C)
}
PI = 3.1415926
成功状态码:0,错误状态码:1
最大长度:100
A = 0 , B = 1 , C = 2
2.3 数据类型
数值类型:
整数:int(随系统位数,32/64 位)、int8/16/32/64、uint(无符号)、uint8(byte)、uint16/32/64、uintptr;
浮点数:float32、float64(默认);
复数:complex64、complex128。
布尔类型:bool(true/false,不可用 0/1 替代)。
字符串类型:string(UTF-8 编码,不可变)。
派生类型:指针、数组、切片、映射、通道、结构体、接口、函数。
package main
import "fmt"
func main () {
var num1 int8 = 127
var num2 uint8 = 255
fmt.Printf("num1 = %d, num2 = %d\n" , num1, num2)
var f1 float32 = 3.14
var f2 float64 = 2.71828
fmt.Printf("f1 = %f, f2 = %.5f\n" , f1, f2)
var str1 string = "Go 语言入门"
var str2 = `多行字符串 使用反引号 无需转义`
fmt.Println("str1 =" , str1)
fmt.Println("str2 =" , str2)
fmt.Printf("字符串长度:%d\n" , len (str1))
for i, c := range str1 {
fmt.Printf("索引%d:%c(Unicode 码点:%d)\n" , i, c, c)
}
}
num1 = 127 , num2 = 255
f1 = 3.140000 , f2 = 2.71828
str1 = Go 语言入门
str2 = 多行字符串 使用反引号 无需转义
字符串长度:12
索引 0:G(Unicode 码点:71)
索引 1:o(Unicode 码点:111)
索引 2:语(Unicode 码点:35821)
索引 5:言(Unicode 码点:35328)
索引 8:入(Unicode 码点:20843)
索引 11:门(Unicode 码点:38376)
2.4 流程控制 Go 的流程控制语法简洁,去除了 Java 中的 do-while、switch 的 break 陷阱,新增了 for-range 遍历。
2.4.1 条件语句(if-else) Go 的 if 语句无需括号,条件后必须紧跟大括号(即使只有一行):
package main
import "fmt"
func main () {
score := 85
if score >= 90 {
fmt.Println("优秀" )
} else if score >= 80 {
fmt.Println("良好" )
} else if score >= 60 {
fmt.Println("及格" )
} else {
fmt.Println("不及格" )
}
if age := 18 ; age >= 18 {
fmt.Println("成年" )
} else {
fmt.Println("未成年" )
}
}
2.4.2 循环语句(for) Go 只有 for 循环,替代了 Java 的 for、while、do-while:
package main
import "fmt"
func main () {
for i := 0 ; i < 5 ; i++ {
fmt.Print(i, " " )
}
fmt.Println()
j := 0
for j < 5 {
fmt.Print(j, " " )
j++
}
fmt.Println()
k := 0
for {
if k >= 5 {
break
}
fmt.Print(k, " " )
k++
}
fmt.Println()
str := "Go 语言"
for index, char := range str {
fmt.Printf("索引%d:%c\n" , index, char)
}
for i := 0 ; i < 5 ; i++ {
if i == 2 {
continue
}
fmt.Print(i, " " )
}
fmt.Println()
}
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
索引 0 :G
索引 1 :o
索引 2 :语
索引 5 :言
0 1 3 4
2.4.3 选择语句(switch) Go 的 switch 更灵活,无需 break,默认 case 结束后自动退出,支持任意类型、表达式:
package main
import "fmt"
func main () {
day := 3
switch day {
case 1 :
fmt.Println("周一" )
case 2 :
fmt.Println("周二" )
case 3 :
fmt.Println("周三" )
case 4 , 5 :
fmt.Println("周四/周五" )
default :
fmt.Println("周末" )
}
score := 75
switch {
case score >= 90 :
fmt.Println("优秀" )
case score >= 80 :
fmt.Println("良好" )
case score >= 60 :
fmt.Println("及格" )
default :
fmt.Println("不及格" )
}
num := 1
switch num {
case 1 :
fmt.Println("1" )
fallthrough
case 2 :
fmt.Println("2" )
case 3 :
fmt.Println("3" )
}
}
三、Go 的核心特性:函数、结构体与接口
3.1 函数 Go 的函数是一等公民,支持多返回值、可变参数、匿名函数、闭包,语法比 Java 简洁。
3.1.1 函数声明 语法:func 函数名 (参数列表) (返回值列表) { 函数体 }
package main
import "fmt"
func sayHello () {
fmt.Println("Hello, Go Function!" )
}
func add (a int , b int ) int {
return a + b
}
func subtract (a, b int ) int {
return a - b
}
func divide (a, b float64 ) (float64 , error ) {
if b == 0 {
return 0 , fmt.Errorf("除数不能为 0" )
}
return a / b, nil
}
func multiply (a, b int ) (result int ) {
result = a * b
return
}
func main () {
sayHello()
sum := add(10 , 20 )
fmt.Println("10+20 =" , sum)
diff := subtract(20 , 10 )
fmt.Println("20-10 =" , diff)
res, err := divide(10 , 2 )
if err != nil {
fmt.Println("错误:" , err)
} else {
fmt.Println("10/2 =" , res)
}
res2, err2 := divide(10 , 0 )
if err2 != nil {
fmt.Println("错误:" , err2)
} else {
fmt.Println("10/0 =" , res2)
}
prod := multiply(10 , 20 )
fmt.Println("10*20 =" , prod)
}
Hello, Go Function!
10+20 = 30
20-10 = 10
10/2 = 5
错误:除数不能为 0
10*20 = 200
3.1.2 可变参数与匿名函数 package main
import "fmt"
func sum (nums ...int ) int {
total := 0
for _, num := range nums {
total += num
}
return total
}
func main () {
fmt.Println("sum(1,2) =" , sum(1 , 2 ))
fmt.Println("sum(1,2,3,4) =" , sum(1 , 2 , 3 , 4 ))
nums := []int {1 , 2 , 3 , 4 , 5 }
fmt.Println("sum(nums...) =" , sum(nums...))
func (msg string ) {
fmt.Println("匿名函数:" , msg)
}("Hello, Anonymous Function!" )
counter := func () func () int {
count := 0
return func () int {
count++
return count
}
}()
fmt.Println("闭包计数 1:" , counter())
fmt.Println("闭包计数 2:" , counter())
fmt.Println("闭包计数 3:" , counter())
}
sum (1 ,2 ) = 3
sum (1 ,2 ,3 ,4 ) = 10
sum (nums...) = 15
匿名函数:Hello, Anonymous Function!
闭包计数 1 :1
闭包计数 2 :2
闭包计数 3 :3
3.2 结构体与方法 Go 没有类(class),但通过结构体(struct)+ 方法(method)实现面向对象的核心特性。
3.2.1 结构体声明与初始化 package main
import "fmt"
type User struct {
ID int
Username string
Age int
Email string
}
func main () {
u1 := User{1 , "jam" , 30 , "[email protected] " }
fmt.Println("u1 =" , u1)
u2 := User{
ID: 2 ,
Username: "ken" ,
Age: 28 ,
Email: "[email protected] " ,
}
fmt.Println("u2 =" , u2)
var u3 User
u3.ID = 3
u3.Username = "go_dev"
fmt.Println("u3 =" , u3)
u4 := &User{4 , "pointer" , 25 , "[email protected] " }
fmt.Println("u4 =" , *u4)
fmt.Println("u4.Username =" , u4.Username)
}
3.2.2 结构体方法 方法是绑定到结构体的函数,语法:func (接收者) 方法名 (参数) 返回值 { 方法体 }
package main
import "fmt"
type User struct {
ID int
Username string
Age int
}
func (u User) GetUsername() string {
return u.Username
}
func (u *User) SetAge(newAge int ) {
u.Age = newAge
}
func (u *User) IsAdult() bool {
return u.Age >= 18
}
func main () {
u := User{1 , "jam" , 17 }
fmt.Println("用户名:" , u.GetUsername())
fmt.Println("是否成年:" , u.IsAdult())
u.SetAge(18 )
fmt.Println("修改后的年龄:" , u.Age)
fmt.Println("是否成年:" , u.IsAdult())
}
用户名:jam
是否成年:false
修改后的年龄:18
是否成年:true
3.3 接口 Go 的接口是'鸭子类型'(只要实现了接口的所有方法,就隐式实现了该接口),无需显式声明 implements,比 Java 的接口更灵活。
3.3.1 接口声明与实现 package main
import "fmt"
type Animal interface {
Speak() string
}
type Dog struct {
Name string
}
type Cat struct {
Name string
}
func (d Dog) Speak() string {
return d.Name + ":汪汪汪"
}
func (c Cat) Speak() string {
return c.Name + ":喵喵喵"
}
func MakeSound (a Animal) {
fmt.Println(a.Speak())
}
func main () {
dog := Dog{Name: "旺财" }
cat := Cat{Name: "咪咪" }
MakeSound(dog)
MakeSound(cat)
var animal Animal
animal = dog
fmt.Println(animal.Speak())
animal = cat
fmt.Println(animal.Speak())
}
旺财:汪汪汪
咪咪:喵喵喵
旺财:汪汪汪
咪咪:喵喵喵
四、Go 的并发编程:goroutine 与 channel 并发是 Go 的核心优势,也是区别于 Java 的关键特性。Java 的并发基于线程(重量级,每个线程占 1-2MB 栈空间),而 Go 的 goroutine 是轻量级线程(初始栈仅 2KB,可动态扩缩容),单机可轻松创建数万个 goroutine。
4.1 并发模型:CSP Go 的并发模型基于 CSP(通信顺序进程),核心思想:'不要通过共享内存来通信,而要通过通信来共享内存'。
goroutine:轻量级执行体,由 Go 运行时管理,而非操作系统内核;
channel:goroutine 间的通信管道,用于安全传递数据,替代共享内存。
flowchart TD
A[主 goroutine]-->B[创建子 goroutine1]
A-->C[创建子 goroutine2]
B-->D[channel]
C-->D
D-->A[数据汇总]
4.2 goroutine 的使用 启动 goroutine 只需在函数调用前加 go 关键字:
示例代码(goroutine_demo1.go):
package main
import (
"fmt"
"time"
)
func task (name string ) {
for i := 0 ; i < 5 ; i++ {
fmt.Printf("任务%s:执行%d次\n" , name, i+1 )
time.Sleep(100 * time.Millisecond)
}
}
func main () {
go task("A" )
go task("B" )
time.Sleep(1 * time.Second)
fmt.Println("所有任务执行完成" )
}
任务 A :执行 1 次
任务 B :执行 1 次
任务 A :执行 2 次
任务 B :执行 2 次
任务 A :执行 3 次
任务 B :执行 3 次
任务 A :执行 4 次
任务 B :执行 4 次
任务 A :执行 5 次
任务 B :执行 5 次
所有任务执行完成
主 goroutine 的生命周期 :主 goroutine 退出后,无论子 goroutine 是否执行完成,都会被强制终止。上面的示例中 time.Sleep(1 * time.Second) 是简单的等待方式,但实际开发中不推荐(无法精准控制等待时间)。
goroutine 的调度 :goroutine 由 Go 运行时(runtime)的 M:N 调度器管理,将 M 个 goroutine 映射到 N 个操作系统线程,调度开销远低于操作系统线程。
goroutine 的栈 :初始栈大小为 2KB,可动态扩展(最大可达 1GB),而 Java 线程栈默认 1MB 且固定,这也是 goroutine 更轻量的核心原因。
优雅等待 goroutine:sync.WaitGroup 实际开发中,使用 sync.WaitGroup 来等待多个 goroutine 完成,替代硬编码的 time.Sleep:
示例代码(goroutine_demo2.go):
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func task (name string ) {
defer wg.Done()
for i := 0 ; i < 5 ; i++ {
fmt.Printf("任务%s:执行%d次\n" , name, i+1 )
time.Sleep(100 * time.Millisecond)
}
}
func main () {
wg.Add(2 )
go task("A" )
go task("B" )
wg.Wait()
fmt.Println("所有任务执行完成" )
}
运行结果与上例一致,但无需依赖 time.Sleep,更可靠。
4.3 channel:goroutine 间的通信管道 channel 是 Go 实现 CSP 并发模型的核心,用于在 goroutine 间安全传递数据,避免共享内存带来的竞态条件。
4.3.1 channel 的声明与初始化 语法:var 变量名 chan 数据类型 或 make(chan 数据类型,缓冲大小)
无缓冲 channel:make(chan int),发送和接收会阻塞,直到对方准备好;
有缓冲 channel:make(chan int, 10),缓冲区未满时发送不阻塞,缓冲区未空时接收不阻塞。
package main
import "fmt"
func main () {
ch := make (chan string )
go func () {
ch <- "Hello, Channel!"
}()
msg := <-ch
fmt.Println("接收到的数据:" , msg)
close (ch)
}
4.3.2 有缓冲 channel 示例 package main
import "fmt"
func main () {
ch := make (chan int , 3 )
ch <- 1
ch <- 2
ch <- 3
fmt.Println("缓冲区已填满,长度:" , len (ch), "容量:" , cap (ch))
fmt.Println("接收:" , <-ch)
fmt.Println("缓冲区剩余长度:" , len (ch))
ch <- 4
fmt.Println("缓冲区长度:" , len (ch))
close (ch)
for num := range ch {
fmt.Println("遍历接收:" , num)
}
}
缓冲区已填满,长度:3 容量:3
接收:1
缓冲区剩余长度:2
缓冲区长度:3
遍历接收:2
遍历接收:3
遍历接收:4
4.3.3 单向 channel 与 channel 的关闭检测 单向 channel 用于限制 channel 的使用场景(仅发送或仅接收),提升代码安全性:
package main
import "fmt"
func sendData (ch chan <- int ) {
for i := 0 ; i < 3 ; i++ {
ch <- i
fmt.Println("发送:" , i)
}
close (ch)
}
func recvData (ch <-chan int ) {
for {
num, ok := <-ch
if !ok {
fmt.Println("channel 已关闭,接收完成" )
break
}
fmt.Println("接收:" , num)
}
}
func main () {
ch := make (chan int )
go sendData(ch)
recvData(ch)
}
发送:0
接收:0
发送:1
接收:1
发送:2
接收:2
channel 已关闭,接收完成
4.3.4 channel 的经典应用:goroutine 池 goroutine 池用于限制并发数,避免创建过多 goroutine 导致资源耗尽,是实际开发中的高频场景:
示例代码(channel_pool_demo.go):
package main
import (
"fmt"
"sync"
"time"
)
type Task struct {
ID int
Msg string
}
func executeTask (task Task, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("执行任务%d:%s\n" , task.ID, task.Msg)
time.Sleep(500 * time.Millisecond)
}
func main () {
const poolSize = 3
const taskCount = 10
var wg sync.WaitGroup
taskChan := make (chan Task, taskCount)
for i := 0 ; i < poolSize; i++ {
go func (workerID int ) {
for task := range taskChan {
fmt.Printf("工作 goroutine%d 开始处理任务%d\n" , workerID, task.ID)
executeTask(task, &wg)
fmt.Printf("工作 goroutine%d 完成任务%d\n" , workerID, task.ID)
}
}(i)
}
wg.Add(taskCount)
for i := 0 ; i < taskCount; i++ {
taskChan <- Task{
ID: i + 1 ,
Msg: fmt.Sprintf("任务内容%d" , i+1 ),
}
}
close (taskChan)
wg.Wait()
fmt.Println("所有任务执行完毕" )
}
工作 goroutine0 开始处理任务 1
执行任务 1:任务内容 1
工作 goroutine1 开始处理任务 2
执行任务 2:任务内容 2
工作 goroutine2 开始处理任务 3
执行任务 3:任务内容 3
工作 goroutine0 完成任务 1
工作 goroutine0 开始处理任务 4
执行任务 4:任务内容 4
工作 goroutine1 完成任务 2
工作 goroutine1 开始处理任务 5
执行任务 5:任务内容 5
工作 goroutine2 完成任务 3
工作 goroutine2 开始处理任务 6
执行任务 6:任务内容 6
...
所有任务执行完毕
4.4 同步原语:sync.Mutex 与 sync.RWMutex 虽然 Go 推荐用 channel 实现通信,但某些场景下仍需共享内存(如计数器),此时需用互斥锁避免竞态条件:
4.4.1 sync.Mutex(互斥锁) package main
import (
"fmt"
"sync"
)
var (
counter int
mutex sync.Mutex
wg sync.WaitGroup
)
func increment () {
defer wg.Done()
for i := 0 ; i < 1000 ; i++ {
mutex.Lock()
counter++
mutex.Unlock()
}
}
func main () {
wg.Add(2 )
go increment()
go increment()
wg.Wait()
fmt.Println("最终计数器值:" , counter)
}
4.4.2 sync.RWMutex(读写锁) 读写锁适用于'读多写少'场景,允许多个读操作并发,写操作独占:
package main
import (
"fmt"
"sync"
"time"
)
var (
data = "初始数据"
rwMutex sync.RWMutex
wg sync.WaitGroup
)
func readData (id int ) {
defer wg.Done()
rwMutex.RLock()
defer rwMutex.RUnlock()
fmt.Printf("读 goroutine%d:读取到数据:%s\n" , id, data)
time.Sleep(100 * time.Millisecond)
}
func writeData (newData string ) {
defer wg.Done()
rwMutex.Lock()
defer rwMutex.Unlock()
fmt.Println("写 goroutine:开始修改数据" )
data = newData
time.Sleep(500 * time.Millisecond)
fmt.Println("写 goroutine:数据修改完成,新数据:" , data)
}
func main () {
for i := 0 ; i < 5 ; i++ {
wg.Add(1 )
go readData(i)
}
wg.Add(1 )
go writeData("修改后的数据" )
for i := 5 ; i < 10 ; i++ {
wg.Add(1 )
go readData(i)
}
wg.Wait()
fmt.Println("所有操作完成" )
}
运行结果(核心特征:写操作期间读操作阻塞,写完成后读操作并发执行):
读 goroutine0:读取到数据:初始数据
读 goroutine1:读取到数据:初始数据
读 goroutine2:读取到数据:初始数据
读 goroutine3:读取到数据:初始数据
读 goroutine4:读取到数据:初始数据
写 goroutine:开始修改数据
写 goroutine:数据修改完成,新数据:修改后的数据
读 goroutine5:读取到数据:修改后的数据
读 goroutine6:读取到数据:修改后的数据
读 goroutine7:读取到数据:修改后的数据
读 goroutine8:读取到数据:修改后的数据
读 goroutine9:读取到数据:修改后的数据
所有操作完成
五、Go 实战开发:从基础到应用
5.1 简易 HTTP 服务器(Go 标准库实现) Go 标准库 net/http 内置 HTTP 服务器,无需依赖 Tomcat/Nginx 等容器,几行代码即可实现:
示例代码(http_server_demo.go):
package main
import (
"fmt"
"net/http"
"time"
)
func rootHandler (w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, Go HTTP Server! 当前时间:%s" , time.Now().Format("2006-01-02 15:04:05" ))
}
func userHandler (w http.ResponseWriter, r *http.Request) {
username := r.URL.Query().Get("username" )
if username == "" {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "参数错误:username 不能为空" )
return
}
fmt.Fprintf(w, "你好,%s!" , username)
}
func main () {
http.HandleFunc("/" , rootHandler)
http.HandleFunc("/user" , userHandler)
fmt.Println("HTTP 服务器启动,监听端口:8080" )
err := http.ListenAndServe(":8080" , nil )
if err != nil {
fmt.Printf("服务器启动失败:%v\n" , err)
}
}
go run http_server_demo.go
访问 http://localhost:8080,输出:Hello, Go HTTP Server! 当前时间:2026-01-15 10:00:00
访问 http://localhost:8080/user?username=jam,输出:你好,jam!
访问 http://localhost:8080/user,输出:参数错误:username 不能为空
5.2 数据库操作(MySQL) Go 标准库 database/sql 提供数据库通用接口,需配合驱动(如 github.com/go-sql-driver/mysql)操作 MySQL:
5.2.1 环境准备 go get github.com/go-sql-driver/mysql@latest
5.2.2 示例代码(mysql_demo.go) package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/go-sql-driver/mysql"
)
type User struct {
ID int
Username string
Age int
Email string
}
func main () {
dsn := "root:123456@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
db, err := sql.Open("mysql" , dsn)
if err != nil {
log.Fatalf("打开数据库连接失败:%v" , err)
}
defer db.Close()
err = db.Ping()
if err != nil {
log.Fatalf("数据库连接失败:%v" , err)
}
fmt.Println("数据库连接成功" )
createTableSQL := `
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
age INT NOT NULL,
email VARCHAR(100)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`
_, err = db.Exec(createTableSQL)
if err != nil {
log.Fatalf("创建表失败:%v" , err)
}
fmt.Println("表创建成功(或已存在)" )
insertSQL := "INSERT INTO users (username, age, email) VALUES (?, ?, ?)"
result, err := db.Exec(insertSQL, "jam" , 30 , "[email protected] " )
if err != nil {
log.Printf("插入数据失败(可能已存在):%v" , err)
} else {
id, _ := result.LastInsertId()
fmt.Printf("插入数据成功,ID:%d\n" , id)
}
var user User
querySQL := "SELECT id, username, age, email FROM users WHERE username = ?"
err = db.QueryRow(querySQL, "jam" ).Scan(&user.ID, &user.Username, &user.Age, &user.Email)
if err != nil {
log.Fatalf("查询数据失败:%v" , err)
}
fmt.Printf("查询到用户:%+v\n" , user)
queryAllSQL := "SELECT id, username, age, email FROM users WHERE age > ?"
rows, err := db.Query(queryAllSQL, 20 )
if err != nil {
log.Fatalf("查询多条数据失败:%v" , err)
}
defer rows.Close()
var users []User
for rows.Next() {
var u User
err := rows.Scan(&u.ID, &u.Username, &u.Age, &u.Email)
if err != nil {
log.Printf("扫描行失败:%v" , err)
continue
}
users = append (users, u)
}
fmt.Printf("查询到的用户列表:%+v\n" , users)
updateSQL := "UPDATE users SET age = ? WHERE username = ?"
_, err = db.Exec(updateSQL, 31 , "jam" )
if err != nil {
log.Fatalf("更新数据失败:%v" , err)
}
fmt.Println("数据更新成功" )
}
数据库连接成功
表创建成功(或已存在)
插入数据成功,ID :1
查询到用户:{ID : 1 Username :jam Age : 30 Email :jam @example .com}
查询到的用户列表:[{ID : 1 Username :jam Age : 30 Email :jam @example .com}]
数据更新成功
5.3 Go 与 Java 核心特性对比(易混淆点区分) 特性 Go 语言 Java 语言 并发模型 goroutine(轻量级)+ channel 线程(重量级)+ 锁/线程池 内存管理 自动 GC(无分代,简单高效) 分代 GC(CMS/G1/ZGC,配置复杂) 编译方式 静态编译为单一二进制文件 编译为字节码,需 JVM 解释/即时编译 面向对象 结构体 + 方法(无类、无继承) 类 + 继承 + 接口 错误处理 多返回值(显式处理错误) 异常捕获(try-catch) 部署方式 单文件部署(无依赖) 需 JRE/JDK,多文件(jar/war) 包管理 Go Module(go mod) Maven/Gradle
六、Go 开发最佳实践(权威依据:Go 官方《Effective Go》)
命名规范 :包名小写且简洁(如 fmt、net/http),结构体名帕斯卡命名(User),函数名帕斯卡命名(导出)/驼峰命名(私有);
错误处理 :显式处理错误,不要忽略 err,错误信息要具体(包含上下文);
并发编程 :优先使用 channel 实现 goroutine 通信,避免共享内存;
资源管理 :使用 defer 释放资源(如文件、数据库连接),确保资源不泄漏;
代码简洁 :去除冗余代码,Go 推崇'简洁即美',避免过度封装;
依赖管理 :使用 Go Module(go mod init/go get)管理依赖,指定版本号避免依赖冲突。
总结
Go 语言的核心优势是高性能、原生并发、极简语法、零依赖部署 ,适合云原生、微服务、高并发后端开发;
goroutine+channel 是 Go 并发编程的核心,遵循'通信共享内存'而非'共享内存通信'的原则;
Go 的实战开发中,标准库(net/http、database/sql)足够覆盖大部分场景,无需依赖第三方框架即可快速开发。
掌握 Go 的核心语法和并发模型后,你可以进一步学习 Go 的高级特性(如反射、接口进阶、性能优化),或结合框架(如 Gin、Beego)进行企业级开发。相比于 Java,Go 的学习曲线更平缓,且能快速落地到实际项目中,是后端开发者必备的技能之一。
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online