超詳細go入門筆記

2021-03-02 51運維com

第1章 Go介紹

初試go

package main
import "fmt"
func main() {
fmt.Println("hello word")
}

第2章 Go基本語法2.1變量2.1.1. go語言中變量分為局部變量和全局變量

•局部變量,是定義在打括號{}內部的變量,打括號內部也是局部變量的作用域•全局變量,是定義在函數和打括號外部{}的變量

2.1.2. 變量聲明

批量聲明未初始化的變量

var {
a int
b string
c []float32
e struct {
x int
y string
}
}

初始化變量

var a int = 20 #標準聲明格式
var b = 30 #自動推斷類型格式
c := 40 #初始化聲明格式,首選

2.1.3.變量多重賦值

以簡單算法交換為例,傳統寫法如下

var a int = 10
var b int = 20
b,a = a,b

2.1.4.匿名變量

Go語言中的函數可以返回多個值,而事實上並不是所有返回值都用的上,那麼就可以用匿名變量 「_」 替換即可,匿名變量不佔用命名空間,也不會分配內存

func GetData()(int,int){
return 10,20
}
a,_ := GetData() //捨棄第二個返回值
_,b = GetData()//捨棄第一個返回值

2.2 數據類型2.3 列印格式化2.4 數據類型轉換

Go語言採用數據類型前置加括號的方式進行類型轉換,格式如:T(表達式)。T表示要轉換的類型

a := 10

b := string(a) //將int型轉換為string型
c := float32(a) //將int型轉換為float型

2.5 常量

相對於變量,常量是不變的值。常量是一個簡單的標識符,在程序運行時,不會被修改

格式如下:
const 標識符 [類型] = 值
const PAI string = "abc"

2.5.1 常量用於枚舉
const (
USERNAME = "geinihua"
PASSWORD = "geinihua"
NETWORK = "tcp"
SERVER = "10.247.22.146"
PORT = "3306"
DATABASE = "demo"
)
dsn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s",USERNAME,PASSWORD,NETWORK,SERVER,PORT,DATABASE)

常量組中如果不指定類型和初始值,則與上一行非空常量值相同

const (
a=10
b
c
)

fmt.PrintLn(a,b,c) //輸出結果10 10 10

2.5.2 iota枚舉

•iota常量自動生成器,每隔一行,自動加1•iota給常量賦值使用•iota遇到下個const,會重置為0•多個常量可以寫一個iota,在一個括號裡•多重賦值,在同一行,值一樣

3. 流程控制

3.1 if 條件判斷語句

func max(num1, num2 int) int {
/* 聲明局部變量 */
var result int

if num1 > num2 {
result = num1
} else {
result = num2
}
return result
}

3.2 switch 條件選擇語句


grade := ""
score := 88.5

switch true {
case score >=90:
grade = "A"
case score >=80:
grade = "B"
case score >=70:
grade = "C"
default:
grade="E"
}

fmt.Printf("你的登記是: %s\n",grade )

3.3 for 循環語句

第一種寫法:
for i:=0;i<=20 ;i++ {
fmt.Printf("%d\n",i)
}
第二種寫法:
var i int
for i<=20 {
fmt.Printf("%d\n",i)
}
第三種寫法(for ...range):
str := "123ABCabc好"
for i,value := range str{
fmt.Printf("第 %d 位的ASCII值=%d,字符是%c\n",i,value,value)
}

4.Go語言的函數與指針4.1 函數
func(參數列表)(返回參數列表){
//函數體
}

4.1.3 函數變量

函數變量是把函數作為值保存到變量中. 在Golang中,,函數也是一種類型,可以和其他類型一樣被保存在變量中

type myFunc func(int) bool
func main(){

nums:=[]int{10,20,40,16,17,3030,49849,204394,43943,2923,23923,}
fmt.Println(filter(nums,isEven))
fmt.Println(filter(nums,isAdd))
}

func filter(arr []int, f myFunc) []int {
var result []int
for _, value := range arr {
if f(value) {
result = append(result, value)
}
}
return result
}
func isEven(num int) bool{
if num%2 == 0 {
return true
}else {
return false
}
}
func isAdd(num int) bool{
if num%2 == 0 {
return false
}
return true
}

4.1.4 匿名函數

匿名函數沒有函數名,只有函數體,可以作為一種類型賦值給變量。匿名函數經常被用於實現回調函數、閉包等

1.在定義匿名函數的時候就可以直接使用
res1 := func (n1 int, n2 int) int {
return n1 + n2
}(10, 30) //括號裡的10,30 就相當於參數列表,分別對應n1和n2

fmt.Println("res1=",res1)

2.將匿名函數賦給一個變量
res1 := func (n1 int, n2 int) int {
return n1 + n2
}
res2 := res1(50,50)
fmt.Println("res1=",res2)

3.匿名函數作為回調函數

