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, 数组超届
错误综合处理方法: