作者:京东科技 韩国凯前言Go语言定义Go(又称 Golang)是 Google 的 Robert Griesemer,Rob Pike 及 Ken Thompson 开发的一种静态、强类型、编译型语
顺晟科技
2023-02-26 09:01:31
161
作者:京东科技韩国CAI
Go(又名GolangGo语言语法与C类似,但在功能上是内存安全性、GC、结构形式和CSP样式的同时计算。
本句适用于学过其他面向对象语言(Java、Php)但没有学过Go语言的初学者。文章主要从Go和Java功能比较出发,阐述了Go语言的基本语法、面向对象编程、并发和错误四个方面。
Go语言的基本语法和一般编程语言差不多。根据声明变量的方式,数组、切片、词典的概念和功能与Java大不相同,但在Java中,所有这些数据结构都可以通过模拟功能在Go中使用。
Go语言有两种方法
1.请注意,使用var关键字进行声明,与大多数强大的类型语言不同,Go语言的声明变量类型位于变量名之后。Go语句末尾不需要分号。
Var num int
Var result string=& amp# 039;this is result & amp# 039;
2.使用:=分配。
Num :=3等于var num int=3
其中,变量的类型根据右侧的值进行匹配。例如& amp# 039;3 & amp# 039;符合int & amp;# 039;3.0 & amp# 039;与float64一致,& amp# 039;result & amp# 039;符合字串。
使用
const声明在声明后不能更改的常量。
Const laugh string=& amp# 039;go & amp# 039;
:仅声明值为
的未指定值为nil的变量。Java的& ampquot空& ampquot和类似。
没有明确初始值的变量声明将被指定零值。
零值为:
数字类型为0;布尔类型为false字符串为& amp# 039;& amp# 039;(空字符串)。
使用func关键字定义方法、方法名称、参数和返回值(如果有,没有返回值就不写)。
Funcmethodname (p1parm、p2parm) int {}
//学习语言要从hello世界开始!
Package main
汇入& amp# 039;fmt & amp# 039;
Func main() {
Fmt。print ln(& amp;# 039;Hello World!& amp# 039;)//hello世界!
Fmt。Println(add(3,5)) //8
Var sum=add(3和5)
}
Func add(a int,b int) int{
return a b;
}
Go函数与其他编程语言有很大区别,就是支持多个返回值。这在发生处理程序错误时很有用。例如,如果上述add函数仅支持添加非负整数,则传递负数将报告错误。
//返回值仅定义未定义类型的返回参数
Func add(a,b int) (int,error) {
If a 0 || b 0 {
Err :=errors。new(& amp;# 039;非负整数加& amp# 039;仅支持)
Return 0、err
}
A *=2
B *=3
Return a b,nil
}
//返回值还定义了参数,可以直接使用return,只有在定义的参数可以直接使用return的情况下,才返回两个参数
Funcadd1 (a,b int) (z int,erreror){
If a 0 || b 0 {
Err :=errors。new(& amp;# 039;非负整数加& amp# 039;仅支持)
Return //实际上返回0 err。z是因为如果没有指定,nil值为0
}
A *=2
B *=3
Z=a b
返回//z err返回
}
Func main() {
x、y :=-1、2
z、err :=add(x、y)
If err!=nil {
Fmt。Println(err .Error())
Return
}
fmt . printf(& amp;# 039;add (%d,% d)=% d \ n & amp;# 039;x、y、z)
}:
func内的func(编号.int) {
For _,number :=range numbers {
Fmt.println(编号)
}
}
片:=[] int {1,2,3,4,5}
//使用.打破切片
Myfunc(切片.):在
Go语言中,变量、函数、类属性和成员方法的可见性与传统的编程导向不同,类属性和成员方法的可见性不是封装在自己的类中,然后通过private维度化,而是作为维嵌入。
Go语言不根据第一个字母的大小写提供这些关键字,包括变量、函数、用户定义的类的属性和成员方法。如果变量名、属性名、函数名或方法名的第一个字母是大写,则可以在包外部直接访问这些变量、属性、函数和方法。否则,只能在包内访问。
名为Domain的文件夹下有三个。如果有go文件,则三个文件中的package必须是domain。其中,具有程序门户main方法的文件是main
//定义此文件属于主软件包
Package main
通过//import导入标记库包
汇入& amp# 039;fmt & amp# 039;
Func main() {
Fmt。print ln(& amp;# 039;Hello World!& amp# 039;)//hello世界!
Fmt。Println(add(3,5)) //8
Var sum=add(3和5)
}
Func add(a int,b int) int{
return a b;
}:在学习
C语言方面,指针仍然很熟悉。我理解的指针实际上是内存中的实际十六进制地址值。参考变量的值通过此地址从内存中获取相应的实际值。
Func main() {
I :=0
使用//接收地址
Fmt.println (I)//0xc000000c054
Var a、b int=3、4
//接收0xc 0000 a0890xc 00000 a090
Fmt。Println(add(a,b))
}
//使用*声明指针类型的参数和使用指针
Func add(a *int,b *int)int{
//0xc00000a0890xc00000a090收到
//0xc00000a089到位置,找到特定数据并将其分配给x
X :=*a
//0xc00000a090到位置,找到具体数据并分配给y
Y :=*b
返回x y
}
与Java语言的if基本相同
//if
If condition {
//do something
}
//if.else.
If condition {
//do something
} else {
//do something
}
//if.else if.else.
If condition1 {
//do something
} else if condition2 {
//do something else
} else {
//catch-all or default
}
sum :=0
//常规for循环
for I:=1;I=100I {
总计=I
}
//无限循环
For{
山姆
If sum=100{
布雷克;
}
}
//条件循环
for RES:=sum 1;Sum 15{
山姆
莱斯
}
//kv循环使用情况如果贴图或数组k是索引,或者关键值v不需要值k、v,则可以用_替换
For k,v :=范围a {
Fmt。Println(k、v)
}
核心:=100
交换机核心{
案例90、100:
Fmt。print ln(& amp;# 039;grade:A & amp;# 039;)
案例80:
Fmt。print ln(& amp;# 039;grade:B & amp;# 039;)
案例70:
Fmt。print ln(& amp;# 039;grade:C & amp;# 039;)
案例65:
Fmt。print ln(& amp;# 039;grade:D & amp;# 039;)
Default:
Fmt。print ln(& amp;# 039;grade:F & amp;# 039;)
}
数组功能与Java语言一样,长度不变,可以使用多维数组或通过arrays[i]存储或获取值。
//声明
Var nums [3]int
//声明和初始化
Var nums=[3] int {1,2,3}==nums:=[3] int {1,2,3}
//使用
For sum :=0,I:=0;I10{
Sum=nums[i]
I
}
//修改值
Num[0]=-1数组使用相对简单,但存在难以解决的问题。长度是固定的。
例如,用户数会随着时间的推移而变化,但不能更改数组长度,因此数组不适合存储长度可能会变化的数据。因此,在Go语言中,使用切片解决上述问题。
片与Java相比是一个全新的概念。在Java中,您可以使用列表界面对任意长度的数据存储结构执行操作。例如,ArrayList和LinkList可以随时添加和导入数据,而不限制长度。但是,Go没有此类界面,而是通过切片进行不确定长度的数据长度存储。
切片和数组最大的区别是切片不声明长度。但是,切片与数组无关。您可以将阵列视为分割的基本阵列,将分割视为阵列中连续片段的参考。切片可以使用数组的部分元素或整个数组来创建,也可以创建比所基于的数组更大的切片。
:包含
片长度的元素数。
切片的容量是从第一个元素到主数组元素的末尾之间的数字。
片s的长度和容量可通过表达式len(s)和cap(s)获得。
片长度在功能上与Java List中的size()相比较。换句话说,如果通过len(切片)识别切片长度,则可以循环len(切片)以动态控制切片中的特定内容。切片的容量在实际开发中没有太多使用,理解那个概念就行了。
//阵列声明
Var nums=[3]int{1,2,3}
//0。直接声明
Var slice=[]int{0,1,2}
//1。引用数组中的切片。其中a:b包含a,但不包含b
varslice 1=nums[0:2]//{ 1,2}
//如果不写,则默认值为0(左)或最大值(右)
varslice 2=slice 1[:2]==varslice 2=slice 1[0:]==varslice 2=slice 1[:]
//2。使用make生成切片。其中int是片类型,4是长度,5是容量
片3:=make ([] int,5)
片4:=make ([] int,4,5):使用
//append向片动态添加元素
Funcappend (s [] t,vs.t) [] T
片5:=make ([] int,4,5)//{0,0,0,0}
片5=附加(片5,1)//{0,0,0,0,1}
//删除第一个零
Sliece5=slice5[1:]
模拟上述问题,使用片解决方案
//磁碟片段宣告
Var userIds=[]int{}
//模拟获取所有用户ID
for I:=0;I 100{
UserIds=append(userIdS,I);
I;
}
//用户信息处理
For k、v :=范围用户{
Userids [k]=v
}
词典是& amp# 039;密钥对& amp# 039;或& amp# 039;键值& amp# 039;也称为,Java是典型的数据结构,具有多种映射接口,通常是HashMap等。Go使用字典实现键值对的存储。词典是无序的,因此不能根据添加的顺序保证数据顺序。
//string是键类型,int是值类型
映射:=映射[字符串] int {
Java & AMP;# 039;1,
Go & amp# 039;2,
python & amp;# 039;3,
}
也可以通过//make创建字典100
Maps=make(地图[string] int,100):
//直接使用
Fmt。print ln(maps[& amp;# 039;java & amp# 039;]) //1
//分配
maps[& amp;# 039;go & amp# 039;]=4
//值还确定贴图中的关键点ok是否为bool类型
Value,ok:=maps[& amp;# 039;one & amp# 039;]
找到If ok {//
//处理找到的值
}
//删除
Delete (testmap,& amp# 039;four & amp# 039;)
:
-1010面向对象语言要求类具有属性、构造函数方法和成员方法三种结构,Go语言也不例外。
Go语言没有明确的类概念。只有struct关键字在功能上是面向对象语言的& ampquot类& ampquot可以用来比喻。例如,要定义学生类,可以这样做。
Type Student struct {
Id int
Name string
Male bool
Score float64
}//定义了学生类,其中每个属性的类型后跟id name。
//定义学生班级的配置方法
Funcnew student (iduint、namestring、malebool、scorefloat 64) * student {
Return student {id,name,male,score}
}
//实例化类对象
Student :=NewStudent(1,& amp# 039;学院群& amp# 039;100)
FMT . PRINTLN(STUDENT)
GO的成员方法声明与其他语言有很大不同。以Student类为例,
//您可以在方法名称前添加相应的类,以便将更改方法视为该类的成员方法。
字串func(sstudent)get name(){
Return s.name
}
//注意:其中学员随*一起提供。这是因为在方法传递过程中有传递值和传递引用(指针)的概念。使用传递值时,编译器会为该参数生成副本传递,因此修改副本实际上没有效果。执行此方法后将删除副本,因此必须使用*Student将要修改的对象指针传递到修改的值,以便正常工作。
func(s * student)set name(name string){
//这里实际上是(*s)。必须使用name=name。因为一个地址的属性没有意义。但是可以这样使用。因为编译器帮助自动转换
S.name=name
}
接口在Go语言中占有非常重要的地位。如果说Goroutine和channel是支撑Go语言同步模型的基石,那么界面就是go语言整个系统类型的基石。Go语言的界面不仅是简单的界面,让我们逐步探索Go语言的界面特征。
:与
类实现一样,Go语言的界面与其他语言提供的界面概念完全不同。例如,在Java、PHP中,接口主要作为不同类之间的协议(Contract)存在,协议的实施是必需的。具体细节是,如果类实现了接口,则必须实现接口声明的所有方法。这是& ampquot履行合同& ampquot中选择光源族。
//iTemplate & amp;# 039;接口声明
Interface iTemplate
{
public function set variable($ name,$ var);
public function geth tml($ template);
}
//实施接口
//下面的写法正确
class template implements itemplate
{
private $ vars=array();
public function set variable($ name、$ var)
{
$ this-vars[$ name]=$ var;
}
public function geth tml($ template)
{
foreach($ this-vars as $ name=$ value){
$ template=str _ replace(& amp;# 039;{ & amp# 039;$ name。& amp# 039;} & amp# 039;$ value、$ template);
}
Return $ template
}
}此时,如果另一个接口iTemplate2声明与iTemplate完全相同的接口方法(甚至是名称)为iTemplate,则只会在另一个命名空间中,编译器认为上述类Titemplate不实现iTemplate2接口,而只实现iTemplate。
这包括类与类之间的继承、类与接口之间的实现,以及单个继承语言(如Java、PHP)中严格的层次关系。一个类只能直接从一个超类继承,一个类只能实现指定的接口。没有从特定父类继承或实现接口的显式声明。
我们称这个接口为入侵接口。所谓的& ampquot入侵& ampquot伊朗意味着实现类必须明确声明实现了特定接口。这种实现方式足够清晰和简单,但存在问题,尤其是在设计标准库时。因为标准库一定包含接口设计,接口的需求者是业务实现类。只有在具体编写业务实施类时,才能知道如何定义。在此之前,标准库的接口已经设计好,如果没有合适的接口,则必须根据约定的接口来实现。这里的问题是接口设计和业务实现是分开的。界面设计师并不总是能够预测商业当事人要实现的功能。这导致设计和实施的脱节。
由于接口的过度设计,可能根本不需要某些声明的方法实现类。如果设计太简单,无法满足业务要求,这实际上是个问题。以PHP中包含的SessionHandlerInterface接口为例,在此接口中声明的接口方法如下:
SessionHandlerInterface {
/*方法*/
Abstract public close (void): bool
abstract public destroy(string $ session _ id):bool
abstract public GC(int $ max life time):int
abstract public open(string $ save _ path,string $ session _ name): bool
abstract public read(string $ session _ id):string
abstract public write(string $ session _ id、string $ session _ data): bool
}自定义会话管理器必须实现接口。也就是说,接口声明的所有方法都需要实现,但实际上,在进行业务开发时,不需要实施某些方法。例如,如果Redis或Memcached基于Session存储,则不需要实施GC方法,因为它本身包含过期回收机制。
由于这种不合理的设计,在编写PHP类库的每个接口时,必须解决以下两个问题(Java也类似):
接口中需要声明的接口方法是什么?如果在多个类中实现相同的接口方法,该如何设计接口?例如,上面的SessionHandlerInterface是否需要拆分为更细分的接口以满足其他实现类的需要?现在让我们看一下Go语言的界面如何避免这些问题。
:在
Go语言中,类到接口的实现与子类继承父类一样,不会通过提供关键字(如implement)明确声明类实现的接口。如果一个类实现了接口要求的所有方法,则该类称为实现了接口。
例如,定义了File类并实现了以下四个方法:Read()、Write()、Seek()和Close()。
Type File struct {
//.
}
Func (f *文件)read (buf [] byte) (n int,err error)
Func (f *文件)write (buf [] byte) (n int,err error)
Func (f *文件)seek (off int 64,whence int) (pos int 64,err error)
Func (f *File) Close() error假设有下列介面(Go语言透过关键字interface宣告介面,以指出与结构类型的差异,大括号内包含一组要实作的方法):
Type IFile interface {
读取(buf [] byte) (n int,err error)
Write(buf []byte) (n int,err error)
Seek (offint64、whenceint) (posint64、errerror)
Close() error
}
Type IReader介面{
读取(buf [] byte) (n int,err error)
}
Type写入程序界面{
Write(buf []byte) (n int,err error)
}
Type ICloser介面{
Close() error
} File类没有显式实现这些接口,完全不知道这些接口的存在,但File类实现所有这些接口声明的方法,因此File类说,它们实现了这些接口。如果类的成员方法集合包含接口声明的所有方法,即接口的方法集合是类成员方法集的子集,则认为该类实现了接口。
与Java和PHP不同,Go语言的这种接口称为非入侵性接口。类和接口的实现关系不是显式声明,而是根据两种方法的集合来判断。这样做有两个好处。
首先,Go语言的标准库不需要绘制类库的继承/实现树图。在Go语言中,类继承树没有意义。你只需要知道这个类实现了什么方法,每个方法都在做什么。第二,在定义接口时,不需要考虑接口需要拆得有多精细。为了实现接口,也不需要引入带有接口的软件包。界面由用户根据需要定义,不需要事先设计。您也不需要考虑以前其他模块是否定义了类似的接口。这样可以完全避免传统面向对象编程中的界面设计问题。
同时处理能力是决定所有优秀语言优缺点的关键。在Go语言中,可以通过Goroutine同时处理。
Func say(s string) {
Fmt。Println(s)
}
Func main() {
//通过go关键字举行新的赞助
GO SAY(& AMP;# 039;世界和美国农业部;# 039;)
say(& amp;# 039;Hello & AMP;# 039;)
}Go语言没有限制资源同时访问的Java那么多锁,只有用于同步操作的互斥。
//向类SafeCounter添加锁定
Type SafeCounter struct {
V map[string]int
Mux sync。Mutex
}
//Inc增加给定关键点的计数器值。
Func (c * safecounter) Inc(密钥字符串){
//对象锁定
C.mux.Lock()
//Lock后,同一时间点只有一个goroutine可以访问c.v
C.v [键]
//解锁
C.mux.Unlock()
}: 010到1010多协议之间通过通道进行的通信在功能上可以与Java的volatile关键字相比较。
Ch :=make(chan int)可以声明int型Channel,并通过Ch在两个协议之间进行int数据通信。
通过通道传输数据。
Ch-v //将v发送到通道Ch。
v:=-从ch //ch接收值并提供v。Package main
汇入& amp# 039;fmt & amp# 039;
Func sum(s []int,c chan int) {
总计:=0
For _,v :=范围s {
总计=v
}
C-sum //和供应给c
}
//对于主方法,这相当于打开赞助
Func main() {
S :=[]int{7,2,8,-9,4,0}
C :=make(chan int)
打开通过//go关键字传递chaneel作为参数的两个协作进程
Go sum(s[:len(s)/2],c)
高总和(s [len (s)/2:],c)
//通过箭头方向获取或传递信息
从x、y :=-c、-c //c接收
Fmt。Println(x、y、x y)
}
Go语言错误处理机制非常简单,不需要学习理解复杂的概念、函数和类型。Go语言定义了error接口,这是错误处理的标准模式error接口。
Type error interface {
Error() string
}其中只声明了一个Error()方法,并返回字符串类型的错误消息。对于大多数函数或类方法,要返回错误,默认情况下可以定义为以下模式——:返回错误类型作为第二个参数。
Funcfoo (paramint) (n int,erreror){
//.
}然后,在调用返回错误消息的函数/方法时,将显示以下& ampquot防卫门& ampquot根据模板编写处理代码即可。
n、err :=Foo(0)
If err!=nil {
//错误处理
} else{
//使用返回值n
}非常简洁优雅。
defer在方法执行完成后执行defer的语句,而不管执行结果是否成功。Java中的try try.catch.类似于使用finally。例如,文件处理会关闭文件流,而不管结果是否成功。
funcread file(filenamestring)([]byte,error) {
f、err:=os.open(文件名)
If err!=nil {
Return nil、err
}
//关闭文件流,而不考虑结果
Defer f.Close()
Var n int64=bytes。MinRead
If fi、err:=f . Stat();Err==nil {
If size :=fi。Size() bytes。MinReadSize n {
N=size
}
}
Return readAll(f、n)
}
Go语言没有太多异常类,没有像Java一样错误类型(Error、Exception等),当然try.也没有catch门。
恐慌(恐慌)是指程序运行过程中发生了错误。如果未捕获到此错误,则可能会发生系统崩溃。例如,简单的panic: a:=1/0。
Panic:发生integer divide by zero。
第一行表示问题的赞助,第二行是有问题代码的包和函数,第三行是问题代码的具体位置,最后一行表示程序的结束状态。通过此信息,可以快速找到并解决问题。
如果没有结束程序冲突,并且存在可预测的错误,则可以使用recover()语句捕获未处理的panic。Recover必须放置在defer语句中,并且必须位于方法之前,以防止在defer语句无法执行时终止系统异常。
Package main
Import(
Fmt & amp# 039;
)。
Func divide() {
通过//defer,此方法必须在每次执行完成时运行匿名方法
Defer func() {
//执行异常捕获
if err:=recover();Err!=nil {
fmt . printf(& amp;# 039;运行时panic caught:% v \ n & amp;# 039;err)
}
}()
Var I=1
Var j=0
K :=I/j
Fmt。printf(& amp;# 039;% d/% d=% d \ n & amp;# 039;I、j、k)
}
Func main() {
Divide()
Fmt。print ln(& amp;# 039;divide方法调用完成后,main函数& amp# 039;返回)
}
即使发生异常,使用recover()捕获后,方法也不会发生系统崩溃,并退出。这里是fmt。printf(& amp;# 039;% d/% d=% d \ n & amp;# 039;I,j,k)语句未执行,因为代码执行在上一步中出现异常,方法提前终止。
4 recover
如果不想在出现可预测的错误时结束程序冲突,可以使用recover()语句捕获未处理的panic。Recover必须放置在defer语句中,并且必须位于方法之前,以防止在defer语句无法执行时终止系统异常。
Package main
Import(
Fmt & amp# 039;
)。
Func divide() {
通过//defer,此方法必须在每次执行完成时运行匿名方法
Defer func() {
//执行异常捕获
if err:=recover();Err!=nil {
fmt . printf(& amp;# 039;运行时panic caught:% v \ n & amp;# 039;err)
}
}()
Var I=1
Var j=0
K :=I/j
Fmt。printf(& amp;# 039;% d/% d=% d \ n & amp;# 039;I、j、k)
}
Func main() {
Divide()
Fmt。print ln(& amp;# 039;divide方法调用完成后,main函数& amp# 039;返回)
}即使发生异常,使用recover()捕获后,方法也不会发生系统崩溃,并退出。这里是fmt。printf(& amp;# 039;% d/% d=% d \ n & amp;# 039;I,j,k)语句未执行,因为代码执行在上一步中出现异常,方法提前终止。
:通过
名的学习,大家可以以使用为目的初步了解go的基础语法,但光靠课文理解go是不够的。例如,go最大的优点之一& amp# 039;协议& amp# 039;银文章目的没有特别详细地展开,感兴趣的学生可以继续学习。