1、程序结构
Go程序结构和C系程序(C/C++/JAVA等)一致,基本语句被组织成函数用于隔离和复用,函数组成源文件和包。Go程序存储在一个或多个.go文件中,每个文件都已pakage开头,表面当前文件属于哪个包;package语句后面是import语句,用于包含其他包以便于使用其他包中的元素;之后是包级别的类型、变量、常量、函数声明,声明不用区分前后顺序。
例:
pakage main
import “fmt”
var name = “tony” func main(){ //函数、控制语句的前半大括号必须和函数名在同一行 fmt.Printf(“name = %s”,name) }
注意:变量声明区别于C的是类型后知,例如var name string;
特点:对比C,go有如下常见特点:
1)、变量声明还可以通过返回值完成,例如name := getname(),称之为短声明;
2)、函数返回值可以有多个,例如ret1,ret2 = getret(),ret1和ret2将获得getret函数返回的两个参数;同时赋值表达式也可以多重赋值,如x,y=10,20 ;
3)、赋值多了一种“:=”,这种用法用于定义一个类型是返回值类型的变量,同时该变量的值为返回值。是一种方便程序员的特性;
4)、新类型声明用type关键字;例如 type mytype string,那么mytype可以等价于string;
5)、包初始化时从包级别变量开始,这些变量按照声明顺序初始化,对于多个文件的包,初始化顺序是传给编译器的文件顺序,每个包,甚至每个文件都可以定义一个init函数,在里边可以自定义初始化一些内容,这个函数会在初始化阶段自动调用,且这个函数不是给用户调用的。
2、数据类型
go的数据类型包括基础类型、聚合类型、引用类型、接口类型四大类。
基础类型包括数字、字符串和布尔型;聚合类型有数组和结构体;引用类型包括slice、指针、map、函数、通道;接口类型即“接口”类型。
1)、基础类型使用上参考C系语言,差别不大,不过go对这些基础类型做了各种优化,比如内置复数类型、提供常量生成器、方便操作字符串等。
2)、数组和结构体长度都是固定的,slice和map的长度是可以动态增长的;
①、slice表示一个拥有相同元素的可变长序列,通常写作[]T ;slice有三个属性,指针、长度、容量,在长度变长的过程中,容量会适时的增加以适应元素增加;len和cap用来返回slice的长度和容量;
②、map,即键值对类型,这种类型在日常开发者很常用,所以go内置了;map的类型是map[K]v ;map要求k必须是相同类型,V是相同类型,但K和V的类型可以不同;并且要求K的值必须是可以通过==操作符来进行比较的数据类型,目的是可以检索某一个K是否已经存在。空map表示为map[k]v{} 。
③、结构体类似于C系语言的结构体;
④、go标准库支持操作json、xml等常用格式标记语言。
3、函数
go函数声明如下格式:
func name(para-list) (ret-list){ body }
常规操作类似于C系语言,所以不再多加描述。
特性:延迟函数调用
用defer关键字后跟函数调用,这种格式的函数调用将会在当前函数执行流程结束后执行;不管是正常return还是程序产生异常都会在最终调用defer语句;defer语句可以在需要的地方多次调用,defer语句经常适用于成对操作中,如文件打开关闭,这时无论文件操作成功与否最后肯定是要关闭文件的,所以这一特性是很有用的。
resp.err := http.Get(url) if err != nil{ return err } defer resp.Body.Close()
4、方法
方法在本质上等同于函数,但它是对于面向对象编程而言的;go是一种支持大多数面向对象编程特性的编程语言,所以也存在继承、复用等。
方法声明和普通函数相比函数前面多了个参数,如下:
func (p class) name(para-list) (ret-list){ body }
多出的这个类参数意图是把当前函数绑定到这个参数对应的类型上。和C系语言不同的是这个参数显示指定了特定类型实例p,而不是用隐性的this、self、that等;这样的好处是由用户自己选择当前方法的接收者p 。
以上的p也可以是指针类型,这样可以避免参数复制。
合法的方法调用仅以下三种情况:
//以下三种情况的 前置条件 func (p Point) Distance(q Point) float64 { body } func (p *Point) ScaleBy (q Point) float64 { body } var pptr *Point
1)、实参接收者和形参接收者类型相同,即同是T或*T
Point{1,2}.Distance(q) //Point
pptr.ScaleBy(q) //*Point
2)、实参接收者是T而形参接收者是*T,编译器会隐式获取地址;
p. ScaleBy(q) //
3)、实参接收者是*T而形参接收者是T,编译器会隐式转换。
pptr.Distance(q)
Notice: nil是个合法接收者
5、封装
go只有一种方式控制命名的可见性:定义的时候首字母大写的标识符是可以从包中导出的,小写的标识符只能在包中使用。
6、接口
接口类型是对其他类型行为的概括和抽象。接口类型是一种抽象类型,它提供的仅仅是一些方法。举个例子:
type ControllerInterface interface { Init(ct *context.Context, controllerName, actionName string, app interface{}) Get() Post() Delete() Put() Head() Patch() Options() }
一个接口类型定义了一组方法,如果一个具体类型要实现这个接口,那么他必须实现接口类型中提供的所有方法。
如果一个类型实现了一个接口所要求的的所有方法,那么这个类型实现了这个接口;接口的赋值规则很简单,仅当一个表达式实现了一个接口时这个表达式才能赋值给该接口。
package io type Reader interface{ Read() } type ReadWriter interface{ Read() Write() } type Writer interface{ Write() }
依据以上前置条件以下赋值情况为:
var w io.Writer
w =os.Stdout //可以 *osFile 有write方法
w = time.Secound //不可以 未实现write方法
所以,接口可以当做普通变量看待,申请变量时它是nil,仅当赋值特定实现它要求的方法的类型后它将获得这个类型的具体方法,之后用这个变量完成特定的操作。
类型断言:
形如X.(T)的操作称之为类型断言,其中X是一个接口类型表达式,T是一个类型;类型断言会检查作为操作数的动态类型是否满足指定的断言类型;作用结果分为两种可能:
1)、T是一个具体类型,从它的操作数中把具体类型的值提取出来;
var w io.Writer
w = os.Stdout
f := w.(*os.File) // f = os.Stuout
2)、 T是一个接口类型,从一个接口类型向另一个更宽松的接口类型转化(方法增多)
var w io.Writer
w = os.Stdout
rw := w.(io.ReadWriter) // 成功,rw将会变成包含Read和Write的接口
7、并发编程
go支持两种类型的并发编程风格,goroutine+channel风格(CSP)和共享内存多线程风格,go编程中第一种比较常见,因为使用很简单方便。
go里每一个并发执行的活动称为goroutine;在函数调用或者表达式前加关键字go即可达成创建新的goroutine去执行表达式任务的目的;所以比起传统的多线程编程要简单得多。
当然程序执行的时候创建很多goroutine达成并发执行的目的了,但这些goroutine之间如果不能有效沟通那程序的功能将很大程度上受限,所以我们需要这些goroutine之间通信的机制,此处引入channel(通道)