func vist(list []float64,f func(float64)) {
for _,value:=range list{
f(value)
}
}
List := []float64{1,2,5,20,90}
vist(List, func(v float64) {
sqrt := math.Pow(v,2)
fmt.Println(sqrt)
})

4.1.5 閉包
//函數f返回了一個函數,返回的這個函數就是一個閉包。這個函數本身中沒有定義變量I的,而是引用了它所在的環境(函數f)中的變量i.
func f(i int) func() int {
return func() int{
i++
return i
}
}
a:=f(0)
fmt.Println(a()) //0
fmt.Println(a()) //1
fmt.Println(a()) //2
fmt.Println(a()) //3

4.1.6 可變參數
語法格式:
func 函數名(參數名...類型)(返回值列表){}

該語法格式定義了一個接受任何數目、任何類型參數的函數。這裡特殊語法是三個點"...",在一個變量後面加上三個點,表示從該處開始接受可變參數

func Tsum(nums ...int) {
fmt.Println(nums)
total:=0
for _,val := range nums{
total+=val
}
fmt.Println( total)
}

4.1.7 golang單元測試

要開始一個單元測試,需要準備一個 go 源碼文件,在命名文件時需要讓文件必須以_test結尾 單元測試源碼文件可以由多個測試用例組成,每個測試用例函數需要以Test為前綴,例如:

格式如下:
func TestXXX( t *testing.T )

func sum2(n1 int, args ...int) int {
sum := n1
for i := 0; i < len(args); i++ {
sum += args[i]
}
return sum
}
func TestAvaiableSum(t *testing.T) {
res := sum2(1, 23, 34, 56)
fmt.Println("res=", res)
}

4.2指針

指針式存儲另一個變量的內存地址的變量。變量是一種使用方便的佔位符。一個指針變量可以指向任何一個值的內存地址 在Go語言中使用地址符&來獲取變量的地址,一個變量前使用&會返回該變量的內存地址

total:=20
fmt.Println("total的內存地址",&total)

4.2.1 聲明指針

格式:var 指針變量 *指針類型 聲明指針,T是指針變量的類型,它指向T類型的值,號用於指定變量是一個指針

var ip *int //指向整型的指針
var fp *float32 //指向浮點型的指針

指針使用流程

1.定義指針變量 2.為指針變量賦值 3.訪問指針變量中指向地址的值 獲取指針變量指向的變量值:在指針類型的變量前加上號。如a

type Student struct {
name string
age int
sex int8
}

func TestZhiz(t *testing.T) {
s1:=Student{"steven",32,2}
s2:=Student{"Sunny",10,1}
var a *Student=&s1 //&s1的內存地址
var b *Student=&s2 //&s2的內存地址
fmt.Printf("s1類型為%T,值為%v\n",s1,s1)
fmt.Printf("s2類型為%T,值為%v\n",s2,s2)
fmt.Printf("a類型為%T,值為%v\n",a,a)
fmt.Printf("b類型為%T,值為%v\n",b,b)

fmt.Printf("s1的值等於a指針\n")
fmt.Printf("s2的值等於b指針\n")
fmt.Printf("*a類型為%T,值為%v\n",*a,*a)
fmt.Printf("*b類型為%T,值為%v\n",*b,*b)

}

• 空指針

if(ptr != nil) //ptr不是空指針
if(ptr == nil)//ptr是空指針

4.2.2 使用指針

1.通過指針修改變量的值

//指針修改變量的值
a2:=32
b2:=&a2
fmt.Println("a2的值",a2) //a2的值 32
fmt.Println("b2地址",b2) //b2地址 0xc4200142d8
fmt.Println("b2的值",*b2) //b2的值 32
*b2++
fmt.Println("b2的值",*b2) //b2的值 33

2.使用指針作為函數的參數

將基本數據類型的指針作為函數的參數,可以實現對傳入數據的修改,這是因為指針作為函數的參數只是賦值了一個指針,指針指向的內存沒有發生改變

func main(){
orgi:=68
ptr:=&orgi
change(ptr)
fmt.Println("執行函數後orgi的值",orgi) //執行函數後orgi的值 20
}
func change(p *int) {
*p=20
}

4.2.3 指針數組
//指針數組
//格式:var ptr [3]*string

ptrArr:=[COUNT]string{"abc","ABC","123","8888"}

i:=0
//定義指針數組
var ptrPoint [COUNT]*string
fmt.Printf("%T,%v \n",ptrPoint,ptrPoint) //[4]*string,[<nil> <nil> <nil> <nil>]

//將數組中的每個元素地址賦值給指針數組
for i=0;i<COUNT;i++ {
ptrPoint[i] = &ptrArr[i]
}

fmt.Printf("%T,%v \n",ptrPoint,ptrPoint) //[4]*string,[0xc42000e800 0xc42000e810 0xc42000e820 0xc42000e830]

