Go搭建与go语言

  • 开学第一周,先提前学了点关于服务计算将来要用的go语言

Go搭建

  • 非面向对象
  • 简单,快捷,开源
  • google发布
  • 直接编译成机器码,没有中间码
  • 快速方便的编译语言像解释型语言一样
  • 强类型 垃圾回收 并发编程

下载配置

brew install go

vim ~/.bash_profile

go env查看

	GOROOT=/usr/local/Cellar/go/1.12.9/libexec
  export GOROOT
  export GOPATH=/Users/liqing/go
  export GOBIN=$GOPATH/bin
  export PATH=$PATH:$GOBIN:$GOROOT/bin

source ~/.bash_profile

再查看go env即已经修改

现在~/go文件夹是用来存放创建的应用程序或者共享软件在内的所有包,都会存到go的src文件夹下

测试

go get github.com/cloudnativego/hello

在go/src/github.com/cloudnativego/hello中已经有对应的文件

go build .

./hello

访问https://localhost:8080

hello world

	package main
  import "fmt"
  func main(){
    fmt.Println("hello world!")
  }

go run hello.go会编译+运行

go build hello.go会编译形成可执行文件hello

  • go没有类,只有函数

  • 函数可以返回多个值:

    	func d(size int)(int,int){
    		..
      }
    
  • 包名:package main

    注明该文件属于哪个包,放在文件非注释的第一行

    每个go程序都有1个main包

    同一个文件夹下的文件,只能有1个包名

  • fmt包:格式化I/O

  • main函数如果之前有init函数,会先执行init函数

  • Println会在末尾自动填上换行符

    Print则不会自动填上

  • 如果标识符(函数,变量,常量,类型,结构字段等)以大写字母开头就可以被其他包可见,如果小写字母开头仅在本包可见

    标识符就是用来命名函数,变量等这些的东西

  • 函数的第一个大括号{不能放在单独的一行上

语法

基本语法:

  • 不需要分号结尾

  • 字符串连接:可以通过+号

  • 导入多个包:

    可以1个个的import

    也可以

    	import(
    		"fmt"
        "math"
      )
    
  • 调用包函数:

    .

    import fmt2 "fmt":把fmt包改名为fmt2

    import . "fmt":调用的时候可以直接Println()而不需要加fmt

数据类型

  • 数据类型可以把数据根据内存大小进行区分

  • 注意,浮点数是float64,float32,没有float类型

  • 指针:

    var ptr *int

变量:

  • 有类型声明:

    var identifier type

    一次多个声明:var identifier1,identifier2 type

    var a,b int = 1,2

    var s string = "asdad"

  • 无类型声明:

    var v = value 会自动判断类型

  • 无var声明:

    value := value1

    这里:=要求左侧的变量是新的变量,否则编译错误

    v1,v2 := 3,4

    这种方式只能用在函数体内,不能用在全局变量的声明与赋值

  • 多变量声明:

    var v1,v2,v3 type

    var v1,v2 = 1,"123"

    v1,v2 := 1,"123"

    	var(
      	x int
        y string
      ) //这种通常用于全局变量的声明
    
  • 函数内的变量被声明后必须使用

    全局的变量可以声明不使用

  • 变量交换:

    a,b = b,a,但要求类型相同

  • 如果函数返回多个值的时候:

    a,b := func()

  • a,_,c = 1,2,3

    这里的_可以将2抛弃掉,1,3赋值给a,c

    在函数返回值中某个值没有用的时候,这样尤其方便

常量:

  • 只能够是布尔,数字,字符串

  • const identifier [type] = value

    这里的type是可选的

  • 常量可以作为枚举:

    	const (
    		a = 123
        b = "123"
      )
    
  • iota:是特殊的常量,可以被编译器修改

    在每个const内部的第一行变成0,const每增一行,iota+1,可以看作iota是const语句块的行索引

    	const(
    		a = iota //0
        b = iota //1
        c = iota //2
      )
    
  • const(
      		a = 100
      b //b如果不赋值,则和前面的
      a相同
    )
    

条件语句:

  • if 布尔表达式

    比如if a>b ..

  • Switch:

      switch var1{
    	  case v1:
     		case v2,v3,v4:
        default:
      }
    

    注意这里的switch不用break,默认case里面自动带break

    var1可以是任何类型

    如果希望匹配完某个case以后,继续执行下面的case:用fallthrough

  • type switch:

    可以用来判断某个interface变量中实际存储的变量类型:

    	var x interface{}
      switch x.(type){
    	  case nil:..
      	case int:..
        default:..
      }
    

循环语句:

  • for循环:

    	for init;condition;post{}
    	for condition{}
    	for {}
    	for key,value := range variable{}//可以对	variable(数组,map,slice等)
    

函数:

  • func f(size int)int{
      
        	}
    
  • 返回多个参数:

    	  func f(size int)(int,string){
    
      	}
    
  • len:内置函数,可以返回类型的长度(字符串,数组等)

  • 函数作为变量声明:

    f := func (size int)int{

    }

  • 函数作为参数传递:

    func f(x func(int)int)int

    这里的x就是传递的函数类型

    当然也可以先类型定义:

    type cb func(int)int

    然后用cb替代函数类型

  • 函数闭包:

    func f() func()int {
      i := 0
      return func()int{
        i += 1
        return i 
      }
    }
    func main(){
      a := f()
      Println(a()) // 1
      Println(a()) // 2
      b := f()
      Println(b()) // 1
    }
    
  • 方法:

    func (variable_name variable_data_type) function_name() [return_type]{
       /* 函数体*/
    }
    

    这里的variable_data_type可以是struct

    这样对于variable_data_type的变量,就拥有了这个函数方法

    可以通过instance.function_name()调用该方法

数组

  • 数组声明:

    var array[10] int

  • 数组初始化:

    var array = [5]int{1,2,3,4,5}

    也可以忽略长度:
    var array = [...]int{1,2,3,4}

    array := [...]int{12,2}

  • 多维数组声明:

    var array1[10][20]int

  • 多维数组初始化:

    var array = [2][2]int{{1,2},{3,4}}

  • 数组传递:

    func f(array [10]int)

    func f(array []int)

指针:

  • 空指针是nil

  • 指针数组:

    var array [10]*int

  • 指针的指针:

    var ptr **int

结构体:

  • 定义:

          type struct_variable_type struct{
    
          }
    
  • 初始化:

    var x := struct_variable_type{"a","b",123}
    var b := struct_variable_type_2{key:"aa",key2:"bb",key3:123}
    
  • 访问成员:

    点号

  • 初始化:

    s := new(struct_type)

切片:

  • 可以动态的改变数组

  • var slice []int

    []int是切片类型

  • s := []int{1,2,3}

    s := array[:]

    表示s是array的引用

    s := array[0:len]

    表示s从0下表到len-1下标,都是array的引用

    s := array[startIndex:]

    s := array[:endIndex]

  • var slice = make([]int,len,cap)

    表示创建的切片长度为len,最大容量为cap

    可以通过内置函数cap,len来求得slice的len和cap

  • 空切片:为nil

  • append

    var slice []int

    var slice = append(slice,1,2,3)

    copy:

    copy(slice1,slice)(slice 拷贝到slice1里面)

    copy通常是用在拓展slice1的cap以后使用

Range:

  • 主要是用在for循环上(数组,切片,map,channel),也可以是字符串

Map:

  • 声明map:

    var m map[string]string后面分别是key和value的类型

    m := make(map[string]int)

    生成的map不初始化是nil

  • m[“Adas”] = “asdad”

  • var m = map[string]string{"asd":"123"}

  • delete可以用来删除key-value对

    delete(map,key)

接口:

  • 接口就是把共有的方法定义在一起,只要实现了这些方法就实现了这个接口

  •     type t interface{
          call()
        }
        type s struct{
    
        }
        func (s1 s) call(){
    
        }
    

错误:

  • error的定义:

    type error interface{
      Error() string
    }
    
  • 通常函数可以返回:func f()(...,error)

    通过返回结果+error,

    如果error是nil就是没错误

    否则就需要处理错误

  • 定义错误类型:

    import "errors"
    type DivideError struct{
      ...
    }
    func (d &DivideError) Error()string{
      return "some Error"
    }
    func divide(a ,b int)(int,error){
      if b ==0 {
        return 0,errors.New("error message")//返回错误的第一种方式
        return 0,&DivideError{100,0}//返回错误的第二种方式,new结构体返回
        //由于这个结构体定义了方法Error,可以通过Error获得错误信息
      }
    }
    func main(){
      result,err := divide(100,0);
      if err == nil{
        ..
      }
      //or
      if result,err := divide(100,0); err == nil{
        s := err.Error() //获得错误信息
      }
    }
    

并行:

  • 可以创建goroutine,这个是轻量级线程

  • 同一个程序的goroutine共享同一个内存地址空间

  • 通过go f(x)来打开另一个线程,执行函数f

  • 通道:

    go中的线程之间需要同步,是通过通道来实现的

    通道是用来传递数据的数据结构

    ch := make(chan int) 默认这种方法不带缓冲

    ch <- c就是把c往通道里传送

    c1 := <- ch就是把通道里输出1个,给c1

    不带通道的缓冲,当往通道输入1个值以后,会被阻塞,直到其他线程从这个通道中取出才会继续运行

    带缓冲通道:
    ch := make(chan int,100) 可以异步,即往通道输入以后会被阻塞到值进入缓冲区中,然后就可以继续运行而不需要等到被获取了。另外,如果此时通道已满,那就需要阻塞直到可以往通道里加入数据

    通道的遍历:

    c := make(chan int,100)
    for i := range c{
      fmt.Println(i)
    }
    //其他goroutine会往这个c进行输入数据,然后这个打印线程会每次可以从通道获取的时候打印出来
    //要注意往通道输入数据的线程结束的前close(c),这样打印线程再检测到close以后才会跳出循环,否则会一直等待
    

其他:

  • fmt.Printf可以保证输出的时候按类似c的格式化输出

你可能感兴趣的:(Go搭建与go语言)