当前位置: 首页 > article >正文

Golang开发

Golang

文章目录

  • Golang
    • 预备技术
      • 一、算法与数据结构
        • 第1章:基础算法
        • 第2章:数据结构
        • 第3章:搜索与图论
        • 第4章:数论
        • 第5章:动态规划
        • 第6章:贪心
        • 第7章:算法竞赛入门
      • 二、Linux操作系统与Shell编程
      • 三、计算机操作系统
      • 四、计算机组成原理
      • 五、计算机网络
      • 六、Docker:云原生核心技术之一
    • Go 开发编程实战
      • 一、基本语法
        • 第1章:走进Golang
        • 第2章:变量与数据类型
        • 第3章:运算符
        • 第4章:流程控制
        • 第5章:函数
        • 第6章:错误处理
        • 第7章:数组
        • 第8章:切片
        • 第9章:映射
        • 第10章:面向对象
        • 第11章:文件的操作
        • 第12章:协程和管道
          • 1. 程序、进程、线程、协程
            • (1)程序 (program)
            • (2)进程 (process)
            • (3)线程 (thread)
            • (4)协程 (goroutine)
          • 2. 协程入门
            • (1)案例
          • 3. 主死从随
            • (1)主死从随
          • 4. 启动多个协程
            • (1)案例
          • 5. 使用WaitGroup控制协程退出
            • (1)WaitGroup的作用
            • (2)主要方法
            • (3)案例代码
          • 6. 多个协程操纵统一数据案例(互斥锁)
            • (1)案例
          • 7. 读写锁的使用
            • (1)互斥锁
            • (2)读写锁
            • (3)案例
          • 8. 管道介绍
            • (1)管道(channel)特质介绍
          • 9. 管道入门案例
            • (1)管道的定义
            • (2)案例
          • 10. 管道的关闭
            • (1)管道的关闭
            • (2)案例
          • 11. 管道的遍历
            • (1)管道的遍历
          • 12. 协程和管道协同工作的案例
            • (1)案例需求
            • (2)原理图
            • (3)代码
          • 13. 声明只读只写管道
            • (1)管道可以声明为只读或者只写性质
            • (2)代码:
          • 14. 管道的阻塞
            • (1)当管道只写入数据,没有读取,就会出现阻塞
            • (2)写的快,读的满(管道读写频率不一致),不会出现阻塞问题
          • 15. select功能
            • (1)select功能
            • (2)代码
          • 16. defer + recover机制处理错误
            • (1)问题原因
            • (2)解决办法
            • (3)案例
        • 第13章:网络编程
          • 1. 引入
            • (1)网络编程
            • (2)通信两个重要的要素:IP+PORT
            • (3)通信协议
            • (4)TCP协议:可靠的
            • (5)UDP协议:不可靠的
          • 2. 基于TCP协议的网络通信——创建客户端
            • (1)调用Dial函数
            • (2)代码
          • 3. 基于TCP协议的网络通信——创建服务器端
            • (1)进行监听:(Listen函数在net包下)
            • (2)代码
          • 4. 基于TCP协议的网络通信——处理终端数据
            • (1)客户端发送数据
            • (2)服务端接受数据
            • (3)处理结果
        • 第14章:反射
          • 1.反射的引入
            • (1)反射可以做什么?
            • (1)反射相关的函数
          • 2.对基本数据类型
            • (1)反射相关的函数
            • (2)代码:
          • 3.对结构体类型反射
            • (1)反射相关的函数
            • (2)代码
          • 4.获取变量的类别
            • (1)获取变量的类别:两种方式
            • (2)Kind的值是常量值
            • (3)代码
          • 5.通过反射修改变量
            • (1)修改基本数据类型的值
            • (2)修改结构体类型的值
          • 6.通过反射操作结构体的属性和方法
            • (1)代码(熟知API)
      • 二、Golang进阶 - 网络通信
      • 三、Golang进阶 - 并发编程
    • Go Web开发之前端技术实战
        • 一、Go Web前置 - Go Module
          • 第1章:GoModule介绍、如何开启GoModule
            • (1)性质
            • (2)验证
          • 第2章:使用命令行创建Go Module项目
            • (1)命令行
            • (2)测试代码
            • (3)尝试利用Go Module安装第三方包
          • 第3章:GoModule基本介绍(了解)
            • (1)常用命令
        • 二、Go Web前置 - HTML
          • 第1章:网络传输三大基石
            • (1)URL
            • (2)HTTP协议
            • (3)HTML
          • 第2章:什么是HTML
            • (1)HTML
            • (2)HTML的作用:
          • 第3章:HTML的标准结构
            • (1)创建
            • (2)编辑
            • (3)运行界面
          • 第4章:HTML Head Body
            • (1)html标签
            • (2)head标签
            • (3)body标签
          • 第5章:head中可用标签
          • 第6章:body中可用标签
            • (1)文本标签
            • (2)实体字符
            • (3)多媒体标签
            • (4)超链接标签
            • (5)列表标签
            • (6)表格标签
          • 第7章:框架
            • (1)内嵌框架
          • 第8章:form表单
            • (1)前后端交互
            • (2)模拟百度搜索
            • (3)表单元素
            • (4)HTML5新增type类型
            • (5)HTML5新增属性
        • 三、Go Web前置 - CSS
        • 四、Go Web前置 - JS
        • 五、Go Web前置 - JQuery
        • 六、前端技术之Vue框架
    • GORM及数据库
      • 一、GORM前置:MySQL
      • 二、MySQL性能调优与架构设计
      • 三、GORM
    • Go开发应用中间件
      • 一、Redis_高效的NoSQL数据库
      • 二、Redis缓存数据库进阶
      • 三、Redis之go编程实战
      • 四、消息中间件-Kafka实战
      • 五、Kafka之go编程实战
      • 六、RocketMQ基础实战版
      • 七、NoSQL-MongoDB实战
      • 八、分布式文件存储系统Minio
    • 全文检索 ES
      • 一、Elasticsearch核心知识篇
    • Go Web开发之企业级框架
      • 一、Web框架
      • 二、Gin深入实战
      • 三、Beego框架开发实战
      • 四、微服务架构
      • 五、Go框架开发
      • 六、Kong入门与实战
      • 七、Logrus日志
    • 企业级项目实战

预备技术

一、算法与数据结构

第1章:基础算法

https://blog.csdn.net/m0_52806260/article/details/125519736

第2章:数据结构

https://blog.csdn.net/m0_52806260/article/details/125592926

第3章:搜索与图论

https://blog.csdn.net/m0_52806260/article/details/125771347

第4章:数论

https://blog.csdn.net/m0_52806260/article/details/125843269

第5章:动态规划

https://blog.csdn.net/m0_52806260/article/details/125459689

第6章:贪心

https://blog.csdn.net/m0_52806260/article/details/125987376

第7章:算法竞赛入门

https://blog.csdn.net/m0_52806260/article/details/146226657

二、Linux操作系统与Shell编程

https://blog.csdn.net/m0_52806260/article/details/126068791

三、计算机操作系统

四、计算机组成原理

五、计算机网络

六、Docker:云原生核心技术之一

Go 开发编程实战

一、基本语法

第1章:走进Golang
第2章:变量与数据类型
第3章:运算符
第4章:流程控制
第5章:函数
第6章:错误处理
第7章:数组
第8章:切片
第9章:映射
第10章:面向对象
第11章:文件的操作
第12章:协程和管道
1. 程序、进程、线程、协程
(1)程序 (program)
  • 程序是为完成特定任务、用某种语言编写的一组指令的集合,是一段静态的代码。 (程序是静态的)
