前言:寫博客也寫了差不多一年了,我更多的時候是記錄自己學習的情況,有時也有自己工作上遇到的bug,自己有時候也比較迷茫,不知道怎么去寫博文,我也很想別人跟我提提建議,但是有時候覺得寫寫博客還是很有成就感的,我有時候覺得快速的把知識點過一遍然后進行大量的練習和補充每個知識點的不足,讓自己寫的博文能更加完善些(關於這個博文是一套視頻和兩本連起來總結的,所有有時候會感覺知識點有點混亂)
學習方向
區塊鏈研發工程師
Go服務器端/游戲軟件工程師
golang分布式/雲計算軟件工程師
學習方法
先know how
,在know why
Golang的語言特點
注意事項
以"go" 為擴展名
執行入口是main()函數
嚴格區分大小寫
每個語句后面不帶分號
定義的變量和import的包如果沒有使用到,代碼不能編譯通過
shift+tab整體向左移動(編輯器)
使用一個表達式形容Go語言: Go=C+Python
print()
println() //print+line
printf() //print+format
Sprintln()//獲取字符串
Sprint()
Sprintf()
%T , 打印變量的數據類型
%d , 打印整數,十進制
%f , 打印浮點,小數
%t , 打印字符串
%s , 打印字符串
%v , 原樣輸出
%c , 打印對應的unicode字符
%p , 打印地址
變量的定義
var 變量 數據類型
變量名=賦值
縮寫成一行
var 變量名 數據類型=賦值
類型推斷
var 變量名=賦值
省略var
變量: =賦值
b:=2 //簡短聲明不支持全局變量
在同一區域同一數據類型內不斷變化的
var num int //默認為0 跟js差不多 undefined
_
只寫,不能讀
常量
const 常量 數據類型=賦值
const 常量名=賦值
所有的字母大寫
如果私有 前加小寫字符c
一組常量中,如果某個常量沒有初始值,那么默認和上一行一致
iota
:計數器 默認為0const( e=iota f g ) //每當定義一個常量累加1
整型
int
uint
和操作系統有關
浮點
float32
單精度float64
雙精度
字符串
一個字符byte
Go語言字符使用UTF-8 英文字符1個字節 漢字3個字節
字符串一旦賦值了,就不能在修改
反引號是直接輸出``
布爾(bool)
只能取true false不能取0,1 占用一個字節
Go在不同類型的變量之間賦值是需要顯式轉換,也就是說Golang中數據類型不能自動轉換
var i int32 = 100
var n1 float32 = float32(i)
var n2 int8 = int8(i)
var n3 int64 = int64(i)
fmt.Print("i=%v n1=%v n2=%v n3=%v", i, n1, n2, n3)
%v 需要轉換的變量
基本數據類型 的默認值
基本類型轉string類型
fmt.Sprintf
會返回轉換后的字符串strconv.FormatFloat
strconv.FormatInt
strconv.formatbool
string類型轉基本數據類型
-
strconv ParseInt() ParseFloat() ParseBool() ParseUint() n1,_=strconv.ParseInt(str2,10,64) f1,_=strconv.ParseFloat(str3,64)
算術運算符
- 對於除號"/" 只保留整數部分而舍棄小數部分
Golang的自增自減只能當作一個獨立語言使用
例如
i++
i--
++和--只能寫在變量的后面,不能寫在變量的前面
go 語言明確不支持三元運算符
鍵盤輸入語句
fmt.Scanln()
fmt.Scanf()
fmt.scanf
可以按照指定的格式輸入fmt.Scanf("%s,%d,%f,%t",&name,&age,&sal,&ispass)
通過&變量保存數據
fmt.Scanln(&name)
if x>y {
fmt.Println("true")
}
if else if
/*希望函數內的變量能改變函數外的數值,可以傳入變量的地址&
函數內以指針的方式操作變量*/
func test03(n1 *int) {
*n1=*n1+10
fmt.Println("test03 n1=",*n1)
}
func main(){
num:=20
test03(&num)
fmt.Println("main() num",num)
}
go函數不支持重載
自定義數據類型
type 自定義數據類型 數據類型
type myInt int
var num1 myInt
var num2 int
num1=400
num2=int(num1) //這里依然需要顯示轉換,go認為myInt和int兩個類型
args
是slice
切片,通過args[index]
可以訪問各個值
func sum(n1 int,args... int)int{
sum:=n1
for i:=0;i<len(args);i++{
sum+=args[i]//args[0] 表示取出args切片的第一個元素之
}
return sum
}
func main(){
res:=sum(1,2,3,4,5,5,6,6)
fmt.Println("res=",res)
}
每一個源文件都可以 包含一個init函數,init會在main函數前調用
執行順序
bufio包
reader:=NewReader(os.Stuio)
reader.ReadLine()====>[]byte
reader.ReadString('\n')-->String
內置包
math包提供數學計算的函數
math.Abs()
go語言特有
fallthrough
穿透
當某個case匹配成功后執行,如果有fallthrough,那么后面緊鄰的case不再匹配,直接執行
//math/rand包下
//時間
t1:=time.Now().Unix()
fmt.Println(t1)
//設置種子數
rand.Seed(t1) //設置獲取隨機數的種子數
num1:=rand.Intn(10) //[0,10)
fmt.Println(num1)
數組
var arr [4]int
var b=[4]int{1,2,3,4}
f:=[...] int{1,2,3,4,5}
len() 長度
cap() 容量
//因為數組是定長的容器,長度和容量是相同的
range 對數組 取數組的下標和value
for index,value :=range arr{
fmt.Println("下標是",index,"數值是",value)
}
for _,v :=range arr{
fmt.Println(v)
}
二維數組
arr:=[4][3]int{{1,2,3},{1,2,3},{1,2,3},{1,2,3}}
切片
切片slice
同數組類似,也叫做變長數組
是一個引用類型的容器,指向了一個底層數據
因為切片是引用數據的數據,直接拷貝的是地址
淺拷貝:拷貝的數據地址 copy()
深拷貝: 拷貝的數據本身
s2:=[]int{1,2,3,4}
內置函數
make()
s1:=make([] 類型 ,len,cap) 第一個參數:切片的類型,第二個參數:len,第三個參數容量,如果省略,默認跟第二個一樣
----------------
s2:=make([] int,3)
s2=append(s2,1,2,3,4)
a[start:end] //包左不包右
當向切片添加數據時,如果沒有超過容量,直接添加,如果超過容量,自動擴容(成倍增加)
/* 數組傳遞的是數據
切片傳遞的是地址*/
s1:=[] int{1,2,3,4,5}
fmt.Println(s1)
s2:=s1
fmt.Println(s2)
s2[0]=100
fmt.Println(s1)
fmt.Println("--------")
s3:=[5] int{1,2,3,4,5}
fmt.Println(s3)
s4:=s3
fmt.Println(s4)
s4[0]=100
fmt.Println(s4)
fmt.Println(s3)
copy copy(s1,s2) 把s2拷貝到s1,但是不會改變長度,能裝多少裝多少
s1 := []int{1, 2, 3, 4, 5}
s2 := []int{6, 7, 8, 9}
copy(s1,s2)
fmt.Println(s1)
字符串操作
-
一個字節的切片
-
strings包下的字符串函數
strings.Contains() 是否包含指定的內容
strings.ContainsAny() 是否包含任意一個字符
Repeat 自己拼接自己count次
大小寫轉換 ToLower() ToUpper()
切割Split() SplitN()
Index IndexAnyLastIndex() LastIndexAny()
Replace(s,old,new,n)
Trim()
HasPerfix() 以xx前綴開頭
HasSuffix() 以xx后綴結尾
查找
EqualFold不區分大小寫的字符串比較
index 找不到返回-1
TrimSpace() 將字符串左邊兩邊的空格去掉
Trim("元字符串",指定要去除的) 將指定的字符串左右兩邊空格去掉(有區別哦)
...
-
字符串遍歷,同時處理有中文的問題
r:[]rune(str)
str:="hello被" r:=[]rune(str) for i:=0;i<len(r);i++{ fmt.Printf("%c\n",r[i]) }
-
字符串轉整數
n,err=strconv.Atoi("12")
b:="65" s2, _ :=strconv.Atoi(b) fmt.Printf("%v",s2)
-
整數轉字符串
str=strconv.Itoa(1234)
-
字符串 轉[]byte:
var bytes=[]byte("hello go")
-
[]byte轉字符串
str=string([]byte{'a','b','c'})
-
十進制轉2,8,16進制
str=strconv.FormatInt(123,2)
//2->8,16
map, 映射
key ,value 也是一個容器,存儲的無序鍵值對
key不重復,重復就覆蓋
map聲明是不會分配內存的,初始化需要make,分配內存后才能賦值和使用
類型
%T
m:=map[string]int{"one":1,"two":2} m:=make(map[string]int) nil 空 將map存進切片中 s1:=make([] map[string]string,0,3)
通過key獲取value,當key如果不存在的時候,我們會得到value的默認值
map1[key]=value,根據key獲取map中對應的value
value,ok:=map[key]
如果鍵值對存在,value就是對應的數據,ok為true如果key不存在獲取,獲取的是 值的默認值,ok為false,默認key為0
value,ok:=map[key] f ok{ fmt.Println("對應的數值是:",v1) }else{ fmt.Println("操作的key不存在",vl) }
內置函數delete
- delete(map1,key)
sort排序
sort.Ints(key) 字符按照字符串的編碼值排序
str="FKJKJFJdfjakfjakKSKJW" map1 := make(map[byte]int) for i := 0; i < len(str); i++ { val, ok := map1[str[i]] if ok { val++ } else { val = 1 } map1[str[i]] = val } keys := make([]byte, 0, len(map1)) for k := range map1 { keys = append(keys, k) } for _, k := range keys { fmt.Printf("%c,%d\n", k, map1[k]) }
map1:=make(map[string]map[string]string) //需要定義長度 map1["name"]=make(map[string]string,3) map1["liming"]=make(map[string]string,3) map1["name"]["name3"]="張三" map1["name"]["name1"]="李四" map1["name"]["name2"]="王五" map1["liming"]["xiaoming1"]="小明" map1["liming"]["xiaoming2"]="小紅" map1["liming"]["xiaoming3"]="小白" for k,v :=range map1{ fmt.Println(k,v) for k2,v2:=range v{ fmt.Println(k2,v2) } }
time
time.new 獲取當前時間
格式化事件
fmt.Printf
fmt.SPrintf
now:=time.Now() fmt.Printf("now=%v,now\n",now) fmt.Printf("%d-%d-%d %d:%d:%d\n",now.Year(),now.Month(),now.Day(),now.Hour(),now.Minute(),now.Second())
time.Format()
fmt.Printf(now.Format("2018-12-12 14:34:23))
Unix() 1970到現在的毫秒值
UnixNano 1970到現在的納秒值
內置函數
len
new
異常處理
//使用defer+recover 來捕獲異常 defer func() { err := recover() //recover()內置函數,可以捕獲到異常 if err != nil { //說明捕獲到錯誤 fmt.Println("err=", err) } }() errors.New("錯誤說明") 會返回一個err類型的值表示一個錯誤 //自定義錯誤 // 定義一個函數,用於求矩形的面積 func getArea(wid,len float64)(float64,error){ errorMsg:="" if wid<=0{ errorMsg="寬度為負數" } if len<0{ if errorMsg==""{ errorMsg="長度為負數" }else{ errorMsg+=",長度也為負數" } } if errorMsg !=""{ return 0,&errorRect{errorMsg,wid,len} } area:=wid*len return area,nil }
函數
go語言支持函數式編程+面向對象
- 函數:獨立功能,直接調用
- 方法:對象的功能,對象來調用
可變參數: 參數名...
函數中,可變參數想當於切片
位置參數>可變參數 (順序)
寫返回值的時候,必須在函數上聲明返回值的類型
函數式編程
一個函數可以作為另一個函數的參數或返回值
遞歸函數
自己調用自己
func getgui(n int) int{
if n==1||n==2{
return 1
}
return getgui(n-1)+getgui(n-2)
}
匿名函數
func(a,b int){
fmt.Println(a,b)
}(1,2)
高階函數(回調函數)
根據go語言函數的數據類型的特點,可以將函數作為另一個函數的參數
func add(a,b int) int{
return a+b
}
func oper(m,n int,fun func(int,int)int)int{
res:=fun(m,n)
return res
}
func main() {
fmt.Println(
oper(10,20,add))
}
閉包
引用外部變量的匿名函數
支持將函數作為另一個函數的返回值
func f(i int) func() int{
return func() int{
i++
return i
}
}
func main() {
str:=f(10)
fmt.Println(str())
fmt.Println(str())
}
-----------------------------
func ExFunc(n int) func () {
sum:=n
a:=func(){
fmt.Println(sum+1)
}
return a
}
func main() {
myFunc:=ExFunc(10)
myFunc()
}
函數的defer(延時機制)
在執行到defer是,暫時不執行,會將defer后面的壓入獨立的棧,按照先入后出的方式出棧,執行
先defer再return
獲取變量的地址,用&
獲取指針類型所指向的值*
fmt.Println(&num)
var ptr *int=&num
值類型,都有對應的指針類型 新式為*數據類型
結構體
//定義一個類,就是定義一個結構體
//類的定義,字段屬性,行為方法
type 結構體名稱 struct{
name type
age int
}
//定義結構體變量
var p1 結構體名稱
p2:=Person{"mary",20}
p6:=new(Person)
var p1 Person
p1.Age=10
p1.Name="小明"
var p2 *Person=&p1
fmt.Println((*p2).Age)
注意不能*p2.Age寫,因為.的優先級比* 高
// 實現結構體的淺拷貝
d4:=new(dog)
d4.color="黃色"
d4.age=2
d4.kind="中華田園犬"
fmt.Println(d4)
d5:=d4
d5.kind="張三"
fmt.Println(d5,d4)
d6:=&d1//*dog
d6.kind="金毛"
fmt.Println(d6,d1)
//匿名結構體:沒有名字的結構體,在創建匿名結構體,同時創建對象
p1:=struct {
name string
age int
}{
name: "zhangsan",
age: 30,
}
//匿名字段
type student struct{
string
int//匿名字段(不能有重復字段)
}
s1:=student{"zhgsan",12}
fmt.Println(s1.string,s1.int)
type Book struct{
bookName string
price float64
auther string
}
//嵌套struct的名稱沖突
有以下兩個名稱沖突的規則(報錯)
* 外部struct覆蓋內部struct的同名字段,同名方法(1)
* 同級別的struct出現同名字段,方法將報錯(2)
type A struct{
a int
b int
}
type B struct{
b int
c string
d string
}
type C struct {
A //繼承
B
a string
c string
}
(1) C.a和C.c分別覆蓋A.a和B.c
(2) A.b和B.b
為什么呢? 繼承的"提升字段"
//結構體嵌套
//模擬面向對象:聚合關系
//一個類作為另一個類的屬性
//定義一個書的結構體
type Book struct{
bookName string
price float64
auther string
}
//定義一個人的結構體
type Person struct{
name string
age int
book Book //聚合
}
func main() {
p3:=Person{
name:"Jerry",
age:26,
book:Book{
bookName:"Go語言是怎么練成的",
price:55.0,
auther:"張三",
},
}
fmt.Println(p3.book.auther,p3.book.price,p3.book.auther)
p4:=Person{"李曉華",20,Book{"四萬個為什么",44.8,"張三"}}
fmt.Println(p4.book.auther,p4.book.price,p4.book.auther)
}
- struct 的數據類型:值類型:默認深拷貝
- 如果結構體的字段類型是:指針,slice,和map的零值都是nil,還沒有分配空間
- 如果需要使用這樣的字段 ,需要先make,才能使用
- 結構體是值類型
方法
type Person struct{
name string
age int
}
type Student struct {
Person
school string
}
//方法
func (p Person) eat(){
fmt.Println("父類的方法,吃我我頭")
}
func (s Student) study(){
fmt.Println("子類的方法..")
}
func (s Student) eat(){
fmt.Println("子類重寫父類的方法")
}
多態繼承
接口(interface
):功能的描述的集合
- 定義有哪些功能
接口的意義
- 解耦合:程序和程序之間的關聯程度,降低耦合性
- 繼承關系:增加耦合
注意點:
- 當需要接口類型的對象時,那么可以使用任意實現類對象代替
- 接口對象不能訪問實現類的屬性
/* 多態*/
//定義一個接口
type Shape interface{
peri() float64 //周長
area() float64 //面積
}
//定義實現類:三角形
type Triangle struct {
a,b,c float64//三個邊
}
func (t Triangle)peri() float64{
return t.a+t.b+t.c//周長
}
func (t Triangle)area() float64{
p:=t.peri()
s:=math.Sqrt(p*(p-t.a)*(p-t.b)*(p-t.c)) //面積
return s
}
type Circle struct {
radius float64//半徑
}
func (c Circle)peri()float64{
return c.radius*2*math.Pi //面積
}
func (c Circle)area()float64{
return math.Pow(c.radius,2)*math.Pi
}
func teshShape(s Shape) {
fmt.Println("周長",s.peri(),"面積",s.area())
}
//轉型
func getType(s Shape) {
/* 方法一:instance,ok:=接口對象.(實際類型)
如果該接口對象是對應的實際類型,那么instance就是轉型之后對象
*/
ins,ok:=s.(Triangle)
if ok{
fmt.Println("是三角形",ins.a,ins.b,ins.c)
}else if ins,ok:=s.(Circle);ok{
fmt.Println("是圓形,半徑是:",ins.radius)
}
}
//2 轉型(向下轉型)
func getType2(s Shape) {
/*方法二:接口對象.(type),配合switch和case語句使用*/
switch ins := s.(type) {
case Triangle:
fmt.Println("三角形", ins.a, ins.b, ins.c)
case Circle:
fmt.Println("圓形", ins.radius)
//case int:
// fmt.Println("整型數據...")
}
}
func main() {
/*
多態:一個事物的多種形態
go語言:通過接口模擬多態性
一個實現類的對象:
看作是一個實現類類型:能夠訪問實現類中的方法和屬性
還可以看作是對應接口類型:只能夠訪問接口中定義的方法
用法一:一個函數如果接受接口類型作為參數,那么實際上可以傳入接口的任意實現類對象作為參數
*/
t1:=Triangle{3,4,5}
fmt.Println(t1.peri())
fmt.Println(t1.area())
fmt.Println(t1.a,t1.b,t1.c)
var s1 Shape
s1=t1
fmt.Println(s1.peri())
fmt.Println(s1.area())
var c1 Circle
c1=Circle{4}
fmt.Println(c1.peri())
fmt.Println(c1.area())
fmt.Println(c1.radius)
var s2 Shape=Circle{5}
fmt.Println(s2.area(),s2.peri())
//定義一個接口類型的數組
arr:=[4]Shape{t1,s1,c1,s2}
fmt.Println(arr)
//接口類型的對象-->對應實現類類型
getType(c1)
}
空接口
也是一個接口,但是該接口中沒有任何方法
所有可以將任意類型作為該接口的實現
//定義一個map:string作為key,任意類型作為value
map1:=make(map[string]interface{})
map1["name"]="王二狗"
map1["age"]=12
//空接口
type A interface {}
var a1 A=Cata{"花貓",1}
var a2 A=Persona{"王二狗","男性"}
練習1
/*
三維坐標,求兩點的距離
*/
type Point struct{
x,y,z float64
}
func (p Point) PrintInfo(){
fmt.Println(p.x,p.y,p.z)
}
func (p Point) getDisance3(p2 Point) float64{
dis:=math.Sqrt(math.Pow(p.x-p2.x,2)+math.Pow(p.y-p2.y,2)+math.Pow(p.z-p2.z,2))
return dis
}
func main() {
p1:=Point{1,2,4}
p2:=Point{0,0,0}
p1.PrintInfo()
p2.PrintInfo()
fmt.Println(p1.getDisance3(p2))
}