Go的第二课-基础开篇

转载请标明出处:

http://blog.csdn.net/qq_27495349/article/details/66974350
本文出自:【CF凌晨的博客】

1.结构分析

    首先我们先来看一看我们上一堂课写的hello world
package main

import "fmt"

func main() {
    fmt.Println("Hello World")
}

1.1 package (包申明)

    首先我们要了解一个概念,Go程序是通过packgae来组织的(类似java)package <pkgName> (例如我们上面例子中的第一行 package main)。
    这一行告诉我们当前文件属于哪个包,而包名main告诉我们它是一个可独立运行的包,它在编译后会产生可执行的文件。
    main包下面必须包含main()函数(没有返回值也没有参数),这就是程序的入口(相信大家会理解)。其它包都会生成*.a文件(也就是包文件)并放置在pkg下。
    每一个可独立运行的Go程序,必须包含main包和main()方法,这也没啥好说的,大家应该能理解。

1.2 import(导入)

    这个关键字是导入的意思,学习java的童鞋应该都懂,就是导入包(C中是include),可以看到我们导入了"fmt"这个包。
    我们在下面使用使用了fmt.Println("Hello World")打印了我们想要的东西(类似java System.out.println(**))。import还有其他的功能,我们依依来列出
1.2.1 . 用法
package main

import ."fmt"

func main() {
    Println("Hello World")
}
    可以看到,我们在包的前面加上了. 我们下面调用Println方法可以省略了fmt了。
    但是这种方法在后期导入的包多了就会导致混淆和加大代码阅读难道,因为你并不知道你到底调用了谁的方法,不推荐使用。
1.2.2 ( )用法
import (
    ."fmt"
    "net"
)
    可以看到,当我们导入更多的包的时候,我们可以使用()把需要导入的包写入下面。同时也支持.的用法。
1.2.3 _ 用法
    为了测试这个方法,我们首先建立一个Directroy,名字为test,书写下面代码
package test

import "fmt"

func init()  {
    fmt.Println("test init")
}
    我们在与main.go的文件同目录下创建了这个名为test的包,里面书写了一个init方法,下面我们在main.go文件中引用此包。
package main

import (
    . "fmt"
    _ "csdn_go/lesson_1/test"
)

func main() {
    Println("Hello World")
}
    可以看到我们这边使用是绝对路径,"csdn_go/lesson_1/test"相当于加载GOPATH/src/csdn_go/lesson_1/test模块。
    如果我们使用go get方法拉下来的包会直接在GOPATH/src/下。"fmt" 意思是在标准库中查找,也就是GOROOT下。
    我们现在来运行下代码,如下图

这里写图片描述

    可以看到,我们main函数中,并没有手动调用test包中的init方法,但是运行的时候打印了,所以这里可以看到_的作用。
    我们在"csdn_go/lesson_1/test"前面加上了_ 他就会自动调用init的方法(此方法规范跟main方法一样);
    如果没有init方法,运行时候也不会出错。
1.2.4 别名
package main

import (
    print "fmt"
    _ "csdn_go/lesson_1/test"
)

func main() {
    print.Println("Hello World")
}
    这个就不多解释了,撸了这么多年的代码应该很懂的。

1.3 func

    Go函数是不支持嵌套、重载和默认参数,函数可以作为一种类型来使用,也支持多返回值,匿名函数、闭包、不定长度变参。
    左大括号是不能另起一行(很严格),下面我来看看函数的定义规范
1.3.1 函数定义规范
func funcName(input1 type1,input2 type2)([output1] type1,[output2] type2) {
    //逻辑处理
    //返回多个数值
    return value1,value2
}
    左大括号是不能另起一行(很严格),[]表示返回值的变量名可以省略,下面我们来写个方法:
package main

import (
    "fmt"
)

func main() {
    fmt.Println(add)
    fmt.Println(add(5, 5))
    fmt.Println(change)
    fmt.Println(change(4, 5))
    a := add
    fmt.Println(a(5, 6))
}
/**
相加
 */
func add(x int, y int) (a int) {
    //这里的a可以省略
    return x + y
}
/**
交换
 */
func change(x int, y int) (int, int) {
    return y, x
}

这里写图片描述

    看上面的代码,我们可以了解,Go语言中的注释跟java类似 /** */ 多行注释 // 表示单行
    Go函数可以返回多个值的,并且也打印了函数的地址,那么可以得知函数也可以是一个类型
    这里我们也发现了每行代码后面不需要更上;这也是Go语言的好处,系统会自动帮你加上。
    下面我们再来看看其他写法