(2)进程 (process)
  • 是程序的一次执行过程。正在运行的一个程序,进程作为资源分配的单位,在内存中会为每个进程分配不同的内存区域。 (进程是动态的)是一个动的过程 ,进程的生命周期 : 有它自身的产生、存在和消亡的过程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(3)线程 (thread)
  • 进程可进一步细化为线程, 是一个程序内部的一条执行路径。
  • 若一个进程同一时间并行执行多个线程,就是支持多线程的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(4)协程 (goroutine)
  • 又称为微线程,纤程,协程是一种用户态的轻量级线程
  • 作用:在执行A函数的时候,可以随时中断,去执行B函数,然后中断继续执行A函数(可以自动切换),注意这一切换过程并不是函数调用(没有调用语句),过程很像多线程,然而协程中只有一个线程在执行(协程的本质是个单线程)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 对于单线程下,我们不可避免程序中出现io操作,但如果我们能在自己的程序中(即用户程序级别,而非操作系统级别)控制单线程下的多个任务能在一个任务遇到io阻塞时就将寄存器上下文和栈保存到某个其他地方,然后切换到另外一个任务去计算。在任务切回来的时候,恢复先前保存的寄存器上下文和栈,这样就保证了该线程能够最大限度地处于就绪态,即随时都可以被cpu执行的状态,相当于我们在用户程序级别将自己的io操作最大限度地隐藏起来,从而可以迷惑操作系统,让其看到:该线程好像是一直在计算,io比较少,从而会更多的将cpu的执行权限分配给我们的线程(注意:线程是CPU控制的,而协程是程序自身控制的,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级)
2. 协程入门
(1)案例
  • 请编写一个程序,完成如下功能:
    (1)在主线程中,开启一个goroutine,该goroutine每隔1秒输出"hello golang"
    (2)在主线程中也每隔一秒输出"good bye golang",输出10次后,退出程序
    (3)要求主线程和goroutine同时执行
  • 实现代码
package main

import (
	"fmt"
	"time"
)

func test() {
	for i := 1; i <= 10; i++ {
		fmt.Println("hello golang")
		time.Sleep(1 * time.Second) // 阻塞一秒
	}
}

func main() {
	go test() // 开启一个协程:go + 函数名
	// 主线程
	for i := 1; i <= 10; i++ {
		fmt.Println("good bye golang")
		time.Sleep(1 * time.Second) // 阻塞一秒
	}
	return
}

  • 运行结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 执行流程示意图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3. 主死从随
(1)主死从随
  • 如果主线程退出了,则协程即使还没有执行完毕,也会退出
  • 当然协程也可以在主线程没有退出前,就自己结束了,比如完成了自己的任务
  • 验证代码
package main

import (
	"fmt"
	"strconv"
	"time"
)

func test() {
	for i := 1; i <= 1000; i++ {
		fmt.Println("hello golang" + strconv.Itoa(i))
		time.Sleep(1 * time.Second) // 阻塞一秒
	}
}

func main() {
	go test() // 开启一个协程:go + 函数名
	// 主线程
	for i := 1; i <= 10; i++ {
		fmt.Println("good bye golang" + strconv.Itoa(i))
		time.Sleep(1 * time.Second) // 阻塞一秒
	}
	return
}

  • 执行结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4. 启动多个协程
(1)案例
package main

import (
	"fmt"
	"time"
)

func main() {
	// 匿名函数 + 外部变量 = 闭包
	for i := 1; i <= 5; i++ {
		go func(n int) {
			fmt.Printf("这是%d号协程\n", n)
		}(i)
	}

	time.Sleep(2 * time.Second) // 避免主死从随
}

5. 使用WaitGroup控制协程退出
(1)WaitGroup的作用

WaitGroup用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量。每个被等待的线程在结束时应调用Done方法。同时,主线程里可以调用Wait方法阻塞至所有线程结束。—> 解决主线程在子协程结束后自动结束

(2)主要方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(3)案例代码
  • Add/Done/Wait
package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func main() {

	// 启动五个协程
	for i := 1; i <= 5; i++ {
		wg.Add(1) // 协程开始
		go func(n int) {
			fmt.Printf("这是%d号协程\n", n)
			wg.Done() // 协程结束
		}(i)
	}
	wg.Wait() // 阻塞等待协程全部完成
}

  • 如果忘记计数器减1,可以提前defer:
package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func main() {

	// 启动五个协程
	for i := 1; i <= 5; i++ {
		wg.Add(1) // 协程开始
		go func(n int) {
			defer wg.Done() // 提前写defer,防止忘记计数器-1
			fmt.Printf("这是%d号协程\n", n)
			/*
				其余代码
				其余代码
				其余代码
				其余代码
				其余代码
			*/
		}(i)
	}
	wg.Wait() // 阻塞等待协程全部完成
}

  • 可以最开始在知道协程次数的情况下先Add操作:
package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

func main() {
	wg.Add(5) // 预先设置协程数
	// 启动五个协程
	for i := 1; i <= 5; i++ {
		go func(n int) {
			defer wg.Done() // 提前写defer,防止忘记计数器-1
			fmt.Printf("这是%d号协程\n", n)
			/*
				其余代码
				其余代码
				其余代码
				其余代码
				其余代码
			*/
		}(i)
	}
	wg.Wait() // 阻塞等待协程全部完成
}

注意:Add中加入的数字和协程的次数一定要保持一致

6. 多个协程操纵统一数据案例(互斥锁)
(1)案例
package main

import (
	"fmt"
	"sync"
)

var totalNum int
var wg sync.WaitGroup

func add() {
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		totalNum++
	}
}
func sub() {
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		totalNum--
	}
}
func main() {
	wg.Add(2) // 预先设置协程数
	// 启动五个协程
	go add()
	go sub()
	wg.Wait() // 阻塞等待协程全部完成s
	fmt.Println(totalNum)
}

  • 结果:在理论上,这个totalNum结果应该是0 ,无论协程怎么交替执行,最终想象的结果就是0,但是事实上:不是

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 问题出现的原因:(图解为其中一种可能性)外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 解决问题:确保一个机制:一个协程在执行逻辑的时候另外的协程不执行 ----> 锁的机制—> 加入互斥锁

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 代码实现
package main

import (
	"fmt"
	"sync"
)

var totalNum int
var wg sync.WaitGroup
var lock sync.Mutex

func add() {
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		lock.Lock() // 上锁
		totalNum++
		lock.Unlock() // 解锁
	}
}
func sub() {
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		lock.Lock() // 上锁
		totalNum--
		lock.Unlock() // 解锁
	}
}
func main() {
	wg.Add(2) // 预先设置协程数
	// 启动五个协程
	go add()
	go sub()
	wg.Wait() // 阻塞等待协程全部完成s
	fmt.Println(totalNum)
}

7. 读写锁的使用

golang中sync包实现了两种锁 Mutex (互斥锁)和 RWMutex(读写锁)

(1)互斥锁
  • 其中Mutex为互斥锁,Lock()加锁,Unlock()解锁,使用Lock()加锁后,便不能再次对其进行加锁,直到利用Unlock()解锁对其解锁后,才能再次加锁.适用于读写不确定场景,即读写次数没有明显的区别
  • 性能、效率相对来说比较低,因为大部分场景都是读多写少
(2)读写锁
  • RWMutex是一个读写锁,其经常用于读次数远远多于写次数的场景.
  • 在读的时候,数据之间不产生影响,写和读之间才会产生影响
(3)案例
package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup // 只定义无需赋值
// 加入读写锁:
var lock sync.RWMutex

func read() {
	defer wg.Done()
	lock.RLock() //如果只是读数据,那么这个锁不产生影响,但是读写同时发生的时候,就会有影响
	fmt.Println("开始读取数据")
	time.Sleep(1 * time.Second)
	fmt.Println("读取数据成功")
	lock.RUnlock()
}

func write() {
	defer wg.Done()
	lock.Lock()
	fmt.Println("开始修改数据")
	time.Sleep(time.Second * 5)
	fmt.Println("修改数据成功")
	lock.Unlock()
}

func main() {
	wg.Add(6)
	// 启动协程 ---> 场合:读多写少
	for i := 0; i <= 5; i++ {
		go read()
	}
	go write()
	wg.Wait()
}

  • 运行结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

8. 管道介绍
(1)管道(channel)特质介绍
  • 管道本质就是一个数据结构——队列
  • 数据是先进先出
  • 自身线程安全,多协程访问时,不需要加锁,channel本身就是线程安全的
  • 管道有类型的,一个string的管道只能存放string类型数据

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

