几个基本点
- 可以不用分号,如果多行写在一行,则必须有分号
- 注释两种类型,同java
//必须为main,否则不能作为运行项
package main
import "fmt"
//虽不报错,以下二者是否可以放在方法之外?
var test = "adfsdf"
test2:= "sdfsdf"
//方法必须为main()
func main(){
// 这也是一种自动推断
var whatISay = "I Want to say!"
whatISay2 := "I Want to Say!"
fmt.Println("Hello world!" + "Haha" + whatISay + whatISay2)
}
基本的数据类型
- 数字类型
int float32 float64 int8 int16 int32 int64 uint8 uint16 uint32 uint64 complex64 complex128 byte(uint8) rune uint(int32) int uintptr(存入指针的无符号) - 布尔型
true false - 字符串类型
可以看成字符序列, UTF-8 - 派生类型
(a) 指针类型(Pointer)
(b) 数组类型
© 结构化类型(struct)
(d) Channel 类型
(e) 函数类型
(f) 切片类型
(g) 接口类型(interface)
(h) Map 类型
变量的零值:
数值类型是0
布尔是false
字符是""
类型推导的前题:
使用了没有类型的var变量 或 :=
package main
import "fmt"
import "reflect"
//基本的变量声明 (原则:如果带了var就带上类型,否则直接用:=)
var length12 int = 3
const length23 int = 5
var boolVar bool = true
var floatVar float64 = 3.335
var uintVar uint = 999
// 一定得都写上,否则会报错,此处不像java一样
var length1, length2 = 3, 2
// 声明时,必须带着var,同类型的话,可以只写最后一个,同mehod参数
var b, c int
const (
day1 = 1
day2 = 2
)
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)
// 以下是类型强转的方式
var a interface{} = f
c := a.(int)
fmt.Println(reflect.TypeOf(c)) // int
//如果多了,就 repl.go:39:28: array index 2 out of bounds [0:2]
array1 := [2]string{"zcy", "yyy"}
array2 := [...]string{"zcy"}
//repl.go:40:17: first argument to append must be slice; have <[2]string>
//array1 = append(array1, "kkk")
fmt.Println(array1, array2)
var slice1 []string = []string{"zcy", "yyy"}
//以此来扩大长度,使其靠近cap
slice1 := append(slice1, "xixi")
fmt.Println(slice1)
//声明的时候,不要少了map关键字
map1 := make(map[string]int, 3)
map1["sdfsdf"] = 1
fmt.Println(map1)
0 1 2 ha ha 100 100 7 8
int
[zcy yyy] [zcy]
[zcy yyy xixi]
map[sdfsdf:1]
14 <nil>
重点关注一下指针运算符
var a int = 4
var b int32
var c float32
var ptr *int
/* 运算符实例 */
fmt.Printf("第 1 行 - a 变量类型为 = %T\n", a )
fmt.Printf("第 2 行 - b 变量类型为 = %T\n", b )
fmt.Printf("第 3 行 - c 变量类型为 = %T\n", c )
/* & 和 * 运算符实例 */
ptr = &a /* 'ptr' 包含了 'a' 变量的地址 */
fmt.Printf("a 的值为 %d\n", a)
fmt.Printf("*ptr 为 %d\n", *ptr)
i, j := 42, 2701
p := &i // point to i
fmt.Println(*p) // read i through the pointer
fmt.Println(p) // 这个肯定是其地址值了,可以终极判断是不是一个对象
*p = 21 // set i through the pointer
fmt.Println(i) // see the new value of i
var a = "abc"
var b = "abc"
//a的地址:824634121680, b的地址:824634121696
fmt.Printf("a的地址:%d, b的地址:%d \n", &a, &b)
//true ?
fmt.Println(a == b)
第 1 行 - a 变量类型为 = int
第 2 行 - b 变量类型为 = int32
第 3 行 - c 变量类型为 = float32
a 的值为 4
*ptr 为 4
42
0xc000698058
21
a的地址:824640676608, b的地址:824640676992
true
5 <nil>
函数的声明与使用
import "strconv"
import "fmt"
//返回两个值的时候,要将返回的东西弄成一个元组,即加括号
//当两个或多个连续的函数命名参数是同一类型,则除了最后一个类型之外,其他都可以省略。
func methodName1(arg1 int, arg2 string, arg3, arg4 int) (string, error) {
fmt.Println(arg3, arg4)
//注意一下int转string的方法
result := (strconv.Itoa(arg1) + arg2)
return result, nil
}
result1, err := methodName1(1, "name", 3, 4)
if(err != nil) {
fmt.Println("error happened")
}
// 此种方法来替代java中的lambda表达式
var methodName2 = func(arg1 int) bool {return arg1 > 2}
fmt.Println(methodName2(3)) // true
// 命名返回值,此种尽量不用,但要理解
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
3 4
true
流程控制语句
- 很少用(),没用必要,而且这样可以在if时进行初始化等
- 没有while语句,for和switch和if三驾马车
import "fmt"
//不用有(),但要有{},同一行是分号
for i:=0; i<4; i++ {
fmt.Println(i)
}
// <span class="burk">在go中是没有while语句的,统统用for</span>
i = 3
for i<10 {
i++
fmt.Println(i)
}
// 这里等同于 while(true){}
//for{}
// 不能用()和for一样
if c:=9; c>3 {
fmt.Println(c)
}
switch os := "windows"; os{
case "windows":
fmt.Println("1====windows")
//不加这个类似于java,所以其与java是相反的,java的break
fallthrough
case "linux":
fmt.Println("2----linux")
default:
fmt.Println("default")
}
// 表达if elseif else 的语句,switch后面都没有跟着c
switch {
case c<4:
fmt.Println("==1")
case c<3:
fmt.Println("===2")
default:
fmt.Println("====3")
}
//interface{} 这个是go中的Object类
var t interface{} = 1
//获取对象的类型的方法,t.(type)仅在switch中可用
switch t := t.(type) {
default:
fmt.Printf("unexpected type %T", t) // %T prints whatever type t has
case bool:
fmt.Printf("boolean %t\n", t) // t has type bool
case int:
fmt.Printf("integer %d\n", t) // t has type int
case *bool:
fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
case *int:
fmt.Printf("pointer to integer %d\n", *t) // t has type *int
}
0
1
2
3
4
5
6
7
8
9
10
9
1====windows
2----linux
==1
integer 1
defer函数的处理
// defer主要是用于资源回收
func method1(arg1 int) int {
// defer是关键字,不是方法,否则怎么传递参数啊
defer method2(arg1)
// 这个先执行,后进先出,是栈的结构
defer method2(arg1 + 1)
return arg1
}
func method2(arg1 int){
import "fmt"
fmt.Println(arg1)
}
method1(2) // 3 2
3
2
2
结构体和专属方法
import "fmt"
import "strconv"
type Member struct {
// 属性后面不带,号
Name string
age int
Children [2]string
nothing int
}
//如果想改变值,就用指针,直接用对象而不加&的时候,编译器会自动转换
func (member *Member) sayHi(){
fmt.Println("member's age is "+ strconv.Itoa(member.age))
fmt.Printf("address is %d \n", &member)
}
// 要么全初始化,要么用名称初始化
m1 := Member{Name: "zcy"}
//too few values in struct initializer: <main.Member> has 4 fields, found 3 initializers
//m2 := Member{"zcy", 23, [2]string{"xixi", "xiaolu"}}
m2 := Member{"zcy", 23, [2]string{"xixi", "xiaolu"}, 2}
m1.sayHi()
fmt.Printf("======================================m1 address is %d \n", (&m1))
(&m1).sayHi()
// 这个可以访问,可以啊,在一个包里,为何不可
m1.age = 23
// 这个方式是错误的
//m1.Children = ['xixi', 'xiaolu']
m1.Children = [2]string{"xixi", "xiaolu"}
//incompatible types in assignment: <[2]string> = <[]string>
//m1.Children = []string{"xixi", "xiaolu"}
fmt.Println(m1, m2)
//如果是这种,即使用值调用也会转为地址调用
func (member *Member) change1(){
member.age = 1
}
//如果是这种声明,即使用地址调用也是会自动转成值复制
func (member Member) change2(){
member.age = 2
}
m_change_1 := Member{}
m_change_2 := Member{}
// 0 1 1
fmt.Printf("age is : %d \n", m_change_1.age)
m_change_1.change1()
fmt.Printf("age is : %d \n", m_change_1.age)
m_change_1.age = 0
(&m_change_1).change1()
fmt.Printf("age is : %d \n", m_change_1.age)
// 0 0 0
fmt.Printf("age is : %d \n", m_change_2.age)
m_change_2.change2()
fmt.Printf("age is : %d \n", m_change_2.age)
m_change_2.age = 0
(&m_change_2).change2()
fmt.Printf("age is : %d \n", m_change_2.age)
member's age is 0
address is 824633745616
======================================m1 address is &{%!d(string=zcy) 0 [%!d(string=) %!d(string=)] 0}
member's age is 0
address is 824633745624
{zcy 23 [xixi xiaolu] 0} {zcy 23 [xixi xiaolu] 2}
age is : 0
age is : 1
age is : 1
age is : 0
age is : 0
age is : 0
12 <nil>
常用的语法
import "fmt"
var array1 []string = []string{"string0", "string1", "string2", "string3", "string4", "string5", "string6"}
// cannot assign <[]string> to <[2]string> in variable declaration: array2 <[2]string>,写2就是数组,不写是切片
//var array2 [2]string = []string{"string1", "string2"}
// ===================切片的操作===================
fmt.Println(array1[1:3], array1[1:], array1[:3], len(array1[:3]))
slice1 := array1[3:]
for i:=0; i<len(slice1); i++ {
fmt.Println(slice1[i])
}
//有容量有什么用,赋值超了len就报错????
b := make([]int, 2, 5) // len(b)=2, cap(b)=5
b[0] = 2
fmt.Println(b[1]) // 0
map1 := make(map[string] int)
map1["a"] = 5
// 容量是6
map2 := make(map[string] int, 6)
// 每个元素都要有逗号,否则 missing ',' before newline in composite literal
map3 := map[string] int {
"a" : 4,
"b" : 5,
}
//现在知道cap的作用了吧,1000这前翻倍,之后加25%
array1 := make([]int, 2, 3)
array1[0] = 1
array1[1] = 2
array1 = append(array1, 3)
array1 = append(array1, 4)
fmt.Printf("len is %d, cap is %d \n", len(array1), cap(array1))
fmt.Println(array1)
// 容量与长度的关系
map4 := make(map[string]int, 2)
map4["1"] = 1
map4["2"] = 1
map4["3"] = 1
map4["4"] = 1
map4["5"] = 1
fmt.Printf("len is %d, cap is nil, map没有容量一说", len(map4))
[string1 string2] [string1 string2 string3 string4 string5 string6] [string0 string1 string2] 3
string3
string4
string5
string6
0
len is 4, cap is 6
[1 2 3 4]
len is 5, cap is nil, map没有容量一说
43 <nil>
可以看到声明的方式 []string 和 map[string]int 和 [2]string
协程和通道
import "fmt"
import "sync"
import "time"
chan1 := make(chan int, 3)
wg := sync.WaitGroup{}
wg.Add(2)
type member struct {
name string
age int
}
chan2 := make(chan member)
func method1(){
fmt.Println("I'm in the go routine now!")
defer wg.Done()
// 此处在没有值来的时候,是阻塞的
receive := <-chan1
fmt.Printf("int receive: %d\n", receive)
// 此处在没有值来的时候,是阻塞的
member_var := <-chan2
fmt.Printf("name: %s, age: %d\n", member_var.name, member_var.age)
}
func method2(){
defer wg.Done()
// 箭头的方向是一样
chan1 <- 7
time.Sleep(time.Second * 10)
chan2 <- member{"zcy", 36}
}
go method1()
time.Sleep(time.Second * 10)
go method2()
wg.Wait()
fmt.Println("\nI'm done!")
I'm in the go routine now!
int receive: 7
name: zcy, age: 36
I'm done!
11 <nil>
import "fmt"
import "sync"
import "time"
wg := sync.WaitGroup{}
wg.Add(2)
type member struct {
name string
age int
}
chan2 := make(chan member, 2)
func method1(){
fmt.Println("I'm in the go routine now!--method1")
defer wg.Done()
// 此处在没有值来的时候,是阻塞的
member_var := <-chan2
fmt.Printf("name: %s, age: %d\n", member_var.name, member_var.age)
fmt.Println("I'm leaving the go routine now!--method1")
}
func method2(){
fmt.Println("I'm in the go routine now!--method2")
defer wg.Done()
// 箭头的方向是一样
// 如果不是缓冲通道,此处也是阻塞的
chan2 <- member{"zcy", 1}
fmt.Println("chan2 <- member{zcy, 1}")
chan2 <- member{"zcy", 2}
fmt.Println("chan2 <- member{zcy, 2}")
// 取决于缓冲区大小,超了,此处也是阻塞的
chan2 <- member{"zcy", 3}
fmt.Println("chan2 <- member{zcy, 3}")
fmt.Println("I'm leaving the go routine now!--method2")
}
go method2()
time.Sleep(time.Second * 10)
go method1()
wg.Wait()
fmt.Println("\nI'm done!")
I'm in the go routine now!--method2
chan2 <- member{zcy, 1}
chan2 <- member{zcy, 2}
I'm in the go routine now!--method1
name: zcy, age: 1
I'm leaving the go routine now!--method1
chan2 <- member{zcy, 3}
I'm leaving the go routine now!--method2
I'm done!
11 <nil>
import "fmt"
import "sync"
import "time"
wg := sync.WaitGroup{}
wg.Add(2)
type member struct {
name string
age int
}
chan2 := make(chan member, 2)
func method1(){
fmt.Println("I'm in the go routine now!--method1")
defer wg.Done()
//chan2在Close后,即会退出这个循环
for member_var := range chan2 {
//member_var := <-chan2
fmt.Printf("name: %s, age: %d\n", member_var.name, member_var.age)
}
fmt.Println("即使关了,我们还是可以取出零值来", <-chan2)
//true
fmt.Println("是不是等于空值:", member{} == <-chan2)
fmt.Println("I'm leaving the go routine now!--method1")
}
func method2(){
fmt.Println("I'm in the go routine now!--method2")
defer wg.Done()
// 箭头的方向是一样
// 如果不是缓冲通道,此处也是阻塞的
chan2 <- member{"zcy", 1}
fmt.Println("chan2 <- member{zcy, 1}")
chan2 <- member{"zcy", 2}
fmt.Println("chan2 <- member{zcy, 2}")
// 取决于缓冲区大小,超了,此处也是阻塞的
chan2 <- member{"zcy", 3}
time.Sleep(time.Second * 10)
chan2.Close()
fmt.Println("chan2 <- member{zcy, 3}")
fmt.Println("I'm leaving the go routine now!--method2")
}
go method2()
time.Sleep(time.Second * 10)
go method1()
wg.Wait()
fmt.Println("\nI'm done!")
I'm in the go routine now!--method2
chan2 <- member{zcy, 1}
chan2 <- member{zcy, 2}
I'm in the go routine now!--method1
name: zcy, age: 1
name: zcy, age: 2
name: zcy, age: 3
chan2 <- member{zcy, 3}
I'm leaving the go routine now!--method2
即使关了,我们还是可以取出零值来 { 0}
是不是等于空值: true
I'm leaving the go routine now!--method1
I'm done!
11 <nil>
import "fmt"
import "sync"
import "time"
//声明一个lock的语句
var wg sync.WaitGroup = sync.WaitGroup{}
wg.Add(4)
var lock sync.Mutex
func method1(flag int){
lock.Lock()
defer lock.Unlock()
defer wg.Done()
time.Sleep(time.Second * 5)
fmt.Printf("flag %d is int the lock area!\n", flag)
}
go method1(1)
fmt.Println("===================1")
go method1(2)
fmt.Println("===================2")
go method1(3)
fmt.Println("===================3")
go method1(4)
fmt.Println("===================4")
wg.Wait()
===================1
===================2
===================3
===================4
flag 1 is int the lock area!flag 4 is int the lock area!flag 2 is int the lock area!flag 3 is int the lock area!
make和new的区别
- new返回的是一个指针,参数是类型,分配的内存会置0,其实其不常用,不如直接声明来的方便
- make仅是为slice, map, channel服务的,除此之外无用
- 二者作用都是来分配内存,且内存都分配在堆上
虽然无关,总结留此:
- 传入参数,返回值等都是按照 “非指针,建对象” 的原则来进行的
- 对一些基本类型,一般不用指针,即使是string,人家在go里可是基本类型
- 引用类型不是没有的,map slice chan func这四个家伙是引用类型
import "fmt"
type member struct {
name string
age int
children []*member
}
m1 := new(member)
m1.name = "zcy1"
m1.age = 36
m2 := new(member)
m2.name = "xixi"
m2.age = 5
m3 := &member{
name: "xiaolu",
age: -1,
}
//??????????????????????????????????
//reflect.Value.Convert: value of type []*struct { ?name string; ?age int; ?children []*struct
//{ ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children
//[]*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string;
//?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct
//{ ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int;
//?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int } } } } } } } } } } } } } } } cannot be converted to type []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int; ?children []*struct { ?name string; ?age int } } } } } } } } } } } } } }
//m1.children = make([]*member, 2, 2)
//m1.children[0] = m2
//m1.children[1] = m3
// m1 = &member {
// name: "zcy1",
// age: 36,
// children: []*member{m2, m3},
// }
func update(m member) bool{
m.name = "zcy1"
m.name = "dad zcy1"
return true
}
//repl.go:31:8: cannot use <*main.member> as <main.member> in argument to update
//update(m1)
//fmt.Println("after update, ", m1)
func update2(m *member) *member {
m.name = "zcy1"
m.name = "dad zcy1"
fmt.Printf("m内存位置是%p \n", m)
return m
}
// 可见,通过传入指针类型,可以进行参数修改
fmt.Printf("m1内存的位置%p \n", m1)
m1 = update2(m1)
fmt.Printf("m1内存的位置%p \n", m1)
fmt.Println("after update2, ", m1)
//虽然string是会建新值,但是其仍然是基本类型,所以可以不用指针
func update3(m *member) (member, string) {
m.name = "zcy2"
m.name = "dad zcy3"
str := "dddd"
//str location 0xc000990ca0
//update3 str内存的位置0xc000990ce0
fmt.Printf("str location %p \n", &str)
return *m, str
}
// 可见,如果返回值型,那么会直接返回新的一个对象
//update3 m1内存的位置0xc0007e2480
//update3 m1内存的位置0xc0007e2900
fmt.Printf("update3 m1内存的位置%p \n", m1)
m4, str := update3(m1)
fmt.Printf("update3 str内存的位置%p \n", &str)
fmt.Printf("update3 m1内存的位置%p \n", &m4)
m1内存的位置0xc00099a9c0
m内存位置是0xc00099a9c0
m1内存的位置0xc00099a9c0
after update2, &{dad zcy1 36 []}
update3 m1内存的位置0xc00099a9c0
str location 0xc000990ca0
update3 str内存的位置0xc000990ce0
update3 m1内存的位置0xc00099af00
40 <nil>
引用类型有哪些:
- array不是,任何对它的传递都是复制后的值传递
- slice是,存储结构是 loc + len + cap
- map是, 存储结构是哈希桶
- func是
- chan是, 有缓冲区的更是
import "fmt"
type member struct {
name string
age int
}
array1 := [...]member{member{"zcy", 12}, member{
name: "zcy2",
age: 23,
}}
fmt.Println(array1, &array1)
fmt.Printf("1 location is %p \n", &array1)
fmt.Printf("1.1 location is %p \n", &(array1[0]))
fmt.Printf("1.2 location is %p \n", &(array1[1]))
func test1(m [2]member){
fmt.Printf("2 location is %p \n", &m)
fmt.Printf("2.1 location is %p \n", &(m[0]))
fmt.Printf("2.2 location is %p \n", &(m[1]))
}
// 果然是整体复制了一圈,所以用array的时候要小心一些
test1(array1)
//可以明显看到slice传递的时候,不会进行复制,仅复制了引用的存储结构
slice1 := []member{member{"zcy1", 2}, member{"zcy2", 32}}
fmt.Println(slice1, &slice1)
fmt.Printf("1 location is %p \n", &slice1)
fmt.Printf("1.1 location is %p \n", &(slice1[0]))
fmt.Printf("1.2 location is %p \n", &(slice1[1]))
func test2(m []member){
fmt.Printf("2 location is %p \n", &m)
fmt.Printf("2.1 location is %p \n", &(m[0]))
fmt.Printf("2.2 location is %p \n", &(m[1]))
}
test2(slice1)
[{zcy 12} {zcy2 23}] &[{zcy 12} {zcy2 23}]
1 location is 0xc00099ab70
1.1 location is 0xc00099ab70
1.2 location is 0xc00099ab88
2 location is 0xc00099bb00
2.1 location is 0xc00099bb00
2.2 location is 0xc00099bb18
[{zcy1 2} {zcy2 32}] &[{zcy1 2} {zcy2 32}]
1 location is 0xc00031cc80
1.1 location is 0xc00099bc50
1.2 location is 0xc00099bc68
2 location is 0xc00031ce80
2.1 location is 0xc00099bc50
2.2 location is 0xc00099bc68