Go语言学习笔记

目录

下载与资源

官网 |
中文站 |
liteide

安装和配置

sudo tar -xzf go1.12.7.linux-amd64.tar.gz -C /usr/local

sudo vim /etc/profile

export GOPATH=/home/jiangzhibin/golang
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin

source /etc/profile 使

//gopath
echo $GOPATH 

gppm

快速启动goland的方法

1. ,goland
sudo ln -s /home/jiangzhibin/soft/goland/bin/goland.sh /usr/bin/goland

2. 
    : gnome-session-properties 
   : goland

liteide

/home/jiangzhibin/soft/liteidex37.4.linux64-qt5.5.1/liteide/bin/liteide
sudo ln -s /home/jiangzhibin/soft/liteidex37.4.linux64-qt5.5.1/liteide/bin/liteide /usr/bin/liteide

基础

//导入主函数的包
package main

import "fmt"

// 单行注释

/*
块注释
 */

func main()  {
    //程序有且只有一个主入口
    fmt.Println("hello world go!")
    fmt.Println("tiantian!")

}

工程管理

src工作目录 main包 主函数
函数是全局的,还有全局变量,都可全局使用。

不同级目录,包名用目录名,函数首字母大写。
主函数如果调用不同级目录中的函数要导入这个包:

如下图所示:
src.jpg

import "userinfo"
func main(){
    userinfo.Login()
}

pkg

bin

变量

//内存地址的别名
var   
var   
 :=    //自动推导类型

var a int = 10
var r float64 = 2.5

//自动推导类型, 注意只能在函数内使用
//会自动为变量选择类型
r := 3.256
a,b,c,d := 10,20,30,60

//匿名变量 _ ,占位但不接受值
a,_ := 1,20



// * 指针运算符,取出地址中的值
var a int = 10
p := &a
*p  //p是指针变量

常量const

常量的值不允许修改,一般大写字母表示,存储在数据区,变量存储在栈区。
常量不能用&来引用

const A int = 20

//字面常量,程序中的硬编码常量 
  

类型转换

var a int = 10
var b float64 = 2.44
c := float64(a) * b

iota枚举

常量声明可以使用常量生成器初始化。

const (
    a = iota
    b = iota
    c = iota
)

输出格式

fmt.Println("hello world")   //输出并换行

a := 10
b := "zhansan"
fmt.Printf("数据是:%d  ,名字是:%s ", a, b) //格式化输出
%d   %f   %t    %s    %c    %p   \n 

接收输入

var a int
fmt.Scan(&a)

fmt.Scanf("%d%s", &a,&b)  //格式化输入

枚举iota

声明一组相似的常量,值会递增

const (
    a = iota  //0
    b = iota  //1
    c = iota  //2
)

字符串处理

import (
    "fmt"
    "strings"
)

func main() {
    str := "hello world"
    res := strings.Contains(str, "he")
    fmt.Println(res)
}

if条件

var a = 10
var b = 20
if a < b {
    fmt.Println("true!")
}

if a < b {

}else if a = b{

}else {

}

switch分支

var a int
fmt.Scan(&a)
switch a {
case 1:
    fmt.Println("monday")
case 2:
    fmt.Println("tuesday")
}

for循环

var a int
for a = 1; a < 20; a++ {
    fmt.Println("tongtong")
}
//break 跳出本层循环
//continue 结束本次循环,继续下次循环
goto

函数的不定参数

可以接受多个参数

func test(args ...int){
    fmt.Printf(args)
}
test(1,2,3)

len()len(args)

函数类型

其实是一个指针,在代码区存储

函数是全局的,可以被项目中的所有文件调用,所以函数名是唯一的。

type funcdemo func()
type functest func(int int)    
type functt func(int int)int   /

函数返回值

//单个返回值
func add(a int, b int) int {
    return a + b
}

//多个返回值
func add(a int, b int) (int, int) {
    return a + b, a - b
}

匿名函数

func() {
    fmt.Println("hello")
}()

闭包

函数的返回值是一个函数,实现函数在栈区的本地化。

func test2() func() int{
    var a int
    return func() int{
        a++
        return a
    }
}

递归函数

本身调用自己的函数

func test3(){
    if i < 3{
        test3()
    }
}

数组

var  []
var arr1 [10]int

遍历数组

for range 可以遍历集合、数组、切片、map中的数据, for的另一种写法
for i, data = range array1

for _,data = range arr{
    sum += data
}

切片

一般不直接使用数组,使用切片slice
不写元素个数是切片,必须写元素个数的是数组。
切片是视图类型view

arr := [...]int{0, 1, 3, 10, 52, 30, 3}
s := arr[2:6]

var s []int = []int{1,2,3,4}
s = append(s,5,6)  //追加数据
slice := s[1:]  //切片取值,从下标是1开始到结尾


s := make([]int, 5)
s[0] = 1

map

字典结构类型,键值对存储:key->value

var m map[int]string
m := make(map[string]int)

m := make(map[int]string)
m[12] = "hello"
m[1] = "wowu"

m := map[int]string{
    1:"zhangsan",
    2:"lisi",
    3:"wangwu",
}

m := map[string]string {
    "name": "zhangsan",
    "site": "baidu.com"
}

// for... range遍历
for k, v := range m {
    fmt.PrintLn(k, v)
}

//删除元素
delete(m, "name")

结构体

go仅支持封装,没有class, 不支持继承和多态
类似于类的概念,于函数外定义,全局作用域

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

type Student struct {
    id   int
    name string
    sex  string
    age  int
    addr string
}

var s Student
s.id = 1
s.name = "zhangsan"
s.age = 20

//或者这样定义
var s Student = Student{id: 1, name: "lisi", sex: "male", age: 20}
s := Student{id:1, name:"zhangsan", age:18}

