GoLang基础知识(1)

1、变量声明

package main

import (
	"fmt"
	"mypro/user"
)

// 如果我们接收到多个变量,有一些变量使用不到,可以使用下划线_表示变 名称,这种变量叫做匿名变量。例
func getNameAndAge() (name string, age int) {
	return "abiao", 23
}

func getNameAndAge1() (string, int) {
	return "abiao", 23
}

func main() {
	s := user.Hello()
	fmt.Printf("s: %v\n", s)

	//变量初始化  var 变量名 类型 = 表达式
	/*
		Go语言在声明变量的时候,会自动对变量对应的内存区域进行初始化操作。每个变量会被初始化成其类型的默认
		值,例如:整型和浮点型变量的默认值为0。字符串变量的默认值为空字符串“”。布尔型变量默认为false。
		片、函数、指针变量的默认为nil。
	*/

	var myName string = "abiao"
	var myAge int = 23
	var isMarried bool = false
	fmt.Printf("myAge: %v\t", myAge)
	fmt.Printf("myName: %v\t", myName)
	fmt.Printf("isMarried: %v\n", isMarried)

	//批量声明
	var (
		name string = "tom"
		age  int    = 20
		b    bool   = true
	)

	fmt.Printf("name: %v \t", name)
	fmt.Printf("age: %v \t", age)
	fmt.Printf("b: %v \n", b)

	//类型推导
	var (
		name1 = "tom"
		site  = "www.baiu.com"
	)
	fmt.Printf("name = %v\t", name1)
	fmt.Printf("name = %v\n", site)

	//批量初始化多个变量
	var name2, site2, age2 = "abiao", "www.ali.com", 23
	fmt.Printf("name2: %v\t", name2)
	fmt.Printf("site2: %v\t", site2)
	fmt.Printf("age2: %v\n", age2)

	//短变量声明--在函数内部,可以使用:= 运算符对变量进行声明和初始化。
	//注意:这种方法只适合在函数内部,函数外面不能使用。
	name3 := "abiaobiao"
	age3 := 24
	fmt.Printf("name3: %v\t", name3)
	fmt.Printf("age3: %v\n", age3)

	name4, age4 := getNameAndAge()
	fmt.Printf("name4: %v\t", name4)
	fmt.Printf("age4: %v\n", age4)

	//如果我们接收到多个变量,有一些变量使用不到,可以使用下划线_表示变量名称, 这种变量叫做匿名变量。例如:
	_, age5 := getNameAndAge()
	fmt.Printf("age5: %v\n", age5)

}

2、常量

常量,就是在程序编译阶段 就确定下来的值,而程序在运行时则无法改变该值。在Go程序中,常量可以是数值类型(包括整型、浮点型和复数类型)、布尔类型、 字符串类型等。

定义一个常量使用const关键字,语法格式如下:

const constantName [type ]= value
const :定义常量关键字
constantName :常名称
type :常量类型
value:常量的值

const定义常量

package main

import "fmt"

func main() {
	const PI float64 = 3.1415926
	const PI2 = 3.141 //类型推断

	//批量声明
	const (
		width  = 100
		height = 200
	)
	fmt.Printf("width: %v\t", width)
	fmt.Printf("height: %v\n", height)

	//多重赋值
	const i, j = 1, 2
	const a, b, c = 1, 2, "foo"
}

iota

iota比较特殊,可以被认为是一个可被编译器修改的常量, 它默认开始值是0,每调用一次加1。遇到const 关键字时被重置为0,

const (
		a1 = iota // 0
		a2 = iota // 1
		a3 = iota // 2
	)
	fmt.Printf("a1: %v\t", a1)
	fmt.Printf("a2: %v\t", a2)
	fmt.Printf("a3: %v\n", a3)

	const (
		a4 = iota // 0
		_         //跳过
		a6 = iota // 2
	)
	fmt.Printf("a4: %v\t", a4)
	fmt.Printf("a6: %v\n", a6)

	const (
		a7 = iota // 0
		a9 = 1000 //中间插队
		a8 = iota // 2
	)
	fmt.Printf("a7: %v\t", a7)
	fmt.Printf("a8: %v\n", a8)

3、go数据类型

package main

import "fmt"

func myFunc() {

}

func main() {
	var name string = "abiao"
	age := 20
	bo := true
	p := &age
	fmt.Printf("name: %T\t", name)
	fmt.Printf("age: %T\t", age)
	fmt.Printf("bo: %T\t", bo)
	fmt.Printf("p: %T\n", p) //name: string	age: int	bo: bool	p: *int

	arr := [2]int{1, 2}
	fmt.Printf("%T\n", arr) //[2]int

	slice := []int{1, 2, 3}
	fmt.Printf("slice: %T\n", slice) //slice: []int

	fmt.Printf("%T", myFunc) //func()
}

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

go 语言布尔类型

func main() {
	var b1 bool = true
	var b2 bool = false

	var b3 = true //自动推导类型
	var b4 = false

	b5 := true //短变量形式
	b6 := false
	fmt.Printf("b1: %v\n", b1)
	fmt.Printf("b2: %v\n", b2)
	fmt.Printf("b3: %v\n", b3)
	fmt.Printf("b4: %v\n", b4)
	fmt.Printf("b5: %v\n", b5)
	fmt.Printf("b6: %v\n", b6)

	age := 12
	if age >= 18 {
		fmt.Printf("你已经成年了\n")
	} else {
		fmt.Printf("还未成年\n")
	}

	count := 10
	for i := 0; i < count; i++ {
		fmt.Printf("i = %v\t", i)
	}

	fmt.Println("")
	age1 := 18
	gender := "man"
	if age1 >= 18 && gender == "man" {
		fmt.Println("你是成年男子")
	}
}

go 语言数字类型

Go语言支持整型和浮点型数字,并且原生支持复数,其中位的运算采用补码。
Go也有基于架构的类型,例如: int、 uint 和uintptr 。
这些类型的长度都是根据运行程序所在的操作系统类型所决定的:
●int和uint 在32位操作系统上,它们均使用32位(4个字节),在64位操作系统上,
(8个字节)。
●uintptr的长度被设定为足够存放一个指针即可。
Go语言中没有float类型。(Go语言中只有 float32和float64 )没有double类型。与操作系统架构无关的类型都有固定的大小,并在类型的名称中就可以看出来:
整数:
●int8 (-128-> 127)
●int16 (-32768 -> 32767)
●int32 (-2,147,483,648 -> 2,147,483,647)
●int64 (-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807)
无符号整数:
●uint8 (0 > 255)
●uint16 (0 -> 65,535)
uint32 (0 -> 4,294,967,295)
●uint64 (0 > 18,446,744,073,709,551,615)

浮点型(EE-754 标准) : .
●float32 (± 1e-45->±3.4* 1e38)
●float64 (± 5 * 1e-324-> 107 * 1e308)
int型是计算最快的一种类型。
整型的零值为0,浮点型的零值为0.0。

golang字符串

package main

import (
	"bytes"
	"fmt"
	"strings"
)

func main() {
	var s string = "hello golang"
	var s1 = "hello"
	s2 := "world"
	s3 := `
		hello,
		biao
		baba
	`

	msg := s1 + s2
	fmt.Printf("s: %v\n", s)
	fmt.Printf("s1: %v\n", s1)
	fmt.Printf("s2: %v\n", s2)
	fmt.Printf("s3: %v\n", s3)
	fmt.Printf("msg: %v\n", msg)

	//字符串拼接
	var name = "abiao"
	age := 23
	message := fmt.Sprintf("name=%s,age=%d", name, age)
	fmt.Printf("message: %v\n", message)

	name1 := "tom"
	age1 := "25"
	message1 := strings.Join([]string{name1, age1}, ",")
	fmt.Printf("message1: %v\n", message1)

	var buffer bytes.Buffer
	buffer.WriteString("tom")
	buffer.WriteString(",")
	buffer.WriteString("28")
	fmt.Printf("buffer.String(): %v\n", buffer.String())

	//字符串切片
	str := "hello world"
	a := 2
	b := 5
	fmt.Printf("s[a]: %c\n", str[a])
	fmt.Printf("str[a:b]: %v\n", str[a:b])
	fmt.Printf("str[a:]: %v\n", str[a:])
	fmt.Printf("str[:b]: %v\n", str[:b])
	//长度
	fmt.Printf("len(str): %v\n", len(str))
	//分割
	fmt.Printf("strings.Split(s, \" \"): %v\n", strings.Split(s, " "))
	//包含
	fmt.Printf("strings.Contains(s, \"hello\"): %v\n", strings.Contains(s, "hello"))
	//转大写
	fmt.Printf("strings.ToUpper(str): %v\n", strings.ToUpper(str))

	fmt.Printf("strings.HasPrefix(\"hello\"): %v\n", strings.HasPrefix(str, "hello"))
	fmt.Printf("strings.HasSuffix(\"world\", str): %v\n", strings.HasSuffix(str, "World"))

	fmt.Printf("strings.Index(str, \"ll\"): %v\n", strings.Index(str, "ll"))

	fmt.Printf("strings.Trim(\"      444  22 \", \" \"): %v\n", strings.Trim("      444  22 ", " "))

}

4、格式化输出

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package main

import "fmt"

type WebSite struct {
	Name string
}

func main() {
	// %v var
	fmt.Printf("\"hello\": %v\n", "hello")
	site := WebSite{Name: "abiao"}
	fmt.Printf("site: %#v\n", site)
	fmt.Printf("site: %v\n", site)
	fmt.Printf("site: %T\n", site)
	fmt.Printf("%%")

	i := 97
	fmt.Printf("i: %v\n", i)
	fmt.Printf("i: %b\n", i)
	fmt.Printf("i: %c\n", i)

	x := 100
	p := &x
	fmt.Printf("p: %p\n", p)

	/* 
		"hello": hello
		site: main.WebSite{Name:"abiao"}
		site: {abiao}
		site: main.WebSite
		%i: 97
		i: 1100001
		i: a
		p: 0xc00001a0d0 
	*/

}

5、golang 运算符

Go语言内置的运算符有:
1.算术运算符
2.关系运算符
3.逻辑运算符
4.位运算符
5.赋值运算符
在这里插入图片描述

package main

import "fmt"