//循環取指針數組中的值
for i=0;i<COUNT;i++ {
fmt.Printf("a[%d]=%v \n",i, *ptrPoint[i])
//a[0]=abc
//a[1]=ABC
//a[2]=123
//a[3]=8888
}

4.2.4 指針的指針

指向指針的指針變量聲明格式如下:


//指針的指針
var a2 int
var ptr2 *int
var pptr **int
a2=1234
ptr2=&a2
fmt.Println("ptr地址",ptr2)
pptr=&ptr
fmt.Println("pptr地址",pptr)

fmt.Printf("變量a2=%d\n",a2)
fmt.Printf("指針變量ptr2=%d\n",*ptr2)
fmt.Printf("指向指針的指針量pptr=%d\n",**pptr)
//輸出結果
/*
ptr地址 0xc4200d4140
pptr地址 0xc4200ec000
變量a2=1234
指針變量ptr2=1234
指向指針的指針量pptr=20
*/

4.3 函數的參數傳遞4.3.1 值傳遞(傳值)
值傳遞是指在調用函數時將實際參數複製一份傳遞到函數中,不會影響原內容數據

4.3.2 引用傳遞(傳引用)
1.引用傳遞是在調用函數時將實際參數的地址傳遞到函數中,那麼在函數中對參數所進行的修改將影響原內容數據
2.Go中可以藉助指針來實現引用傳遞。函數參數使用指針參數,傳參的時候其實是複製一份指針參數,也就是複製了一份變量地址
3.函數的參數如果是指針,當調用函數時,雖然參數是按複製傳遞的,但此時僅僅只是複製一個指針,也就是一個內存地址,這樣不會造成內存浪費、時間開銷

函數傳int類型的值與引用對比

package main

import "fmt"

func main() {

//函數傳int類型的值與引用對比
a:=200
fmt.Printf("變量a的內存地址%p,值為:%v\n",&a,a)
changeIntVal(a)
fmt.Printf("changeIntVal函數調用後變量a的內存地址%p,值為:%v\n",&a,a)
changeIntPtr(&a)
fmt.Printf("changeIntPtr函數調用後變量a的內存地址%p,值為:%v\n",&a,a)
/*
變量a的內存地址0xc420080008,值為:200
changeIntVal函數,傳遞的參數n的內存地址:0xc420080018,值為:200
changeIntVal函數調用後變量a的內存地址0xc420080008,值為:200
changeIntPtr函數,傳遞的參數n的內存地址:0xc42008a020,值為:0xc420080008
changeIntPtr函數調用後變量a的內存地址0xc420080008,值為:50
*/
}
func changeIntVal(n int) {
fmt.Printf("changeIntVal函數,傳遞的參數n的內存地址:%p,值為:%v\n",&n,n)
n=90
}
func changeIntPtr(n *int) {
fmt.Printf("changeIntPtr函數,傳遞的參數n的內存地址:%p,值為:%v\n",&n,n)
*n=50
}

函數傳slice類型的值與引用對比

import "fmt"
func main() {
//函數傳slice類型的值與引用對比
a:=[]int{1,2,3,4}
fmt.Printf("變量a的內存地址%p,值為:%v\n",&a,a)
changeSliceVal(a)
fmt.Printf("changeSliceVal函數調用後變量a的內存地址%p,值為:%v\n",&a,a)
changeSlicePtr(&a)
fmt.Printf("changeSlicePtr函數調用後變量a的內存地址%p,值為:%v\n",&a,a)
}
func changeSliceVal(n []int) {
fmt.Printf("changeSliceVal函數,傳遞的參數n的內存地址:%p,值為:%v\n",&n,n)
n[0]=90
}
func changeSlicePtr(n *[]int) {
fmt.Printf("changeSlicePtr函數,傳遞的參數n的內存地址:%p,值為:%v\n",&n,n)
(*n)[1]=50
}

函數傳結構體

package main
import "fmt"
type Teater struct {
name string
age int
}
func main() {
//函數傳結構體
a:=Teater{"xll",200}
fmt.Printf("變量a的內存地址%p,值為:%v\n",&a,a)
changeStructVal(a)
fmt.Printf("changeStructVal函數調用後變量a的內存地址%p,值為:%v\n",&a,a)
changeStructPtr(&a)
fmt.Printf("changeStructPtr函數調用後變量a的內存地址%p,值為:%v\n",&a,a)
}
func changeStructVal(n Teater) {
fmt.Printf("changeStructVal函數,傳遞的參數n的內存地址:%p,值為:%v\n",&n,n)
n.name="testtest"
}
func changeStructPtr(n *Teater) {
fmt.Printf("changeStructPtr函數,傳遞的參數n的內存地址:%p,值為:%v \n",&n,n)
(*n).name="ptrptr"
(*n).age=899
}

第5章 Go語言的內置容器5.1 數組

•數組語法

1.數組長度必須是整數且大於0,未初始化的數組不是nil,也就是說沒有空數組(與切片不同) 2.初始化數組格式如下:

