golang学习笔记

1、Println、Print、Printf

Println 打印内容会换行

Print 打印内容不会换行

Printf 打印可以使用%v占位符动态展示数据

package main

import "fmt"

func main() {
	a := "aaa"
	b := "bbb"
	c := "ccc"
	// fmt.Println("a=", a, "b=", b, "c=", c) //换行
	// fmt.Print("Hello golang")   //不换行
	fmt.Printf("a=%v,b=%v,c=%v", a, b, c)
}

2、变量声明

var声明可用作全局,可先声明不赋值,但需要规定类型。也可声明赋值,同时声明多个类型使用()包裹

:= 局部声明,该方法只适用于局部变量赋值,不允许全局使用。

_为匿名变量,不需要接受的内容用_,它不会分配内存,也不会存在重复声明变量问题。

package main

import (
	"fmt"
)

func getUser() (string, int) { //返回多个值
	return "xiaoluo", 12
}

func main() {
	// var 定义类型
	var a string   //初始化参数 需要追加类型
	fmt.Println(a) //变量声明没赋值 为空
	a = "hello"
	fmt.Println(a)

	var b int = 100 //如果不写int 也会类型推导出 b 为 int类型
	fmt.Println(b)

	var a1, a2 string // 同时声明多个类型
	a1 = "hello"
	a2 = "world"
	fmt.Println(a1, a2)

	var (
		name    string
		age     int
		hobbies []string
	) // 声明多个变量  类型不同
	name = "test"
	age = 10
	hobbies = []string{"coding", "eating", "sleeping"}
	fmt.Println(name, age, hobbies)

	var (
		name2    string   = "xiaoluo"
		age2     int      = 21
		hobbies2 []string = []string{"sleep", "code", "moive"}
	) //声明并赋值
	fmt.Println(name2, age2, hobbies2)

	//  短变量声明  := 创建变量c并赋值  仅适用于声明局部变量,不能用做全局
	c := "test"
	fmt.Println(c)

	d1, d2, d3 := 11, 22, "33" //短变量声明多个变量赋值
	fmt.Printf("d1类型%T,d2类型%T,d3类型%T", d1, d2, d3)

	var username, _ = getUser() //只需要username,_下划线为匿名变量,不会分配内存,不存在重复声明问题
	var _, ages = getUser()
	fmt.Println(username, ages)
}
3、常量

const 声明  定义就要赋值 不能改变

同时赋值多个  const ( ) 括号包住

iota配合const使用, 可出现自增情况

golang是严格区分变量大小写的 age | AGE

package main

import "fmt"

func main() {
	//const  常量
	const pi = 3.1415926 //定义就需要赋值,后续不能改变
	fmt.Println(pi)

	const ( //同时声明多个常量
		A = "A"
		B = "B"
	)
	fmt.Println(A, B)

	const ( //只赋值了第一个,下面的就全是a
		a = "a"
		b
		c = "c"
		d
		e
	)
	fmt.Println(a, b, c, d, e) // "a a c c c"

	// iota 计数器,搭配const使用
	// 每次const出现,会让iota自自增长加一
	const m = iota // m=0
	const (
		n = iota // n=0
		o        // o=1
	)
	fmt.Println(n, o)

	const ( // _ 跳过
		a1 = iota
		_  ///跳过了1
		a3
		a4
	)
	fmt.Println(a1, a3, a4) // 0,203

	//iota 声明中间插队
	const (
		b1 = iota
		b2 = 100
		b3 = iota
		b4
	)
	fmt.Println(b1, b2, b3, b4) // 0,100,2,3

	const (
		c1, c2 = iota + 1, iota + 2 //1,2
		c3, c4                      //2,3
		c5, c6                      //3,4
	)
	fmt.Println(c1, c2)
	fmt.Println(c3, c4)
	fmt.Println(c5, c6)

	var age = 18
	var AGE = 18
	fmt.Println(age, AGE) // 18 18  go严格区分大小写
}
4、类型

golang数据类型

基本类型:整型、浮点型、布尔型、字符串型

复合数据类型:数组、切片、结构体、函数、map、通道(channel)、接口deng

4.1 整型int

int 分为 有符号整型 | 无符号整型   区别在于 有无负号

unsafe.Sizeof() 可查看变量占用字符数量

整型:分为 有符号整型 | 无符号整型   区别在于 有无负号

unsafe.Sizeof() 可查看变量占用字节数量

%v 原样输出  %o八进制输出 %d十进制输出 %x十六进制输出

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	//整形分为 有符号整型|对应的无符号整型
	/*
		有符号:
		 int8    -128~127        占一个字节
		 int16   -32768~32767    占两个字节
		 int32                    占四个字节
		 int64                    占八个字节
	*/
	/*  无符号:
	    uint8   0~255            占一个字节
	    uint16
	    uint32
	    uint64                   占八个字节
	*/
	var num int8 = 12
	fmt.Printf("num=%v 类型是%T\n", num, num) //num=100 类型是int
	//unsafe.Sizeof() 查看变量占用字符数
	fmt.Println(unsafe.Sizeof(num)) // 1 占一个字节

	// int 不同长度转换
	var a1 int32 = 10
	var a2 int64 = 21
	fmt.Println(int64(a1) + a2) //不同类型需要转换成同一类型加

	//高位转地位 可能有问题
	var n1 int16 = 200
	fmt.Println(int8(n1)) // -56  出现问题

	// %d表示10进制输出  %b表示二进制输出 %o八进制输出 %x表示16进制

	num1 := 40
	fmt.Printf("n1=%v 类型:%T\n", num1, num1)
	fmt.Println(unsafe.Sizeof(num1)) // 8
	fmt.Printf("num=%v\n", num1)     //原样输出
	fmt.Printf("num=%o\n", num1)     //50
	fmt.Printf("num=%d\n", num1)     //40
	fmt.Printf("num=%x\n", num1)     //28
}
4.2 浮点型float

float浮点型存在 float32 和 float64 两种类型  分别占4|8 个字节

go语言中 float运算会存在精度丢失问题,需要使用第三方包来处理

int转float   float转换int 会截取整数,需要注意

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	//1、定义float类型

	var a float32 = 3.12

	fmt.Printf("值:%v--%f\n", a, a) // 3.12 -- 3.120000  保留六位小数点
	fmt.Println(unsafe.Sizeof(a))  // 4   float32占用4个字节

	var b float64 = 3.12
	fmt.Printf("值:%v--%f\n", b, b) // 3.12--3.120000
	fmt.Println(unsafe.Sizeof(b))  // 8  float64占用八个字节

	//2、 %f 输出float类型 %.2f输出数据保留两位小数
	fmt.Printf("%v-%.2f\n", b, b) //3.12-3.12
	fmt.Printf("%v-%.4f\n", b, b) //3.12-3.1200

	// 64位操作系统中Go语言中浮点默认是float64

	f1 := 3.1415926
	fmt.Printf("%f--%T\n", f1, f1) //3.141593--float64

	//3、GO中科学计数 表示浮点数
	var f2 float32 = 3.14e2       //表示3.14*10的二次方
	fmt.Printf("%v-%T\n", f2, f2) //314-float32
	var f3 float32 = 3.14e-2      //表示3.14/10的二次方
	fmt.Printf("%v-%T\n", f3, f3) //0.0314-float32

	//3、精度丢失问题
	var f4 float64 = 1129.6
	fmt.Println(f4 * 100) //112959.99999999999

	m1 := 8.2
	m2 := 3.8
	fmt.Println(m1 - m2) //4.3999999999999995

	//4、int类型转换成float
	x := 4
	x1 := float64(a)
	fmt.Printf("a类型:%T,b类型:%T\n", x, x1) //a类型:int,b类型:float64

	//5、float类型转换成int
	var x2 float64 = 3.13
	x3 := int(x2)
	fmt.Printf("x2类型:%T,x3类型:%T\n", x2, x3) //x2类型:float64,x3类型:int
}
 4.3 布尔型bool

默认值为false  占一个字节

package main

import (
	"fmt"
	"unsafe"
)

func main() {

	/*
	   go语言中以bool类型声明布尔类型数据,只有true和false两个值
	   1、布尔类型变量默认为false
	   2、go语言中不允许将整型强转至布尔型
	   3、布尔型无法参与数值运算,也无法与其他类型进行转换
	*/
	var flag bool
	fmt.Println(flag)                 //false 默认值
	fmt.Println(unsafe.Sizeof(flag))  //1 占一个字节
	fmt.Printf("%v,%T\n", flag, flag) // false,bool

	var s string
	fmt.Printf("%v\n", s) //默认为空

	var i int
	fmt.Printf("%v ", i) //默认 0

	var f float32
	fmt.Printf("%v", f) //默认 0.0

	 
}
5、字符串&字符
5.1 字符串

字符串转译   \转移 一些特殊字符,\\ 转译成\  多行字符串类似于js的模板字符串

package main

import "fmt"

func main() {
	//1、定义字符串
	var str1 string = "你好"
	var str2 = "你好"
	str3 := "你好"
	fmt.Printf("%v--%T\n", str1, str1)
	fmt.Printf("%v--%T\n", str2, str2)
	fmt.Printf("%v--%T\n", str3, str3)

	//2、转义字符
	str4 := "this \n is str" // 输入 this  换行  is str
	fmt.Println(str4)
	str5 := "C:\\GO\\Bin"
	fmt.Println(str5) // C:\GO\Bin
	str6 := "C:\\GO\\\"Bin\""
	fmt.Println(str6) // C:\GO\"Bin"

	//3、多行字符串   类似于 js模版字符串
	str7 := `
	   你好
	   换行
	   呵呵
	`
	fmt.Println(str7)
}
5.2 字符

字符只能用单引号括起来

字符串不能随意通过索引去修改

中文是通过unicode编码编译

一个汉字占用三个字符,如果遍历存在汉字的字符串 需要配合使用rune

package main

import "fmt"

func main() {

	//1、golang定义字符  单引号是定义字符
	var a = 'a'                      //定义字符 只能有一个元素
	fmt.Printf("值:%v,类型:%T\n", a, a) //值:97,类型:int32

	//2、输出原样字符
	fmt.Printf("值:%c,类型:%T\n", a, a) //值:a,类型:int32

	//3、输出字符串中的字符
	var str = "this"
	fmt.Printf("值:%c,类型:%T\n", str[2], str[2]) //值:i,类型:uint8

	//4、一个汉字占用3个字节(golang中使用 utf-8编码),一个字母占用一个字节
	//unsafe.Sizeof() 无法查看字符串类型占用存储空间 使用len()可以
	fmt.Println(len(str)) //4
	str2 := "你好golang"
	fmt.Println(len(str2)) //12

	var s = '国'                      // unicode编码 国 对应 22269
	fmt.Printf("值:%v,类型:%T\n", s, s) //值:22269,类型:int32

	//5、遍历输出字符串  for   range
	for i := 0; i < len(str); i++ { //byte 类型   代表ASCII码的一个字符
		fmt.Printf("%v(%c)", str2[i], str2[i]) //28(ä)189(½)160( )229(å) 有中文 导致乱了
	}

	for _, r := range str2 { //rune类型   代表utf-8的一个字符
		fmt.Printf("%v(%c)\n", r, r) //20320(你)22909(好)103(g)111(o)108(l)97(a)110(n)103(g)
	}

	//5、修改字符串
	s1 := "big"
	// s1[0] = "s"  //直接修改报错
	byteStr := []byte(s1) //如果存在汉字不能私用byte
	byteStr[0] = 'p'
	fmt.Println(string(byteStr)) //pig

	s2 := "你好"
	runeStr := []rune(s2)
	runeStr[0] = '我'
	fmt.Println(string(runeStr)) //我好
}
5.3 其它类型转字符串类型

有两种方式:1、使用string.Sprint转换       2、使用第三方包 strconv转换

package main

import (
	"fmt"
	"strconv"
)

