新秀语言go:Golang入门语法知识点总结

为什么是Golang?

Go将成为未来的服务器语言。“ - TobiasLütke,Shopify

因为他是Google开发的语言!

谷歌非常擅长创造最注重效率和用户友好性的产品。

Go代码既并发高效又美观

代码可读性与效率

GoRoutings

Java中创建新线程不是内存有效的。由于每个线程消耗大约1MB的内存堆大小,并且最终如果你开始旋转数千个线程,它们将对堆施加巨大压力并导致由于内存不足而导致关闭。此外,如果要在两个或多个线程之间进行通信,则非常困难。

另一方面,Go于2009年发布,当时多核处理器已经上市。这就是为什么Go是在保持并发性的基础上构建的。Gogoroutines而不是线程。它们从堆中消耗了大约2KB的内存。因此,您可以随时旋转数百万个goroutine

Go直接在底层硬件上运行

与其他现代高级语言(如Java /Python)相比,使用CC ++的最大好处是它们的性能。因为C / C ++是编译的而不是解释的。

处理器理解二进制文件。通常,在编译项目时使用Java或其他基于JVM的语言构建应用程序时,它会将人类可读代码编译为字节代码,JVM或在底层操作系统之上运行的其他虚拟机可以理解这些代码。执行时,VM会解释这些字节码并将其转换为处理器可以理解的二进制文件。

基于VM的语言的执行步骤

直接编译为可执行的二进制文件

Go很像像C / C ++这样的低级语言,Go是编译语言。这意味着性能几乎接近C / C ++。它还使用垃圾收集来分配和删除对象。所以,不再有malloc()fre()语句

用Go编写的代码易于维护

Go没有像其他语言那样疯狂的编程语法。它的语法非常整洁。

谷歌Go的设计者在创建语言时就考虑到了这一点。由于谷歌拥有非常庞大的代码库,成千上万的开发人员正在开发相同的代码库,因此对于其他开发人员来说代码应该很容易理解,而代码的一段代码应该对代码的另一部分产生最小的副作用。这将使代码易于维护且易于修改。

  • 没有类
  • 没有继承
  • 没有构造函数
  • 没有注解
  • 没有泛型
  • 没有异常

一开始学习的同学可能会很不适应,尤其是从面向对象语言转过来的人,慢慢的相信你会体会到Go作者这样设计的良苦用心。

用过Go写了几个项目之后的人,都说,回不去了,再也不想写Java

语法

基础

func variableZeroValue() {
    //初始化0值
    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 variableTypeDeduction() {
    //类型自动分配
    var a, b, c, s = 3, 4, true, "def"
    fmt.Println(a, b, c, s)
}

func variableShorter() {
    //更简单的变量声明
    a, b, c, s := 3, 4, true, "def"
    b = 5
    fmt.Println(a, b, c, s)
}

func euler() {
    //对复数的原生支持
    fmt.Printf("%.3f\n",
        cmplx.Exp(1i*math.Pi)+1)
}

func triangle() {
    var a, b int = 3, 4
    //函数的调用
    fmt.Println(calcTriangle(a, b))
}

func calcTriangle(a, b int) int {
    var c int
    c = int(math.Sqrt(float64(a*a + b*b)))
    return c
}

func consts() {
    //常量的声明
    const (
        filename = "abc.txt"
        a, b     = 3, 4
    )
    var c int
    c = int(math.Sqrt(a*a + b*b))
    fmt.Println(filename, c)
}

func enums() {
    //枚举的声明,iota代表才0开始
    const (
        cpp = iota
        _
        python
        golang
        javascript
    )

    //枚举的声明,10 * iota代表才0开始,0,10,100,1000...
    const (
        b = 1 << (10 * iota)
        kb
        mb
        gb
        tb
        pb
    )

    fmt.Println(cpp, javascript, python, golang)
    fmt.Println(b, kb, mb, gb, tb, pb)
}

测试

//函数名TestXXX(t *testing.T)固定写法
func TestTriangle(t *testing.T) {
    //参数化测试
    tests := []struct{ a, b, c int }{
        {3, 4, 5},
        {5, 12, 13},
        {8, 15, 17},
        {12, 35, 37},
        {30000, 40000, 50000},
    }

    for _, tt := range tests {
        if actual := calcTriangle(tt.a, tt.b); actual != tt.c {
            t.Errorf("calcTriangle(%d, %d); " + "got %d; expected %d", tt.a, tt.b, actual, tt.c)
        }
    }
}

分支

func grade(score int) string {
    g := ""
    switch {
    case score < 0 || score > 100:
        panic(fmt.Sprintf("Wrong score: %d", score))
    case score < 60:
        g = "F"
    case score < 80:
        g = "C"
    case score < 90:
        g = "B"
    case score <= 100:
        g = "A"
    }
    return g
}

循环

func forever() {
    //死循环
    for {
        fmt.Println("abc")
    }
}


func convertToBin(n int) string {
    result := ""
    //三分号循环
    for ; n > 0; n /= 2 {
        lsb := n % 2
        result = strconv.Itoa(lsb) + result
    }
    return result
}


func printFileContents(reader io.Reader) {
    scanner := bufio.NewScanner(reader)
    //循环
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }
}