9. 管道入门案例
(1)管道的定义

var 变量名 chan 数据类型

  • chan管道关键字
  • 数据类型指的是管道的类型,里面放入数据的类型,管道是有类型的,int类型的管道只能写入整数int
  • 管道是引用类型,必须初始化才能写入数据,即make后才能使用
(2)案例
package main

import "fmt"

func main() {
	// 定义管道,声明管道 ---> 定义一个int类型的管道
	var intChan chan int
	// 通过make进行初始化:管道可以存放3个int类型的数据
	intChan = make(chan int, 3)
	// 证明管道是引用类型
	fmt.Printf("intChan:%v\n", intChan)
	// 向管道存放数据
	intChan <- 10
	num := 20
	intChan <- num
	intChan <- 40
	// 注意:不能存放大于容量的数据

	num1 := <-intChan
	num2 := <-intChan
	num3 := <-intChan

	// 输出管道的长度
	fmt.Printf("管道的实际长度:%v,管道的容量是:%v\n", len(intChan), cap(intChan))

	fmt.Println(num1)
	fmt.Println(num2)
	fmt.Println(num3)

	// 注意:在没有使用协程的情况下,如果管道的数据已经全部取出,那么再取就会报错:
	//num4 := <-intChan
	//fmt.Println(num4)
}

10. 管道的关闭
(1)管道的关闭
  • 使用内置函数close可以关闭管道,当管道关闭后,就不能再向管道写数据了,但是仍然可以从该管道读取数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2)案例
package main

import "fmt"

func main() {
	// 定义管道,声明管道 ---> 定义一个int类型的管道
	var intChan chan int
	// 通过make进行初始化:管道可以存放3个int类型的数据
	intChan = make(chan int, 3)
	// 在管道中存放数据:
	intChan <- 10
	intChan <- 20
	
    // 关闭管道:
	close(intChan)
    
	// 再次写入数据:--->报错
	// intChan<- 30
	// 当管道关闭后,读取数据是可以的:
	num := <-intChan
	fmt.Println(num)
}

11. 管道的遍历
(1)管道的遍历
  • 管道支持for-range的方式进行遍历,请注意两个细节
    (1)在遍历时,如果管道没有关闭,则会出现deadlock的错误
    (2)在遍历时,如果管道已经关闭,则会正常遍历数据,遍历完后,就会退出遍历。
package main

import (
	"fmt"
)

func main() {
	// 定义管道 、 声明管道
	var intChan chan int
	// 通过make初始化:管道可以存放3个int类型的数据
	intChan = make(chan int, 100)
	for i := 0; i < 100; i++ {
		intChan <- i
	}
	// 在遍历前,如果没有关闭管道,就会出现deadlock的错误
	// 所以我们在遍历前要进行管道的关闭
	close(intChan)
	// 遍历:for-range
	for v := range intChan {
		fmt.Println("value = ", v)
	}
}

12. 协程和管道协同工作的案例
(1)案例需求
  • 请完成协程和管道协同工作的案例,具体要求:

(1)开启一个writeData协程,向管道中写入50个整数.

(2)开启一个readData协程,从管道中读取writeData写入的数据。

(3)注意: writeData和readDate操作的是同一个管道

(4)主线程需要等待writeData和readDate协程都完成工作才能退出

(2)原理图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(3)代码
package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup
var intChan chan int

func writeData() {
	defer wg.Done()

	for i := 1; i <= 50; i++ {
		intChan <- i
		fmt.Println("写入的数据为:", i)
		time.Sleep(1 * time.Second)
	}

	close(intChan)
}

func readData() {
	defer wg.Done()

	for v := range intChan {
		fmt.Println("读取的数据为:", v)
	}
}

func main() {
	intChan = make(chan int, 50)

	wg.Add(2)
	
	go writeData()
	go readData()

	wg.Wait()

}

  • 运行结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

13. 声明只读只写管道
(1)管道可以声明为只读或者只写性质
(2)代码:
package main

import (
	"fmt"
)

func main() {
	// 默认情况下,管道是双向的--》可读可写:
	// var intChan1 chan int

	// 声明为只写:chan<- int
	var intChan2 chan<- int // 管道具备<- 只写性质
	intChan2 = make(chan int, 3)
	intChan2 <- 20
	//num := <-intChan2 报错
	fmt.Println("intChan2:", intChan2)

	// 声明为只读:<-chan int
	var intChan3 <-chan int // 管道具备<- 只读性质
	if intChan3 != nil {
		num1 := <-intChan3
		fmt.Println("num1:", num1)
	}
	
	// intChan3<- 30 报错

}

14. 管道的阻塞
(1)当管道只写入数据,没有读取,就会出现阻塞
package main

import (
	"fmt"
	"sync"
	_ "time"
)

var wg sync.WaitGroup // 只定义无需赋值
// 写:
func writeData(intChan chan int) {
	defer wg.Done()
	for i := 1; i <= 10; i++ {
		intChan <- i
		fmt.Println("写入的数据为:", i)
		//time.Sleep(time.Second)
	}
	//管道关闭:
	close(intChan)
}

// 读:
func readData(intChan chan int) {
	defer wg.Done()
	// 遍历:
	for v := range intChan {
		fmt.Println("读取的数据为:", v)
		//time.Sleep(time.Second)
	}
}
func main() { // 主线程
	// 写协程和读协程共同操作同一个管道-》定义管道:
	intChan := make(chan int, 10)
	wg.Add(2)
	// 开启读和写的协程:
	go writeData(intChan)
	// go readData(intChan)
	// 主线程一直在阻塞,什么时候wg减为0了,就停止
	wg.Wait()
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2)写的快,读的满(管道读写频率不一致),不会出现阻塞问题
package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup // 只定义无需赋值
// 写:
func writeData(intChan chan int) {
	defer wg.Done()
	for i := 1; i <= 10; i++ {
		intChan <- i
		fmt.Println("写入的数据为:", i)
		//time.Sleep(time.Second)
	}
	// 管道关闭:
	close(intChan)
}

// 读:
func readData(intChan chan int) {
	defer wg.Done()
	// 遍历:
	for v := range intChan {
		fmt.Println("读取的数据为:", v)
		time.Sleep(time.Second)
	}
}
func main() { // 主线程
	// 写协程和读协程共同操作同一个管道-》定义管道:
	intChan := make(chan int, 10)
	wg.Add(2)
	// 开启读和写的协程:
	go writeData(intChan)
	go readData(intChan)
	// 主线程一直在阻塞,什么时候wg减为0了,就停止
	wg.Wait()
}

15. select功能
(1)select功能
  • 解决多个管道的选择问题,也可以叫做多路复用,可以从多个管道中随机公平地选择一个来执行

  • case后面必须进行的是io操作,不能是等值,随机去选择一个io操作

  • default防止select被阻塞住,加入default

(2)代码
package main

import (
	"fmt"
	"time"
)

func main() {
	// 定义一个int管道:
	intChan := make(chan int, 1)
	go func() {
		time.Sleep(time.Second * 15)
		intChan <- 10
	}()

	// 定义一个string管道:
	stringChan := make(chan string, 1)
	go func() {
		time.Sleep(time.Second * 12)
		stringChan <- "golang"
	}()

	//fmt.Println(<-intChan)// 本身取数据就是阻塞的
	select {
	case v := <-intChan:
		fmt.Println("intChan:", v)
	case v := <-stringChan:
		fmt.Println("stringChan:", v)
	default:
		fmt.Println("防止select被阻塞")
	}
}

16. defer + recover机制处理错误
(1)问题原因
  • 多个协程工作,其中一个协程出现panic,导致程序崩溃
(2)解决办法
  • 利用refer+recover捕获panic进行处理,即使协程出现问题,主线程仍然不受影响可以继续执行。
(3)案例
package main

import (
	"fmt"
	"time"
)

// 输出数字:
func printNum() {
	for i := 1; i <= 10; i++ {
		fmt.Println(i)
	}
}

// 做除法操作:
func devide() {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println("devide()出现错误:", err)
		}
	}()
	num1 := 10
	num2 := 0
	result := num1 / num2
	fmt.Println(result)
}

