go语言基本语法

Go语言

一、特点

1、函数式编程   闭包

2、工程化   资源管理,错误处理,测试文档,

3、并发编程   goroutine和channel  调度器。

4、接口编程, interface

5、全新的静态类型开发语言

6、更丰富的内置类型slice

7、错误处理:
defer, panic和recover

 

 

 

 

 

 

二、语法

Package声明:表示go代码所属的包。建立一个名字为main的包,在该包中包含一个叫做main()的函数。无参,也没有定义返回值。

 

声明以后是import语句,引入需要的模块。

 

需要使println()函数所以引入的是fmt

 

函数定义使用func开头。

 

所有Go函数(包括在对象编程中会提到的类型成员函数)以关键字func开头。一个常规的 函数定义包含以下部分: func 函数名(参数列表)(返回值列表) { // 函数体 } 对应的一个实例如下: func Compute(value1 int, value2 float64)(result float64, err error) { // 函数体 } Go支持多个返回值。以上的示例函数Compute()返回了两个值,一个叫result,另一个是 err。并不是所有返回值都必须赋值。在函数返回时没有被明确赋值的返回值都会被设置为默认 值,比如result会被设为0.0,err会被设为nil。

 

三、定义变量

package main

import "fmt"

var(
   a=3
   b=4
   c=2
   d=true
)
func variableZeroValue(){
   var a int
   var s string
   fmt.Printf("%d %q\n", a, s)

}
func  variableInitialValue()  {
   var a, b int = 3, 4
   var s string = "abc"
  
fmt.Println(a, b, s)
}
func variableDecouments(){
   var a, b, c, d = 1, 3, true, "def"
  
fmt.Println(a,b,c,d)
}
func  variableShorters()  {
   a, b, c ,d := true, 0, 3, 5
   fmt.Println(a, b, c, d)
}

func main()  {
   fmt.Println("hello world")
   variableZeroValue()
   variableInitialValue()
   variableDecouments()
   variableShorters()
   fmt.Println(a, b, c, d)
}

 

 

 

:= 只能在函数内使用

 

四、内建变量类型

1、bool,string

2、(u)int无符号整数,不加u是有符号整数。

Int32,int16,int8,int64,uintptr指针

3、byets,rune字符型

4、浮点型float32,float64,complex64, complex128负数

cmplx.Pow(math.E, 1i * math.Pi)+1

 

5、类型转换是强制的

 

 

func euler(){

   fmt.Println(cmplx.Pow(math.E, 1i * math.Pi)+1)
   c := 3 + 4i
   fmt.Println(cmplx.Abs(c))
}

 

五、常量

1、普通的定义

func consts(){
   const filename = "abc"
   const a
, b = 3, 4
   fmt.Println(filename, a, b)
}

 

定义在函数内和函数外是一样的,都可以定义

2、枚举定义

普通类型就是自己定义

 

Iota是自增类型

 

func enums(){
   const (cpp  = iota
   python
   golang
   javascript
)
   fmt.Println(cpp, python, golang, javascript)
}

 

 

 

六、条件语句

1、if

package main

import (
   "fmt"
   "io/ioutil"
)

//func main()  {
// const filename  = "abc.txt"
// contents, err := ioutil.ReadFile(filename)
// if err != nil{
//    fmt.Println(err)
// }else {
//    fmt.Println("%s\n", contents)
// }
//}

 

 

func main()  {
   const filename  = "abc.txt"
   if
contents, err := ioutil.ReadFile(filename); err == nil{
      fmt.Println(string(contents))
   }else {
      fmt.Println("cannot print file contents", err)
     
   }

}

 

直接父类加

2、switch

func grade(source int) string{
   g := ""
   switch 
{
   case source < 0 || source > 100:
      panic(fmt.Sprintf("wrong score: %d", source))
   case source < 60:
      g = "f"
   case
source < 80:
      g = "c"
   case
source < 900:
      g = "b"
   case
source <= 100:
      g = "a"
  
}
   return g
}

 

 

panic异常捕获语句

switch不需要break,自动有的break

 

3、for循环

func sums(){
   sum := 0
   for i := 1; i <=100; i++{
      sum += i
   }
   fmt.Println(sum)
}

 

for 什么不加的话就是死循环,不用的是while

 

 

七、函数

func div(a, b int)(q, r int){
   return a / b, a % b
}

 

 

函数名(参数,参数类型)(返回值,返回值类型)

 

 

可变参数只有一个就是..int

函数作为参数

 

 

 

八、指针

只有值传递一种方式。

*a ,*b,指针

&a,&b取地址

第一种实现

func swap(a, b *int)  {
   *a, *b = *b, *a
}

 

a, b := 2, 4
swap(&a, &b)
fmt.Println(a, b)

 

第二种实现方式:

func swaps(a, b int) (int, int) {
   return b, a
}

九、数组

package main

import "fmt"

func
main(){
   var arr1 [5] int
   arr2 := [3]int{1, 3 ,5}
   arr3 := [...]int{2, 4, 6, 8, 10}
   var grid[4][5] int
   fmt.Println(arr1, arr2, arr3, grid)
   for i, value := range arr3{
      fmt.Println(i, value)
   }
}

 

定义了必须使用,要不然就使用_忽略变量

 

利用range。

意义明显。

 

 

数组是值类型,值类型会进行相应的拷贝文件

package main

import "fmt"
func
printarray(arr [5]int){
   for i, v := range arr{
      fmt.Println(i, v)
   }

}

 

func main(){
   var arr1 [5] int
   arr2 := [3]int{1, 3 ,5}
   arr3 := [...]int{2, 4, 6, 8, 10}
   var grid[4][5] int
   fmt.Println(arr1, arr2, arr3, grid)
   //for i, value := range arr3{
      //fmt.Println(i, value)
   //}
  
printarray(arr3)
   }

 

var arr[5] int 指定的是数组的长度和值得类型

数组作为函数的参数穿进去的时候就会拷贝数组

一般不会直接使用数组

 

十、切片slice

package main

import "fmt"

func
main()  {
   arr := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
   s := arr[2:6]
   s1 := s[3:5]
   fmt.Println(s)
   fmt.Println(s1)
}

 

s1 因为知道其索引脚标,所以能够取出所在的值。因为其知道s的值,向后拓展只要不是超过s的长度即可

 

 

 

可以进行append,进行写入相关数据。

 

 

 

十一、Map

Map[k]v,map[k1]map[k2]v

创建make ,map    m2 := make(map[string]int)      m := map[string]string

遍历

查找值

删除

 

 

Map的key:map使用的是哈希表,必须可以比较相等。

 

除了slice,map,function的内建类型都可以作为key,

Struct类型不包括上述字段,也可以作为key

 

package main

import "fmt"

func
main()  {
   m := map[string]string{    //no1
     
"name":"mouse",
      "course":"golang",
      "site":"imocc",
      "quality":"notbad",
   }
   m2 := make(map[string]int)   // no2   == empty map
  
var m3 map[string]int      //no3   == nil
  
fmt.Println(m, m2, m3)
   for k := range m{       //遍历
     
fmt.Println(k)

   }
   for k,v := range m{     //遍历
     
fmt.Println(k,v)
   }
   courname := m["course"]     //索引查找,通过k查找v。如果不存在则是显示flase
  
fmt.Println(courname)
   name, ok := m["course"]
   fmt.Println(name, ok)
   delete(m, "name")     //删除元素
  
fmt.Println(m)
   }

 

十二、字符串

package main


func lengthSubstr(s string) int{
   last0curred := make(map[byte]int)
   start := 0
   maxlength := 0
   for i, ch := range []byte(s){
      if last0curred[ch] < start{
         start = last0curred[ch] + 1

      }
      if i - start + 1 > maxlength{
         maxlength = i - start +1
      }
      last0curred[ch] = i
   }
   return maxlength
}

func main() {
   lengthSubstr()
}
 

 
Strings.map等操作
 

 
 

十三、类

只是支持封装,不支持继承和多态。

 

 

没有class 只有struct

 

Type point struct {i,j,int}

 

 

 

package main

import "fmt"

type
treeNode struct {
   value int
   left, right * treeNode
}

func createNode(value int) *treeNode{
   return &treeNode{value:value}
}

func main()  {
   var root treeNode
   root = treeNode{value:3}
   root.left = &treeNode{
   }
   root.right = &treeNode{5, nil, nil}
   root.right.left = new(treeNode)
   root.left.right = createNode(2)

   nodes := []treeNode{
      {value:3},
      {},
      {6, nil, &root},
   }
   fmt.Println(nodes)
}

 

结构创建在堆上还是栈上呢。

 

 

 

 

 

利用指针接收

 

 

 

十四、封装

名字一般使用camelcase

 

首字母大写public

 

首字母小写 private

 

十五、扩展已有类型

 

包:每个目录里面一个包

Main包包含可执行入口

 

为结构定义的方法必须放在同一个包内

 

可以是不同的文件。

 

Go语言没有继承:

 

如何扩充已有类型和别人的类型。

 

(1)定义别名

package queue

import "fmt"

type
Queue []int

func (q *Queue) Push(v int){
   *q = append(*q, v)
}

func (q *Queue) Pop() int {
   head := (*q)[0]
   *q = (*q)[1:]
   return head
}

func main(){
   q := queue.Queue{1}

   q.Push(2)
   q.Push(3)
   fmt.Println(q.Pop())
   fmt.Println(q.Pop())
   fmt.Println(q.IsEmpty())
   fmt.Println(q.Pop())
   fmt.Println(q.IsEmpty())
}

 

 

(2)使用组合

 

 

 

 

十六、Gopath环境

 

 

 

 

不用的包不能导入,否则就会报错的

 

 

 

 

 

 

 

十七、接口

1、duck typing

 

 

 

2、Go语言中的duck typing

 

假的retriever
package mock

type Retriever struct {      //定义retriever类
   Contents string
}

func (r Retriever) Get(url string) string{
   return r.Contents
}

 

 

really接口

package real

import (
   "net/http"
   "net/http/httputil"
   "time"
)

type Retriever struct {
   UserAgent string
   TimeOut time.Duration
}
func (r Retriever) Get(url string) string{
   resp, err := http.Get(url)
   if err != nil{
      panic(err)
   }
   result, err := httputil.DumpResponse(resp, true)
   resp.Body.Close()

   if err != nil{
      panic(err)
   }
   return string(result)
   }

 

 

 

 

package main

import ("fmt"
   "go-projects/retriever/mock"
)

type Retriever interface {     //定义了接口
   Get(url string) string     //定义了方法而已
}

func download(r Retriever) string {  //接口作为参数传入,然后调用定义 的Get函数
   return r.Get("www.123.com")
}
func main()  {
   var r Retriever
   r = mock.Retriever{"this is imock"}
   fmt.Println(download(r))

}

3、接口定义:

 

接口是隐士的,只是实现里面的方法啊而已

 

Interface  里面有两个东西一个是值,一个是类型,真实的值copyde,也可以是指针的。

 

 

 

4、接口的组合:

 

type RetrieverPoster interface {
   Retriever
   Poster
}

 

是许多小接口的组合,把许多小接口放在一起

 

 

传值的时候传retriever,都是具备的,r只是具备单一的

 

5、go语言标准的接口

 

Stringer:

 

Writer

 

十八、函数式编程

 

函数式编程  

 

函数指针   高阶函数,函数到闭包

 

 

正统函数式编程:

1、不可变性:不能有状态,只有常量和函数

2、函数只能有一个参数。

 

package main

import "fmt"

func
adder() func(int) int{
   sum := 0
   return func(v int) int {
      sum += v
      return sum
   }
   }

func main() {
   a := adder()
   for i := 0; i < 10; i++{
      fmt.Println(a(i))
   }
}

 

 

 

 

正统式函数编程:

type iAdder func(int)(int, iAdder) 

func adder2(base int) iAdder {
   return func(v int) (int, iAdder) {
      return base + v, adder2(base + v)
   }
}
func main() {
a1 := adder2(0)
for i := 0; i < 10; i++{
   var s int
   s, a1 = a1(i)
   fmt.Printf("0 + 1 + ... + %d = %d\n", i, s)
}

}

 

Go语言中也有匿名函数,没有名字的。

 

 

十九、异常处理

1、defer调用

package main

import "fmt"

func
tryDefer()  {
   defer fmt.Println(1)
   fmt.Println(2)
   fmt.Println(3)

}
func main() {
   tryDefer()
}

 

添加defer之后不受return 和panic的影响

Go语言是因为栈的,先进后出的。

 

 

实现斐波那契数列,必须注意的是函数名字的大写

package fib

func Fibonacci() func() int{
   a, b := 0, 1
   return func() int {
      a, b = b, a + b
      return a
   }
}

 

 

package main

import (
   "bufio"
   "fmt"
   "os"
   "go-projects/functional/fib"
)

func tryDefer()  {
   defer fmt.Println(1)
   fmt.Println(2)
   fmt.Println(3)

}
func writeFile(filename string){
   file, err := os.Create(filename)
   if err != nil{
      panic(err)
   }
   defer file.Close()

   writer := bufio.NewWriter(file)
   defer writer.Flush()

   f := fib.Fibonacci()
   for i := 0; i<20; i ++{
      fmt.Fprintln(writer, f())
   }
}
func main() {
   tryDefer()
   writeFile("abc.txt")
}

 

 

何时调用 defer  

 

在open/close

 

Lock/unlock

 

Printhead/printfooter

 

 

错误处理的概念:

 

func writeFile(filename string){
   //file, err := os.Create(filename)
  
file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666)
   if err != nil{
      if pathError, ok := err.(*os.PathError); !ok{
         panic(err)
      }else {
         fmt.Println("%s, %s, %s\n",
            pathError.Op,
            pathError.Path,
            pathError.Err)
      }
      return
     
}
   defer file.Close()

   writer := bufio.NewWriter(file)
   defer writer.Flush()

   f := fib.Fibonacci()
   for i := 0; i<20; i ++{
      fmt.Fprintln(writer, f())
   }
}
func main() {
   tryDefer()
   writeFile("abc.txt")
}

 

自己创建error的类型

 

err = errors.New("this is a custom error")

 

 

 

 

 

 

统一的错误处理逻辑:

 

 

2、服务器内部的错误资源处理流程,统一的处理

 

 

package main

import (
   "io/ioutil"
   "net/http"
   "os"
)

func main() {
   http.HandleFunc("/list/",
      func(writer http.ResponseWriter, request *http.Request) {
         path := request.URL.Path[len("/list/"):]
         file, err := os.Open(path)
         if err != nil{
            panic(err)
         }
         defer file.Close()
         all, err := ioutil.ReadAll(file)
         if err != nil{
            panic(err)
         }
         writer.Write(all)
         })
   err := http.ListenAndServe(":888", nil)
   if err != nil{
      panic(err)
   }
}

一个简单的网页 程序。

 

 

直接给前端页面返回的是程序的错误代码

http.Error(writer,
   err.Error(),
http.StatusInternalServerError)

 

 

os.isempty   如果是空的话

 

 

 

使用主函数处理函数内部逻辑,外部进行一场的捕获即处理例如Python的装饰器的形式

 

func main() {
   http.HandleFunc("/list/",
      errWrapper(filelisting.Handlefilelist))

   err := http.ListenAndServe(":8888", nil)
   if err != nil{
      panic(err)
   }
}

 

 

 

panic和recover的区别:

 

1、panic

停止当前函数执行

一直向上返回,执行每一层的defer

如果没有recover,程序退出。

 

2、recover

 

仅在defer调用中使用

获取panic的值

如果无法获取,可重新panic

 

 

工作流程如下:

panic(errors.New("this is an error"))自己可以创建的异常的值和类型。
 
package main

import (
   "errors"
   "fmt"
)

func tryRecover()  {
   defer func() {
      r := recover()
      if err, ok := r.(error); ok{
         fmt.Println("Error occurred", err)
      }else {
         panic(r)
      }
   }()
   panic(errors.New("this is an error"))
}
 
 
尽量少用panic。
 
意料之内的使用error, 文件打不开
 
意料之外的使用panic, 数组超届
 
错误综合处理方法:

 
 
posted @ 2018-11-13 11:20  Python爱好者666  阅读(1047)  评论(0编辑  收藏  举报