var arr = [6]int{1,2,3,4,5,6}

初始化數組中{}中的元素個數不能大於[]中的數字 如果忽略[]的數字,不設置數組長度,Go默認會設置數組的長度。可以忽略聲明中數組的長度並將其替換為"..."。編譯器會自動計算長度,格式如下

var arr = [...]int{1,2,3,4,5,6}

3.修改數組內容,可用格式

數組元素可以通過索引位置來讀取,所以從0開始 4.通過函數len()獲取數組長度

var arr = [...]int{1,2,3,4,5,6}
fmt.Printf("數組長度%d \n",len(arr))

•數組遍歷

arr:=[...]int{1,2,3,4,5,6}
//第一種遍歷方式
for i:=0;i<len(arr);i++ {
fmt.Printf("元素值%v\n",arr[i])
}
fmt.Println("-")
//第二種遍歷方式
for index,value:=range arr{
fmt.Printf("第 %d個元素值%v\n",index,value)
}

•多維數組 二維數聲明格式如下:

二維數組可以使用循環嵌套獲取元素

func main() { arr:=[5][2]int{ {0,0}, {1,2}, {2,4},         {3,6}, {4,8}, } for i1,v:=range arr{ for i2,v2:=range v { fmt.Printf("arr[%d][%d]=%d\n",i1,i2,v2) } }

}


* 數組是值類型
> Go中數組並非引用類型,而是值類型。當被分配給一個新變量時,會將原始數組複製出一份給新變量,因此對新變量進行更改,原始數組不會有影響

#### 5.2 切片
* 切片概念
1.Go中提供了一種內置類型"切片",彌補了數組長度不變的缺陷。
2.切片是可變長度序列,序列中每個元素都是相同類型
3.從底層看,切片引用了數組對象。切片可以追加元素,追加元素時容量增大,與數組相比切片不需要設定長度
4.切片數據結構可以理解為一個結構體,包含三個要素。
指針,指向數組中切片指定的開始位置
長度,即切片長度
容量,也就是**切片開始位置**到數組最後位置的長度

* 切片語法
1.聲明切片
```go
var identifier []type
切片不需要說明長度,該切片默認為nil,長度為0

使用make()函數創建切片,格式如下:

var sliceVar []type = make([]type,len,cap)
簡寫:sliceVar:=make([]type,len,cap)//cap容量

sliceVar:=make([]int,3,5)
fmt.Printf("len=%d cap=%d slice=%v\n",len(sliceVar),cap(sliceVar),sliceVar) //len=3 cap=5 slice=[0 0 0]

2.初始化切片 (1)直接初始化切片

(2)通過數組截取來初始化切片

arr:=[...]int{1,2,3,4,5}
s1:=arr[:]

切片中包含數組中所有元素

s:=arr[startIndex:endIndex]

將arr中從下標startIndex到endIndex-1下的元素創建為一個新的切片(前閉後開),長度是endIndex-startIndex 預設endIndex時表示一直到arr的最後一個元素

s:=arr[startIndex:endIndex]

預設startIndex時表示從arr的第一個元素開始

s:=arr[startIndex:endIndex]

func main()  {
arr:=[]int{0,1,2,3,4,5,6,7,8}
printSlice(arr[:6]) //0 1 2 3 4 5 不包含6元素
printSlice(arr[3:]) //3 4 5 6 7 8 從索引3開始到末尾
printSlice(arr[2:5]) //2 3 4 從索引2開始(包含)到索引5(不包含)結束
}
func printSlice(n []int) {
fmt.Printf("len=%d cap=%d slice=%v\n",len(n),cap(n),n)
}

(3) 深入理解切片的容量,我們可以把容量當做成總長度減去左指針走過的元素值,比如

s[:0] ——> cap = 6 - 0 =6;

s[2:] ——> cap = 6 - 2 = 4。

•append()和copy()函數

由此可知,容量隨著底層數組長度的變化而不斷變化,如果底層數組長度為4,在添加了一個元素後變成5,則容量變為 42=8,如果len=12,cap=12,如果追加一個元素後,那麼新的cap=27=14。

切片是引用類型 切片沒有自己的任何數據,它僅是底層數組的一個引用,對切片的任何修改都將影響底層數組的數據,數組是值類型,切片是引用類型

切片的原始碼學習 可參考博客 https://www.cnblogs.com/xull0651/p/14067809.html

func growslice(et *_type, old slice, cap int) slice {

newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < cap {
newcap += newcap / 4
}
// Set newcap to the requested cap when
// the newcap calculation overflowed.
if newcap <= 0 {
newcap = cap
}
}
}
return slice{p, old.len, newcap}
}

從上面的源碼,在對 slice 進行 append 等操作時,可能會造成 slice 的自動擴容。其擴容時的大小增長規則是:a.如果切片的容量小於 1024,則擴容時其容量大小乘以2;一旦容量大小超過 1024,則增長因子變成 1.25,即每次增加原來容量的四分之一。b.如果擴容之後,還沒有觸及原數組的容量,則切片中的指針指向的還是原數組,如果擴容後超過了原數組的容量,則開闢一塊新的內存,把原來的值拷貝過來,這種情況絲毫不會影響到原數組。

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5a0ca32a0c934898ba8ed26ccce28b9c~tplv-k3u1fbpfcp-zoom-1.image)
#### 5.3 map
* 5.3.1 map概念
Map 是一種無序的鍵值對的集合。Map 最重要的一點是通過 key 來快速檢索數據,key 類似於索引,指向數據的值。
Map 是一種集合,所以我們可以像迭代數組和切片那樣迭代它。不過,Map 是無序的,我們無法決定它的返回順序,這是因為 Map 是使用 hash 表來實現的
Map是hash表的一個引用,類型寫為:map[key]value,其中的key, value分別對應一種數據類型,如:map[string]string
要求所有的key的數據類型相同,所有value數據類型相同(註:key與value可以有不同的數據類型)
* 5.3.2 map語法
聲明map
```go
第一種方法
mapVar := map[key類型]value類型
第二種方法
mapVar := make(map[key類型]value類型)

