Go

Go语言

Go语言介绍

​ 全称Golang,是Google公司2009年11月对外公布的一门编程语言
静态强类型(静态:类型固定 强类型:不同类型不允许直接运算)
属于 编译型语言 源代码编译成机器语言,由机器直接执行机器码即可执行。

python是解释型语言,想要编译成可执行文件需要借助于 pipinstaller

https://zhuanlan.zhihu.com/p/430490285

python代码防泄漏方案

1.启动起来后删除源代码
2.使用pipinstaller,打包成可执行文件
3. 做到docker镜像中,运行容器需要进行授权码校验。即-e PASSWORD

Go语言特性

​ 1. 跨平台的编译型语言

​ 2. 语法接近c语言

​ 3. 管道(channel),切片(slice),并发(routine)

​ 4. 支持面向对象和面向过程编程模式

Go语言发展

2015年8月19日 go1.5 —— 实现的架构变化,同时保留了和旧版本的兼容性,本次更新中移除了”最后残余的C代码”---》以后再写go不用c写了,用go写---》语言的自举
2018年8月24日 go1.11 —— modules和WebAssembly支持  go mod模式  包管理
最新 1.20版

Go语言应用程序

​ 像k8s,docker,七牛都是Go语言编写的

环境搭建

1.下载go的sdk(开发环境)

https://golang.google.cn/dl/
    下载msi文件

2.下载编辑器 goland ,vscode

https://www.jetbrains.com.cn/go/download/other.html

先安装环境在安装编辑器,一路下一步

hello world

package main // package 声明 这个go文件属于哪个包
import (
	"fmt" // fmt.Println 会自动导入这个 goland做的
	"time"
)

func main() {
	fmt.Println("hello world")
	time.Sleep(10 * time.Second)
}

// 编译代码成可执行文件,在哪个平台就默认编译成当前平台的

// 命令行执行go build 文件名.go 就可以生成可执行文件

变量名命名规范

​ Go语言中的函数名、变量名、常量名、类型名和包名等所有的命名,都遵循一个简单的命名规则

  1. 一个名字必须用字母开头或下划线开头,后面可以跟着任意数量的数字字母
  2. 大写字母与小写字母有不同的含义。
  3. 关键字和保留字不能作为变量名

25个关键字

break      default       func     interface   select
case       defer         go       map         struct
chan       else          goto     package     switch
const      fallthrough   if       range       type
continue   for           import   return      var

37个保留字

内置常量: true false iota nil
内置类型:  int int8 int16 int32 int64
          uint uint8 uint16 uint32 uint64 uintptr
          float32 float64 complex128 complex64
          bool byte rune string error
内置函数: make len cap new append copy close delete
          complex real imag
          panic recover

建议定义变量,函数名使用:用驼峰

go文件命名:建议用下划线

变量的定义和使用

package main

import "fmt"

// 变量定义
func main() {
	// 1.完整定义var关键字 变量名 变量类型 = 变量值
	//var name string = "lxj" // string  必须用双引号
	//var age int = 19        // 数字

	// 2. 类型推导,根据值自动推导出变量类型
	//var name = "lxj"
	//name = "pyy"      // 只能更改成同一类型

	// 3.简略声明 必须使用:= 不能分家
	//name := "lqz"

	// 4.一次性声明多个变量
	// 完整定义 1
	//var name, age, hobby string = "坤", "21", "唱跳rap打篮球"  // 问题是只能指定一个类型
	// 完整定义多个变量 2
	//var (
	//	name  string = "爱坤"
	//	age   int    = 19
	//	hobby string = "唱跳Rap打篮球"
	//)

	// 5.类型推导 1
	//var name, age, hobby = "坤", 19, "唱跳rap打篮球"
	// 方法  2
	//var (
	//	name  = "爱坤"
	//	age   = 19
	//	hobby = "唱跳Rap打篮球"
	//)

	// 6.简略声明
	//name, age, hobby := "坤", 19, "唱跳rap打篮球"

	// 7.完整定义和类型推导混合使用
	//var (
	//	name      = "坤"
	//	age   int = 19
	//	hobby     = "唱跳rap打篮球"
	//)

	// 8 变量名不能重复定义
	//var name = "坤"
	//var name = "lxj"  // 报错 重复定义
	//name := "lxj"     // 报错没有新变量在左侧

	// 9 变量可以先定义在赋值,必须使用完整定义反射
	//var name string // 默认值空字符串
	//var age int     // 默认值0
	//
	//age = 19               // 可以后面在赋值

	//fmt.Println(name, age) // 定义了变量必须用,不用会报错
	// 10,查看变量类型
	a := 19
	fmt.Printf("a的值是:%v,类型是:%T", a, a) // printf可以打印出变量的值和类型

}