func main() {
	//转换建议地位转换成高位 ,如果 地位转高位  可能会存在数据不准   int-> float
	var a int8 = 20
	var b int16 = 80
	fmt.Println(int16(a) + b) //不同种类型需要先转成同一类型  低转高

	var f1 float32 = 1.23456789
	var f2 float64 = 1.23456789
	fmt.Println(float64(f1) + f2) //情况一样

	fmt.Println(float64(a) + f2)

	//其它类型转string
	var i int = 20
	var f float64 = 3.14
	var t bool = false
	var be byte = 'a'
	//1、strings.Sprint 转换
	str1 := fmt.Sprintf("%d", i)
	str2 := fmt.Sprintf("%f", f)
	str3 := fmt.Sprintf("%t", t)
	str4 := fmt.Sprintf("%c", be)
	fmt.Printf("值:%v,类型:%T\n", str1, str1)  //值:20,类型:string
	fmt.Printf("值:%v,类型:%T\n", str2, str2)  //值:3.140000,类型:string
	fmt.Printf("值:%v,类型:%T\n", str3, str3)  //值:false,类型:string
	fmt.Printf("值:%v ,类型:%T\n", str4, str4) //值:a ,类型:string

	//2、通过strconv转换
	str5 := strconv.FormatInt(int64(i), 10)             // 默认结束 int64类型
	str6 := strconv.FormatFloat(float64(f), 'f', 2, 64) //接受四个参数  1-变量 2-格式化类型 3-保留小数点 4-格式化类型
	str7 := strconv.FormatBool(t)
	str8 := strconv.FormatUint(uint64(be), 10)
	fmt.Printf("值:%v,类型:%T\n", str5, str5) //值:20,类型:string
	fmt.Printf("值:%v,类型:%T\n", str6, str6) //值:3.14,类型:string
	fmt.Printf("值:%v,类型:%T\n", str7, str7) //值:false,类型:string
	fmt.Printf("值:%v,类型:%T\n", str8, str8) //值:97,类型:string

}
5.4 string型转换成其它类型
package main

import (
	"fmt"
	"strconv"
)

func main() {
	//string类型转换成整型
	str := "123456.11"
	fmt.Printf("%v--%T\n", str, str) //123456--string

	//
	/* ParseInt
	   1、string数据
	   2、进制
	   3、位数
	*/
	num, _ := strconv.ParseInt(str, 10, 32) // 0 返回两个值  如果转换失败 会返回0
	fmt.Println(num)

	flo, _ := strconv.ParseFloat(str, 64)
	fmt.Println(flo) // 123456.11

	b, _ := strconv.ParseBool("xxx")
	fmt.Printf("值:%v,类型:%T", b, b) //值:false,类型:bool
}
6、运算符

位运算符  分为  &与   | 或   ^ 非    <<左移n位    >>右移n位

package main

import "fmt"

func main() {
	//位运算符
	/*
	   &  左右两边都为1返回1
	   | 左右两边有一个1返回1
	   ^ 左右两边不相同返回1
	   << 左移n位就是乘以2的n次方  “a<<b” 把a的各个二进制全部往左移动b位,高位丢弃,低位补0
	   << 右移n位就是乘以2的n次方
	*/
	var (
		a = 5  // 0101
		b = 10 // 1010
	)
	fmt.Println("a&b", a&b)   //0    0000
	fmt.Println("a|b", a|b)   //15   1111
	fmt.Println("a^b", a^b)   //15   1111
	fmt.Println("a<<b", a<<b) //5120   0101向左转移10位变成-> 1010000000000
	fmt.Println("a<<b", a>>b) //0   向左

}
7、if & for 循环
  • if 有局部变量声明的方法
  • golang中无while语句,可用for替换
package main

import "fmt"

func main() {
	//if语句
	//区别在于 上者a是全局变量, 下方b为局部变量
	var a = 10
	//普通写法   注意判断用 ==   一个是赋值
	if a == 10 {
		fmt.Println(a) //10
	}

	//另一种写法
	if b := 40; b != 40 {
		fmt.Println(b)
	} else {
		fmt.Println("不符合") //不符合
	}

	// for 循环遍历
	for i := 0; i < 10; i++ {
		if i < 6 {
			fmt.Println(i)
		} else {
			break //不符合条件跳出循环
		}
	}

	i := 0
	for { //类似于 while  golang中没有while语句
		if i < 7 {
			fmt.Println(i)
		} else {
			break
		}
		i++
	}

}
8、range循环 & fallthrought

for..range循环 返回两个参数 参数1:key 参数2:val    不需要的参数可以用_接受

switch中的fallthought 穿透,加上它可以穿透到下方判断(即使当前case不满足)

package main

import "fmt"

func main() {
	var str = "你好,golang"
	//for range 遍历字符串
	for key, val := range str {
		// fmt.Println("key=", key, "val=", val)
		/*  汉字使用utf-8输出  字符以 ASCII码值输出
		key= 0 val= 20320
		key= 3 val= 22909
		key= 6 val= 44
		key= 7 val= 103
		key= 8 val= 111
		key= 9 val= 108
		key= 10 val= 97
		key= 11 val= 110
		key= 12 val= 103
		*/
		fmt.Printf("Key=%v,val=%c\n", key, val)
		/*
			Ksey=0,val=你
			Key=3,val=好
			Key=6,val=,
			Key=7,val=g
			Key=8,val=o
			Key=9,val=l
			Key=10,val=a
			Key=11,val=n
			Key=12,val=g
		*/
	}

	// for range 遍历切片
	var arr = []string{"php", "java", "js"}
	for _, val := range arr {
		fmt.Println("val=", val)
	}
	/*
			val= php
		    val= java
		    val= js
	*/

	//golang中 switch的穿透fallthrought
	// fallthrought 语法可以执行满足条件的case的下一个case,为了兼容C语言中的case设计的

	var a int8 = 2
	switch {
	case a < 3:
		fmt.Println("a<3")
		fallthrough //向下穿透打印(即使当前case不符合也会穿透至下方)  ( 可以打印出来a<3 a<4 )不加上fallthrough只打印这一行就跳出
	case a < 4:
		fmt.Println("a<4")
	case a < 5:
		fmt.Println("a<5") //因为上面a<4未穿透,即跳出 不打印 a<5
	}

}
9、break & continue & goto & label:

 break  结束当前当前循环  break可以搭配label 来实现跳出多重循环

continue 结束当前循环,开始下一轮循环

goto 配合label使用,无条件跳转至代码处,并执行下列代码

package main

import "fmt"

func main() {
	/*
		    golang中break语句
			· 用于跳出循环中的语句,并开始执行循环之后的语句
			· break在switch(开关语句)中执行一条case后跳出语句的作用
			· 在多重循环中,可以用标号 label标出想 break 的循环
	*/

	// 双重循环,用break只能跳出当前循环, 但是使用label: 标记,是可以完成跳出多成循环的效果的
label1:
	for i := 0; i < 10; i++ {
		for j := 0; j < 10; j++ {
			if j == 3 {
				break label1
			}
			fmt.Printf("i=%v j=%v\n", i, j)
		}
	}
	/*
	   i=0 j=0
	   i=0 j=1
	   i=0 j=2
	*/

	//break 跳出当前循环
	for i := 0; i < 10; i++ {
		if i == 3 {
			break
		}
		fmt.Printf("%d", i)
		/*  到3截止
		i=0 j=0
		i=0 j=1
		i=0 j=2
		*/
	}

	//continue 结束当前循环,开始下一次循环
	for i := 0; i < 10; i++ {
		if i == 3 {
			continue
		}
		fmt.Printf("%d", i) //012456789 跳过了3
	}

	//goto 语句通过标签进行代码间的无条件跳转,goto语句可以快速跳出循环,避免重复

	var n = 30
	if n > 20 {
		fmt.Println("成年人")
		goto test
	}

	fmt.Println("小屁孩")
	fmt.Println("hello world") // 跳过了这两行  直接到 how are you
test:
	fmt.Println("hello ,how are you")

}
10、数组&切片
10.1 声明数组的方式

var arr [3]int     var strArr  = [3]string{"aa","bb","cc"}

var arr2 [...]int 可自动推导长度    数组长度定了就不能去修改 

package main

import "fmt"

func main() {
	//数组  长度是固定的,不可改变

	//1、数组的长度也是类型的一部分
	var arr1 [3]int
	var arr2 [4]int
	var arr3 [3]string

	fmt.Printf("arr1:%T,arr2:%T,arr3:%T\n", arr1, arr2, arr3) //arr1:[3]int,arr2:[4]int,arr3:[3]string

	//2、数组的初始化
	fmt.Println(arr1) // [0 0 0]
	fmt.Println(arr3) // [  ]

	//1、先声明 再下标赋值
	arr1[0] = 1
	arr1[1] = 2
	arr1[2] = 3
	fmt.Println(arr1) //[1 2 3]

	var strArr [3]string
	strArr[0] = "hello"
	strArr[1] = "world"
	strArr[2] = "!"
	fmt.Println(strArr) //[hello world !]

	//2、声明时赋值
	var arr4 = [4]int{1, 2, 3}
	fmt.Println(arr4) // [1,2,3,0]  第四项没有填充默认补0

	//3、不清楚长度时 三个点可以自动推导
	var arr5 = [...]int{123, 23, 241}
	// arr5[3]=3  这里不能赋值,超出范围
	fmt.Println(arr5)      // [123 23 241]
	fmt.Println(len(arr5)) //3

	//4、index类型声明,长度有index最大值确定
	arr6 := [...]int{0: 1, 1: 10, 2: 20, 5: 50} //前者是index 后者是val  无用0补气
	fmt.Println(len(arr6))                      // 6
	fmt.Println(arr6)                           //[1 10 20 0 0 50 ]

	//5、数组的循环遍历
	arr7 := [...]int{2, 4, 6, 1, 2, 6}
	for key, val := range arr7 {
		fmt.Println("key:", key, "val:", val)
	}

	//todo:  查找数组中的最大值和索引
	arr8 := [5]int{2, 5, 1, 7, 3}
	index := 0
	val := arr8[0]
	for i := 0; i < len(arr8); i++ {
		if arr8[i] > val {
			val = arr8[i]
			index = i
		}
	}
	fmt.Println(val, index)
}
10.2 值类型|引用类型

数组是值类型,切片是引用类型,其声明区别就是在声明时是否定义长度

package main

import "fmt"

func main() {
	// 值类型:  改变副本值,不会改变原来的值  相当于复制了一层
	// 引用类型: 改变副本值,会改变原来的值   相当于共享一块数据 都指向同一块内存地址
	// 基本数据类型 和 数组都是值类型

	//数组  定义时定了长度
	var a = 10
	var b = a
	fmt.Println(a == b) // true

	var arr1 = [3]int{1, 0}
	var arr2 = [3]int{1}
	fmt.Println(arr1 == arr2) // true
	arr1[2] = 55
	fmt.Println(arr1, arr2)

	var arr3 = [2]int{1, 2}
	var arr4 = arr3
	arr3[1] = 10
	fmt.Println(arr3 == arr4) // false
	fmt.Println(arr3, arr4)   // [1 10] [1 2]   修改了arr3并没有去影响到arr4

	// 切片  是引用类型  定义没定义长度
	var arr5 = []int{1, 2, 3, 45}
	arr6 := arr5
	arr5[1] = 10
	fmt.Println(arr5, arr6) //1 10 3 45] [1 10 3 45]  arr6的值也被影响了, 这是因为arr6是引用了arr5的同一内存地址

}
10.3 多维数组

var arr = [2][3]string{{"1"},{""2}} 声明二维数组 

package main

import "fmt"

func main() {
	//1、多维数组定义
	// var arr1 = [3]int{1,2,3} 一维数组
	var arr2 = [2][3]string{ //两行三列
		{"吃饭", "睡觉", "打豆豆"},
		{"唱歌", "跳舞", "看医生"},
	}
	fmt.Println(arr2)       // [[吃饭 睡觉 打豆豆] [唱歌 跳舞 看医生]]
	fmt.Println(arr2[0][2]) // 打豆豆

	//2、循环遍历多位数组
	for _, item1 := range arr2 {
		for _, item2 := range item1 {
			fmt.Printf("%v\\", item2) // 吃饭\睡觉\打豆豆\唱歌\跳舞\看医生\
		}
	}
}
10.4 切片

