Golang是我最喜歡的一門語言,它簡潔、高效、易學習、開發效率高、還能夠編譯成機器碼... 雖然它一出世,就飽受關注,並且如今在市面上逐漸流行開來,可是,它畢竟是一門新興語言,還有不少讓人不太習慣的地方(即坑,(^__^)),我做爲新手,一邊學習,一邊踩坑,但願對其餘人有借鑑做用。php
__test.go
爲結尾Golang的source文件的命名和其餘語言本無差異,可是Golang自帶Unit test
,它的unit test
有個小規範:全部unit test
文件都要以__test.go
爲結尾! 因此,當你命名一個非unit test
文件爲XXX_test.go,並且執意要編譯時,就會報錯:no buildable Go source files in XXXXXX(你的文件路徑)
。 因此,切記,以__test.go
爲結尾的都是unit test
的文件,且切記不要把unit test
文件和普通Go文件放到一塊兒,必定要把unit test
文件集體放到一個目錄中,不然會編譯不過的。git
fmt.Println("這裏是漢字:" + 字符串變量)
字符串變量的值打印不出來的問題現有以下程序:github
package main
import "fmt"
func main() {
m1 := getString()
fmt.Println("如今是:" + m1)
}
func getString()string{
return "abd"
}
複製代碼
運行指令go run test.go
golang
可是單獨打印變量m1
卻能夠正常顯示數組
import "fmt"
func main() {
m1 := getString()
fmt.Println(m1)
fmt.Println("如今是:" + m1)
}
func getString()string{
return "abd"
}
複製代碼
這是爲何呢?很奇怪啊! 其實這要怪IDE,個人IDE是phpstorm + Golang插件包,IDE自帶的console對中文的支持很不友好,帶中文的字符串打印出來後,容易顯示不全,其實經過terminal
打印出來,是正確的!緩存
defer
出現的時候,多個defer
之間按照LIFO(後進先出)的順序執行package main
import "fmt"
func main(){
defer func(){
fmt.Println("1")
}()
defer func(){
fmt.Println("2")
}()
defer func(){
fmt.Println("3")
}()
}
複製代碼
對應的輸出是:bash
3
2
1
複製代碼
panic
中能夠傳任何值,不只僅能夠傳stringpackage main
import "fmt"
func main(){
defer func(){
if r := recover();r != nil{
fmt.Println(r)
}
}()
panic([]int{12312})
}
複製代碼
輸出:微信
[12312]
複製代碼
for range
來遍歷數組或者map的時候,被遍歷的指針是不變的,每次遍歷僅執行struct值的拷貝import "fmt"
type student struct{
Name string
Age int
}
func main(){
var stus []student
stus = []student{
{Name:"one", Age: 18},
{Name:"two", Age: 19},
}
data := make(map[int]*student)
for i, v := range stus{
data[i] = &v //應該改成:data[i] = &stus[i]
}
for i, v := range data{
fmt.Printf("key=%d, value=%v \n", i,v)
}
}
複製代碼
因此,結果輸出爲:app
key=0, value=&{two 19}
key=1, value=&{two 19}
複製代碼
import "fmt"
type student struct{
Name string
Age int
}
func (p *student) love(){
fmt.Println("love")
}
func (p *student) like(){
fmt.Println("like first")
p.love()
}
type boy struct {
student
}
func (b * boy) love(){
fmt.Println("hate")
}
func main(){
b := boy{}
b.like()
}
複製代碼
輸出:phpstorm
like first
love
複製代碼
func main(){
a := 1
defer print(function(a))
a = 2;
}
func function(num int) int{
return num
}
func print(num int){
fmt.Println(num)
}
複製代碼
輸出:
1
複製代碼
struct
的函數,仍是* struct
的函數import "fmt"
type people interface {
speak()
}
type student struct{
name string
age int
}
func (stu *student) speak(){
fmt.Println("I am a student, I am ", stu.age)
}
func main(){
var p people
p = student{name:"RyuGou", age:12} //應該改成 p = &student{name:"RyuGou", age:12}
p.speak()
}
複製代碼
輸出:
cannot use student literal (type student) as type people in assignment:
student does not implement people (speak method has pointer receiver)
複製代碼
make(chan int)
和 make(chan int, 1)
是不同的chan
一旦被寫入數據後,當前goruntine
就會被阻塞,知道有人接收才能夠(即 " <- ch"),若是沒人接收,它就會一直阻塞着。而若是chan帶一個緩衝,就會把數據放到緩衝區中,直到緩衝區滿了,纔會阻塞
import "fmt"
func main(){
ch := make(chan int) //改成 ch := make(chan int, 1) 就行了
ch <- 1
fmt.Println("success")
}
複製代碼
輸出:
fatal error: all goroutines are asleep - deadlock!
複製代碼
select 的代碼形式和 switch 很是類似, 不過 select 的 case 裏的操做語句只能是"IO操做"(不只僅是取值<-channel
,賦值channel<-
也能夠), select 會一直等待等到某個 case 語句完成,也就是等到成功從channel中讀到數據。 則 select 語句結束
import "fmt"
func main(){
ch := make(chan int, 1)
ch <- 1
select {
case msg :=<-ch:
fmt.Println(msg)
default:
fmt.Println("default")
}
fmt.Println("success")
}
複製代碼
輸出:
1
success
複製代碼
default
能夠判斷chan是否已經滿了
import "fmt"
func main(){
ch := make(chan int, 1)
select {
case msg :=<-ch:
fmt.Println(msg)
default:
fmt.Println("default")
}
fmt.Println("success")
}
複製代碼
輸出:
default
success
複製代碼
此時由於ch
中沒有寫入數據,爲空,因此 case不會讀取成功。 則 select 執行 default 語句。
變量定義基本方式爲:
var 髮量名字 類型 = 表達式
複製代碼
其中類型和表達式都可省略,若是初始化表達式被省略,將用零值初始化該變量。
var s string
fmt.Println(s) // ""
複製代碼
:=
注意的問題:=
定義的變量,僅能使用在函數內部。:=
周圍不必定是所有都是剛剛聲明的,有些可能只是賦值,例以下面的err變量in, err := os.Open(infile)
// TODO
out, err := os.Create(outfile)
複製代碼
new
在Go語言中只是一個預約義的函數,它並非一個關鍵字,咱們能夠將new
做爲變量或者其餘例如:
func delta(old, new int) int {
return new - old
}
複製代碼
以上是正確的。
new
就必定會在堆上分配內存編譯器會自動選擇在棧上仍是在堆上分配存儲空間,但可能使人驚訝的是,這個選擇並非由用var
仍是new
聲明變量的方式決定的。
請看例子:
var global *int
func f() {
var x int x=1
global = &x
}
func g() {
y := new(int)
*y = 1
}
複製代碼
f()
函數中的x
就是在堆上分配內存,而g()
函數中的y
就是分配在棧上。
init
函數在同一個文件中能夠包含多個在同一個包文件中,能夠包含有多個init
函數,多個init
函數的執行順序和定義順序一致。
package main
import (
"fmt"
)
type test struct {
name string
}
func (t *test) getName(){
fmt.Println("hello world")
}
func main() {
var t *test
t = nil
t.getName()
}
複製代碼
能正常輸出嗎?會報錯嗎?
輸出爲:
hello world
複製代碼
能夠正常輸出。Go本質上不是面向對象的語言,Go中是不存在object的含義的,Go語言書籍中的對象也和Java、PHP中的對象有區別,不是真正的」對象」,是Go中struct的實體。
調用getName方法,在Go中還能夠轉換,轉換爲:Type.method(t Type, arguments) 因此,以上代碼main函數中還能夠寫成:
func main() {
(*test).getName(nil)
}
複製代碼
*
符號的含義&的意思你們都明白的,取地址,假如你想得到一個變量的地址,只需在變量前加上&便可。
例如:
a := 1
b := &a
複製代碼
如今,我拿到a的地址了,可是我想取得a指針指向的值,該如何操做呢?用*
號,*b
便可。 *的意思是對指針取值。
下面對a的值加一
a := 1
b := &a
*b++
複製代碼
*
和&
能夠相互抵消,同時注意,*&
能夠抵消,可是&*
不能夠;因此a
和*&a
是同樣的,和*&*&*&a
也是同樣的。
os.Args
獲取命令行指令參數,應該從數組的1座標開始os.Args
的第一個元素,os.Args[0]
, 是命令自己的名字
package main
import (
"fmt"
"os"
)
func main() {
fmt.Println(os.Args[0])
}
複製代碼
以上代碼,通過go build
以後,打包成一個可執行文件main
,而後運行指令./main 123
輸出:./main
請看下列代碼:
import (
"fmt"
)
func main(){
array := [4]int{10, 20, 30, 40}
slice := array[0:2]
newSlice := append(slice, 50)
newSlice[1] += 1
fmt.Println(slice)
}
複製代碼
請問輸出什麼? 答案是:
[10 21]
複製代碼
若是稍做修改,將以上newSlice改成擴容三次,newSlice := append(append(append(slice, 50), 100), 150)以下:
import (
"fmt"
)
func main(){
array := [4]int{10, 20, 30, 40}
slice := array[0:2]
newSlice := append(append(append(slice, 50), 100), 150)
newSlice[1] += 1
fmt.Println(slice)
}
複製代碼
輸出爲:
[10 20]
複製代碼
這特麼是什麼鬼? 這就要從Golang切片的擴容提及了;切片的擴容,就是當切片添加元素時,切片容量不夠了,就會擴容,擴容的大小遵循下面的原則:(若是切片的容量小於1024個元素,那麼擴容的時候slice的cap就翻番,乘以2;一旦元素個數超過1024個元素,增加因子就變成1.25,即每次增長原來容量的四分之一。)若是擴容以後,尚未觸及原數組的容量,那麼,切片中的指針指向的位置,就仍是原數組(這就是產生bug的緣由);若是擴容以後,超過了原數組的容量,那麼,Go就會開闢一塊新的內存,把原來的值拷貝過來,這種狀況絲絕不會影響到原數組。 建議儘可能避免bug的產生。
map
引用不存在的key,不報錯請問下面的例子輸出什麼,會報錯嗎?
import (
"fmt"
)
func main(){
newMap := make(map[string]int)
fmt.Println(newMap["a"])
}
複製代碼
答案是:
0
複製代碼
不報錯。不一樣於PHP,Golang的map和Java的HashMap相似,Java引用不存在的會返回null,而Golang會返回初始值
請看下面的例子:
import (
"fmt"
)
func main(){
newMap := make(map[int]int)
for i := 0; i < 10; i++{
newMap[i] = i
}
for key, value := range newMap{
fmt.Printf("key is %d, value is %d\n", key, value)
}
}
複製代碼
輸出:
key is 1, value is 1
key is 3, value is 3
key is 5, value is 5
key is 7, value is 7
key is 9, value is 9
key is 0, value is 0
key is 2, value is 2
key is 4, value is 4
key is 6, value is 6
key is 8, value is 8
複製代碼
是雜亂無章的順序。map的遍歷順序不固定,這種設計是有意爲之的,能爲能防止程序依賴特定遍歷順序。
一個函數在將channel做爲一個類型的參數來聲明的時候,能夠將channl聲明爲只能夠取值(<- chan)或者只能夠發送值(chan <-),不特殊說明,則既能夠取值,也能夠發送值。
例如:只能夠發送值
func setData(ch chan <- string){
//TODO
}
複製代碼
若是在以上函數中存在<-ch則會編譯不經過。
以下是隻能夠取值:
func setData(ch <- chan string){
//TODO
}
複製代碼
若是以上函數中存在ch<-則在編譯期會報錯
package main
import (
"fmt"
)
func main(){
ch := make(chan string)
go setData(ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
}
func setData(ch chan string){
ch <- "test"
ch <- "hello wolrd"
ch <- "123"
ch <- "456"
ch <- "789"
}
複製代碼
以上代碼的執行流程是怎樣的呢? 一個基於無緩存channel的發送或者取值操做,會致使當前goroutine阻塞,一直等待到另外的一個goroutine作相反的取值或者發送操做之後,纔會正常跑。 以上例子中的流程是這樣的:
主goroutine等待接收,另外的那一個goroutine發送了「test」並等待處理;完成通訊後,打印出」test」;兩個goroutine各自繼續跑本身的。 主goroutine等待接收,另外的那一個goroutine發送了「hello world」並等待處理;完成通訊後,打印出」hello world」;兩個goroutine各自繼續跑本身的。 主goroutine等待接收,另外的那一個goroutine發送了「123」並等待處理;完成通訊後,打印出」123」;兩個goroutine各自繼續跑本身的。 主goroutine等待接收,另外的那一個goroutine發送了「456」並等待處理;完成通訊後,打印出」456」;兩個goroutine各自繼續跑本身的。 主goroutine等待接收,另外的那一個goroutine發送了「789」並等待處理;完成通訊後,打印出」789」;兩個goroutine各自繼續跑本身的。
記住:Golang的channel是用來goroutine之間通訊的,且通訊過程當中會阻塞。
互聯網技術窩
或者加微信共同探討交流: