.go ->go build -> .exe -> 执行 ./exe | go run hello.go
行尾自动加;(体现简洁性)一行 < 80 字符;定义未使用,编译失败
var gog int ; var gog int = 2; var gog = 2; gog := 2
//基本数据类型
bool
string
int、int8、int16、int32、int64
uint、uint8、uint16、uint32、uint64、uintptr
byte // uint8 的别名
rune // int32 的别名 代表一个 Unicode 码
float32、float64
complex64、complex128
//派生数据类型
* []int struct pipline func slice interface map
why: 强类型语言
//整型之间转换
var a int8 var b int64 b = int64(a)
// 基本类型转string
1、var n1 float = 1.00 ;var s1 string = fmt.Sprintf("%f", n1)
2、var n1 int = 18; var s2 string = strconv.FormatFloat(n2,'f',9,64) //9 表示 小数保留9位,64 表示float64类型
// string 类型转 other
var s1 = "88" ;b, err = stronv.ParseInt (s1)
ParseFloat ParseBool; ParseUnit
//赋值
resume := func (num1 int , num2 int) int {
return num1 + num2
}
//赋值并执行
resume := func (num1 int , num2 int) int {
return num1 + num2
}(10,20)
// 赋值 给全局变量, 则全局有效
// 本质:是匿名函数,这个函数引入了外界的变量/参数(匿名函数+应用外界变量、参数=闭包)
func sum() func(num int) int {
var sum int = 0 // 变量 与 匿名函数 可以看成一个整体(函数类型的对象), 缓存在内存中,可以对其进行操作
return func (num int) int {
sum := sum + num
return sum
}
}
var sum = sum()
sum(5) = 5
sum(7) = 12
//使用场景:闭包可以保留上次引用的某个值,传入一次就可以反复使用了
func sum(num2 int) func(num int) int {
var sum int = 0 // 变量 与 匿名函数 可以看成一个整体(函数类型的对象), 缓存在内存中,可以对其进行操作
return func (num int) int {
sum := sum + num + num2
return sum
}
}
var sumF = sumFor(2)
//defer 后的语句压入栈中,等{} 执行完后,再执行defer语句 ****即使程序恐慌中断, 也会执行****
defer fmt.Println("num=", num1)// 会将值压入栈中,不会随着函数后面的变化而变化,
//使用场景, 关闭 一些资源, file 、connect (避免后面忘记关闭)
len(str)
[]rune(str) //切片
strconv.Atoi, Itoa//整型与string的转换
time.Format("2006/01/02 15/04/05")
num := new(int)
//内建哈数 recover
//采用 defer recover 捕获异常
defer func(){
err := recover()
if err!= nil {
fmt.Println(err)
}
}()
func main() {
err := testPanic()
if err != nil {
fmt.Println(err)
panic(err) //中断程序
}
fmt.Println("result")
}
func testPanic() error {
num1 := 10
num2 := 0
if num2 == 0 {
return errors.New("除数不能为0")
} else {
result := num1 / num2
fmt.Println(result)
return nil
}
}
//定义格式
var name [size]type ex: var nums [4]int
// 初始化
var arr1 [3]int = [3]int{3,6,9}//
var arr2 = [3]int{3,6,9}//
var arr2 = [...]int{3,6,9}//
var arr2 = [...]int{2:3,1:6,0:9}//
// 遍历
for ;;; {}
for key, value := range arr1 {}
//方式一 : 引用数组
var arr [3]int = [3]int{3,6,9}
slice := arr[:]
//方式二:make 内置函数 参数:type ,len, cap
slice := make([]int, 3, 3)
//PS : make底层创建一个数组,对外不可见,所以不可以直接操作这个数组,要通过slice去间接的访问各个元素,不可以直接对数组进行维护/操作
//方式三:定义一个切片 直接 指定具体数组 类似make
slice := []int{1,3,7}
slice := []int{}
//====================遍历=======================
for
for range
//===================append======================
var a []int
a = append(a, 1) // 追加1个元素
a = append(a, 1, 2, 3) // 追加多个元素, 手写解包方式
a = append(a, []int{1,2,3}...) // 追加一个切片, 切片需要解包
//===================copy 返回元素的数量==========
copy( destSlice []T, srcSlice []T) int
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{5, 4, 3}
copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中
copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
//================delete==========================
//从开头位置删除
a = []int{1, 2, 3}
a = a[N:] // 删除开头N个元素
a = []int{1, 2, 3}
a = append(a[:0], a[N:]...) // 删除开头N个元素(a[0]地址不变)
//还可以用 copy() 函数来删除开头的元素:
a = []int{1, 2, 3}
a = a[:copy(a, a[N:])] // 删除开头N个元素
//从中间位置删除
//对于删除中间的元素,需要对剩余的元素进行一次整体挪动,同样可以用 append 或 copy 原地完成:
a = []int{1, 2, 3, ...}
a = append(a[:i], a[i+1:]...) // 删除中间1个元素
a = append(a[:i], a[i+N:]...) // 删除中间N个元素
a = a[:i+copy(a[i:], a[i+1:])] // 删除中间1个元素
a = a[:i+copy(a[i:], a[i+N:])] // 删除中间N个元素
//从尾部删除
a = []int{1, 2, 3}
a = a[:len(a)-1] // 删除尾部1个元素
a = a[:len(a)-N] // 删除尾部N个元素
//删除开头的元素和删除尾部的元素都可以认为是删除中间元素操作的特殊情况, 开头
//1、定义:var name map[keytype]valuetype
var mymap map[int]string | var mymap = make(map[int]string)
//2、初始化:
mymap = map[int]string{20:"hello",30:"go"} | a := make(map[int]string) == a := map[int]string{}
//注意:可以使用 make(),但不能使用 new() 来构造 map,如果错误的使用 new() 分配了一个引用对象,会获得一个空引用的指针,相当于声明了一个未初始化的变量并且取了它的地址:
//3、map 容量
map2 := make(map[string]float, 100)
//当 map 增长到容量上限的时候,如果再增加新的 key-value,map 的大小会自动加 1,所以出于性能的考虑,对于大的 map 或者会快速扩张的 map,即使只是大概知道容量,也最好先标明。
//4、用切片作为 map 的值(一个key=>n个value), 父进程(pid 为整形)作为 key,所有的子进程(以所有子进程的 pid 组成的切片)作为 value
mp1 := make(map[int][]int)
mp2 := make(map[int]*[]int)
myMap := make(map[string]int)
myMap["route"] = 66
myMap["brazil"] = 4
myMap["china"] = 960
//删除
delete(scene, "route")
//遍历
for k, v := range myMap {
fmt.Println(k, v)
}
//清空 无 可以 make一个新的空的map
// 并发读写 线程不安全, 运行代码会报错 fatal error: concurrent map read and map write
// 加锁效率低
//Go语言在 1.9 版本中提供了一种效率较高的并发安全的 sync.Map,sync.Map 和 map 不同,不是以语言原生形态提供,而是在 sync 包下的特殊结构。
var scene sync.Map
// 将键值对保存到sync.Map
scene.Store("london", 100)
// 从sync.Map中根据键取值
fmt.Println(scene.Load("london"))
// 根据键删除对应的键值对
scene.Delete("london")
// 遍历所有sync.Map中的键值对
scene.Range(func(k, v interface{}) bool {
fmt.Println("iterate:", k, v)
//初始化列表
//1) 通过 container/list 包的 New() 函数初始化 list
a := list.New()
//2) 通过 var 关键字声明初始化 list
var a list.List
//nil 是 map、slice、pointer、channel、func、interface 的零值 零值是Go语言中变量在声明之后但是未初始化被赋予的该类型的一个默认值。
var s1 []int
fmt.Println(s1 == nil)
PS D:\code> go run .\main.go
true
不同类型的 nil 值占用的内存大小可能是不一样的
var p *struct{}
fmt.Println( unsafe.Sizeof( p ) ) // 8
var s []int
fmt.Println( unsafe.Sizeof( s ) ) // 24
引入:方便统一管理变量
定义:type student struct {name string Age int}
使用:
var stu student
var stu = student{"wh":10}
var stu *student = new(student) stu.name = "who";stu.age = 10
var stu *student = &student{"go":10}
结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)
type Student struct {
Age int
}
type Person struct {
Age int
}
func main(){
var s Student = Student{10}
var p Person = Person{10}
s = Student(p)
fmt.Println(s)
fmt.Println(p)
}
结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以强转
type Student struct {
Age int
}
type Stu Student
func main(){
var s1 Student = Student{19}
var s2 Stu = Stu{19}
s1 = Student(s2)
fmt.Println(s1)
fmt.Println(s2)
}
//(1)结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式
type Person struct {
Name string
}
func (p *Person) test() {
(*p).Name = "露露" // (*p)=>p
fmt.Println((*p).Name)//(*p)=>p
}
func main() {
var p Person
(&p).test() //(&p)=>p
}
底层编译器做了优化,底层会自动帮我们加上 & *
//(2)如果一个类型实现了String()这个方法,那么fmt.Println默认会调用这个变量的String()进行输出
以后定义结构体的话,常定义String()作为输出结构体信息的方法,在fmt.Println会自动调用
//传入地址,如果绑定了String方法就会自动调用
fmt.Println(&stu)
//1、调用方式
//2、方法需要绑定类型
//3、函数 强参数类型(参数类型一致);对于方法来说,接收者为值类型,可以传入指针类型,接受者为指针类型,可以传入值类型。
func method01(s Student){
fmt.Println(s.Name)
}
func (s Student) test01(){
fmt.Println(s.Name)
}
func main(){
var s Student = Student{"丽丽"}
method01(s)
//method01(&s)错误
s.test01()
(&s).test01()//虽然用指针类型调用,但是传递还是按照值传递的形式(把sudent 对象拷贝一份传过去)
}
// 1、按顺序
var s1 Student = Student{"小李",19}
// 2、按类型
var s2 Student = Student{name:"小李",age:19}
//结构体首字母小写,跨包访问没问题:---》工厂模式(通过一个方法返回 实例对象)
// 封装:把抽象出来的字段和对字段的操作 封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作方法才能对字段操作
why: 隐藏实现细节;可以对数据进行验证
how: 字段,结构体首字母小写;结构体所在包提供一个工厂模式函数首字母大写;提供 Set,Get 方法对属性判断赋值取值等操作
// 继承:多个结构体存在相同的属性和方法时,抽象出公共的部分,其他结构体通过嵌套此结构的匿名结构体,即可获得公共的属性和方法 ,从而实现继承的特性
why: 提高代码的复用性,扩展性
how: type C struct{A}
注意事项:
组合模式:字段的类型为 A 结构体, 与嵌套有区别 type C struct{name A}
嵌入的结构体类别:自定义类型;基本数据类型;指针 type C struct{A int *B}
Golang中支持多继承: type C struct{A int *B}
结构体可以使用嵌套匿名结构体所有的字段和方法,编译器采用就近访问原则访问
// 接口:定义规则、定义规范,定义某种能力
总结:只要实现全部方法即可,和实际接口耦合性很低,比Java松散得多
接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量
只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型
接口可以继承多个接口,当时此接口时还需实现全部继承的接口方法
// 多态:变量(实例)具有多种形态 如:func greet(s SayHello){s.sayHello()} 一个函数可以接受任意实现了SayHello能力的实例对象,
s 通过上下文来时识别具体是什么类型的实例,就体现出多态
how: 接口参数 (s SayHello)或者是多态参数;多态数组 [3]SayHello{A,B,C}
// 断言:判断是否是该类型的变量 value, ok := element.(T) value变量的值,ok bool,element是interface变量,T是断言的类型。
switch s.(type){//type属于go中的一个关键字,固定写法
case Chinese:
ch := s.(Chinese)
ch.niuYangGe()
case American:
am := s.(American)
am.disco()
}
//文件复制
func main(){
//定义源文件:
file1Path := "d:/Demo.txt"
//定义目标文件:
file2Path := "d:/Demo2.txt"
//对文件进行读取:
content,err := ioutil.ReadFile(file1Path)
if err != nil {
fmt.Println("读取有问题!")
return
}
//写出文件:
err = ioutil.WriteFile(file2Path,content,0666)
if err != nil {
fmt.Println("写出失败!")
}
}
//带缓存读写
//打开文件:
file,err := os.Open("d:/Test.txt")
if err != nil {//打开失败
fmt.Println("文件打开失败,err=",err)
}
//当函数退出时,让file关闭,防止内存泄露:
defer file.Close()
//创建一个流:
reader := bufio.NewReader(file)
//读取操作:
for {
str,err := reader.ReadString('\n')//读取到一个换行就结束
if err == io.EOF {//io.EOF 表示已经读取到文件的结尾
break
}
//如果没有读取到文件结尾的话,就正常输出文件内容即可:
fmt.Println(str)
}
//写入文件操作:
//打开文件:
file , err := os.OpenFile("d:/Demo.txt",os.O_RDWR | os.O_APPEND | os.O_CREATE,0666)
if err != nil {//文件打开失败
fmt.Printf("打开文件失败",err)
return
}
//及时将文件关闭:
defer file.Close()
//写入文件操作:---》IO流---》缓冲输出流(带缓冲区)
writer := bufio.NewWriter(file)
for i := 0; i < 10;i++ {
writer.WriteString("你好 马士兵\n")
}
//流带缓冲区,刷新数据--->真正写入文件中:
writer.Flush()
s :=os.FileMode(0666).String()
fmt.Println(s)
//1、WaitGroup:解决主线程在子协程结束后自动结束
//定义一个变量:
var totalNum int
var wg sync.WaitGroup //只定义无需赋值
//加入互斥锁:
var lock sync.Mutex
func add(){
defer wg.Done()
for i := 0 ;i < 100000;i++{
//加锁
lock.Lock()
totalNum = totalNum + 1
//解锁:
lock.Unlock()
}
}
func sub(){
defer wg.Done()
for i := 0 ;i < 100000;i++{
//加锁
lock.Lock()
totalNum = totalNum - 1
//解锁:
lock.Unlock()
}
}
func main(){
wg.Add(2)
//启动协程
go add()
go sub()
wg.Wait()
fmt.Println(totalNum)
}
//2、sync.Mutex: 解决互斥问题
//3、RWMutex(读写锁):在读的时候,数据之间不产生影响, 写和读之间才会产生影响(提高效率)
// channel : 数据结构-队列;先进先出;自身线程安全
定义:var intchan chan int *** var intchan chan<- int <-chan 只读只写
初始化:intchan = make(chan,int,3)
使用:写 intchan<- 8 取 a := <-intchan
关闭:close(intchan)
注意事项:应用类型;空时再取就会报错,满时再写会报错
for range 遍历,遍历前要关闭chan (会报错,deadlock)
阻塞:当管道只写入数据,没有读取,就会出现阻塞;写的快,读的满(管道读写频率不一致),不会出现阻塞问题:
多路复用:select
case后面必须进行的是io操作,不能是等值,随机去选择一个io操作
select{
case v := <-intChan :
fmt.Println("intChan:",v)
case v := <-stringChan :
fmt.Println("stringChan:",v)
default:
fmt.Println("防止select被阻塞")
}
**** 多个协程工作,其中一个协程出现panic,导致程序崩溃
解决办法:利用refer+recover捕获panic进行处理,即使协程出现问题,主线程仍然不受影响可以继续执行。
defer func(){
err := recover()
if err != nil{
fmt.Println("devide()出现错误:",err)
}
}()
package main
import(
"fmt"
"net" //所需的网络编程全部都在net包下
"bufio"
"os"
)
func main(){
//打印:
fmt.Println("客服端启动。。")
//调用Dial函数:参数需要指定tcp协议,需要指定服务器端的IP+PORT
conn,err := net.Dial("tcp","127.0.0.1:8888")
if err != nil {//连接失败
fmt.Println("客户端连接失败:err:",err)
return
}
fmt.Println("连接成功,conn:",conn)
//通过客户端发送单行数据,然后退出:
reader := bufio.NewReader(os.Stdin)//os.Stdin代表终端标准输入
//从终端读取一行用户输入的信息:
str,err := reader.ReadString('\n')
if err != nil {
fmt.Println("终端输入失败,err:",err)
}
//将str数据发送给服务器:
n,err := conn.Write([]byte(str))
if err != nil{
fmt.Println("连接失败,err:",err)
}
fmt.Printf("终端数据通过客户端发送成功,一共发送了%d字节的数据,并退出",n)
}
package main
import(
"fmt"
"net" //所需的网络编程全部都在net包下
)
func process(conn net.Conn){
//连接用完一定要关闭:
defer conn.Close()
for{
//创建一个切片,准备:将读取的数据放入切片:
buf := make([]byte,1024)
//从conn连接中读取数据:
n,err := conn.Read(buf)
if err != nil{
return
}
//将读取内容在服务器端输出:
fmt.Println(string(buf[0:n]))
}
}
func main(){
//打印:
fmt.Println("服务器端启动了。。")
//进行监听:需要指定服务器端TCP协议,服务器端的IP+PORT
listen,err := net.Listen("tcp","127.0.0.1:8888")
if err != nil{//监听失败
fmt.Println("监听失败,err:",err)
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不一样的
}
}
//1、 基本类型
//利用一个函数,函数的参数定义为空接口:
func testReflect(i interface{}){//空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为我们可以把任何一个变量赋给空接口。
//1.调用TypeOf函数,返回reflect.Type类型数据:
reType := reflect.TypeOf(i)
fmt.Println("reType:",reType)
fmt.Printf("reType的具体类型是:%T",reType)
//2.调用ValueOf函数,返回reflect.Value类型数据:
reValue :=reflect.ValueOf(i)
fmt.Println("reValue:",reValue)
fmt.Printf("reValue的具体类型是:%T",reValue)
//num1 := 100
//如果真想获取reValue的数值,要调用Int()方法:返回v持有的有符号整数
num2 := 80 + reValue.Int()
fmt.Println(num2)
//reValue转成空接口:
i2 := reValue.Interface()
//类型断言:
n := i2.(int)
n2 := n + 30
fmt.Println(n2)
}
func main(){
var num int = 100
testReflect(num)
}
//1、 对结构体类型进行反射
//利用一个函数,函数的参数定义为空接口:
func testReflect(i interface{}){//空接口没有任何方法,所以可以理解为所有类型都实现了空接口,也可以理解为我们可以把任何一个变量赋给空接口。
//1.调用TypeOf函数,返回reflect.Type类型数据:
reType := reflect.TypeOf(i)
fmt.Println("reType:",reType)
fmt.Printf("reType的具体类型是:%T",reType)
//2.调用ValueOf函数,返回reflect.Value类型数据:
reValue :=reflect.ValueOf(i)
fmt.Println("reValue:",reValue)
fmt.Printf("reValue的具体类型是:%T",reValue)
//reValue转成空接口:
i2 := reValue.Interface()
//类型断言:
n,flag := i2.(Student)
if flag == true {//断言成功
fmt.Printf("学生的名字是:%v,学生的年龄是:%v",n.Name,n.Age)
}
}
//定义学生结构体:
type Student struct{
Name string
Age int
}
func main(){
stu := Student{
Name : "丽丽",
Age : 18,
}
testReflect(stu)
}
// 3、获取变量的类别
(1)reflect.Type.Kind()
(2)reflect.Value.Kind()
Type是类型, Kind是类别,Type和Kind 可能是相同的,也可能是不同的.
比如:var num int = 10 num的Type是int , Kind也是int
比如:var stu Studentstu的 Type是 pkg1.Student , Kind是struct
// 4、修改基本数据类型的值:
var num int = 100
testReflect(&num) //传入指针地址
//通过SetInt()来改变值:
reValue.Elem().SetInt(40)
package main
import(
"fmt"
"reflect"
)
//定义一个结构体:
type Student struct{
Name string
Age 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 = name
s.Age = age
}
//定义函数操作结构体进行反射操作:
func TestStudentStruct(a interface{}){
//a转成reflect.Value类型:
val := reflect.ValueOf(a)、、
fmt.Println(val)
//通过reflect.Value类型操作结构体内部的字段:
n1 := val.NumField()
fmt.Println(n1)
//遍历-获取具体的字段:
for i := 0; i < n1;i++{
fmt.Printf("第%d个字段的值是:%v",i,val.Field(i))
}
fmt.Println()
//通过reflect.Value类型操作结构体内部的方法:
n2 := val.NumMethod()
fmt.Println(n2)
//调用CPrint()方法:
//调用方法,方法的首字母必须大写才能有对应的反射的访问权限
//方法的顺序按照ASCII的顺序排列的,a,b,c,,,,,,索引:0,1,2,,,,,
val.Method(2).Call(nil)
//调用AGetSum方法:
//定义Value的切片:
var params []reflect.Value
params = 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)
}
package main
import(
"fmt"
"reflect"
)
//定义一个结构体:
type Student struct{
Name string
Age 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 = name
s.Age = age
}
//定义函数操作结构体进行反射操作:
func TestStudentStruct(a interface{}){
//a转成reflect.Value类型:
val := reflect.ValueOf(a)
fmt.Println(val)
n := val.Elem().NumField()
fmt.Println(n)
//修改字段的值:
val.Elem().Field(0).SetString("张三")
}
func main(){
//定义结构体具体的实例:
s := Student{
Name : "丽丽",
Age : 18,
}
//调用TestStudentStruct:
TestStudentStruct(&s)
fmt.Println(s)
}