func main() {
	// 启动两个协程:
	go printNum()
	go devide()
	time.Sleep(time.Second * 5)
}
  • 运行结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第13章:网络编程
1. 引入
(1)网络编程
  • 把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大、功能强的网络系统,从而使众多的计算机可以方便地互相传递信息、共享硬件、软件、数据信息等资源。
  • 设备之间在网络中进行数据的传输,发送/接收数据。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2)通信两个重要的要素:IP+PORT

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(3)通信协议

设备之间进行传输的时候,必须遵照一定的规则

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(4)TCP协议:可靠的
  • 建立连接:三次握手

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 释放连接:四次挥手

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(5)UDP协议:不可靠的

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2. 基于TCP协议的网络通信——创建客户端
(1)调用Dial函数

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2)代码
package main

import (
	"fmt"
	"net" // 所需的网络编程全部都在net包下
)

func main() {
	fmt.Println("客户端启动")

	// 调用Dial函数:参数需要指定tcp协议,需要指定服务器端的 IP + PORT
	conn, err := net.Dial("tcp", "127.0.0.1:8888")

	if err != nil {
		fmt.Println("客户端连接失败")
		return
	}
	fmt.Println("连接成功,conn", conn)
}

3. 基于TCP协议的网络通信——创建服务器端
(1)进行监听:(Listen函数在net包下)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2)代码
package main

import (
	"fmt"
	"net"
)

func main() {
	fmt.Println("服务器端启动")
	// 进行监听:需要指定服务器端TCP协议,服务器端的 IP + PORT
	listen, err := net.Listen("tcp", "127.0.0.1:8888")

	if err != nil {
		fmt.Println("监听失败")
		return
	}

	for {
		conn, err2 := listen.Accept()
		if err2 != nil { // 客户端等待失败
			fmt.Println("客户端等待失败,err2:", err2)
		} else {
			// 连接成功
			fmt.Println("等待链接成功,con=%v ,接收到的客户端信息:%v \n", conn, conn.RemoteAddr().String())
		}
	}
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4. 基于TCP协议的网络通信——处理终端数据
(1)客户端发送数据
package main

import (
	"bufio"
	"fmt"
	"net" // 所需的网络编程全部都在net包下
	"os"
)

func main() {
	fmt.Println("客户端启动")

	// 调用Dial函数:参数需要指定tcp协议,需要指定服务器端的 IP + PORT
	conn, err := net.Dial("tcp", "127.0.0.1:8888")

	if err != nil {
		fmt.Println("客户端连接失败")
		return
	}

	fmt.Println("连接成功,conn", conn)
	reader := bufio.NewReader(os.Stdin)

	str, err := reader.ReadString('\n')
	if err != nil {
		fmt.Println("从终端读取失败,err:", err)
	}

	n, err := conn.Write([]byte(str))
	if err != nil {
		fmt.Println("连接失败,err:", err)
	}

	fmt.Printf("成功发送%d字节\n", n)
}

(2)服务端接受数据
package main

import (
	"fmt"
	"net"
)

func process(conn net.Conn) {
	defer conn.Close() // 连接用完一定要关闭

	for {
		buf := make([]byte, 1024)

		n, err := conn.Read(buf)
		if err != nil {
			return
		}

		fmt.Println(string(buf[0:n]))
	}
}

func main() {
	fmt.Println("服务器端启动")
	// 进行监听:需要指定服务器端TCP协议,服务器端的 IP + PORT
	listen, err := net.Listen("tcp", "127.0.0.1:8888")

	if err != nil {
		fmt.Println("监听失败")
		return
	}

	for {
		conn, err2 := listen.Accept()
		if err2 != nil { // 客户端等待失败
			fmt.Println("客户端等待失败,err2:", err2)
		} else {
			// 连接成功
			fmt.Printf("等待链接成功,con=%v ,接收到的客户端信息:%v \n", conn, conn.RemoteAddr().String())

			go process(conn) // 不同的客户端的请求,连接conn不一样的
		}
	}
}

(3)处理结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第14章:反射
1.反射的引入
(1)反射可以做什么?
  • 反射可以在运行时动态获取变量的各种信息,比如变量的类型,类别等信息

  • 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)

  • 通过反射,可以修改变量的值,可以调用关联的方法。

  • 使用反射,需要import (“reflect”)

(1)反射相关的函数
  • reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型

  • reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型(reflect.Value是一个结构体类型),通过reflect.Value,可以获取到关于该变量的很多信息。

2.对基本数据类型
(1)反射相关的函数
  • reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型

  • reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型(reflect.Value是一个结构体类型),通过reflect.Value,可以获取到关于该变量的很多信息

(2)代码:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

package main

import (
	"fmt"
	"reflect"
)

// 利用一个函数,函数的参数定义为空接口:
func testReflect(i interface{}) { // 空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为我们可以把任何一个变量赋给空接口。
	// 1.调用TypeOf函数,返回reflect.Type类型数据:
	reType := reflect.TypeOf(i)
	fmt.Println("反射出i的type为:", reType)
	// 2.调用ValueOf函数,返回reflect.Value类型数据:
	reValue := reflect.ValueOf(i)
	fmt.Println("反射出i的value为:", reValue)
	// reValue不能直接当int使用,是value类型
	// 如果真想获取reValue的数值,要调用Int()方法:返回v持有的有符号整数
	num2 := 80 + reValue.Int()
	fmt.Println(num2)

	// reValue转成空接口:
	i2 := reValue.Interface()
	// 类型断言:
	n := i2.(int)
	n2 := n + 30
	fmt.Println(n2)
}

func main() {
	// 对基本数据类型进行反射
	var num int = 100
	testReflect(num)
}

3.对结构体类型反射
(1)反射相关的函数
  • reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型

  • reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型(reflect.Value是一个结构体类型),通过reflect.Value,可以获取到关于该变量的很多信息。

(2)代码
package main

import (
	"fmt"
	"reflect"
)

// 利用一个函数,函数的参数定义为空接口:
func testReflect(i interface{}) { // 空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为我们可以把任何一个变量赋给空接口。
	// 1.调用TypeOf函数,返回reflect.Type类型数据:
	reType := reflect.TypeOf(i)
	fmt.Println("反射出i的type为:", reType)
	// 2.调用ValueOf函数,返回reflect.Value类型数据:
	reValue := reflect.ValueOf(i)
	fmt.Println("反射出i的value为:", reValue)
	// reValue不能直接当int使用,是value类型

	i2 := reValue.Interface()
	n, ok := i2.(Student)
	if ok == true {
		fmt.Println(n.Name)
	}
}

type Student struct {
	Name string
	Age  int
}

func main() {
	// 对结构体进行反射
	stu := Student{
		Name: "aa",
		Age:  18,
	}
	testReflect(stu)
}
4.获取变量的类别
(1)获取变量的类别:两种方式
  • reflect.Type.Kind()
  • reflect.Value.Kind()
(2)Kind的值是常量值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(3)代码
package main

import (
	"fmt"
	"reflect"
)

// 利用一个函数,函数的参数定义为空接口:
func testReflect(i interface{}) { // 空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为我们可以把任何一个变量赋给空接口。
	// 1.调用TypeOf函数,返回reflect.Type类型数据:
	reType := reflect.TypeOf(i)
	fmt.Println("反射出i的type为:", reType)
	// 2.调用ValueOf函数,返回reflect.Value类型数据:
	reValue := reflect.ValueOf(i)
	fmt.Println("反射出i的value为:", reValue)
	// reValue不能直接当int使用,是value类型

	// 获取变量的类别:
	//(1)reType.Kind()
	k1 := reType.Kind()
	fmt.Println(k1)
	//(2)reValue.Kind()
	k2 := reValue.Kind()
	fmt.Println(k2)

	// 获取变量的类型:
	// reValue转成空接口:
	i2 := reValue.Interface()
	// 类型断言:
	n, flag := i2.(Student)
	if flag == true { // 断言成功
		fmt.Printf("结构体的类型是:%T", n)
	}
}