函数

//无需第三个变量交换两个变量的值
func swap(a, b int) (int, int) {
    return b, a
}


//可变参数
func sum(numbers ...int) int {
    s := 0
    for i := range numbers {
        s += numbers[i]
    }
    return s
}


//多个返回值
func div(a, b int) (q, r int) {
    return a / b, a % b
}


//函数式参数
func apply(op func(int, int) int, a, b int) int {
    p := reflect.ValueOf(op).Pointer()
    opName := runtime.FuncForPC(p).Name()
    fmt.Printf("Calling function %s with args "+
        "(%d, %d)\n", opName, a, b)
    return op(a, b)
}


//调用时传入一个匿名函数作为参数
fmt.Println("pow(3, 4) is:", apply(
        func(a int, b int) int {
            return int(math.Pow(
                float64(a), float64(b)))
        }, 3, 4))

原子性

//结构体声明
type atomicInt struct {
    //变量名首字母小写即private,大写即public
    value int
    lock  sync.Mutex
}


func (a *atomicInt) increment() {
    fmt.Println("safe increment")
    //包在一个匿名函数里,是为了实现在匿名函数结束之前unlock,以防increment后面还有更多代码
    func() {
        a.lock.Lock()
        // defer就是把这一行延迟到该函数的最后去执行,一般lock和unlock、open和close之类的就是成对出现
        defer a.lock.Unlock()
        a.value++
    }()
    //some more code
}


func (a *atomicInt) get() int {
    a.lock.Lock()
    defer a.lock.Unlock()
    return a.value
}

“面向对象”

type Node struct {
    Value       int
    Left, Right *Node
}

func (node Node) Print() {
    fmt.Print(node.Value, " ")
}

func (node *Node) SetValue(value int) {
    if node == nil {
        fmt.Println("Setting Value to nil " +
            "node. Ignored.")
        return
    }
    node.Value = value
}

func CreateNode(value int) *Node {
    return &Node{Value: value}
}
func (myNode *myTreeNode) postOrder() {
    if myNode == nil || myNode.node == nil {
        return
    }

    left := myTreeNode{myNode.node.Left}
    right := myTreeNode{myNode.node.Right}

    left.postOrder()
    right.postOrder()
    myNode.node.Print()
}

数组

//数组的声明
var arr1 [5]int
arr2 := [3]int{1, 3, 5}
arr3 := [...]int{2, 4, 6, 8, 10}
var grid [4][5]int


func printArray(arr [5]int) {
    arr[0] = 100
    for i, v := range arr {
        fmt.Println(i, v)
    }
}

切片

