目录

数据类型

基本数据类型

复合数据类型

类型转换

零值

变量声明

使用var关键字声明变量

初始化变量

短变量声明:=

数组&切片

数组(Arrays)

声明数组:

初始化数组:

访问数组元素:

切片(Slices)

声明切片:

初始化切片:

使用make函数创建切片:

切片操作:

示例:

两者区别

结构体

声明结构体

创建结构体实例

访问结构体字段

匿名结构体

结构体嵌套

方法与接收者

接口

Map

声明和初始化Map

插入和访问Map元素

删除Map元素

检查Map中是否存在某个键

遍历Map

Map的长度

注意事项

循环

for 循环

range 循环

循环控制语句

无限循环

函数

函数的定义

函数的调用

函数参数

函数返回值

匿名函数

函数闭包

defer 关键字

协程

协程的创建

协程调度

协程特点

协程与线程的区别

协程的优势

通道

通道的特性

通道的创建

通道发送和接收操作

无缓冲通道

缓冲通道

关闭通道

文件操作

文件读取

1. 使用 os 包进行文件读取

2.使用 io/ioutil 包进行文件读取

文件写入

1. 使用 os 包进行文件写入

 2.使用 io/ioutil 包进行文件写入

反射

反射的基本概念

Type&Value

1. Type(类型)

2. Value(数值)

区别与联系

基本用法示例

HTTP网络编程

创建简单的 HTTP 服务器

发送 HTTP 请求

使用自定义处理器处理 HTTP 请求


数据类型

基本数据类型

  1. 整数类型(integers):用于表示整数值,包括有符号整数和无符号整数。

    • 有符号整数类型:int8、int16、int32、int64、int。
    • 无符号整数类型:uint8、uint16、uint32、uint64、uint。
  2. 浮点数类型(floats):用于表示实数值,包括32位浮点数和64位浮点数。

    • float32:单精度浮点数。
    • float64:双精度浮点数。
  3. 复数类型(complex):用于表示复数,包括complex64和complex128。

  4. 布尔类型(booleans):用于表示逻辑值,只有两个取值true和false。

  5. 字符串类型(strings):用于表示文本字符串,由一系列字符组成。

  6. 字节类型(bytes):用于表示原始数据,通常用于存储二进制数据。

  7. 指针类型(pointers):用于存储变量的内存地址。

复合数据类型

  1. 数组类型(arrays):用于存储固定长度的相同类型元素的集合。

  2. 切片类型(slices):用于存储动态长度的相同类型元素的集合,是对数组的抽象。

  3. 映射类型(maps):用于存储键值对的集合,每个键都是唯一的。

  4. 结构体类型(structs):用于表示具有不同属性的复合数据类型。

  5. 接口类型(interfaces):用于定义对象的行为,是一组方法签名的集合。

  6. 函数类型(functions):用于表示可以被调用的函数类型。

类型转换

在Go语言中,可以使用类型转换来将一个类型的值转换为另一个类型。

var num1 int = 10
var num2 float64 = float64(num1) // 将整数转换为浮点数

零值

在Go语言中,所有的变量都有一个默认的零值,即在声明变量但未初始化时的默认值。

变量声明

在Go语言中,变量声明可以通过关键字var或使用短变量声明:=来实现

使用var关键字声明变量

使用var关键字可以显式地声明一个或多个变量,语法如下:

var variableName type

 示例:

var age int // 声明一个整型变量age
var name string // 声明一个字符串变量name
var isStudent bool // 声明一个布尔型变量isStudent

初始化变量

在使用var声明变量时,可以同时对变量进行初始化,语法如下:

var variableName type = value

示例:

var score int = 90 // 声明一个整型变量score并初始化为90
var message string = "Hello, World!" // 声明一个字符串变量message并初始化为"Hello, World!"
var isReady bool = true // 声明一个布尔型变量isReady并初始化为true

 

短变量声明:=

