1 指针

package mainimport "fmt"func main() {// 指针问题// 1 指针:是存储另一个变量的内存地址的变量var num int = 199var p_num *int = &numfmt.Printf("this num is %v  this loac is %v\n", num, &num)fmt.Printf("this p_num is %v\n", p_num)// this num is 199  this loac is 0xc0000180a8// this p_num is 0xc0000180a8// p_num 的值就是 num的地址位 -- 指针变量存储的是另一个变量的地址// 2 获取变量的地址// 通过 & 来对变量操作来获取一个变量的地址// &变量名num_loc := 999fmt.Printf("this num_loc is %v\n", &num_loc)//  0xc0000a6090  是一个物理地址数据// 3 指针变量的定义// var 变量名 *类型type Stu struct {name stringage  int}num_a := 999name_a := "String"float_a := 199.77stu_a := Stu{name: "Zhang", age: 999}var num_a_p *intvar name_a_p *stringvar float_a_p *float64var stu_a_p *Stunum_a_p = &num_aname_a_p = &name_afloat_a_p = &float_astu_a_p = &stu_afmt.Printf("this num_a_p is %v\n", num_a_p)fmt.Printf("this name_a_p is %v\n", name_a_p)fmt.Printf("this float_a_p is %v\n", float_a_p)fmt.Printf("this stu_a_p is %v\n", stu_a_p)/*this num_a_p is 0xc0000a6098this name_a_p is 0xc000088220this float_a_p is 0xc0000a60a0this stu_a_p is &{Zhang 999}*/// 4 空指针// 声明后没有复值,没有默认值 是nil 可以判断是不是nilvar ptr *intif ptr == nil {fmt.Printf("this point is %v\n", ptr)// this point is <nil>}// 5 获取指针的值// 定义了指针变量,后面使用就要获取到这个变量的值// 格式: *指针变量var a_num int = 1009var ptr_a_num *intptr_a_num = &a_num // 取指针变量的值fmt.Printf("this point loc is %v get this value is %v\n", ptr_a_num, *ptr_a_num)// this point loc is 0xc0000180f8 get this value is 1009// ptr_a_num 的值是一个地址  *ptr_a_num 表示到ptr_a_num 值表示的地址上面,查该地址的值// 6 通过指针来改变一个变量的数值var b_num int = 100ptr_b_num := &b_numfmt.Printf("before value is %v\n", b_num)// 通过 ptr_b_num来修改哦b_num的值*ptr_b_num = 999fmt.Printf("after value is %v\n", b_num)// before value is 100// after value is 999// 通过 *指针变量 获取到b_num的值,然后 = 999 给这块地址复值为 999// 7 指针数据在函数中传递var get_num int = 999fmt.Printf("this get_num before is %v\n", get_num)get_pointer(&get_num)fmt.Printf("this get_num after is %v\n", get_num)// this get_num before is 999// this get_num after is 1111// 函数接收的是一个形参是一个 指针类型 val *int 入参数要是 这个数据的 地址&get_num// 使用数组在函数中传递的时候,尽量转成切片的形式,更加方便 因为切片数据在函数传递中就是一个地址传递// 8 指针的指针// 使用一个指针来保存一个指针的指针var pp_num int = 999var ptr_num *int = &pp_numvar ptr_ptr_num **int = &ptr_numfmt.Printf("this pp_num is %v loc is %v\n", pp_num, &pp_num)fmt.Printf("this ptr_num is %v loc is %v\n", ptr_num, &ptr_num)fmt.Printf("this ptr_ptr_num is %v\n", ptr_ptr_num)/*this pp_num is 999 loc is 0xc0000a60d0this ptr_num is 0xc0000a60d0 loc is 0xc0000ca020this ptr_ptr_num is 0xc0000ca020ptr_num 存的是 pp_num的地址, ptr_ptr_num 存的是ptr_num的地址*/// 取值 通过 ptr_ptr_numfmt.Printf("get ptr_num`s value is %v\n",*ptr_ptr_num)fmt.Printf("get pp_num`s value is %v\n",**ptr_ptr_num)// get ptr_num`s value is 0xc0000a60d0// get pp_num`s value is 999
}
func get_pointer(val *int) {*val = 1111
}

2 方法(method)

