golang基础
变量类型
下面是Golang中变量类型的分类和示例,整理成表格形式:
基本类型
类型 | 示例代码 | 说明 |
---|---|---|
布尔类型 | var b bool = true | 布尔值,取值为true 或false |
整数类型 | var i int = 10 | 有符号整数 |
无符号整数 | var u uint = 20 | 无符号整数 |
int8 | var i8 int8 = -128 | 8位有符号整数 |
uint8 | var ui8 uint8 = 255 | 8位无符号整数 |
int16 | var i16 int16 = 32767 | 16位有符号整数 |
uint16 | var ui16 uint16 = 65535 | 16位无符号整数 |
int32 | var i32 int32 = 2147483647 | 32位有符号整数 |
uint32 | var ui32 uint32 = 4294967295 | 32位无符号整数 |
int64 | var i64 int64 = 9223372036854775807 | 64位有符号整数 |
uint64 | var ui64 uint64 = 18446744073709551615 | 64位无符号整数 |
浮点数类型 | var f32 float32 = 3.14 | 32位浮点数 |
浮点数类型 | var f64 float64 = 2.718 | 64位浮点数 |
复数类型 | var c64 complex64 = 1 + 2i | 64位复数 |
复数类型 | var c128 complex128 = 3 + 4i | 128位复数 |
字符串类型 | var s string = "Hello, Go" | 字符串 |
复合类型
类型 | 示例代码 | 说明 |
---|---|---|
数组 | var arr [5]int = [5]int{1, 2, 3, 4, 5} | 固定长度的相同类型元素的集合 |
切片 | var slice []int = []int{1, 2, 3, 4, 5} | 动态数组 |
字典 (Map) | var m map[string]int = map[string]int{"one": 1, "two": 2} | 键值对的集合 |
结构体 | type Person struct { Name string; Age int } | 自定义类型的集合 |
指针 | var ptr *int; var x int = 10; ptr = &x | 指向某个变量地址的变量 |
函数 | var f func(int) int; f = func(x int) int { return x * x } | 函数类型 |
接口 | type Stringer interface { String() string } | 定义一组方法的集合 |
常量类型
类型 | 示例代码 | 说明 |
---|---|---|
常量 | const pi float64 = 3.1415 | 定义后值不可更改 |
批量常量声明 | const (Sunday = 0; Monday = 1; Tuesday = 2) | 批量声明多个常量 |
示例代码
package main
import ( "fmt")
func main() { // 基本类型 var b bool = true var i int = 42 var u uint = 100 var f32 float32 = 3.14 var f64 float64 = 2.718 var c64 complex64 = 1 + 2i var s string = "Hello, Go"
// 复合类型 var arr [3]int = [3]int{1, 2, 3} var slice []int = []int{4, 5, 6} var m map[string]int = map[string]int{"one": 1, "two": 2}
type Person struct { Name string Age int } var p Person = Person{Name: "Alice", Age: 25}
var ptr *int var x int = 10 ptr = &x
var f func(int) int f = func(x int) int { return x * x }
// 打印变量 fmt.Println(b) fmt.Println(i, u) fmt.Println(f32, f64) fmt.Println(c64) fmt.Println(s) fmt.Println(arr) fmt.Println(slice) fmt.Println(m) fmt.Println(p) fmt.Println(*ptr) fmt.Println(f(5))}
类型转换
在Go语言(Golang)中,类型转换(Type Conversion)用于将一种数据类型的值转换为另一种数据类型的值。Go语言不支持隐式类型转换,所有的类型转换都必须显式指定。以下是一些常见的类型转换方式和示例。
基本数据类型的转换
在Go中,可以使用类型名作为函数来进行类型转换。例如:
package main
import ( "fmt")
func main() { var a int = 10 var b float64 = float64(a) // int 转换为 float64 var c int = int(b) // float64 转换为 int fmt.Printf("a: %d, b: %f, c: %d\n", a, b, c)
var d string = "123" var e int fmt.Sscanf(d, "%d", &e) // string 转换为 int fmt.Printf("d: %s, e: %d\n", d, e)}
字符串和数值类型之间的转换
在Go中,字符串和数值类型之间的转换可以通过标准库中的 strconv
包实现。
package main
import ( "fmt" "strconv")
func main() { // int 转换为 string var a int = 10 var strA string = strconv.Itoa(a) fmt.Printf("strA: %s\n", strA)
// string 转换为 int var strB string = "20" var b int var err error b, err = strconv.Atoi(strB) if err != nil { fmt.Println("Error:", err) } else { fmt.Printf("b: %d\n", b) }
// float64 转换为 string var c float64 = 3.14159 var strC string = strconv.FormatFloat(c, 'f', -1, 64) fmt.Printf("strC: %s\n", strC)
// string 转换为 float64 var strD string = "2.71828" var d float64 d, err = strconv.ParseFloat(strD, 64) if err != nil { fmt.Println("Error:", err) } else { fmt.Printf("d: %f\n", d) }}
自定义类型的转换
Go语言允许定义自定义类型,并且可以通过显式转换在这些类型之间进行转换。
package main
import ( "fmt")
type MyInt int
func main() { var a int = 10 var b MyInt = MyInt(a) // int 转换为 MyInt fmt.Printf("b: %d\n", b)
var c MyInt = 20 var d int = int(c) // MyInt 转换为 int fmt.Printf("d: %d\n", d)}
接口类型的转换
在Go中,接口类型的转换可以通过类型断言(type assertion)和类型开关(type switch)实现。
类型断言
类型断言用于将接口类型转换为具体类型。
package main
import ( "fmt")
func main() { var i interface{} = "hello"
// 类型断言 s, ok := i.(string) if ok { fmt.Printf("s: %s\n", s) } else { fmt.Println("i is not a string") }}
类型开关
类型开关用于根据接口变量的具体类型执行不同的操作。
package main
import ( "fmt")
func main() { var i interface{} = 10
switch v := i.(type) { case int: fmt.Printf("i is an int: %d\n", v) case float64: fmt.Printf("i is a float64: %f\n", v) case string: fmt.Printf("i is a string: %s\n", v) default: fmt.Println("unknown type") }}
字符串格式化
在Go语言中,字符串格式化有几种常见的方法,以下是每种方法的示例:
1. 使用 fmt.Sprintf
这是Go语言中最常用的字符串格式化函数。
package main
import ( "fmt")
func main() { name := "Alice" age := 30 formattedString := fmt.Sprintf("My name is %s and I am %d years old.", name, age) fmt.Println(formattedString)}
2. 使用 fmt.Printf
这种方法适用于直接输出格式化的字符串。
package main
import ( "fmt")
func main() { name := "Alice" age := 30 fmt.Printf("My name is %s and I am %d years old.\n", name, age)}
3. 使用 fmt.Fprintf
这种方法适用于将格式化字符串写入到一个 io.Writer
接口(例如文件或网络连接)。
package main
import ( "fmt" "os")
func main() { name := "Alice" age := 30 fmt.Fprintf(os.Stdout, "My name is %s and I am %d years old.\n", name, age)}
4. 使用 strings.Builder
对于大量的字符串拼接和构建,strings.Builder
提供了更高效的方法。
package main
import ( "fmt" "strings")
func main() { name := "Alice" age := 30 var sb strings.Builder sb.WriteString("My name is ") sb.WriteString(name) sb.WriteString(" and I am ") fmt.Fprintf(&sb, "%d", age) sb.WriteString(" years old.") formattedString := sb.String() fmt.Println(formattedString)}
5. 使用字符串拼接
对于简单的字符串拼接,可以直接使用 +
运算符。
package main
import ( "fmt")
func main() { name := "Alice" age := 30 formattedString := "My name is " + name + " and I am " + fmt.Sprint(age) + " years old." fmt.Println(formattedString)}
变量定义
在Golang中,变量的定义有多种方式,主要包括显式类型声明、隐式类型声明、批量声明和短变量声明。以下是详细介绍:
1. 显式类型声明
这是最基本的变量定义方式,明确指定变量的类型。
var x intx = 10
var y stringy = "Hello"
2. 隐式类型声明
Go支持根据初始值自动推断变量类型,因此可以省略类型声明。
var a = 10 // a 是 int 类型var b = "Go" // b 是 string 类型
3. 短变量声明
这是Go特有的一种简洁的变量声明方式,常用于函数内部。这种方式只能用于函数内部,不能用于全局变量。
c := 20 // c 是 int 类型d := "Go" // d 是 string 类型
4. 批量声明
可以在一个声明语句中定义多个变量。
var ( e int f string g float64)
或者使用批量声明并赋值:
var ( h, i int = 5, 10 j, k = "Hello", 15.5)
5. 常量声明
常量的值在程序运行时不能改变。常量声明可以显式指定类型,也可以使用隐式类型。
const pi float64 = 3.1415const e = 2.7182
批量声明常量:
const ( Sunday = 0 Monday = 1 Tuesday = 2)
6. 全局变量
全局变量可以在包级别声明,通常在包的最上方。
package main
import "fmt"
var globalVar int = 100
func main() { fmt.Println(globalVar)}
示例代码
以下是一个示例代码,展示了上述各种变量声明方式的使用:
package main
import "fmt"
// 全局变量var global int = 100
func main() { // 显式类型声明 var x int x = 10
// 隐式类型声明 var y = "Hello"
// 短变量声明 z := 3.14
// 批量声明 var ( a int b string ) a = 20 b = "Go"
// 常量声明 const pi float64 = 3.1415
// 打印变量 fmt.Println(x) fmt.Println(y) fmt.Println(z) fmt.Println(a, b) fmt.Println(pi) fmt.Println(global)}
条件判断
在Golang中,判断语句主要包括if
语句和switch
语句。下面是这些语句的详细介绍和示例。
if
语句
if
语句用于根据条件执行代码块。
基本语法
if condition { // 执行的代码}
带有else
的if
语句
if condition { // 条件为 true 时执行的代码} else { // 条件为 false 时执行的代码}
多个条件判断
if condition1 { // 条件1为 true 时执行的代码} else if condition2 { // 条件2为 true 时执行的代码} else { // 上述条件都为 false 时执行的代码}
示例代码
package main
import ( "fmt")
func main() { x := 10
if x > 5 { fmt.Println("x is greater than 5") } else if x == 5 { fmt.Println("x is equal to 5") } else { fmt.Println("x is less than 5") }}
switch
语句
switch
语句用于基于多个条件值执行不同的代码块。
基本语法
switch variable {case value1: // 当变量等于 value1 时执行的代码case value2: // 当变量等于 value2 时执行的代码default: // 变量不等于任何已定义的值时执行的代码}
switch
语句中的表达式
switch
语句也可以基于表达式的结果进行判断。
switch {case x > 10: fmt.Println("x is greater than 10")case x == 10: fmt.Println("x is equal to 10")default: fmt.Println("x is less than 10")}
示例代码
package main
import ( "fmt")
func main() { day := "Monday"
switch day { case "Monday": fmt.Println("Today is Monday") case "Tuesday": fmt.Println("Today is Tuesday") default: fmt.Println("Today is not Monday or Tuesday") }
x := 10
switch { case x > 10: fmt.Println("x is greater than 10") case x == 10: fmt.Println("x is equal to 10") default: fmt.Println("x is less than 10") }}
循环
在Golang中,循环语句主要包括for
循环。Go语言只有一种循环结构,即for
循环,但它可以通过不同的方式使用来实现多种循环功能,例如普通的for循环、基于条件的循环、和无限循环。下面是这些循环语句的详细介绍和示例。
基本的 for
循环
语法
for 初始化语句; 条件表达式; 迭代语句 { // 循环体}
示例
package main
import ( "fmt")
func main() { for i := 0; i < 5; i++ { fmt.Println(i) }}
基于条件的 for
循环
语法
for 条件表达式 { // 循环体}
示例
package main
import ( "fmt")
func main() { i := 0 for i < 5 { fmt.Println(i) i++ }}
无限循环
语法
for { // 循环体}
示例
package main
import ( "fmt")
func main() { i := 0 for { fmt.Println(i) i++ if i >= 5 { break } }}
for
循环遍历数组、切片、map 和字符串
数组和切片
package main
import ( "fmt")
func main() { arr := []int{1, 2, 3, 4, 5} for index, value := range arr { fmt.Println(index, value) }}
Map
package main
import ( "fmt")
func main() { m := map[string]int{"a": 1, "b": 2, "c": 3} for key, value := range m { fmt.Println(key, value) }}
字符串
package main
import ( "fmt")
func main() { str := "Hello" for index, runeValue := range str { fmt.Printf("%d: %c\n", index, runeValue) }}
函数
在Golang中,函数是代码组织和重用的基本单位。函数定义包括函数名、参数列表、返回值类型以及函数体。下面是Go中函数定义的详细介绍和示例。
基本函数定义
语法
func 函数名(参数列表) 返回值类型 { // 函数体}
示例
package main
import ( "fmt")
func add(x int, y int) int { return x + y}
func main() { sum := add(3, 4) fmt.Println(sum) // 输出: 7}
多返回值函数
Go函数可以返回多个值,这在处理错误时非常有用。
示例
package main
import ( "fmt" "errors")
func divide(x, y float64) (float64, error) { if y == 0 { return 0, errors.New("division by zero") } return x / y, nil}
func main() { result, err := divide(10, 2) if err != nil { fmt.Println("Error:", err) } else { fmt.Println("Result:", result) // 输出: 5 }}
命名返回值
Go允许为返回值命名,这样可以在函数体内直接使用这些变量,并且可以使用return
关键字返回所有命名返回值。
示例
package main
import ( "fmt")
func swap(x, y string) (first, second string) { first = y second = x return}
func main() { a, b := swap("hello", "world") fmt.Println(a, b) // 输出: world hello}
匿名函数和闭包
匿名函数是在没有名字的情况下定义的函数,闭包是引用了其外围作用域中变量的函数。
示例
package main
import ( "fmt")
func main() { add := func(x, y int) int { return x + y } fmt.Println(add(3, 4)) // 输出: 7
// 闭包示例 counter := func() func() int { i := 0 return func() int { i++ return i } }
c := counter() fmt.Println(c()) // 输出: 1 fmt.Println(c()) // 输出: 2}
变参函数
变参函数可以接受不定数量的参数。
示例
package main
import ( "fmt")
func sum(numbers ...int) int { total := 0 for _, number := range numbers { total += number } return total}
func main() { fmt.Println(sum(1, 2, 3, 4)) // 输出: 10 fmt.Println(sum(10, 20)) // 输出: 30}
方法
方法是绑定到特定类型的函数。
示例
package main
import ( "fmt")
type Rectangle struct { width, height float64}
// 为Rectangle类型定义方法func (r Rectangle) area() float64 { return r.width * r.height}
func main() { rect := Rectangle{width: 10, height: 5} fmt.Println("Area:", rect.area()) // 输出: Area: 50}
组合
在Golang中,继承不是通过类和子类来实现的,因为Go没有传统的类继承机制。相反,Go通过组合和接口来实现代码复用和多态性。
组合
组合是一种包含一个或多个其他结构体类型的字段,从而实现类似于继承的效果。
示例
package main
import ( "fmt")
type Person struct { Name string Age int}
type Employee struct { Person Position string}
func main() { emp := Employee{ Person: Person{ Name: "Alice", Age: 30, }, Position: "Engineer", }
// 可以直接访问嵌入的Person结构体的字段 fmt.Println("Name:", emp.Name) fmt.Println("Age:", emp.Age) fmt.Println("Position:", emp.Position)}
在这个示例中,Employee
结构体嵌入了Person
结构体,从而可以直接访问Person
的字段,就像传统的继承一样。
接口
接口定义了一组方法,任何实现了这些方法的类型都被认为实现了该接口。接口用于实现多态性。
示例
package main
import ( "fmt")
// 定义一个接口type Describer interface { Describe() string}
type Person struct { Name string Age int}
func (p Person) Describe() string { return fmt.Sprintf("%s is %d years old.", p.Name, p.Age)}
type Employee struct { Person Position string}
func (e Employee) Describe() string { return fmt.Sprintf("%s is %d years old and works as a %s.", e.Name, e.Age, e.Position)}
func main() { var d Describer
p := Person{Name: "Bob", Age: 24} d = p fmt.Println(d.Describe())
e := Employee{ Person: Person{Name: "Alice", Age: 30}, Position: "Engineer", } d = e fmt.Println(d.Describe())}
在这个示例中,Person
和Employee
都实现了Describer
接口的Describe
方法,因此它们可以作为Describer
类型使用。
多态性
通过组合和接口,Go实现了多态性,可以根据实际类型调用相应的方法。
示例
package main
import ( "fmt")
type Describer interface { Describe() string}
type Person struct { Name string Age int}
func (p Person) Describe() string { return fmt.Sprintf("%s is %d years old.", p.Name, p.Age)}
type Employee struct { Person Position string}
func (e Employee) Describe() string { return fmt.Sprintf("%s is %d years old and works as a %s.", e.Name, e.Age, e.Position)}
func printDescription(d Describer) { fmt.Println(d.Describe())}
func main() { p := Person{Name: "Bob", Age: 24} e := Employee{ Person: Person{Name: "Alice", Age: 30}, Position: "Engineer", }
printDescription(p) printDescription(e)}
在这个示例中,printDescription
函数接受一个Describer
接口类型的参数,因此它可以处理任何实现了Describer
接口的类型。
错误处理
在Golang中,错误处理是一种显式的、结构化的方式,主要通过返回错误值来处理。Go语言没有异常机制,而是通过返回值的方式处理错误。以下是Go语言中错误处理的详细介绍和示例。
错误类型
Go语言中,错误类型是内置的接口类型error
,它定义如下:
type error interface { Error() string}
任何实现了Error()
方法的类型都可以作为错误类型。
创建错误
可以使用errors
包中的New
函数创建一个简单的错误:
import ( "errors")
var errExample = errors.New("this is an example error")
返回错误
函数通常会返回一个error
类型的值来表示是否发生了错误。返回值可以是nil
,表示没有错误。
示例
package main
import ( "errors" "fmt")
// 定义一个可能返回错误的函数func divide(x, y float64) (float64, error) { if y == 0 { return 0, errors.New("division by zero") } return x / y, nil}
func main() { result, err := divide(10, 2) if err != nil { fmt.Println("Error:", err) } else { fmt.Println("Result:", result) }
result, err = divide(10, 0) if err != nil { fmt.Println("Error:", err) } else { fmt.Println("Result:", result) }}
自定义错误类型
可以定义一个自定义的错误类型,通过实现Error()
方法来满足error
接口。
示例
package main
import ( "fmt")
// 自定义错误类型type MyError struct { Code int Message string}
func (e *MyError) Error() string { return fmt.Sprintf("Error %d: %s", e.Code, e.Message)}
func riskyOperation(x int) (int, error) { if x < 0 { return 0, &MyError{Code: 123, Message: "negative number"} } return x * 2, nil}
func main() { result, err := riskyOperation(-1) if err != nil { fmt.Println("Error:", err) } else { fmt.Println("Result:", result) }
result, err = riskyOperation(10) if err != nil { fmt.Println("Error:", err) } else { fmt.Println("Result:", result) }}
包装错误
可以使用fmt.Errorf
来格式化和包装错误信息。
示例
package main
import ( "fmt" "errors")
func validateAge(age int) error { if age < 0 { return fmt.Errorf("invalid age %d: %w", age, errors.New("age cannot be negative")) } return nil}
func main() { err := validateAge(-5) if err != nil { fmt.Println("Error:", err) } else { fmt.Println("Age is valid") }}
通过errors
包检查和比较错误
Go 1.13引入了errors.Is
和errors.As
函数,用于错误检查和比较。
示例
package main
import ( "errors" "fmt")
var ErrNegativeNumber = errors.New("negative number")
func riskyOperation(x int) (int, error) { if x < 0 { return 0, ErrNegativeNumber } return x * 2, nil}
func main() { _, err := riskyOperation(-1) if err != nil { if errors.Is(err, ErrNegativeNumber) { fmt.Println("Encountered a negative number error") } else { fmt.Println("Encountered an unknown error") } }}
项目结构
在Golang中,项目结构的设计是开发过程中一个非常重要的部分。一个良好的项目结构可以提高代码的可维护性和可扩展性。以下是一个典型的Go项目结构以及每个部分的说明和示例。
典型的Go项目结构
myproject/├── bin/├── cmd/│ ├── myapp/│ │ └── main.go├── pkg/│ └── mypackage/│ ├── mypackage.go│ └── mypackage_test.go├── internal/│ └── myinternalpackage/│ ├── myinternalpackage.go│ └── myinternalpackage_test.go├── api/│ └── v1/│ ├── api.go│ └── api_test.go├── web/│ ├── static/│ └── templates/├── scripts/│ └── build.sh├── configs/│ └── config.yaml├── deployments/│ └── deployment.yaml├── test/│ ├── e2e/│ └── integration/├── go.mod└── go.sum
目录和文件说明
-
bin/
:编译后的二进制文件目录。可以通过构建脚本将编译输出设置到此目录。 -
cmd/
:存放项目的主要应用入口。每个子目录代表一个可独立构建和运行的应用。cmd/myapp/main.go
:项目的主要入口文件,包含main
函数。
-
pkg/
:存放可供其他项目使用的库代码。pkg/mypackage/
:一个公共包,包含主要功能代码和测试文件。
-
internal/
:存放私有包,这些包仅对本项目可见,其他项目无法导入。internal/myinternalpackage/
:一个内部包,包含内部功能代码和测试文件。
-
api/
:存放API相关代码,通常用于定义API路由和处理请求。api/v1/
:API的v1版本目录。api/v1/api.go
:API的实现文件。api/v1/api_test.go
:API的测试文件。
-
web/
:存放Web相关资源,包括静态文件和模板。web/static/
:静态文件目录。web/templates/
:模板文件目录。
-
scripts/
:存放构建、安装、分析等操作的脚本。scripts/build.sh
:构建脚本。
-
configs/
:存放配置文件。configs/config.yaml
:配置文件示例。
-
deployments/
:存放部署相关文件。deployments/deployment.yaml
:Kubernetes部署文件示例。
-
test/
:存放测试相关文件。test/e2e/
:端到端测试文件目录。test/integration/
:集成测试文件目录。
-
go.mod
:模块依赖管理文件,定义项目的模块名称和依赖项。 -
go.sum
:记录所有依赖项的精确版本和校验和。
示例代码
cmd/myapp/main.go
package main
import ( "example.com/myproject/pkg/mypackage" "fmt")
func main() { result := mypackage.MyFunction() fmt.Println(result)}
pkg/mypackage/mypackage.go
package mypackage
func MyFunction() string { return "Hello from mypackage!"}
pkg/mypackage/mypackage_test.go
package mypackage
import "testing"
func TestMyFunction(t *testing.T) { expected := "Hello from mypackage!" result := MyFunction() if result != expected { t.Errorf("expected '%s' but got '%s'", expected, result) }}
其他常见目录结构
根据项目的规模和需求,项目结构可以有所不同。以下是一些常见的项目结构变化:
-
小型项目:不需要复杂的目录结构,可以直接在根目录下放置所有代码。
myproject/├── main.go├── mypackage.go├── mypackage_test.go├── go.mod└── go.sum -
大型项目:可以引入更多的目录层次和模块划分。
myproject/├── cmd/├── pkg/├── internal/├── api/├── web/├── scripts/├── configs/├── deployments/├── test/├── docs/ # 文档目录├── tools/ # 工具目录├── third_party/# 第三方库目录├── examples/ # 示例代码目录├── go.mod└── go.sum