变量类型

数字

# 整数有正负
int8    int16   int32	 int64
"rune 是 int32的别名"

# 正整数
uint8   uint16  uint(32位系统是32,64位系统是64)
"byte 是uint8的别名"

# 浮点型
float32   float64  # 小数后面范围不一样

# 复数类型
complex64  complex128
  • int8:-128 ~ 127
  • int16:-32768 ~ 32767
  • int32:-2147483648 ~ 2147483647
  • int64:-9223372036854775808 ~ 9223372036854775807

字符

# string 
双引号包裹不能换行, 反引号可以回车换行
"go 用单引号包裹字符,会返回对应的ASCII码"

布尔类型

true 
false

常量

定义常量

func main() {
	//1 .定义常量
	const name string = "lxj"
	const username = "nxm"
	println(name, username)
}

常量与变量的作用范围

在内部可以重新定义变量的

package main
var name string = "lxj"
func main() {
	// 内部没有用外部的
	//name := "pyy"   // 内部有优先用内部的
	println(name)
}

内置常量iota

想要使用必须用const 关键字,他的作用是自增

func main() {
	const (
		a = iota // 0
		b        // 1
		c        // 2
		d        // 3
	)
	println(a, b, c, d)
}

===================================================================
func main() {
	const (
		a = 10   // 10
		b = iota // 1   iota在哪一行 就从那一行开始自增
		c        // 2
		d        // 3
	)
	println(a, b, c, d)
}

iota 后续的行都会自增,不需要在+iota

函数

基础用法

普通函数
func main() {
	test()

}
func test() {
	fmt.Println("我是普通函数")
}

有参函数

func main() {
	test(1, "1")

}
// 必须指定参数的类型
func test(a int, b string) {
	fmt.Println(a, b)
}
===================================================================
func main() {
	test(1, 10, "ss")

}
// 多个参数相同类型可以简写
func test(a, c int, b string) {
	fmt.Println(a, b, c)
}

有返回值的有参函数
func main() {
	res := test(1, 10)
	fmt.Println(res)
}
// 需要在test(a, b int) 后面指定返回值的类型
func test(a, b int) int {
	return a + b
}
===================================================================
// 多个参数多个返回值
func main() {
	res, res1 := test(1, 10, "成功")
	fmt.Println(res, res1)
}

func test(a, b int, c string) (int, string) {
	return a + b, c
}

高级用法

同一个包下的变量函数只能定义一次,用了同样的名字会报错

匿名函数,一般定义在其他函数的内部
package main

import "fmt"

func main() {
	test()
}
func test() {
	func() {
		fmt.Println("我是内部的匿名函数")
	}() // 需要加括号调用,不然报错
}
定义变量接收函数,匿名函数带参数
package main

import "fmt"

func main() {
	test1()
}
func test() {
	//f := func() {
	//	fmt.Println("我是内部的匿名函数")
	//} // 报错需要指定f的类型
	var f func() = func() {
		fmt.Println("我是内部的匿名函数")
	}
	fmt.Printf("%T", f) // 打印出来是func() 类型,可以自定义
}