package mainimport "fmt"type Student struct {name  stringage   intmoney float64
}func main() {// 方法// 一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。// 所有给定类型的方法属于该类型的方法集/*1 方法只是一个函数,它带有一个特殊的接收器类型,它是在func关键字和方法名之间编写的。接收器可以是struct类型或非struct类型。接收方可以在方法内部访问。2 方法能给用户自定义的类型添加新的行为。它和函数的区别在于方法有一个接收者,给一个函数添加一个接收者,那么它就变成了方法。接收者可以是值接收者,也可以是指针接收者3 在调用方法的时候,值类型既可以调用值接收者的方法,也可以调用指针接收者的方法;指针类型既可以调用指针接收者的方法,也可以调用值接收者的方法。也就是说,不管方法的接收者是什么类型,该类型的值和指针都可以调用,不必严格符合接收者的类型。-- 相当与接受者相当一个"类" 这个方法是作用这个"类"上面的一个函数*/// 1 函数很方法的定义区别/*-- 方法 (被指定的行的接受者调用)func (接受收者 接受者类型)方法名(参数列表)(返回值列表){}-- 函数 (任意对象调用)func 函数名(参数列表)(返回值列表){}*/stu := Student{"feng", 10, 99.11}_, _ = stu.get_student(10001, "CCC")/*this is method get_student !stu.name is fengstu.age is 10stu.money is 99.11*/// 2 相同方法名  -- 相当于 类 的多肽/*- 不同的接收者(接收器)之间的方法名可以相同- 统一个接收器的 的方法名不能相同go 中没有类的概念,但是通过方法的接收之间的关系,来模拟类的用法相同名称的方法可以在不同的类型上定义,而具有相同名称的函数是不允许的。*/// 3 变量的作用域// 作用域为已声明标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围。/*- 局部变量- 全局变量- 形式参数*/// 4 指针作为收者stu1 := Student{"中国", 1111, 1111}fmt.Printf("use method before is %v\n", stu1)stu1.point_method()fmt.Printf("use methos after is %v\n", stu1)// (stu *Student)   使用指针类型 修改原来的数据// use method before is {中国 1111 1111}// in method change stu is &{china 999999 1}// use methos after is {china 999999 1}// (stu Student)    不适用指针类型,相当于元素复制一份,不修改原来的数据// use method before is {中国 1111 1111}// in method change stu is &{china 999999 1}// use methos after is {中国 1111 1111}// 5 metho的继承// method是可以继承的,如果匿名字段实现了一个method,那么包含这个匿名字段的struct也能调用该method// Human 结构图 Boss 和 Employee 都有 Human的匿名字段boss := Boss{Human{"Feng", 10, "iphone"}, 1}employee := Employee{Human{"Liu", 30, "xiaomi"}, 2}boss.sayHi()employee.sayHi()// 只能只能访问收者内部的字段名称的数据// this obj = &{Feng 10 iphone}  h.name = Feng  h.age = 10  h.phone = iphone// this obj = &{Liu 30 xiaomi}  h.name = Liu  h.age = 30  h.phone = xiaomi// 6 metho的重写boss.sayHi()employee.sayHi()//  1.Boss接收者 中含有Human的匿名字段,能够使用 Human的sayHi()方法//  2.Boss接收者再重新写一个sayHi()方法,使用boss调用的时候,默认是使用 boss的方法(b *Boss) sayHi()//  3.如果Boss 接收者没有指定的字段,向上查找 Human的字段,(存在继承关系时,按照就近原则,进行调用)// this is boss = &{{Feng 10 iphone} 1}  b.name = Feng h.phone = 10  h.leave = 1// this obj = &{Liu 30 xiaomi}  h.name = Liu  h.age = 30  h.phone = xiaomi}type Human struct {name  stringage   intphone string
}
type Boss struct {Humanleave int
}
type Employee struct {Humanleave int
}// 通过 Human实现 sayHi方法
func (h *Human) sayHi() {fmt.Printf("this obj = %v  h.name = %v  h.age = %v  h.phone = %v \n", h, h.name, h.age, h.phone)
}// 通过 Human实现 sayHi方法
func (b *Boss) sayHi() {fmt.Printf("this is boss = %v  b.name = %v h.phone = %v  h.leave = %v\n", b, b.name, b.age, b.leave)
}func (stu *Student) point_method() {stu.name = "china"stu.age = 999999stu.money = 1fmt.Printf("in method change stu is %v\n", stu)
}func (stu Student) get_student(a_num int, a_string string) (string, int) {// 在这里 (stu Student) 规定只能使用 Student 类型才能调用 stu 相当与 python类中的selffmt.Printf("this is method get_student !\n")fmt.Printf("stu.name is %v\n", stu.name)fmt.Printf("stu.age is %v\n", stu.age)fmt.Printf("stu.money is %v\n", stu.money)return a_string + " Hello World!", a_num + 99
}

interface 值相关