1.3.2 函数的其他简写与匿名函数
/**
可以申明多个同一类型的值
 */
func add1(x, y int) int {
    return x + y
}
/**
可以直接把返回值类型变量当成局部变量,并且已经是声明好的,那么我们可以直接return,因为go已经知道你要返回谁
 */
func change1(x, y int) (a, b int) {
    a, b = y, x;
    return
}
        上面的注释也写的很清楚,从此可以看出go语言很精简,写起来很爽,哈哈。
        下面我们再来看看匿名函数
    b := func(a, b int) int {
        return a + b
    }
    fmt.Println(b(5, 6))

    fmt.Println(func(a, b int) int {
        return a + b
    }(5, 6))
        很多人看这个可能看的头很疼,作为有C经验的人看这个其实很亲切,对于其他人来说,可能看起来很憋妞。
        其实上面第一个例子大家会很好理解,那下面的直接在大括号后面直接跟上了(5,6);
        其实我们完全可以把前面的一部分完全想象成b
        (这里大家可能看不到 b:=是什么意思,大家只需要知道这是申明,这个下篇文章会讲,这里给出所有的申明方法),把这个想象成b就很好理解了。
    var b func(int,int)int = func(a, b int) int {
        return a + b
    }
    fmt.Println(b(5, 6))

    var c=func(a, b int) int {
        return a + b
    }
    fmt.Println(c(5, 6))

    d:=func(a, b int) int {
        return a + b
    }
    fmt.Println(d(5, 6))
    这几段代码是一样的效果,具体的我们下篇文章会讲到。
1.3.3 函数的闭包
    下面我们重新来写个包用来测试闭包。
package closure

import "fmt"

func init() {
    f := closure(10);
    fmt.Println(f(11))
    fmt.Println(f(12))
}

/**
测试闭包
 */
func closure(a int) func(int) int {
    fmt.Printf("%p\n", &a)
    return func(b int) int {
        fmt.Printf("%p\n", &a)
        return a + b;
    }
}
    我们在main.go中调用它(_),运行结果如下

这里写图片描述

    可以发现我们打印a的地址全是一样的,也就是说,a不是值的拷贝,而是指针的拷贝,所以我们就得出了结论,Go的函数是支持闭包的
    (不了解闭包的可以自行google噢)
1.3.4 defer
    defer类似析构函数,在函数结束的时候按照调用顺序相反顺序逐个执行。即使函数发生严重的错误也会执行,支持匿名函数的调用;
    通常用于资源清理,文件关闭,解锁与记录时间等。
    下面我们来看个栗子
package testdefer

import "fmt"

func init() {
    defer fmt.Println("a")
    defer fmt.Println("b")
    defer fmt.Println("c")
}
    运行结果如下,正如我们所说的,会按照调用顺序相反顺序逐个执行(先进后出)

这里写图片描述
下面我们再来一个栗子

package testdefer

import "fmt"

func init() {
    /*defer fmt.Println("a")
    defer fmt.Println("b")
    defer fmt.Println("c")*/
    forDefer()
}

func forDefer() {
    for i := 0; i < 5; i++ {
        defer func() {
            fmt.Println(i)
        }()// () 表示执行 已经讲过了
    }
}
    大家猜猜结果会是多少呢?有没有人会说是这个答案呢(4,3,2,1,0),如果说了这个答案就相当于上面的闭包还没理解透,我们来看看运行结果

这里写图片描述

    因为闭包的原因,所以所有的defer方法定义结束之后,开始执行的时候,此时的i已经变成了5,然而闭包是指针的拷贝,所有打印出来的都是5。
    那如何才能输出(4,3,2,1,0)呢?这里也给出答案
func forDefer1() {
    for i := 0; i < 5; i++ {
        defer func(i int) {//参数传递是值的考呗
            fmt.Println(i)
        }(i)// () 表示执行 已经讲过了
    }
}

总结

    今天就讲到这里,可能大家也有些不明白,大家也不用担心,以后我们会慢慢的来,毕竟这篇文章只是一个基础开篇,给那些有基础的人了解。
    相应的代码也提交到了github上,下面会给出代码地址,项目是lesson_1,我们下次再见!!!

源码

https://github.com/CFlingchen/Go

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值