字符串、指针、结构体、方法、接口、并发编程
1 字符串
package main // 字符串 func main() { // 1 定义字符串 //var s = "中alqz" // 2 字符串可以按下标取值,不能改 //s[0]=98 //fmt.Println(s[0]) // 取字节,是个数字 //fmt.Println(s[3]) //fmt.Printf("%T\n", s[3]) //uint8 类型 byte 取字符才是int32也就是rune // 3 循环字符串 //for _, v := range s { // fmt.Println(string(v)) //字符 //} //for i := 0; i < len(s); i++ { // fmt.Println(string(s[i])) // 字节 //} // 4 字符串长度 //fmt.Println(len(s)) // 7 字节长度 //fmt.Println(utf8.RuneCountInString(s)) // 5 个字符 // 5 字符串是由字节或字符切片构成的 // 5.1 字符串和字节切片相互转化 //var s []byte = []byte{99, 97, 98, 'a'} //fmt.Println(s) // 字节切片,转成字符串 //fmt.Println(string(s)) //s := "abac" // 把字符串转成字节切片 //var sss []byte = []byte(s) //fmt.Println(sss) //5.2 字符切片和字符串相互转化 //var s []rune = []rune{99, 97, 98, 'a', '你', '好', 20013} //fmt.Println(string(s)) //s := "lqz中国" //fmt.Println([]rune(s)) //22269 //s := "你好中国" //fmt.Println(s) //r := []rune(s) //r[0] = 22269 //fmt.Println(string(r)) }
2 指针
# 指针:指针是一种存储变量内存地址的【变量】 # 三句话背过 1 定义变量时,类型前加 * *int *string *[3]int ,表示类型,指向这个类型的指针类型 2 在变量前加 & 表示取这个变量的地址 3 在指针变量前加 * ,表示解引用,反解出内存地址指向的值
package main import "fmt" func main() { // 指针是引用类型 // 1 定义指针 //var p *int //fmt.Println(p) //<nil> // 2 定义并赋初值 //var a int = 10 //var p *int = &a //fmt.Println(p) // 3 指针的解引用 ---》 //fmt.Println(*p) // 4 取指针变量的地址 //var a int = 10 //var p *int = &a //var p1 **int = &p // 取p的地址 //fmt.Println(p) //fmt.Println(p1) //var p2 ***int = &p1 //fmt.Println(p2) //// 解引用 ////fmt.Println(*p2) //fmt.Println(**p2) //fmt.Println(***p2) // 5 向函数传递指针类型参数 //var a int = 10 ////test1(a) ////fmt.Println(a) //var p *int = &a //test2(p) //fmt.Println(a) // 6 不要向函数传递 【数组的指针】,而应该使用切片 //var a [3]int //var p *[3]int = &a //fmt.Println(p) // &[0 0 0] //test4(p) //fmt.Println(a) ////test5(p) // //test6(a[:]) // 7 数组指针:数组的指针 指针数组: 数组里面的值是指针类型 // 数组的指针 //var a=[3]int{4,5,6} //var p *[3]int = &a //var p *[3]int = &[3]int{4, 5, 6} //fmt.Println(p) //指针数组 //var p1 [3]*int //var a, b, c int = 9, 4, 6 //p1[0] = &a //p1[1] = &b //p1[2] = &c //fmt.Println(p1) //fmt.Println(*(p1[0])) // 8 指针不允许运算 //var p *[3]int = &[3]int{4, 5, 6} //fmt.Println(p) //fmt.Println(p++) } func test1(a int) { a = 99 fmt.Println(a) } func test2(p *int) { *p = 99 fmt.Println(*p) } func test3(a [3]int) { a[0] = 99 fmt.Println(a) } func test4(a *[3]int) { //(*a)[0] = 99 a[0] = 99 // 可以简写成,等同于上面 fmt.Println(a) } func test5(a *[4]int) { //(*a)[0] = 99 a[0] = 99 // 可以简写成,等同于上面 fmt.Println(a) } func test6(a []int) { a[0] = 99 fmt.Println(a) }
3 结构体
# go 的面向对象,go中没有面向对象的语法(class),但有面向对象概念(继承封装多态) #什么是结构体(就是类的概念:类是若干方法和属性的集合) 结构体是用户定义的【类型】,表示若干个字段(Field)的集合
package main import ( "fmt" ) // 1 定义一个结构体体,定义个人结构体 type Person struct { name string age int address string hobby Hobby } //type Hobby struct { // hobbyName string // hobbyId int //} // 9 匿名字段 type Hobby struct { string int } func main() { // 2 使用结构体。定义结构体变量 //var p Person = Person{name: "lqz", age: 19, address: "上海"} // 3 结构体属性取值,赋值 //p.name = "彭于晏" //fmt.Println(p) //fmt.Println(p.age) // 4 结构体是值类型--->不需要初始化,就有默认值,当参数传入函数中,修改,不会影响原来的,想修改原来的,要取地址 //var p Person //p.name = "lqz" //fmt.Println(p.name) // 5 匿名结构体 ---》定义在函数内部,结构体没名字---》只用一次 --->没有名字,没有type就是匿名结构体 //var cat = struct { // name string // age int //}{age: 19} //fmt.Println(cat.name) //fmt.Println(cat.age) // 6 结构体初始化 //var p Person //var p Person = Person{name: "lqz", age: 19, address: "上海"} //var p Person = Person{name: "lqz"} // 没传的就是默认值 //var p Person = Person{"lqz",19,"上海"} //var p Person = Person{"lqz"} //var p Person = Person{} // 7 结构体零值 ---》值类型,有默认值,不初始化也行 //var p Person ////fmt.Println(p) ////test7(p) ////fmt.Println(p) //test8(&p) //fmt.Println(p) // 8 通过 . 访问和修改,结构体字段 (在包内,一定要导出) //d := entity.Dog{Name: "小狗"} //fmt.Println(d.Name) //fmt.Println(d.Age) // 9 匿名字段 初始化 匿名字段,类型名就是字段名 //var h Hobby = Hobby{"篮球", 100} //var h Hobby = Hobby{string: "篮球", int: 19} //fmt.Println(h) //fmt.Println(h.string) //fmt.Println(h.int) // 10 结构体嵌套 //var p =Person{"lqz",19,"上海",Hobby{"篮球",100}} //var p = Person{name: "lqz", age: 19, hobby: Hobby{string: "乒乓球", int: 1002}} //fmt.Println(p.name) //fmt.Println(p.hobby.string) // 11 结构体嵌套+匿名字段,玩点高级的---》面向对象的继承 type Animal struct { age int name string } type Dog struct { name string Animal // 结构体嵌套+匿名字段 提升字段,Animal中的字段,可以直接在dog对象中使用---》相当于Dog继承了Animal,dog可以直接使用Animal的属性 } //var d = Dog{"小野狗", Animal{age: 19}} //fmt.Println(d.name) //fmt.Println(d.Animal.age) //// 提升字段 //fmt.Println(d.age) //d.age = 22 //fmt.Println(d.Animal.age) var d = Dog{"小野狗", Animal{age: 19, name: "动物"}} // 提升字段,有冲突,用自己的,如果要取到父类的,需要指名道姓 fmt.Println(d.name) fmt.Println(d.Animal.name) } func test7(p Person) { p.name = "xxx" fmt.Println(p) } func test8(p *Person) { //(*p).name = "xxx" p.name = "xxx" // 简写成 fmt.Println(p) }
4 方法
# 方法和函数? 方法可以自动传值 # 方法其实就是一个函数,在 func 这个关键字和方法名中间加入了一个特殊的接收器类型。接收器可以是结构体类型或者是非结构体类型。接收器是可以在方法的内部访问的 # 方法是绑定给结构体的,结构体有了一系列字段,又有了方法 ,那他就是类的概念了
package main import "fmt" // 方法 type Dog struct { name string age int Wife } type Wife struct { wifeName string } func (w Wife) printName() { fmt.Println(w.wifeName) } func (d Dog) printName() { fmt.Println(d.name) } // 给结构体绑定一个方法 func (d Dog) speak() { fmt.Println(d.name, "在旺旺叫") } func (d Dog) run() { fmt.Println(d.name, "在走路") } // 值类型接收器 //func (d Dog) changeName(name string) { // d.name = name // fmt.Println(d) // 改了 //} // 指针类型接收器 func (d *Dog) changeName(name string) { d.name = name fmt.Println(d) // 改了 } // 函数 // // func speak(d Dog) { // fmt.Println(d.name, "在旺旺叫") // // } //func main() { // 1 使用方法 //var dog = Dog{"小榔头", 12} //dog.speak() //dog.run() // 2 为什么有了函数还要方法 方法是绑定给结构体的,可以自动传值,函数有几个值就要传几个值 //speak(dog) //dog.speak() // 3 值类型接收器和指针类型接收器 //var dog = Dog{"小榔头", 12} //dog.changeName("小狼狗") //fmt.Println(dog.name) // 4 想修改外部的对象使用指针类型接收器,如果值很占内存,也可以使用指针类型接收器----》python中的self其实就是指针 // 5 匿名字段的方法,同理,也会提升 //var dog = Dog{"小榔头", 18, Wife{"小母狗"}} //dog.printName() //dog.Wife.printName() // 6 在方法中使用值接收器 与 在函数中使用值参数 //var cat =Cat{"小野猫"} //cat.speak() //speak(cat) // 使用指针 //var cat *Cat = &Cat{"小野猫"} //cat.speak() // 值可以调用,指针也可以调用 ////speak(cat) // 函数,参数是什么类型,就必须传什么类型 //cat.changeName("xx") // 方法中能不能改值,跟谁调用没关系,跟接收器类型有关系,只要是指针类型接收器,都能改,只要是值类型接收器,都不改 //fmt.Println(cat) // 7 在方法中使用指针接收器 与 在函数中使用指针参数 //var cat = Cat{"小野猫"} //cat.changeName("xxx") //fmt.Println(cat) //changeName(&cat,"xxx") //var cat = &Cat{"小野猫"} //cat.changeName("xxx") //fmt.Println(cat) //changeName(cat, "xxx") // 1 方法可以值来调,也可以指针来调用 // 2 如果想改结构体对象的属性,必须用指针类型接收器来改 // 8 非结构体上的方法 //} // 6 在方法中使用值接收器 与 在函数中使用值参数 //type Cat struct { // name string //} // //func (c Cat) speak() { // fmt.Println(c.name, "喵喵叫") //} //func (c Cat) changeName(name string) { // c.name = name //} //func speak(c Cat) { // fmt.Println(c.name, "喵喵叫") //} // 7 在方法中使用指针接收器 与 在函数中使用指针参数 type Cat struct { name string } func (c *Cat) changeName(name string) { c.name = name } func changeName(c *Cat, name string) { c.name = name } // 8 非结构体上的方法 int 内置的类型是不能绑定方法的,但是自定义的类型,可以绑定方法 //var a int =10 //a.Add() //fmt.Println(a) // 11 type MyInt int func (i *MyInt) Add() { (*i)++ } func main() { var i MyInt = 10 i.Add() i.Add() i.Add() i.Add() fmt.Println(i) }
5 接口
# 在面向对象的领域里,接口一般这样定义:接口定义一个对象的行为。接口只指定了对象应该做什么,至于如何实现这个行为(即实现细节),则由对象本身去确定。 class Animal: def sepak(): pass def run(): pass class Dog(Animal): def sepak(): print('ss') def run(): print('ss') class Cat(Animal): def sepak(): print('ss') def run(): print('ss') #在 Go 语言中,接口就是方法的集合。当一个类型实现了接口中的所有方法,我们称它实现了该接口。这与面向对象编程(OOP)的说法很类似。接口指定了一个类型应该具有的方法,并由该类型决定如何实现这些方法 # GO也是鸭子类型
package main import ( "fmt" ) // 接口 // 1 定义一个接口 type Duck interface { speak() // 没有具体实现,只是声明 run() } // 2 写一个结构体,实现接口 type PDuck struct { age int } func (d PDuck) speak() { fmt.Println("普通鸭子嘎嘎叫") } func (d PDuck) run() { fmt.Println("普通鸭子歪歪扭扭走路") } type TDuck struct { name string age int wife string } func (d TDuck) speak() { fmt.Println("唐老鸭说人话") } func (d TDuck) run() { fmt.Println("唐老鸭人走路") } func main() { // 1 多态:不考虑对象具体类型的情况下使用对象,把对象当成接口类型,只要实现了接口,就直接使用 //var p = PDuck{1} //var t = TDuck{"唐老鸭", 13, "女唐老鸭"} //test9(p) //test9(t) // 2 接口也是一种类型 //var ddd Duck //ddd = PDuck{1} //ddd = TDuck{"唐老鸭", 13, "女唐老鸭"} //fmt.Println(ddd) // 3 一旦把具体对象赋值给接口类型---》只能使用接口中的方法, 其他方法或属性,都取出出来了 //var ddd Duck //ddd = TDuck{"唐老鸭", 13, "女唐老鸭"} //fmt.Println(ddd.name) // 4 把接口类型, 转换成具体类型 类型断言 //var ddd Duck //var t = TDuck{"唐老鸭", 13, "女唐老鸭"} ////var t = ddd.(TDuck) //ddd接口类型.(具体类型) ---》把这个接口类型转成具体类型 ////fmt.Println(t.age) //test9(t) // 5 直接强转可能会报错,类型选择 //var t = TDuck{"唐老鸭", 13, "女唐老鸭"} //var p = PDuck{1} //test10(p) // 6 空接口类型 所有类型都实现了空接口 //var a int =10 //var t = TDuck{"唐老鸭", 13, "女唐老鸭"} //test11(a) //test11("lqz") //test11(t) // 7 匿名空接口 fmt.Println() } // func test9(p Duck) { // p.speak() // // p 到底是普通鸭子呢,还是唐老鸭 // //var t = p.(PDuck) // 会报错 // var t = p.(TDuck) // 会报错 // fmt.Println(t.name) // } //func test10(p Duck) { // switch v := p.(type) { // case PDuck: // fmt.Println(v.age) // case TDuck: // fmt.Println(v.name) // } //} //type Empty interface { //} // //func test11(p Empty) { // fmt.Println(p) //} // 匿名,空接口 func test12(p interface{}) { }
6 并发编程
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具