map初始化和遍歷

    mapVar:=map[string]string{
"a":"t1",
"b":"t2",
"c":"t3",
}
//遍歷map
for key, value := range mapVar {
fmt.Printf("key=%v value=%v\n",key,value)
}

//查看元素在集合中是否存在
if value,ok:=mapVar["aa"];ok {
fmt.Println("存在value",value)
}else {
fmt.Println("不存在value")

}

•5.3.3 map是引用類型

第6章 Go常用內置包

可參考官網[1]

字符串遍歷

str:="strings包:遍歷帶有中文的字符串"
for _, value := range []rune(str) {
fmt.Printf("%c\n",value)
}

json序列化和反序列化

package main
import (
"encoding/json"
"fmt"
)
type Stu struct {
Name string `json:"name"`
Age int
HIgh bool
sex string
Class *Class `json:"class"`
}
type Class struct {
Name string
Grade int
}
func main() {
//實例化一個數據結構,用於生成json字符串
cla := new(Class)
cla.Name = "1班"
cla.Grade = 3
stu := Stu{
"張三",
18,
true,
"男",
cla,//指針變量
}
//Marshal失敗時err!=nil
jsonStu, err := json.Marshal(stu)
if err != nil {
fmt.Println("生成json字符串錯誤")
}
//jsonStu是[]byte類型,轉化成string類型便於查看
fmt.Println(string(jsonStu))

//反序列化操作Unmarshal()
var per_data Stu
err2 := json.Unmarshal([]byte(jsonStu),&per_data)
if err2 != nil {
fmt.Printf("反序列化錯誤:%v\n", err)
}
fmt.Printf("personal_json反序列化=%v\n", per_data)
fmt.Printf("per_data=%v\n", *per_data.Class)
}

第7 章Go面向對象結構體

•匿名結構體 和結構體匿名欄位 匿名結構體就是沒有名字的結構體,無須通過type關鍵字定義就可以直接使用。創建匿名結構體的時候,同時也要創建結構體對象

  //匿名結構體
addr:=struct{
name string
age int
}{"slaiven",39}
fmt.Println(addr)

•*匿名欄位就是在結構體中的欄位沒有名字,只包含一個沒有欄位名的類型**•*如果欄位沒有名字,那麼默認使用類型作為欄位名,同一類型只能有一個匿名欄位**

//匿名欄位
user:=new(User)
user.string="apce"
user.int=84
fmt.Printf("名字%v,年齡%v",user.string,user.int) //名字apce,年齡84

•結構體嵌套 將一個結構當作另一結構體的欄位(屬性),這種就是結構體嵌套,可以模擬以下兩種關係.•*聚合關係:一個類作為另一個類的屬性,一定要採用有名字的結構體作為欄位**•*繼承關係:一個類作為另一個類的子類。子類與父類的關係。採用匿名欄位的形式,匿名欄位就該結構體的父類**

//聚合關係:一個類作為另一個類的屬性
type Address struct {
province,city string
}
type Person struct {
name string
age int
address *Address
}