func test1() {
	var f func() = func() {
		fmt.Println("内层函数")
	}
	// func(a int) 也可以不写 函数的参数和返回值都是类型的一部分(如果写类型也要写参数)
	var f1 func(a int) = func(a int) {
		fmt.Println(a)
	}
	var f2 func(b int) int = func(b int) int {
		fmt.Println(b)
		return b + 10
	}

	var f3 func(b int) string = func(b int) string {
		fmt.Println(b)
		return "我是你爹"
	}
	fmt.Printf("%T\n", f)  // 打印出来是func()
	fmt.Printf("%T\n", f1) // 打印出来是func(int)
	fmt.Printf("%T\n", f2) // 打印出来是func(int) int
	fmt.Printf("%T\n", f3) // 打印出来是func(int) string
	f3(1)

}

可以简写:
f3  := func(b int) string {
		fmt.Println(b)
		return "我是你爹"
	}
函数作为返回值
func main() {
	res := test2()
	fmt.Printf("%T", res)
}

func test2() func() {
	return func() {
		fmt.Println("我是内层函数")
	}
}
调用函数返回闭包函数 (闭包函数内用到了外部作用域的变量)
func main() {
	res := test2()
	res()  // 取决于闭包函数有没有参数,有就要传
	fmt.Printf("%T", res)
}

func test2() func() {
	a := 10
	f := func() {
		fmt.Println(a)
	}
	return f
}
复杂用法
package main

import "fmt"

func main() {
	f := test(func() {
		fmt.Println("我是传给c的函数")
	})   // 传给了test参数c一个函数,并返回了一个func(int, int) int  函数
	d := f(1, 2)  // f 调用了返回的func(int, int) int 函数  返回值给了d
	fmt.Println(d)  // 3
}

// test函数接收了一个c参数 这个参数是函数类型,没有参数也没有返回值
// test 有返回值,返回了一个函数,函数有两个参数,和一个返回值,上面就要指定func(int, int) int
func test(c func()) func(int, int) int {
	c()
	return func(a, b int) int {
		return a + b
	}
}

类型重命名

type MyFunc = func(int, int) int这种写法是重命名了

type Myint int 这种写法是重新定义了

package main

import "fmt"

// 类型重命名
type MyFunc = func(int, int) int

func main() {
	//// 完整定义  写起来太麻烦
	//var f func(int, int) int = test(func() {
	//	fmt.Println("传给了c")})
	//  类型重命名
	var f MyFunc = test(func() {
		fmt.Println("传给了c")
	})
	// 传给了test参数c一个函数,并返回了一个func(int, int) int  函数
	d := f(1, 2)   // f 调用了返回的func(int, int) int 函数  返回值给了d
	fmt.Println(d) // 3
}

// test函数接收了一个c参数 这个参数是函数类型,没有参数也没有返回值
// test 有返回值,返回了一个函数,函数有两个参数,和一个返回值,上面就要指定func(int, int) int
func test(c func()) func(int, int) int {
	c()
	return func(a, b int) int {
		return a + b
	}
}

可变长参数
// 可变长参数
func test(a ...int) {
	fmt.Println(a, "\n") //[1 2 3 4 5 6]  切片类型
	fmt.Printf("%T", a)  // []int  int类型切片
}

func main() {
	test(1, 2, 3, 4, 5, 6)
}