//结构体数组
var arr [5]Student
arr[0].id = 1
arr[0].name = "hello"

arr[1] = Student{id: 1, name: "lisi", sex: "male", age: 20}

//
type treeNode struct {
    value int
    left, right *treeNode
}
func main(){
    var root treeNode
    root = treeNode{value: 3}
    root.left = &treeNode{}
    root.right = {5, nil, nil}
    root.right.left = new(treeNode)
}

指针变量

基本类型前加 * ,可以通过指针访问变量和赋值
指针不能运算, go只有值传递
值传递就是直接拷贝数值, 引用传递是地址拷贝

var a = 10
var p *int  //空指针
//p = oxf589 野指针, 指针定义后不能直接赋值,直接赋值为野指针
p = &a
*p  /  == a

//指针空间 在堆区创建空间
var p *int
p = new(int)

//两个变量互换值
//以下的方法不会改变
func swap(a, b int){
    a, b = b, a  //结果不会改变
}

//需要使用指针的办法
func swap(a, b *int){
    *a, *b = *b, *a 
}
//或者将数据返回
func swap(a, b int) (int, int){
    a, b = b, a
    return a, b
}

数组指针

可以直接使用指针操作数组元素

var arr [5]int = [5]int{1,2,3,4,5}
var p *[5]int
p = &arr

fmt.Printf("%T", p) // *[5]int

p[0] //arr[0] 1
len(p) //也可以获取数组的长度

切片指针

和数组指针类似,但有区别

var slice []int = []int{4, 12, 12, 6}
var p *[]int
p = &slice

(*p)[0] //slice[0] 4

匿名字段

实现继承操作,结构体嵌套结构体

type person struct {
    name string
    sex  string
    age  int
}
type Student struct {
    person
    id    int
    score int
}
func main() {
    var stu1 Student
    stu1.id = 1
    stu1.name = "zhangsan"
    fmt.Println(stu1)
}

//或者这样初始化
var stu1 Student = Student{person{"zhangsan", "male", 18}, 1, 60}

//如果有同名字段,则用子类(就近原则),或者使用父类名初始化。
stu1.person.name = "zhangsan"

方法

为对象绑定方法,和函数类似,但也有不同。

type Int int //为基本类型取别名,因为基本类型不能绑定方法 
//func (方法授受者) 方法名(参数列表) 返回值类型
func (a Int) add(b Int) Int {
    return a + b
}
func main() {
    var a Int = 10 //根据数据类型,绑定方法 
    value := a.add(15)
    fmt.Println(value)
}

//结构体绑定方法 
type Student struct {
    name string
    age  int
}
func (s Student) PrintInfo() {  //结构体绑定方法 PrintInfo
    fmt.Println(s.name)
}
func main() {
    var s Student = Student{"zhangsan", 18}
    s.PrintInfo()
}

接口

//接口方法只定义不实现
type Human interface {
    sayHello()
}

type Person struct {
    name string
    age  int
}

func (alice *Person) sayHello() {
    fmt.Println("hello world", alice.name)
}

func main() {
    var bob Person = Person{"alice", 18}
    var h Human = &bob
    h.sayHello()
}

接口继承

type Human interface {  //子集
    sayHello()
}

type Person interface {  //超集
    Human   //一组子集的集合
    dance()
}

空接口

空接口可以接受任意类型数据

var i interface {}
i = 10
i = "hello world"

多态

多态是将接口类型作为函数参数,多态实现了接口的统一处理

type Human interface {
    sayHello()
}

type Person struct {
    name string
    age  int
}

func (alice *Person) sayHello() {
    fmt.Println("hello world", alice.name)
}

func sayHello(h Human) {
    h.sayHello()
}

func main() {
    var bob Person = Person{"alice", 18}
    var h Human = &bob
    // h.sayHello()
    sayHello(h)
}

错误处理

errors panic recover

//可以从panic中获取控制权
defer func(){
    recover()
}() 

defer

延迟调用
defer func

文件

import (
    "fmt"
    "os"
)

fp, _ := os.Create("./test.txt")
fp.WriteString("hello golang!")
defer fp.Close()

并行和并发

并行:有两队同时使用两台咖啡机。 在同一时刻,有多个任务在多个处理器上同时进行。
并发:有两队同时轮流使用一台咖啡机。

goroutine

协程goroutime, 轻量级线程,
go关键字,开一个子协程,
主协程退出了,其它子协程也会退出,
非抢占式多任务处理,由协程主动交出任务权

package main

import (
    "fmt"
    "time"
)

func newTask() {
    for {
        fmt.Println("new task")
        time.Sleep(time.Second)
    }

}

func main() {
    go newTask()
    for {
        fmt.Println("hello main")
        time.Sleep(time.Second)
    }

}

//
func main() {
    for i := 0; i < 1000; i++ {
        go func(i int) {  //并发
            for {
                fmt.Printf("hello2\n", i)
            }
        }(i)
    }
    time.Sleep(time.Millisecond)
}

//让别的协程先执行
runtime.Gosched()

channel

make(chan type)   //无缓存的通道
make(chan type, capacity) //有缓存的通道
close(channel)  //关闭通道
eg:
channel <- value  //发送value到channnel
<- channel        //接受并弃用
x := <- channel   //从channnel接受并赋值给x
x, ok := <- channel //功能同上,同时检查通道是否关闭或为空

func chanDemo() {
    c := make(chan int)
    go func() {
        for {
            n := <-c
            fmt.Println(n)
        }
    }()

    c <- 1
    c <- 2
    time.Sleep(time.Millisecond)
}

//bufferedChannel 构建缓冲区
c := make(chan int, 3)
H2
H3
H4
3 columns
2 columns
1 column
Join the conversation now