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, 数组超届
错误综合处理方法:
原文地址:https://www.cnblogs.com/wangchunli-blogs/p/9951245.html