学习笔记 | Golang基础,Go语言快速入门!

本文将介绍 Go 语言的基础语法,包括环境配置、数据类型、流程控制、函数、结构体、接口、异常、文本处理、并发编程、网络编程等。本文是对多个 Go 入门视频的总结,并结合个人理解对内容进行了简化。由于水平有限,文中可能存在些许错误,烦请评判指正。

前言

Go 的作者:Rob Pike(罗伯·派克)、Ken Thompson(肯·汤姆森)、Robert Griesemer(罗伯特·格里茨默)。

Go 的开源时间:2009 年 11 月 10 日。

Rob Pike 曾说:“你是否认同 Go,取决于你是否认同少就是多,还是少就是少(Less is more or less is less)”。Go 的设计哲学是:将简单、使用体现得淋漓尽致。

使用 Go 的项目:Docker、Kubernetes、etcd、beego、codis。

使用 Go 的公司:Google、Faebook、腾讯、百度、七牛云、京东、小米、字节跳动。

环境配置

下载地址:All releases - The Go Programming Language (google.cn),推荐下载 zip 格式。

配置环境变量:添加以下变量,并将 %GOROOT%\bin 添加到 Path 变量中。

变量说明
GOROOTD:\Development\Environment\Go\Go-1.23.1Go 安装目录
GOPATHD:\LocalRepository\GoGo 依赖安装目录
GOPROXYhttps://mirrors.aliyun.com/goproxy/Go 依赖镜像

CMD 执行 go version 查看 Go 版本。

集成开发环境:GoLand by JetBrains: More than just a Go IDE

基础语法

注释
// 单行注释(推荐)

/*
多行注释
*/
package main


// 普通
import "fmt"

// 忽略
import _ "fmt"

// “解构”
import . "fmt"

// 别名
import ifmp "fmt"

// “分组”
import (
	"fmt"
    "os"
)

// 主函数
func main(){
    
}

一个可执行程序必须包含一个 main 包,一个 main 包必须包含一个 main 函数。

所在目录(父级)相同 go 文件的包名必须相同。

变量

变量被声明后必须被使用。

// 定义
var a int
var a, b string
var (
	a string
    b string
)
// 定义并初始化
var a string = "a"
var a, b string = "a", "b"
var a, b = "a", "b"
a, b := "a", "b"			// 自动类型推导

零值(默认值):整型为0,浮点型为0.0,字符串为空字符串,布尔型为false,切片、函数、指针默认为 nil。

变量遵循驼峰命名。对于全局变量,若首字母大写,则认定为 “public”。

交换变量的值。

a, b, c = b, c, a

匿名变量。匿名变量不占用空间,不会分配内存。

a, _ := getTwo()
_ = getOne()

变量作用域:局部变量、全局变量。全局变量与局部变量可同名。

// 全局变量
age := 14

func main(){
    // 局部变量
    age := 15
    fmt.Println(age)	// 15
}

常量。常量的定义和初始化与变量类似。

// 单个常量
const a = 1

// 多个常量
const a, b = 1, 2
const (
	b = 2
    c = 3
)

// iota
const (
    a = iota	// 0
    b 			// 1
    c = "c"		// c
    d			// c
    e = 100		// 100
    f			// 100
    g = iota	// 6
    h			// 7
)
const (
	i = iota	//0
    j			// 1
)
数据类型

布尔型:默认 false。

 var flag = true

整型。字符用 int32 存储。

类型描述
uint8 / byte0 到 2^8
uint160 到 2^16
uint32 / uint(32位系统)0 到 2^32
uint64 / uint(64位系统)0 到 2^64
int8-2^7 到 2^7-1
int16-2^15 到 2^15-1
int32 / rune / int(32位系统)-2^31 到 2^31-1
int64 / int(64位系统)-2^63 到 2^64-1

浮点型。

类型描述
float32IEEE-754 32 位浮点数
float64IEEE-754 64 位浮点数

字符串。

var s string = "tom"
fmt.Println(s[0])

类型转换。

// 数值转数值
b := float64(a)