type Student struct {
	Name string
	Age  int
}

func main() {
	// 对结构体进行反射
	stu := Student{
		Name: "aa",
		Age:  18,
	}
	testReflect(stu)
}
5.通过反射修改变量
(1)修改基本数据类型的值
package main

import (
	"fmt"
	"reflect"
)

// 利用一个函数,函数的参数定义为空接口:
func testReflect(i interface{}) { // 空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为我们可以把任何一个变量赋给空接口。
	reValue := reflect.ValueOf(i)
	// 通过SetInt()来改变值:
	reValue.Elem().SetInt(40)

}
func main() {
	// 对基本数据类型进行反射:
	// 定义一个基本数据类型:
	var num int = 100
	testReflect(&num) // 传入指针地址
	fmt.Println(num)
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2)修改结构体类型的值
package main

import (
	"fmt"
	"reflect"
)

// 定义一个结构体:
type Student struct {
	Name string
	Age  int
}

// 定义函数操作结构体进行反射操作:
func TestStudentStruct(a interface{}) {
	// a转成reflect.Value类型:
	val := reflect.ValueOf(a)
	fmt.Println(val)

	numberOfFiled := val.Elem().NumField()
	fmt.Printf("共%d个字段\n", numberOfFiled)

	// 修改字段的值:
	val.Elem().Field(0).SetString("张三")
}
func main() {
	// 定义结构体具体的实例:
	s := Student{
		Name: "丽丽",
		Age:  18,
	}
	// 调用TestStudentStruct:
	TestStudentStruct(&s)
	fmt.Println(s)
}
6.通过反射操作结构体的属性和方法
(1)代码(熟知API)
package main

import (
	"fmt"
	"reflect"
)

// 定义一个结构体:
type Student struct {
	Name string
	Age  int
}

// 给结构体绑定方法:
func (s Student) CPrint() {
	fmt.Println("调用了Print()方法")
	fmt.Println("学生的名字是:", s.Name)
}
func (s Student) AGetSum(n1, n2 int) int {
	fmt.Println("调用了AGetSum方法")
	return n1 + n2
}
func (s Student) BSet(name string, age int) {
	s.Name = name
	s.Age = age
}

// 定义函数操作结构体进行反射操作:
func TestStudentStruct(a interface{}) {
	// a转成reflect.Value类型:
	val := reflect.ValueOf(a)
	fmt.Println(val)

	// 通过reflect.Value类型操作结构体内部的字段:
	numberOfFiled := val.NumField()
	fmt.Println(numberOfFiled)
	// 遍历-获取具体的字段:
	for i := 0; i < numberOfFiled; i++ {
		fmt.Printf("第%d个字段的值是:%v\n", i, val.Field(i))
	}

	// 通过reflect.Value类型操作结构体内部的方法:
	numberOfMethod := val.NumMethod()
	fmt.Println(numberOfMethod)

	// 调用方法,方法的首字母必须大写才能有对应的反射的访问权限
	// 方法的顺序按照ASCII的顺序排列的,a,b,c,,,,,,索引:0,1,2,,,,,
	val.Method(2).Call(nil) // 调用CPrint()方法:
	// 调用AGetSum方法:
	// 定义Value的切片:
	var params []reflect.Value
	params = append(params, reflect.ValueOf(10))
	params = append(params, reflect.ValueOf(20))
	result := val.Method(0).Call(params)
	fmt.Println("AGetSum方法的返回值为:", result[0].Int())
}
func main() {
	// 定义结构体具体的实例:
	s := Student{
		Name: "丽丽",
		Age:  18,
	}
	// 调用TestStudentStruct:
	TestStudentStruct(s)
}

二、Golang进阶 - 网络通信

三、Golang进阶 - 并发编程

Go Web开发之前端技术实战

一、Go Web前置 - Go Module
第1章:GoModule介绍、如何开启GoModule
(1)性质

类似Java中的Maven

cmd中输入

go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
(2)验证

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第2章:使用命令行创建Go Module项目
(1)命令行

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2)测试代码
package main

import "fmt"

func main() {
	fmt.Println("hello go module")
}

(3)尝试利用Go Module安装第三方包
$ go get -u github.com/gin-gonic/gin
  • mod文件发生变化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第3章:GoModule基本介绍(了解)
(1)常用命令
  • 查看帮助
$ go mod help
  • 初始化一个module
$ go mod init ProjectName
  • 添加缺少或删除没有使用的modules
$ go mod tidy
二、Go Web前置 - HTML
第1章:网络传输三大基石

三大基石:URL,HTTP协议,HTML

(1)URL
  • 在WWW上,每一信息资源都有统一的且在网上唯一的地址,该地址就叫URL(Uniform Resource Locator,统一资源定位符),它是WWW的统一资源定位标志,就是指网络地址。
(2)HTTP协议
  • http是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII码形式给出;而消息内容则具有一个类似MIME的格式。这个简单模型是早期Web成功的有功之臣,因为它使得开发和部署是那么的直截了当。
(3)HTML
  • HTML称为超文本标记语言。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第2章:什么是HTML
(1)HTML

指的是超文本标记语言: HyperText Markup Language

  • 超文本:
     普通人 vs 超人(比普通的人厉害)
    普通文本 vs 超文本(比普通的文本厉害)
  • 标记:
    标记=标签
    标签: 由尖括号包围起来的关键词
     分类:双标记标签/封闭类型标签

    单标记标签/非封闭类型标签
  • 语言:HTML是一个描述网页的语言

总结:学习HTML就是学习各种各样的标签,然后组成一个页面,这个页面可以被浏览器解析,解析完以后可以在浏览器中将页面进行展示。

(2)HTML的作用:

学习HTML就是学习各种各样的标签,然后组成一个页面,这个页面可以被浏览器解析,解析完以后可以在浏览器中将页面进行展示。

第3章:HTML的标准结构
(1)创建

先用普通文本文档新建一个文本,将文本的后缀改为.html 或者 .htm (大小写不区分)

(2)编辑

标准结构

<html>
        <head></head>
        <body>
                this is my first html....
        </body>
</html>
(3)运行界面

让浏览器解析:直接用浏览器将文件打开即可

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

建议:使用谷歌浏览器

第4章:HTML Head Body

HTML文档标准结构:

<html>
        <head>
        </head>
        <body>
                this is my second html... 
        </body>
</html>
(1)html标签
  • 定义 HTML 文档,这个元素我们浏览器看到后就明白这是个HTML文档了,所以你的其它元素要包裹在它里面,标签限定了文档的开始点和结束点,在它们之间是文档的头部和主体。
(2)head标签
  • head标签用于定义文档的头部,它是所有头部元素的容器 —> 里面放的是页面的配置信息
  • <head> 中的元素可以引用脚本、指示浏览器在哪里找到样式表。文档的头部描述了文档的各种属性和信息,包括文档的标题、在 Web 中的位置以及和其他文档的关系等。绝大多数文档头部包含的数据都不会真正作为内容显示给读者。
    下面这些标签可用在 head 部分:
  • <title>、<meta>、<link>、<style>、<script>、 <base>
  • 应该把 <head> 标签放在文档的开始处,紧跟在 <html> 后面,并处于 <body> 标签之前。
  • 文档的头部经常会包含一些 <meta> 标签,用来告诉浏览器关于文档的附加信息。
(3)body标签
  • body 元素是定义文档的主体 —> 里面放的就是页面上展示出来的内容
  • body 元素包含文档的所有内容(比如文本、超链接、图像、表格和列表等等。)
  • body是用在网页中的一种HTML标签,标签是用在网页中的一种HTML标签,表示网页的主体部分,也就是用户可以看到的内容,可以包含文本、图片、音频、视频等各种内容!
第5章:head中可用标签
<html>
<!-- 这是一个注释,注释的快捷键是ctrl+shift+/-->
<!--
        head标签中:放入:页面的配置信息
        head标签中可以加入:
        <title>、<meta>、<link>、<style>、 <script>、 <base>。
