目录
1. 第一个程序
package main
import "fmt"
func main(){
fmt.Println("hello world");
}
//go run test.go可以运行
//go build test.go 可以编译成exe文件
2. 格式化字符串
package main
import "fmt"
func main(){
var id = 123
var name = "tom"
var url = "id is %d, name is %s"
var newUrl = fmt.Sprintf(url, id, name)
fmt.Println(newUrl)
}
3. 变量的基本使用
package main
import "fmt"
// 这种因式分解关键字的写法一般用于声明全局变量,可以不使用(不报错)
var (
a int
b int
)
func main(){
var id int
id = 1
//g := 123只能在函数体内声明
g := 123 //推荐写法
var j = id
var aa string = "abc"
aa = "cd"
//空白标识符 _ 也被用于抛弃值
a1, _ := 2, 4
fmt.Print(id, g, j, &id, &j, " ", aa, " ", a1)
//输出: 1 123 1 0xc0000ac058 0xc0000ac070 cd 2
}
4. 常量
package main
import "fmt"
func main(){
const aa = "abc"
var alen = len(aa)
fmt.Print(alen)
//iota 用法
const (
a = iota //0
b //1
c //2
d = "ha" //独立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢复计数
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
}
5.运算符
一些基本的操作
其他运算符
& 返回变量存储地址
* 指针变量
package main
import "fmt"
func main() {
var id = 4
var pst *int
pst = &id
fmt.Print(&id, pst)
//输出一样的地址
}
其实这样也输出一样的地址
package main
import "fmt"
func main() {
var id = 4
var pst = &id
fmt.Print(&id, pst)
//输出一样的地址
}
6. 条件语句
package main
import "fmt"
func main() {
var a = 111
if a < 20 {
fmt.Printf("a 小于 20\n" )
} else {
fmt.Printf("a 大于 20\n" )
}
var score = 60
switch score{
case 90:
fmt.Println("优秀");
case 80:
fmt.Println("良好");
case 60:
fmt.Println("及格");
}
var x interface{} = 7
switch x.(type){
case nil:
fmt.Print("x 的类型 nil")
case int:
fmt.Print("x 的类型int");
case float64:
fmt.Print("x 的类型float")
}
/*
fallthrough
使用 fallthrough 会强制执行后面的 case 语句,fallthrough 不会判断下一条 case 的表达式结果是否为 true。
*/
/*
select 语句。 select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。
*/
}
7.循环语句
7.1 无限循环
func main() {
for true {
fmt.Printf("这是无限循环。\n");
}
}
7.2 for语句
7.2.1
func main() {
sum := 0
for i:=0; i<=10; i++{
sum += i
}
fmt.Println(sum)
}
7.2.2
func main() {
sum := 1
for ; sum <= 100;{
sum += sum
}
fmt.Println(sum)
}
7.2.3 可以简写7.2.2
func main() {
sum := 1
for sum <= 100{
sum += sum
}
fmt.Println(sum)
}
7.2.4 类似foreach
func main() {
arr := []string{"age", "name", "school"}
for k, v := range arr{
fmt.Println(k, v)
/*
输出
0 age
1 name
2 school
*/
}
}
7.3 break语句
7.3.1
func main() {
var a int = 10
for a<20 {
fmt.Printf("a的值是 %d\n", a)
a++;
if a>15 {
break;
}
}
}
7.3.2 不使用标记
func main() {
for i := 1; i <= 3; i++ {
fmt.Printf("i: %d\n", i)
for i2 := 11; i2 <= 13; i2++ {
fmt.Printf("i2: %d\n", i2)
break //跳出了当前循环
}
}
}
/*
输出:
i: 1
i2: 11
i: 2
i2: 11
i: 3
i2: 11
*/
7.3.3 使用标记
func main() {
re:
for i := 1; i <= 3; i++ {
fmt.Printf("i: %d\n", i)
for i2 := 11; i2 <= 13; i2++ {
fmt.Printf("i2: %d\n", i2)
break re//跳出到re
}
}
}
/*
输出:
i: 1
i2: 11
*/
7.4 continue
跳过当前循环执行下一次循环语句,也有标记和不标记
7.4.1 不使用标记
func main() {
for i := 1; i <= 3; i++ {
fmt.Printf("i: %d\n", i)
for i2 := 11; i2 <= 13; i2++ {
fmt.Printf("i2: %d\n", i2)
continue
}
}
}
/*
输出:
i: 1
i2: 11
i2: 12
i2: 13
i: 2
i2: 11
i2: 12
i2: 13
i: 3
i2: 11
i2: 12
i2: 13
*/
7.4.2 使用标记
func main() {
re:
for i := 1; i <= 3; i++ {
fmt.Printf("i: %d\n", i)
for i2 := 11; i2 <= 13; i2++ {
fmt.Printf("i2: %d\n", i2)
continue re
}
}
}
/*
输出:
i: 1
i2: 11
i: 2
i2: 11
i: 3
i2: 11
*/
7.4.3 和7.4.2效果一样
func main() {
for i := 1; i <= 3; i++ {
fmt.Printf("i: %d\n", i)
for i2 := 11; i2 <= 13; i2++ {
fmt.Printf("i2: %d\n", i2)
break
}
}
}
7.5 goto语句,在结构化程序设计中一般不主张使用 goto 语句, 以免造成程序流程的混乱
8. 函数
8.1 值传递
这里x, y string 里的string要写,类型声明一样,不然会报undefined x undefined y
func swap(x, y string) (string, string){
return y, x
}
func main() {
a, b := swap("aa", "bb")
fmt.Println(a, b)
}
8.1.1 下面会报错
func swap(x, y string){
return "abc"
}
func main() {
a := swap("aa", "bb")
fmt.Println(a)
}
需要这样写才正确
func swap(x, y string) (string){
return "abc"
}
8.2 引用传递
func swap(x, y string){
tmp := x
x = y
y = tmp
}
func main() {
var a = "aa"
var b = "bb"
swap(a, b)
fmt.Println(a, b)
}
输出 aa bb
如果想被交换应该这样写
func swap(x *string, y *string){
tmp := *x
*x = *y
*y = tmp
}
func main() {
var a = "aa"
var b = "bb"
swap(&a, &b)
fmt.Println(a, b)
}
输出 bb aa
8.3 函数作为另外一个参数
// 声明一个函数类型
type cb func(int) int
func main() {
testCallBack(1, callBack)
testCallBack(2, func(x int) int {
fmt.Printf("我是回调,x:%d\n", x)
return x
})
}
func testCallBack(x int, f cb) {
f(x)
}
func callBack(x int) int {
fmt.Printf("我是回调,x:%d\n", x)
return x
}
输出:
我是回调,x:1
我是回调,x:2
8.4 闭包
和JavaScript的差不多
func getSequence() func() string {
i := "a"
return func() string {
i = i + "b"
return i
}
}
func main(){
/* nextNumber 为一个函数,函数 i 为 0 */
nextNumber := getSequence()
/* 调用 nextNumber 函数,i 变量自增 1 并返回 */
fmt.Println(nextNumber())
fmt.Println(nextNumber())
fmt.Println(nextNumber())
}
输出
Ab
Abb
Abbb
8.5 函数 -(方法),比较特殊的用法
方法就是一个包含了接受者的函数
package main
import "fmt"
type Circle struct{
radius float64
}
func(cc Circle) getArea() float64{
return 3.14 * cc.radius * cc.radius
}
func main(){
var c1 Circle
c1.radius = 10.00
fmt.Print("圆的面积是", c1.getArea())
}
9. 作用域
在函数体外声明的变量称之为全局变量
10. 数组
func main(){
var balance [10] float32
var age = [5]float32{1,2,3,4}
fmt.Print(age, balance)
//少的补0,输出[1 2 3 4 0] [0 0 0 0 0 0 0 0 0 0]
var money = [5]float32{1:100, 3:400}
fmt.Print(money)
//输出[0 100 0 400 0],特殊指定索引的值
}
- 指针
略,上面有写到
- 结构体
12.1 定义结构体
func main(){
var b1 = Book{"laravel", "php框架", "xiaobai", 18}
fmt.Println(b1)
//age补0
var b2 = Book{name:"名字", title:"标题", author:"作者"}
fmt.Println(b2)
}
12.2 访问结构体成员
type Book struct{
title string
name string
author string
age int
}
func main(){
var b1 Book
b1.title = "Go 语言"
fmt.Printf("book title %s", b1.title)
fmt.Printf("book author %s", b1.author)
fmt.Printf("author age %d", b1.age)
/*
输出
book title Go 语言
book author
author age 0
*/
}
12.3 结构体作为函数参数
type Book struct{
title string
name string
author string
age int
}
func main(){
var b1 Book
b1.title = "Go 语言"
printInfo(b1)
}
func printInfo(bb Book){
fmt.Print(bb.title)
fmt.Print(bb.author)
}
12.4 结构体指针
type Book struct{
title string
name string
author string
age int
}
func main(){
var bb Book
bb.title = "标题"
bb.age = 18
printInfo(&bb)
}
func printInfo(myBook *Book){
fmt.Print(myBook.title)
fmt.Print(myBook.age)
}
12.5 和12.4效果一样
func main(){
var bb Book
bb.title = "标题"
bb.age = 18
printInfo(bb)
}
func printInfo(myBook Book){
fmt.Print(myBook.title)
fmt.Print(myBook.age)
}
- 切片
13.1 定义
可以声明一个未指定大小的数组来定义切片:
var bb []int
或者使用make来创建切片
func main(){
var bb []int = make([]int, 10)
fmt.Print(bb) //输出[0 0 0 0 0 0 0 0 0 0]
}
或者上面的简写为
func main(){
//var bb []int = make([]int, 10)
bb := make([]int, 10)
fmt.Print(bb) //输出[0 0 0 0 0 0 0 0 0 0]
}
13.2 切片初始化
func main(){
var arr = [] int {1,2,3,4,5,6}
fmt.Print(arr)//输出 [1 2 3 4 5 6]
s := arr[2:5] //startIndex 到 endIndex-1 下的元素创建为一个新的切片
fmt.Print(s)//输出[3 4 5]
s1 := arr[2:]
fmt.Print(s1)//输出 [3 4 5 6]
}
13.3 len()和cap() 函数
func main(){
var x = make([]int, 3, 5) //make([]T, length, capacity) capacity是容量大小
fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x) //输出 len=3 cap=5 slice=[0 0 0]
}
13.4 空(nil)切片
func main() {
var numbers []int
if(numbers == nil){
fmt.Printf("切片是空的")
}
}
13.5 切片截取
Arr[from: to]
func main() {
/* 创建切片 */
numbers := []int{0,1,2,3,4,5,6,7,8}
printSlice(numbers) //cap is 9
numbers1 := make([]int,0,5)
printSlice(numbers1) //cap is 5
number2 := numbers[:2]
printSlice(number2) //cap is 9
number3 := numbers[1:5]
printSlice(number3) //cap is 8 左指针偏移了1步,所以cap为9-1=8
/*
输出
len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]
len=0 cap=5 slice=[]
len=2 cap=9 slice=[0 1]
len=4 cap=8 slice=[1 2 3 4]
*/
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
13.6 append()和copy() 函数
func main() {
var numbers []int
printSlice(numbers)
numbers = append(numbers, 0)
printSlice(numbers)
numbers = append(numbers, 1)
printSlice(numbers)
numbers = append(numbers, 2,3,4)
printSlice(numbers)//为什么这里容量是6
/*
解释如下
1.先将旧的slice容量乘以2,如果乘以2后的容量仍小于新的slice容量,则取新的slice容量(append多个elems)
2.如果新slice小于等于旧slice容量的2倍,则取旧slice容量乘以2
3.如果旧的slice容量大于1024,则新slice容量取旧slice容量乘以1.25
4.代码后面还会对newcap进行roundup,比如在64位平台,newcap是奇数的话就会+1
*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
copy(numbers1,numbers)
printSlice(numbers1)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
/*
输出
len=0 cap=0 slice=[]
len=1 cap=1 slice=[0]
len=2 cap=2 slice=[0 1]
len=5 cap=6 slice=[0 1 2 3 4]
len=5 cap=12 slice=[0 1 2 3 4]
*/
- 范围
range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素
func main(){
sum := 0
var arr = []int{1,2,3,4}
for _,v := range arr{
sum += v
}
fmt.Print(sum)
}
- 集合Map
func main(){
var myMap map[string]string
myMap = make(map[string]string) //必须要加这个
myMap["name"] = "tom"
myMap["country"] = "China"
for k := range myMap{
fmt.Print(k, myMap[k])
}
vv, ok := myMap["name"] //这里name可以换成xxx
if(ok){
//输出: 某个exist-tom
fmt.Print("某个exist-", vv)
}else{
fmt.Print("不存在")
}
}
15.1 delete函数
func main(){
var myMap map[string]string
myMap = make(map[string]string) //必须要加这个
myMap["name"] = "tom"
myMap["country"] = "China"
delete(myMap, "name")
fmt.Print(myMap)
}
- 递归函数
略
- 类型转换
func main(){
var b = 123
var c float32
c = float32(b) // 不然c=b会报错
fmt.Print(c)
}
- 接口,需要理解,有点意思
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。
18.1 例子1
package main
import (
"fmt"
)
type Phone interface {
call()
dd()
}
type NokiaPhone struct {
}
func (nokiaPhone NokiaPhone) call() {
fmt.Println("I am Nokia, I can call you!")
}
func (nokiaPhone NokiaPhone) dd() {
fmt.Println("dd111")
}
func main() {
var phone Phone
phone = new(NokiaPhone)
phone.call()
phone.dd()
}
18.2 例子2
package main
import (
"fmt"
)
type Sleeper interface{
Sleep()
}
type Dog struct{
Name string
}
func (d Dog) Sleep(){
fmt.Printf("Dog %s is sleeping\n", d.Name)
}
func AnimalSleep(s Sleeper){
s.Sleep()
}
//用go的接口实现了多态,我们先声明了一个接口类型的值(这里是var s Sleeper),只要实现了这个接口的struct变量(这里是Sleep()),都可以赋值给它,而调用方法的时候,go会根据实际类型选择使用哪个struct的方法。
func main(){
var s Sleeper
dog := Dog{Name: "xiaobai"}
//1 https://www.bilibili.com/video/BV1MC4y187bo?t=549
s = dog //假想:一个dog的实例,给一个接口的实例
AnimalSleep(s)
//2
sleepList := []Sleeper{Dog{Name: "wangwang"}}
for _, s := range sleepList{
s.Sleep()
}
}
18.3 例子3 接口嵌入
package main
import (
"fmt"
)
type Sleeper interface{
Sleep()
}
type Eater interface{
Eat(foodName string)
}
type LazyAnimal interface{
Sleeper
Eater
}
type Dog struct{
Name string
}
func (d Dog) Sleep(){
fmt.Printf("Dog %s is sleeping\n", d.Name)
}
func (d Dog) Eat(foodName string){
fmt.Printf("Dog %s is eating %s\n", d.Name, foodName)
}
func AnimalSleep(s Sleeper){
s.Sleep()
}
func main(){
var lz LazyAnimal
dog := Dog{Name: "xiaobai"}
lz = dog
lz.Sleep()
lz.Eat("chocolate")
/*
输出
Dog xiaobai is sleeping
Dog xiaobai is eating chocolate
*/
//类型断言
if dogBB,ok := lz.(Dog); ok{
fmt.Printf("I am a Dog, my name is %s", dogBB.Name)
}
}
- 并发
Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。 goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。
19.1 go并发
package main
import (
"fmt"
"time"
)
func say(s string){
for i:=0; i<5; i++{
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main(){
go say("world")
say("hello")
/*
输出
hello
world
world
hello
hello
world
world
hello
world
hello
或者又可能
world
hello
world
hello
hello
world
world
hello
hello
*/
}
19.2 通道 channel,计算就和
package main
import (
"fmt"
)
func sum(s []int, c chan int){
sum :=0
for _,v :=range s{
sum += v
}
c <- sum //把sum发送到c通道
}
func main(){
s := []int{7, 2, 8, -9, 4, 2}
c := make(chan int)
go sum(s[:len(s) / 2], c)
go sum(s[len(s) / 2:], c)
x, y := <-c, <-c //从通道c中接收值
fmt.Println(x, y , x+y) //输出:-3 17 14
}
19.3 通道缓冲区
通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:
ch := make(chan int, 100)
19.4 遍历通道与关闭通道
略