// 转字符串
s := strconv.FormatInt(int64(v), 10)
s := strconv.Itoa(v)	// 特殊

// 字符串转数值
v, _ := strconv.ParseInt(s, 10)
v, _ := strconv.Atoi(s)	// 特殊

指针:无需手动回收,内存回收由 GC 自动完成。

var p *int		// 默认nil
var p = &a
var p = new(int)

var b = *p

数组:数组大小必须为常量。

var arr [5]int
var arr [5]int = [5]int{1, 2}	// 未初始化的值默认为0
var arr = [5]int{1, 2, 3, 4}
arr := [5]int{1, 2, 3, 4}

a := [3]int{1, 2, 3}
b := [3]int{1, 2, 3}
fmt.Println(a == b)		// true

切片:自动扩容。

var a []int
var a []int = []int{1, 2, 3}
var a = []int{1, 2, 3}
a := []int{1, 2, 3}
a := make([]int, 10, 15)	// 类型 长度 容量

// 长度
len(a)
// 容量
cap(a)

// 追加
a = append(a, 1)
a = append(a, b...)

// 截取
b := a[1:2:1]	// [low:high:max] 左边右开 长度=high-low 容量=max-high
b := a[1:2]
b := a[:2]
b := a[1:]

// 数组切片-浅拷贝
a := []int{1, 2, 3}
b := a[1:2]
b = append(b, 4)
b[0] = 5
fmt.Println(a)	// 1 5 4
fmt.Println(b)	// 5 4

map。

var mp map[string]int
var mp = map[string]int{"a": 1, "b": 2}
mp := make(map[string]int, 10)
运算符

算术运算符

+	-	*	/	%	++	--

关系运算符

==	!=	>	>=	<	<=

逻辑运算符

&&	||	!

位运算符:a &^ b,将 a 中与 b 中 1 的对应位置 0,称为位清空。

&	|	^	&^	<<	>>	

赋值运算符

=    +=    -=    *=    /=    %=    <<=    >>=    &=    ^=    |=
输入输出
fmt.Println(1)
fmt.Printf("%d", 2)
fmt.Print(3)

fmt.Scan(&age)
fmt.Scanln(&age)
fmt.Scanf("%s", &age)
流程控制

if-else

if age < 18 {

} else if age < 35 {

} else {

}
// 含初始化语句
if a := 10; a >= 10 {

} else if b := 20; b >= 20 {

}else {}

switch:fallthrough、break。

// 执行匹配项
switch age {
    case 18:{}
    case 35, 45:{}
    default:{}
}
// 执行最后一个匹配项
switch {
    case age < 18:{}
    case age < 35:{}
    default:{}
}
// 执行true
switch {
	case true:{}
	case false:{}
	default:{}
}
// fallthrough执行下一个case
// break跳出switch
switch idx {
	case 1:
		fallthrough
	case 2:
    	break
    	fallthrough
	default:
}
// 判断类型
switch x.(type) {
	case string:
	case int:
	default:
}

for:break、continue。

for i := 0; i < 10; i++ {}
i := 0
for ; i < 10; i++ {}
i := 0
for i < 10 { i++ }
for { }
for i := range s {}
for i, ch := range s { }

goto

func f() {
	goto L1
	fmt.Println(1)
L1:
	fmt.Println(2)
}
函数
func say(a, b string) (c, d string) {
	return b, a
}
func main() {
    e, f := say("a", "b")
}
// 可变参数
func say(cnt int, args ...string) {
	fmt.Println(cnt)
	for _, v := range args {
		fmt.Println(v)
	}
}
// 匿名函数(立即执行)
func main() {
	func() {
		fmt.Println("s")
	}()
}

参数传递

  • 值传递:int、float64、string、bool、array等。
  • 引用传递:slice、map、chan等。