arr := []int{0, 1, 2, 3, 4, 5, 6, 7}
fmt.Println("arr[2:6] =", arr[2:6])
fmt.Println("arr[:6] =", arr[:6])
s1 := arr[2:]
fmt.Println("s1 =", s1)
s2 := arr[:]
fmt.Println("s2 =", s2)


func printSlice(s []int) {
    fmt.Printf("%v, len=%d, cap=%d\n", s, len(s), cap(s))
}


s2 := make([]int, 16)
//[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], len=16, cap=16

s3 := make([]int, 10, 32)
//[0 0 0 0 0 0 0 0 0 0], len=10, cap=32


//pop
fmt.Println("Popping from front")
front := s2[0]
s2 = s2[1:]


fmt.Println("Popping from back")
tail := s2[len(s2)-1]
s2 = s2[:len(s2)-1]

字符串操作

s := "Yes我爱中国!" // UTF-8
fmt.Println(s)
//Yes我爱中国!

for _, b := range []byte(s) {
   fmt.Printf("%X ", b)//十六进制
}
fmt.Println()
//59 65 73 E6 88 91 E7 88 B1 E4 B8 AD E5 9B BD 21 

for i, ch := range s { // ch is a rune
   fmt.Printf("(%d %X) ", i, ch)
}
fmt.Println()
//(0 59) (1 65) (2 73) (3 6211) (6 7231) (9 4E2D) (12 56FD) (15 21) 

fmt.Println("Rune count:", utf8.RuneCountInString(s))
//Rune count: 8

bytes := []byte(s)
for len(bytes) > 0 {
   ch, size := utf8.DecodeRune(bytes)
   bytes = bytes[size:]
   fmt.Printf("%c ", ch)
}
fmt.Println()
//Y e s 我 爱 中 国 ! 

//rune的强大之处
for i, ch := range []rune(s) {
   fmt.Printf("(%d %c) ", i, ch)
}
fmt.Println()
//(0 Y) (1 e) (2 s) (3 我) (4 爱) (5 中) (6 国) (7 !) 

map

//map的声明
m := map[string]string{
    "name":    "ccmouse",
    "course":  "golang",
    "site":    "imooc",
    "quality": "notbad",
}
m2 := make(map[string]int) // m2 == empty map
var m3 map[string]int // m3 == nil


// map的遍历
fmt.Println("Traversing map m")
for k, v := range m {
    fmt.Println(k, v)
}


// get
fmt.Println("Getting values")
courseName := m["course"]
fmt.Println(`m["course"] =`, courseName)


//get 和 if 混写
if causeName, ok := m["cause"]; ok {
    fmt.Println(causeName)
} else {
    fmt.Println("key 'cause' does not exist")
}


//delete
fmt.Println("Deleting values")
delete(m, "name")

gorouting

func main() {
    for i := 0; i < 1000; i++ {
        go func(i int) {
            for {
                fmt.Printf("Hello from "+
                    "goroutine %d\n", i)
            }
        }(i)
    }
    time.Sleep(time.Minute)

recover

package main


import (
    "fmt"
)

func tryRecover() {
    defer func() {
        r := recover()
        if r == nil {
            fmt.Println("Nothing to recover. " + "Please try uncomment errors " + "below.")
            return
        }
        if err, ok := r.(error); ok {
            fmt.Println("Error occurred:", err)
        } else {
            panic(fmt.Sprintf("I don't know what to do: %v", r))
        }
    }()

    // Uncomment each block to see different panic
    // scenarios.

    // Normal error
    //panic(errors.New("this is an error"))

    // Division by zero
    //b := 0
    //a := 5 / b
    //fmt.Println(a)

    // Causes re-panic
    //panic(123)
}

func main() {
    tryRecover()
}

http

func main() {
    request, err := http.NewRequest(http.MethodGet, "http://www.imooc.com", nil)
    request.Header.Add("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1")

    client := http.Client{
        CheckRedirect: func(req *http.Request, via []*http.Request) error {
            fmt.Println("Redirect:", req)
            return nil
        },
    }
    resp, err := client.Do(request)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    s, err := httputil.DumpResponse(resp, true)
    if err != nil {
        panic(err)
    }

    fmt.Printf("%s\n", s)
}

channel

package main


import (
    "fmt"
    "sync"
)

func doWork(id int, w worker) {
    for n := range w.in {
        fmt.Printf("Worker %d received %c\n",
            id, n)
        w.done()
    }
}

type worker struct {
    in   chan int
    done func()
}

func createWorker(id int, wg *sync.WaitGroup) worker {
    w := worker{
        in: make(chan int),
        done: func() {
            wg.Done()
        },
    }
    go doWork(id, w)
    return w
}

func chanDemo() {
    var wg sync.WaitGroup

    var workers [10]worker
    for i := 0; i < 10; i++ {
        workers[i] = createWorker(i, &wg)
    }

    wg.Add(20)
    for i, worker := range workers {
        worker.in <- 'a' + i
    }
    for i, worker := range workers {
        worker.in <- 'A' + i
    }

    wg.Wait()
}

func main() {
    chanDemo()
}
package main


import (
    "fmt"
    "time"
)

func worker(id int, c chan int) {
    for n := range c {
        fmt.Printf("Worker %d received %c\n", id, n)
    }
}

func createWorker(id int) chan<- int {
    c := make(chan int)
    go worker(id, c)
    return c
}

func chanDemo() {
    var channels [10]chan<- int
    for i := 0; i < 10; i++ {
        channels[i] = createWorker(i)
    }

    for i := 0; i < 10; i++ {
        channels[i] <- 'a' + i
    }

    for i := 0; i < 10; i++ {
        channels[i] <- 'A' + i
    }

    time.Sleep(time.Millisecond)
}

func bufferedChannel() {
    c := make(chan int, 3)
    go worker(0, c)
    c <- 'a'
    c <- 'b'
    c <- 'c'
    c <- 'd'
    time.Sleep(time.Millisecond)
}

func channelClose() {
    c := make(chan int)
    go worker(0, c)
    c <- 'a'
    c <- 'b'
    c <- 'c'
    c <- 'd'
    close(c)
    time.Sleep(time.Millisecond)
}

func main() {
    fmt.Println("Channel as first-class citizen")
    chanDemo()
    fmt.Println("Buffered channel")
    bufferedChannel()
    fmt.Println("Channel close and range")
    channelClose()
}

select

package main


import (
    "fmt"
    "math/rand"
    "time"
)

func generator() chan int {
    out := make(chan int)
    go func() {
        i := 0
        for {
            time.Sleep(
                time.Duration(rand.Intn(1500)) *
                    time.Millisecond)
            out <- i
            i++
        }
    }()
    return out
}

func worker(id int, c chan int) {
    for n := range c {
        time.Sleep(time.Second)
        fmt.Printf("Worker %d received %d\n",
            id, n)
    }
}

func createWorker(id int) chan<- int {
    c := make(chan int)
    go worker(id, c)
    return c
}

func main() {
    var c1, c2 = generator(), generator()
    var worker = createWorker(0)

    var values []int
    tm := time.After(10 * time.Second)
    tick := time.Tick(time.Second)
    for {
        var activeWorker chan<- int
        var activeValue int
        if len(values) > 0 {
            activeWorker = worker
            activeValue = values[0]
        }

        select {
        case n := <-c1:
            values = append(values, n)
        case n := <-c2:
            values = append(values, n)
        case activeWorker <- activeValue:
            values = values[1:]

        case <-time.After(800 * time.Millisecond):
            fmt.Println("timeout")
        case <-tick:
            fmt.Println(
                "queue len =", len(values))
        case <-tm:
            fmt.Println("bye")
            return
        }
    }
}

interface

Sort 函数的形参是一个 interface,包含了三个方法:Len()Less(i,j int)Swap(i, j int)。使用的时候不管数组的元素类型是什么类型(int, float, string…),只要我们实现了这三个方法就可以使用 Sort 函数,这样就实现了“泛型编程”。

package main

import (
   "fmt"
   "sort"
)

type Person struct {
   Name string
   Age  int
}

func (p Person) String() string {
   return fmt.Sprintf("%s: %d岁\n", p.Name, p.Age)
}

type ByAge []Person

//以下三个函数实现自Interface接口
func (a ByAge) Len() int {
   return len(a)
}

func (a ByAge) Less(i, j int) bool {
   return a[i].Age < a[j].Age
}

func (a ByAge) Swap(i, j int) {
   a[i], a[j] = a[j], a[i]
}

func main() {
   people := []Person{
      {"Bob", 31},
      {"John", 42},
      {"Michael", 17},
      {"Jenny", 26},
   }

   fmt.Println(people)
   sort.Sort(ByAge(people))
   fmt.Println(people)
}

/*[Bob: 31岁
John: 42岁
Michael: 17岁
Jenny: 26岁
]
[Michael: 17岁
Jenny: 26岁
Bob: 31岁
John: 42岁
]*/

init方法

init函数的主要作用:

  • 初始化不能采用初始化表达式初始化的变量。
  • 程序运行前的注册。
  • 实现sync.Once功能。
  • 其他

init函数的主要特点:

  • init函数先于main函数自动执行,不能被其他函数调用;
  • init函数没有输入参数、返回值;
  • 每个包可以有多个init函数;
  • 包的每个源文件也可以有多个init函数,这点比较特殊;
  • 同一个包的init执行顺序,golang没有明确定义,编程时要注意程序不要依赖这个执行顺序。
  • 不同包的init函数按照包导入的依赖关系决定执行顺序。
package main                                                                                                                     

import (
   "fmt"              
)

var T int64 = a()

func init() {
   fmt.Println("init in main.go ")
}

func a() int64 {
   fmt.Println("calling a()")
   return 2
}
func main() {                  
   fmt.Println("calling main")     
}


/*输出:
calling a()
init in main.go
calling main

初始化顺序:变量初始化->init()->main() */


/*---------------------------------------------*/


//init函数比较特殊,可以在包里被多次定义。
package main
import "fmt"
func init() {
   fmt.Println("init 1")
}
func init() {
   fmt.Println("init 2")
}
func main() {
   fmt.Println("main")
}

/*输出:
init 1
init 2
main */
import _ "net/http/pprof"

golang对没有使用的导入包会编译报错,但是有时我们只想调用该包的init函数,不使用包导出的变量或者方法,这时就采用上面的导入方案。

执行上述导入后,init函数会启动一个异步协程采集该进程实例的资源占用情况,并以http服务接口方式提供给用户查询。

学习实践项目

https://github.com/HarborZeng/zhenai-crawler (珍爱网个人信息爬虫)

参考资料


   转载规则


《新秀语言go:Golang入门语法知识点总结》 Harbor Zeng 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
第一个静态博客告你日记 第一个静态博客告你日记
node环境搭建什么是Node.jsnode.js不是一个库,也不是一个新的语言,他是一个平台,一个基于JavaScript的运行容器。它被创造出来的目的就是使得浏览器里面才能运行的代码能在电脑,手机上随时运行。 假设这样一个例子:我们想在浏览器里面上传一个文件,JavaScript是不能读取磁盘上的文件的,我们必须通过<input type="file"/>这样
下一篇 
为什么是静态博客 为什么是静态博客
什么是静态博客所谓静态博客,即通过工具,直接将作者所书写的博文编译成最终的html,css,js等文件。作者/博主,只需将生成的文件部署在静态server上面即可被在互联网中访问。 而传统的数据库型博客对服务器的要求就比较大了 使用静态博客生成工具 用户通过浏览器访问服务器,直接就回获得已编译的静态资源。 静态博客的好处 因为用户在浏览器上访问博客时,只用从服务器(如nginx)拿来编译好
  目录