func main() {
	a := 100
	b := 22
	fmt.Printf("a+b: %v\n", a+b)
	fmt.Printf("a-b: %v\n", a-b)
	fmt.Printf("a*b: %v\n", a*b)
	fmt.Printf("a/b: %v\n", a/b)
	fmt.Printf("a%%b: %v\n", a%b)

	a++ //c := a++  ++不可以应用到表达式
	fmt.Printf("a: %v\n", a)
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6、流程控制

在这里插入图片描述

package main

import "fmt"

func main() {
	for i := 0; i < 5; i++ {
		fmt.Printf("i: %v\n", i)
	}

	x := [...]int{1, 2, 3}
	for _, v := range x {
		fmt.Printf("v: %v\n", v)
	}
}

golang 中的 if语句

go语言if语句使用提示:
1.不需使用括号将条件包含起来
2. 大括号{}必须存在,即使只有一行语句
3.左括号必须在if或else的同一行.
4.在if之后,条件语句之前,可以添加变量初始化语句,使用;进行分隔

	if age := 20; age > 18 {
		fmt.Printf("你已经成年")
	} else {
		fmt.Print("你还未成年")
	}

注意:不能使用0或非0表示真假

golang if else语句

golang if else if语句

在这里插入图片描述

在这里插入图片描述

switch语句

func f() {
	grade := 'A'
	switch grade {
	case 'A':
		fmt.Println("优秀")
	case 'B':
		fmt.Println("优秀")
	default:
		fmt.Println("其他")
	}
}

func f2() {
	day := 1
	switch day {
	case 1, 2, 3, 4, 5:
		fmt.Printf("工作日")
	case 6, 7:
		fmt.Printf("休息日")
	default:
		fmt.Printf("非法输入")
	}
}

func f3() {
	score := 60
	switch {
	case score >= 60:
		fmt.Printf("及格")
	case score >= 80 && score < 90:
		fmt.Printf("优秀")
	default:
		fmt.Printf("其他")
	}
}

//fallthrough
func f4() {
	num := 100
	switch num {
	case 100:
		fmt.Println("100")
		fallthrough
	case 200:
		fmt.Println("200")
	case 300:
		fmt.Println("300")
	}
}
//结果 100 200

没有break
case后支持多个值
case后支持条件
fallthrough

for 循环

for 初始语句;条件表达式;结束语句{
	循环体语句
}

func f1() {
	for i := 1; i < 5; i++ {
		fmt.Printf("i: %v\n", i)
	}
}

//初始化语句放外面
func f2() {
	i := 1
	for ; i < 5; i++ {
		fmt.Printf("i: %v\n", i)
	}
}

//可以直接写条件,初始语句和结束语句放其他地方
func f3() {
	i := 1
	for i <= 4 {
		fmt.Printf("i: %v\n", i)
		i++
	}
}

//死循环
func f4() {
	for {
		fmt.Println("running....")
	}
}

for range循环

Go语言中可以使用for range 遍历数组、切片、字符串、map 及通道(channel) 。通过for range 遍历的返回值有以下规律:

  1. 数组、切片、字符串返回索引和值。
  2. map返回键和值。
    3.通道. (channel)只返回通道内的值。
func f1() {
	//数组
	a := [...]int{1, 2, 3}
	for i, v := range a {
		fmt.Printf("i: %v\t", i)
		fmt.Printf("v: %v\n", v)
	}

	for i, _ := range a {
		fmt.Printf("i: %v\n", i)
	}

	for _, i := range a {
		fmt.Printf("i: %v\n", i)
	}
}

func f2() {
	//切片
	var s = []int{1, 2, 3, 4}
	for _, v := range s {
		fmt.Printf("v: %v\n", v)
	}
}

func f3() {
	//map
	m := make(map[string]string, 0)
	m["name"] = "tom"
	m["age"] = "20"
	for k, v := range m {
		fmt.Printf("k: %v\t", k)
		fmt.Printf("v: %v\n", v)
	}
}

break关键字

break语句可以结束for、 switch 和select的代码块。
go语言使用break注意事项
1.单独在select中使用break和不使用break没有啥区别。
2.单独在表达式switch语询,且没有fallthough,使用break和不使用break没有啥区别。
3.单独在表达式switch语句,组有fallthough,使用break能够终止fallthough后面的case语句的执行。
4.带标签的break,可以跳出多层select/ switch 作用域。让break更加灵活,写法更加简单灵活,不需要使用控制变量一层一层跳出循环, 没有带break的只能跳出当前语句块。

package main

import "fmt"

//跳出for循环
func f1() {
	for i := 0; i < 10; i++ {
		if i >= 5 {
			break
		}
		fmt.Printf("i: %v\n", i)
	}
}

//跳出switch
func f2() {
	i := 2 //i = 1
	switch i {
	case 1:
		fmt.Printf("1")
		break // i=1的时候,加不加都一样
	case 2:
		fmt.Printf("2")
		break //2的时候终止穿透,没有break会穿透
		fallthrough
	case 3:
		fmt.Println("3")
	}
}

//跳转到标签处
func f3() {
MYLABEL:
	for i := 0; i < 10; i++ {
		fmt.Printf("i: %v\n", i)
		if i >= 5 {
			break MYLABEL
		}
	}

	fmt.Println("end.....")
}

func main() {
	f3()
}

continue关键字

continue只能用在循环中,在go中只能用在for循环中,它可以终止本次循环,进行下一次循环。在continue语句后添加标签时,表示开始标签对应的循环。

func f1() {
	for i := 0; i < 10; i++ {
	MYLABEL:
		for j := 0; j < 10; j++ {
			if i == 2 && j == 2 {
				continue MYLABEL //不打印 2 2,不写标签也具有相同效果
			}
			fmt.Printf("%v, %v\n", i, j)
		}
	}
}

goto 关键字

goto语句通过标签进行代码间的无条件跳转。goto 语句可以在快速跳出循环、避免重复退出上有一定的帮助。Go语言中使用goto语句能简化一些代码的实现过程。例如双层嵌套的for循环要退出时:

package main

import (
	"fmt"
)

func f1() {
	i := 1
	if i >= 2 {
		fmt.Println("2")
	} else {
		goto END
	}
END:
	fmt.Println("END...")
}

func f2() {
	for i := 0; i < 10; i++ {
		for j := 0; j < 10; j++ {
			if i >= 2 && j >= 2 {
				goto END
			}
			fmt.Printf("%v-%v\t", i, j)
		}
	}
END:
	fmt.Println("END...")
}

7 、golang数组

在这里插入图片描述

数组初始化

package main

import "fmt"

func test() {
	var a1 [2]int
	var a2 [3]string
	var a3 [2]bool
	fmt.Printf("a1: %T\n", a1)
	fmt.Printf("a2: %T\n", a2)
	fmt.Printf("a1: %v\n", a1)
	fmt.Printf("a2: %v\n", a2)
	fmt.Printf("a3: %v\n", a3)
	/*
		a1: [0 0]
		a2: [  ]
		a3: [false false]
	*/

	//使用初始化列表
	var a4 = [2]int32{1, 2}
	fmt.Printf("a4: %v\n", a4)

	var a5 = [3]string{"hello", "world", "!"}
	fmt.Printf("a5: %v\n", a5)

	var a6 = [2]bool{false, true}
	fmt.Printf("a6: %v\n", a6)
}

func test1() {
	var a = [...]int{1, 2, 3}
	fmt.Printf("len(a): %v\n", len(a)) //3
}

func test2() {
	//指定索引初始化
	var a1 = [...]int{2: 15, 3: 27, 6: 15}
	fmt.Printf("a1: %v\n", a1)

	var a2 = [...]bool{1: true, 6: true}
	fmt.Printf("a2: %v\n", a2)
}

func test3() {
	//访问数组下标
	var a1 = [...]int{1, 2, 3}
	a1[2] = 100
	fmt.Printf("a1: %v\n", a1)
}

func main() {
	test3()
}

访问数组

func test1() {
	var a1 [5]int
	a1[0] = 10
	a1[1] = 100
	fmt.Printf("a1[0]: %v\n", a1[0])
	fmt.Printf("a1[1]: %v\n", a1[1])
	fmt.Println("")

	a1[0] = 20
	a1[4] = 2000
	fmt.Printf("a1[0]: %v\n", a1[0])
	fmt.Printf("a1[4]: %v\n", a1[4])
	fmt.Println("")

	fmt.Printf("len(a1): %v\n", len(a1))

	//遍历1
	for i := 0; i < len(a1); i++ {
		fmt.Printf("a1[i]: %v\t", a1[i])
	}
	fmt.Println("")
	fmt.Printf("len(a1): %v\n", len(a1))

	//遍历2
	for _, v := range a1 {
		fmt.Printf("v: %v\t", v)
	}
	fmt.Println("")
}

8 、golang切片

前面我们学习了数组,数组是固定长度,可以容纳相同数据类型的元素的集合。当长度固定时,使用还是带来一些限制,比如:我们申请的长度太大浪费内存,太小又不够用。
鉴于上述原因,我们有了go语言的切片,可以把切片理解为,可变长度的数组,其实它底层就是使用数组实现的,增加了自动扩容功能。坊(Slice) 是一个拥有相同类型元素的可变长度的序列。

在这里插入图片描述

切片初始化

func test1() {
	var a1 = [3]int{1, 2, 3} //数组

	//声明切片1
	var s1 []int //切片 []中没有数字也没有...
	var s2 []string
	fmt.Printf("a1: %v\n", a1) //a1: [1 2 3]
	fmt.Printf("s1: %v\n", s1) //s1: []
	fmt.Printf("s2: %v\n", s2) //s2: []

	fmt.Printf("type of a1: %T\t", a1) //type of a1: [3]int
	fmt.Printf("type of s1: %T\t", s1) //type of s1: []int
	fmt.Printf("type of s2: %T\n", s2) //type of s2: []string

	//声明切片2
	var s3 = make([]int, 2)    //类型,长度
	fmt.Printf("s3: %v\n", s3) //s3: [0 0]

	//打印容量
	var s4 = []int{1, 2, 3}
	fmt.Printf("len(s4): %v\n", len(s4))  //len(s4): 3
	fmt.Printf("cap(s4): %v\n", cap(s4))  //cap(s4): 3
	fmt.Printf("s4[0]: %v\n", s4[0])  //s4[0]: 1
}

func test1() {
	var s1 = []int{1, 2, 3, 4, 5, 6}
	var s2 = s1[0:3]
	fmt.Printf("s2: %v\n", s2) //s2: [1 2 3]

	s3 := s1[3:]
	fmt.Printf("s3: %v\n", s3) //s3: [4 5 6]

	s4 := s1[2:5]
	fmt.Printf("s4: %v\n", s4) //s4: [3 4 5]

	s5 := s1[:]
	fmt.Printf("s5: %v\n", s5) //s5: [1 2 3 4 5 6]

}

func test2() {
	var a1 = [...]int{1, 2, 3, 4, 5, 6}
	a2 := a1[0:3]
	fmt.Printf("a2: %v\n", a2)
	a3 := a1[3:]
	fmt.Printf("a3: %v\n", a3)
	a4 := a1[2:5]
	fmt.Printf("a4: %v\n", a4)
	a5 := a1[:]
	fmt.Printf("a5: %v\n", a5)
}

切片元素的添加、删除、copy

//增
func test1() {
	var s1 = []int{}
	s1 = append(s1, 100)
	s1 = append(s1, 2200)
	s1 = append(s1, 200)
	fmt.Printf("s1: %v\n", s1)
}

//删
func test2() {
	var s1 = []int{1, 2, 3, 4}
	s1 = append(s1[:2], s1[3:]...)
	fmt.Printf("s1: %v\n", s1)
}

//改
func test3() {
	var s1 = []int{1, 2, 3, 4}
	s1[1] = 100
	fmt.Printf("s1: %v\n", s1)
}

//查
func test4() {
	var s1 = []int{1, 2, 3, 4}
	key := 2
	for i, v := range s1 {
		if v == key {
			fmt.Printf("%v", i)
		}
	}
}

//copy
func test5() {
	var s1 = []int{1, 2, 3, 4}
	s2 := s1
	s1[1] = 200
	fmt.Printf("s1: %v\n", s1) //s1: [1 200 3 4]
	fmt.Printf("s2: %v\n", s2) // s2: [1 200 3 4]
}

func test6() {
	var s1 = []int{1, 2, 3, 4}
	var s2 = make([]int, 4)
	copy(s2, s1)
	s1[1] = 200
	fmt.Printf("s1: %v\n", s1) //s1: [1 200 3 4]
	fmt.Printf("s2: %v\n", s2) // s2: [1 2 3 4]
}

9、 golang map

map是一 种key :value键值对的数据结构容器。map内部实现是哈希表( hash )。map最重要的一点是通过key来快速检索数据,key类似于索引,指向数据的值。
map是引用类型的。

在这里插入图片描述

package main

import (
	"fmt"
)

func test1() {
	var m1 map[string]string
	m1 = make(map[string]string)
	fmt.Printf("m1: %v\n", m1) //m1: map[]
	fmt.Printf("m1: %T\n", m1) //m1: map[string]string
}

func test2() {
	var m1 = map[string]string{"name": "abiao", "age": "18"}
	fmt.Printf("m1: %v\n", m1) //m1: map[age:18 name:abiao]

	m2 := make(map[string]string)
	m2["name"] = "abiaobiao"
	m2["age"] = "19"
	fmt.Printf("m2: %v\n", m2)
}

func test3() {
	m1 := map[string]string{"name": "abiao", "age": "18"}
	var key = "name"
	//查询key对应value
	var value = m1[key]
	fmt.Printf("value: %v\n", value)
	fmt.Println("")

	v, ok := m1["email"]
	fmt.Printf("v: %v\n", v)   //v:
	fmt.Printf("ok: %v\n", ok) //ok: false

	v, ok = m1["name"]
	fmt.Printf("v: %v\n", v)   //v: abiao
	fmt.Printf("ok: %v\n", ok) // ok: true
}

func main() {
	test3()
}

遍历map

func tets4() {
	m1 := map[string]string{"name": "abiao", "email": "abiao.163.com"}
	for k := range m1 {
		fmt.Printf("k: %v\n", k)
	}

	for k, v := range m1 {
		fmt.Printf("%v :: %v\n", k, v)
	}

}

10、golang 函数

函数是go语言中的一级公民,我们把所有的功能单元都定义在函数中,可以重复使用。函数包含函数的名称、参数列表和返回值类型I这些构成了函数的签名(signature) 。

go语言中函数特性

1 go语言中有3种函数:普通函数、匿名函数(没有名称的函数)、方法(定义在struct上的函数)。
2. go语言中不允许函数重载(overload),也就是说不允许函数同名。
3. go语 言中的函数不能嵌套函数,但可以嵌套匿名函数。
4.函数是一个值,可以将函数赋值给变量,使得这个变量也成为函数。
5.函数可以作为参数传递给另一个函数。
6.函数的返回值可以是一个函数。
7.函数调用的时候,如果有参数传递给函数,则先拷贝参数的副本,再将副本传递给函数。
8.函数参数可以没有名称。

go语言中函数的定义和调用

函数在使用之前必须先定义,可以调用函数来完成某个任务。函数可以重复调用,从而达到代码重用。

在这里插入图片描述

package main

import "fmt"

func sum(a int, b int) (ret int) {
	ret = a + b
	return ret
}

func comp(a int, b int) int {
	var max int
	if a > b {
		max = a
	} else {
		max = b
	}
	return max
}

func main() {
	res := comp(100, 20)
	fmt.Printf("res: %v\n", res)
}

函数返回值

函数可以有0或多个返回值,返回值需要指定数据类型,返回值通过return关键字来指定。return可以有参数,也可以没有参数,这些返回值可以有名称,也可以没有名称。go中的函数可以有多个返回值。
1 return关键字中指定了参数时,返回值可以不用名称。如果return省略参数,则返回值部分必须带名称
2.当返回值有名称时,必须使用括号包围,逗号分隔,即使只有一个返回值
3.但即使返回值命名了,return 中也可以强制指定其它返回值的名称,也就是说return的优先级更高
4.命名的返回值是预先声明好的,在函数内部可以直接使用,无需再次声明。命名返回值的名称不能和函数参数名称相同,否则报错提示变重复定义
5. return 中可以有表达式,但不能出现赋值表达式,这和其它语言可能有所不同。例如return a+b 是正确的,但return c=a+b 是错误的。
在这里插入图片描述

函数参数

go语言函数可以有0或多个参数,参数需要指定数据类型。
声明函数时的参数列表叫做形参,调用时传递的参数叫做实参。
go语言是通过传值的方式传参的,意味着传递给函数的是拷后的副本,所以函数内部访问、修改的也是这个副本。
go语言可以使用变长参数,有时候并不能确定参数的个数,可以使用变长参数,可以在函数定义语句的参数部分
使用ARGS… .TYPE的方式。这时会将… 代表的参数全部保存到一个名为ARGS的slice中,注意这些参数的数据类型都是TYPE。

map、slice 、interface 、channel 这些数据类型本身就是指针类型的,所以就算是拷贝传值也是拷贝的指针,拷贝后的参数仍然指向底层数据结构,所以修改它们可能会影响外部数据结构的值。

package main

import "fmt"

func f3(args ...int) {
	for _, v := range args {
		fmt.Printf("v: %v\n", v)
	}
}

func f4(name string, ok bool, args ...int) {
	fmt.Printf("name: %v\n", name)
	fmt.Printf("ok: %v\n", ok)
	for _, v := range args {
		fmt.Printf("v: %v\n", v)
	}
}

func main() {
	f4("abc", false, 3, 4, 5)
}

高阶函数

函数作为参数

package main

import (
	"fmt"
)

func sayHello(name string) {
	fmt.Printf("Hello, %s\n", name)
}

func sayHi(name string) {
	fmt.Printf("HI,%s\n", name)
}

func test(name string, fName func(string)) {
	fName(name)
}

func main() {
	test("myname", sayHello)
	test("myname", sayHi)
}

函数作为返回值

func add(a int, b int) int {
	return a + b
}

func sub(a int, b int) int {
	return a - b
}

func cal(operator string) func(int, int) int {
	switch operator {
	case "+":
		return add
	case "-":
		return sub
	default:
		return nil

	}
}

func main() {
	subOperate := cal("+")
	res := subOperate(10, 25)
	fmt.Printf("res: %v\n", res)

	addOperate := cal("-")
	res = addOperate(100, 56)
	fmt.Printf("res: %v\n", res)
}

匿名函数

go语言函数不能成套,但是在函数内部可以定义匿名函数,实现一下简单功能调用。
所谓匿名函数就是,没有名称的函数。
在这里插入图片描述

package main

import "fmt"

func main() {

	//匿名函数
	max := func(a int, b int) int {
		if a > b {
			return a
		} else {
			return b
		}
	}
	res := max(10, 96)
	fmt.Printf("res: %v\n", res)

	//自己调用自己
	r := func(a int, b int) int {
		if a > b {
			return a
		} else {
			return b
		}
	}(15, 56)
	fmt.Printf("r: %v\n", r)
}

11、golang 闭包

闭包可以理解成定义在一个函数内部的函数。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。或者说是函数和其引用环境的组合体。
闭包指的是一个函数和与其相关的引用环境组合而成的实体。简单来说,闭包=函数+引用环境。首先我们来看一个例子:
在这里插入图片描述

package main

import "fmt"

func add() func(y int) int {
	var x int
	return func(y int) int {
		x += y
		return x
	}
}

func main() {
	f := add()
	res := f(10)
	fmt.Printf("res: %v\n", res) //res: 10
	res = f(20)
	fmt.Printf("res: %v\n", res) // res: 30
	res = f(30)
	fmt.Printf("res: %v\n", res) // res: 60

	fmt.Println("----------------------")
	fmt.Println("变量f是一个函数并且它引用了其外部作用域中的x变量,此时f就是一个闭包。 在f的生命周期内,变量x也一直有效。")
	f = add()//闭包的生命周期主要在重新引用
	r := f(100)
	fmt.Printf("r: %v\n", r) //100
	r = f(200)
	fmt.Printf("r: %v\n", r) //300
}

变量f是一个函数并且它引用了其外部作用域中的x变量,此时f就是一个闭包。 在f的生命周期内,变量x 也一直有效。

package main

import "fmt"

func cal(base int) (func(int) int, func(int) int) {

	add := func(a int) int {
		base += a
		return base
	}

	sub := func(a int) int {
		base -= a
		return base
	}
	return add, sub
}

func main() {
	add, sub := cal(100)
	r := add(100)
	fmt.Printf("r: %v\n", r) //200
	r = sub(50)
	fmt.Printf("r: %v\n", r) //150

	add1, sub1 := cal(100)
	r = add1(100)
	fmt.Printf("r: %v\n", r) //200
	r = sub1(150)
	fmt.Printf("r: %v\n", r) //5

}

12、golang递归

函数内部调用函数自身的承数称为递归函数。
使用递归函数最重要的三点:
1.递归就是自己调用自己。
2.必须先定义函数的退出条件,没有退出条件,递归将成为死循环。
3. go语言递归函数很可能会产性一大堆的goroutine, 也很可能会出现栈空间内存溢出问题。

package main

import "fmt"

//a的阶乘
func f1(a int) int {
	if a == 1 {
		return 1
	}
	return a * f1(a-1)
}

func main() {
	fmt.Printf("f1(5): %v\n", f1(5))
}

13、defer 语句

go语言中的defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行。

defer特性

1.关键字defer 于注册延迟调用。
2.这些调用直到return 前才被执。因此,可以用来做资源清理。
3.多个defer 语句,按先进后出的方式执行。
4. defer 语句中的变量,在defer声明时就决定了。

defer用途

1.关闭文件句柄
2.锁资源释放
3.数据库连接释放

package main

import "fmt"

func test1() {
	fmt.Println("satrt...")
	defer fmt.Println("step1...")
	defer fmt.Println("step2...")
	defer fmt.Println("step3...")
	fmt.Println("end...")
}

func main() {
	test1()
	/*
	结果:
	satrt...
	end...
	step3...
	step2...
	step1...
	*/
}

14、 init函数

golang有一个特殊的函数init函数,先于main函数执行,实现包级别的一些初始化操作。
init函数的主要特点
●init函数先于main函数自动执行,不能被其他函数调用;
●init函数没有输入参数、返回值;
●每个包可以有多个init函数;
包的每个源文件也可以有多个init函数,这点比较特殊;
●同一个包的init执行顺序, golang没有明确定义,编程时要注意程序不要依赖这个执行顺序。
●不同包的init函数按照包导入的依赖关系决定执行顺序。
golang初始化顺序
初始化顺序:变量初始化->init()->main()

package main

import "fmt"

var i int = initVar()

func initVar() int {
	fmt.Println("initVar")
	return 100
}

func init() {
	fmt.Println("init2...")
}

//init函数必须没有参数和返回值
func init() {
	fmt.Println("init...")
}

func main() {
	fmt.Println("main...")
}

initVar
init2…
init…
main…

init函数快捷键finit

15、指针

Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个 指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。
类型指针不能进行偏移和运算。
Go语言中的指针操作非常简单,只需要记住两个符号: & (取地址) 和*(根据地址取值)。
指针地址和指针类型
每个变量在运行时都拥有一个地址, 这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行取地址操作。Go语言中的值类型(int、 float、 bool、 string、 array、 struct)都有对应的指针类型,如: *int、 *int64、 *string等。
在这里插入图片描述

package main

import "fmt"

func main() {
	var ip *int
	fmt.Printf("ip: %v\n", ip) //ip: <nil>
	fmt.Printf("ip: %T\n", ip) //ip: *int

	var i int = 100
	ip = &i
	fmt.Printf("ip: %v\n", ip)  //ip: 0xc000122090
	fmt.Printf("ip: %v\n", *ip) //ip: 100

	var sp *string
	var s string = "hello"
	sp = &s
	fmt.Printf("sp: %T\n", sp)  //sp: *string
	fmt.Printf("sp: %v\n", *sp) //sp: hello

}

指向数组的指针

在这里插入图片描述

package main

import "fmt"

func main() {
	a := [3]int{1, 2, 3}
	var pa [3]*int
	//pa: [<nil> <nil> <nil>]
	fmt.Printf("pa: %v\n", pa)

	for i := 0; i < len(a); i++ {
		pa[i] = &a[i]
	}

	//pa: [0xc000018120 0xc000018128 0xc000018130]
	fmt.Printf("pa: %v\n", pa)

	for i := 0; i < len(pa); i++ {
		fmt.Printf("%v\t", *pa[i]) // 1	2	3
	}
}

16、类型定义和类型别名

类型定义的语法
type NewType Type
go语言类型定义和类型别名的区别
1.类型定义相当于定义了一个全新的类型,与之前的类型不同;但是类型别名并没有定义一个新的类型,而是使用一个别名来替换之前的类型
2.类型别名只会在代码中存在,在编译完成之后并不会存在该别名
3.因为类型别名和原来的类型是一致的, 所以原来类型所拥有的方法,类型别名中也可以调用,但是如果是重新定义的一个类型,那么不可以调用之前的任何方法

17、 结构体

go语言没有面向对象的概念了,但是可以使用结构体来实现,面向对象编程的一些特性,例如:继承、组合等特性。

在这里插入图片描述

结构体定义

package main

import "fmt"

//两种定义方式
type Person struct {
	id    int
	name  string
	age   int
	email string
}

type Customer struct {
	id, age     int
	name, email string
}

func main() {
	var tom Person
	fmt.Printf("tom: %v\n", tom) //tom: {0  0 }
	tom.name = "tom"
	tom.age = 20
	tom.id = 1000001
	tom.email = "tom@gmail.com"
	fmt.Printf("tom: %v\n", tom) //tom: {1000001 tom 20 tom@gmail.com}
	fmt.Printf("tom.age: %v\n", tom.age)
	fmt.Printf("tom.email: %v\n", tom.email)

	var customer Customer
	fmt.Printf("customer: %v\n", customer) //customer: {0 0  }

	//匿名结构体
	var lucy struct {
		id    int
		name  string
		age   int
		email string
	}
	lucy.id = 100002
	lucy.name = "lucy"
	lucy.age = 16
	lucy.email = "lucy123@163.com"
	fmt.Printf("lucy: %v\n", lucy) //lucy: {100002 lucy 16 lucy123@163.com}
}

golang结构体的初始化

未初始化的结构体,成员都是零值int 0 float 0.0 bool false string
nil nil

func main() {
	type Person struct {
		id    int
		name  string
		age   int
		email string
	}
	var tom Person
	tom = Person{
		id:    101,
		name:  "tom",
		age:   25,
		email: "tom@gmail.com",
	}
	fmt.Printf("tom: %v\n", tom) //tom: {101 tom 25 tom@gmail.com}
	tom = Person{1, "tom", 21, "tom.163.com"}
	fmt.Printf("tom: %v\n", tom) //tom: {1 tom 21 tom.163.com}
	//部分初始化
	tom = Person{
		id: 1, age: 20,
	}
	fmt.Printf("tom: %v\n", tom) //tom: {1  20 }
}

结构体指针

回顾普通指针

package main

import "fmt"
func main() {
	var name string
	name = "tom"
	var p_name *string
	p_name = &name
	fmt.Printf("name: %v\n", name)
	fmt.Printf("p_name: %v\n", p_name)
	fmt.Printf("* p_name: %v\n", *p_name)
	/*
		name: tom
		p_name: 0xc000054250
		* p_name: tom
	*/

}

结构体指针

package main

import "fmt"

func main() {
	test2()

}

func test1() {
	type Person struct {
		id   int
		age  int
		name string
	}
	tom := Person{
		id:   101,
		name: "mimi",
		age:  2,
	}

	var p_person *Person
	p_person = &tom
	fmt.Printf("tom: %v\n", tom)             //tom: {101 2 mimi}
	fmt.Printf("p_person: %p\n", p_person)   //p_person: 0xc0000563c0
	fmt.Printf("p_person: %v\n", p_person)   // p_person: &{101 2 mimi}
	fmt.Printf("*p_person: %v\n", *p_person) //*p_person: {101 2 mimi}

}

func test2() {
	type Person struct {
		id   int
		age  int
		name string
	}
	var Tom = new(Person)
	Tom.id = 655
	(*Tom).age = 4
	Tom.name = "TTTooMMM"
	fmt.Printf("Tom: %v\n", Tom)  //Tom: &{655 4 TTTooMMM}
	fmt.Printf("Tom: %p\n", Tom)  //Tom: 0xc0000a03a0
	fmt.Printf("Tom: %v\n", *Tom) //Tom: {655 4 TTTooMMM}
}

golang结构体作为函数参数

go结构体可以像普通变量-样,作为函数的参数,传递给函数,这里分为两种情况:
1.直接传递结构体,这是是一个副本(拷贝), 在函数内部不会改变外面结构体内容。
2.传递结构体指针,这时在函数内部,能够改变外部结构体内容。

传结构体本身

package main

import "fmt"

type Person struct {
	id   int
	name string
}

func showPerson(person Person) {
	person.id = 123456
	person.name = "abiaooo"
	fmt.Printf("person: %v\n", person) //person: {123456 abiaooo}
}

func main() {
	person := Person{
		id:   14789,
		name: "kuaile",
	}
	showPerson(person)
	fmt.Printf("person: %v\n", person) //person: {14789 kuaile}
}

传结构体指针

package main

import "fmt"

type Person struct {
	id   int
	name string
}

func showPerson2(person *Person) {
	person.id = 123456
	person.name = "abiaooo"
	fmt.Printf("person: %v\n", *person) //person: {123456 abiaooo}
}

func main() {
	person := Person{
		id:   14789,
		name: "kuaile",
	}
	showPerson2(&person)
	fmt.Printf("person: %v\n", person) //person: {123456 abiaooo}
}

嵌套结构体

go语言没有面向对象编程思想,也没有继承关系,但是可以通过结构体嵌套来实现这种效果。
下面通过实例演示如何实现结构体嵌套,加入有一个人Person结构体,这个人还养了一个 宠物Dog结构体。

package main

import "fmt"

func main() {
	type Dog struct {
		name  string
		age   int
		color string
	}

	type Person struct {
		dog  Dog
		name string
		age  int
	}

	dog1 := Dog{
		name:  "花花",
		age:   5,
		color: "white",
	}

	per := Person{
		dog:  dog1,
		age:  20,
		name: "小小",
	}
	fmt.Printf("per: %v\n", per) //per: {{花花 5 white} 小小 20}
	fmt.Printf("per.dog.name: %v\n", per.dog.name) //per.dog.name: 花花
}

18、方法

go语言没有面向对象的特性,也没有类对象的概念。但是,可以使用结构体来模拟这些特性,我们都知道面向对
象里面有类方法等概念。我们也可以声明一些方法,属于某个结构体。|
在这里插入图片描述

package main

import "fmt"

type Customer struct {
	name string
}

func (customer Customer) login(name string, password string) bool {
	fmt.Printf("customer.name: %v\n", customer.name) //customer.name: tom
	if name == "tom" && password == "abiao123" {
		return true
	} else {
		return false
	}
}

func main() {
	customer := Customer{
		name: "tom",
	}
	res := customer.login("tom", "abiao123")
	fmt.Printf("res: %v\n", res) //res: true
}

go语言方法的注意事项

1.方法的receiver type并非一定要是struct类型, type定义的类型别名、 slice、 map、channel、 func类型等都可以。
2. struct结合它的方法就等价于面向对象中的类。 不过struct可以和它的方法分开,并非一定要属于同一个文件,但必须属于同一个包。
3.方法有两种接收类型: (T Type) 和(T *Type) ,它们之间有区别。
4.方法就是函数,所以Go中没有方法重载(overload)的说法,也就是说同一个类型中的所有方法名必须都唯一。
5.如果receiver是一 个指针类型, 则会自动解除引用。
6.方法和type是分开的,意味着实例的行为(behavior)和数据存储(field)是分开的,但是它们通过receiver建立起关联关系。

golang方法接收者类型

结构体实例,有值类型和指针类型,那么方法的接收者是结构体,那么也有值类型和指针类型。区别就是接收者是否复制结构体副本。值类型复制,指针类型不复制。

值类型结构体和指针类型结构体

结构体

type Person struct {
	name string
}

func showPerson1(per Person) {
	per.name = "tom...."
}

func showPerson2(per *Person) {
	per.name = "tom...." //(*per).name = "tom...."
}

func main() {
	p1 := Person{
		name: "tom",
	}

	p2 := &Person{
		name: "tom",
	}

	showPerson1(p1)
	fmt.Printf("p1: %v\n", p1) //p1: {tom}

	showPerson2(p2)
	fmt.Printf("p2: %v\n", *p2) //p2: {tom....}

}

方法

type Person struct {
	name string
}

func (person Person) showPerson3() {
	person.name = "tom...."
}

func (person *Person) showPerson4() {
	person.name = "tom...." //(*per).name = "tom...."
}

func main() {
	p1 := Person{
		name: "tom",
	}

	p2 := &Person{
		name: "tom",
	}

	p1.showPerson3()
	fmt.Printf("p1: %v\n", p1) //p1: {tom}

	p2.showPerson4()
	fmt.Printf("p2: %v\n", *p2) //p2: {tom....}

}

19、golang接口

接口像是一-个公司里面的领导,他会定义一些通用规范,只设计规范,而不实现规范。
go语言的接口,是-种新的类型定义,它把所有的具有共性的方法定义在一起, 任何其他类型只要实现了这些方法就是实现了这个接口。
语法格式和方法非常类似。

接口语法格式

在这里插入图片描述

package main

import "fmt"

//vscode快捷键 tyi--type name interface
type USB interface {
	read()
	write()
}

//vscode快捷键 tys--type name interface
type Computer struct {
	name string
}

//vscode快捷键 tys--type name interface
type Mobile struct {
	model string
}

func (c Computer) read() {
	fmt.Printf("c.name: %v\n", c.name)
	fmt.Println("read....")
}

func (c Computer) write() {
	fmt.Printf("c.name: %v\n", c.name)
	fmt.Println("write....")
}

func (m Mobile) read() {
	fmt.Printf("m.model: %v\n", m.model)
	fmt.Println("read....")
}

func (m Mobile) write() {
	fmt.Printf("m.model: %v\n", m.model)
	fmt.Println("write....")
}

func main() {
	c := Computer{
		name: "dell",
	}
	c.read()
	c.write()

	m := Mobile{
		model: "5G",
	}
	m.read()
	m.write()
}

c.name: dell
read....
c.name: dell
write....
m.model: 5G
read....
m.model: 5G
write....

golang接口值类型接收者和指针类型接收者

这个话题,本质上和方法的值类型接收者和指针类型接收者,的思考方法是一样的,值接收者是一个拷贝, 是一个副本,而指针接收者,传递的是指针。

package main

import "fmt"

type Pet interface {
	eat(string) string
}

type Dog struct {
	name string
}

// func (dog Dog) eat(name string) string {
// 	dog.name = "嘟嘟"
// 	fmt.Printf("name: %v\n", name)
// 	return "吃的好肥"
// }

func (dog *Dog) eat(name string) string {
	dog.name = "嘟嘟肥柯基"
	fmt.Printf("name: %v\n", name) //name: 大骨头
	return "吃的好肥"
}

func main() {
	dog := &Dog{
		name: "蛋蛋小柯基",
	}
	ret := dog.eat("大骨头")
	fmt.Printf("dog: %v\n", *dog) //dog: {嘟嘟肥柯基}
	fmt.Printf("ret: %v\n", ret)  //ret: 吃的好肥
}

golang接口和类型的关系

1.一个类型可以实现多个接口
2.多个类型可以实现同一个接口(多态)
一个类型实现多个接口
一个类型实现多个接口,例如:有一个Player接口可以播放音乐,有一个Video接口可以播放视频,一个手机Mobile
实现这两个接口,既可以播放音乐,又可以播放视频。

一个类型可以实现多个接口

package main

import "fmt"

type Player interface {
	playMusic()
}

type Video interface {
	playVideo()
}

type Mobile struct {
}

func (mobile Mobile) playMusic() {
	fmt.Println("play music")
}

func (mobile Mobile) playVideo() {
	fmt.Println("play video")
}

func main() {
	mobile := Mobile{}
	mobile.playMusic()
	mobile.playVideo()
}

多个类型实现同一个接口

比如,一个宠物接口Pet,猫类型Cat和狗类型Dog都可以实现该接口,都可以把猫和狗当宠物类型对待,这在其他语言中叫做多态。

package main

import "fmt"

type Pet interface {
	eat()
}

type Cat struct {
}

type Dog struct {
}

func (cat Cat) eat() {
	fmt.Println("cat eat...")
}

func (dog Dog) eat() {
	fmt.Println("dog eat...")
}

func main() {
	// cat := Cat{}
	// dog := Dog{}
	// cat.eat()
	// dog.eat()

	var pet Pet
	pet = Dog{}
	pet.eat() //dog eat...
	pet = Cat{}
	pet.eat() //cat eat...

}

接口嵌套

接口可以通过嵌套,创建新的接口。例如:飞鱼,既可以飞。又可以游泳。我们创建一个飞Fly接口, 创建一个游泳接口Swim,"飞鱼接口由这两个接口组成。

package main

import "fmt"

type Flyer interface {
	fly()
}

type Swimmer interface {
	swim()
}

type FlyFish interface {
	Flyer
	Swimmer
}

type Fish struct{}

func (fish Fish) fly() {
	fmt.Println("fly........")
}

func (fish Fish) swim() {
	fmt.Println("swim........")
}

func main() {
	var fish FlyFish
	fish = Fish{}
	fish.fly()  //fly........
	fish.swim() //swim........
}

通过接口实现OCP设计原则

而面向对象的可复用设计的第一块基石, 便是所谓的”开闭“原则(Open-Closed Principle,常缩写为0CP)。虽然,go不是面向对象语言,但是也可以模拟实现这个原则。
对扩展开放、对修改关闭

package main

import "fmt"

type Pet interface {
	eat()
	sleep()
}

type Dog struct {
}

type Cat struct {
}

type Person struct {
}

func (dog Dog) eat() {
	fmt.Println("Dog,eat........")
}

func (dog Dog) sleep() {
	fmt.Println("Dog,sleep........")
}

func (cat Cat) eat() {
	fmt.Println("cat,eat........")
}

func (cat Cat) sleep() {
	fmt.Println("cat,sleep........")
}

//Pet既可以传递Dog也可以传递Cat
func (person Person) care(pet Pet) {
	pet.eat()
	pet.sleep()
}

func main() {
	person := Person{}
	dog := Dog{}
	cat := Cat{}
	person.care(dog)
	person.care(cat)
}

20、golang模拟OOP的属性和方法

golang没有面向对象的概念,也没有封装的概念,但是可以通过结构体struct和函数绑定来实现0OP的属性和方法等特性。接收者receiver方法。
例如,想要定义一个Person类,有name和age属性, 有eatsleep/work方法。

package main

import "fmt"

type Person struct {
	name string //公共的是Name,首字母大写
	age  int
}

func (person Person) eat() {
	fmt.Println("eat.........")
}

func (person Person) sleep() {
	fmt.Println("sleep.........")
}

func (person Person) work() {
	fmt.Println("work.........")
}

func main() {
	tom := Person{
		name: "tom",
		age:  25,
	}
	fmt.Printf("tom: %v\n", tom)
	tom.sleep()
	tom.eat()
	tom.work()
	tom.eat()
	tom.sleep()
}

21、 继承

golang本质上没有ogp的概念, 也没有继承的概念,但是可以通过结构体嵌套实现这个特性。

package main

import "fmt"

type Animal struct {
	name string
	age  int
}

func (a Animal) eat() {
	fmt.Println("eat.....")
}

func (a Animal) sleep() {
	fmt.Println("sleep.....")
}

type Dog struct {
	a     Animal //可以理解为继承
	color string
}

type Cat struct {
	a     Animal //可以理解为继承
	breed string
}

type Dog2 struct {
	Animal //可以理解为继承,匿名
	color  string
}

type Cat2 struct {
	Animal //可以理解为继承,匿名
	breed  string
}

func main() {
	dog := Dog{
		a:     Animal{name: "嘟嘟", age: 3},
		color: "brown",
	}

	dog.a.eat()
	dog.a.sleep()

	//匿名————直接调用
	dog2 := Dog2{
		Animal{name: "嘟嘟", age: 3},
		"black",
	}

	//匿名————直接调用
	cat2 := Cat2{
		Animal{"花花", 23},
		"labuladuo",
	}

	//匿名————直接调用
	dog2.eat()
	dog2.sleep()
	fmt.Printf("dog2.color: %v\n", dog2.color)

	//匿名————直接调用
	cat2.eat()
	cat2.sleep()
	fmt.Printf("cat2.breed: %v\n", cat2.breed)
}

22、构造函数

package main

import "fmt"

type Person struct {
	name string
	age  int
}

func NewPerson(name string, age int) (*Person, error) {
	if name == "" {
		return nil, fmt.Errorf("name 不能为空")
	}
	if age < 0 {
		return nil, fmt.Errorf("age 不能小于0")
	}
	return &Person{name: name, age: age}, nil
}

func main() {
	person, err := NewPerson("aBiao", 15)
	if err == nil {
		fmt.Printf("person: %v\n", *person)
		fmt.Printf("err: %v\n", err)
	}
}

23、包

包可以区分命令空间(一个文件夹中不能有两个同名文件),也可以更好的管理项目。go中创建一 个包,-般
创建一 个文件夹,在该文件夹里面的go文件中,使用package关键字声明包名称,通常,文件夹名称和包名称相
同。并且,同一个文件下面只有一个包

在这里插入图片描述

包注意事项
●一个文件夹下只能有一个package
0 import后面的其实是‘GOPATH
开始的相对目录路径,包括最后-段。但由于一个目录下只能有一个
package,所以import一个路径就等于是import了这个路径下的包。
。注意,这里指的是“直接包含”的go文件。如果有子目录,那么子目录的父目录是完全两个包。
●比如你实现了一个计算器package,名叫calc,位于calc录下;仅想给别人一个使用范例,于是在ca
下可以建个example子眼(calc/example/) , 这个子目录里有个example.go (calc/example/example.go)。
时,example.go可以是main包, 面还可以有个main函数。
●一个package的文件不能在多个文件夹下
。如果多个文件夹下有重名的package, 它们其实是彼此无关的package。
。如果一个go文件需要同时使用不同目录下的同名package,需要在import这些目录时为每个目录指定一
个package的别名。

go module

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
自动编译到缓存里,然后可以搜索到

在这里插入图片描述

…待续

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值