切片定义类似数组,不定义长度的数组即位切片

切片为引用类型

长度:切片包含的元素个数
容量:从第一个元素开始数,到底层数组元素末尾的个数
       切片s的长度和容量可以通过len(s)和cap(s)来获取

package main

import "fmt"

func main() {
	// 1、切片声明  和 数组区别在于没有确认长度
	var arr1 = []int{1, 2, 3}
	fmt.Println(arr1)                                       //[1,2,3]
	fmt.Printf("值:%v,类型:%T,长度:%v\n", arr1, arr1, len(arr1)) //值:[1 2 3],类型:[]int,长度:3

	var arr2 = []int{1: 2, 2: 3, 3: 4}                      //空余补0
	fmt.Printf("值:%v,类型:%T,长度:%v\n", arr2, arr2, len(arr2)) //值:[0 2 3 4],类型:[]int,长度:4

	var arr3 []int
	fmt.Println(arr3 == nil) //true  golang中切片声明后,默认值为nil

	//2、切片循环遍历
	var strSlice = []string{"a", "b", "c", "d"}
	for i, v := range strSlice {
		fmt.Printf("i=%v,v=%v|", i, v) // i=0,v=a|i=1,v=b|i=2,v=c|i=3,v=d|
	}

	//3、基于数组定义切片
	a := [5]int{1, 2, 3, 4, 5}

	b := a[:]                      //获取数组里面所有的值
	fmt.Printf("类型:%T,值:%v", b, b) //[[]int,值:[1 2 3 4 5]    []int 是切片
	//切片截取
	c := a[1:4]    //获取索引1-4(包含1,不包含4  【1,4)  )的值  左闭右开
	fmt.Println(c) //[2 3 4]
	d := a[2:]     //包含索引2之后的数据
	fmt.Println(d) // [3 4 5]
	e := a[:3]     // 索引3之前的数据
	fmt.Println(e) //[1 2 3]

	//4、基于切片定义切片
	var numSlice = []int{1, 2, 3, 4, 5}
	f := numSlice[1:4]
	fmt.Println(f) //[2 3 4]
	g := numSlice[2:]
	fmt.Println(g) //[3 4 5]
	h := numSlice[:3]
	fmt.Println(h) //[1 2 3]

	//5、关于切片的长度和容量
	/*
	   长度:切片包含的元素个数
	   容量:从第一个元素开始数,到底层数组元素末尾的个数
	   切片s的长度和容量可以通过len(s)和cap(s)来获取
	*/
	s := []int{2, 3, 5, 7, 8, 88}
	fmt.Printf("s长度:%v,s容量:%v\n", len(s), cap(s)) //s长度:6,s容量:6

	t := s[2:]                                    //5, 7, 8, 88
	fmt.Printf("s长度:%v,s容量:%v\n", len(t), cap(t)) //s长度:4,s容量:4

	j := s[2:5]                                   //5, 7, 8     容量4的由来   5,7,8,88
	fmt.Printf("s长度:%v,s容量:%v\n", len(j), cap(j)) //s长度:3,s容量:4
}
10.5 make&copy&append
package main

import "fmt"

func main() {
	/*
	   切片声明和初始化的几种方法
	*/

	//1、var定义
	// var slice1 []int

	//2、make函数创建   make([]T,size,cap)
	var slice2 = make([]int, 5, 10) // [0 0 0 0 0]   参数1:类型  参数2:长度  参数3:容量
	fmt.Println(slice2)
	fmt.Printf("长度:%v,容量:%v\n", len(slice2), cap(slice2)) //长度:5,容量:10

	/* slice2[5] = 2    报错 无法使用下标给切片扩容
	fmt.Println(slice2) */

	//3、切片扩容 使用append追加扩容
	sl2ice2 := append(slice2, 1)
	fmt.Println(sl2ice2) //[0 0 0 0 0 1]

	//4、append方法合并切片
	sliceA := []string{"php", "java"}
	sliceB := []string{"nodejs", "js"}
	sliceA = append(sliceA, sliceB...)
	fmt.Println(sliceA) //[php java nodejs js]

	//5、切片扩容策略
	var sliceC []int
	for i := 1; i <= 10; i++ {
		sliceC = append(sliceC, i)
		fmt.Printf("长度:%v,容量:%v\n", len(sliceC), cap(sliceC))
		/*
					长度:1,容量:1
				    长度:2,容量:2
				    长度:3,容量:4
				    长度:4,容量:4
				    长度:5,容量:8
				    长度:6,容量:8
			    	长度:7,容量:8
				    长度:8,容量:8
			     	长度:9,容量:16
				    长度:10,容量:16
		*/
	}

	/*
	   6、值类型:改变变量富文本的值,不会改变变量本身的值
	    引用类型:改变变量赋文本的值,会改变变量本身的值
	*/
	sliceD := []int{1, 2, 3, 4}
	sliceE := sliceD
	sliceE[0] = 100
	fmt.Println("sliceD=", sliceD) //sliceD= [100 2 3 4]  改变sliceE 的值 sliceD也变了

	sliceF := []int{1, 2, 3, 4}
	sliceG := make([]int, 5, 5)
	copy(sliceG, sliceF) // 将F的值复制给D
	sliceF[1] = 999
	fmt.Println("sliceF=", sliceF, "sliceG=", sliceG) //sliceF= [1 999 3 4] sliceG= [1 2 3 4 0]

	//7、slice切片删除元素
	//append方法   删除索引为2的元素, 注意:append合并切片的时候最后一个元素需要...
	a := []int{1, 2, 3, 4, 5}
	a = append(a[:2], a[3:]...)
	fmt.Println(a) //[1 2 4 5]

	//8、字符串转换
	str1 := "hello golang"
	byteStr := []byte(str1)
	byteStr[0] = 'H'
	fmt.Println(string(byteStr)) //Hello golang

	str2 := "你好golang"
	runStr := []rune(str2)
	runStr[0] = '大'
	fmt.Println(string(runStr)) //大好golang

}
10.6 切片排序&sort包
package main

