Internet_worm
Internet_worm的博客

导航

 

Go

配置Go开发环境

1.下载SDK:

Downloads - The Go Programming Language (google.cn)

2.解压配置环境变量

以解压缩的版本为例,假设将文件解压到D盘并重命名为goSDK ,在环境变量path中添加新的值,D:\goSDK\bin 即可

验证:在任意目录开启dos窗口 执行指令:go -version

结果:

 

3.目录结构

通常GO语言的工作空间为:goProject/src/goCode/Project01

代码目录一般在Project01/main下

4.第一个Go代码(helloWorld.go)

package main

import "fmt"

func main(){

fmt.Println("Hello WOrld!")

}

5.编译和执行

在helloWorld.go 同级目录下执行指令 “go build helloWorld.go"将会生成一个名称为helloWorld.exe 的二进制可执行文件

执行helloWorld.exe ,控制台输出 Hello World!

6.总结

至此,Go语言的开发环境就配置完毕

GoLang 正题

1.API

Go语言提供了大量的标准库,官方标准库中文社区:https://studygolang.com/pkgdoc

2.变量声明

2.1

声明变量的格式:

//第一种方式:声明类型+初始化
var age int = 20
//第二种方式:1.声明类型 2.初始化  ,如果不对变量进行初始化,那么在明确类型的情况下,系统会给定默认值
var age int
age = 20
//第三种方式:不声明类型+初始化,系统会根据初始化的值自动推断变量的类型
var num = 10
//第四种方式:省略var 关键字,关键字":="
sex := "男"
//第五种方式:一次性声明多个变量
var(
    v1 = 10
    v2 = "Develoven"
    v3 = 3.1415
)
​

go 是强类型语言,如果不对变量进行初始化,会有默认值

如果一个变量名、函数名、常量名是以大写字母开头,则可以被其他的包访问,如果首字母是小写只能被所在包访问

PS:若需要左侧显示目录的树形结构, ”设置“——user——features——Explorer——Compact Folders(取消勾选)

3.数据类型

4.指针

指针就是一个存储了一个内存地址的变量

package main
​
import "fmt"
​
func main(){
    
    var age int = 18
    fmt.Println("变量age的值:",age)
    fmt.Println("变量age的地址:",&age)
​
    var prt * int = &age
    fmt.Println("指针变量prt的值:",prt)
    fmt.Println("指针变量prt的地址:",&prt)
    fmt.Println("指针变量prt指向的地址的值:",*prt)
​
}

运行结果:

4.1 可以通过指针改变指向值

package main
import "fmt"
func main (){
var num int = 10
fmt.Println("num初始值:",num)
var prt *int = &num
*prt = 20
fmt.Println("num当前值:",num)
}

运行结果:

5.包引用

在go语言中,如果导入自定义包,那么包名是从 $GOPATH/src后开始计算的,所以在配置环境变量时不仅需要在PATH上添加GO语言的SKD目录里还需要配置GOPATH环境变量,GOPAT表示工作目录,类似于idea和eclipse的工作空间,前边提到的工作空间的常见目录结构:goProject/src/goCode/Project01,其中goProject层目录就是GOPATH应该指向的目录

go有两种包管理模式,gomod和gopath,两者相互不兼容,在一些版本中,默认是启用gomod 的,在一些版本中是使用gopath的,还有一些版本中是模棱两可的(这个版本中若不做环境修改则不能导如第三方依赖),使用指令go env查看

