18910140161

30分钟入门基本go(Java kid版)

顺晟科技

2023-02-26 09:01:31

161

作者:京东科技韩国CAI

前言

Go语言定义

Go(又名GolangGo语言语法与C类似,但在功能上是内存安全性、GC、结构形式和CSP样式的同时计算。

适用范围

本句适用于学过其他面向对象语言(Java、Php)但没有学过Go语言的初学者。文章主要从Go和Java功能比较出发,阐述了Go语言的基本语法、面向对象编程、并发和错误四个方面。

一、基础语法

Go语言的基本语法和一般编程语言差不多。根据声明变量的方式,数组、切片、词典的概念和功能与Java大不相同,但在Java中,所有这些数据结构都可以通过模拟功能在Go中使用。

1.1 变量、常量、nil与零值、方法、包、可见性、指针

1.1.1 变量声明

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;符合字串。

使用

1.1.2 常量声明

const声明在声明后不能更改的常量。

Const laugh string=& amp# 039;go & amp# 039;

:仅声明值为

1.1.3 nil与零值

的未指定值为nil的变量。Java的& ampquot空& ampquot和类似。

没有明确初始值的变量声明将被指定零值。

零值为:

数字类型为0;布尔类型为false字符串为& amp# 039;& amp# 039;(空字符串)。

1.1.4 方法、包

Go中方法的定义

使用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;

}:在学习

1.1.5 指针

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

}

1.2 条件、循环、分支

1.2.1 条件

与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

}

1.2.2 循环

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)

}

1.2.3 分支

核心:=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;)

}

1.3 数组、切片、字典

1.3.1 数组

数组功能与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语言中,使用切片解决上述问题。

1.3.2 切片

片与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

}

1.3.3 字典

词典是& 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;)

二、面向对象编程

:

2.1 Go语言中的类

-1010面向对象语言要求类具有属性、构造函数方法和成员方法三种结构,Go语言也不例外。

2.1.1 类的声明与初始化

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)

2.1.2 成员方法

GO的成员方法声明与其他语言有很大不同。以Student类为例,

//您可以在方法名称前添加相应的类,以便将更改方法视为该类的成员方法。

字串func(sstudent)get name(){

Return s.name

}

//注意:其中学员随*一起提供。这是因为在方法传递过程中有传递值和传递引用(指针)的概念。使用传递值时,编译器会为该参数生成副本传递,因此修改副本实际上没有效果。执行此方法后将删除副本,因此必须使用*Student将要修改的对象指针传递到修改的值,以便正常工作。

func(s * student)set name(name string){

//这里实际上是(*s)。必须使用name=name。因为一个地址的属性没有意义。但是可以这样使用。因为编译器帮助自动转换

S.name=name

}

2.2 接口

接口在Go语言中占有非常重要的地位。如果说Goroutine和channel是支撑Go语言同步模型的基石,那么界面就是go语言整个系统类型的基石。Go语言的界面不仅是简单的界面,让我们逐步探索Go语言的界面特征。

:与

2.2.1 传统侵入式接口实现

类实现一样,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语言的界面如何避免这些问题。

:在

2.2.2 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语言中,类继承树没有意义。你只需要知道这个类实现了什么方法,每个方法都在做什么。第二,在定义接口时,不需要考虑接口需要拆得有多精细。为了实现接口,也不需要引入带有接口的软件包。界面由用户根据需要定义,不需要事先设计。您也不需要考虑以前其他模块是否定义了类似的接口。这样可以完全避免传统面向对象编程中的界面设计问题。

三、并发与多线程

3.1 Goroutine

同时处理能力是决定所有优秀语言优缺点的关键。在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)

}

3.2 Channel

四、错误处理

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

}非常简洁优雅。

4.1 error

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)

}

4.2 defer

Go语言没有太多异常类,没有像Java一样错误类型(Error、Exception等),当然try.也没有catch门。

恐慌(恐慌)是指程序运行过程中发生了错误。如果未捕获到此错误,则可能会发生系统崩溃。例如,简单的panic: a:=1/0。

Panic:发生integer divide by zero。

-w926

第一行表示问题的赞助,第二行是有问题代码的包和函数,第三行是问题代码的具体位置,最后一行表示程序的结束状态。通过此信息,可以快速找到并解决问题。

4.3 panic

如果没有结束程序冲突,并且存在可预测的错误,则可以使用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;返回)

} -w747

即使发生异常,使用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)语句未执行,因为代码执行在上一步中出现异常,方法提前终止。

:通过

4.4 recover

名的学习,大家可以以使用为目的初步了解go的基础语法,但光靠课文理解go是不够的。例如,go最大的优点之一& amp# 039;协议& amp# 039;银文章目的没有特别详细地展开,感兴趣的学生可以继续学习。

  • TAG:
我们已经准备好了,你呢?
2024我们与您携手共赢,为您的企业形象保驾护航