import (
	"fmt"
	"sort"
)

func main() {
	//找出下标和为8的两个数
	var arr = [...]int{1, 3, 5, 7, 8}
	for i := 0; i < len(arr); i++ {
		for j := i + 1; j < len(arr); j++ {
			if arr[i]+arr[j] == 8 {
				fmt.Println("i=", i, "j=", j)
			}
		}
	}
	/*
		选择排序:进行从小到大排序 [9,4,6,1,8,7]
	*/
	var arr2 = []int{9, 4, 6, 1, 8, 7}
	for i := 0; i < len(arr2); i++ {
		for j := i + 1; j < len(arr2); j++ {
			if arr2[i] > arr2[j] {
				arr2[i], arr2[j] = arr2[j], arr2[i] // 交换位置
			}
		}
	}
	fmt.Println(arr2) //[1 4 6 7 8 9]
	/*
		冒泡排序:进行从小到大排序 [2,6,4,1,9,3]
	*/
	var arr3 = []int{2, 6, 4, 1, 9, 3}
	for i := 0; i < len(arr3); i++ {
		for j := i + 1; j < len(arr3); j++ {
			if arr3[i] > arr3[j] {
				temp := arr3[i]
				arr3[i] = arr3[j]
				arr3[j] = temp
			}
		}
	}
	fmt.Println(arr3)
	/*
	  sort包
	*/
	intList := []int{2, 5, 7, 3, 1, 0}
	float64List := []float64{2.5, 5.7, 3.1, 1.0, 0.0}
	stringList := []string{"a", "b", "g", "d", "e", "c"}
	//升序
	sort.Ints(intList)
	sort.Float64s(float64List)
	sort.Strings(stringList)
	fmt.Println(intList)
	fmt.Println(float64List)
	fmt.Println(stringList)
	/*
		[0 1 2 3 5 7]
		[0 1 2.5 3.1 5.7]
		[a b c d e g]
	*/

	//降序
	sort.Sort(sort.Reverse((sort.IntSlice(intList))))
	sort.Sort(sort.Reverse((sort.Float64Slice(float64List))))
	sort.Sort(sort.Reverse((sort.StringSlice(stringList))))
	fmt.Println(intList)
	fmt.Println(float64List)
	fmt.Println(stringList)
	/*
	 [7 5 3 2 1 0]
	 [5.7 3.1 2.5 1 0]
	 [g e d c b a]
	*/
}
11、map类型

map为引用类型

map的curd操作,查 v,ok := mapObj["key"]   成功返回value  true   失败返回 空  false

delete(mapObj,key) 删除map数据中的键值对

strings包的Split 分割  类似于js的split   

 ⚠️ map的range遍历是随机的 代码中有方法让其按key变量

package main

import (
	"fmt"
	"sort"
	"strings"
)

func main() {
	// map为引用类型
	// 1、 make创建map类型的数据

	var userInfo = make(map[string]string)
	userInfo["name"] = "zs"
	userInfo["age"] = "18"
	fmt.Println(userInfo) //map[age:18 name:zs]

	//2、声明时初始化值
	userInfo2 := map[string]string{
		"username": "ls",
		"age":      "21",
	}
	fmt.Println(userInfo2) //map[age:21 username:ls]

	//3、循环遍历map类型数据
	for k, v := range userInfo2 {
		fmt.Printf("key:%v,value:%v\n", k, v)
		/*
			key:username,value:ls
			key:age,value:21
		*/
	}

	//4、map类型数据的curd
	userInfo3 := make(map[string]string)
	userInfo3["name"] = "zw" //增
	userInfo3["name"] = "qw" //改

	v, ok := userInfo3["age"] //查
	fmt.Println(v, ok)        // 空  false

	delete(userInfo3, "name")
	fmt.Println(userInfo3) // map[]

	//5、map类型的切片 map不初始化值为nil
	var people = make([]map[string]string, 3, 3) // map类型切片
	fmt.Println(people[0])                       // map[]  不初始化值 默认值时nil
	fmt.Println(people[0] == nil)                // true
	if people[0] == nil {
		people[0] = make(map[string]string)
		people[0]["name"] = "ls"
		people[0]["age"] = "21"
	}
	for k, v := range people {
		fmt.Println("k=", k, "v=", v)
	}
	/*
		k= 0 v= map[age:21 name:ls]
		k= 1 v= map[]
		k= 2 v= map[]
	*/
	//6、map类型存放多多值类型
	var course = make(map[string][]string)
	course["java"] = []string{"springMVC", "myBatis", "odbc"}
	course["javascript"] = []string{"nodejs", "vue", "react"}
	for k, v := range course {
		fmt.Println("k=", k, "v=", v)
	}
	/*
		k= java v= [springMVC myBatis odbc]
		k= javascript v= [nodejs vue react]
	*/

	//7、map的排序
	map1 := make(map[int]int, 10)
	map1[10] = 100
	map1[4] = 43
	map1[6] = 50
	map1[1] = 13
	fmt.Println(map1) //map[1:200 2:43 3:50 4:13]
	for k, v := range map1 {
		fmt.Println("k=", k, "v=", v)
	}
	/*     map遍历的值是随机的  不会从第一项开始 比如 map[1]
			   k= 4 v= 43
		       k= 6 v= 50
		       k= 1 v= 13
	       	   k= 10 v= 100
	*/

	//疑问?如何实现按照key升序排序呢
	//1、把map的key放在切片里
	var slice []int
	for k, _ := range map1 {
		slice = append(slice, k)
	}
	fmt.Println(slice) // [10 4 6 1]
	//2、让key进行升序排序
	sort.Ints(slice)
	fmt.Println(slice, "slice")
	//循环slice输出map对应key值
	for _, v := range slice {
		fmt.Printf("%v\t", map1[v]) //13 43 50 100
	}

	//8、字符串 split分割成切片
	var str = "so beautiful you are, cute girl!"
	var sliceStr = strings.Split(str, " ") //使用strings包 分割str生产切片,以空格分割
	fmt.Println(sliceStr)                  // [so beautiful you are, cute girl!]

}
12、func 函数
12.1 可变参数

