发布时间:2025-12-09 18:43:19 浏览次数:4
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的一个特殊字符,可以代表任何字符,但是其值会被忽略。不能用关键字当标识符。
关键字与保留字
定义变量
/*变量*/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关键字定义函数,如下所示
定义函数的左中括号不能出现在下一行。以及所有形似该定义的方式
匿名函数
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提供了接口断言来得到其类型。
如下图代码,除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)}
这样处理错误的好处是不会影响整个程序的执行,只是程序的某些功能发生异常,整个程序还在运行着。匿名函数中可以通过日志和控制台来打印错误。
Go语言内置的错误信息,一般不满足全部的开发需求,Go也提供了自定义错误,来扩展错误。
自定义错误更为灵活,但是自定义错误需要程序员去写逻辑判断,并返回错误信息。由于程序需要返回错误信息的话函数的返回值就必须是一个error错误类型:
遇到爆裂性的错误,需要是程序停止的使用panic(error)函数抛出错误并停止程序:
抛出错误后程序停止,后面代码也没执行。
总结: