当前位置:   article > 正文

Go语言基础(一篇上手go语言基本语法)

go语言

Go简介

Go语言的创始人有三位,分别是图灵奖获得者、C语法联合发明人、Unix之父肯·汤普森(Ken Thompson)、Plan 9操作系统领导者、UTF-8编码的最初设计者罗伯·派克(Rob Pike),以及Java的HotSpot虚拟机和Chrome浏览器的JavaScript V8引擎的设计者之一罗伯特·格瑞史莫(Robert Griesemer),三位大牛领导设计。

在这里插入图片描述

Go语言是一种静态编译语言,却有动态语言高效率,继承了c语言的表达式语法,控制结构,指针等;Go语言引入报的概念,一个文件归属一个包,不能独立存在;Go语言从底层支持高并发。

Go的应用领域:区块链应用、后端服务器应用、云计算和云服务开发。

基础语法

编程规范

Go语言官方推荐使用行注释来注释整个方法和语句;不同行的代码之间要有正确的缩进;gofmt能够打印源代码;每行字符不能太多合理使用换行符。

Go的函数、变量、常量、自定义类型、包(package)的命名方式统称为标识符,标识符由数字,字母,下划线组成·,数字不能开头,严格区分大小写,_是go的一个特殊字符,可以代表任何字符,但是其值会被忽略。不能用关键字当标识符。

在这里插入图片描述

关键字与保留字

  • Go的关键字
 break        default      func         interface    select
 case         defer        go           map          struct
 chan         else         goto         package      switch
 const        fallthrough  if           range        type
 continue     for          import       return       var
  • 1
  • 2
  • 3
  • 4
  • 5
  • 保留字
 true  false  iota  nil

 int  int8  int16  int32  int64  
 uint  uint8  uint16  uint32  uint64  uintptr
 float32  float64  complex128  complex64
 bool  byte  rune  string  error

make  len  cap  new  append  copy  close  delete
complex  real  imag
panic  recover
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

定义变量

在这里插入图片描述

/*变量*/
var i = 10  //初始化定义
var j int
j = 10
fmt.Println("初始化声明定义:")
fmt.Println(i,j)

fmt.Println("未初始化定义:")
var a int
var b int64
fmt.Println(a,b)


fmt.Println("类型推导")
var c = "hello go"
fmt.Println(c)

fmt.Println("简洁写法")
d := 100
fmt.Println(d)


fmt.Println("多变量声明")
var x,y,z int = 10,11,12
fmt.Println(x,y,z)

fmt.Println("有初始化的变量声明")
var n1,n2,n3 = 100, "jack",12.5
fmt.Println(n1,n2,n3)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

定义函数
func关键字定义函数,如下所示
在这里插入图片描述

//func 关键字
func [methodName] ([参数列表及类型])  返回值类型{
	//代码块
	return [返回值]
}

