【Go】面向萌新的速通Golang基础语法学习笔记(详细)

目录

数据类型

基本数据类型

复合数据类型

类型转换

零值

变量声明

使用var关键字声明变量

初始化变量

短变量声明:=

数组&切片

数组(Arrays)

声明数组:

初始化数组:

访问数组元素:

切片(Slices)

声明切片:

初始化切片:

使用make函数创建切片:

切片操作:

示例:

两者区别

结构体

声明结构体

创建结构体实例

访问结构体字段

匿名结构体

结构体嵌套

方法与接收者

接口

Map

声明和初始化Map

插入和访问Map元素

删除Map元素

检查Map中是否存在某个键

遍历Map

Map的长度

注意事项

循环

[for 循环](#for 循环)

[range 循环](#range 循环)

循环控制语句

无限循环

函数

函数的定义

函数的调用

函数参数

函数返回值

匿名函数

函数闭包

[defer 关键字](#defer 关键字)

协程

协程的创建

协程调度

协程特点

协程与线程的区别

协程的优势

通道

通道的特性

通道的创建

通道发送和接收操作

无缓冲通道

缓冲通道

关闭通道

文件操作

文件读取

[1. 使用 os 包进行文件读取](#1. 使用 os 包进行文件读取)

[2.使用 io/ioutil 包进行文件读取](#2.使用 io/ioutil 包进行文件读取)

文件写入

[1. 使用 os 包进行文件写入](#1. 使用 os 包进行文件写入)

[2.使用 io/ioutil 包进行文件写入](#2.使用 io/ioutil 包进行文件写入)

反射

反射的基本概念

Type&Value

[1. Type(类型)](#1. Type(类型))

[2. Value(数值)](#2. Value(数值))

区别与联系

基本用法示例

HTTP网络编程

[创建简单的 HTTP 服务器](#创建简单的 HTTP 服务器)

[发送 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语言中,所有的变量都有一个默认的零值,即在声明变量但未初始化时的默认值。

  • 数值类型(int、float)的零值为0。
  • 布尔类型的零值为false。
  • 字符串类型的零值为空字符串""。
  • 指针类型的零值为nil。

变量声明

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

使用var关键字声明变量

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

var variableName type
  • variableName:变量名,遵循标识符命名规则。
  • type:变量的数据类型,例如int、string、float64等。

示例:

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

初始化变量

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

var variableName type = value
  • value:变量的初始值,必须与变量类型匹配。

示例:

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

短变量声明:=

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

variableName := value
  • 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))

注意事项

  • Map中的键必须是支持相等运算符的类型,例如整数、浮点数、字符串、指针、数组、结构体等。
  • Map是无序的,每次迭代的顺序可能不同。
  • 在并发情况下,对Map的操作不是并发安全的,需要使用互斥锁进行保护。

循环

在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 {
    // 循环体
}
  • index:当前元素的索引(或键)。
  • value:当前元素的值。

示例:

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

循环控制语句

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

  • break:跳出循环。
  • continue:跳过当前循环执行下一次循环。
  • goto:无条件跳转到指定标签处。

无限循环

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

函数

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

函数的定义

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

func functionName(parameters) returnType {
    // 函数体
}
  • 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() 方法获取类型的种类(如 int, struct, slice 等)。
    • 获取字段和方法:可以通过 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()
}
相关推荐
jerry6094 小时前
7天用Go从零实现分布式缓存GeeCache(改进)(未完待续)
分布式·缓存·golang
杜杜的man5 小时前
【go从零单排】Closing Channels通道关闭、Range over Channels
开发语言·后端·golang
甘橘籽10 小时前
【RPC】 gRPC、pb基本使用--经验与总结
golang
杜杜的man10 小时前
【go从零单排】HTTP客户端和服务端
开发语言·http·golang
材料苦逼不会梦到计算机白富美10 小时前
golang分布式缓存项目 Day6 防止缓存击穿
分布式·缓存·golang
杜杜的man13 小时前
【go从零单排】Environment Variables环境变量
golang
郝同学的测开笔记15 小时前
云原生探索系列(十二):Go 语言接口详解
后端·云原生·go
材料苦逼不会梦到计算机白富美15 小时前
golang HTTP基础
http·golang·iphone
友大冰17 小时前
Go 语言已立足主流,编程语言排行榜24 年 11 月
开发语言·后端·golang
hummhumm18 小时前
第 10 章 - Go语言字符串操作
java·后端·python·sql·算法·golang·database