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 mainimport ("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 mainimport ("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 mainimport ("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 mainimport ("fmt""sync"
)var wg sync.WaitGroupfunc 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 mainimport ("fmt""sync"
)var wg sync.WaitGroupfunc main() {// 启动五个协程for i := 1; i <= 5; i++ {wg.Add(1) // 协程开始go func(n int) {defer wg.Done() // 提前写defer,防止忘记计数器-1fmt.Printf("这是%d号协程\n", n)/*其余代码其余代码其余代码其余代码其余代码*/}(i)}wg.Wait() // 阻塞等待协程全部完成
}
- 可以最开始在知道协程次数的情况下先Add操作:
package mainimport ("fmt""sync"
)var wg sync.WaitGroupfunc main() {wg.Add(5) // 预先设置协程数// 启动五个协程for i := 1; i <= 5; i++ {go func(n int) {defer wg.Done() // 提前写defer,防止忘记计数器-1fmt.Printf("这是%d号协程\n", n)/*其余代码其余代码其余代码其余代码其余代码*/}(i)}wg.Wait() // 阻塞等待协程全部完成
}
注意:Add中加入的数字和协程的次数一定要保持一致
6. 多个协程操纵统一数据案例(互斥锁)
(1)案例
package mainimport ("fmt""sync"
)var totalNum int
var wg sync.WaitGroupfunc 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() // 阻塞等待协程全部完成sfmt.Println(totalNum)
}
- 结果:在理论上,这个totalNum结果应该是0 ,无论协程怎么交替执行,最终想象的结果就是0,但是事实上:不是

-
问题出现的原因:(图解为其中一种可能性)

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

- 代码实现
package mainimport ("fmt""sync"
)var totalNum int
var wg sync.WaitGroup
var lock sync.Mutexfunc 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() // 阻塞等待协程全部完成sfmt.Println(totalNum)
}
7. 读写锁的使用
golang中sync包实现了两种锁 Mutex (互斥锁)和 RWMutex(读写锁)
(1)互斥锁
- 其中Mutex为互斥锁,Lock()加锁,Unlock()解锁,使用Lock()加锁后,便不能再次对其进行加锁,直到利用Unlock()解锁对其解锁后,才能再次加锁.适用于读写不确定场景,即读写次数没有明显的区别
- 性能、效率相对来说比较低,因为大部分场景都是读多写少
(2)读写锁
- RWMutex是一个读写锁,其经常用于读次数远远多于写次数的场景.
- 在读的时候,数据之间不产生影响,写和读之间才会产生影响
(3)案例
package mainimport ("fmt""sync""time"
)var wg sync.WaitGroup // 只定义无需赋值
// 加入读写锁:
var lock sync.RWMutexfunc 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 mainimport "fmt"func main() {// 定义管道,声明管道 ---> 定义一个int类型的管道var intChan chan int// 通过make进行初始化:管道可以存放3个int类型的数据intChan = make(chan int, 3)// 证明管道是引用类型fmt.Printf("intChan:%v\n", intChan)// 向管道存放数据intChan <- 10num := 20intChan <- numintChan <- 40// 注意:不能存放大于容量的数据num1 := <-intChannum2 := <-intChannum3 := <-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 mainimport "fmt"func main() {// 定义管道,声明管道 ---> 定义一个int类型的管道var intChan chan int// 通过make进行初始化:管道可以存放3个int类型的数据intChan = make(chan int, 3)// 在管道中存放数据:intChan <- 10intChan <- 20// 关闭管道:close(intChan)// 再次写入数据:--->报错// intChan<- 30// 当管道关闭后,读取数据是可以的:num := <-intChanfmt.Println(num)
}
11. 管道的遍历
(1)管道的遍历
- 管道支持
for-range的方式进行遍历,请注意两个细节
(1)在遍历时,如果管道没有关闭,则会出现deadlock的错误
(2)在遍历时,如果管道已经关闭,则会正常遍历数据,遍历完后,就会退出遍历。
package mainimport ("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-rangefor v := range intChan {fmt.Println("value = ", v)}
}
12. 协程和管道协同工作的案例
(1)案例需求
- 请完成协程和管道协同工作的案例,具体要求:
(1)开启一个writeData协程,向管道中写入50个整数.
(2)开启一个readData协程,从管道中读取writeData写入的数据。
(3)注意: writeData和readDate操作的是同一个管道
(4)主线程需要等待writeData和readDate协程都完成工作才能退出
(2)原理图

(3)代码
package mainimport ("fmt""sync""time"
)var wg sync.WaitGroup
var intChan chan intfunc writeData() {defer wg.Done()for i := 1; i <= 50; i++ {intChan <- ifmt.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 mainimport ("fmt"
)func main() {// 默认情况下,管道是双向的--》可读可写:// var intChan1 chan int// 声明为只写:chan<- intvar intChan2 chan<- int // 管道具备<- 只写性质intChan2 = make(chan int, 3)intChan2 <- 20//num := <-intChan2 报错fmt.Println("intChan2:", intChan2)// 声明为只读:<-chan intvar intChan3 <-chan int // 管道具备<- 只读性质if intChan3 != nil {num1 := <-intChan3fmt.Println("num1:", num1)}// intChan3<- 30 报错}
14. 管道的阻塞
(1)当管道只写入数据,没有读取,就会出现阻塞
package mainimport ("fmt""sync"_ "time"
)var wg sync.WaitGroup // 只定义无需赋值
// 写:
func writeData(intChan chan int) {defer wg.Done()for i := 1; i <= 10; i++ {intChan <- ifmt.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 mainimport ("fmt""sync""time"
)var wg sync.WaitGroup // 只定义无需赋值
// 写:
func writeData(intChan chan int) {defer wg.Done()for i := 1; i <= 10; i++ {intChan <- ifmt.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 mainimport ("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 mainimport ("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 := 10num2 := 0result := num1 / num2fmt.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 mainimport ("fmt""net" // 所需的网络编程全部都在net包下
)func main() {fmt.Println("客户端启动")// 调用Dial函数:参数需要指定tcp协议,需要指定服务器端的 IP + PORTconn, 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 mainimport ("fmt""net"
)func main() {fmt.Println("服务器端启动")// 进行监听:需要指定服务器端TCP协议,服务器端的 IP + PORTlisten, 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 mainimport ("bufio""fmt""net" // 所需的网络编程全部都在net包下"os"
)func main() {fmt.Println("客户端启动")// 调用Dial函数:参数需要指定tcp协议,需要指定服务器端的 IP + PORTconn, 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 mainimport ("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 + PORTlisten, 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 mainimport ("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 + 30fmt.Println(n2)
}func main() {// 对基本数据类型进行反射var num int = 100testReflect(num)
}
3.对结构体类型反射
(1)反射相关的函数
-
reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型
-
reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型(reflect.Value是一个结构体类型),通过reflect.Value,可以获取到关于该变量的很多信息。
(2)代码
package mainimport ("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 stringAge int
}func main() {// 对结构体进行反射stu := Student{Name: "aa",Age: 18,}testReflect(stu)
}
4.获取变量的类别
(1)获取变量的类别:两种方式
- reflect.Type.Kind()
- reflect.Value.Kind()
(2)Kind的值是常量值

(3)代码
package mainimport ("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 stringAge int
}func main() {// 对结构体进行反射stu := Student{Name: "aa",Age: 18,}testReflect(stu)
}
5.通过反射修改变量
(1)修改基本数据类型的值
package mainimport ("fmt""reflect"
)// 利用一个函数,函数的参数定义为空接口:
func testReflect(i interface{}) { // 空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为我们可以把任何一个变量赋给空接口。reValue := reflect.ValueOf(i)// 通过SetInt()来改变值:reValue.Elem().SetInt(40)}
func main() {// 对基本数据类型进行反射:// 定义一个基本数据类型:var num int = 100testReflect(&num) // 传入指针地址fmt.Println(num)
}

(2)修改结构体类型的值
package mainimport ("fmt""reflect"
)// 定义一个结构体:
type Student struct {Name stringAge 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 mainimport ("fmt""reflect"
)// 定义一个结构体:
type Student struct {Name stringAge 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 = names.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.Valueparams = 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 mainimport "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> 5月 26日,“建议父母持合格<父母证>上岗©”冲上微博热搜,迅速引发热议。在正在召开的全国两会上,全国政协委员许洪玲建议在社区举办家长课堂,建立“家长教育指导工作室”。针对准备入小学的家长开展相关课程教育,颁发“合格父母”上岗证随学生档案入学。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,Istart:设置起始标号
-->
<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:可以使用在线解析工具查看:urlencodeaaa:目标资源 --》去当前项目下找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"readonlyreadonly = "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日志
企业级项目实战
相关文章:
Golang开发
Golang 文章目录 Golang预备技术一、算法与数据结构第1章:基础算法第2章:数据结构第3章:搜索与图论第4章:数论第5章:动态规划第6章:贪心第7章:算法竞赛入门 二、Linux操作系统与Shell编程三、计…...
AI入门7:python三种API方式调用本地Ollama+DeepSeek
回顾 书接上篇:各种方式搭建了本地知识库: AI入门:AI模型管家婆ollama的安装和使用-CSDN博客 AI入门2:本地AI部署,用ollama部署deepseek(私有化部署)-CSDN博客 AI入门3:给本地d…...
IIS+ASP程序500错误排查及解决方法
IISASP程序500错误排查及解决方法 一、基础配置检查 检查IWAM账户密码一致性 IIS运行ASP程序依赖IWAM账户,若系统更新或域策略调整导致密码不一致会引发500错误。需同步IIS元数据库与应用池账户密码15。 验证目录权限 确保站点文件目录的Network Servi…...
Java 中 LinkedList 的底层数据结构及相关分析
Java 中 LinkedList 的底层数据结构及相关分析 1. 概述 LinkedList 是 Java 集合框架(Java Collections Framework,JCF)中的一个双向链表实现,它位于 java.util 包下,支持 列表(List) 和 队列…...
《线程池:Linux平台编译线程池动态库发生的死锁问题》
关于如何编译动态库可以移步《Linux:动态库动态链接与静态库静态链接》-CSDN博客 我们写的线程池代码是闭源的,未来想提供给别人使用,只需要提供so库和头文件即可。 系统默认库文件路径为: usr/lib usr/loacl/lib 系统默认头文件…...
Python Bug修复案例分析:Python 中常见的 IndentationError 错误 bug 的修复
在 Python 编程的世界里,代码的可读性和规范性至关重要。Python 通过强制使用缩进来表示代码块的层次结构,这一独特的设计理念使得代码更加清晰易读。然而,正是这种对缩进的严格要求,导致开发者在编写代码时,稍有不慎就…...
合React宝宝体质的自定义防抖hook
本文为开发开源项目的真实开发经历,感兴趣的可以来给我的项目点个star,谢谢啦~ 具体博文介绍: 开源|Documind协同文档(接入deepseek-r1、支持实时聊天)Documind 🚀 一个支持实时聊天和接入 - 掘…...
以太坊节点间通信机制 DEVp2p 协议
文章目录 概要1. 协议概述2. 协议栈与关键技术3. RLPx 协议核心机制3.1 数据包结构3.2 加密握手流程 4. 核心子协议与消息类型4.1 基础控制消息4.2 以太坊子协议示例4.3 网络 ID 列表 5. 安全与防攻击机制6. 节点标识与声誉管理7. 对比其他区块链通信协议8. 总结 概要 1. 协议…...
Pytorch使用手册—自定义 C++ 和 CUDA 扩展(专题五十二)
提示 从 PyTorch 2.4 开始,本教程已被废弃。请参考 PyTorch 自定义操作符,了解关于通过自定义 C++/CUDA 扩展扩展 PyTorch 的最新指南。 PyTorch 提供了大量与神经网络、任意张量代数、数据处理等相关的操作。然而,您可能仍然会发现自己需要一个更自定义的操作。例如,您可能…...
AI大模型在物联网行业的应用场景深度解析
AI大模型在物联网行业的应用场景 引言 AI大模型与物联网(IoT)的融合正在重塑产业智能化格局。通过海量数据的实时处理与智能决策能力,AI大模型为物联网设备赋予了更高效的感知、分析和响应机制,推动智慧城市、智能制造、医疗健康…...
OpenCV旋转估计(1)用于估计图像间仿射变换关系的类cv::detail::AffineBasedEstimator
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 基于仿射变换的估计器。 这种估计器使用匹配器估算的成对变换来为每个相机估算最终的变换。 cv::detail::AffineBasedEstimator 是 OpenCV 库中…...
PyCharm的终端(terminal)中进入指定conda虚拟环境
参考这篇博文: PyCharm的终端(terminal)中进入指定conda虚拟环境_pycharm配置conda终端-CSDN博客...
高级java每日一道面试题-2025年3月05日-微服务篇[Eureka篇]-Eureka在微服务架构中的角色?
如果有遗漏,评论区告诉我进行补充 面试官: Eureka在微服务架构中的角色? 我回答: 在微服务架构中,Eureka作为Netflix开源的服务发现组件,在解决服务间通信的寻址问题方面扮演着至关重要的角色。以下是结合提供的内容对Eureka在微服务架构中的角色进行…...
c++类和对象(下篇)下
下面就来补充一下c雷和对象最后一点内容. 首先先补充一下上一篇博客上c类和对象(下篇)上-CSDN博客最后学习的静态成员变量的小练习求123...n_牛客题霸_牛客网 (nowcoder.com)下面就是题解.灵活的运用了静态成员变量不销毁的特点,建立数组利用构造函数来完成n次相加. class A{ …...
HTTP 失败重试(重发)方案
在 Qt 网络开发中,使用 QNetworkAccessManager 进行 HTTP 请求时,可能会遇到网络超时、服务器错误等情况。为了提高请求的可靠性,可以实现 HTTP 失败重试(重发) 机制。下面介绍几种常见的 失败重发方案: 单…...
使用WebDAV将文件传输到实时(RT)目标 转发
如何配置Web分布式创作和版本控制(WebDAV)服务器并使用它来与我的实时(RT)目标之间传输文件? 在目标上安装 WebDAV 和 SSL 支持 NI Linux Real-Time 您无需完成任何安装 WebDAV 和 SSL 支持的步骤。默认情况下,这些组件在NI Linu…...
Web爬虫利器FireCrawl:全方位助力AI训练与高效数据抓取
Web爬虫利器FireCrawl:全方位助力AI训练与高效数据抓取 一、FireCrawl 项目简介二、主要功能三、FireCrawl应用场景1. 大语言模型训练2. 检索增强生成(RAG):3. 数据驱动的开发项目4. SEO 与内容优化5. 在线服务与工具集成 四、安装…...
如何避免PRD(需求文档)成为“沟通黑洞”
在撰写PRD(需求文档)时,要避免成为“沟通黑洞”,必须聚焦目标清晰、需求拆解、协同评审、持续迭代等关键点。其中,协同评审尤其重要——通过在文档完成初期就邀请相关部门共同审阅讨论,可以及早发现需求逻辑…...
c++基础知识--返回值优化
在 C 中,Named Return Value Optimization(NRVO,具名返回值优化) 是一种编译器优化技术,用于消除函数返回一个局部对象时的拷贝或移动操作。它是 返回值优化(RVO) 的一种更复杂的变体࿰…...
go面向对象编程三大特性,封装、继承和多态
1.简介 go具有面向对象编程的封装、继承和多态的特性,只是实现的方式和其它OOP语言不一样,下面看下go的三大特性是如何实现的。 2.封装 2.1基本介绍 封装就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其它包只能通过被授权的操作(方法),才能…...
巧用符号链接搬移C盘中的软件数据目录到其他盘
#工作记录 我们知道,在Windows11系统,有些软件是不能指定安装目录的,有些软件即使指定了安装目录可是在更新版本之后还是会安装到默认的C盘目录中(比如剪映),而且每次安装某些软件之后,这些软件…...
使用 PIC 微控制器和 Adafruit IO 的基于 IoT 的 Web 控制家庭自动化
使用 PIC 微控制器和 Adafruit IO 的基于 IoT 的 Web 控制家庭自动化 家庭自动化一直是我们大多数人的灵感来源。从我们舒适的椅子或任何房间的床上切换交流负载,而无需伸手去触碰另一个房间的开关,听起来很酷,不是吗!.现在,在物联网时代,多亏了 ESP8266 模块,它使从世界…...
高性能Java并发编程:线程池与异步编程最佳实践
Future模式与CompletableFuture 处理异步任务时,Future与CompletableFuture是强有力的工具。 实战案例:多API并行调用 假设我们需要从多个微服务获取数据,然后合并结果: public UserProfileDto getUserProfile(Long userId) {…...
【Java篇】一气化三清:类的实例化与封装的智慧之道
文章目录 类和对象(中)五、对象的构造及初始化5.1 如何初始化对象5.2 构造方法5.2.1 构造方法的概念5.2.2 构造方法的特性 5.3 默认初始化5.4 就地初始化 六、封装6.1 封装的概念6.2 访问限定符6.3 封装扩展之包6.3.1 包的概念6.3.3导入包6.3.3全类名6.3…...
VMware上调整centos终端的背景颜色
目录 1. 正常打开一个终端,背景颜色默认为白色 2. 在打开的终端页面上右击,选择“配置文件首选项” 3. 取消默认勾选的 “使用系统主题中的颜色” 即可 1. 正常打开一个终端,背景颜色默认为白色 2. 在打开的终端页面上右击,选择…...
Netty源码—1.服务端启动流程二
大纲 1.服务端启动整体流程及关键方法 2.服务端启动的核心步骤 3.创建服务端Channel的源码 4.初始化服务端Channel的源码 5.注册服务端Channel的源码 6.绑定服务端端口的源码 7.服务端启动流程源码总结 5.注册服务端Channel的源码 (1)注册服务端Channel的入口 (2)注册…...
Latex2024安装教程(附安装包)Latex2024详细图文安装教程
文章目录 前言一、Latex2024下载二、Texlive 2024安装教程1.准备安装文件2.启动安装程序3.配置安装选项4.开始安装5.安装完成6.TeX Live 2024 安装后确认 三、Texstudio 安装教程1.准备 Texstudio 安装2.启动 Texstudio 安装向导3.选择安装位置4.等待安装完成5.启动 Texstudio6…...
用了Cline和华为云的大模型,再也回不去了
这两年AI火热,受影响最大的还是程序员群体,因为编程语言是高度形式化的,完全可以用BNF等形式精确地定义,不像自然语言那样,容易出现歧义。另外开源是软件界的潮流,GitHub上有海量的开源代码可供AI来训练&am…...
解码软件需求的三个维度:从满足基础到创造惊喜
在软件开发的世界里,用户需求就像一张复杂的地图,指引着产品前进的方向。但并非所有需求都能带来同样的价值——有些是产品生存的“氧气”,有些是吸引用户的“磁石”,还有一些则是让人眼前一亮的“魔法”。如何区分它们࿱…...
<table>内有两行<tr>,第一行设定高度为60,剩余第二行,和右侧元素高度补齐。
实现 <table> 内第一行高度设定为 60px,第二行和右侧元素高度补齐的效果,你可以通过 CSS 样式来控制。示例: 为第一行 <tr> 设置固定高度 60px。对于右侧元素,假设它是一个 <div> 或者其他容器,将其…...