Go语言还提供了一种简洁的方式来声明并初始化变量,即短变量声明:=,语法如下:

variableName := value

 示例:

age := 25 // 声明并初始化一个整型变量age,类型自动推断为int
name := "Alice" // 声明并初始化一个字符串变量name,类型自动推断为string
isPassed := false // 声明并初始化一个布尔型变量isPassed,类型自动推断为bool

数组&切片

数组(Arrays)

数组是具有固定长度且拥有相同数据类型元素的集合。

在声明数组时,需要指定数组的长度,并且该长度在创建后无法更改。

数组的索引是从0开始的整数,可以通过索引访问数组中的元素。

声明数组:

var arr [5]int // 声明一个包含5个整型元素的数组

初始化数组:

arr := [3]int{1, 2, 3} // 声明并初始化一个包含3个整型元素的数组

访问数组元素:

fmt.Println(arr[0]) // 访问数组arr的第一个元素

切片(Slices)

切片是对数组的抽象,提供了一种灵活、动态长度的数据结构。
切片不固定长度,可以根据需要动态增加或减少其长度。
切片是一个引用类型,底层指向一个数组。

声明切片:

var slice []int // 声明一个整型切片,长度和容量为0

初始化切片:

slice := []int{1, 2, 3} // 声明并初始化一个包含3个整型元素的切片

使用make函数创建切片:

slice := make([]int, 5) // 创建一个包含5个整型元素的切片

切片操作:

  • 切片支持类似数组的索引访问和切片操作。
  • 使用append函数向切片添加元素。
  • 利用切片表达式进行切片操作。
示例:
slice := []int{1, 2, 3, 4, 5}
fmt.Println(slice[0]) // 访问切片第一个元素
slice = append(slice, 6) // 向切片末尾添加元素6
newSlice := slice[1:3] // 对切片进行切片操作,获取索引1到2的元素

两者区别

结构体

在Go语言中,结构体(struct)是一种用户自定义的复合数据类型,用于表示一组不同类型的字段。结构体可以包含零个或多个字段,并且每个字段可以有不同的数据类型。下面是关于Go语言中结构体的详细介绍:

声明结构体

要声明一个结构体,需要使用type关键字和struct关键字,指定结构体名称及结构体包含的字段。

type Person struct {
    Name string
    Age  int
}

创建结构体实例

可以使用结构体类型来创建实例,并初始化其字段的值。

var p Person
p.Name = "Alice"
p.Age = 25

也可以在声明时直接初始化结构体实例:

p := Person{Name: "Bob", Age: 30}

访问结构体字段

可以使用.操作符访问结构体实例的字段:

fmt.Println(p.Name) // 访问结构体实例p的Name字段
fmt.Println(p.Age)  // 访问结构体实例p的Age字段

匿名结构体

在某些情况下,我们可以使用匿名结构体,即没有命名的结构体,直接在声明时定义结构体字段:

person := struct {
    Name string
    Age  int
}{Name: "Charlie", Age: 35}

结构体嵌套

结构体可以嵌套在其他结构体中,形成复杂的数据结构:

package main

import "fmt"

type Animal struct {
    Name string
}

func (a *Animal) Speak() {
    fmt.Println("Animal speaks")
}

type Dog struct {
    Animal // 结构体嵌套
    Breed  string
}

func main() {
    dog := Dog{
        Animal: Animal{Name: "Buddy"},
        Breed:  "Labrador",
    }

    fmt.Println(dog.Name) // 可以访问Animal结构体的字段
    fmt.Println(dog.Breed)

    dog.Speak() // 可以调用Animal结构体的方法
}

方法与接收者

结构体可以定义方法,方法是一种与特定类型关联的函数。在方法的定义中,可以指定一个接收者,即方法作用的对象。

func (p Person) sayHello() {
    fmt.Printf("Hello, my name is %s.\n", p.Name)
}
p.sayHello() // 调用Person结构体的sayHello方法

接口