在控制台输入并运行指令“go env”

  • GO111MODULE=off,无模块支持,go命令行将不会支持module功能,寻找依赖包的方式将会沿用旧版本那种通过vendor目录或者GOPATH模式来查找。

  • GO111MODULE=on,模块支持,go命令行会使用modules,而一点也不会去GOPATH目录下查找

  • GO111MODULE= ,此模式下不能引入包,必须设置为on或者off(这是我初学阶段遇到的一个大坑

重申一下,在go语言中,变量、常量、函数的权限用首字母的大小写控制,也就是说如果先让外部的包能够调用到内部定义的变量、常量、函数,必须将其首字母大写

6.获取用户终端输入

在编程中,需要接受用户数的数据时,可以使用键盘键入获取用户输入数据

6.1 fmt.Scanln

Scanln 以换行符为终止扫描的信号

    package main
​
import "fmt"
​
//键入学生的  姓名   性别  地址   电话 等信息并输出
func main(){
    var name string
    fmt.Println("请输入学生姓名:")
    fmt.Scanln(&name)
​
    var gender string
    fmt.Println("请输入学生性别:")
    fmt.Scanln(&gender)
​
    var address string
    fmt.Println("请输入学生家庭住址:")
    fmt.Scanln(&address)
​
    var phone string
    fmt.Println("请输入学生手机号:")
    fmt.Scanln(&phone)
​
    fmt.Println("学生姓名:",name)
    fmt.Println("学生性别:",gender)
    fmt.Println("学生家庭住址 :",address)
    fmt.Println("学生手机号:",phone)
​
}

运行结果:

6.2 fmt.Scanf

Scanf需要自定义键入格式,因为自定义键入格式比较麻烦,所以很少使用

package main
import "fmt"
func main(){
    var name string
    var gender string
    var address string
    var phone string
    fmt.Println("请依次输入学生的名称/性别/地址/手机号并以“ ”隔开")
    fmt.Scanf("%s %s %s %s", &name, &gender, &address, &phone)
    fmt.Println("学生姓名:", name)
    fmt.Println("学生性别:", gender)
    fmt.Println("学生家庭住址 :", address)
    fmt.Println("学生手机号:", phone)
}

执行结果:

7.流程控制

7.1 if

if与Java相同,有两个小细节需要注意:

1.条件表达式可以不用(),go推荐不适用()

2.{}必须要存在,即使是一句逻辑代码,也不能省略

3.if后可以并列接入变量的定义

package main
​
import "fmt"
​
func main() {
    if count := 20; count < 30 {
        fmt.Println("count<30")
    }
}
​

执行结果

7.2 switch

switch示例代码

package main
import "fmt"
func main(){
​
    var score int 100
    switch score/10
    case 10 :
        fmt.Println("满分")
    case 9 :
    fmt.Println("90+")
    case 8 :
    fmt.Println("80+")
    case 7 :
    fmt.Println("70+")
    case 6 :
    fmt.Println("60+")
    case 5 :
    fmt.Println("50+")
    default :
    fmt.Println("成绩低于50分,不合格")
}

注意点:

1.switch后的表达式可以是一个常量值、变量,也可以是一个有返回值的函数

2.如果case后的值是一个常量表达式,那么必须不能重复

3.case后的值必须与switch后的表达式属于同一类型

4.case后的值可以是多个,中间用","隔开

package main
import (
"fmt"
"math"
)
func main(){
var a int 
fmt.Println("请输入a的值:")
fmt.Scanln(&a)
switch a/math.Abs(a){
    case 1,-1 :
    fmt.Println("a=",a)
    fmt.Println("Abs(a)=",Abs(a))
​
} 
}

运行结果

5.case后不需要break,golang会自动短路

6.default 语句的位置是任意的,而且不是必需的

7.switch后可以不带表达式,当作if语句使用,可以使用但 不推荐

package main
import "fmt"
func main(){
var a int32 = 2 
    var a int32 = 2
    switch {
​
    case a == 1:
            fmt.Println("a==1")
    case a == 2:
            fmt.Println("a==2")
    default:
            fmt.Println("default")
    }
}

运行结果:

8.switch后可以定义一个变量,以分号结束,可以使用,但不推荐

package main
import "fmt"
func main(){
    switch a := 7 ; {
        case a >= 7:
        fmt.Println("a >= 7")
    default :
        fmt.Println("a < 7")
    }
}

9.switch穿透,关键字fallthrouth,如果在case后加入关键字fallthrough,则会继续执行下一个case,称作switch穿透

package main
import "fmt"
func main(){
​
    var score int 
    fmt.Println("请输入成绩:")
    fmt.Scanln(&score)
    fmt.Println("输入的成绩是:",score)
    switch score/10 {
    case 10 :
        fmt.Println("满分")
        fallthrough
    case 9 :
    fmt.Println("90+")
    case 8 :
    fmt.Println("80+")
    case 7 :
    fmt.Println("70+")
    case 6 :
    fmt.Println("60+")
    case 5 :
    fmt.Println("50+")
    default :
    fmt.Println("成绩低于50分,不合格")
    }
    
}

执行结果:

7.3 for

在golang中,循环表达式只有for,格式与其他语言格式相同,注意点是初始化表达式不能使用 var i int =0 初始化必须使用 i := 0 的方式定义初始化表达式:

for 初始化表达式 ;布尔表达式;迭代因子 {

循环体;

}

package main
​
import "fmt"
​
func main() {
    var sum int = 0
    for i := 0; i <= 10; i++ {
        sum += i
    }
    fmt.Println("sum = ", sum)
}

执行结果:

7.3.1 for循环的灵活格式
package main

import "fmt"
//格式1:基础格式
func main() {
	var sum int = 0
	for i := 0; i <= 10; i++ {
		sum += i
	}
	fmt.Println("sum = ", sum)
}
package main

import "fmt"
//格式2:将初始化表达式和迭代因子分别提起到for循环外和循环体内,也可单独提取出其中一个
func main() {
	var sum int = 0
	i := 0
	for i <= 10 {
		sum += i
		i++
	}
	fmt.Println("sum = ", sum)
}
package main
import "fmt"
//golang中的死循环格式1
func main(){
	for {
		fmt.Println("Golang 是世界上最好的语言")
	} 
}
package main
import "fmt"
//golang中的死循环格式2
func main() {
    for {
        fmt.Println("Golang 是世界上最好的语言")
    }
}

for ... range 结构,for…range结构是Golang独有的一种迭代结构,for…range可以遍历数组、切片、map以及通道,for…range结构类似于其他语言中的foreach,一般的格式为:

for key,value :range coll {

循环体

}

package main
​
import "fmt"
//暂时不适用中文,学习完切片即可使用
func main() {
    var str string = "hello golang"
    for i := 0; i < len(str); i++ {
        fmt.Printf("%c\n", str[i])
    }
}

执行结果:

使用 for…range格式实现上述代码:

package main
​
import "fmt"
​
func main() {
    //此处的中文是可以使用的
    var str string = "hello golang,你好"
    for i, value := range str {
        fmt.Printf("i = %d,value = %c\n", i, value)
    }
}

执行结果:

7.4 break

break 的用途:

1.switch case 的case块后都需要break关键字来结束,在传统的语言中如果不加入break关键字会出现代码穿透的效果,但是在go语言中可以省略

package main
​
import "fmt"
​
func main() {
    var score int64
    fmt.Print("请输入成绩:")
    fmt.Scanln(&score)
    switch score / 10 {
    case 10:
        fmt.Println("满分")
        break
    case 9:
        fmt.Println("优秀")
        break
    case 8:
        fmt.Println("80+")
        break
    default:
        fmt.Println("80-")
    }
}
​

运行结果:

2.break可以跳出当前循环

package main

import "fmt"

func main() {
	for i := 1; i <= 5; i++ {
		for j := 2; j <= 4; j++ {
			fmt.Printf("i = %v,j = %v \n", i, j)
			if i == 2 && j == 3 {
				break
			}
		}
	}
}

执行结果:

3.break跳出指定循环(标签的使用)

package main
​
import "fmt"
​
func main() {
tag:
    for i := 0; i <= 5; i++ {
        for j := 1; j <= 4; j++ {
            fmt.Printf("i = %v ; j = %v ;\n", i, j)
            if i == 2 && j == 2 {
                break tag
            }
        }
    }
}

执行结果:

7.5 continue

continue结束本次循环

package main

import "fmt"

func main() {
	for i := 1; i <= 5; i++ {
		if i == 2 {
			continue
		} else {
			fmt.Printf("i = %v \n", i)
		}
	}
}

运行结果:

7.6 go...to

在汇编中常用,但是在Golang 、Java的高级语言中不推荐使用

8 函数

ps:Golang的函数比较特殊,他的返回值可以是多个

Golang的函数也支持可变长度,与Java相同 比如:func test(args...int){

函数体,函数体内部处理可变参数时候,可将其当作切片来处理,切片类似于Java中的数

}

8.1 函数定义的格式:

func 函数名 (形参列表) (返回值列表){

函数体

}

形参列表描述了函数的参数类型以及参数名称,他们作为局部变量,由函数的调用者提供,函数的返回值列表描述了函数返回值的变量名以及类型,

8.1.1定义函数计算两个整数的和
package main
​
import "fmt"
​
func main() {
    var a int = 10
    var b int = 20
    sum := sum(a, b)
    fmt.Printf("%v + %v = %v", a, b, sum)
}
func sum(a int, b int) int {
    sum := a + b
    return sum
}
​

运行结果:

8.1.2 定义函数计算两个数的差
package main

import "fmt"

func main() {
	var a int = 10
	var b int = 20
	difference := difference(a, b)
	fmt.Printf("%v - %v = %v", a, b, difference)
}
func difference(a int, b int) int {
	difference := a - b
	return difference
}

运行结果:

8.1.3定义函数计算两个数的和与差
package main
​
import "fmt"
​
func main() {
    var a int
    var b int
    fmt.Print("请输入a的值:")
    fmt.Scanln(&a)
    fmt.Println()
    fmt.Print("请输入b的值:")
    fmt.Scanln(&b)
    sum, difference := sumAndDifference(a, b)
    fmt.Printf("%v + %v = %v \n%v - %v = %v", a, b, sum, a, b, difference)
}
func sumAndDifference(a int, b int) (int, int) {
    sum := a + b
    difference := a - b
    return sum, difference
}

运行结果:

8.1.4 可变参数

Golang的函数也支持可变长度,与Java相同 比如:func test(args...int){

函数体,函数体内部处理可变参数时候,可将其当作切片来处理,切片类似于Java中的数

}

package main

import "fmt"

func main() {
	test(1)
	fmt.Println("--------------------------------")
	test(1, 2, 3)
	fmt.Println("--------------------------------")
	test(1, 2, 3, 4, 5)
}
func test(args ...int) {
	for i := 0; i < len(args); i++ {
		fmt.Println(args[i])
	}
}

运行结果:

8.1.5 传值与传指针

当我们传一个参数值到被调用函数里面时,实际上是传了这个值的一份copy,当在被调用函数中修改参数的值的时候,调用函数中响应实参并不会发生任何变化,因为数值变化只是作用在copy上

我们来看一个例子:

package main
import "fmt"
func add(a int ) int {
    a = a+1
    return a 
}
func main (){
    x := 3
    fmt.Println("x = ",x)//输出 x=3
    x1 := add(x)
    fmt.Println("x1 = ",x1)//输出x1 = 4
    fmt.Println("x = ",x)//输出X=3
}

我们可以看到,虽然我们调用了add函数,并且在add中执行了 a = a+1 的操作,但是x的值是没有发生变化的,理由很简单,因为当我们调用add的时候,add接收到的参数其实是x的copy ,而并不是x本身。那如果我们真的需要传这个X的本身,该怎么办呢?

这个就牵扯到指针。变量在内存中是存放在一定地址上的,修改变量实际是修改变量地址处的内存。只有add函数知道x变量所在的地址,才能修改x变量的值,所以我们只需要将x变量的地址&x传入函数,并将函数的参数类型从int 修改为 *int即可,即将函数的参数类型修改为指针类型,这样就能在函数中修改x的值。此时参数仍然是按copy传递的,这是copy的是一个指针,示例如下

package main
import "fmt"
func add1(a * int ) int {
    *a = *a +1
    return *a
}
func main(){
    m := 100
    fmt.Println("m = ",m)//输出m = 100
    m1 := add1(&m)
    fmt.Println("m1 = ",m1)//输出 m1 = 101 
    fmt.Println("m = ",m)//输出 m = 101
}

这样我们就达到了修改x的目的,那么到底传指针有什么好处呢?

①传指针使得多个函数能操作同一个对象

②传指针比较轻量级(8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会话费相对较多的系统开销(内存和时间)。所以当需要传递打的结构体的时候,用指针是一个明智 的选择

③Go语言中,channel,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针,但是如果要盖面slice的长度,需要去地址传递指针

8.2 init函数

init函数是初始化函数,可以用来执行一些初始化操作,每一个.go文件都可以拥有一个init函数,该函数会在main函数之前执行,等同于Java中的静态代码块

package main
​
import "fmt"
​
func main() {
    fmt.Println("main--------------------")
}
func init() {
    fmt.Println("init----------------------")
}

8.3 匿名函数

Golang支持匿名函数,当我们希望一个函数只被调用1次时,可以使用匿名函数

8.3.1 匿名函数的调用方式
8.3.1.1 在匿名函数定义时直接调用这种匿名函数只能调用一次
package main
​
import "fmt"
​
func main() {
    a := func(i int, j int) (k int) {
        k = i + j
        return
    }(10, 20)
    fmt.Println(a)
}

执行结果:

8.4 闭包

//需求,求和
package main
import "fmt"
func main(){
    
}
func getSum() func(int) int{
    int sum =0
    return func(num int ){
        return sum+num
    }
}
​

8.5关键字defer

defer关键字的使用:在函数中,经常需要释放资源,在函数执行完毕后及时释放资源,在Go语言中使用defer关键字

当程序执行到defer关键字定义的语句时,会把当前语句压入一个特殊栈中,待所有非defer语句执行完成后,再执行特殊栈中的语句,

注意点①

入栈时,入栈的信息为代码按照顺序执行执行到defer语句时的即时信息,例如

var num int = 10

defer fmt.Println("numDefer=",num)

num += 50

fmt.Println("num=",num)

此段代码输出语句有两条,先输出num=60 再输出 numDefer =10

注意点②

栈的规则时先进后出,所以后压入的语句会先执行

//案例展示
package main
​
import "fmt"
​
func main() {
    var num int = 10
    defer fmt.Println("  numDefer1=", num)
    num += 50
    defer fmt.Println("  numberDefer=", num)
    fmt.Println("  num=", num)
}
​

执行结果:

总结:延迟语句(defer),可以在函数中添加多个defer语句,当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭响应的资源,不然很容易造成资源泄露的问题,在打开一个资源的时候,我们一般会采用如下代码:

func ReadWrite() bool {
    file.open("file")
    //do something
    if(failureX){
        file.Close()
        return false
    }
    if(failureY){
        fiel.Close()
        return false
    }
    file.Close()
    return true
}
​

在上述代码中,有很多的重复代码,Go语言的defer就有效地解决了这个问题,使用defer之后,不仅代码量减少了很多,而且程序变得更加优雅。在defer后指定的函数会在函数退出前调用,示例如下:

func ReadWrite() bool {
    file.open("file")
    defer file.Close()
    //do something
    if(failureX){
        return false
    }
    if(failureY){
        return false
    }
    return ture
}

下面这段代码的输出结果:

for i:= 0; i < 5 ; i++ {
    defer fmt.Printf("%d",i)
}

 

8.6系统常用函数

//Golang常用系统函数演示
package main
​
import 
(
    "fmt"
    "strconv"
    "strings"
)
func main() {
    //字符串长度函数 len(),中文汉字在Golang中占3个字节
    var str string = "Golang 你好"
    fmt.Println(len(str))
    fmt.Println("------------------------------------------")
    //遍历字符串 for …… range
    for i, value := range str {
        fmt.Printf("索引:%d,值:%c \n", i, value)
    }
    fmt.Println("------------------------------------------")
    //遍历字符串,切片方式
    r := []rune(str)
    for i := 0; i < len(r); i++ {
        fmt.Printf("索引:%d,值:%c \n",i, r[i])
    }
​
    fmt.Println("------------------------------------------")
    //字符串与整数的相互转换
    //①字符串转整形
    num,err := strconv.Atoi("666")
    if err != nil{
        fmt.Println("发生错误:",err)
    }
        fmt.Println("num=",num)
        fmt.Println("------------------------------------------")
    //整形转字符串
    strCov := strconv.Itoa(1234)
    fmt.Println("strCov=",strCov)
    fmt.Println("------------------------------------------")
    //一个字符串中有几个指定的字符串
    countStr := "java"
    subStr := "a"
    count := strings.Count(countStr,"a")
    fmt.Printf("字符串%v中包含%v个字符串%v\n",countStr,count,subStr)
    fmt.Println("------------------------------------------")
    //不区分大小写的字符串比较
    flag := strings.EqualFold("hello","HELLO")
    fmt.Println("flag=",flag)
    fmt.Println("------------------------------------------")
    //区分大小写的字符串比较
    fmt.Println("hello"=="HELLO")
    fmt.Println("------------------------------------------")
    //返回字符串中某个字符串出现的第一个索引位置
    fmt.Println(strings.Index("java","a"))
    fmt.Println("------------------------------------------")
    //字符串替换:func Replace(s, old, new string, n int) string 
    //返回将s中前n个不重叠old子串都替换为new的新字符串,如果n<0会替换所有old子串。
    fmt.Println(strings.Replace("20220412","2","1",-1))
    fmt.Println("------------------------------------------")
    //字符串分割
    strs := strings.Split("go-python-java-c-c++-curl","-")
    fmt.Println(strs)
    fmt.Println("------------------------------------------")
    //大小写转换
    fmt.Println(strings.ToUpper("hello"))
    fmt.Println(strings.ToLower("OK"))
    fmt.Println("------------------------------------------")
    //去除字符串两边空格
    var trimSpaceStr = "  aaaa  "
    fmt.Printf("去空格之前%v,去空格之后%v\n",trimSpaceStr,strings.TrimSpace(trimSpaceStr))
    fmt.Println("------------------------------------------")
    //去除字符串两边指定的字符
    var trimStr = "~aa~aa~"
    fmt.Printf("去除~之前%v,去除~之后%v\n",trimStr,strings.Trim(trimStr,"~"))
    fmt.Println("------------------------------------------")
    //去除字符串左边的字符
    var trimLeftStr = "~aaaa~~"
    fmt.Printf("去除左边~之前%v,去除左边~之后%v\n",trimLeftStr,strings.TrimLeft(trimLeftStr,"~"))
    //去除字符串右边的字符
    var trimRightStr = "~aaaa~~"
    fmt.Printf("去除右边~之前%v,去除右边~之后%v\n",trimRightStr,strings.TrimRight(trimRightStr,"~"))
    fmt.Println("------------------------------------------")
    //判断字符串是否以指定的字符串开头
    var prefixStr = "http://localhost:8080"
    var preStr = "http"
    fmt.Printf("原始字符串:%v,起始字符串:%v,是否开始字符串:%v\n",prefixStr,preStr,strings.HasPrefix(prefixStr,preStr))
    fmt.Println("------------------------------------------")
    //判断字符串是否
    var suffixStr = "你好中国"
    var sufStr = "中国"
    fmt.Printf("原始字符串%v,结束字符串:%v,是否结束字符串:%v\n",suffixStr,sufStr,strings.HasSuffix(suffixStr,sufStr))
}

执行结果:

8.7 时间和日期函数

package main

import (
	"fmt"
	"time"
)

//时间日期函数
func main() {
	fmt.Println("----------------")
	now := time.Now()
	//当前时间
	fmt.Printf("时间:%v,类型:%T\n", now, now)
	fmt.Printf("年:%v\n", now.Year())
	fmt.Printf("月:%v\n", now.Month())
	fmt.Printf("月:%v\n", int(now.Month()))
	fmt.Printf("日:%v\n", now.Day())
	fmt.Printf("时:%v\n", now.Hour())
	fmt.Printf("分:%v\n", now.Minute())
	fmt.Printf("秒:%v\n", now.Second())
	fmt.Println("-------------------------------")
	//时间格式化
	//Printf 输出字符串
	fmt.Printf("当前时间:%d-%d-%d %d:%d:%d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
	//Sprintf 输出并返回字符串
	dateStr := fmt.Sprintf("当前时间:%d-%d-%d %d:%d:%d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
	fmt.Println(dateStr)
	fmt.Println("---------------------------")
	//按照指定格式格式化 格式(连接符)可以变化,但是年份时间字符串必须是Go语言诞生的时间 2006年01月02日 15点04分05秒
	fmt.Println(now.Format("2006-01-02 15:04:05"))
}

 

执行结果:

8.8利用defer+recover机制捕获异常

Golang 没有结构化异常,使用panic抛出异常,recover捕获异常。

异常的使用场景简单描述:Go中可以抛出一个panic异常,然后再defer中通过recover捕获这个异常,然后做正常处理

场景演示:

//利用defer+recover机制捕获异常
package main

import "fmt"

func main() {

	var a int = 2
	var b int = 0
	//由于除数为0 ,报错终止
	res := divisionIntRevocer(a, b)
	fmt.Println("res=", res)
	//下面的语句无法正常运行
	var a1 int = 10
	var b1 int = 5
	res1 := divisionIntRevocer(a1, b1)
	fmt.Println("res1=", res1)
}

func divisionIntRevocer(a int, b int) int {
	// 不做异常处理,程序会报0除异常,并终止
	return a / b
}

以上没有做异常捕获的代码执行结果为:

代码抛出了 0除 异常后,就被终止了,下面我们加上defer+recover 机制进行异常捕获

//defer + recover 异常捕获机制
package main

import "fmt"

func main() {
	var a int = 10
	var b int = 0

	res := devisionIntRecover(a, b)
	fmt.Println("res=", res)

	var a1 int = 10
	var b1 int = 5
	res1 := devisionIntRecover(a1, b1)
	fmt.Println("res1=", res1)
}

func devisionIntRecover(a int, b int) (ret int) {

	defer func() {
		err := recover()
		if err != nil {
			//打印异常,并关闭资源
			fmt.Println("err信息", err)
			ret = -3
		}
	}()
	ret = a / b
	return ret
}

执行结果如下:

8.9 自定义异常

 

9 GO Web编程

Gin框架,参考 https://www.topgoer.com/ gin 框架模块

9.1 基础代码

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
	"strings"
	"fmt"
)

func main() {

	
	r := gin.Default()
	r.GET("/ping",func(c *gin.Context){
		c.JSON(http.StatusOK,gin.H{
			"message": "Pong",
		})
	})

	//API参数通过Context的Param方法获取API参数
	r.GET("/user/:name/*action",func(c *gin.Context){
		name := c.Param("name")
		action := c.Param("action")
		//截取
		action = strings.Trim(action, "/")
		c.String(http.StatusOK,name+" is "+action)
	})
	// URL参数
	r.GET("/super",func(c *gin.Context){
		// name := c.Param("name")
		//指定默认值
		name := c.DefaultQuery("name","小老虎")
		c.String(http.StatusOK,fmt.Sprintf("hello %s",name))
	})
	//表单参数
	r.POST("/form",func(c *gin.Context){
		types := c.DefaultPostForm("types","post")
		userName := c.PostForm("username")
		password := c.PostForm("userpassword")
		c.String(http.StatusOK,fmt.Sprintf("userName:%s,password:%s,types:%s",userName,password,types))
	})
	//上传单个文件
	//1、multipart/form-data 格式用于文件上传
	//2、gin文件上传与原生的net/http方法类似。不同在于gin把原生的request封装到c.Request中
	//限制上传最大尺寸
	r.MaxMultipartMemory = 8 << 20
	r.POST("/upload",func(c *gin.Context){
		file,err := c.FormFile("file")
		if err != nil {
			c.String(500,"上传图片出错")
		}
		c.SaveUploadedFile(file,file.Filename)
		c.String(http.StatusOK,file.Filename)
	})

	//多文件上传
	//限制表单大小8M,默认32M
	r.MaxMultipartMemory = 8 << 20
	r.POST("/upload2File",func(c *gin.Context){
		form,err:= c.MultipartForm()
		if err != nil {
			c.String(http.StatusBadRequest,fmt.Sprintf("get err %s",err.Error()))
		}

		//获取所有文件
		files := form.File["files"]
		//遍历所有文件
		for _,file := range files {
		//逐个保存
			if err := c.SaveUploadedFile(file,file.Filename); err != nil {
				c.String(http.StatusBadRequest,fmt.Sprintf("upload err %s",err.Error()))
				return
			}
		}
		c.String(http.StatusOK,fmt.Sprintf("upload completed %d files ",len(files)))
	})


	//routes group  是为了管理一些相同的URL 等同于 JAVA Controller类级别的 @RequestMapping()
	//路由1 处理GET请求
	v1 := r.Group("/v1")
	//此处的{}是书写规范
	{
		v1.GET("/login",login)
		v1.GET("/submit",submit)
	}
	//路由2 处理POST请求
	v2 := r.Group("/v2")
	{
		v2.POST("/login",login)
		v2.POST("/submit",submit)
	}
	r.Run(":9090")
}

func login(c *gin.Context){
	name := c.DefaultQuery("name","小老虎")
	c.String(http.StatusOK,"hello %s \n",name)
}

func submit(c *gin.Context){
	name := c.DefaultQuery("name","小老虎")
	c.String(http.StatusOK,"hello %s \n",name)
}

9.2 路由拆分与注册

以上基础代码中的gin路由注册方式,适用于路由条目比较少的简单项目或者项目demo,当项目的规模增大后就不太适合继续在项目的main.go 文件中取实现路由注册相关逻辑,我们会倾向于把路由部分的代码拆分出来,形成一个单独的文件或者包:

 
posted on 2023-06-21 10:45  Internet_worm  阅读(10)  评论(0)    收藏  举报