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之间通讯的,且通讯过程当中会阻塞。
互联网技术窝
或者加微信共同探讨交流: