首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

go基础简介

Go语言是一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。Go 的正式语法使用分号来结束语句,但大多数情况下是可以省略的(编译器会自动添加);如果你在一行中写多个语句,则需要用分号隔开不同的语句:

Go程序是通过package来组织的;

只有package名称为main的包可以包含main函数;

一个可执行程序有且只有一个main包;

如果导入的包但是没有用到类型或者函数则会报编译错误;

package别名: import io "fmt" 这样将fmt设置一个别名为io,调用时为io.Println("...");

在 main.main 函数执行之前所有代码都运行在同一个goroutine,也就是程序的主系统线程中。因此,如果某个 init 函数内部用go关键字启动了新的goroutine的话,新的goroutine只有在进入 main.main 函数之后才可能被执行到。

go的注释与C++类似:

单行注释 // ...

多行注释 /* ... */

数据类型

go中定义了以下基本数据类型:

整数类型:

与系统架构有关的:int与uint(在32位系统下为32,64位系统下为64);

固定长度的:int8/16/32/64、uint8/16/32/64;

浮点类型:float32、float64

布尔类型:bool

复数类型: complex64、complex128

字符类型: byte(uint8的别名)、rune(int32的别名,标识Unicode)

字符串类型: string

一个字符串是一个不可改变的字节序列,可以包含任意的数据,包括byte值0;

for range 语法对UTF8字符串提供了特殊支持;

对字符串和 []rune 类型的相互转换提供了支持:如utf8.RuneCountInString(str);

字符串其实是一个结构体,因此字符串的赋值操作也就是reflect.StringHeader 结构体的复制过程,并不会涉及底层字节数组的复制。

高级类型:

数组:[N]T,[...]T

切片:[]T

字典:map[K]T

通道类型:chan T

向无缓存通道写入时,会阻塞等待读取;

关闭nil或已关闭通道会引发panic;

向已关闭的通道发生数据会引发panic;

从已关闭的通道读取,总是返回0值和false(对于缓存通道,先读取缓存的数据);

自定义数据类型(可使用type定义):

别名:type BuffSize int;BuffSize为新的类型,底层类型与int相同,但是为不同的类型,互操作时要进行类型转换(BuffSize(var));

自定义类型: type Name struct { ... }

c2 := make(chan struct{}) // 空结构体

go func() {

fmt.Println("c2")

c2

}()

interface接口:golang不支持完整的面向对象思想,它没有继承,多态完全依赖接口实现;通过接口模拟继承(本质是组合)。

Interface定义一组方法,且不能包含任何变量;

只要类型包含了一个接口中的所有方法,那么这个类型就实现这个接口;

如果一个类型含有了多个interface的方法,那么就实现了多个接口;

如果一个类型只含有了一个interface的部分方法,那就没有实现这个接口。

type User struct {

Name string

Age int32

}

var user User

var user1 *User = &User{}

var user2 *User = new(User)

// 实现Stringer接口(用于Sprintf)

func (p *User) Name() String{

return p.name

}

类似下面的接口,要注意防止递归循环:

type MyString string

func (m MyString) String() string {

// return fmt.Sprintf("MyString=%s", m) // 错误:会无限递归

return fmt.Sprintf("MyString=%s",string(m)) // 可以:注意转换

}

数组与切片

Go语言中数组是值语义。一个数组变量即表示整个数组,它并不是隐式的指向第一个元素的指针(比如C语言的数组),而是一个完整的值。当一个数组变量被赋值或者被传递的时候,实际上会复制整个数组

数组的大小是其类型的一部分,不同长度或不同类型的数据组成的数组都是不同的类型,类型 [10]int 和 [20]int 是不同的。

切片是可以动态增长和收缩的序列,切片通过对数组进行封装,为数据序列提供了更通用、强大而方便的接口。若某个函数将一个切片作为参数传入,则它对该切片元素的修改对调用者而言同样可见, 这可以理解为传递了底层数组的指针。

切片的容量可通过内建函数 cap 获得,它将给出该切片可取得的最大长度。

尽管 Append 可修改 slice 的元素,但切片自身(其运行时数据结构包含指针、长度和容量) 是通过值传递的。所以append时需要使用返回值重新赋值:

我们还可以定义一个空的数组,长度为0的数组在内存中并不占用空间。

var d [0]int // 定义一个长度为0的数组

var f = [...]int{} // 定义一个长度为0的数组

控制语句

控制语句包括if、for、switch 与 select,其的左大括号一定紧跟在同一行(不能放在下一行)。

if 和 switch 像 for 一样可接受可选的初始化语句;没有圆括号,而其主体必须始终使用大括号括住。

if err := file.Chmod(0664); err != nil {

log.Print(err)

} else { // else必须跟在右括号后面,不能在下一行

...

}

循环只有一个更通用的 for

// Like a C for

for init; condition; post { }

// Like a C while

for condition { }

// Like a C for(;;)

for { }

闭包对捕获的外部变量并不是传值方式访问,而是以引用的方式访问。这种行为可能会导致一些隐含的问题:

for i := 0; i

func(){ println(i) } ()

}

每个函数引用的都是同一个i迭代变量,在循环结束后这个变量的值为3,因此最终输出的都是3。修复的思路是在每轮迭代中为每个函数生成独有的变量。

for i := 0; i

i := i// 定义一个循环体内局部变量i

func(){ println(i) } ()

}

// 通过函数传入i

for i := 0; i

func(i int){ println(i) } (i)

}

Go 没有逗号操作符,而 ++ 和 -- 为语句而非表达式。 因此,若你想要在 for 中使用多个变量,应采用平行赋值的方式 (因为它会拒绝 ++ 和 --)