defer 关键字延迟调用与执行顺序
func main() {
	// 1.普通用法
	func() {
		fmt.Println("我他妈是个帅哥")
	}()
	defer fmt.Println("呃") // 延迟调用,main函数代码执行完了才会走这个
	fmt.Println("zyg是狗屎")
	
	// 闭包函数
	var a = 10
	//defer func() {
	//	fmt.Println(a) // 99 整个代码走完了才会走。因为下面给a重新赋值了
	//	fmt.Println("我他妈是个帅哥")
	//}()
	defer func(i int) {
		fmt.Println(i) // 10 因为走到这个函数的时候(a)就已经把a传进来了
		fmt.Println("我他妈是个帅哥")
	}(a)
	a = 99
	fmt.Println("我觉得你很傻")

	// 多个defer执行的顺序 ,
	// 由此得知当前函数代码执行完毕会按照defer从下往上的顺序执行
	defer fmt.Println("我zyg挺帅的")  // 4
	defer fmt.Println("我李晓健更帅") // 执行顺序3
	fmt.Println("zyg就是坨屎")     // 执行顺序1
	fmt.Println("我同意李晓健帅")   // 执行顺序2
	
}

包的使用

python中模块是一个py文件,包是一个文件夹里面有__init__

go 的包是在一个文件下, 这个文件夹下所有的go文件第一行要声明包

go中也有sdk内置包,自定义包,第三方包

注意事项:

1. 在包里的go文件的参数想要在外部使用,需要`大写字母`开头声明。
2. 同一个包下无论是函数,变量的名字都只能定义一次
  1. 在同一个包下使用函数 不需要导入直接使用不区分大小写

  2. 包的package 声明的名字可以和包名不一样,必须要重命名import 自定义名 "awesomeProject/包名"

  3. 包内的init函数,可以写多个,会依次执行。如果包内的多个go文件有多个init按照从上往下的顺序执行

  4. 包下还可以建别的(文件夹)包,直接导入就可以使用且可以和外部的名字重复。

bao
  b1.go
  b2.go    // 不能用b1里出现过的名字
  bao2
    c1.go   // 可以用b1或b2的名字,也可以导入bao用 b1或b2里的函数
    c2.go

b1.go 包

package qqq // 修改包名
// package bao
import "fmt"

// 在go中 大写字母开头表示导出,如果小写在外部用不了
func Add(a, b int) int {
	return a + b
}

func Test() {
	// 同一个包下的函数可以直接调用
	res := Add(1, 2)
	fmt.Println(res)
}

执行文件

我们在导入包以后必须要使用不然会报错,如果你是个犟种那么可以使用impory _ "awesomeProject/bao" 导入

package main
// 正常情况下
//import (
//	"awesomeProject/bao"
//	"fmt"
//)
//
//func main() {
//	res1 := bao.Add(5, 67)
//	fmt.Println(res1)
//	bao.Test()
//}


import (
	abc "awesomeProject/bao" // 包里的package 修改名字后必须要重命名
	"fmt"
)

func main() {
	res1 := abc.Add(2, 3)
	fmt.Println(res1)

	abc.Test()
}

第三方包的使用

gin包 web服务

使用gin包开一个web服务监听本地端口,从而可以从浏览器访问

安装

go get  github.com/gin-gonic/gin
// 局部修改 当前项目
打开编辑器的File 》 settings 》 Go 》Go Moduiles 》 在Environment里输入
GOPROXY=https://goproxy.cn,direct
// 全局修改
go env -w GOPROXY=https://goproxy.cn,direct

改完之后重启一下goland

使用

package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()            // 做路由
	r.LoadHTMLGlob("templates/*") // 加载整个文件夹
	r.GET("/lxj", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "李晓健是帅哥",
		}) // 向/lxj 发送get请求就会执行func这个视图函数

	})
	r.GET("/index", func(c *gin.Context) {
		c.HTML(200, "index.html", gin.H{"name": "李晓健", "age": 18}) // gin.H 填写需要渲染的东西
	})
	//r.Run()   // 可以传参数  监听0.0.0.0:8080
	r.Run(":8001") // 可以传参数  监听0.0.0.0:8001
}

if-else

go 的流程控制

package main

import "fmt"

func main() {
	score := 44
	if score >= 90 {
		fmt.Println("成绩好啊")
	} else if score < 90 && score > 80 { // go中的and 用 &&    or 用 ||
		fmt.Println("下次给我加把劲")
	} else if score < 80 && score > 60 {
		fmt.Println("你怎么考的")
	} else {
		fmt.Println("你真该死啊")
	}
}

