go語言基本語法
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
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, 陣列超屆
錯誤綜合處理方法: