个人博客首发 http://www.geekqian.com/post/919b1b0c.html
Go
中可以在 结构体 (Struct) 中封装属性和操作, 概念类似 java
中的类. 定义方式如下:
type Student struct {
Name string
Age int
Gender string
}
实现方式如下:
s1 := Student{Name:"Mike", Age:11, Gender:"boy"}
// 可以如此简写, 但必须注意参数顺序的一一对应.
s2 := Student{"Rose", 12, "girl"}
s3 := new (Student)
fmt.Println(s1.Name, s2.Gender, s3.Age) // 值为 Mike, girl, 0
s3 := struct {
Name string
Age int
Gender string
}{"Robert", "boy", 13}
注意: 匿名结构体无法定义方法.
因为为结构体定义方法要在结构体的外部. 如下:
type Student struct {
Name string
Age int
Gender string
}
// * 是指向 Student 结构体类型
func (s *Student) fmtName() {
fmt.Println(s.Name)
}
func main() {
s3 := new (Student)
s3.Name = "王二狗"
s3.fmtName()
}
直接看代码:
// 定义结构体 Person
type Person struct {
Country string
}
// 定义结构体 Student 并继承 Person
type Student struct {
Name string
Age int
Gender string
Person // 继承 Person 所有属性与方法
}
// * 是指向 Student 结构体类型
func (s *Student) fmtName() {
fmt.Println(s.Name)
}
// 打印国家
func (p *Person) fmtCountry() {
fmt.Println(p.Country)
}
func main() {
s1 := new (Student)
// 这是 Person 的属性与方法
s1.Country = "中国"
s1.fmtCountry()
// 这是 Student 的属性与方法
s1.Name = "王二狗"
s1.fmtName()
}
接口是用来保存共性方法的容器. 看如下代码定义:
// 定义接口
type Skill interface {
Say() string
Eat()
}
// 定义结构体 Person
type Person struct {
// 属性
Country string
}
// 定义结构体 Student 并继承 Person
type Student struct {
Name string
Age int
Gender string
Person // 继承 Person 所有属性与方法
}
// 实现接口方法
func (s Student) Say() string{
fmt.Println("我叫" + s.Name + ",我来自" + s.Country)
return "over"
}
func (s Student) Eat() {
fmt.Println("我吃茶叶蛋")
}
func main() {
s1 := new (Student)
// 这是 Person 的属性与方法
s1.Country = "中国"
s1.fmtCountry()
// 这是 Student 的属性与方法
s1.Name = "王二狗"
msg := s1.Say()
fmt.Println(msg)
s1.Eat()
}
指针涉及到两个操作符 &
和 *
, &
代表 取址符 *
代表 声明符 / 取值符
取址符 指的是取得内存地址的意思. 例如:
var a int = 1
fmt.Println("变量a的内存地址是: " , &a)
输出结果为:
// 变量a的内存地址是: 0xc00004a090
声明符 在使用指针时需要为指针值声明一个类型, 这个类型就是用 声明符 来定义的. 例如:
// 声明指针变量p 指针类型为int
var p *int
取值符 使用指针变量获取指针值
// 获取指针值
var b = *p
例子演示 :
// 定义变量a并赋值
var a int = 1
// 声明指针变量p 指针类型为int
var p *int
// 获取变量a的内存地址赋值给p
p = &a
fmt.Println("变量a的内存地址是: " , &a)
fmt.Println("指针变量p的存储地址是: " , p)
fmt.Println("指针变量p的值是: " , *p)
注意: 这里 p 的存储地址与内存地址是有区别的, 我们可以把 p 看做是一个容器, 存储了一个 a 的内存地址, 而 p 本身也是指向一个内存地址的, 所以如果使用 &p
这种写法的话就会得到 p 本身所指向的内存地址.而不是储存的 a 的地址.
使用下面这种方式声明:
const len int = 3
var ptr [len]*int
实例:
func main() {
// 定义常量长度
const len int = 3;
// 定义指针数组
var ptr [len]*int
// 定义整数数组
arr := []int{1, 2, 3}
var i int
for i = 0; i < len; i ++{
// 把数组值的内存地址储存到指针数组中
ptr[i] = &arr[i]
}
// 遍历打印
for i = 0; i < len; i++ {
fmt.Printf("a[%d] = %d\n", i, *ptr[i] )
}
}
打印结果为:
a[0] = 1
a[1] = 2
a[2] = 3
顾名思义, 指针也可以储存另一个指针的地址. 定义方式如下:
var ptr **int;
实例:
func main() {
// 定义变量a
var a int = 1
// 定义指针
var ptr *int
// 定义指向指针的指针
var pptr **int
// 定义指针 ptr 地址
ptr = &a
// 定义指针 pptr 地址
pptr = &ptr
fmt.Printf("指针变量 *ptr = %d\n", *ptr )
fmt.Println("指针变量 *pptr = ", *pptr )
}
打印结果如下:
指针变量 *ptr = 1
指针变量 *pptr = 0xc00004a068
各种判断选择语句
import (
"fmt"
"math/rand"
"time"
)
func main() {
// 程序重启后,产生的随机数和上次一样。是因为程序使用了相同的种子, 使用rand.Seed(seed)来设置一个不同的种子值。
// 利用当前时间的UNIX时间戳初始化rand包
rand.Seed(time.Now().UnixNano())
// 定义变量a,b 并用随机数赋值
var a int = rand.Intn(100)
var b int = rand.Intn(100)
// 使用if判断两个数值
if a > b {
fmt.Printf("结果是 a (%d) > b (%d)", a, b)
} else if b > a {
fmt.Printf("结果是 b (%d) > a (%d)", b, a)
}
}
也可以使用这种方式
func main() {
// 定义变量 i
var i int
// 判断是否小于 100
if i := 1; i < 100 {
fmt.Printf("结果是 i (%d) < 100 \n", i)
}
// 打印 i 的值
fmt.Printf("i = %d", i)
}
打印结果:
结果是 i (1) < 100
i = 0
由此可得出结论, 在 if 中定义的 变量 i
是与外部定义的变量 i
是相互独立的.
func main() {
// 定义变量 s
s := "golang"
switch s {
case "java" :
fmt.Println("使用的语言是java")
case "PHP" :
fmt.Println("使用的语言是PHP")
case "golang" :
fmt.Println("使用的语言是golang")
default :
fmt.Println("不是匹配的语言")
}
}
与 if 相同, 也可以这么定义:
func main() {
// 定义变量 s
arr := []string{"java", "golang", "PHP", "Python"}
switch s := arr[3]; s {
case "java" :
fmt.Println("使用的语言是java")
case "PHP" :
fmt.Println("使用的语言是PHP")
case "golang" :
fmt.Println("使用的语言是golang")
default :
fmt.Printf("%v不是匹配的语言", s)
}
}
打印的结果为
Python不是匹配的语言变量
类型switch 判断类型的语句.
func main() {
// 定义变量 i
v := 1
// 使用类型 switch 判断
switch i := interface{}(v).(type) {
case int, int8, int16, int32, int64:
fmt.Printf("变量 %d 的类型为 %T. \n", i, v)
case uint, uint8, uint16, uint32, uint64:
fmt.Printf("变量 %d 的类型为 %T. \n", i, v)
default:
fmt.Printf("%v不是匹配的类型", i)
}
}
打印的结果为
变量 1 的类型为 int.
// 定义条件循环.
for i := 0; i < 10; i++ {
fmt.Print(i, " ")
}
// 定义死循环
for {
fmt.Print("abc")
}
特殊的范围表达式, 用法如下:
func main() {
// 定义数组
arrs := []int{1, 2, 3 ,4}
// 使用 range 表达式会返回一个 key, value 形式的返回值
for k , v := range arrs {
fmt.Printf("key = %d, value = %d \n", k, v)
}
}
打印结果为 :
key = 0, value = 1
key = 1, value = 2
key = 2, value = 3
key = 3, value = 4
再举几个栗子 :
栗子1
// 如果只想获取值, 可使用空白符 _ 代表不接收 key
for _ , v := range arrs {
fmt.Printf("value = %d \n", v)
}
value = 1
value = 2
value = 3
value = 4
栗子2
// 遍历字符串
for k, v := range "go语言"{
fmt.Printf("key = %d, value = %c\n", k , v)
}
key = 0, value = g
key = 1, value = o
key = 2, value = 语
key = 5, value = 言
注意
: key 为何不是连续的? 因为一个中文字符在经过UTF-8编码之后会表现为三个字节。如下图:
select
语句类似于 switch
语句,但是 select
会随机执行一个可运行的 case
。如果没有 case
可运行,它将阻塞,直到有 case
可运行.
select 语句的语法特点:
case
都必须是一个通信channel
表达式都会被求值case
都可以运行,select
会随机公平地选出一个执行。其他不会执行。否则:
default
子句,则执行该语句。default
字句,select
将阻塞,直到某个通信可以运行;Go 不会重新对 channel
或值进行求值。栗子1
// 测试select是否随机执行case
for i := 0 ; i < 5 ; i++ {
c1 := make(chan int , 1)
c1 <- 1
c2 := make(chan int , 1)
c3 := make(chan int , 1)
c3 <- 3
var i1, i2 int
select {
case i1 = <- c1 :
fmt.Printf("i1 = %d \n", i1)
case c2 <- i2 :
fmt.Printf("发送 i2 (%d) 到通道 c2\n", i2)
case i3, ok := <- c3 :
if ok{
fmt.Printf("i3 = %d \n", i3)
} else {
fmt.Printf("c3 is closed")
}
default:
fmt.Printf("---------当前没有通信--------\n\n")
}
}
打印结果 :
发送 i2 (0) 到通道 c2
i1 = 1
i3 = 3
发送 i2 (0) 到通道 c2
发送 i2 (0) 到通道 c2
栗子2
// 测试select是否在匹配完所有合适的case后进入默认语句
for i := 0 ; i < 3 ; i++ {
c1 := make(chan int , 1)
c1 <- 1
c2 := make(chan int , 1)
c3 := make(chan int , 1)
c3 <- 3
var i1, i2 int
for i := 0 ; i < 4 ; i++ {
select {
case i1 = <- c1 :
fmt.Printf("i1 = %d \n", i1)
case c2 <- i2 :
fmt.Printf("发送 i2 (%d) 到通道 c2\n", i2)
case i3, ok := <- c3 :
if ok{
fmt.Printf("i3 = %d \n", i3)
} else {
fmt.Printf("c3 is closed")
}
default:
fmt.Printf("---------当前没有通信--------\n")
}
}
}
打印结果 :
发送 i2 (0) 到通道 c2
i1 = 1
i3 = 3
---------当前没有通信--------
i1 = 1
i3 = 3
发送 i2 (0) 到通道 c2
---------当前没有通信--------
发送 i2 (0) 到通道 c2
i3 = 3
i1 = 1
---------当前没有通信--------