for i, j := 0, len(a)-1; i i, j = i+1, j-1{

a[i], a[j] = a[j], a[i]

}

for key, value := range oldMap {

newMap[key] = value

}

Go 的 switch 比 C 的更通用。其表达式无需为常量或整数,case 语句会自上而下逐一进行求值直到匹配为止;switch 并不会自动下溯(相当于默认有一个隐藏的break),只有在case中明确使用fallthrough关键字,才会继续执行紧跟的下一个case;也 可通过逗号分隔来列举相同的处理条件

switch c {

case ' ', '?', '&', '=', '#', '+', '%':

return true

}

switch 也可用于判断接口变量的动态类型。

var t interface{}

t = functionOfSomeType()

switcht := t.(type){

default:

fmt.Printf("unexpected type %T", t) // 输出 t 是什么类型

case bool:

fmt.Printf("boolean %t\n", t)

case int:

fmt.Printf("integer %d\n", t) // t 是 int 类型

case *int:

fmt.Printf("pointer to integer %d\n", *t) // t 是 *int 类型

}

当 select 有多个分支可用时,会随机选择一个可用的管道分支(可用于模拟随机数生成),如果没有可用的管道分支则选择 default 分支,否则会一直保存阻塞状态。

for i := 0; i

select { // 通道中随机写入0/1

case c

case c

}

}

函数

Go 与众不同的特性之一就是函数和方法可返回多个值。返回值或结果 “形参” 可被命名,就像传入的形参一样,可赋值修改。 命名后:就会被初始化为零值; 若该函数执行了一条不带实参的 return 语句,则结果形参的当前值将被返回。

func ReadFull(r Reader, buf []byte) (n int, err error) {

for len(buf) > 0 && err == nil {

var nr int

nr, err = r.Read(buf)

n += nr

buf = buf[nr:]

}

return // 若没有命名,则必须return n,err

}

从函数中返回一个局部变量的地址完全没有问题,这点与 C 不同。该局部变量对应的数据 在函数返回后依然有效。

错误处理

Go语言中的错误是一种接口类型。接口信息中包含了原始类型和原始的值。只有当接口的类型和原始的值都为空的时候,接口的值才对应 nil 。其实当接口中类型为空的时候,原始值必然也是空的;反之,当接口对应的原始值为空的时候,接口对应的原始类型并不一定为空的。在处理错误返回值的时候,没有错误的返回值最好直接写为 nil。

func returnsError() error {

var p *MyError = nil

if bad() {

p = ErrBad

}

return p // 总是返回non-nil error;应直接return nil;

}

恐慌使用panic抛出,recover用于捕获panic。recover 函数调用有着更严格的要求:我们必须在defer 函数中直接调用 recover ;如果是在嵌套的 defer 函数中调用 recover 也将导致无法捕获异常。

示例程序

go程序的基本框架示例

package main // 包名

import( // 引入包

"fmt"

"time"

"context"

"os"

"os/signal" // 使用signal引用包内元素

"syscall"

"strconv"

)

var _ = strconv.Itoa // 防止未使用包报错

func TryClose(put chan

defer func() {

if err := recover(); err!=nil{ // 捕获恐慌,只能在defer中直接捕获

fmt.Println("Close fail: ", err)

}

}()

close(put)

}

func Producer(fact int, put chan

OutFor:

for i:=1 ; ; i++ {

select{

default:

case

fmt.Println(ctx.Err())

break OutFor// 跳到循环外部,否则只是跳出select

}

put

time.Sleep(100*time.Millisecond)

}

TryClose(put)

}

func Consumer(get

for v := range get {

fmt.Println(v)

}

fmt.Println("Consumer over")

}

type ByteSize float64

const (

_ =iota// 通过赋予空白标识符来忽略第一个值0

KB ByteSize = 1

MB

GB // 1

)

const MAXSIZE ByteSize = 1024*8

const MINSIZE = 16

func (p ByteSize)String() string{

switch{

case p>=GB:

return fmt.Sprintf("%.2fGB", p/GB)

case p>=MB:

return fmt.Sprintf("%.2fMB", p/MB)

case p>=KB:

return fmt.Sprintf("%.2f", p/KB)

}

return fmt.Sprintf("%.2fB", p)

}

func VarType(info interface{})(result int){

defer func() ()

// ifv, ok := info.(int); ok{

//fmt.Println("int: ", v)

//result = v

// }else if v, ok := info.(string); ok{

//fmt.Println("string: ", v)

// }else {

//fmt.Printf("%v\n", info)

// }

// 直接通过switch-type判断

switchv := info.(type){

case int: fmt.Println("int: ", v)

case string: fmt.Println("string: ", v)

default: fmt.Printf("Deufalt: %v\n", info)

}

return // 0,实际返回1,因defer在return之后执行

}

func ForTest(que []int){

for _, v := range que{

v := v // 若不重新定义,会全部输出最后一个值

go func() {

fmt.Println(v)

}()

}

}

func main(){

ch := make(chan int, 10)

// 1秒钟后超时,若在此之前cancel,也会退出

ctx, cancel := context.WithTimeout(context.Background(),1*time.Second)

go Producer(3, ch, ctx)

go Producer(5, ch, ctx)

go Consumer(ch)

// 不同变量定义方式

var zero = 0

var one, two int

three, four := 3, 4

ksize := ByteSize(1024*1024*8)

fmt.Println(zero, one, two, three, four, ksize)

VarType(1)

VarType("test")

ForTest([]int)

// Ctrl+C to quit

fmt.Println("Press Ctrl+C to quit:")

sig := make(chan os.Signal, 1)

signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)

fmt.Printf("quit (%v)\n",

cancel()

}

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180820G1KHBT00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券