初試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的值 332.使用指針作為函數的參數
將基本數據類型的指針作為函數的參數,可以實現對傳入數據的修改,這是因為指針作為函數的參數只是賦值了一個指針,指針指向的內存沒有發生改變
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.如果擴容之後,還沒有觸及原數組的容量,則切片中的指針指向的還是原數組,如果擴容後超過了原數組的容量,則開闢一塊新的內存,把原來的值拷貝過來,這種情況絲毫不會影響到原數組。

#### 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章 異常處理deferdefer語句只能出現在函數或方法中 如果出現多個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