//例如主函数
package test
import "fmt"
func main(){
	fmt.Println("Hello World")
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

定义函数的左中括号不能出现在下一行。以及所有形似该定义的方式

匿名函数

go支持匿名函数,如果某个函数只使用一次,可以使用匿名函数
在这里插入图片描述
在这里插入图片描述
匿名函数也可以赋值给一个变量,则改变了也变成了一个函数,可以通过该变量调用匿名函数:

在这里插入图片描述

这点和js语法有点像。

在这里插入图片描述

标识符大小写控制访问权限

可变参数
在函数中可以传递可变参数,Go语言支持可变参数,接收变参的函数时有着不定数量的参数,在定义函数的参数时,需要使其接收可变参数。可变参数如下定义:

func myfunc(args ...int){}

参数传递后会以切片的形式接收,对切片的操作可以实现对参数的运用:

func main(){
	getSum(1,2,3,4,5)
}
  • 1
  • 2
  • 3
func getSum(nums ... int)  {
	sum := 0
	for i := 0; i < len(nums); i++ {
		sum += nums[i]
	}
	fmt.Println("总和:",sum)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

除了可变参数,go的指针传值也需要注意:

//主函数
func main(){
	var a = 99
	fmt.Println("定义的变量为:",a)
	pin(&a)
	fmt.Println("函数调用后变量为",a)
}

//方法·
func pin(p *int){
	fmt.Println("传入时指变量的值",*p)
	*p = 100
	fmt.Println("传入后的指针变量的值",*p)
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述
指针作为参数会改变变量的值。

返回值

go语言的函数支持多个返回值,而不需要使用数据结构如集合,数组等包装

package main

import "fmt"

func operation(n int, m int) (int, int, int, int) {
	return n + m, n - m, n * m, n / m
}
func main() {
	i, i2, i3, i4 := operation(4, 2)
	fmt.Println(i, i2, i3, i4)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

没有任何返回值就不写。

转义字符

(1)\\一个\,在go中\为转义字符
(2) \t一个制表位,实现对齐功能
(3) \n换行符
(4) \r一个回车位,从当前行最前行覆盖输出

import "fmt"

func main()  {
	//fmt.Println("Hello World")
	fmt.Println("姓名\t年龄\t籍贯\t住址")
	fmt.Println("john\t12\t河北\t北京")
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

注释

(1)行注释://注释文字

(2)块注释:/* 注释文字 */,块注释不能嵌套

换行符

,是go的手动换行符、\n是go的自动换行符

import (
	"fmt"
)

func main() {
	//手动换行符
	fmt.Println("螃蟹在剥我的壳,笔记本在写我。",
		"漫天的我落在枫叶上雪花上。",
		"而你在想我。")
	//自动换行符
	fmt.Println("螃蟹在剥我的壳,笔记本在写我。\n漫天的我落在枫叶上雪花上。\n而你在想我。")
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

数据类型

在这里插入图片描述

  • 基本数据类型
    在go中,字符本质是一个整数,直接输出时是该字符对应的utf-8编码的的码值,因此字符可以直接用int等基本数据类型类型接收,要输出字符需要格式化输出%d,%c等。

数值型的各个关键字标识不同范围的类型,一个int4各字节,一个字节是8位,能表示的范围0-512,以此计算,各个类型的范围在合适的区间类选择相应类型。

go中没有专门的字符类型,字符都是在go用会转为位utf-8所对应的码值,因此字符直接用整数类型接收,需要注意的是不要字符的编码值要在数值类型所表示的范围内。

字符串有两种标识方法,""为普通字符会识别特殊符号,`` 反引号为特殊字符,不会识别特殊符号,原样保存字符。字符串的拼接用+,多个字符串拼接式+需要留在上一行,不能作为开头。

基本数据类型未初始化是会有默认值的,如下图:

在这里插入图片描述

数据类型的转换

go的不数据类型的变量之间赋值需要显示转换,不能制动转换。也就是在转换时需要声明转换的类型。

语法表达式:类型(转换值)

//类型转换
var i int32 = 100
var num float32 = float32(i)
fmt.Println(num)
  • 1
  • 2
  • 3
  • 4

go中可以用_丢弃不需要的变量(go中未使用的变量会报错)

//_忽略变量
var b,_ = "a", "err"
fmt.Println(b)
  • 1
  • 2
  • 3

指针

变量分配的内存存储的是变量的值,变量之间的数据传递叫值传递,值传递会产生新的变量和内存空间,即对原变量的直接克隆;而引用是对已有变量的别名,对引用变量的操作极即为对原变量的操作,没有产生新的内存空间;而指针是指向内存空间的标识,实现了对该段内存空间的绝对控制,用于存储或操作变量。

三种变量的传值方式为值传递,引用传递,指针传递,都是为了优化内存空间和减少无效变量创建的。

&用于获取变量的地址,*用于定义指针,指针变量的本身为变量地址。

var a int = 10
var b = &a
fmt.Println("b变量的地址:",b)

var pointer * int = &a
fmt.Println("指针变量为:",pointer)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

通过*指针变量来取内存的值

var pin = *pointer
fmt.Println("指针的值为:",pin)
  • 1
  • 2

值类型都有对应的指针类型,形如*数据类型,int对应的就是int,int64对应int64,类型要匹配。

访问权限

在这里插入图片描述

首字母的大小写限制访问权限。

包管理于引入包

  • 包简介

包在go语言中十分重要,每个文件都是都属于一个包,go以包的形式来管理文件和项目目录结构。

在这里插入图片描述

包通过import关键字引入,内置包直接通过函数名引入,其他通过路径引入import 包名/包名,也可以在引入时为包重命名语法: import [名称] 包名/包名

  • 引入内置包
//使用包的方法时许哟先引入包
import "包名"
import ([包名])

//一个go文件归属于一个包package
package main
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 引入自定义包

在这里插入图片描述

导入自定义模块必须要从根目录开始,不然go就会在配置的GOROOT下寻找,当作内置内置模块包来查找。

在这里插入图片描述

package main
import "fmt"
import "unit-2/src/main/demo04"
func main(){
	var a = demo04.Add(1,4)
	fmt.Println(a)

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
package demo04

func Add(a int,b int) int {
	return a+b
}
  • 1
  • 2
  • 3
  • 4
  • 5

go文件的命名在导入包的过程中为起到作用,主要是包名,和方法名,变量名等作为导入的内容。首字母大写为共有,首字母小写为私有。

在这里插入图片描述

在统一包下不能有相同的函数名,如果要编译成一个可执行文件,就需要将这个包声明为main,且存在主函数main函数。

go的每一个源文件都有一个init函数,init函数会在main函数之前完成,每个init执行的顺序与引入的顺序有关。

运算符

大多数语言的运算符都功能都是差不多的。

流程管理

  • 顺序结构

代码本身的执行流程为顺序结构。

switch语句用于基于不同条件执行不同动作,每个分支都是唯一的,从上到下注意测试,直到匹配位置,因此switch也可以看作是顺序结构。

在这里插入图片描述

switch 表达式{
	case 表达式1,表达式2,... :
		语句块1
	case 表达式3,表达式4,... :
		语句块2
	// 若干case语句
	default :  语句块
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

    表达式可以是任意变量,常量,有返回值的函数等。

    在这里插入图片描述

    fallthrough穿透,默认只能穿透下一层,执行一个case后会继续网后执行一个case。

    • 分支结构
    if  表达式{
    	//代码块
    }else{
    	//代码块
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    表达式为true是就会执行{}的代码,{}不可省略,if后的{不能出现在下一行,else紧跟上一个{不能出现在第二行。表达式也可以用()括起来。

    if  表达式{
    	//代码块
    }
    else if (表达式){
    
    }{
    .....
    }else{
    	//代码块
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    省略号位置可以扩充任意else if,同样满足{不出现下一行。

    import "fmt"
    
    func main()  {
    	var age int
    	fmt.Println("输入年龄:")
    	fmt.Scanln(&age)
    	idfi(age)
    
    }
    
    //判断年龄的分支结构
    func idfi(a int){
    	if (a <= 18) {
    		fmt.Println("未成年")
    	}else if (a>18){
    		fmt.Println("已成年")
    	}else {
    		fmt.Println("输入错误")
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 循环结构

    go的循环的关键字是for,形如:

    在这里插入图片描述

    for i := 0; i < count; i++ {
    		fmt.Println("hello go")
    	}
    
    • 1
    • 2
    • 3
    func forTest(){
    	var count =10
    	for i := 0; i < count; i++ {
    		fmt.Println("hello go")
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    对于for循环有4个要素,循环的初始化,循环的条件,循环体。

    在这里插入图片描述
    for关键字后面是循环的条件,该条件可以被分块至其他位置,如下:

    var j = 0
    for j < 10 {
    	fmt.Println("hello")
    	j++
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    还有一种无线循环的写法,如下:

    var i int 
    for{
    	i++
    	if(i == 100){
    		fmt.Println("100")
    		break
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    该循环等价于 for ; ;由于没有结束条件,所以编程了死循环,需要借助break结束循环。

    高阶for循环 for-range:

    for-range循环自带索引和值,常用于遍历属猪,集合,切片等,其用法如下:

    index,item := range [需遍历的值]

    var str = "asdfghjA"
    for index,val := range str{
    	fmt.Println(index,val)
    }
    
    • 1
    • 2
    • 3
    • 4

    字符会按utf-8的编码转换为对应的整数,所以需要格式化输出%d,%c。在循环中常用到的的break,continue分别是结束循环和跳过本次循环。
    在这里插入图片描述

    func gotoTest()  {
    	
    	fmt.Println("start")
    	goto programming1
    	for i := 0; i < 10; i++ {
    		fmt.Println("aaaaaaaaaa")
    	}
    	programming1:
    	fmt.Println("finish")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    goto关键字后接程序跳转的入口,自定义命名,在任意位置引用该命名程序入口加:即可跳转到该处。

    闭包

    在这里插入图片描述

    闭包简单来说就是函数内部定义函数,内部函数调用外部函数的变量会使外层函数变量的值持久化。

    数组

    数组定义形如:
    var arr_name [arr_length] type_name

    arr_name 表示定义数组名称
    arr_length 表示数组长度
    type_name 表示数组元素存储的数据类型

    var arr_name = [arr_length] type_name{value1,value2 ....}

    var arr_name = [...] type_name {value1,value2...}

    ...表示不定长度的数组

    go语言也支持make函数定义数组,make函数可以动态创建数组,指定数组长度,容量以及数组元素类型:

    var arr_name = make ([]type_name,length,capacity)

    arr_name表示数组名称
    type_name数组元素存储的数据类型
    length数组长度
    capacity 数组容量

    var numbers  = make([]int ,5 ,10)
    
    • 1

    切片

    切片是对数组的抽象,其本身没有任何数据,只是对现有的数据的引用。

    切片定义:

    var identify []type

    切片不需要说明长度(这是与定义数组的关键区别),或者使用make函数来创建切片:

    这个和python的切片是不一样的,python的切片可以对任意有索引的的数据结构直接使用,go既可定义切片也可以应用切片。

    ## python
    a = [1,2,3,4,5]
    b = a[:3] 
    print(b)
    
    • 1
    • 2
    • 3
    • 4

    go对数组切片的引用:

    sli := arr[start:end]
    sli := arr[: end]
    sli := arr[start:]

    var arr1 = [5]int{1,2,3,4,5}
    arr2 := arr1[:3]
    fmt.Println(arr2)
    
    • 1
    • 2
    • 3

    map

    在这里插入图片描述

    在这里插入图片描述

    var map1 map[int]string
    var map2 := make(map[int]string)
    var map3 := map[int]string{1:"go",2:"java"}
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    可以通过map[key]来获取,由于当key不存在时,go会获取value的默认值,所以优势并不能按程序应有的结果走,需要判断一下,使用ok-idiomvalue,ok :=map[key]。当然也可以直接通过默认值来判断,但这是需要知道value的类型的情况下,通过该类型的默认值判空。

    var map1 map[int]string
    var map2 = make(map[int]string)
    var map3 = map[int]string{1:"go",2:"java"}
    
    fmt.Println(map1,map2,map3)
    
    val,ok := map3[1]
    if ok {
    	fmt.Println("map3的值不为空,其值为:",val)
    }else{
    	fmt.Println("map3的值为空")
    }
    
    val1:= map3[2]
    if val1 == "" {
    	fmt.Println("map3为空")
    }else{
    	fmt.Println(val1)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    map通过key来添加和修改,如果存在key就修改,不存在就添加;删除1通过内置的delete(map,key)函数。

    map是无序的只能通过for-range来遍历:

    var map3 = map[int]string{1:"go",2:"java",3:"python"}
    for k,v := range map3{
    	fmt.Println(k,v)
    }
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    结构体

    结构体

    数组可以存储同一类型的数据,结构体可以为不同项定义不同的数据类型,结构体是由一系列据具有相同或不同类型的数据构成的集合。

    • 结构体的定义

    在这里插入图片描述

    结构体需要借助typestruct两个关键字定义,仍然同首字母的大小写来决定外部是否可以访问:

    type Person struct{
    	name string
    	age int
    	sex string
    	address string
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    go语言抛弃了面向对象的那些复杂的特性,通过结构体来实现数据结构。另外结构体声明后就相当于自定义的数据类型,需要声明再使用。

    • 结构体访问

    结构体成员直接通过.来访问,如下

    package main
    
    import "fmt"
    
    func main()  {
    
    	makeP1()
    }
    
    type Person struct{
    	name string
    	age int
    	sex string
    	address string
    }
    
    func makeP1(){
    	var p1 Person
    	p1.name = "_小许_"
    	p1.age = 21
    	p1.sex = "男"
    	p1.address = "河北"
    
    	fmt.Println(p1)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在这里插入图片描述
    除了声明式定义外还啊看可以初始化定义:

    //方法一
    func makeP2(){
    	var p2 = Person{}
    	p2.name = "_小许_"
    	p2.age = 21
    	p2.sex = "男"
    	p2.address = "河北"
    
    	fmt.Println(p2)
    }
    //方法二
    func makeP3(){
    	var p3 = Person{name:"_小许_",age:21,sex:"男",address:"河北"}
    	fmt.Println(p3)
    }
    
    //方法三
    func makeP4(){
    	p4 := Person{
    		"小许",
    		21,
    		"男",
    		"河北",
    	}
    	fmt.Println(p4)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    指针变量修改:

    func makeP5(){
    	p5 := Person{
    		"小许",
    		21,
    		"男",
    		"河北",
    	}
    	fmt.Println("改变前p5:",p5)
    	var p *Person = &p5
    	(*p).age = 18
    	fmt.Println("改变后p5:",p5)
    	fmt.Println("指针变量的值:",*p)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    在这里插入图片描述

    结构体也可以通过new关键字创建,该关键字是创建创建一段新的内存空间,相当于一一个*T类型,作用是创建某种类型的指针函数。如下:

    func makeP8(){
    	p8 := new(Person)
    	(*p8).name = "_xiaoxu_"
    	(*p8).age = 18
    	fmt.Println(*p8)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    需要注意的是传递的参数是内存地址,通过指针类型修改。

    在这里插入图片描述

    • 结构体指针

    go中没有opp面向对象的该概念,因此自定义的结构体将作为主要的对象来进行参数的传递。不同于java等语言,go既有值传递又有引用传递,引用传递需要借助指针来进行:

    值传递,和引用传递需要区分,前者创建了新的内存空间,后者是对已定义的变量的地址引用。

    //指针传递结构体
    func makeBook3(book *Books){
    	fmt.Println("图书的名称:",(*book).name)
    	fmt.Println("图书的价格:",(*book).price)
    	fmt.Println("图书的作者:",(*book).author)
    }
    
    //函数返回Books类型
    func reBooks() (Books){
    	book := Books{
    		name :"三国演义",
    		price : 22.1,
    		author : "罗贯中",
    	}
    	return book
    }
    
    func main()  {
    	book := reBooks()
    	makeBook3(&book) 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    案例是对一个结构体指针传递的应用,主函数中book返回了一个Books结构体变量,makeBook3方法参数类型是Books的指针类型,在go中用*定义指针,其类型是一个指针类型,指针是go的一种数据类型。在实际中,指针又是又是表示一个变量的地址,因此在参数传递时,传入的参数是地址(指针类型变量所表示的值)。

    除了函数可以匿名,结构体也可以匿名,但是根据匿名的特性,是无法被调用的无法被修改的,所以只能在定义的地方赋值和使用:

    //结构体测试
    b := struct{
    	name string
    	age int
    }{
    	//直接赋值
    	"_小许_",
    	22,
    }
    fmt.Println(b)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    定义一个结构体就相当于在go源文件中定义了一个新的数据类型,这个数据类型可以像基本数据类型那样调用,但是需要按照结构体规则进行。

    //结构体嵌套
    type Liabary struct{
    	name string
    	address string
    	room Room
    }
    
    type Room struct{
    	name  string
    	pid  string 
    }
    
    
    func makeRoom(){
    	//第一种创建方式
    	lia := Liabary{}
    	lia.name = "_xiaoxu_"
    	lia.address = "教八"
    	lia.room.name = "五楼"
    	lia.room.pid = "511"
    	fmt.Println(lia)
    
    	//第二种创建方式
    	lia2 := Liabary{
    		name : "aaa",
    		address : "bbb",
    		room : Room{name : "ccc",pid : "ddd"},
    	}
    	fmt.Println(lia2)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    面向对象

    面向对象的三大特性:继承,封装和多态。Go没有面向对象的概念,但是可以通过结构体模拟面向对象的特性。

    • 继承

    已动物的继承关系模拟继承的代码:
    在这里插入图片描述

    目录结构:

    在这里插入图片描述

    /*
    animals包
    */
    package animals
    
    //定义一个动物
    type Animals struct{
    	name string 
    	color string
    	age int
    	shot string
    }
    
    
    /*
    	狗继承动物
    */
    
    type Dog struct{
    	animals Animals   //结构体的嵌套模拟继承关系
    	foot string
    	teech string 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    package dog
    
    import "unit-2/src/main/demo11/animals"
    
    /*
    宠物狗继承狗
    */
    type PetDog struct{
    	animals animals.Animals
    	homedog string
    	listen string
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    package main
    
    import "unit-2/src/main/demo11/dog"
    import "unit-2/src/main/demo11/animals"
    import "fmt"
    
    /*
    主函数代码
    */
    func main()  {
    	petDog := dog.PetDog{
    		animals : animals.Animals{				
    			name :"小狗",
    			color : "白色",
    			age : 1,
    			shot : "旺旺...",
    		},
    		dog1: dog.Dog {
    			foot : "四条腿",
    			teech : "肉食动物",
    		},
    		homedog : "宠物狗",
    		listen : "听指挥",
    	}	
    
    	fmt.Println(petDog)
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    案例的主程序代码,创建了小外层的对象,PetDog同时继承了Dog和Animals,但上述代码是由问题的,如下:

    在这里插入图片描述
    unknown field 'animals' in struct literal of type dog.PetDog

    这是由于go的访问权限的限制,由于变量首字母的大小写决定了访问权限,所以大概就有三个部分的可访问性:结构体本身、结构体变量、结构体变量的成员变量在同包小无限制随意访问。有如下限制:

    1. 结构体名的大小写影响结构体本身的可访问性,首字母小写则包外不可见;
    2. 结构体变量名的首字母大小写 将影响其在包外的可访问性;
    3. 同包内,结构体变量的成员变量可随时被访问,不受首字母大小写的影响

    相信很多学过Java的会疑惑,这和Java的访问权限控制系统private,protected,default,public不太一样啊,注意不要联想,每个语言都有自己的规则,按照规则记忆即可,Go的权限访问系统相对简单。

    由于访问权限的限制,需要对代码重构,修改权限,如下:

    /*
    animals包下
    */
    package animals
    
    //定义一个动物
    type Animals struct{
    	Name string 
    	Color string
    	Age int
    	Shot string
    }
    
    
    
    //狗继承动物
    type Dog struct{
    	Danimals Animals
    	Foot string
    	Teech string 
    }
    
    /*
    dog包下
    */
    package dog
    
    import "unit-2/src/main/demo11/animals"
    
    //定义宠物狗
    type PetDog struct{
    	Pdog animals.Dog
    	Homedog string
    	Listen string
    }
    
    /*
    主函数包下
    */
    package main
    
    import "unit-2/src/main/demo11/dog"
    import "unit-2/src/main/demo11/animals"
    import "fmt"
    
    //主函数
    func main()  {
    	//最外层宠物狗
    	petDog := dog.PetDog{
    		//内层狗
    		Pdog: animals.Dog {
    			//动物类
    			Danimals : animals.Animals{				
    				Name :"小狗",
    				Color : "白色",
    				Age : 1,
    				Shot : "旺旺...",
    			},
    			Foot : "四条腿",
    			Teech : "肉食动物",
    		},
    		Homedog : "宠物狗",
    		Listen : "听指挥",
    	}	
    
    	fmt.Println(petDog)
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    代码重构只是将结构体和结构体变量的首字母改为大写或首字母大写的重命名,改完后在外包也可以访问到,报错消失:

    在这里插入图片描述
    运行成功:
    在这里插入图片描述

    这家伙比面向对象编程的继承可难搞多了,继承三层就这莫深了,难搞喔!

    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小惠珠哦/article/detail/897153
    推荐阅读
    相关标签