-->
<head>
    <!--页面标题-->
    <title>百度一下,你就知道</title>
    <!--设置页面的编码,防止乱码现象
            利用meta标签,
            charset="utf-8" 这是属性,以键值对的形式给出  k=v a=b
            告诉浏览器用utf-8来解析这个html文档
    -->
    <meta charset="utf-8" /><!--简写-->
    <!--繁写形式:(了解)-->
    <!--<meta http-equiv="content-type" content="text/html;charset=utf-8" />-->
    <!--页面刷新效果:三秒跳转到百度-->
    <meta http-equiv="refresh" content="3;https://www.baidu.com" />
    <!--页面作者-->
    <meta name="author" content="yzletter" />
    <!--设置页面搜索的关键字-->
    <meta name="keywords" content="Golang后端开发" />
    <!--页面描述-->
    <meta name="description" content="Golang后端开发" />
    <!--link标签引入外部资源-->
    <link rel="shortcut icon" href="https://www.baidu.com/favicon.ico" type="image/x-icon" />
</head>
<!--
        body标签中:放入:页面展示的内容
-->
    <body>
    this is a html,你好
    </body>
</html>
第6章:body中可用标签
(1)文本标签
<!DOCTYPE html>
<html>
        <head>
            <meta charset="UTF-8">
            <title>Title</title>
        </head>

        <body>
        <!--文本标签-->
        <!--下面的文字就是普通的文本,文本编辑器中的任何效果:比如空格,换行 都不影响页面,
                页面想要实现效果 必须通过标签来实现
        -->
        媒体:为人父母,                                     要不要“持证上岗”?
        媒体:为人父母,要不要“持证上岗”?
        媒体:为人父母,要不要“持证上岗”?
        媒体:为人父母,要不要“持证上岗”?
        <!--标题标签
                h1-h6  字号逐渐变小,每个标题独占一行,自带换行效果
                h7之后都属于无效标签,但是浏览器也不会报错,而是以普通文本的形式进行展现
        -->
        <h1>媒体:为人父母,要不要“持证上岗”?</h1>
        <h2>媒体:为人父母,要不要“持证上岗”?</h2>
        <h3>媒体:为人父母,要不要“持证上岗”?</h3>
        <h4>媒体:为人父母,要不要“持证上岗”?</h4>
        <h5>媒体:为人父母,要不要“持证上岗”?</h5>
        <h6>媒体:为人父母,要不要“持证上岗”?</h6>
        <h7>媒体:为人父母,要不要“持证上岗”?</h7>
        <h8>媒体:为人父母,要不要“持证上岗”?</h8>

        <!--横线标签
        width:设置宽度
                        300px :固定宽度
                        30%:页面宽度的百分比,会随着页面宽度的变化而变化
        align:设置位置  left ,center,right    默认不写的话就是center居中效果
-->
        <hr style="width:300px; align-content:center"/>
        <hr style="width:30%; align-content:center"/>

        <!--段落标签:
                        段落效果:段落中文字自动换行,段落和段落之间有空行
                -->
        <p>&nbsp;&nbsp;&nbsp;&nbsp;5月&emsp;26日,“建议父母持合格&lt;父母证&gt;上岗&copy;”冲上微博热搜,迅速引发热议。在正在召开的全国两会上,全国政协委员许洪玲建议在社区举办家长课堂,建立“家长教育指导工作室”。针对准备入小学的家长开展相关课程教育,颁发“合格父母”上岗证随学生档案入学。5月26日,“建议父母持合格父母证上岗”冲上微博热搜,迅速引发热议。在正在召开的全国两会上,全国政协委员许洪玲建议在社区举办家长课堂,建立“家长教育指导工作室”。针对准备入小学的家长开展相关课程教育,颁发“合格父母”上岗证随学生档案入学。5月26日,“建议父母持合格父母证上岗”冲上微博热搜,迅速引发热议。在正在召开的全国两会上,全国政协委员许洪玲建议在社区举办家长课堂,建立“家长教育指导工作室”。针对准备入小学的家长开展相关课程教育,颁发“合格父母”上岗证随学生档案入学。</p>
        <p>5月26日,“建议父母持合格父母证上岗”冲上微博热搜,迅速引发热议。在正在召开的全国两会上,全国政协委员许洪玲建议在社区举办家长课堂,建立“家长教育指导工作室”。针对准备入小学的家长开展相关课程教育,颁发“合格父母”上岗证随学生档案入学。5月26日,“建议父母持合格父母证上岗”冲上微博热搜,迅速引发热议。在正在召开的全国两会上,全国政协委员许洪玲建议在社区举办家长课堂,建立“家长教育指导工作室”。针对准备入小学的家长开展相关课程教育,颁发“合格父母”上岗证随学生档案入学。5月26日,“建议父母持合格父母证上岗”冲上微博热搜,迅速引发热议。在正在召开的全国两会上,全国政协委员许洪玲建议在社区举办家长课堂,建立“家长教育指导工作室”。针对准备入小学的家长开展相关课程教育,颁发“合格父母”上岗证随学生档案入学。</p>
        <p>5月26日,“建议父母持合格父母证上岗”冲上微博热搜,迅速引发热议。在正在召开的全国两会上,全国政协委员许洪玲建议在社区举办家长课堂,建立“家长教育指导工作室”。针对准备入小学的家长开展相关课程教育,颁发“合格父母”上岗证随学生档案入学。5月26日,“建议父母持合格父母证上岗”冲上微博热搜,迅速引发热议。在正在召开的全国两会上,全国政协委员许洪玲建议在社区举办家长课堂,建立“家长教育指导工作室”。针对准备入小学的家长开展相关课程教育,颁发“合格父母”上岗证随学生档案入学。5月26日,“建议父母持合格父母证上岗”冲上微博热搜,迅速引发热议。在正在召开的全国两会上,全国政协委员许洪玲建议在社区举办家长课堂,建立“家长教育指导工作室”。针对准备入小学的家长开展相关课程教育,颁发“合格父母”上岗证随学生档案入学。</p>


        <!--加粗倾斜下划线-->
        <b>加粗</b>
        <i>倾斜</i>
        <u>下划线</u>
        <i><u><b>加粗倾斜下划线</b></u></i>
        <!--删除线-->
        <del>你好 你不好</del>

        <!--预编译标签:在页面上显示原样效果-->
        <pre>
        public static void main(String[] args){
                System.out.println("hello golang....");
        }
        </pre>

        <!--换行-->
        5月26日,“建议父母持合格父母证上岗”冲上微博<br />热搜,迅速引发热议。在正在召开的全国两会上,全国政

        <!--字体标签-->
        <span style="color: #397655; font-size: xx-large; font-family: 微软雅黑; ">建议父母持合格父母证上岗</span>

        </body>
</html>
(2)实体字符

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(3)多媒体标签
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    <!--图片
            src:引入图片的位置
                    引入本地资源
                    引入网络资源
            width:设置宽度
            height:设置高度
            注意:一般高度和宽度只设置一个即可,另一个会按照比例自动适应
            title:鼠标悬浮在图片上的时候的提示语,默认情况下(没有设置alt属性) 图片如果加载失败那么提示语也是title的内容
            alt:图片加载失败的提示语
    -->
    <img src="img/ss6.jpg" width="300px" title="这是一个美女小姐姐" alt="图片加载失败"/>
    <img src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1833909874,761626004&fm=26&gp=0.jpg" />
    <!--音频-->
    <embed src="music/我要你.mp3"></embed>
    <br />
    <!--视频-->
    <embed src="video/周杰伦 - 说好的幸福呢.mp4" width="500px" height="500px"></embed>
    <embed src="//player.video.iqiyi.com/38913f9ed7358c0933e82a03d9b26ec1/0/0/v_19rv8qeokk.swf-albumId=9194699400-tvId=9194699400-isPurchase=0-cnId=undefined" allowFullScreen="true" quality="high" width="480" height="350" align="middle" allowScriptAccess="always" type="application/x-shockwave-flash"></embed>
    </body>
</html>
(4)超链接标签

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