func TestMoudelStrings(t *testing.T) { //實例化Address結構體 addr:=Address{} addr.province="北京市" addr.city="豐臺區" //實例化Person結構體 p:=Person{} p.name="Strven" p.age=28 p.address=&addr fmt.Println("姓名:",p.name,"年齡:",p.age,"省:",p.address.province,"市:",p.address.city) //如果修改了Person對象的address數據,那麼對Address對象會有影響麼?肯定的 p.address.city="大興區" fmt.Println("姓名:",p.name,"年齡:",p.age,"省:",p.address.province,"市:",addr.city) //修改Address對象,是否會影響Persion對象數據?肯定的 addr.city="朝陽區"     fmt.Println("姓名:",p.name,"年齡:",p.age,"省:",p.address.province,"市:",addr.city) } //繼承關係:一個類作為另一個類的子類。子類與父類的關係

type Address struct { province,city string } type Person struct { name string age int Address //匿名欄位,Address是Person的父類 } func TestMoudelStrings(t *testing.T) { //實例化Address結構體 addr:=Address{} addr.province="北京" addr.city="豐臺區" //實例化Person結構體 p:=Person{"strven",38,addr} fmt.Printf("姓名:%v 年齡:%v 省:%v 市:%v\n",p.name,p.age,p.Address.province,p.Address.city) //姓名:strven 年齡:38 省:北京 市:豐臺區 }


#### 方法

* Go中同時有函數和方法,方法的本質是函數,但是與函數又不同
1.含義不同,函數是一段具有獨立功能的代碼,可以被反覆多次調用,而方法是一個類的行為功能,只有該類的對象才能調用
2.方法有接受者而函數沒有,Go語言的方法是一種作用域特定類型變量的函數,這種類型變量叫作接受者(receiver),接受者的概念類似於傳統面向對象中的this或self關鍵字
3.方法可以重名(接受者不同),而函數不能重名,
```go
type Per struct {
name string
age int
}
func ( p Per ) getData() {
fmt.Printf("名字:%v 年齡:%v",p.name,p.age) //名字:aaa 年齡:39
}
func TestMethod(t *testing.T) {
p1:=Per{"aaa",39}
p1.getData()
}

•方法繼承 方法是可以繼承的,如果匿名欄位實現了一個方法,那麼包含這個匿名欄位的struct也能調用該匿名欄位中的方法

type Human struct {
name, phone string
age int
}

type Stu struct { Human school string } type Employee struct { Human company string } func TestMethod(t *testing.T) { s1:=Stu{Human{"dav","1850103930",7}," 洛陽一中"} s1.SayHi() } func (h *Human) SayHi() { fmt.Printf("我是%s,%d歲,電話%s\n",h.name,h.age,h.phone) }

* 方法重寫
```go
type Human struct {
name, phone string
age int
}

type Stu struct {
Human
school string
}
type Employee struct {
Human
company string
}
func TestMethod(t *testing.T) {
s1:=Stu{Human{"dav","1850103930",7}, " 洛陽一中"}
s2:=Employee{Human{"dav","1850*****",17},"太空梭"}
s1.SayHi()

s2.SayHi()
}
func (h *Human) SayHi() {
fmt.Printf("我是%s,%d歲,電話%s\n",h.name,h.age,h.phone)
}
func (h *Stu) SayHi() {
fmt.Printf("我是%s,%d歲,電話%s,學校%s\n",h.name,h.age,h.phone,h.school)
}
func (h *Employee) SayHi() {
fmt.Printf("我是%s,%d歲,電話%s,工作%s\n",h.name,h.age,h.phone,h.company)
}

接口

•接口定義與實現 Go當中的接口和java中接口類似,接口中定義對象的行為。接口指定對象應該做什麼,實現這種行為方式(實現細節)由對象來決定

type implUser interface {
run()
call()
}
type Ameriacan struct {

} func (a Ameriacan) run() { fmt.Printf("美國人跑了\n") } func (a Ameriacan) call() { fmt.Printf("美國人call()方法" + "\n") } func TestOver(t *testing.T) {

var user implUser
user=new(Ameriacan)
user.run()
user.call()

}


* 接口對象轉型(類型斷言)
類似於java中的向上轉型和向下轉型
```go
//接口對象轉型(類型斷言)
type Compute interface {
perimeter() float64 //實現2個類型
}
type Rectangle struct {
a,b float64
}
type Triangle struct {
a,b,c float64
}

//定義2個類型的方法,
func (r Rectangle)perimeter() float64 {

return r.a*r.b
}
func (r Triangle)perimeter() float64 {

return r.a*r.b*r.c
}

//接口斷言
func getType(c Compute) {
if instance,ok:=c.(Rectangle);ok{
fmt.Printf("矩形長度:%0.2f,%0.2f\n",instance.a,instance.b)

} else if instance,ok:=c.(Triangle);ok {
fmt.Printf("三角形形長度:%0.2f,%0.2f,%0.2f\n",instance.a,instance.b,instance.c)
}
}
//定義返回結果,
func getResult(s Compute) {

fmt.Printf("結果是%.2f \n",s.perimeter())
}
func main() {

var c Compute
c = Rectangle{2.0,3.0}
getType(c)
getResult(c)
}

第8章 異常處理defer

defer語句只能出現在函數或方法中 如果出現多個defer語句的話,當函數執行到最後時,這些defer語句會按照逆序執行,最後該函數返回

func main(){
defer funcA()
funcB()
defer funcC()
fmt.Println("main執行")
/* 輸出結果
funcB執行
main執行
funcC執行
funcA執行
*/
}
func funcA() {
fmt.Println("funcA執行")
}
func funcB() {
fmt.Println("funcB執行")
}
func funcC() {
fmt.Println("funcC執行")
}

defer在方法中使用

type deferPerson struct {
name string
age int
}
func main(){
P:=deferPerson{"t0",38}
defer P.getName()
fmt.Println("Welcome")
/*輸出結果
Welcome
name:t0
*/
}
func (d deferPerson) getName() {
fmt.Printf("name:%v\n",d.name)
}

堆棧的延遲,當一個函數有多個延遲調用時,他們被添加到一個堆棧中,並按 "後進先出"的順序執行


func main() {
str:="One Pice 歡迎大家"
fmt.Printf("原始字符串:\n %s\n",str)
fmt.Println("翻轉後字符串:")
ReverseString(str)
/*
原始字符串:
One Pice 歡迎大家
翻轉後字符串:
家大迎歡 eciP enO
*/
}

func ReverseString(str string) {
for _, value := range [] rune(str) {
defer fmt.Printf("%c",value)
}
}

panic和recover機制

•panic讓當前的程序進入恐慌,終端程序的執行,中斷原有的流程控制•recover()讓程序恢復,可以攔截panic的錯誤,必須在defer函數中執行才有效,正常程序運行中,調用revover會返回nil

func main()  {
catch(2,8)
}
func catch(nums ...int)int {
defer func() {
if r := recover();r!=nil{
log.Println("[E]",r)
}
}()
return nums[1] * nums[2] * nums[3]
}

持續更新中

定期同步:個人網站[2]

更多精彩關注公眾號「51運維com」

References

[1] 可參考官網: https://golang.org/pkg/
[2] 個人網站: http://h5.xuliliang.com

相關焦點

  • 《Go語言實戰》筆記(二) | Go開發工具
    《Go 語言實戰》一書是Go語言領域極具聲望的技術展專家力作,以下為本書讀者精彩的讀書筆記。在Go語言中,我們很多操作都是通過go命令進行的,比如我們要執行go文件的編譯,就需要使用go build命令,除了build命令之外,還有很多常用的命令,這一次我們就統一進行介紹,對常用命令有一個了解,這樣我們就可以更容易地開發我們的Go程序了。
  • 從零開始學化妝超詳細化妝入門教程
    從零開始學化妝超詳細化妝入門教程,零基礎學化妝,如果你還不會化妝,那你就Out了。化妝並不是取悅別人的工具,而是美化自己的法寶。接下來上海薈藝化妝學校就來為大家分享超詳細化妝入門課程化妝前必須先要把臉部清潔乾淨,之後擦上保養品,如果是白天,最後再上防曬品。
  • 英語入門對話185|For here or to go?
    Eating here or to go? 內用還是外帶?For here or to go? 內用還是外帶?It is a takeout restaurant. 這是外帶餐廳。You can get free refills for your coke. 您的可樂可以免費續杯。
  • 《365天子彈筆記》:一本對小白很友好的子彈筆記入門書
    比如在學習「子彈筆記」這種高效記錄方法的時候,《365天子彈筆記》這本書就是一個良好的開端。「子彈筆記」是美國設計師賴得·卡羅爾總結設計的一套筆記管理體系。它是一個將時間、精力和情緒,進行可視化的管理工具。
  • 篆書入門必看!超詳細的字形結構分析
    小編嘔心瀝血給喜愛篆書的朋友們整理了篆書入門字形結構分析圖,幫助大家在練習篆書的時候更快入門!>(四)附立結構;(五)天覆結構;(六)地載結構;(七)排疊結構:分排和疊;(八)包裹結構:分上包下、右包左、下包上、左包右、四包圍;下面通過圖片詳細講解
  • [GO語言基礎] 一.為什麼我要學習Golang以及GO語言入門普及
    這系列文章入門部分將參考「尚矽谷」韓順平老師的視頻和書籍《GO高級編程》,詳見參考文獻,並結合作者多年的編程經驗進行學習和豐富,且看且珍惜吧!後續會結合網絡安全進行GO語言實戰深入,加油~這些年我學過各種程式語言,從最早的C語言到C++,再到VB、C#、PHP、JAVA,再到IOS開發、Python,到最新的GO語言,學得是真的雜。
  • 入門教程:花 5 分鐘學習 Go 語言
    main.go 文件,輸入如下代碼,並將文件保存在上面創建的 hello 目錄下。使用之前必須使用 make() 函數初始化:counts := make(map[string]int)counts["Apples"] = 7關於數組、slice 和 map 更詳細的用法可以查看文末推薦閱讀掌握。
  • GO語言入門(第一個go程序)
    本文節選自《go入門指南》GO語言介紹指導設計原則
  • 英語特級教師整理:完型填空筆記,超詳細,為孩子列印收藏!
    因此,為了幫助小同學們更好訓練提升,小編老師給同學們分享一份【完型填空】超詳細筆記,建議家長給孩子列印下來練習!
  • Github星標超3k的推薦系統入門資料合集
    關注下方公眾號回復【RSPapers】打包獲取超300篇論文PDF合集。後臺回復關鍵字【綜述】,即可獲得推薦系統領域涉及全場景的20多篇綜述文章,比如協同過濾綜述、社會化推薦綜述、可解釋性綜述等,幫你快速輕鬆入門,快速搭建知識大廈,快速走向人生巔峰。
  • GitHub開源中文版《Go入門指南》學習教程
    Go 適合寫工具,比如 hugo 、hub、fzf,還有國人寫的 linux 下的百度 pan client 都是 go 實現的。3. Go 適合實現 C/C++ 一部分業務,Java 的大部分業務。4.
  • 超詳細購機攻略,22款相機推薦,從入門到專業
    超詳細購機攻略,22款相機推薦,從入門到專業 很多新手攝影師過去一開始都會對買什麼相機困惑,我整理了22款高性價比相機推薦給大家! 1、預算3000-4000 (1)佳能1500(參考價:2899) 優點:佳能入門機器,性價比高,適合入門 缺點:非觸屏,使用感一般 (2)尼康D3500(參考價:2999
  • Go語言(Golang)環境搭建詳解
    最近寫了很多Go語言的原創文章,其中Go語言實戰系列30篇,近15W字,還有最近更新的Go經典庫系列,不過通過大家的諮詢來看,還是想要一些入門的知識,這一篇文章寫於2017年初,這裡再更新一下,發給大家。
  • Github免費中文書《Go入門指南》,帶你從零學Go
    作者 | 無聞整理 | Jane出品 | AI科技大本營(ID:rgznai100)《Go 入門指南》是英文書《The Way to Go》的中文翻譯版,目前主要由志願者「無聞」在進行翻譯,並以開源的形式免費分享給有需要的 Go 語言愛好者。
  • Go 快速入門篇(三):單元測試、問題定位及代碼調試
    單元測試文件默認以同一目錄下文件名後綴 _test 作為標識,比如我們在 simplemath 目錄下新建 add_test.go 和 sqrt_test.go 文件,分別為 add.go 和 sqrt.go 編寫單元測試,對應的目錄結構如下:
  • WGCNA新手入門筆記(含代碼和數據)
    讓大家能夠入門WGCNA進行實操是我整理這一學習筆記的最終目的。筆記內容涉及到WGCNA的簡介,安裝運行,代碼解析和靈活變換,跑出的圖有什麼意義等,準備分3-4次說。WGCNA能夠從複雜數據中(N多分組)快速地提取出與樣本特徵相關的基因共表達模塊,以供後續分析。
  • 【鉤針愛好者必備】超詳細的鉤針符號圖解大全
    超詳細的鉤針符號圖解大全   DIY吧   今天萌媽終於找到一本超詳細的電子書   鉤針的基礎知識——鉤針編織類圖解的看法   裡面包含各種鉤針的針法圖解   從新手入門到進階都介紹的非常詳細   除了平時常用的辮子針、短針、
  • 終極Go 指南、全文搜尋引擎 MeiliSearch、酷炫 UI 框架 arwes...
    本周特推1.1 終極 Go 指南:ultimate-go本周 star 增長數:1300+ultimate-go 是作者學習 Ardan Labs 的終極 Go 課程中學習 Go 程式語言時的筆記匯總,作者通過實例來學習
  • 《方格筆記零基礎入門課》10月20日思學老師免費公開課
    你的筆記是否記了就沒再打開看過,或者等你再打開看的時候,發現再也看不懂了?!或許你會默默的在心裡說:「是!」,你的筆記是上圖哪一種?如果是第一種,你是否想過,這是一種阻礙你進步,阻礙你成功的筆記方式?或許你已經發現了,可是卻不知道用什麼樣的方式記筆記更好,我們壓根兒沒有學過如何記筆記,學校似乎也沒有教過我們如何有效的記筆記!今天,讓我們來了解能讓人頭腦越來越聰明,甚至能幫助你逆襲人生的筆記方式——方格筆記!
  • 新生葵花寶典學業篇丨筆記記錄&無紙化學習之入門
    如果讀完上面那麼長的軟體入門,你也許已經對無紙化筆記軟體的強大功能有了初步的印象,那麼,如何運用好這些功能,合理記錄老師上課的重點呢?這裡簡單分享一些tips:1.課上怎麼記?!有一些神仙老師(比如大一心理學導論的劉豔老師)會在上課之前將課程的提綱發給大家,上面清楚地列舉了課程的主題、小標題,甚至講解的詳略程度都可直接看出,這對於課堂筆記記錄是非常有用的。什麼地方需要詳細地記錄?什麼地方只大概聽懂,無需動筆?在課前就可以做好預判。