新秀语言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.js node.js不是一个库,也不是一个新的语言,他是一个平台,一个基于JavaScript的运行容器。它被创造出来的目的就是使得浏览器里面才能运行的代码能在电脑,手机上随时运行。 假设这样一个例子:我们想在浏览器里面上传一个文件,JavaScript是不能读取磁盘上的文件的,我们必须通过<input type="file"/&g
下一篇 
为什么是静态博客 为什么是静态博客
什么是静态博客 所谓静态博客,即通过工具,直接将作者所书写的博文编译成最终的html,css,js等文件。作者/博主,只需将生成的文件部署在静态server上面即可被在互联网中访问。 而传统的数据库型博客对服务器的要求就比较大了 使用静态博客生成工具 用户通过浏览器访问服务器,直接就回获得已编译的静态资源。 静态博客的好处 因为用户在浏览器上访问博客时,只用从服务器(如nginx)拿
  目录