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

发布时间:2025-12-09 18:43:19 浏览次数:4

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 selectcase defer go map structchan else goto package switchconst fallthrough if range typecontinue for import return var
  • 保留字
true false iota nilint int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptrfloat32 float64 complex128 complex64bool byte rune string errormake len cap new append copy close deletecomplex real imagpanic recover

定义变量

/*变量*/var i = 10 //初始化定义var j intj = 10fmt.Println("初始化声明定义:")fmt.Println(i,j)fmt.Println("未初始化定义:")var a intvar b int64fmt.Println(a,b)fmt.Println("类型推导")var c = "hello go"fmt.Println(c)fmt.Println("简洁写法")d := 100fmt.Println(d)fmt.Println("多变量声明")var x,y,z int = 10,11,12fmt.Println(x,y,z)fmt.Println("有初始化的变量声明")var n1,n2,n3 = 100, "jack",12.5fmt.Println(n1,n2,n3)

定义函数
func关键字定义函数,如下所示

//func 关键字func [methodName] ([参数列表及类型]) 返回值类型{//代码块return [返回值]}//例如主函数package testimport "fmt"func main(){fmt.Println("Hello World")}

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

匿名函数

go支持匿名函数,如果某个函数只使用一次,可以使用匿名函数


匿名函数也可以赋值给一个变量,则改变了也变成了一个函数,可以通过该变量调用匿名函数:

这点和js语法有点像。

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

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

func myfunc(args ...int){}

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

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

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

//主函数func main(){var a = 99fmt.Println("定义的变量为:",a)pin(&a)fmt.Println("函数调用后变量为",a)}//方法·func pin(p *int){fmt.Println("传入时指变量的值",*p)*p = 100fmt.Println("传入后的指针变量的值",*p)}

函数定义时指出,函数定义时有参数,该变量可称为函数的形参。形参就像定义在函数体内的局部变量。

但当调用函数,传递过来的变量就是函数的实参,函数可以通过两种方式来传递参数:

  • 值传递:指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
  • 引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
  • 值传递是值的拷贝。引用传递是地址的拷贝,一般来说,地址拷贝更为高效。而值拷贝取决于拷贝的对象大小,对象越大,则性能越低。

    • 方法

    Golang 方法总是绑定对象实例,并隐式将实例作为第一实参。一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。

    简单来说就是方法必须依赖于某个变量,可以是命名类型或者结构体类型的一个值或者是一个指针,而函数可以单独存在。

    这是对象定义函数的体现,变量,结构体内部不能定义函数,需要在外部定义函数指向变量,如下

    说到方法依赖于某个变量,那么其调用也必须通过变量该调用,不能直接像函数一样调用:

    package mainimport "fmt"type Person struct{name string age int}//定义方法func (per Person) futrue( age int){age = per.ageif(age > 18){fmt.Println("上大学")}if(age < 18){fmt.Println("上小学")}}func main(){p1 := Person{"_小许_",20,}fmt.Println(p1)p1.futrue(0)}

    在上面的程序中,定义了一个futrue方法指向Person结构体,通过指向的结构体变量参数可以调用结构体的成员变量,通过方法的参数可以于结构体参数交互。

    go包含指针的概念,所以任何关于参数传递都有两种方式,值传递和指针传递,这里指针传递自行思考。

    方法的意义在于,实现类的行为,模拟面向对象编程的多态。

    go结构体模拟继承中,方法通过指向不同的接收者也实现了方法的继承,方法名同的话,子类方法会覆盖父类方法(方法的重写)。

    package mainimport "fmt"type Person struct{name string age int}//定义方法func (per Person) futrue( age int){age = per.ageif(age > 18){fmt.Println("上大学")}if(age < 18){fmt.Println("上小学")}}//定义Person的子类type Student struct{person Personaddress string}//重写父类的方法func (stu Student) futrue(age int){age = stu.person.ageif(age>= 3 && age <7 ){fmt.Println("孩子在上学前班")}if(age >= 7 && age <=13){fmt.Println("孩子在上小学")}if(age >13 && age <= 16){fmt.Println("孩子在上初中")}if(age >16 && age <= 19){fmt.Println("孩子在上高中")}if(age > 19 && age <= 23){fmt.Println("孩子在上大学")}if(age >23){fmt.Println("待续...")}}func main(){p1 := Person{"_小许_",20,}fmt.Println(p1)p1.futrue(0)s1 := Student{person:Person{"_小许_",21,},address : "河北",}fmt.Println(s1)s1.futrue(0)}

    接口

    • 接口介绍及使用

    接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。interface是一组method的集合,接口做的事情就像是定义一个协议(规则),不关心属性(数据),只关心行为(方法)。

    接口具有以下特性:

    • 接口是一个或多个方法签名的集合;
    • 接口只有方法声明,没有实现,没有数据字段;
    • 接口可以匿名嵌入其他接口,或嵌入到结构中;
    • 一个对象只要全部实现了接口中的方法,那么就实现了这个接口,无需显示声明。

    接口实现OOP的特性的一般步骤如下:

    案例:在如下代码中,定义了装置中作和结束的方法集(inteface),通过方法不能直接实现接口,需要通过结构体,于是创建了电脑(Computer)以及指向电脑结构体的方法(start和end),Keyboard,Mouse也是同样的道理。

    package mainimport "fmt"func main(){//电脑对象实例化computer := Computer{}computer.start()computer.end()//鼠标对象实例化mouse := Mouse{}mouse.start()mouse.end()//键盘keyboard := Keyboard{}keyboard.start()keyboard.end()}//定义设备接口type Device interface{start()end()}//定义鼠标结构体实现类type Mouse struct {name string}//定义键盘结构体实现类type Keyboard struct{name string}//定义电脑结构体实现类type Computer struct{name string}/*鼠标类实现接口方法*/func (m Mouse) start() {m.name = "鼠标"fmt.Println("鼠标准备就绪!")}func (m Mouse) end() {m.name = "鼠标"fmt.Println("鼠标已正常使用!")}func (k Keyboard) start() {k.name = "键盘"fmt.Println("键盘准备就绪!")}func (k Keyboard) end() {k.name = "键盘"fmt.Println("键盘已正常使用!")}func (c Computer) start() {c.name = "电脑"fmt.Println("电脑准备就绪!")}func (c Computer) end() {c.name = "电脑"fmt.Println("电脑已正常使用!")}

    接口是一个方法集,接口方法作用对象为结构体,结构体通过指向结构体的方法即与接口相同的方法产生关系。

    上面是通过方法将接口与结构体联系起来,并通过结构体的对象来调用属性和方法,其实接口也是可以直接使用的,这也是Go模拟面向对象的多态性的特性。

    //直接使用接口var device1 Devicedevice1.start()device1.end()

    空指针异常,方体都是空的。

    //接口实现其实现类的实例var device2 Devicedevice2 = Mouse{"鼠标"}device2.start()device2.end()

    这是面向对象的多态性的体现。

    • 空接口

    空接口是指没有定义任何方法的接口。因此任何类型都实现了空接口。

    package mainimport "fmt"func main() {// 定义一个空接口xvar x interface{}fmt.Printf("type:%T\n", x)var y interface{start()}fmt.Printf("type:%T\n", y)var z interface {start() int}fmt.Printf("type:%T\n", z)}

    不论接口中有没有方法或者方法返回值是何种类型,接口类型都是nil(空)类型,只有在具体实现时才有实际意义,接口的类型取决于实现类的类型。

    Go 语言 中 函数 可以不返回任何值,也可以返回一个或者多个值,没有返回任何的值,因此没有返回值列表,同时,在函数体里面不需要使用 return 语句。因此接口有一个或若干方法,就确定了接口的某个方法一定是那种类型,那么在接口作为参数时只能传递接口方法返回值的类型。

    空接口没有任何方法,也就没有相应的方法返回值,所以其类型完全取决于赋值的类型。因此空接口类型的变量可以存储任意类型的变量。也可以说任何类型都实现了空接口。

    package mainimport "fmt"func main() {// 定义一个空接口xvar x interface{}fmt.Printf("type:%T\n", x)//yvar y interface{start()}fmt.Printf("type:%T\n", y)//yvar z interface {start() int}fmt.Printf("type:%T\n", z)s := "_小许_"x = sfmt.Printf("type:%T value:%v\n", x, x)i := 21x = ifmt.Printf("type:%T value:%v\n", x, x)b := truex = bfmt.Printf("type:%T value:%v\n", x, x)}

    空接口可以赋值任意类型的变量,那么其作为参数就可以接收任意类型的参数。

    空接口可以赋值任意类型,那么在其作为为参数时如何确定其类型呢?Go提供了接口断言来得到其类型。

  • 确定类型的接口作为参数
  • package mainimport "fmt"func main(){var p1 Whichp1 = Phone{"小米","性价比高"}fmt.Println("name:",p1.getName()+" "+"usage:",p1.getUsage)showWhich(p1)}//接口解释type Which interface{getName() stringgetUsage() string}type Phone struct{name stringusage string}func (p Phone) getName() string {return p.name}func (p Phone) getUsage() string {return p.usage}//接口方法func showWhich(w Which){fmt.Printf("name:%s,usage:%s",w.getName(),w.getUsage())}

    如下图代码,除0是错误的,是显性的,但实际可以并不知到该代码会出错,那么如何对该段可能出错的代码进行错误捕获呢?

    num1 := 10num2 := 0result := num1/num2fmt.Println(result)

    Go语言中使用defer和recover来捕获错误,并抛出错误,需要注意的是defer和recover需要定义在函数中,用来抛出特定的有意义的错误:

    import "fmt"func main(){fmt.Println("Hello World")//调用有异常的函数test()fmt.Println("程序执行了!")}func test() {//匿名函数处理错误defer func () {err:= recover()if err != nil{fmt.Println("err",err)}}()num1 := 10num2 := 0result := num1/num2fmt.Println(result)}


    这样处理错误的好处是不会影响整个程序的执行,只是程序的某些功能发生异常,整个程序还在运行着。匿名函数中可以通过日志和控制台来打印错误。

    • 自定义错误error

    Go语言内置的错误信息,一般不满足全部的开发需求,Go也提供了自定义错误,来扩展错误。


    自定义错误更为灵活,但是自定义错误需要程序员去写逻辑判断,并返回错误信息。由于程序需要返回错误信息的话函数的返回值就必须是一个error错误类型:

    //自定义错误func readFile(name string) error {if name == "conf.yml" {return nil}else {return errors.New("配置文件读取失败!")}}//主函数调用fmt.Println("Hello World")err := readFile("aaa")fmt.Println(err)


    遇到爆裂性的错误,需要是程序停止的使用panic(error)函数抛出错误并停止程序:

    //主函数捕获错误改造err := readFile("aaa")//fmt.Println(err)if(err != nil){panic(err)}fmt.Println("Hello World")


    抛出错误后程序停止,后面代码也没执行。

    总结:

  • Go语言使用defer和recover来捕获错误,通过匿名函数抛出错误;
  • Go的错误类型为error,支持位于errors包下的New()方法自定义错误信息。
  • Go中通过panic函数捕获错误,抛出错误信息并停止程序。
  • 需要做网站?需要网络推广?欢迎咨询客户经理 13272073477