Go 语言中的接口是一种抽象类型,它定义了一组方法的集合。接口提供了一种方式来指定对象的行为,而无需关注对象的具体类型。在 Go 中,接口由方法签名定义,而不包含实际的实现代码。一个类型只需要实现了接口定义的所有方法,就被认为是该接口的实现类型。

接口的定义形式如下:

type 接口名称 interface {
    方法1(参数列表) 返回值列表
    方法2(参数列表) 返回值列表
    // 更多方法
}

接口的结构实现主要包括以下几点:

  1. 接口定义:定义一个接口,声明接口中包含的方法,并指定方法的参数和返回值类型。

  2. 类型实现接口:某个具体的类型定义了接口中的所有方法,这个类型就被认为是接口的实现类型。

  3. 接口变量:可以使用接口类型的变量来存储任何实现了该接口的类型的实例。

  4. 接口断言:通过使用类型断言,可以判断一个接口变量是否实现了特定的接口,并获取其实际的类型。

 下面是一个简单的示例,演示了如何定义接口、实现接口并使用接口变量:

package main

import "fmt"

// 定义接口
type Shape interface {
    Area() float64
}

// 定义结构体 Circle
type Circle struct {
    Radius float64
}

// Circle 结构体实现 Shape 接口的 Area 方法
func (c Circle) Area() float64 {
    return 3.14 * c.Radius * c.Radius
}

func main() {
    // 创建一个 Circle 实例
    c := Circle{Radius: 5}

    // 将 Circle 实例赋值给 Shape 接口变量
    var s Shape
    s = c

    // 调用接口方法
    fmt.Println("Area of the circle:", s.Area())
}

 

Map

在Go语言中,Map(映射)是一种内置的数据结构,用于存储键值对集合。Map在其他编程语言中也被称为字典(Dictionary)、哈希表(Hash table)等。下面是关于Go语言中Map集合的详细介绍:

声明和初始化Map

要声明一个Map,可以使用map关键字,指定键的类型和值的类型:

var m map[string]int // 定义一个键为string类型,值为int类型的Map

在Go语言中,声明的Map变量默认值为nil,需要使用make函数来创建一个非空的Map:

m := make(map[string]int)

插入和访问Map元素

可以使用来插入或更新Map中的元素,并使用来获取对应的

m["apple"] = 5
fmt.Println(m["apple"]) // 输出:5

删除Map元素

可以使用delete函数删除Map中的指定元素:

delete(m, "apple") // 删除键为"apple"的元素

检查Map中是否存在某个键

可以使用多重赋值来检查Map中是否存在某个键:

value, ok := m["apple"]
if ok {
    fmt.Println("apple 存在,值为:", value)
} else {
    fmt.Println("apple 不存在")
}

遍历Map

可以使用range关键字来遍历Map中的键值对:

for key, value := range m {
    fmt.Println("Key:", key, "Value:", value)
}

Map的长度

可以使用len函数获取Map中键值对的数量:

fmt.Println("Map长度:", len(m))

注意事项

循环

在Go语言中,循环(Loop)是一种重复执行特定代码块的结构,用于简化重复性的任务。Go语言提供了两种类型的循环:for循环和range循环。

for 循环

for循环是Go语言中最常用的一种循环。它的基本语法如下:

for 初始语句; 条件表达式; 结束语句 {
    // 循环体
}

示例:

for i := 0; i < 5; i++ {
    fmt.Println(i)
}

range 循环

range循环可用于遍历数组、切片、字符串、map等集合。它的基本语法如下:

for index, value := range collection {
    // 循环体
}

示例:

numbers := []int{1, 2, 3, 4, 5}
for index, value := range numbers {
    fmt.Printf("Index: %d, Value: %d\n", index, value)
}

 

循环控制语句

在循环中,可以使用以下控制语句来控制循环的流程:

无限循环

如果省略for循环的条件表达式,将创建一个无限循环,需要在循环体内部使用break或其他方式来退出循环。

