代码示例:
import _ "github.com/go-sql-driver/mysql"
// _操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的 init 函数。
-----------------------------------------------------------------------------
package main //声明文件所在的包,每个go文件必须有归属的包
import "fmt" //引入程序中需要的包,为了使用包下的函数,比如:Println
func main() { //main 主函数,程序入口
fmt.Println("Hello Golang!") //在控制台打印输出一句话,双引号中的内容会原样输出
}
参数解析两种:
name := flag.String("name","world","specify the name")flag //解析参数如go run main.go --name jason, 这样就解析了--name
flag.Parse()
fmt.Println("os args is:", os.Args) //打印程序名和参数
自定义报错:
fmt.Errorf("aaa is not allowed") //抛出一个报错
编译执行:
go build test.go
./test.exe
go run test.go #编译和运行
定义变量和使用:
package main
import "fmt"
//全局变量:定义在函数外的变量
// var n7 = 100
// var n8 = 9.7
//或
var (
n7 = 100
n8 = 9.7
)
func main() {
//定义在{}中的变量叫:局部变量
// 第一种: 变量的使用方式
var num int = 18
fmt.Println(num)
//第二种: 指定变量类型,但是不赋值,使用默认的变量值
var num2 int
fmt.Println(num2)
//第三种: 如果没有协变量的类型,那么根据=后面的值进行判定变量的类型(自动类型推断)
var num3 = "tom"
fmt.Println(num3)
//第四种:省略var, 注意:=不能写为=
sex := "男"
fmt.Println(sex)
fmt.Println("-------------------------------")
//声明多个变量,短变量声明:
n1, n2, n3 := 1, 2, 3
fmt.Println(n1, n2, n3)
fmt.Println(n7)
}
函数外的每个语句都必须以关键字开始(所以短变量不能在函数体外声明,函数体外部声明需要用var)
数据类型:
基本数据类型:
- 数值型
- 整数类型(int) #int8占用存储空间1字节(1字节=8位),默认为0
- 浮点类型(float) #默认为0
- 字符型(没有单独的字符型,使用byte来保存单个字母字符)
- 布尔型(bool) #默认是false
- 字符串(string) #默认为空
基本数据类型间的转换(显示转换):
基本类型转string类型
方式1:fmt.Sprintf("%参数",表达式) ---》 重点练习这个,推荐方式
package main
import "fmt"
func main(){
var n1 int = 19
var n2 float32 = 4.78
var n3 bool = false
var n4 byte = 'a'
var s1 string = fmt.Sprintf("%d",n1)
fmt.Printf("s1对应的类型是:%T ,s1 = %q \n",s1, s1)
var s2 string = fmt.Sprintf("%f",n2)
fmt.Printf("s2对应的类型是:%T ,s2 = %q \n",s2, s2)
var s3 string = fmt.Sprintf("%t",n3)
fmt.Printf("s3对应的类型是:%T ,s3 = %q \n",s3, s3)
var s4 string = fmt.Sprintf("%c",n4)
fmt.Printf("s4对应的类型是:%T ,s4 = %q \n",s4, s4)
//变量定义后不能再定义一个同样的变量,只能修改
i := 0
i = 1 //这里是修改,所以不能加冒号再次定义
fmt.Println(i)
}
方式2:使用strconv包的函数
复杂数据类型:
- 指针(创造出来的目的是节省内存的)
打印空间地址:
var age int 18
fmt.Println(&age) #获取age的地址
var ptr *int = &age #定义一个指针
fmt.Println(&ptr) #获取ptr指针的地址
fmt.Println(*ptr) #获取ptr这个指针指向的数据
或者:
v4 := new(int) //定义指针,new是用于创建内存并进行内部数据的初始化,并返回一个指针类型。并且给一个默认值为0, 先开一个内存空间给一个内存地址,然后指向值为0
var v3 *int //和上面同样,如果没给值的话,指向的值为nil值
nil //nil指go语言中的空值,比如var v100 *int和var v101 *int6 默认都指向nil
数组
结构体
管道
函数
切片
接口
map
命名规范
变量名、函数名、常量名 : 采用驼峰法
如果变量名、函数名、常量名首字母大写,则可以被其他的包访问
如果首字母小写,则只能在本包中使用
运算符
算数运算: +, -, *, /, %
赋值运算符: =, +=, -=, *=, /=, %=
逻辑运算符: ==, !=, >, <, >=, <=
位运算符: &, |, ^
其他运算符: &, *
获取用户终端输入
scanln
scanf
if
在golang里,if后面可以并列的加入变量的定义
if count := 20;count < 30 {
fmt.Println("对不起,口罩存量不足")
}
多分支
方式一:
var count int = 111
if count < 30 {
fmt.Println("对不起,口罩存量不足")
} else if 30 < count && count < 50 {
fmt.Println("口罩存量充足")
} else {
fmt.Println("口罩存量很多")
}
方式二:
if count >= 50 {
fmt.Println("口罩存量很多")
} else if count >=30 { // count <= 50 && count >=30,因为包含了else
fmt.Println("口罩存量充足")
} else {
fmt.Println("对不起,口罩存量不足")
} //建议保证else存在,只有有了else才会真正的起到多选一的效果
Switch分支
一般来实现多分支
func main() {
var score int = 87
switch score / 10 { //switch后面跟表达式,表达式的结果依次跟case进行比较,放变量、常量、函数都可以
case 10: //case后面可以写多个值,使用逗号分隔,如case 10,11,12:
fmt.Println("您的等级为A级")
case 9:
fmt.Println("您的等级为A级")
case 8:
fmt.Println("您的等级为B级")
case 7:
fmt.Println("您的等级为C级")
case 10:
fmt.Println("您的等级为D级")
case 6:
fmt.Println("您的等级为E级")
case 5:
fmt.Println("您的等级为E级")
case 4:
fmt.Println("您的等级为E级")
case 3:
fmt.Println("您的等级为E级")
default: //default分支可以放到任意位置上
fmt.Println("您的等级有误")
}
}
for循环
package main
import "fmt"
func main() {
var sum int = 0
for i := 1; i <= 5; i++ {
fmt.Println(i, sum)
sum += i
}
}
灵活写法:
var i int = 1
for i <= 5 {
fmt.Println("nihao")
i++
}
死循环:
for {
fmt.Println("nihao")
}
for range
package main
import "fmt"
func main() {
var str string = "hello golang你好"
for i, value := range str {
fmt.Printf("索引为: %d, 具体的值为: %c\n", i, value)
}
}
break
package main
import "fmt"
func main() {
var sum int = 0
for i := 1; i <= 100; i++ {
sum += i
fmt.Println(sum)
if sum >= 300 {
break //停止并跳出当前循环
}
}
}
可以给每个循环加label,使用break label可以结束指定标签的循环:
package main
import "fmt"
func main() {
//双重循环:
label2:
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 == 2 {
break label2 //结束指定标签对应的循环
}
}
}
fmt.Println("-----ok")
}
continue
package main
import "fmt"
func main() {
for i := 1; i <= 100; i++ {
if i%6 != 0 {
continue //结束本次循环,继续下一次循环。
}
fmt.Println(i)
}
}
continue也可以跟label:
package main
import "fmt"
func main() {
//双重循环:
label:
for i := 1; i <= 5; i++ {
for j := 2; j <= 4; j++ {
if i == 2 && j == 2 {
continue label
}
fmt.Printf("i: %v, j: %v \n", i, j)
}
}
fmt.Println("-----ok")
}
return
结束当前的函数
函数
调用函数:
package main
import "fmt"
func cal(num1 int, num2 int) (int) { //(int) 表示指定返回值的类型,如果没有return,可以不写。如果返回值只有一个,那(int)的括号可以不写。如果返回值多个,需要写上,如(int, int)
return num1 + num2
}
func main() {
sum := cal(20, 20)
fmt.Println(sum)
}
函数可以返回两个值和多个值:
package main
import "fmt"
func cal(num1 int, num2 int) (int, int) {
var sum int = 0
sum += num1
sum += num2
var result int = num1 - num2
return sum, result
}
func main() {
sum, result := cal(20, 20) //或者只想接收sum,可以这样:sum, _ := cal(20, 20), _表示忽略
fmt.Println(sum, result)
}
可变参数(使用...)
package main
import "fmt"
func test(args ...int) {
for i := 0; i < len(args); i++ {
fmt.Println(args[i])
}
}
func main() {
test()
test(3)
test(1, 2, 3, 4, 5, 6, 7, 8)
}
改main函数里的变量值(用到指针,通过内存地址传递来改)
package main
import "fmt"
func test(num *int) {
*num = 30
}
func main() {
var num int = 10
fmt.Println(&num)
test(&num)
fmt.Println(num)
}
将函数赋给变量
type起别名
对函数返回值命名
package main
import "fmt"
func test(num1 int, num2 int) (sum int, sub int) {
sum = num1 + num2
sub = num1 - num2
return
}
func main() {
ss, bb := test(10, 20)
fmt.Println(ss, bb)
}
包的引入(引入的包里函数必须大写开头才能被引用)
main.go //导入utils.go中的函数
utils.go //专门定义一些工具函数,让其他文件来调用
db.go //专门定义对数据库操作的函数
包名是从$GOPATH/src/后开始计算的,使用/进行路径分隔
多个包导入:
import(
"fmt"
"gocode/testproject01/unit5/demo09/crm/dbutils"
)
函数名,变量名首字母大写,函数,变量可以被其它包访问
init函数(优先执行,用于一些初始化操作,只运行一次)
package main
import "fmt"
func init() {
fmt.Println("init函数被执行")
}
func main() {
fmt.Println("main函数被执行")
}
匿名函数(只调用一次,使用一次)
package main
import "fmt"
func main() {
result := func(num1 int, num2 int) int {
return num1 + num2
}(10, 20)
fmt.Println(result)
}
闭包:
- 也是一个匿名函数
- 不能独立存在
- 可以赋值给其他变量
x := func(){}
- 可以直接调用
func(x,y int){println(x+y)}(1,2)
- 可以作为函数返回值
func Add() (func(b int) int)
defer func(){
if r :=recover(); r != nil {
println("recovered in FuncX")
}
}
x := func(){} //一个函数的声明
系统函数(需要导包)
1.打印字符串长度len():
package main
import "fmt"
func main() {
str := "golang你好"
fmt.Println(len(str))
}
2.字符串遍历:
package main
import "fmt"
func main() {
str := "golang你好"
fmt.Println(len(str))
for i, value := range str {
fmt.Printf("索引为: %d, 具体值: %c\n", i, value)
}
}
或利用切片:
r := []rune(str)
for i := 0; i < len(r); i++ {
fmt.Println(r[i])
}
3.字符串转整数
num1, _ := strconv.Atoi("666")
fmt.Printf("%T", num1)
4.整数转字符串
num1 := strconv.Itoa(666)
fmt.Println(num1)
5.查找字符串是否在指定的字符串中
count := strings.Contains("javaandgolang", "goo")
fmt.Println(count)
6.统计一个字符串有几个指定的子串
count := strings.Count("javaandgolang", "a")
fmt.Println(count)
7.不区分大小写的字符串比较
count := strings.EqualFold("go", "Go")
fmt.Println(count)
8.区分大小写的字符串比较
fmt.Println("hello" == "Hello")
9.返回字符串在字符串第一次出现的索引值,如果没有返回-1
fmt.Println(strings.Index("golangandjavaga", "ga0"))
10.字符串的替换
strings.Replace("goandjavagogo", "go", "golang", n) //n可以指定你希望替换几个,如果n=-1表示全部替换,替换两个n就是2
str := strings.Replace("goandjavagogo", "go", "golang", 2)
fmt.Println(str)
11.按照指定的某个字符,为分割标识,将一个学符串拆分成字符串数组
strings.Split("go-python-java", "-")
str := strings.Split("go-python-java", "-")
fmt.Println(str)
12.将字符串的字母进行大小写的转换
str := strings.ToLower("Go")
str1 := strings.ToUpper("go")
fmt.Println(str, str1)
13.将字符串左右两边的空格去掉
str := strings.TrimSpace(" go and java ")
fmt.Println(str)
14.将字符串左右两边指定的字符去掉
strings.Trim("~golang~ ", " ~")
15.将字符串右边指定的字符去掉
strings.TrimRight("~golang~", "~")
16.将字符串左边指定的字符去掉
strings.TrimLeft("~golang~", "~")
17.判断字符串是否以指定的字符串开头
strings.HasPrefix("http://java.sun.com/jsp/jstl/fmt", "http")
18.判断字符串是否以指定的字符串结束
strings.HasSuffix("demo.png", ".png")
日期函数:
1.获取当前时间
now := time.Now()
fmt.Println(now)
2.当想获取指定的字段
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Printf("%v~~~~对应的类型为: %T\n", now, now)
// fmt.Println(now)
fmt.Printf("年: %v \n", now.Year())
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())
}
3.日期的格式化,将日期以年月日时分秒按照格式输出为字符串,如果是printf只是将字符串输出,而sprintf可以赋值给变量
datestr := fmt.Sprintf("当前年月日: %d-%d-%d 时分秒: %d:%d:%d", now.Year(), int(now.Month()), now.Day(), now.Hour(), now.Minute(), now.Second())
fmt.Println(datestr)
4.按照指定格式(里面的时间和日期不能修改)
datestr2 := now.Format("2006/01/02 15:04:05")
fmt.Println(datestr2)
datestr3 := now.Format("2006/01/02 15:04")
fmt.Println(datestr3)
内置函数(不需要导包,直接使用):
close //管道关闭
len, cap //返回数组、切片、Map的长度或容量
new, make //内存分配
copy, append //操作切片
panic, recover //错误处理
print, println //打印
complex, real, imag //操作复数
5.new函数,分配内存,主要用来分配值类型(int系列, float系列, bool, string、数组和结构体struct)
6.分配内存,主要用来分配引用类型(指针、slice切片、map、管道chan、interface 等)
回调函数(Callback,函数作为参数传入其他函数,并在其他函数内部调用执行)
错误处理
package main
import (
"fmt"
)
func main() {
test()
fmt.Println("上面的除法操作成功。。。")
fmt.Println("正常执行下面的逻辑。。。")
}
func test() {
defer func() { //匿名函数,捕获错误,程序不会轻易的挂掉,提高程序的健壮
err := recover()
if err != nil {
fmt.Println("错误已经捕获")
fmt.Println("err是:", err)
}
}()
num1 := 10
num2 := 0
result := num1 / num2
fmt.Println(result)
}
自定义错误:需要调用errors包下的New函数:函数返回error类型
package main
import (
"errors"
"fmt"
)
func main() {
err := test()
if err != nil {
fmt.Println("自定义错误:", err)
panic(err) //加入panic后,程序终止
}
fmt.Println("上面的除法操作成功。。。")
fmt.Println("正常执行下面的逻辑。。。")
}
func test() (err error) {
num1 := 10
num2 := 0
if num2 == 0 {
//抛出自定义错误
return errors.New("除数不能为0~~")
} else {
result := num1 / num2
fmt.Println(result)
return nil
}
}
数组
定义格式:var 数组名 [数组大小]数据类型
package main
import "fmt"
func main() {
var scores [5]int
scores[0] = 95
scores[1] = 91
scores[2] = 39
scores[3] = 60
scores[4] = 21
sum := 0
for i := 0; i < len(scores); i++ {
sum += scores[i]
}
fmt.Println(sum)
}
遍历数组一般用for range:
for stu, score := range scores {
fmt.Printf("第%d个学生的成绩为: %d\n", stu+1, score)
}
数组的初始化(必须定义长度):
package main
import "fmt"
func main(){
//第一种:
var arr1 [3]int = [3]int{3,6,9}
fmt.Println(arr1)
//第二种:
var arr2 = [3]int{1,4,7}
fmt.Println(arr2)
//第三种:
var arr3 = [...]int{4,5,6,7}
fmt.Println(arr3)
//第四种:
var arr4 = [...]int{2:66,0:33,1:99,3:88} //下标为2位置上的值是66
fmt.Println(arr4)
二维数组初始化:
var arr = [2][3]int{{1, 3, 4}, {2, 3, 5}}
fmt.Println(arr)
切片(构建在数组之上,用数组的情况少,切片的时候多)
package main
import "fmt"
func main() {
var intarr [6]int = [6]int{3, 6, 9, 1, 4, 7}
slice := intarr[1:3] //1-3,不包含3, 定义切片是:=, 定义数组是=
fullSlice := intarr[:] //不限下标,意思就是整个
fmt.Println(slice)
}
添加:
mySlice := []int{} //slice []不会定义长度
mySlice := myArrayp[1:3]
mySlice = append(mySlice, 1)
删除很麻烦,需要自定义一个删除函数:
func deleteItem(slice []int, index int) []int {
return append(slice[:index], slice[index+1:]...)
}
比如删除索引2的切片:
package main
import (
"fmt"
)
func main() {
myArray := [5]int{1, 2, 3, 4, 5}
mySlice := myArray[1:3]
fmt.Printf("mySlice %+v\n", mySlice)
fullSlice := myArray[:]
fmt.Println(fullSlice)
remove3rdItem := deleteItem(fullSlice, 2)
fmt.Printf("remove3rdItem %+v\n", remove3rdItem)
}
func deleteItem(slice []int, index int) []int {
return append(slice[:index], slice[index+1:]...)
}
构造切片: 可以使用New和Make
New:
make: 关注程序运行效率的话,需要定义好length(长度)和cap(容量),不然程序会自己去内存找
go语言在任何地方都是值传递,比如我要修改数组的元素,直接修改元素对原有的数组没影响,需要根据下标来修改
mySlice := []{10,20,30,40,50}
//这样相当于定义了一个value的临时变量来乘以2,对原有的元素不影响
for _,value := range mySlice {
value *= 2
}
fmt.Printf("mySlice %+v\n", mySlice) //%v只把值原样打印,%+v会把类型也打印出来,一般用来debug
//打印值:mySlice [10 20 30 40 50]
//Printf() 是把格式化字符串输出到标准到标准输出(一般是屏幕,可以重定向)
//Sprintf() 是把格式化字符串输出到指定的字符串中,可以用一个变量来接受,然后在打印
//这样才是影响原有的元素,通过下标的方式
for index,_ := range mySlice { //_可以不要,因为是取下标了
mySlice[index] *= 2
}
fmt.Printf("mySlice %+v\n", mySlice)
//打印值:mySlice [20 40 60 80 100]
map(键值对(key-value): 一堆匹配的信息),相当于字典,定义:make(map[type of key]type of value)
比如:学生学号 - 学生姓名
package main
import "fmt"
func main() {
// var a map[int]string
//myMap := make(map[string]string, 10)
var a = make(map[int]string, 10) //可以存10个键值对
a[20095452] = "张山"
a[20095387] = "李四"
a[20097291] = "王五"
fmt.Println(a)
}
使用前一定要make
key-value是无序的
定义方式:
package main
import "fmt"
func main(){
//方式1:
//定义map变量:
var a map[int]string
//只声明map内存是没有分配空间
//必须通过make函数进行初始化,才会分配空间:
a = make(map[int]string,10) //map可以存放10个键值对
//将键值对存入map中:
a[20095452] = "张三"
a[20095387] = "李四"
//输出集合
fmt.Println(a)
//方式2:
b := make(map[int]string)
b[20095452] = "张三"
b[20095387] = "李四"
fmt.Println(b)
//方式3:
c := map[int]string{
20095452 : "张三",
20098765 : "李四",
}
c[20095387] = "王五"
fmt.Println(c)
}
增和改:
没有就添加,有就修改
删:
delete(b,20095387) //b[20095387]
查:
package main
import "fmt"
func main() {
//定义map
b := make(map[int]string)
//增加:
b[20095452] = "张三"
b[20095387] = "李四"
//修改:
b[20095452] = "王五"
//删除:
// delete(b, 20095387)
delete(b, 20089546)
// fmt.Println(b)
//查找:
value, flag := b[20095387] //flag为bool型,如果value存在,则为true,反之为false
fmt.Println(value)
fmt.Println(flag)
if flag { //习惯性语法,kubernetes经常用到
Println(value)
}
}
获取长度:
fmt.Println(len(b))
遍历,只能for range:
a := make(map[string]map[int]string)
a["班级1"] = make(map[int]string, 3)
a["班级1"][20096677] = "zhangshan"
a["班级1"][20098833] = "lisi"
a["班级1"][20097722] = "ffei"
a["班级2"] = make(map[int]string, 3)
a["班级2"][20089911] = "xiaoming"
a["班级2"][20085533] = "xiaolong"
a["班级2"][20087244] = "xiaobei"
for k1, v1 := range a {
fmt.Println(k1)
for k2, v2 := range v1 {
fmt.Printf("学生学号: %v, 学生姓名: %v\n", k2, v2)
}
}
面向对象(类-结构体struct)
结构体 -> 类
结构体支持标签
type MyType struct {
Name string `json:"name"`
}
func main(){
mt := MyType{Name: "test"}
myType := reflect.TypeOf(mt)
name := myType.Field(0)
tag := name.Tag.Get("json")
println(tag)
}
创建方式一:
package main
import "fmt"
type Teacher struct { //定义结构体(类)
Name string
//Age int
School string
//或者这样定义: Name, School string
}
type IF interface { //interface是一组函数的抽象,只能定义行为
getName() string
}
func main() {
var t1 Teacher //将Teacher赋给t1
fmt.Println(t1)
t1.Name = "mashibing"
t1.Age = 45
t1.School = "qinghua"
fmt.Println(t1)
fmt.Println(t1.Age + 10)
}
创建方式二:
package main
import "fmt"
type Teacher struct {
Name string
Age int
School string
}
func main() {
var t Teacher = Teacher{}
fmt.Println(t)
t.Name = "mashibing"
t.Age = 45
t.School = "qinghua"
fmt.Println(t)
}
创建方式三,指针:
package main
import "fmt"
type Teacher struct {
Name string
Age int
School string
}
func main() {
var t *Teacher = new(Teacher)
(*t).Name = "mashibing"
(*t).Age = 45
t.School = "qinghua" //也可以使用简化的方式,符合程序员的习惯
fmt.Println(*t)
}
方式四:
package main
import "fmt"
type Teacher struct {
Name string
Age int
School string
}
func main() {
var t *Teacher = &Teacher{}
(*t).Name = "mashibing"
t.Age = 45
t.School = "qinghua"
fmt.Println(*t)
}
方法:作用在接受者上的函数
func (recv receiver_type) methodName(parameter_list) (return_value_list)
package main
import "fmt"
//定义结构体
type Person struct {
Name string
}
//给Person结构体绑定方法test
func (p Person) test() {
fmt.Println(p.Name)
}
func main() {
var p Person //创建结构体对象
p.Name = "lili"
p.test()
}
工厂模式:解决跨包访问结构体小写问题
封装:
继承:
package main
import (
"fmt"
)
// 定义动物结构体
type Animal struct {
Age int
Weight float32
}
// 给Animal绑定方法:喊叫
func (an *Animal) Shout() {
fmt.Println("大声喊叫")
}
// 给Animal绑定方法:自我展示
func (an *Animal) ShowInfo() {
fmt.Printf("动物的年龄是:%v, 动物的体重是: %v", an.Age, an.Weight)
}
// ---------上面是一个父类,下面开始定义猫或狗来继承,go里叫做加入匿名结构体
// 定义结构体: Cat
type Cat struct {
//为了复用性,体现继承思维,加入匿名结构体
Animal
}
// 对Cat绑定特有的方法
func (c *Cat) scratch() {
fmt.Println("我是小猫,我可以挠人")
}
func main() {
//创建Cat结构体示例,以下的Animal可以去掉
cat := &Cat{}
cat.Animal.Age = 3
cat.Animal.Weight = 10.6
cat.Animal.Shout()
cat.Animal.ShowInfo()
cat.scratch()
}
接口:
接口定义一组方法集合
type IF interface {
Method1(param_list) return_type
}