// 执行顺序:d、e、c、b、a,defer逆序执行
func main() {
	defer a()
	defer b()
	defer c()
    d()
    e()
}
// 函数类型
var f  func(int,int)
var f = func(int, int) { }
// 回调函数
func f1(a, b int) int {
	return a + b
}
func f2(a, b int, f func(int, int) int) int {
	return f(a, b)
}
// 闭包
func main() {
	fc := f()
	fmt.Println(fc()) // 1
	fmt.Println(fc()) // 2

}
func f() func() int {
	i := 0
	return func() int {
		i++
		return i
	}
}

main 函数:一个可执行程序必须包含一个 main 包,一个 main 包必须包含一个 main 函数,仅 main 包下的 main 可作为入口函数。

package main

func main() {
	
}

init 函数:在导入一个包的同时,会直接执行这个包的 init(),main 包的 init() 会先于 main() 执行。

内置函数。

len()
cap()

a = append(a, 1)
a = append(a, b...)

a := []int{1, 2, 3}
b := []int{4, 5}
copy(a, b)
fmt.Println(a)	// 4 5 3
fmt.Println(b)	// 4 5

delete(a, 1)
编码规范

导包。

import (
	"fmt"
    "json"
)

命名:一个目录一个 go 文件,go 文件包名与目录名保持一致,包名与目录名采用小写,多个单词用下划线分隔。

结构体

type Student struct {
	id   int
	name string
}
func main() {
    // 按顺序赋值
	var s1 = Student{1, "tom"}
    // 指定字段赋值
	var s2 = Student{
		id:   1,
		name: "tom",
	}
    // 指针
	s3 := &s1
	s4 := new(Student)
    
	fmt.Println(s1.id, s2.name)
}

type Student struct {
	id   int
    // 首字母大写才可跨包可见(public)
	Name string
}
继承
// 结构体匿名变量
type Person struct {
	id int
}
type Student struct {
	Person
	name string
}

func main() {
	s := Student{Person{1}, "tom"}
	s.Person= Person{2}
	s.Person.id = 1
	s.id = 2
	s.name = "jack"
}
// 结构体指针匿名变量
type Person struct {
	id int
}
type Student struct {
	*Person
	name string
}

func main() {
	s := Student{&Person{1}, "tom"}
}
// 同名变量
type Person struct {
	id int
}
type Student struct {
	*Person
	id int
}

func main() {
	s := Student{&Person{1}, 2}
	fmt.Println(s.id)				// 2
}
// 非结构体类型匿名变量
type Student struct {
	int
}

func main() {
	s := Student{1}
	fmt.Println(s.int)
}
方法
type Student struct {
	id int
}
// 方法挂载到自定义类型
func (s Student) say() {
    // s是实参的拷贝
	fmt.Println(s.id)
}
func main() {
	 Student{1}.say()
}
type str string
// 方法挂载到自定义类型
func (s str) say() {
	fmt.Println(s)
}
func main() {
	s := str("hi")
	s.say()
}

方法集:指针变量和非指针变量可看作共用方法集(语法糖)

type Student struct {
	age int
}
func (s *Student) add(v int) {
	s.age += v
}
func main() {
	s := Student{1}
	s.add(1)
	fmt.Println(s.age)	// 2
}

方法的继承。

type Person struct {
	id int
}
type Student struct {
	Person
}

func (p *Person) say() {
	fmt.Println(p.id)
}
func main() {
	s := Student{Person{1}}
	s.say()
}

方法的重写。

type Person struct {
	id int
}
type Student struct {
	Person
}

func (p *Person) say() {
	fmt.Println("p")
}

func (s *Student) say() {
	fmt.Println("s")
}
func main() {
	s := Student{Person{1}}
	s.say()			// s
    s.Person.say()	// p
}

方法值。

p := Person{1}
pf := p.say
pf()

方法表达式。

p := Person{1}
pf := (*Person).say
pf(&p)

接口

若一个类型实现了一个接口的所有方法,便认为该类型体实现了该接口。

type Person interface {
	say()
}
type Student struct {
}

func (s *Student) say() {
	fmt.Println("Student")
}

func main() {
	var p Person = new(Student)
    // 多态
	p.say()
}
接口的继承
type Person interface {
	say()
}
type GoodPerson interface {
	Person
}
type Person interface {
	say()
}
type GoodPerson interface {
	Person
	sayGood()
}
type Student struct {}