<!DOCTYPE html>
<html>
        <head>
          <meta charset="UTF-8">
          <title></title>
        </head>
        <body>
        <!--超链接标签:作用:实现页面的跳转功能
                href:控制跳转的目标位置
                target:_self 在自身页面打开 (默认效果也是在自身页面打开)    _blank 在空白页面打开
        -->
        <a href="文本标签.html">这是一个超链接01</a><!--跳转到本地资源-->
        <a href="">这是一个超链接02</a> <!--跳转到自身页面-->
        <a href="abc">这是一个超链接03</a><!--跳转的目标找不到,提示找不到资源-->
        <a href="https://www.baidu.com" target="_self">这是一个超链接04</a><!--_self表示在自身页面,跳转到网络资源-->
        <a href="https://www.baidu.com" target="_blank">这是一个超链接05</a><!--_blank表示在新的空白页面,跳转到网络资源-->
        <a href="https://www.baidu.com" target="_blank"><img src="img/ss.jpg" /></a>
        </body>
</html>

  • 设置锚点:

    应用场合:当一个页面太长的时候,就需要设置锚点,然后可以在同一个页面的不同位置之间进行跳转。

  • 同一个页面不同位置的跳转:


<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
<a name="1F"></a>
<h1>手机</h1>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<p>华为p40</p>
<a name="2F"></a>
<h1>化妆品</h1>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<p>大宝</p>
<a name="3F"></a>
<h1>母婴产品</h1>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<p>奶粉</p>
<a name="4F"></a>
<h1>图书</h1>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<p>thinking in java</p>
<a href="#1F">手机</a>
<a href="#2F">化妆品</a>
<a href="#3F">母婴产品</a>
<a href="#4F">书籍</a>
</body>
</html>

  • 不同页面利用锚点:
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
<a href="设置锚点.html#3F">超链接</a>
</body>
</html>
(5)列表标签
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
<!--无序列表ul:
        type:可以设置列表前图标的样式   type="square"
        如果想要更换图标样式,需要借助css技术: style="list-style:url(img/act.jpg) ;"
-->
<h1>起床以后需要做的事</h1>
<ul type="square">
  <li>睁眼</li>
  <li>穿衣服</li>
  <li>上厕所</li>
  <li>吃早饭</li>
  <li>洗漱</li>
  <li>出门</li>
</ul>
<!--有序列表ol:
        type:可以设置列表的标号:1,a,A,i,I
        start:设置起始标号
-->
<h1>学习java的顺序</h1>
<ol type="A" start="3">
  <li>JAVASE</li>
  <li>ORACLE</li>
  <li>MYSQL</li>
  <li>HTML</li>
  <li>CSS</li>
  <li>JS</li>
</ol>

</body>
</html>

(6)表格标签
  • 应用场景:在页面布局很规整的时候,可能利用的就是表格。
  • 合并原理:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
<!--表格:4行4列
        table:表格
        tr:行
        td:单元格
        th:特殊单元格:表头效果:加粗,居中
        默认情况下表格是没有边框的,通过属性来增加表框:
        border:设置边框大小
        cellspacing:设置单元格和边框之间的空隙
        align="center"  设置居中
        background 设置背景图片 background="img/ss.jpg"
        bgcolor :设置背景颜色
        rowspan:行合并
        colspan:列合并
-->
<table border="1px" cellspacing="0px" width="400px" height="300px" bgcolor="darkseagreen" >
  <tr bgcolor="bisque">
    <th>学号</th>
    <th>姓名</th>
    <th>年纪</th>
    <th>成绩</th>
  </tr>
  <tr>
    <td align="center">1001</td>
    <td>丽丽</td>
    <td>19</td>
    <td rowspan="3">90.5</td>
  </tr>
  <tr>
    <td colspan="2" align="center">2006</td>
    <td>30</td>
  </tr>
  <tr>
    <td>3007</td>
    <td>小明</td>
    <td>18</td>
  </tr>
</table>
</body>
</html>

第7章:框架
(1)内嵌框架
  • 内嵌框架是用于在网页中嵌入一个网页并让它在网页中显示
  • 添加内嵌框架的语法:
<iframe src=" URL "></iframe>
  • URL 指定独立网页的路径.

  • 案例:展示图书:

  • 书籍展示首页:

<!DOCTYPE html>
<html>
        <head>
                <meta charset="UTF-8">
                <title></title>
        </head>
        <body>
                <iframe src="书籍导航页面.html" height="700px" width="30%"></iframe>
                <!--内嵌框架-->
                <iframe name="iframe_my" width="67%" height="700px" src="img/think in java.jpg"></iframe>
        </body>
</html>
  • 左侧导航页面:
<!DOCTYPE html>
<html>
        <head>
                <meta charset="UTF-8">
                <title></title>
        </head>
        <body>
                <h1>我喜欢的图书展示</h1>
                <ul>
                        <li>
                                <a href="img/java核心技术.jpg" target="iframe_my">java核心技术</a>
                        </li>
                        <li>
                                <a href="img/think in java.jpg" target="iframe_my">think in java</a>
                        </li>
                        <li>
                                <a href="img/大话设计模式.jpg" target="iframe_my">大话设计模式</a>
                        </li>
                        <li>
                                <a href="img/深入理解java虚拟机.jpg" target="iframe_my">深入理解java虚拟机</a>
                        </li>
                        <li>
                                <a href="img/算法图解.jpg" target="iframe_my">算法图解</a>
                        </li>
                </ul>
        </body>
</html>
  • 右侧书籍展示页面:
<!DOCTYPE html>
<html>
        <head>
                <meta charset="UTF-8">
                <title></title>
        </head>
        <body>
                这是展示列表
        </body>
</html>
第8章:form表单
(1)前后端交互

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 表单在 Web 网页中用来给访问者填写信息,从而能采集客户端信息,使网页具有交互的功能。一般是将表单设计在一个Html文档中,当用户填写完信息后做提交(submit)操作,于是表单的内容就从客户端的浏览器传送到服务器上,经过服务器上程序处理后,再将用户所需信息传送回客户端的浏览器上,这样网页就具有了交互性。这里我们只讲怎样使用Html标志来设计表单。

  • 所有的用户输入内容的地方都用表单来写,如登录注册、搜索框。

  • 一个表单一般应该包含用户填写信息的输入框,提交按钮等,这些输入框,按钮叫做控件,表单很像容器,它能够容纳各种各样的控件。

<form action="url" method=get|post name="myform" ></form>
-name:表单提交时的名称
-action:提交到的地址
-method:提交方式,有get和post两种,默认为get

<!DOCTYPE html>
<html>
        <head>
                <meta charset="UTF-8">
                <title></title>
        </head>
        <body>
                <!--定义form表单:form表单会采集包含的有效数据,提交到后端,进行交互-->
                <!--
                        【1】action属性
                        地址栏信息:
                        http://127.0.0.1:8020/Form%E8%A1%A8%E5%8D%95/aaa?username=nana&pwd=123123
                        
                        ?之前是提交的资源的目标地址
                        ?之后是提交的具体的数据 
                        
                        http : 信息交互遵照协议 http协议
                        127.0.0.1  :代表本机的IP地址
                        8020 :Hbuilder内置服务器的端口号
                        Form%E8%A1%A8%E5%8D%95:指的是你的项目名字:Form表单
                        PS:浏览器的地址栏是不支持中文的,都会转成编码传送,如果你在地址栏看到中文,只是当前的那个浏览器给你一个友好的显示
                        PS:可以使用在线解析工具查看:urlencode
                        aaa:目标资源 --》去当前项目下找aaa了
                        
                        ?后的内容:
                        username=nana&pwd=123123
                        我们写的文本框,密码框等必须要加入一个属性:name
                        然后name属性和具体录入的信息会拼成一个键值对的形式
                        多个键值对之间 ,用&符号进行拼接
                        
                        PS:只有放在form表单中的内容才会被收集并提交
                        
                        【2】method属性:默认情况下不写method属性的时候就相当于method="get"
                        get方式:提交数据可见,不安全,提交数据长度有限制,效率高
                        post方式 :提交数据不可见,安全,提交数据长度没有限制,效率低
                -->
                <form action="aaa" method="post">
                        用户名:<input type="text" name="username" /><br />
                        密码:<input type="password" name="pwd" /><br />
                        <!--提交按钮-->
                        <input type="submit" />
                </form>
                用户名2:<input type="text" name="username2" />
        </body>
