go基础学习 | 青训营笔记

22 阅读6分钟

这是我参与「第五届青训营 」笔记创作活动的第 2

|| 🎶今日笔记🎶 ||

学习过程的重点内容:

  • go的基础语法
  • 与类不一样的——结构体
  • 独特的继承机制
  • 代码优化之——工厂设计模式

go的基础语法

关键字:

  • varconst 是 Go语言基础里面的变量和常量申明

  • packageimport 用于分包和导入

  • func 用于定义函数和方法

  • return 用于从函数返回

  • defer 用于类似析构函数

  • go 用于并发

  • select 用于选择不同类型的通讯

  • interface 用于定义接口

  • struct 用于定义抽象数据类型

  • breakcasecontinueforfallthroughelseifswitchgotodefault 用于流程控制

  • chan用于channel通讯

  • type用于声明自定义类型

  • map用于声明map类型数据

  • range用于读取slice、map、channel数据

错误类型:

Go内置有一个error类型,专门用来处理错误信息,Go的package里面还专门有一个包errors来处理错误

err := errors.New("This is my new errors")
if err != nil {
    fmt.Print(err)
}

Array,Slice,Map:

array就是数组,它的定义方式如下:

var arr [your arr len]type
var arr [10]int  // 声明了一个int类型的数组
arr[0] = 12      // 数组下标是从0开始的
arr[1] = 33      // 赋值操作
fmt.Println("The first element is", arr[0])  // 获取数据,返回12
fmt.Println("The last element is", arr[9]) //返回未赋值的最后一个元素,默认返回0

[your arr len]type中,your arr len为你自定义数组的长度,type表示存储元素的类型。对数组的操作和其它语言类似,都是通过[]来进行读取或赋值:

由于长度也是数组类型的一部分,因此[3]int[4]int是不同的类型,数组也就不能改变长度。数组之间的赋值是值的赋值,即当把一个数组作为参数传入函数的时候,传入的其实是该数组的副本,而不是它的指针。

数组可以使用另一种:=来声明

a := [3]int{1, 2, 3} // 声明了一个长度为3的int数组
b := [10]int{1, 2, 3} // 声明了一个长度为10的int数组,其中前三个元素初始化为1、2、3,其它默认为0
c := [...]int{4, 5, 6} // 可以省略长度而采用`...`的方式,Go会自动根据元素个数来计算长度
复制代码

Go支持嵌套数组,即多维数组。比如下面的代码就声明了一个二维数组:

// 声明了一个二维数组,该数组以两个数组作为元素,其中每个数组中又有4个int类型的元素
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
// 上面的声明可以简化,直接忽略内部的类型
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}

结构体 & 类?

与其他语言不同,go的“类”称为结构体,虽然go也是一门面向对象的高级语言,但他不是纯粹的面向对象语言,通过下面的struct例子来对说明

type Person struct { 通过type xxx struct的形式定义一个结构体
   Name string `json:"name"` //定义字段&属性(string类型)
   Age  int    `json:"age"`  //定义字段&属性(int类型)
   Sex  string `json:"sex"`  //定义字段&属性(string类型)
}

其中'json:"name"'的形式将字段转成json形式
detail: 字段建议以驼峰的形式定义,首字母大写可以使本包以及其他包下的程序来访问(访问权限如Java中的Public),首字母小写的访问权限范围为本类(访问权限如Java中的Private)

func (person Person) pretest(ags []string) Person {
   person.Name = ags[0] //给Name字段赋值
   person.Sex = ags[1] //给Sex字段赋值
   return person
}

将方法与Person进行绑定,这也是方法与函数的区别之一。

type Teacher struct {
   Tid   int
   TName string
}

func (t *Teacher) String() string { //将Teacher设置为指针类型
   str := fmt.Sprintf("Tid=%v,TName=%v", t.Tid, t.TName)
   return str
}

func main{
teacher := Teacher{
   TName: "yang",
   Tid:   250,
}

fmt.Println("teacher-->", &teacher)
}

将Teacher设置为指针类型
detail:其中,将方法名设为 String并且返回类型为 string的话,如同 Java中的 ToString,在 main方法中输出 &teacher(指针类型输出地址值为其字段的真正地址。String()被视为匿名函数处理

控制台输出

teacher--> Tid=250,TName=yang

Process finished with the exit code 0

独特的继承机制

go语言的继承与其他面向对象语言不同,不通过我们常见的extend关键字来实现,通过在子类struct中以内嵌的方式来实现,通俗的说就是在普通继承的时候就是直接extend,而go的是基于 鸭子类型 理论,就是你只要拥有某种特征,你就是他,比如你定义一个鸭子的接口,这个鸭子接口拥有叫和走, 无论你定义狗,还是定义猫,只要他们也实现了鸭子的叫,和走,那么 他们就是鸭子!

type Animal struct {
	Name string
}

func (a *Animal) Eat() {
	fmt.Printf("%v is eating", a.Name)
	fmt.Println()
}

type Cat struct {
	Animal
}

cat := &Cat{
	Animal: Animal{
		Name: "cat",
	},
}
cat.Eat() // cat is eating

首先,我们实现了一个 Animal 的结构体,代表动物类。并声明了 Name 字段,用于描述动物的名字。

然后,实现了一个以 Animal 为 receiver 的 Eat 方法,来描述动物进食的行为。

最后,声明了一个 Cat 结构体,组合了 Animal 字段。实例化一个猫,调用 Eat 方法,可以看到正常的输出。

Cat 结构体本身没有 Name 字段,也没有去实现 Eat() 方法。唯一有的就是匿名嵌套的方式继承了 Animal 父类。至此,我们证明了 Go 通过匿名嵌套的方式实现了继承。

上面是嵌入类型实例,同样地也可以嵌入类型指针。

type Cat struct { 
       *Animal 
} 
cat := &Cat{
    Animal: &Animal{ Name: "cat", 
    }, 
}

工厂模式

这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。并且,若将struct的首字母设为小写,则其他包下的程序访问不到,这时也可以通过工厂模式来实现:

package factory

/**
工厂设计模式
*/

// client 自定义结构体
// client首字母为小写,只能在factory中使用
type client struct {
   Id   string
   name string
}

// GetName 跟Java类似
// 与上面一样,name字段小写其他包也无法直接使用,提供一个方法实现
func (c *client) GetName() string {
   return c.name
}

// NewClient Factory
func NewClient(n string, s string) *client {
   return &client{
      Id: n,
      name: s,
   }
}

~ End ~