函数

Go语言中的函数是一种可重复使用的代码块,用于执行特定任务。函数可以接收参数并返回一个或多个值。下面是关于Go语言中函数的详细介绍:

函数的定义

在Go语言中,函数的定义使用func关键字,其基本语法如下:

func functionName(parameters) returnType {
    // 函数体
}

函数的调用

要调用函数,只需使用函数名和传递给函数的参数列表。如果函数有返回值,则可以将返回值赋给一个变量。

sum := add(3, 5)

函数参数

Go语言中的函数参数支持多种形式,包括:

示例:

func greet(name string) {
    fmt.Println("Hello, ", name)
}

func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

func divide(x, y float64) (quotient float64, err error) {
    if y == 0 {
        return 0, errors.New("cannot divide by zero")
    }
    quotient = x / y
    return quotient, nil
}

函数返回值

函数可以返回一个或多个值。当函数返回多个值时,它们用逗号分隔,并且可以用括号括起来。

func swap(x, y string) (string, string) {
    return y, x
}

匿名函数

匿名函数是一种没有函数名的函数,可以在声明时直接调用或赋值给变量。它们常用于函数内部、闭包和并发编程。

func() {
    fmt.Println("Anonymous function")
}()

add := func(x, y int) int {
    return x + y
}

函数闭包

闭包是一个函数值,它引用了其函数体之外的变量。该函数可以访问并修改这些外部变量,即使在其外部函数已经返回之后。

func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

func main() {
    counter1 := counter()
    fmt.Println(counter1()) // 输出 1
    fmt.Println(counter1()) // 输出 2

    counter2 := counter()
    fmt.Println(counter2()) // 输出 1
}

defer 关键字

defer关键字用于延迟函数的执行,它会在函数执行结束时执行,无论函数是正常返回还是发生了panic。

func hello() {
    defer fmt.Println("world")
    fmt.Println("hello")
}

func main() {
    hello() // 输出 hello world
}

协程

在Go语言中,协程(Goroutine)是一种轻量级的线程管理方式,由Go语言运行时环境管理。相比传统的线程,Go协程的创建和销毁开销更小,可以更高效地利用系统资源。以下是关于Go语言协程的详细介绍:

协程的创建

在Go语言中,使用关键字go可以创建一个协程,让函数在一个新的协程中并发执行。

func main() {
    go func() {
        fmt.Println("Hello, Goroutine!")
    }()
}

协程调度

Go语言的运行时会将协程调度到适当的系统线程上执行,实现了协程的并发执行。Go语言的调度器使用了类似用户级线程池的技术,可以高效地管理大量协程。

协程特点

  • 轻量级: 创建和销毁协程的开销很小,可以同时启动成千上万个协程。
  • 并发性: 协程能够实现并发执行,避免了传统线程的阻塞,提高程序的响应速度。
  • 通道通信: 协程之间通过通道(Channel)进行通信,实现数据的安全传递。

协程与线程的区别

  • 栈空间: 协程的栈空间动态增长与收缩,而线程的栈空间是固定的。
  • 调度: Go语言的协程由运行时环境进行调度,而线程由操作系统进行调度。
  • 开销: 协程的创建和销毁开销较小,线程的开销较大。

协程的优势

  • 简单性: 使用go关键字即可创建协程,无需手动管理线程。
  • 高效性: 协程的轻量级设计使得可以方便地创建大量并发任务。
  • 便捷的通信: 通过通道进行协程间通信,实现了数据的安全交换。

示例:

package main

import (
    "fmt"
    "time"
)

func sayHello() {
    for i := 0; i < 5; i++ {
        fmt.Println("Hello")
        time.Sleep(time.Second)
    }
}

func main() {
    go sayHello()
    
    for i := 0; i < 5; i++ {
        fmt.Println("World")
        time.Sleep(time.Second)
    }
}

通道