package mainimport "fmt"func main() {// 1 接口的值/*interface能存什么类型的数据1 interface 能存储实现这个interface的 任意类型数据ps: inter 是一个Men interface的变量 所以能存储 Human,Student,Employee*/// 创建 四个实例化 类型数据feng := Student{Human{name: "feng", age: 20}, "to_study!"}zhang := Student{Human{name: "zhang", age: 19}, "i love study!"}liu := Employee{Human{name: "liu", age: 56}, "go_to_work!"}zhao := Employee{Human{name: "zhao", age: 40}, "I love work!"}// 定义接口类型的变量var inter Maninter = fengfmt.Printf("this is feng =============\n")inter.call()inter.sayHi("go go go  to study!")// this is feng =============// this is call = {feng 20 }// I`M a student {{feng 20 } to_study!}  sayHi is go go go  to study!inter = liufmt.Printf("this is liu =============\n")inter.call()inter.sayHi("i i i i am working! ")// this is liu =============// this is call = {liu 56 }// this is call = {liu 56 }  and 11111 i i i i am working!// 接口类型的切片 春初不同的数据类型fmt.Printf("this is inter_slice =============\n")inter_slice := make([]Man, 3)inter_slice[0] = zhanginter_slice[1] = fenginter_slice[2] = zhaofor _, v := range inter_slice {v.call()}// this is inter_slice =============// this is call = {zhang 19 }// this is call = {feng 20 }// this is call = {zhao 40 }// 2 接口断言/*因为空接口 interface{}没有定义任何函数,因此 Go 中所有类型都实现了空接口。当一个函数的形参是interface{},那么在函数中,需要对形参进行断言,从而得到它的真实类型。判断某个接口是否被某一个类型实现 -- 接口的断言语法:// 安全断言目标类型的值,布尔参数 := 表达式.(目标类型)// 不安全断言目标类型的值 := 表达式.(目标类型)建议使用安全的断言 不然会发生恐慌 panic*/var inter_pic interface{} = new(Student)st, ok := inter_pic.(Student)fmt.Printf("data is %v real or not is %v %T\n", st, ok, st)// data is {{ 0 } } real is false main.Studentres, ok := inter.(Employee)fmt.Printf("data is %v real or not is %v %T\n", res, ok, res)// data is {{liu 56 } go_to_work!} real or not is true main.Employee
}// 定义一个接口
type Man interface {call()sayHi(txt string)
}// Human 接口
type Human struct {name  stringage   intphone string
}// 创建三个属性类type Student struct {Humantodo string
}type Employee struct {Humantodo string
}// 实现接口 重写接口
func (hu Human) call() {fmt.Printf("this is call = %v\n", hu)
}
func (hu Human) sayHi(txt string) {fmt.Printf("this is call = %v  and 11111 %v\n", hu, txt)
}// Student 重写这个方法
func (stu Student) sayHi(txt string) {fmt.Printf("I`M a student %v  sayHi is %v\n", stu, txt)
}

3 go的OOP编程

