基础语法
Hello World!
package main // 代表这个文件是main包,即程序的入口文件
// 导入包
import (
"fmt" // 导入fmt包,用于格式化输出
)
// 定义一个函数
func main() {
fmt.Println("hello world")
}
执行方法:
// 直接运行
go run main.go
// 编译成二进制文件
go build main.go // 会生成main可执行文件
Go的变量和数据类型
变量的声明和初始化
var关键字:可以声明一个或多个变量,并分别初始化它们,或者同时声明并初始化。
var x int // 声明一个变量
var ( // 声明多个变量
y string
z float64
)
// 初始化
x = 10
y = "hello"
z = 3.14
// 同时声明并初始化
var (
d int = 100 // 声明并初始化一个 int 类型的变量 d
e string = "Go" // 声明并初始化一个 string 类型的变量 e
f float64 = 1.618 // 声明并初始化一个 float64 类型的变量 f
)
// 直接初始化变量时,可以省略类型,Go 会根据赋值的值推导出变量的类型
var a = 42 // a 被推导为 int
var b = "Go" // b 被推导为 string
var c = 3.14 // c 被推导为 float64
- 简短变量声明:使用
<font style="color:rgb(77, 77, 77);">:=</font>声明并初始化一个或多个变量,这种方式只在函数内部有效****。
x, y, z := 10, "hello", 3.14
- 常量声明: 使用
const关键字声明常量,这些常量的值在程序运行期间是不可变的。
const pi = 3.14
const greeting = "Hello, World!"
- 匿名变量: 在某些情况下,如果不需要使用某个返回值,可以使用匿名变量
_来丢弃它。
x, _ := someFunction() // 忽略第二个返回值
**make**函数:初始化切片、映射和通道(channel)这三种内置数据结构,返回已初始化的类型的值。
slice := make([]int, 5) // 创建一个长度为 5 的 int 类型切片
mapVar := make(map[string]int) // 创建一个 string 到 int 的映射
channel := make(chan int) // 创建一个 int 类型的通道
基本数据类型
Go语言是强类型语言,每个变量都会有固定的类型
整数类型
- 有符号整数:
int(根据系统架构可为 32 或 64 位)、int8、int16、int32、int64 - 无符号整数:
uint、uint8、uint16、uint32、uint64 - 特殊无符号类型:
uintptr,用于存储指针地址
浮点数类型
- 单精度:
float32 - 双精度:
float64
复数类型
- 复数:
complex64(实部和虚部都是 float32) - 复数:
complex128(实部和虚部都是 float64)
布尔类型
- 布尔:
bool(值为true或false)
字符串类型
- 字符串:
string,表示 UTF-8 编码的文本
其他类型
- 字节类型:
byte(相当于uint8) - 字符类型:
rune(相当于int32,用于表示 Unicode 码点)
数组(Array):
- 定义固定大小的元素序列,每个元素必须是相同的类型。
- 数组长度是其类型的一部分,因此
[3]int和[4]int是不同的类型。
var arr [5]int = [5]int{1, 2, 3, 4, 5}
// 二维
var twoD [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
twoD[i][j] = i + j
}
}切片(Slice):
- 动态大小的序列,比数组更灵活。切片不需要指定长度,可以动态增长。
- 切片是对底层数组的引用,因此切片更为灵活且高效。
var s []int = []int{1, 2, 3, 4, 5}
s = append(s, 6, 7)
s = append(s, 6, 7)
映射(Map):
- 键值对的数据结构,类似于其他语言的字典或哈希表。
- 可以通过键快速查找对应的值。
var m map[string]int = map[string]int{"Alice": 23, "Bob": 25}结构体(Struct):
- 聚合多个不同类型的字段,适合表示复杂的数据结构。
- 每个字段可以有不同的类型,可以定义为命名或匿名字段。
type Person struct {
Name string
Age int
}
var p = Person{Name: "Alice", Age: 30}接口(Interface):
- 接口是一种抽象类型,定义了一组方法而不具体实现。
- 可以通过接口实现多态,任何类型只要实现了接口定义的所有方法,就可以视为该接口的类型。
type Speaker interface {
Speak() string
}指针(Pointer):
- 指针是存储变量地址的变量,允许在函数中传递大型结构体时避免拷贝以提高效率。
- Go 没有指针运算,但可以通过
&和*操作符操作指针。
var a int = 10
var ptr *int = &apackage main
import "fmt"
func main() {
number := 10
if number > 0 { // 与C++的不同,没有括号
fmt.Println("Positive number")
} else if number < 0 {
fmt.Println("Negative number")
} else {
fmt.Println("Zero")
}
}
循环语句
Go 只有一种循环结构,即 for 循环,可以用于多种情况,包括传统的 for 循环和 while 风格的循环。
package main
import "fmt"
func main() {
for i := 0; i < 5; i++ {
fmt.Println(i)
}
}
package main
import "fmt"
func main() {
j := 0
for j < 5 {
fmt.Println(j)
j++
}
}
package main
import "fmt"
func main() {
k := 0
for {
if k >= 5 {
break
}
fmt.Println(k)
k++
}
}
switch语句
switch 语句用于替代多重 if 语句,根据不同的条件执行不同的代码块。与C++不同的是,Go中在执行完某个case时不需要加break关键字,不会执行到其他case中。
另外,Go中switch对变量类型没有限制。几乎可以替换if使用。
package main
import (
"fmt"
"time"
)
func main() {
a := 2
switch a {
case 1:
fmt.Println("one") // 不需要加break
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
case 4, 5:
fmt.Println("four or five")
default:
fmt.Println("other")
}
t := time.Now()
switch { // 类似于 if else
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
}
range语句
在 Go 语言中,range 关键字用于遍历切片、数组、映射、字符串和通道等数据结构。它简化了循环操作,避免了手动索引。
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
sum := 0
for i, num := range nums {
sum += num
if num == 2 {
fmt.Println("index:", i, "num:", num) // index: 0 num: 2
}
}
fmt.Println(sum) // 9
m := map[string]string{"a": "A", "b": "B"}
for k, v := range m {
fmt.Println(k, v) // b 8; a A
}
for k := range m {
fmt.Println("key", k) // key a; key b
}
}
函数语句
Go语言中函数的参数和返回值的类型是跟在参数名后,而不是在前面。且函数可以返回多个值(类似于Python),这在处理错误或同时返回多个结果时非常方便。例如,可以返回计算结果和状态值。
package main
import "fmt"
func add(a int, b int) int {
return a + b
}
func add2(a, b int) int {
return a + b
}
func exists(m map[string]string, k string) (v string, ok bool) {
v, ok = m[k]
return v, ok
}
func main() {
res := add(1, 2)
fmt.Println(res) // 3
v, ok := exists(map[string]string{"a": "A"}, "a")
fmt.Println(v, ok) // A True
}
结构体方法
在 Go 语言中,结构体(struct)可以定义方法,以便与特定的结构体类型关联。方法是与特定类型(通常是结构体)相关联的函数,可以通过该类型的实例调用。这使得 Go 支持面向对象的编程风格。 (即类成员函数)
func (receiver Type) MethodName(parameters) ReturnType {
// 方法体
}
/*
receiver:接收者,表示方法所属于的结构体类型。
Type:结构体类型。
MethodName:方法名。
parameters:方法参数。
ReturnType:返回值类型。
*/
// 示例
package main
import "fmt"
type user struct {
name string
password string
}
func (u user) checkPassword(password string) bool {
return u.password == password
}
func (u *user) resetPassword(password string) {
u.password = password
}
func main() {
a := user{name: "wang", password: "1024"}
a.resetPassword("2048") // 和成员函数一样
fmt.Println(a.checkPassword("2048")) // true
}
错误处理
在 Go 语言中,错误处理是通过返回值实现的,通常函数的最后一个返回值是 error 类型。这种设计使得错误处理显式且简洁。 Go 的 error 类型是一个接口,定义如下:
type error interface {
Error() string
}
任何实现了 Error() 方法的类型都可以被视为 error 类型。
错误处理示例:
package main
import (
"errors"
"fmt"
)
type user struct {
name string
password string
}
func findUser(users []user, name string) (v *user, err error) {
for _, u := range users {
if u.name == name {
return &u, nil
}
}
return nil, errors.New("not found")
}
func main() {
u, err := findUser([]user{{"wang", "1024"}}, "wang")
if err != nil { // 有错误
fmt.Println(err)
return
}
fmt.Println(u.name) // wang
if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
fmt.Println(err) // not found
return
} else {
fmt.Println(u.name)
}
}
在 Go 语言中,
nil是一个预定义的标识符,表示“没有值”或“无效值”。 在错误处理中,一般用来表示没有错误。
字符串相关操作
在 Go 语言中,字符串是一个不可变的基本数据类型。Go 提供了丰富的内置函数和标准库来操作字符串,主要集中在 strings 包中。以下是一些常用的字符串操作函数和示例:
- 长度:获取字符串的长度(字符数)。
str := "Hello, 世界"
length := len(str) // 字节长度
- 拼接:使用
+运算符或strings.Join()函数拼接字符串。
s1 := "Hello"
s2 := "World"
result := s1 + ", " + s2 // 使用 + 运算符
:::info
**strings**** 包中的常用函数**
:::
import "strings"
// 包含
contains := strings.Contains("Hello, World", "World") // true
// 计数
count := strings.Count("Hello, Hello", "Hello") // 2
// 前缀和后缀
hasPrefix := strings.HasPrefix("Hello, World", "Hello") // true
hasSuffix := strings.HasSuffix("Hello, World", "World") // true
// 拆分和连接
parts := strings.Split("a,b,c", ",") // ["a", "b", "c"]
joined := strings.Join([]string{"a", "b", "c"}, ",") // "a,b,c"
// 去除空白
trimmed := strings.TrimSpace(" Hello, World ") // "Hello, World"
// 转换大小写
lower := strings.ToLower("Hello, World") // "hello, world"
upper := strings.ToUpper("Hello, World") // "HELLO, WORLD"
// 替换
replaced := strings.Replace("Hello, World", "World", "Go", 1) // "Hello, Go"
格式化输出
在 Go 语言中,格式化输出通过 fmt 包提供的函数实现,特别是 fmt.Printf 函数。
**fmt.Printf**:
fmt.Printf函数用于格式化输出,可以控制输出的格式。**%v**:默认格式,输出值的表示。fmt.Printf("s=%v\n", s)输出字符串的默认格式。fmt.Printf("n=%v\n", n)输出整数的默认格式。fmt.Printf("p=%v\n", p)输出结构体的默认格式。
**%+v**:输出结构体时包括字段名。fmt.Printf("p=%+v\n", p)输出字段名和字段值。
**%#v**:输出值的 Go 语法表示。fmt.Printf("p=%#v\n", p)输出结构体的完整类型信息和字段值。
**%.2f**:格式化浮点数,保留两位小数。fmt.Printf("%.2f\n", f)输出浮点数的格式化表示。
package main
import "fmt"
// 定义一个结构体 point
type point struct {
x, y int
}
func main() {
s := "hello" // 字符串
n := 123 // 整数
p := point{1, 2} // 结构体实例
// 基本输出
fmt.Println(s, n) // 输出: hello 123
fmt.Println(p) // 输出: {1 2}
// 使用格式化输出
fmt.Printf("s=%v\n", s) // 输出: s=hello
fmt.Printf("n=%v\n", n) // 输出: n=123
fmt.Printf("p=%v\n", p) // 输出: p={1 2}
fmt.Printf("p=%+v\n", p) // 输出: p={x:1 y:2}
fmt.Printf("p=%#v\n", p) // 输出: p=main.point{x:1, y:2}
f := 3.141592653 // 浮点数
fmt.Println(f) // 输出: 3.141592653
fmt.Printf("%.2f\n", f) // 输出: 3.14(保留两位小数)
}