循环

go中只有for循环,包含了while功能

package main

import "fmt"

func main() {
	// 1.基本语法
	//for i := 0; i < 10; i++ {
	//	fmt.Println(i)
	//} // i的作用范围只在for的内部作用域有效

	// 省略掉第一部分 ;不可以省
	//i := 0
	//for ; i < 10; i++ {
	//	fmt.Println(i)
	//}

	// 省略第一和第三部分
	//i := 0
	//for i < 10 {
	//	fmt.Println(i)
	//	i++
	//}

	//  实现while 死循环
	//for true {
	//	fmt.Println("死循环了")
	//}

	// 基于索引循环
	a := "lxj李晓健" // range是按字符的,中文也可以1个中文占3个字节,也就是3个索引
	for i, v := range a {
		fmt.Println(i) // 拿的是索引
		fmt.Println(v) // 拿的是字母对应的ascii码
	}
	// break 与continue
	for i := 0; i < 10; i++ {
		if i == 3 {
			continue // 遇到3 返回开始下次一循环
		}
		if i > 5 {
			break //遇到大于5直接结束循环
		}
		fmt.Println(i)
	}
}

switch

是一个条件语句,用来替代if else 优雅的替换掉

有个关键字fallthrough 条件成立也会无条件走下一个

package main

import "fmt"

func main() {

	//score := 92
	// 有表达式
	//switch score {
	//case 90, 92, 93: // 多条件
	//	fmt.Println("90")
	//case 80:
	//	fmt.Println("80")
	//case 70:
	//	fmt.Println("70")
	//
	////不符合上述条件走default
	//default:
	//	fmt.Println("不知道")
	//}
	//score := 44
	//// 无表达式
	//switch {
	//case score >= 90:
	//	fmt.Println("考的好")
	//case score > 80 && score < 90:
	//	fmt.Println("还行")
	//case score < 80 && score > 60:
	//	fmt.Println("没考好啊")
	//default:
	//	fmt.Println("考的啥啊")
	//}
	//

	// fallthrough  默认·情况下每个条件执行完会默认加break,其他语言必须要加,不加就会无条件执行下一个,Fallthrough就可以在go里无条件执行下一个
	score := 92 // 就算符合条件也会一直走到最后一个
	switch {
	case score >= 90:
		fmt.Println("考的好")
		fallthrough
	case score > 80 && score < 90:
		fmt.Println("还行")
		fallthrough
	case score < 80 && score > 60:
		fmt.Println("没考好啊")
		fallthrough
	default:
		fmt.Println("考的啥啊")
	}

}

数组

​ 数组 只能存放同一类型元素的集合,形参一个数组,不能存放不同类型的元素。

[3]int 长度为3的int数组

数组是在内存中申请一个连在一起的多个空间,每个空间里只能存放相同类型元素。

而python中的列表是存放的内存地址,每个地址又指向数据,可以是不同的数据类型。

定义数组

package main

import "fmt"

func main() {
	var a [3]int   // 定义一个长度为3的int数组
	fmt.Println(a) // [0 0 0] 数字类型没有值的情况下默认为0
	// 使用数组的方法
	a[0] = 1
	a[1] = 3
	fmt.Println(a) // [1 3 0]  如果是int8 类型不能存放超过int8范围的数字,其他类型也是同样情况、

	// 定义数组并初始化
	var b [4]int = [4]int{1, 2, 3, 4} // [4]int{1,2,3,4} 初始化设置必须也要指定类型
	c := [5]int{1, 2, 3, 4, 5}
	fmt.Println(b, c)
}

定义后长度不可以增加。

posted @ 2023-04-25 16:04  李阿鸡  阅读(21)  评论(0编辑  收藏  举报
Title