package mainimport "fmt"func main() {fmt.Printf("This is oop ========")// go 语言的OOP编程/*OOP:面向对象编程---go 不是一个纯粹的面向对象编程,go中没有类,但是可以直送结构体代替类的概念Go并没有提供类class,但是它提供了结构体struct,方法method,可以在结构体上添加。提供了捆绑数据和方法的行为,这些数据和方法与类类似。*/// 1 创建结构体和方法,并且给结构体跟函数绑定// 创建一个结构体对象stu := Student{"Feng", 99, 174.99, 3}ok := stu.introduce()fmt.Printf("is intrduce over %v!\n", ok)// This is oop ========Let`s Go To Introduce Me!// My name is Feng// My age is 99// My lenght is 174.99// My leavl is 3// is intrduce over true!// 2 使用 new() 函数代替java的构造函数stu1 := New("张", 80, 4, 15.2323)stu1.introduce()// Let`s Go To Introduce Me!// My name is 张// My age is 80// My lenght is 15.2323// My leavl is 4// 3 继承和派生  --// 创建一个子类 Teacherfmt.Printf("this is jicheng and Paisheng =============\n")t := &Teacher{&Person{"zhang", 30}, "codeing"}t.setAge(999) // 设置 父类 age = 999// t Teacher 是子类 同时也能访问 父类 setAge 方法  --- 方法也可以继承t.tell() // 调用自己的tell方法// 子类-------codeing// zhang---999t.Person.tell() // t 当前类型  t.Perosn 父类  t.Person.tell() 调用父类的方法// zhang---999fmt.Print("this is duotai in go =========\n")// 4 多肽  -- 不同的类型可以用相同名字的方法hw := Phone{"HuaWei", "QL9000"}xm := Phone{"XiaoMi", "XL8G2"}t1 := Tel{"台式电话", "有限上网"}goPlay(&hw)goPlay(&xm)// this is a Phone calling!// this is a Phone goNetting!// this is a Phone calling!// this is a Phone goNetting!goPlay(&t1)// this is a Tel calling!// this is a Tel goNetting!}
func goPlay(gp Play) {gp.call()gp.goNet()}type Play interface {call()goNet()
}
type Phone struct {name stringcode string
}func (p *Phone) call() {fmt.Printf("this is a Phone calling!\n")
}
func (p *Phone) goNet() {fmt.Printf("this is a Phone goNetting!\n")
}type Tel struct {name stringcode string
}func (tel *Tel) call() {fmt.Printf("this is a Tel calling!\n")
}
func (tel *Tel) goNet() {fmt.Printf("this is a Tel goNetting!\n")
}// 父类 Person
type Person struct {name stringage  int
}func (p *Person) tell() {fmt.Printf("%v---%v\n", p.name, p.age)
}
func (p *Person) setAge(age int) {p.age = age
}// 子类继承于Person类 Teacher
type Teacher struct {*Person        // 嵌套 Person类型 --- 继承Person类型 对应的属性和方法也继承了work    string // 子类自己的方法
}// 派生:课可以在子类中残生新的方法中重用 父类的一些方法和功能
func (t *Teacher) tell() {fmt.Printf("子类-------%v\n", t.work)t.Person.tell()
}// 结构体,相当于类
type Student struct {name   stringage    intlenght float64leavl  int
}// 创建方法并绑定结构体
func (stu Student) introduce() bool {// (stu Student) 相当于给这个方法绑定给 Student 类型fmt.Printf("Let`s Go To Introduce Me!\n")fmt.Printf("My name is %v\n", stu.name)fmt.Printf("My age is %v\n", stu.age)fmt.Printf("My lenght is %v\n", stu.lenght)fmt.Printf("My leavl is %v\n", stu.leavl)return true
}
func New(name string, age int, leavl int, lenght float64) Student {stu := Student{name, age, lenght, leavl}return stu
}

4 错误处理 (需要补充)

package mainimport ("errors""fmt""os"
)func main() {// 错误处理/*错误:可能出现问题的地方出现了问题,Ps:打开一个文件失败的情况,这种情况在人的预料之中异常:在不该出现的地方出现了问题,Ps:空指针异常,出人意料,错误是业务的一部分,异常不是。在狗眼中错误也是一种类型 。是错误用内置的error类型表示,就和int float64 intN string 一样错误值可以存储在变量中,冲函数中返回。*/// 1 第一个错误_, err := os.Open("../../../text.txt")if err == nil {fmt.Println("文件读取成功!")} else {fmt.Println(err)fmt.Println("文件读取失败!")}// err: open /text.txt: The system cannot find the file specified. -- 找不到读取的文件// 文件读取失败!// 2 表示错误类型/*// 自定义一个错误类型type error interface {Error() string}1 任何实现这个接口的 雷西那个 都可以作为一个错误来使用2 当打印错误的时候,会调用 Error()方法来实现 回去错误的描述*/// 2 断言 Error() 获取 PathError 结构的路径err1, ok := err.(*os.PathError)if ok {fmt.Printf("this Erroe Path is %v \n", err1.Path)// this Erroe Path is ../../../text.txt}// 3 自定义错误/*使用 errors包func New(text string) error {}fmt包:func Errorf(format string, a ...interface{}) error {}*/f, err2 := calData(2.3, 0)if err2 != nil {fmt.Println(err2)} else {fmt.Println(f)}// 被除数不能为 0 !_, err3 := calData2(2.3, 0)if err3 != nil {fmt.Println(err3)}// 这是在向 错误中添加一些东西 0// panic() 和 recover()/*这是两个内置的函数,是用来出发和终止异处理流程的函数panic()     主动抛出错误recover() 用于捕获opanic的错误内容panic  恐慌1 程序的调用2 程序运行中产生错误3 发生panic后,程序会从发生panic的位置立即返回,逐层直线法函数的 defer 语句,逐层打印函数的调用栈,直到最外层或者被recover捕获4 panic不但可以在函数正常流程中抛出,在defer逻辑里也可以再次调用panic或抛出panic。defer里面的panic能够被后续执行的defer捕获。5 recover用来捕获panic,阻止panic继续向上传递。recover()和defer一起使用,但是defer只有在后面的函数体内直接被掉用才能捕获panic来终止异常,否则返回nil,异常继续向外传递。*/}// 计算类
func calData(num1, num2 float64) (float64, error) {if num2 == 0 {return 0, fmt.Errorf("这是在向 错误中添加一些东西 %v",num2)}return num1 / num2, nil
}func calData2(num1, num2 float64) (float64, error) {if num2 == 0 {return 0.0, errors.New("被除数不能为 0 !")}return num1 / num2, nil
}

5 文件的相关操作

package mainimport ("fmt""os""path/filepath"
)func main() {// 关于文档的操作/*文件操作:1.路径:相对路径:relativeab.txt相对于当前工程绝对路径:absolute/Users/ruby/Documents/pro/a/aa.txt.当前目录..上一层2.创建文件夹,如果文件夹存在,创建失败os.MkDir(),创建一层os.MkDirAll(),可以创建多层3.创建文件,Create采用模式0666(任何人都可读写,不可执行)创建一个名为name的文件,如果文件已存在会截断它(为空文件)os.Create(),创建文件4.打开文件:让当前的程序,和指定的文件之间建立一个连接os.Open(filename)os.OpenFile(filename,mode,perm)5.关闭文件:程序和文件之间的链接断开。file.Close()5.删除文件或目录:慎用,慎用,再慎用os.Remove(),删除文件和空目录os.RemoveAll(),删除所有*/// 1 路径filePath1 := "C:/Users/FengHan7309/Desktop/vsCode/demo_001/doc.txt" // 绝对路径filePath2 := "doc.txt"                                              // 相对路径fmt.Println(filepath.IsAbs(filePath1))                              //truefmt.Println(filepath.IsAbs(filePath2))                              //false// 判断是否是绝对路径fmt.Println(filepath.Abs(filePath1))fmt.Println(filepath.Abs(filePath2))// 查找当前的绝对路径fmt.Println(filepath.Base(filePath1))// 最终的文件// 2 创建文件夹 os.MkDir() 创建单层文件夹   os.MkDirAll()// 路径 和 权限// err:=os.Mkdir("/Users/FengHan7309/Desktop/vsCode/demo_001/创建的单层文件夹",os.ModePerm)// if err!=nil{//    fmt.Println(err)// }// fmt.Println("创建单层文件夹成功!!!!!!!!!!!!!!")// err1:=os.MkdirAll("/Users/FengHan7309/Desktop/vsCode/demo_001/lv1/lv2lv3",os.ModePerm)// if err1!=nil{//    fmt.Println(err1)// }// fmt.Println("创建多层文件夹成功!!!!!!!!!!!!!!")// 3 创建文件 os.Create()// createFile, err := os.Create("/Users/FengHan7309/Desktop/vsCode/demo_001/创建的文件.txt")// if err != nil {//  fmt.Println(err)// }// fmt.Printf("创建的文件是:%v", createFile.Name())// // 创建的文件是:/Users/FengHan7309/Desktop/vsCode/demo_001/创建的文件.txt// 4 打开文件os.Open(文件名)   os.OpenFile(文件名,mode,perm)// file1,err:=os.Open("C:/Users/FengHan7309/Desktop/vsCode/demo_001/doc.txt") // 只读模式// if err!=nil {//     fmt.Println(err)// }// fmt.Printf("open the obj is %v\n",file1.Name())// // open the obj is &{0xc000004a00}f, err := os.OpenFile("C:/Users/FengHan7309/Desktop/vsCode/demo_001/doc.txt", os.O_RDONLY|os.O_WRONLY, os.ModePerm)if err != nil {fmt.Println(err)}fmt.Println(f) // &{0xc000004a00}// 5 关闭文件 os.Close()//file4.Close()//6.删除文件或文件夹://删除文件//err :=  os.Remove("/Users/ruby/Documents/pro/a/aa.txt")//if err != nil{//   fmt.Println("err:",err)// return//}//fmt.Println("删除文件成功。。")//删除目录// err := os.RemoveAll("/Users/ruby/Documents/pro/a/cc")// if err != nil {//  fmt.Println("err:", err)//    return// }// fmt.Println("删除目录成功。。")}

IO操作 很多–之后操作

package mainfunc main() {// I/O// 1 i/o 包/*io包中提供I/O原始操作的一系列接口。它主要包装了一些已有的实现,如 os 包中的那些,并将这些抽象成为实用性的功能和一些其他相关的接口。由于这些接口和原始的操作以不同的实现包装了低级操作,客户不应假定它们对于并行执行是安全的。在io包中最重要的是两个接口:Reader和Writer接口,首先来介绍这两个接口.Reader接口的定义,Read()方法用于读取数据。```gotype Reader interface {Read(p []byte) (n int, err error)}``````Read 将 len(p) 个字节读取到 p 中。它返回读取的字节数 n(0 <= n <= len(p))以及任何遇到的错误。即使 Read 返回的 n < len(p),它也会在调用过程中使用 p的全部作为暂存空间。若一些数据可用但不到 len(p) 个字节,Read 会照例返回可用的东西,而不是等待更多。当 Read 在成功读取 n > 0 个字节后遇到一个错误或 EOF 情况,它就会返回读取的字节数。它会从相同的调用中返回(非nil的)错误或从随后的调用中返回错误(和 n == 0)。这种一般情况的一个例子就是 Reader 在输入流结束时会返回一个非零的字节数,可能的返回不是 err == EOF 就是 err == nil。无论如何,下一个 Read 都应当返回 0, EOF。调用者应当总在考虑到错误 err 前处理 n > 0 的字节。这样做可以在读取一些字节,以及允许的 EOF 行为后正确地处理I/O错误。Read 的实现会阻止返回零字节的计数和一个 nil 错误,调用者应将这种情况视作空操作。```Writer接口的定义,Write()方法用于写出数据。```gotype Writer interface {Write(p []byte) (n int, err error)}``````Write 将 len(p) 个字节从 p 中写入到基本数据流中。它返回从 p 中被写入的字节数n(0 <= n <= len(p))以及任何遇到的引起写入提前停止的错误。若 Write 返回的n < len(p),它就必须返回一个非nil的错误。Write 不能修改此切片的数据,即便它是临时的。```Seeker接口的定义,封装了基本的 Seek 方法。```gotype Seeker interface {Seek(offset int64, whence int) (int64, error)}``````Seeker 用来移动数据的读写指针Seek 设置下一次读写操作的指针位置,每次的读写操作都是从指针位置开始的whence 的含义:如果 whence 为 0:表示从数据的开头开始移动指针如果 whence 为 1:表示从数据的当前指针位置开始移动指针如果 whence 为 2:表示从数据的尾部开始移动指针offset 是指针移动的偏移量返回移动后的指针位置和移动过程中遇到的任何错误```ReaderFrom接口的定义,封装了基本的 ReadFrom 方法。```gotype ReaderFrom interface {ReadFrom(r Reader) (n int64, err error)}``````ReadFrom 从 r 中读取数据到对象的数据流中直到 r 返回 EOF 或 r 出现读取错误为止返回值 n 是读取的字节数返回值 err 就是 r 的返回值 err```WriterTo接口的定义,封装了基本的 WriteTo 方法。```gotype WriterTo interface {WriteTo(w Writer) (n int64, err error)}``````WriterTo 将对象的数据流写入到 w 中直到对象的数据流全部写入完毕或遇到写入错误为止返回值 n 是写入的字节数返回值 err 就是 w 的返回值 err```定义ReaderAt接口,ReaderAt 接口封装了基本的 ReadAt 方法```gotype ReaderAt interface {ReadAt(p []byte, off int64) (n int, err error)}``````ReadAt 从对象数据流的 off 处读出数据到 p 中忽略数据的读写指针,从数据的起始位置偏移 off 处开始读取如果对象的数据流只有部分可用,不足以填满 p则 ReadAt 将等待所有数据可用之后,继续向 p 中写入直到将 p 填满后再返回在这点上 ReadAt 要比 Read 更严格返回读取的字节数 n 和读取时遇到的错误如果 n < len(p),则需要返回一个 err 值来说明为什么没有将 p 填满(比如 EOF)如果 n = len(p),而且对象的数据没有全部读完,则err 将返回 nil如果 n = len(p),而且对象的数据刚好全部读完,则err 将返回 EOF 或者 nil(不确定)```定义WriterAt接口,WriterAt 接口封装了基本的 WriteAt 方法```gotype WriterAt interface {WriteAt(p []byte, off int64) (n int, err error)}``````WriteAt 将 p 中的数据写入到对象数据流的 off 处忽略数据的读写指针,从数据的起始位置偏移 off 处开始写入返回写入的字节数和写入时遇到的错误如果 n < len(p),则必须返回一个 err 值来说明为什么没有将 p 完全写入```*/// 2 文件读写/*func (f *File) Read(b []byte) (n int, err error)//Read方法从f中读取最多len(b)字节数据并写入b。它返回读取的字节数和可能遇到的任何错误。文件终止标志是读取0个字节且返回值err为io.EOF。func (f *File) ReadAt(b []byte, off int64) (n int, err error)//ReadAt从指定的位置(相对于文件开始位置)读取len(b)字节数据并写入b。它返回读取的字节数和可能遇到的任何错误。当n<len(b)时,本方法总是会返回错误;如果是因为到达文件结尾,返回值err会是io.EOF。func (f *File) Write(b []byte) (n int, err error)//Write向文件中写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值n!=len(b),本方法会返回一个非nil的错误。func (f *File) WriteString(s string) (ret int, err error)//WriteString类似Write,但接受一个字符串参数。func (f *File) WriteAt(b []byte, off int64) (n int, err error)//WriteAt在指定的位置(相对于文件开始位置)写入len(b)字节数据。它返回写入的字节数和可能遇到的任何错误。如果返回值n!=len(b),本方法会返回一个非nil的错误。func (f *File) Seek(offset int64, whence int) (ret int64, err error)//Seek设置下一次读/写的位置。offset为相对偏移量,而whence决定相对位置:0为相对文件开头,1为相对当前位置,2为相对文件结尾。它返回新的偏移量(相对开头)和可能的错误。func (f *File) Sync() (err error)//Sync递交文件的当前内容进行稳定的存储。一般来说,这表示将文件系统的最近写入的数据在内存中的拷贝刷新到硬盘中稳定保存。*/
}

6 并发

package mainimport ("fmt""sync""time"
)var wg sync.WaitGroup // 定义一个调度队列func main() {// 协程 是轻量级的线程/*1 主线程线程结束后,协程才会被打断2 需要有效的阻塞机制语法:go 需要进行协程执行的函数*/// go func() {//    for i := 0; i < 10; i++ {//       time.Sleep(1 * time.Second)//       fmt.Printf("this is %v\n", i+1)//    }// }()// 直接运行 发现什么都没有打印 -- 函数执行的时候,主线程已经结束,所用协程没有执行// 下面阻塞一下主线程 -- 阻塞 5s主线程// time.Sleep(5*time.Second)// this is 1// this is 2// this is 3// this is 4// 在5s内 协程执行了 4 次// 协程的延时和阻塞 WaitGroup// 没人知道一个协程能走多久 引入 WaitGroup 模块,让他们 自己决定走多久/*var wg sync.WaitGroup   // 定义阻塞队列wg.Add(2)   // 添加调度任务数量wg.Done()   // 结束一个调度wg.Wait()  // 调度阻塞 当 wg中的数量位0后,恢复程序运行*/wg.Add(2) // 添加两个任务// 创建两个线程go printString("Let`s Go !")time.Sleep(200 * time.Millisecond)go printString("中国牛逼!")// 主线程进行阻塞 等待调度任务执行wg.Wait()fmt.Println("当前主程序执行完成!!!!")// Let`s Go !// Let`s Go !// 中国牛逼!// Let`s Go !// 中国牛逼!// Let`s Go !// 中国牛逼!// Let`s Go !// 中国牛逼!// 中国牛逼!// 当前主程序执行完成!!!!}
func printString(txt string) {for i := 0; i < 5; i++ {time.Sleep(150 * time.Millisecond)fmt.Println(txt)}wg.Done() // 一个调度结束后,销毁这个调度的任务
}
package mainimport ("fmt""sync"
)var sum = 0
var sumLock = 0
var wg sync.WaitGroup// 创建一个锁
var lock = sync.Mutex{}func main() {// 1 提出问题// 协程安全问题 -- 多个线程同时访问一个资源数据,造成的数据出问题// 创建两个线程来进行计算这个和是多少 目的是 200000wg.Add(2)fmt.Println("开始之前sum是:", sum)go addNum()go addNum()wg.Wait()fmt.Println("开始之前sum是:", sum)// 第一次// 开始之前sum是: 0// 开始之前sum是: 104217// 第二次// 开始之前sum是: 0// 开始之前sum是: 136070// 第三次// 开始之前sum是: 0// 开始之前sum是: 102082// 结果是不一定的 原因就是 两个协程同时去访问 了 sum 导致 sum只加了一次// 2 解决问题  --  给公共资源上锁-协程同时只能有一个执行fmt.Println("=============================")wg.Add(2)fmt.Println("开始之前sum是:", sumLock)go addNumLOck()go addNumLOck()wg.Wait()fmt.Println("开始之前sum是:", sumLock)// 第一次// 开始之前sum是: 0// 开始之前sum是: 200000// 第二次// 开始之前sum是: 0// 开始之前sum是: 200000
}
func addNum() {for i := 1; i <= 100000; i++ {sum++}wg.Done()}func addNumLOck() {// 给资源上锁lock.Lock()for i := 1; i <= 100000; i++ {sumLock++}// 给资源解锁lock.Unlock()wg.Done()}

7 通道

package mainimport ("fmt"
)func main() {// 通道 -- 让goroutine之前进行通讯/*1 每个通道 都有其相关的类型,该类型是数据允许传输的类型 通道的 零值位nil2 用于goroutine之间传输数据的3 都有自己相关联的数据类型4 数据传递chan <- data 数据传入的通道里data <- chan 数据从通道中传出5 阻塞发送数据:chan <- data,阻塞的,直到另一条goroutine,读取数据来解除阻塞读取数据:data <- chan,也是阻塞的。直到另一条goroutine,写出数据解除阻塞。6 本身channel就是同步的,意味着同一时间,只能有一条goroutine来操作。7 通道是goroutine之间的连接,所以通道的发送和接收必须处在不同的goroutine中。*/// 定义通道/*var 通道名 chan 数据类型通道名 = make(chan 数据类型)*/// var chan1 chan int// fmt.Println(chan1)                     // <nil>// fmt.Printf("this type is %T\n", chan1) // this type is chan int// 3 通道接收和发送数据var chan2 = make(chan int)go func() {data,ok := <-chan2fmt.Println("通道的状态", ok)fmt.Println("读取后的通道:", chan2)fmt.Println("读取的数据:", data)}()a := 99chan2 <- a   // 如果方在读取协程之前会报错,因为 发送的时候,会进行阻塞fmt.Println("接受数据后的通道:", chan2)// 通道的状态 true// 创建的 通道 其实是一个存储数据的地址// 读取后的通道: 0xc00001c180// 读取的数据: 99// 接受数据后的通道: 0xc00001c180// 3 关闭通道 close(通道名)}

学习go语言的一些笔记(三)相关推荐

  1. 《Go语言实战》笔记(三) | Go Doc 文档

    <Go语言实战>读书笔记,未完待续,欢迎关注公众号flysnow_org,第一时间看后续笔记. 对于协作开发或者代码共享来说,文档是一个可以帮助开发者快速了解以及使用这些代码的一个教程,文 ...

  2. 学习javascript语言精粹的笔记

    1.枚举: 用for in 语句来遍历一个对象中所有的属性名,该枚举过程将会列出所有的属性也包括涵数和方法,如果我们想过滤掉那些不想要的值,最为常用的过滤器为hasOwnProperty方法,以及使用 ...

  3. 学习 Go 语言(Golang)读书笔记

    go语言基本语法注意事项 rune类型,go语言独有,go字符本身采用unicode编码,rune相当于32int占用4个字节代表一个unicode字符. [-]int{1,2,3} 代表一个[3]i ...

  4. 学习C语言深入解剖笔记之关键字的秘密

    C语言关键字的秘密 重点说明: 1.if(略) 2.void ①C语言规定只有相同类型的指针才可以相互赋值 ②void*指针作为左值用于"接收"任意类型的指针 ③void*指针作为 ...

  5. JavaScript语言精粹--读书笔记三之replace()与正则

    今天有人问我repalce(),他那个题目很有意思.我也不会做,于是我就去查,结果发现就是最基础的知识的延伸. 所以啊最基础的知识才是很重要的,千万不能忽略,抓起JS就写代码完全不知到所以然,只知道写 ...

  6. python慕课笔记_MOOC python笔记(三) 序列容器:字符串、列表、元组

    Python Python开发 Python语言 MOOC python笔记(三) 序列容器:字符串.列表.元组 容器概念 容器是Python中的重要概念,分为有序与无序. 有序容器也称为序列类型容器 ...

  7. C 语言学习笔记(三):C 语言开发环境搭建

    文章目录 一.Windows 二.Linux 2.1 VMware Workstation Pro软件简介及安装 2.2 安装 Ubuntu 系统 2.2.1 Ubuntu 下载 2.2.2 安装 U ...

  8. GEE (Google Earth Engine)最基础代码学习笔记三

    GEE (Google Earth Engine)代码学习笔记三 本次学习核心为:将JavaScript objects and primitives放入Earth Engine 容器传到服务器,并处 ...

  9. MySQL学习笔记(三)查询

    写在前面:本篇为作者自学总结,学习内容为课堂所学和网络学习笔记汇总,对于内容引用部分在文中和文末注明. 文章仅供参考,如需深入了解,请查阅MySQL参考手册.附上下载链接: 链接:https://pa ...

最新文章

  1. 腾讯首席战略官詹姆斯: 从互联网信息的永久性和稀缺性看腾讯的投资逻辑
  2. cordova contacts测试
  3. java的开源项目哪里找,我想参加开源项目的开发,请问在网上去哪找这样的项目? 纯C语言的(非C++或JAVA)...
  4. 平台积分体系设计方案
  5. RabbitMQ从安装到使用
  6. android进程优先级的计算
  7. Angular 根据指定条件动态决定是否显示自定义的popup hover Component
  8. JavaME:Google静态地图API
  9. 串口与modem流量控制大全(2)
  10. 【Python】import自己的模块报错
  11. html画布画带百分比饼状图,echart.js如何画带百分比的饼状图
  12. flutter去掉输入框最大字数显示
  13. 锂电池荷电状态预测方法
  14. 从零搭建美团饿了么外卖红包CPS小程序教程
  15. python5_学习python5面向
  16. 3D打印切片软件--cura的二次开发(2)(界面分析与汉化)(修改版增加图片)
  17. 数字货币 2018年市值排行榜前100币种简介
  18. 微信里的APK链接接打不开怎么办?微信扫描二维码下载APK的解决方案
  19. Java - 什么时候用断言(assert)?
  20. oracle错误汇总

热门文章

  1. 十分钟了解绘图神器——Graphviz学习笔记
  2. Redux-前端开发者的福音
  3. CMock使用手册翻译
  4. 如何在手机上将图片转换为文字
  5. Sonatype Nexus3 搭建私有仓库
  6. 未来: 从Uber到Suber
  7. 聚苯硫醚离子液体|苯硼酸离子液体|聚缩醛离子液体|透明质酸离子液体
  8. 浪潮2020年Q1闪存存储领涨,出货量增速跃居中国第一
  9. 运营商大数据怎么获客的?一篇文章告诉你
  10. EMMC内存芯片和NAND区别