有些时候参数数量不确定 使用  x ...int    x可以接受之后所有的int成一个数组

package main

import "fmt"

// 函数
func sumTowNum(a int, b int) int {
	return a + b
}

/*
  1、参数问题
*/
// 参数类型一致 简写
func sumTowNum2(a, b, c int) int {
	return a + b + c
}

// 参数数量不固定  类似于 js中的 ...args 接受参数成数组了
func sumTowNum3(x ...int) {
	fmt.Println(x) // [1 2 3 4]
	for _, v := range x {
		fmt.Println(v) // 1 2 3 4
	}
}

// x 接受第一个参数 , y接受其余参数
func sumTowNum4(x int, y ...int) {
	fmt.Println("x=", x, "y=", y) // x= 1 y= [11 22 33 44]
}

func main() {
	fmt.Println(sumTowNum(1, 2))     //3
	fmt.Println(sumTowNum2(1, 2, 3)) //6
	sumTowNum3(1, 2, 3, 4)
	sumTowNum4(1, 11, 22, 33, 44)
}
12.2 函数返回值

返回多个值,不需要的使用_匿名接受

package main

import "fmt"

/*
函数返回多个值
*/
func cacl(x, y int) (int, int) {
	return x + y, x - y
}

// 返回值命名: 函数自定义可以返回多个命名,并在函数体中直接使用这个变量,最后通过return 返回
func cacl2(x, y int) (sum, sub int) {
	sum = x + y
	sub = x - y
	return sub, sum
}

func main() {
	fmt.Println(cacl(1, 2))  //3  -1
	fmt.Println(cacl2(1, 2)) //-1  3  //返回值sub sum取反返回

	a, _ := cacl2(1, 2)
	fmt.Println(a) // 匿名接受第一个返回值
}
12.3 定义函数
package main

import "fmt"

// 1、定义函数类型
type calc func(int, int) int
type myInt int //自定义类型

func add(x, y int) int {
	return x + y
}
func sub(x, y int) int {
	return x - y
}

func test() {
	fmt.Println("test...")
}

type cbType func(int, int) int

// 2、方法作为另一个函数的参数 改函数接受了两个int类型参数,第三个参数是一个 cbType类型的函数 有点像callback函数
func receiveCb(x, y int, cb cbType) int {
	return cb(x, y)
}

func main() {
	// fmt.Println(factorial(8))
	var c calc
	c = add
	fmt.Printf("c的类型:%T\n", c) // main.calc

	d := add
	fmt.Printf("d的类型:%T\n", d) //func(int, int) int  类型推倒

	var a1 int = 100
	var a2 myInt = 200
	fmt.Printf("a的类型:%T,b的类型:%T\n", a1, a2) //a的类型:int,b的类型:main.myInt
	fmt.Println(myInt(a1) + a2)             // 虽然都是Int数字类型 但是一个是myInt 仍然需要转换成同一类型

	fmt.Println(receiveCb(100, 200, sub))
}
12.4 函数的闭包
package main

import "fmt"

// 函数的递归
func factorial(n int) int {
	if n == 0 {
		return 1
	}
	return n * factorial(n-1)
}

// 实现阶层相加
func add(n int) int {
	if n == 0 {
		return 0
	}
	return n + add(n-1)
}

// 闭包
func add2() func(x int) int {
	var i = 10
	return func(x int) int {
		i += x
		return i
	}
}

func main() {
	//1、匿名函数
	func() {
		fmt.Println("test") //test
	}()

	//接受参数的匿名自执行函数   类似于js
	func(x, y int) {
		fmt.Println(x + y)
	}(1, 6) //7

	var myFn = func() {
		fmt.Println("myFn") // myFn
	}
	myFn()

	fmt.Println(factorial(5)) // 120
	fmt.Println(add(100))     //5050

	var test = add2()
	fmt.Printf("%T\n", test) // test是返回的那个函数
	fmt.Println(test(10))    //20
	fmt.Println(test(10))    //30
	fmt.Println(test(10))    //40
}
13、panic&recover处理异常

golang中没有异常处理机制  panic可以在任意地方抛出错误,recover只能在defer中的函数使用

panic抛出错误,如果不接受,程序会直接报错

package main

import "fmt"

/*
Go 语言中没有异常机制, 可以使用 panic/recover 模式来处理错误
panic 可以在任何地方引发, 但 recover 只能在 defer调用的函数中有效
*/
func fn1() {
	fmt.Println("fn1")
}
func fn2() {
	defer func() {
		err := recover()
		if err != nil { //err 不等于nil  说明有异常
			fmt.Println(err) //抛出异常
		}
	}()
	panic("抛出异常")
}

func fn3(a, b int) int {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println("err:", err) //err: runtime error: integer divide by zero
		}
	}()
	return a / b
}

func main() {
	fn1()
	fn2()
	fn3(10, 0)
	fmt.Println("结束")
}
14、time包&时间戳
14.1 格式化

时间格式化模板不再是常用的 YYYY-MM-DD

需要使用Go的诞生时间 2006-01-02 15:04:05 

小时中 15表示24小时制。03 表示 12小时制

package main

import (
	"fmt"
	"time"
)

func main() {
	//1、获取当前日期
	timeObj := time.Now() //2023-12-20 23:02:42.596486 +0800 CST m=+0.000429084
	fmt.Println(timeObj)
	Year := timeObj.Year()
	Month := timeObj.Month()
	Day := timeObj.Day()
	Hour := timeObj.Hour()
	Minute := timeObj.Minute()
	Second := timeObj.Second()
	//%02d 表示宽度, 整数不够2列就补0
	fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", Year, Month, Day, Hour, Minute, Second)

	/*
	   时间类型有一个自带的方法Format进行格式化
	   需要注意的是Go语言中格式化时间模板不是常见的YYYY-MM-DD HH:mm:ss
	   而是使用Go的诞生时间2006年1月2号15点04分(记忆口诀为 2006 1 2 3 4)
	   2006年
	   01 月
	   02 日
	   03 时  12小时制  15 24小时制
	   04 分
	   05 秒
	*/
	fmt.Println(timeObj.Format("2006/01/02 15//04/05")) //  2023/12/20 23//13/55

	//2、获取当前时间戳
	unixTime := timeObj.Unix()
	fmt.Println("当前时间戳", unixTime) //1703085511

	//3、时间戳转换成日期
	var timeNum int64 = 1703085511
	timeTest := time.Unix(timeNum, 0)
	fmt.Println(timeTest, timeTest.Format("2006-01-02 03-04-05")) //2023-12-20 23:18:31 +0800 CST  |  2023-12-20 11-18-31

	//4、字符串转换时间戳
	//step1、先转成时间格式
	var str = "2023-12-21 12:06:47"
	strObj, _ := time.ParseInLocation("2006-01-02 03:04:05", str, time.Local)
	fmt.Println(strObj) //2023-12-21 12:06:47 +0800 CST
	//step2、时间格式转成时间戳
	fmt.Println(strObj.Unix()) //1703131607

	//5、time包定义的时间间隔类型的常量
	/*
		   const (
			Nanosecond Duration = 1
			Microsecond         = 1000 * Nanosecond
			Millisecond         = 1000 * Microsecond
			Second              = 1000 * Millisecond
			Minute              = 60 * Second
			Hour                = 60 * Minute
		   )
	*/
	fmt.Println(time.Nanosecond)
	fmt.Println(time.Microsecond)

	//6、时间操作函数
	var timeObj2 = time.Now()
	fmt.Println(timeObj2.Add((time.Hour))) //增加了一小时
}


14.2 定时器
package main

import (
	"fmt"
	"time"
)

/*
golang定时器
*/
func main() {
	//1、time.Now实现
	ticker := time.NewTicker(time.Second)
	n := 5
	//ticker.C
	for t := range ticker.C {
		n--
		fmt.Println(t)
		if n == 0 {
			ticker.Stop() //终止定时器执行
			break
		}
	}

	//2、time.Sleep()  休眠
	fmt.Println("a1")
	time.Sleep(time.Second)
	fmt.Println("a2")
	time.Sleep(time.Second)
	fmt.Println("a3")
	time.Sleep(time.Second)
	fmt.Println("a4")

	for {
		time.Sleep(time.Second)
		fmt.Println("我在执行定时任务")
	}

}
15、指针
15.1 定义|&|*
package main

import "fmt"

func main() {
	/*
		1、指针
		·变量的本质是给存储数据的内存地址起了一个好记的别名,比如定义一个变量 a:=10,这时直接通过
		  a这个变量来读取内存中保存10的这个值。在计算机底层a这个变量其实对应了一个内存地址
		·指针也是一个变量,但它是一种特殊的变量,它存储的数据不是一个普通的值,而是另一个变量的内存地址
	*/
	var a int = 10
	fmt.Printf("a的类型是:%T,a的值是:%v,a的地址是:%p\n", a, a, &a)
	//a的类型是:int,a的值是:10,a的地址是:0x1400009c018

	var p = &a
	fmt.Printf("p的类型是:%T,p的值是:%v\n", p, p)
	//p的类型是:*int,p的值是:0x1400000e0a8

	/*
		    2、指针地址和指针类型
			每个变量在运行时都会有一个地址,改地址代表变量在内存中的位置,Go中&字符放在变量前面
			对变量进行去地址操作。Go中值类型(int,float,bool,string,array,struct)都有对应
			指针类型,如*int,*int64,*string
	*/
	var b int = 100
	var b1 = &b
	fmt.Printf("b的地址:%p,b1的地址:%p,b的值:%v\n", b1, &b1, *b1)
	//b的地址:0x14000110030,b1的地址:0x14000116020,b的值:100

	/*
	 3、地址取值 *取值 可以取出改变量对应地址存储的值
	  通过 *修改的值会影响其指定的地址存储值,因为其本就是引用 类型
	*/
	var c = "hello golang"
	c1 := &c
	c2 := &c1
	fmt.Printf("c的地址:%p,c1的地址:%p,c2的地址:%p,c的值:%v\n", c1, c2, &c2, *c1)
	fmt.Printf("c的地址:%p,c1的地址:%p,c2的地址:%p,c的值:%v\n", &c, &c1, &c2, c)
	//	c的地址:0x1400008e020,c1的地址:0x140000a2028,c2的地址:0x140000a2030,c的值:hello golang
	// c的地址:0x1400008e020,c1的地址:0x140000a2028,c2的地址:0x140000a2030,c的值:hello golang
	*c1 = "hello js"
	fmt.Printf("c的新值:%v\n", c) //c的新值:hello js

}
15.2 声明指针
package main

import "fmt"

func fn1(x int) int {
	x = 10
	return x
}

func fn2(x *int) {
	*x = 20
}

func main() {
	var a int = 5
	fn1(5)
	fmt.Println(a) //5   修改的是函数内部的值,不影响外部的a
	fn2(&a)
	fmt.Println(a) //20  通过指针修改了外部的值

	/*
		声明指针
	*/
	/* 错误写法
	   var b *int
	   *b = 100
	   fmt.Println(*b)
	*/

	//new初始化
	var c = new(int)
	fmt.Printf("c的值:%v,类型:%T,指针指向的地址数据值是:%v\n", c, c, *c)
	//c的值:0x1400009c020,类型:*int,指针指向的地址数据值是:0

	var d *int
	d = new(int)
	*d = 100
	fmt.Println(*d) //100
}

11、声明变量的默认值 nil
当我们声明了一个变量,但还没有赋值时,golang会自动给变量赋值一个默认零值

bool       --  false
numbers    --  0
string     --  ""
pointers   --  nil
slices     --  nil
maps       --  nil
channels   --  nil
functions  --  nil
interfaces --  nil

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值