网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
其中FmtProfile 是方法名,而(person Profile) :表示将 FmtProfile 方法与 Profile 的实例绑定。
我们把 Profile 称为方法的接收者,而 person 表示实例本身
,在方法内可以使用 person.属性名 的方法来访问实例属性。
package main
import “fmt”
// 定义一个名为Profile 的结构体
type Profile struct {
name string
age int
gender string
mother *Profile // 指针
father *Profile // 指针
}
// 定义一个与 Profile 的绑定的方法
func (person Profile) FmtProfile() {
fmt.Printf(“名字:%s\n”, person.name)
fmt.Printf(“年龄:%d\n”, person.age)
fmt.Printf(“性别:%s\n”, person.gender)
}
func main() {
// 实例化
myself := Profile{name: “小明”, age: 24, gender: “male”}
// 调用函数
myself.FmtProfile()
}
//输出如下
名字:小明
年龄:24
性别:male
//Person 结构体
type Person struct {
name string
age int8
}
//NewPerson 构造函数
func NewPerson(name string, age int8) *Person {
return &Person{
name: name,
age: age,
}
}
//Dream Person做梦的方法
func (p Person) Dream() {
fmt.Printf(“%s的梦想是学好Go语言!\n”, p.name)
}
func main() {
p1 := NewPerson(“测试”, 25)
p1.Dream()
}
当你想要在方法内改变实例的属性
的时候,必须使用指针做为方法的接收者
。
package main
import “fmt”
// 声明一个 Profile 的结构体
type Profile struct {
name string
age int
gender string
mother *Profile // 指针
father *Profile // 指针
}
// 重点在于这个星号: *, 说明已经有一个struct对象实例化了
//是 再次基础上做修改
func (person *Profile) increase_age() {
person.age += 1
}
func main() {
myself := Profile{name: “小明”, age: 24, gender: “male”}
fmt.Printf(“当前年龄:%d\n”, myself.age)
myself.increase_age()
fmt.Printf(“当前年龄:%d”, myself.age)
}
//结果
当前年龄:24
当前年龄:25
可以看到在方法内部对 age 的修改已经生效。你可以尝试去掉 *,使用值做为方法接收者,看看age是否会发生改变(答案是:不会改变
)
至此,我们知道了两种定义方法的方式:
-
以
值
做为方法接收者 -
以
指针
做为方法接收者
那我们如何进行选择呢?
- 直接使用
指针
做为方法的接收者。
-
你需要在方法内部改变结构体内容的时候
-
出于性能的问题,当结构体过大的时候(相当于,组合)
- 有些情况下,以值或指针做为接收者都可以,但是考虑到代码一致性,
建议
都使用指针做为接收者。
指针类型的接收者
// SetAge 设置p的年龄
// 使用指针接收者
func (p *Person) SetAge(newAge int8) {
p.age = newAge
}
调用该方法:
func main() {
p1 := NewPerson(“测试”, 25)
fmt.Println(p1.age) // 25
p1.SetAge(30)
fmt.Println(p1.age) // 30
}
Go 语言本身并不支持继承。
可以使用组合的方法,实现类似继承的效果。
组合:比如一台电脑,是由机身外壳,主板,CPU,内存等零部件组合在一起,最后才有了我们用的电脑。
在 Go 语言中,把一个结构体嵌入到另一个结构体的方法,称之为组合。
现在这里有一个表示公司(company)的结构体,还有一个表示公司职员(staff)的结构体。
type company struct {
companyName string
companyAddr string
}
type staff struct {
name string
age int
gender string
position string
}
若要将公司信息与公司职员关联起来,一般都会想到将 company 结构体的内容照抄到 staff 里。
借鉴继承的思想,我们可以将公司的属性都“继承”过来。
但是在 Go 中没有类的概念,只有组合,
可以将 company 这个 结构体嵌入
到 staff 中,做为 staff 的一个匿名字段,staff 就直接拥有了 company 的所有属性了。
type staff struct {
name string
age int
gender string
position string
company // 匿名字段
// cpmpay compay // 嵌套
}
验证一下。
package main
import “fmt”
type company struct {
companyName string
companyAddr string
}
type staff struct {
name string
age int
gender string
position string
company
}
func main() {
myCom := company{
companyName: “Tencent”,
companyAddr: “北京市”,
}
staffInfo := staff{
name: “小明”,
age: 28,
gender: “男”,
position: “云计算工程师”,
company: myCom,
}
/*
user1 := User{
Name: “pprof”,
Gender: “女”,
Address: Address{
Province: “陕西”,
City: “西安”,
},
}
*/
fmt.Printf(“%s 在 %s 工作\n”, staffInfo.name, staffInfo.companyName)
fmt.Printf(“%s 在 %s 工作\n”, staffInfo.name, staffInfo.company.companyName)
}
结果,可见staffInfo.companyName 和 staffInfo.company.companyName 的效果是一样的。
小明 在 Tencent 工作
小明 在 Tencent 工作
嵌套类型
嵌套结构体内部可能存在相同的字段名
。这个时候为了避免歧义需要指定具体的内嵌结构体的字段
//Address 地址结构体
type Address struct {
Province string
City string
}
//User 用户结构体
type User struct {
Name string
Gender string
Address //匿名结构体
}
func main() {
var user2 User
user2.Name = “pprof”
user2.Gender = “女”
user2.Address.Province = “陕西” //通过匿名结构体.字段名访问
user2.City = “西安” //直接访问匿名结构体的字段名
fmt.Printf(“user2=%#v\n”, user2) //user2=main.User{Name:“pprof”, Gender:“女”, Address:main.Address{Province:“黑龙江”, City:“哈尔滨”}}
}
继承
使用结构体也可以实现其他编程语言中面向对象的继承
//Animal 动物
type Animal struct {
name string
}
func (a *Animal) move() {
fmt.Printf(“%s会动!\n”, a.name)
}
//Dog 狗
type Dog struct {
Feet int8
*Animal //通过嵌套匿名结构体实现继承
}
func (d *Dog) wang() {
fmt.Printf(“%s会汪汪汪~\n”, d.name)
}
func main() {
d1 := &Dog{
Feet: 4,
Animal: &Animal{ //注意嵌套的是结构体指针
name: “乐乐”,
},
}
d1.wang() //乐乐会汪汪汪~
d1.move() //乐乐会动!
}
函数名的首字母大小写非常重要,它被来实现控制对方法的访问权限。
-
当方法的首字母为
大写
时,这个方法对于所有包都是Public,其他包可以随意调用
-
当方法的首字母为
小写
时,这个方法是Private,其他包是无法访问
。
第一种:正常实例化
func main() {
xm := Profile{
name: “小明”,
age: 18,
gender: “male”,
}
}
第二种:使用 new
func main() {
xm := new(Profile)
// 等价于: var xm *Profile = new(Profile)
fmt.Println(xm)
// output: &{ 0 }
xm.name = “iswbm” // 或者 (*xm).name = “iswbm”
xm.age = 18 // 或者 (*xm).age = 18
xm.gender = “male” // 或者 (*xm).gender = “male”
fmt.Println(xm)
//output: &{iswbm 18 male}
}
第三种:使用 &
func main() {
var xm *Profile = &Profile{}
fmt.Println(xm)
// output: &{ 0 }
xm.name = “iswbm” // 或者 (*xm).name = “iswbm”
xm.age = 18 // 或者 (*xm).age = 18
xm.gender = “male” // 或者 (*xm).gender = “male”
fmt.Println(xm)
//output: &{iswbm 18 male}
}
从一个结构体实例对象中获取字段的值,通常都是使用 . 这个操作符,该操作符叫做 选择器
。
当你对象是结构体对象的指针时,你想要获取字段属性
时,按照常规理解应该这么做
type Profile struct {
Name string
}
func main() {
//这里直接将p1定义为指针类型
p1 := &Profile{“iswbm”}
fmt.Println((*p1).Name) // output: iswbm
}
有一个更简洁的做法,可以直接省去 * 取值的操作
,选择器 . 会直接解引用,示例如下
type Profile struct {
Name string
}
func main() {
p1 := &Profile{“iswbm”}
fmt.Println(p1.Name) // output: iswbm
}
JSON
是一种轻量级的数据交换格式
。
JSON键值对是用来保存JS对象
的一种方式,键/值
对组合中的键名写在前面并用双引号""
包裹,使用冒号:
分隔,然后紧接着值;多个键值之间使用英文,
分隔。
//Student 学生
type Student struct {
ID int
Gender string
Name string
}
//Class 班级
type Class struct {
Title string
Students []*Student
}
func main() {
c := &Class{
Title: “101”,
Students: make([]*Student, 0, 200),
}
for i := 0; i < 10; i++ {
stu := &Student{
Name: fmt.Sprintf(“stu%02d”, i),
Gender: “男”,
ID: i,
}
c.Students = append(c.Students, stu)
}
//JSON序列化:结构体–>JSON格式的字符串
data, err := json.Marshal©
if err != nil {
fmt.Println(“json marshal failed”)
return
}
fmt.Printf(“json:%s\n”, data)
//JSON反序列化:JSON格式的字符串–>结构体
str := {"Title":"101","Students":[{"ID":0,"Gender":"男","Name":"stu00"},{"ID":1,"Gender":"男","Name":"stu01"},{"ID":2,"Gender":"男","Name":"stu02"},{"ID":3,"Gender":"男","Name":"stu03"},{"ID":4,"Gender":"男","Name":"stu04"},{"ID":5,"Gender":"男","Name":"stu05"},{"ID":6,"Gender":"男","Name":"stu06"},{"ID":7,"Gender":"男","Name":"stu07"},{"ID":8,"Gender":"男","Name":"stu08"},{"ID":9,"Gender":"男","Name":"stu09"}]}
c1 := &Class{}
err = json.Unmarshal([]byte(str), c1)
if err != nil {
fmt.Println(“json unmarshal failed!”)
return
}
fmt.Printf(“%#v\n”, c1)
}
Tag是结构体的元信息
,可以在运行的时候通过反射的机制读取出来。
Tag在结构体字段的后方定义,由一对反引号
包裹起来,具体的格式如下:
key1:"value1" key2:"value2"
结构体标签由一个或多个
键值对组成。键与值使用冒号
分隔,值用双引号
括起来。键值对之间使用一个空格分隔
。
注意事项: 为结构体编写Tag时,必须严格遵守键值对的规则。结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。例如不要在key和value之间添加空格。
例如我们为Student结构体的每个字段定义json序列化
时使用的Tag:
//Student 学生
type Student struct {
ID int json:"id"
//通过指定tag实现json序列化该字段时的key
Gender string //json序列化是默认使用字段名作为key
name string //私有不能被json包访问
}
func main() {
s1 := Student{
ID: 1,
Gender: “女”,
name: “pprof”,
}
data, err := json.Marshal(s1)
if err != nil {
fmt.Println(“json marshal failed!”)
return
}
fmt.Printf(“json str:%s\n”, data) //json str:{“id”:1,“Gender”:“女”}
}
package main
import “fmt”
type student struct {
id int
name string
age int
}
func main() {
ce := make(map[int]student)
ce[1] = student{1, “xiaolizi”, 22}
ce[2] = student{2, “wang”, 23}
fmt.Println(ce)
//如何删除
delete(ce, 2)
fmt.Println(ce)
}
(面试经常问到)
package main
import (
“fmt”
“sort”
)
func main() {
map1 := make(map[int]string, 5)
map1[1] = “www.topgoer.com”
map1[2] = “rpc.topgoer.com”
map1[0] = “xiaohuang”
map1[5] = “qcqly”
map1[3] = “xiaohong”
sli := []int{}
for k, _ := range map1 {
sli = append(sli, k)
}
sort.Ints(sli)
for i := 0; i < len(map1); i++ {
fmt.Println(map1[sli[i]])
}
}
package main
import “fmt”
type student struct {
id int
name string
age int
}
func demo(ce []student) {
//切片是引用传递,是可以改变值的
ce[1].age = 999
// ce = append(ce, student{3, “xiaowang”, 56})
// return ce
}
func main() {
var ce []student //定义一个切片类型的结构体
ce = []student{
student{1, “xiaoming”, 22},
student{2, “xiaozhang”, 33},
}
fmt.Println(ce)
demo(ce)
fmt.Println(ce)
}
========================================================================
在定义一些临时数据结构等场景下还可以使用匿名结构体
。
package main
import (
“fmt”
)
func main() {
var user struct{Name string; Age int}
user.Name = “qcq.cn”
user.Age = 18
fmt.Printf(“%#v\n”, user)
}
我们还可以通过使用new
关键字对结构体进行实例化,得到的是结构体的地址
。 格式如下:
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
struct {
id int
name string
age int
}
func demo(ce []student) {
//切片是引用传递,是可以改变值的
ce[1].age = 999
// ce = append(ce, student{3, “xiaowang”, 56})
// return ce
}
func main() {
var ce []student //定义一个切片类型的结构体
ce = []student{
student{1, “xiaoming”, 22},
student{2, “xiaozhang”, 33},
}
fmt.Println(ce)
demo(ce)
fmt.Println(ce)
}
========================================================================
在定义一些临时数据结构等场景下还可以使用匿名结构体
。
package main
import (
“fmt”
)
func main() {
var user struct{Name string; Age int}
user.Name = “qcq.cn”
user.Age = 18
fmt.Printf(“%#v\n”, user)
}
我们还可以通过使用new
关键字对结构体进行实例化,得到的是结构体的地址
。 格式如下:
[外链图片转存中…(img-LjIWDKSw-1715628116422)]
[外链图片转存中…(img-vsVjcz3o-1715628116422)]
[外链图片转存中…(img-KTrc4GVx-1715628116423)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新