在Go语言中,通道(Channel)是一种用来在协程之间传递数据和同步的机制。通道可以避免显式使用锁或条件变量,简化了并发编程的复杂性。以下是关于Go语言通道的详细介绍:

通道的特性

通道的创建

在Go语言中,可以使用make()函数创建一个通道,指定通道中元素的类型。

ch := make(chan int) // 创建一个整数类型的通道

通道发送和接收操作

  • 使用<-运算符可以向通道发送数据,例如ch <- value
  • 使用<-运算符可以从通道接收数据,并赋值给一个变量,例如data := <- ch

无缓冲通道

无缓冲通道在发送和接收数据时会进行同步操作,发送者发送数据后会阻塞直到有接收者接收数据,接收者接收数据后会阻塞直到有发送者发送数据。

ch := make(chan int)

缓冲通道

缓冲通道可以在通道中存储一定数量的元素,发送者可以一直发送数据直到通道满,接收者可以一直接收数据直到通道空。

ch := make(chan int, 5) // 创建一个可以存储5个整数的缓冲通道

关闭通道

使用close()函数可以关闭通道,关闭后的通道不能再发送数据,但可以继续接收已发送的数据。

close(ch)

 

文件操作

在 Go 语言中,文件操作是一个常见的任务,可以通过标准库中的 osio/ioutil 包来实现文件读取和文件写入操作。下面详细介绍如何进行文件读取和文件写入:

文件读取

1. 使用 os 包进行文件读取

package main

import (
	"fmt"
	"os"
)

func main() {
	file, err := os.Open("test.txt")
	if err != nil {
		fmt.Println("Error opening file:", err)
		return
	}
	defer file.Close()

	data := make([]byte, 1024)
	count, err := file.Read(data)
	if err != nil {
		fmt.Println("Error reading file:", err)
		return
	}

	fmt.Println("Read", count, "bytes:", string(data))
}

2.使用 io/ioutil 包进行文件读取

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	data, err := ioutil.ReadFile("test.txt")
	if err != nil {
		fmt.Println("Error reading file:", err)
		return
	}

	fmt.Println("File content:", string(data))
}

文件写入

1. 使用 os 包进行文件写入

package main

import (
	"fmt"
	"os"
)

func main() {
	file, err := os.Create("output.txt")
	if err != nil {
		fmt.Println("Error creating file:", err)
		return
	}
	defer file.Close()

	content := []byte("Hello, World!\n")
	_, err = file.Write(content)
	if err != nil {
		fmt.Println("Error writing to file:", err)
		return
}

fmt.Println("Write successful")
}

 2.使用 io/ioutil 包进行文件写入

package main

import (
	"fmt"
	"io/ioutil"
)

func main() {
	content := []byte("Hello, World!\n")
	err := ioutil.WriteFile("output.txt", content, 0644)
	if err != nil {
		fmt.Println("Error writing to file:", err)
		return
	}

	fmt.Println("Write successful")
}

反射

在 Go 语言中,反射是指程序在运行时检查和操作变量、接口、切片、结构体等数据类型的能力。通过反射,我们可以动态地获取类型信息、调用方法、修改字段值等。Go 语言提供了内置的 reflect 包来支持反射操作。

下面详细介绍一下 Go 语言中的反射:

反射的基本概念

  1. 类型和数值的反射reflect 包中的 TypeValue 结构体代表了类型和数值的信息,可以通过反射获取和操作它们。

  2. 获取反射对象:使用 reflect.TypeOf()reflect.ValueOf() 函数可以获取任意类型的反射对象。

  3. 操作反射对象:通过反射对象可以获取类型信息、调用方法、修改字段值等操作。

Type&Value

在 Go 的反射机制中,reflect 包中的 TypeValue 结构体是用来表示类型信息和数值信息的两个重要部分。它们分别代表着不同的概念,下面将详细介绍它们:

1. Type(类型)

  • 概念reflect.Type 结构体表示一个 Go 类型的元数据信息,包括类型的名称、方法集、字段信息等。

  • 功能

    • 获取类型信息:可以通过 reflect.TypeOf() 函数获取任意对象的类型信息。
    • 比较类型:可以使用 == 运算符比较两个类型是否相等。
    • 分析类型:可以通过 Kind() 方法获取类型的种类(如 intstructslice 等)。
    • 获取字段和方法:可以通过 NumField()Field()NumMethod()Method() 等方法获取结构体字段和方法信息。
  • 示例

    type Person struct {
        Name string
        Age  int
    }
    
    pType := reflect.TypeOf(Person{})
    fmt.Println("Type:", pType.Name())
    fmt.Println("Number of fields:", pType.NumField())
    

2. Value(数值)

  • 概念reflect.Value 结构体表示一个具体的值,包括基本类型值、结构体实例、指针、函数等。

  • 功能

    • 获取值信息:可以通过 reflect.ValueOf() 函数获取任意对象的值信息。
    • 修改值:可以通过 SetXXX() 系列方法修改可修改的值(前提是该值可被修改)。
    • 调用方法:可以通过 MethodByName() 方法调用对象的方法。
    • 转换类型:可以通过 Interface() 方法将 Value 转换为接口类型。

区别与联系

  • 区别

    • Type 表示类型信息,包括方法、字段等的元数据。
    • Value 表示具体的值,可以是任意类型的实例或基本类型的值。
  • 联系

    • 通过 Type 可以获取类型信息,通过 Value 可以获取具体的数值信息。
    • 一般情况下,首先使用 reflect.TypeOf() 获取类型信息,然后使用 reflect.ValueOf() 获取值信息来进行反射操作。

基本用法示例

下面是一个简单的示例,演示了如何使用反射获取类型信息和调用方法:

package main

import (
	"fmt"
	"reflect"
)

type Person struct {
	Name string
	Age  int
}

func (p Person) SayHello() {
	fmt.Println("Hello, my name is", p.Name)
}

func main() {
	p := Person{Name: "Alice", Age: 30}

	// 获取反射对象
	v := reflect.ValueOf(p)
	t := reflect.TypeOf(p)

	// 获取类型信息
	fmt.Println("Type:", t)

	// 调用方法
	method := v.MethodByName("SayHello")
	if method.IsValid() {
		method.Call(nil)
	} else {
		fmt.Println("Method not found")
	}
}

在这个示例中,我们定义了一个 Person 结构体和一个 SayHello 方法。在 main 函数中,通过 reflect.TypeOf()reflect.ValueOf() 获取了 Person 实例的反射对象,然后打印了类型信息并调用了 SayHello 方法。

HTTP网络编程

Go 语言提供了强大的标准库来进行 HTTP 网络编程,开发者可以使用这些库来创建 Web 服务器、发送 HTTP 请求和处理 HTTP 响应。下面将详细介绍如何在 Go 中进行 HTTP 网络编程:

创建简单的 HTTP 服务器

创建一个简单的 HTTP 服务器,它监听 8080 端口并对所有请求返回 "Hello, World!"。

package main

import (
	"fmt"
	"net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, World!")
}

func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":8080", nil)
}

发送 HTTP 请求

使用 http.Get() 函数向指定 URL 发送了一个 GET 请求,并读取并打印了响应体内容。

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	resp, err := http.Get("https://www.example.com")
	if err != nil {
		fmt.Println("Error making GET request:", err)
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("Error reading response body:", err)
		return
	}

	fmt.Println(string(body))
}

使用自定义处理器处理 HTTP 请求

创建一个自定义的处理器 MyHandler,并将其用于处理 HTTP 请求

package main

import (
	"fmt"
	"net/http"
)

type MyHandler struct{}

func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, World!")
}

func main() {
	handler := &MyHandler{}
	server := &http.Server{
		Addr:    ":8080",
		Handler: handler,
	}
	server.ListenAndServe()
}
04-02 15:16