</html>

(2)模拟百度搜索
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>百度一下,你就知道</title>
        <link rel="shortcut icon" href="https://www.baidu.com/favicon.ico" type="image/x-icon" />
    </head>
    <body>
    <form action="https://www.baidu.com/s" method="get">
        <!--文本框-->
        <input type="text" name="wd"/>
        <!--提交按钮-->
        <input type="submit" value="百度一下"/>
    </form>
    </body>
</html>
(3)表单元素
  • form表单中可以放入的标签 就是表单元素
<!DOCTYPE html>
<html>
        <head>
                <meta charset="UTF-8">
                <title></title>
        </head>
        <body>
                <form action="" method="get">
                        <!--表单元素-->
                        <!--文本框:
                                input标签使用很广泛,通过type属性的不同值,来表现不同的形态。
                                type="text"  文本框,里面文字可见
                                表单元素必须有一个属性:name 有了name才可以提交数据,才可以采集数据
                                然后提交的时候会以键值对的形式拼到一起。
                                value:就是文本框中的具体内容
                                键值对:name=value的形式
                                如果value提前写好,那么默认效果就是value中内容。
                                一般默认提示语:用placeholder属性,不会用value--》value只是文本框中的值。
                                
                                readonly只读:只是不能修改,但是其他操作都可以,可以正常提交
                                disabled禁用:完全不用,不能正常提交
                                
                                写法:
                                readonly="readonly"
                                readonly
                                readonly = "true"
                        -->
                        <input type="text" name="uname"  placeholder="请录入身份证信息"/>
                        <input type="text" name="uname2" value="123123" readonly="true"/>
                        <input type="text" name="uname3" value="456456" disabled="disabled"/>
                        <!--密码框:效果录入信息不可见-->
                        <input type="password" name="pwd"  />
                        <!--单选按钮:
                                注意:一组单选按钮,必须通过name属性来控制,让它们在一个分组中,然后在一个分组里只能选择一个
                                正常状态下,提交数据为:gender=on ,后台不能区分你提交的数据
                                不同的选项的value值要控制为不同,这样后台接收就可以区分了
                                
                                默认选中:checked="checked"
                        -->
                        性别:
                        <input type="radio" name="gender" value="1" checked="checked"/><input type="radio" name="gender" value="0"/><!--多选按钮:
                                必须通过name属性来控制,让它们在一个分组中,然后在一个分组里可以选择多个
                                不同的选项的value值要控制为不同,这样后台接收就可以区分了
                                多个选项提交的时候,键值对用&符号进行拼接:例如下:
                                favlan=1&favlan=3
                        -->
                        你喜欢的语言:
                        <input type="checkbox" name="favlan" value="1" checked="checked"/>java
                        <input type="checkbox" name="favlan" value="2" checked="checked"/>python
                        <input type="checkbox" name="favlan" value="3"/>php
                        <input type="checkbox" name="favlan" value="4"/>c#
                        
                        <!--文件-->
                        <input type="file" />
                        <!--隐藏域-->
                        <input type="hidden" name="uname6" value="123123" />
                        <!--普通按钮:普通按钮没有什么效果,就是可以点击,以后学了js,可以加入事件-->
                        <input type="button" value="普通按钮" />
                        <!--特殊按钮:重置按钮将页面恢复到初始状态-->
                        <input type="reset" />
                        <!--特殊按钮:图片按钮-->
                        <img src="img/java核心技术.jpg" />
                        <input type="image" src="img/java核心技术.jpg" />
                        
                        
                        <!--下拉列表
                                默认选中:selected="selected"
                                多选:multiple="multiple"
                        -->
                        你喜欢的城市:
                        <select name="city" multiple="multiple">
                                <option value="0">---请选择---</option>
                                <option value="1">哈尔滨市</option>
                                <option value="2" selected="selected">青岛市</option>
                                <option value="3">郑州市</option>
                                <option value="4">西安市</option>
                                <option value="5">天津市</option>
                        </select>
                        
                        <!--多行文本框
                                利用css样式来控制大小不可变:style="resize: none;"
                        -->
                        自我介绍:
                        <textarea style="resize: none;" rows="10" cols="30">请在这里填写信息。。</textarea>
                        <br />
                        <!--label标签
                                一般会在想要获得焦点的标签上加入一个id属性,然后label中的for属性跟id配合使用。
                        -->
                        <label for="uname">用户名:</label><input type="text" name="uername" id="uname"/>
                        
                        <!--特殊按钮:提交按钮:具备提交功能-->
                        <input type="submit" />
                </form>
        </body>
</html>

(4)HTML5新增type类型
<!DOCTYPE html>
<html>
        <head>
                <meta charset="UTF-8">
                <title></title>
        </head>
        <body>
                <form action="" method="get">
                        <!--email:
                                html5的类型可以增加校验
                        -->
                        <input type="email" name="email" />
                        <!--url-->
                        <input type="url" />
                        <!--color-->
                        <input type="color" />
                        <!--number:
                                min:最小值
                                max:最大值
                                step:步长
                                value:默认值:一定在步长的范围中,否则不能提交
                        -->
                        <input type="number" min="1" max="10" step="3" value="4"/>
                        <!--range-->
                        1<input type="range" min="1" max="10" name="range" step="3"/>10
                        <!--date-->
                        <input type="date" />
                        <!--month-->
                        <input type="month" />
                        <!--week-->
                        <input type="week" />
                        <!--提交按钮-->
                        <input type="submit" />
                </form>
        </body>
</html>

(5)HTML5新增属性
<!--
                                HTML5新增属性:
                                multiple:多选
                                placehoder:默认提示
                                autofocus:自动获取焦点
                                required:必填项
                        -->
                        <input type="text" autofocus="autofocus"/>
                        <input type="text" required="required" />
三、Go Web前置 - CSS
四、Go Web前置 - JS
五、Go Web前置 - JQuery
六、前端技术之Vue框架

GORM及数据库

一、GORM前置:MySQL

二、MySQL性能调优与架构设计

三、GORM

Go开发应用中间件

一、Redis_高效的NoSQL数据库

二、Redis缓存数据库进阶

三、Redis之go编程实战

四、消息中间件-Kafka实战

五、Kafka之go编程实战

六、RocketMQ基础实战版

七、NoSQL-MongoDB实战

八、分布式文件存储系统Minio

全文检索 ES

一、Elasticsearch核心知识篇

Go Web开发之企业级框架

一、Web框架

二、Gin深入实战

三、Beego框架开发实战

四、微服务架构

五、Go框架开发

六、Kong入门与实战

七、Logrus日志

企业级项目实战


http://www.kler.cn/a/592636.html

相关文章:

  • 【Dive Into Stable Diffusion v3.5】1:开源项目正式发布——深入探索SDv3.5模型全参/LoRA/RLHF训练
  • 使用 Wireshark 在 Ubuntu 22.04 上抓包分析网络流量
  • 危化品经营单位考试:从基础夯实到能力提升的进阶之路​
  • Docker 存储
  • 麒麟服务器操作系统Node.js环境部署手册
  • 当今前沿技术:改变生活的创新趋势
  • 微信小程序检测滚动到某元素位置的计算方法
  • 黑马跟学.苍穹外卖.Day08
  • Android Launcher3 首屏图标锁定技术方案解析
  • docker登陆问题
  • Linux 安装apache服务
  • Spring Boot Bean 的生命周期管理:从创建到销毁
  • Eclipse 创建 Java 类
  • 【前端 vue 或者麦克风,智能语音识别和播放功能】
  • 利用flex布局写的一个样式
  • Python的内置函数 - min()
  • 英伟达“AI 超级碗”开幕
  • 事件、页面跳转、wxml语法——微信小程序学习笔记
  • 机器学习 Day08,案例实现,代码学习,数据分析基本完成
  • WPF 布局舍入(WPF 边框模糊 或 像素错位 的问题)