func (s *Student) say() {}
func (s *Student) sayGood() {}

func main() {
	var p Person = &Student{}
	var gp GoodPerson = &Student{}
	// 下转上
	p = gp
	// 上转下 不允许
	// gp = p

空接口

可以将任何类型的值赋给空类型的变量。

var v interface{}
v = &Student{}

类型断言。

// 断言v是Person类型
// ok=true表示断言成功,否则断言失败
// 若断言成功,则会将v转为Person类型并赋给sv,否则sv为零值
if sv, ok := v.(Person); ok {
    sv.say()
}
func main() {
	var v interface{}
	v = &Student{}	// Student实现了Person
	switch v.(type) {
        case int:
            fmt.Println("int")
        case Student:
            fmt.Println("Student")
        case Person:
            fmt.Println("Person")
        default:
            fmt.Println("default")
	}
}
// 输出Person

异常

内置 error 接口。

// builtin.go
type error interface {
	Error() string
}

error 接口的实现。

// errors.go
type errorString struct {
	s string
}
func (e *errorString) Error() string {
	return e.s
}

使用。

func main() {
	err := fmt.Errorf("%s", "error msg")
	fmt.Println(err)
}
func main() {
	err:=errors.New("error msg")
	fmt.Println(err)
}
func div(a, b int) (res int, err error) {
	if b == 0 {
		err = errors.New("divide by zero")
	} else {
		res = a / b
	}
	return
}
func main() {
	res, err := div(10, 0)
	fmt.Println(res, err)
}

当发生致命错误时,可调用 panic 函数中断程序。

func div(a, b int) (res int, err error) {
	if b == 0 {
		panic("divide by zero")
	}
	res = a / b
	return
}

recover 函数可拦截异常。

func div(a, b int) (res int, err error) {
	defer func() {
		if e := recover(); e != nil {
			fmt.Println(e)
		}
	}()
	if b == 0 {
		panic(errors.New("divide by zero"))
	}
	res = a / b
	return
}

文本处理

字符串操作。

strings.Contains()
strings.Join()
strings.Index()
strings.Repeat()
strings.Replace()
strings.Split()
strings.Trim()
strings.Fields()

字符串转换。

// 其他类型转string
strconv.FormatXXX()
// string转其他类型
strconv.ParseXXX()

正则表达式。

func main() {
	r, s := ".*", "abc"
	reg := regexp.MustCompile(r)
	reg.Match([]byte(s))
	reg.FindStringIndex(s)
}

JSON。

type Student struct {
	Id   int    `json:"id"`
	Name string `json:"-"`
	Flag bool   `json:"flag,string"`
}

func main() {
	s := Student{1, "tom", true}
	js, _ := json.Marshal(s)
	_ = json.Unmarshal(js, &s)
}

文件操作

创建/打开文件

func Create(name string) (*File, error)
func Open(name string) (*File, error) 
func OpenFile(name string, flag int, perm FileMode) (*File, error)

写文件

func WriteFile(name string, data []byte, perm FileMode) error

读文件

func ReadFile(name string) ([]byte, error) 

删除文件

func Remove(name string) error
func RemoveAll(path string) error 

命令行参数

os.Args

并发编程

协程
func main() {
	go func() {
		
	}()
}
// 设置CPU核数
runtime.GOMAXPROCS(1)
// 当前协程让出时间片
runtime.Gosched()
// 退出当前协程
runtime.Goexit()
channel

goroutine奉行通过通信来共享内存,而不是通过共享内存来通信。

// 通道-无缓冲
var ch = make(chan int)

func main() {
	go func() {
		// 存放数据,阻塞等待
		ch <- 1
	}()
	// 阻塞等待,获取数据
	d := <-ch
	fmt.Println(d)
}
// 通道-有缓冲
var ch = make(chan int, 2)

func main() {
	cnt := 3
	go func() {
		for i := 0; i < cnt; i++ {
			// 存放数据,缓存区满时阻塞等待
			ch <- 1
		}
	}()
	for i := 0; i < cnt; i++ {
		// 阻塞等待数据
		d := <-ch
		fmt.Println(d)
	}
}
// 关闭通道
func main() {
	ch := make(chan int, 2)
	go func() {
		for i := 0; i < 3; i++ {
			ch <- 1
		}
		close(ch)
	}()
	for {
		if v, ok := <-ch; ok {
			fmt.Println(v)
		} else {
			break
		}
	}
}
func main() {
	ch := make(chan int, 2)
	go func() {
		for i := 0; i < 3; i++ {
			ch <- 1
		}
		close(ch)
	}()
	for d := range ch {
		fmt.Println(d)
	}
}
// 双向 单向
func main() {
	// 双向
	ch := make(chan int, 2)
	// 单向只读
	var rch <-chan int = ch
	// 单向只写
	var wch chan<- int = ch
}
// 通道参数
func main() {
	ch := make(chan int, 2)
	go read(ch)
	go write(ch)
}

func read(rch <-chan int) {}
func write(wch chan<- int) {}
定时器
// 等待5s
timer := time.NewTimer(5 * time.Second)
<-timer.C	

// 等待1s
time.Sleep(1 * time.Second)

// 等待5s
time.After(5 * time.Second)
timer :=time.NewTimer(time.Second)
// 重设定时器
timer.Reset(time.Minute)
// 停止定时器
timer.Stop()
// 定时执行
ticker := time.NewTicker(2 * time.Second)
for i := 0; ; i++ {
    <-ticker.C
    fmt.Println(time.Now().Unix())
    if i >= 10 {
        ticker.Stop()
        break
    }
}
// select
func main() {
	c1, c2 := make(chan int), make(chan int)
	go func() {
		x, y := 1, 2
		for i := 0; i < 10; i++ {
			select {
			// 写数据
			case c1 <- x:
				fmt.Println("c1", i, x)
				x, y = y, x
				// 读数据
			case k := <-c2:
				fmt.Println("c2", i, k)
			}
		}
		// 对于每轮循环,select会随机选择一个未阻塞的case来执行
		// 如果所有case都阻塞,select会阻塞 
	}()
	for i := 0; i < 3; i++ {
		// 读数据
		<-c1
		// 写数据
		c2 <- i
	}
}

// 超时取消
go func() {
label:
    for {
        select {
        case v := <-ch:
            fmt.Println(v)
        case <-time.After(time.Second * 5):
            fmt.Println("timeout")
            break label
        }
    }
}()

网络编程

Scoket编程

服务端。

func main() {
	// 监听
	listener, _ := net.Listen("tcp", ":8080")
	defer listener.Close()
	// 处理请求
	for {
		conn, _ := listener.Accept()
		go handleConn(conn)
	}
}

func handleConn(conn net.Conn) {
	buf := make([]byte, 1024)
	n, _ := conn.Read(buf)
	fmt.Println(string(buf[:n]))
	_ = conn.Close()
}

客户端。

func main() {
	conn, _ := net.Dial("tcp", ":8080")
	// 连接服务器
	defer conn.Close()
	// 发送数据
	_, _ = conn.Write([]byte("Hi"))
}
Http编程

服务端。

func main() {
	// 处理请求
	http.HandleFunc("/", handleConn)
	// 监听
	_ = http.ListenAndServe(":8080", nil)
}

func handleConn(w http.ResponseWriter, req *http.Request) {
	_, _ = w.Write([]byte("Hi"))
}

客户端。

func main() {
	resp, _ := http.Get("https://www.baidu.com")
	defer resp.Body.Close()
	buf := make([]byte, 10240)
	n, _ := resp.Body.Read(buf)
	fmt.Println(string(buf[:n]))
}

END

以上就是本文的全部内容,文档会根据自己的实际使用和各位提出的问题而不断更新。

如果觉得本文对您有一点点帮助,欢迎点赞、转发加关注,这会对我有非常大的帮助,如果有任何问题,欢迎在评论区留言,咱们下期见!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值