p.s.这是萌新自己自学总结的笔记,如果想学习得更透彻的话还是请去看大佬的讲解
目录
结构体
go
package main
import "fmt"
//声明一种行数据类型 myint, 是int的一个别名
type myint int
//定义一个结构体
type Book struct {
title string
auth string
}
func changeBook(book Book) {
//传递一个book的副本
book.auth = "lisi"
}
func changeBook2(book *Book) {
//指针传递才能修改原始数据
book.auth = "wangwu"
}
func main() {
var book1 Book
book1.title = "Golang"
book1.auth = "zhangsan"
fmt.Printf("%v\n", book1)
changeBook(book1)
fmt.Printf("%v\n", book1)
changeBook2(&book1)
fmt.Printf("%v\n", book1)
}
封装
go
package main
import "fmt"
//如果类名首字母大写,表示其他包也能够访问
type Hero struct {
//如果说类的属性首字母大写,表示该属性是对外能够访问的,否则的话只能够类的内部访问
Name string
Age int
Level int
}
func (this Hero) Show() {
fmt.Println("Name = ", this.Name)
fmt.Println("Age = ", this.Age)
fmt.Println("Level = ", this.Level)
}
func (this *Hero) GetName() string {
return this.Name
}
// func (this Hero) SetName(newName string) {
// //值拷贝,修改的是副本
// this.Name = newName
// }
func (this *Hero) SetName(newName string) {
this.Name = newName
}
func main() {
//创建一个对象
hero := Hero{Name: "zhangsan", Age: 100, Level: 1}
hero.Show()
hero.SetName("lisi")
hero.Show()
}
继承
go
package parent
import "fmt"
type Parent struct {
Name string
Age int
}
func (this Parent) Show() {
println("Name = ", this.Name)
println("Age = ", this.Age)
}
func (this Parent) Eat() {
fmt.Println("Parent is eating.")
}
go
package main
import "fmt"
import "src/parent"
type Child struct {
parent.Parent //继承了Parent类
Grade int
}
//重写父类方法
func (this Child) Eat() {
fmt.Println("Child is eating.")
}
//子类的新方法
func (this Child) Study() {
fmt.Println("Child is studying.")
}
func main() {
p:= parent.Parent{"Tom", 40}
p.Show()
p.Eat()
c := Child{parent.Parent{"Tom", 40}, 5}
c.Show() //调用父类方法
c.Eat()
c.Study()
// var c2 Child
// c2.Name = "Jerry"
// c2.Age = 10
// c2.Grade = 3
// c2.Show()
}
多态
go
package main
import "fmt"
//本质是指针
type Animal interface {
Sleep()
GetClolor() string
GetType() string
}
type Cat struct {
Color string
}
type Dog struct {
Color string
}
func (this Cat) Sleep() {
fmt.Println("Cat is sleeping.")
}
func (this Cat) GetClolor() string {
return this.Color
}
func (this Cat) GetType() string {
return "Cat"
}
func (this Dog) Sleep() {
fmt.Println("Dog is sleeping.")
}
func (this Dog) GetClolor() string {
return this.Color
}
func (this Dog) GetType() string {
return "Dog"
}
func main() {
var animal1 Animal
animal1 = Cat{"Yellow"}
fmt.Printf("Animal type: %s, color: %s\n", animal1.GetType(), animal1.GetClolor())
animal1.Sleep()
var animal2 Animal
animal2 = Dog{"Black"}
fmt.Printf("Animal type: %s, color: %s\n", animal2.GetType(), animal2.GetClolor())
animal2.Sleep()
}
多态三要素:
有一个父类(有接口)
有子类(实现了父类的全部接口方法)
父类类型的变量(指针)指向(引用)子类的具体数据变量
万能数据类型
go
package main
import "fmt"
//interface{}是Go语言中的空接口类型,可以接受任何类型的值。它是所有类型的父类型,因此可以用来存储任何类型的数据,即万能数据类型
func myFunc(arg interface{}){
fmt.Println(arg)
//给interface{}类型的参数进行类型断言,判断它是否是某个具体的类型
if str, ok := arg.(string); ok {
fmt.Println("这是一个字符串:", str)
} else if num, ok := arg.(int); ok {
fmt.Println("这是一个整数:", num)
} else if book, ok := arg.(Book); ok {
fmt.Println("这是一本书,作者是:", book.Author)
} else {
fmt.Println("未知类型")
}
}
type Book struct {
Author string
}
func main() {
book :=Book{"zhangsan"}
myFunc(book)
myFunc(123)
myFunc("Hello, World!")
}
反射
静态类型、具体类型
在 Go 语言中,理解"静态类型"与"接口的动态类型"是掌握其类型系统、多态性和反射机制的关键。这两者代表了类型在不同生命周期(编译时与运行时)的不同表现形式。
- 静态类型
静态类型,也称为编译时类型,是 Go 语言作为静态类型语言的根本体现。
定义:静态类型指的是变量、常量、函数参数和返回值在代码声明阶段就已被明确指定的类型。这个类型信息是固定写在源代码中的。
核心特征:
编译时确定与检查:编译器在将源代码转换为可执行程序的过程中,会完全知晓并验证所有实体的静态类型。任何不符合类型规则的赋值、传参或运算都会导致编译错误,程序无法生成。这为程序提供了强大的、早期(运行前)的类型安全保障。
不可变性:一个变量一旦被声明为某种静态类型(如 int, string, 或自定义的 struct),在它的整个生命周期内,其静态类型不会改变。你可以改变变量的值,但不能改变它"是什么类型"的这一事实。
性能优化基础:由于编译器确切知道每个数据的类型,它可以进行深度的优化,例如生成更高效的机器指令、进行内联优化等,从而提升程序运行性能。
代码清晰性:明确的静态类型声明本身就是一种文档,它清晰地表明了数据的用途和预期的操作,提高了代码的可读性和可维护性。
举例说明:当你写下 var count int时,count的静态类型就是 int。编译器会确保你只能给 count赋值整数,也只能对它进行整数所允许的操作。如果你尝试写入一个字符串,编译器会立即报错。- 接口的动态类型
动态类型的概念与 Go 的接口机制紧密相关,它体现了 Go 实现多态(一个接口,多种实现)的方式。
定义:当一个变量被声明为某个接口类型时,该变量的"动态类型"指的是在程序实际运行时刻,存储在该接口变量中的那个具体值的实际类型。
核心特征:
运行时确定:与静态类型在编译时确定不同,动态类型要到程序执行时才能知晓。同一个接口类型的变量,在不同的执行时刻,其内部可能包裹着完全不同具体类型的值。
实现多态的基础:这是 Go 语言实现"面向接口编程"和"多态"的核心。你可以定义一个 io.Writer接口类型的变量,在运行时,它可以指向一个向文件写入的 *os.File对象,也可以指向一个在内存中缓冲数据的 *bytes.Buffer对象。调用其 Write方法时,实际执行的是当时所持有的具体类型的方法。
需要类型断言或反射探查 :由于编译器在编译时只知道变量是某个接口类型,而不知道其动态的具体类型,因此当你需要访问具体类型独有的字段或方法时,必须使用类型断言(如 value, ok := var.(ConcreteType))或反射(reflect包)来"探查"和获取其动态类型信息。这个过程如果失败,可能会导致运行时恐慌。
灵活性带来的代价:接口提供的动态类型能力增加了程序的灵活性,但类型断言和反射操作会带来微小的运行时开销,并且不当的使用会引入运行时错误的风险。
举例说明:声明 var writer io.Writer。此时,writer的静态类型是 io.Writer(接口)。在运行时,你执行 writer = os.Stdout,那么此刻 writer的动态类型就是 *os.File。之后你又执行 writer = new(bytes.Buffer),此时 writer的动态类型就变成了 *bytes.Buffer。它的静态类型始终是 io.Writer,但动态类型随着赋值而改变。
go
package main
import (
"fmt"
"reflect"
)
func reflectNum(arg interface{}) {
fmt.Println("type : ", reflect.TypeOf(arg))
fmt.Println("value : ", reflect.ValueOf(arg))
fmt.Println("-----------------------------")
}
type Student struct {
Name string
Age int
}
func reflectStruct(arg interface{}) {
val := reflect.ValueOf(arg)
typ := reflect.TypeOf(arg)
fmt.Println("name : ", typ.Name())
fmt.Println("kind : ", typ.Kind())
fmt.Println(val)
fmt.Println("-----------------------------")
for i := 0; i < val.NumField(); i++ {
fieldType := typ.Field(i) // 获取字段类型信息
fieldValue := val.Field(i) // 获取字段值
value := fieldValue.Interface()
fmt.Printf("%s: %v = %v\n", fieldType.Name, fieldType.Type, value)
}
}
func main() {
var num float64 = 1.2345
reflectNum(num)
student := Student{Name: "Alice", Age: 20}
reflectStruct(student)
}
解析tag
go
package main
import (
"fmt"
"reflect"
)
type resume struct {
Name string `info:"name" doc:"我的名字"`
Sex string `info:"sex"`
}
func findTag(str interface{}) {
t := reflect.TypeOf(str).Elem()
for i := 0; i < t.NumField(); i++ {
taginfo := t.Field(i).Tag.Get("info")
tagdoc := t.Field(i).Tag.Get("doc")
fmt.Println("info: ", taginfo, " doc: ", tagdoc)
}
}
func main() {
var re resume
findTag(&re)
}
解析json
go
package main
import (
"encoding/json"
"fmt"
)
type Movie struct {
Title string `json:"title"`
Year int `json:"year"`
Price int `json:"rmb"`
Actors []string `json:"actors"`
}
func main() {
movie := Movie{"red alert 2", 2012, 17, []string{"zhangsan", "lisi"}}
//编码的过程 结构体---> json
jsonStr, err := json.Marshal(movie)
if err != nil {
fmt.Println("json marshal error", err)
return
}
fmt.Printf("jsonStr = %s\n", jsonStr)
//解码的过程 jsonstr ---> 结构体
//jsonStr = {"title":"喜剧之王","year":2000,"rmb":10,"actors":["xingye","zhangbozhi"]}
myMovie := Movie{}
err = json.Unmarshal(jsonStr, &myMovie)
if err != nil {
fmt.Println("json unmarshal error", err)
return
}
fmt.Println("myMovie = ", myMovie)
}
