转自 https://www.cnblogs.com/xhen/p/11106776.html
go学习笔记
0、值类型:变量直接存储值,内存通常在栈中分配:int、float、bool、string以及数组和struct
引用类型:变量存储的是一个地址,这个地址存储最终的值。内存通常在堆上分配。通过gc回收:指针、slice、map、chan等都是引用类型
使用&结构体{}就相当与使用new实例化了一次结构体
1、变量(或常量)包含数据,这些数据可以有不同的数据类型,简称类型。使用 var 声明的变量的值会自动初始化为该类型的零值。类型定义了某个变量的值的集合与可对其进行操作的集合。
类型可以是基本类型,如:int、float、bool、string;结构化的(复合的),如:struct、array、slice、map、channel;只描述类型的行为的,如:interface。
结构化的类型没有真正的值,它使用 nil 作为默认值(在 Objective-C 中是 nil,在 Java 中是 null,在 C 和 C++ 中是NULL或 0)。值得注意的是,Go 语言中不存在类型继承。
比如:定义变量的两种方式 var i int=15 或 i :=15
定义数组:arr1 :=[] int { } 数组名称 数组长度 数组里面的类型 数组值
2、如果设定的全局变量希望被外部的包使用,其首字母应该大写。
3、字符串:Strings.HasPrefix(string,prefix) 字符串是否以xx开头 Strings.HasSuffix(string,suffix) 字符串是否以xx结尾 eg:str="this is a example" String.HasPrefix(str,"th");]
4、时间和日期:获取当前时间:time.Now(),获取当前年份:time.Now().Year() 获取当前月份:time.Now().Month() 获取当前天:time.Now().Day()
5、代码块: a ,err :=strconv.Itoa(an) 注意这里的error是为了防止转换失败 if !err { fmt.Printf("输出成功:%d”,a)}
6、定义函数 func 函数名称 参数类型 返回参数值 eg:
1
2
3
4
5
6
|
func
func1(s string) (n int, err error) {
defer
func
() {
log.Printf(
"func1(%q) = %d, %v"
, s, n, err)
}()
return
7, io.EOF
}
|
7、利用闭包来调试函数:
1
2
3
4
5
6
7
8
9
|
where :=
func
() {
_, file, line, _ := runtime.Caller(1)
log.Printf(
"%s:%d"
, file, line)
}
where()
// some code
where()
// some more code
where()
|
8、记算代码执行时间:
1
2
3
4
5
|
start := time.Now()
longCalculation()
end := time.Now()
delta := end.Sub(start)
fmt.Printf(
"longCalculation took this amount of time: %s\n"
, delta)
|
9、使用new创建得数组和普通创建数组得不同
1
2
3
4
5
6
7
8
9
10
11
12
|
var
arr1=new([5]int)
for
i:=0;i<len(arr1);i++{
arr1[i]=1+i
}
var
arr2= *arr1
arr2[0]=7
for
i,_ :=
range
arr1{
fmt.Printf(
"this value is %d"
,arr1[i])
}
//注意使用new([5]int创建得数组是带有地址的 而普通创建的没有
//例如:使用new创建的类型是 *[5]int,而 使用var arr1=[5]int创建的类型是 [5]int。
//var arr2=*arr1 如果不带指针符号则修改arr2的值会使得arr1的值也改变
|
10、将一个大数组传递给函数会消耗很多内存,有两种方法可以避免:①传递数组的指针 ②使用数组的切片
11、切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此千万不能指针指向切片,因为它自己就是一个指针)
声明切片的格式是: var identifier []type
(不需要说明长度)
切片的初始化格式是:var slice1 []type = arr1[start:end]
还可以使用make创建:var slice1 []int = make([]int, 10)
var slice1 []type = arr1[:]
那么 slice1 就等于完整的 arr1 数组(所以这种表示方式是 arr1[0:len(arr1)]
的一种缩写)。另外一种表述方式是:slice1 = &arr1
。
arr1[2:]
和 arr1[2:len(arr1)]
相同,都包含了数组从第三个到最后的所有元素。
arr1[:3]
和 arr1[0:3]
相同,包含了从第一个到第三个元素(不包括第四个)。
如果你想去掉 slice1 的最后一个元素,只要 slice1 = slice1[:len(slice1)-1]
。
一个由数字 1、2、3 组成的切片可以这么生成:s := [3]int{1,2,3}[:]
(注: 应先用s := [3]int{1, 2, 3}
生成数组, 再使用s[:]
转成切片) 甚至更简单的 s := []int{1,2,3}
。
s2 := s[:]
是用切片组成的切片,拥有相同的元素,但是仍然指向相同的相关数组。
一个切片 s 可以这样扩展到它的大小上限:s = s[:cap(s)]
,如果再扩大的话就会导致运行时错误
12、数组与切片的应用:可以直接通过 c := []byte(s)
来获取一个字节的切片 c。另外,您还可以通过 copy 函数来达到相同的目的:copy(dst []byte, src string)
。可以使用 c := []int32(s)
语法,这样切片中的每个 int 都会包含对应的 Unicode 代码,因为字符串中的每次字符都会对应一个整数。len([]int32(s))
来获得字符串中字符的数量,但使用 utf8.RuneCountInString(s)
效率会更高一点。
13、将一个字符串添加到一个字节切片尾部:
var b []byte var s string b = append(b, s...)
14、获取字符串的某一部分:substr := str[start:end]
可以从字符串 str 获取到从索引 start 开始到 end-1
位置的子字符串。go中字符串是不可变的,srt[index]这种事不可以放在等号左侧的,如果想要修改字符串,就必须先把字符串转化为字符串切片,然后在进行变换,代码如下:
1
2
3
4
|
s :=
"hello"
c := []byte(s)
c[0] =
'c'
s2 := string(c)
// s2 == "cello"
|
15、对数组或者切片进行排序:可以使用sort包中的函数,sort.Ints(arr)来对int类型的数组或切片进行排序。为了检查某个数组是否已经被排序,可以通过函数 IntsAreSorted(a []int) bool
来检查,如果返回 true 则表示已经被排序。类似的可以使用sort.Float64s来对Float类型的数组或切片进行排序,使用sort.Strings对字符串类型的数组或切片进行排序。
16、对数组或切片进行搜索:该数组或切片必须先被排序(因为标准库的搜索算法使用的是二分法)。然后,可以使用函数 sort.SearchInts(a []int, n int),类似的还可以使用sort.
SearchFloat64s(a []int, n int),sort.
SearchStrings(a []int, n int)来对不同类型的数组或切片进行排序。
17、map类型:map是引用类型,可以使用make来声明(尽量使用make来声明),不能用new来声明否则的到的是一个空引用的指针。处于性能的考虑对于大容量的map和容易快速扩张的数组,也尽量规定它的容量。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
map2 := make(
map
[string]float32, 100)<br>
//定义值为整形,键为字符型的map<br>tmp := make(map[string]int)<br>
//下面代码证明了map的值可以是任意类型的
package
main
import
"fmt"
func
main() {
mf :=
map
[int]
func
() int{
1:
func
() int {
return
10 },
2:
func
() int {
return
20 },
5:
func
() int {
return
50 },
}
fmt.Println(mf)
}
|
18、在map中如果key不存在当 value1 := tmp[key]时,如果key不存在会返回空,如果它本来设置得就是空就无法区分是否存在该key,所以可以使用value,ispresent :=tmp[key] 如果存在则ispresent=true,如果不存在则ispresent=false。可以直接从map使用delete(map,key)删除某个key。map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序,所以遍历map时你会发现它得输出时随机的。
19、map类型的切片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package
main
import
"fmt"
func
main(){
//map类型的切片
//第一种定义值得方式Version A
items :=make([]
map
[int]int,5)
for
i,_ :=
range
items{
items[i] = make(
map
[int]int,1)
//定义items的第一个元素是一个切片
items[i][1]=2
}
fmt.Printf(
"version B:value of items:%v\n"
,items)
//第二种定义值得方式Version B
items2 := make([]
map
[int]int,5)
for
_,items :=
range
items2{
items =make(
map
[int]int)
items[1]=2
}
fmt.Println(items2)
}
//应当像 A 版本那样通过索引使用切片的 map 元素。
//在 B 版本中获得的项只是 map 值的一个拷贝而已,
//所以真正的 map 元素没有得到初始化。
|
20、对map类型进行排序:因为map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序。为了达到这个效果需要将key保存到一个切片,再对切片进行排序。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package
main
import
(
"fmt"
"sort"
)
var
(
sortMap =
map
[string]int{
"cherry"
:4,
"apple"
:3,
"lemon"
:9,
"banana"
:5,
"durain"
:1}
)
func
main(){
fmt.Println(
"this is unsort map"
)
for
key,value :=
range
sortMap{
fmt.Printf(
"this key is:%v,this value is :%v \n"
,key,value)
}
fmt.Println(
"this is sort map"
)
keys :=make([]string,len(sortMap))
i :=0
for
key :=
range
sortMap{
keys[i]=key
i++
}
sort.Strings(keys)
for
_,value :=
range
keys{
fmt.Printf(
"this key is:%v,this value is:%v \n"
,value,sortMap[value])
}
}
|
21、strconv包的使用:
①字符串转整型,整型转字符串:这是最常见的两种,Atoi(string to int)和 Itoa(int to string)。eg:strconv.Atoi("-42") strconv.Itoa(-42)
②ParseBool,ParseFloat,ParseInt 和 ParseUint 将字符串转换为值 eg: b, err := strconv.ParseBool("true")f, err := strconv.ParseFloat("3.1415", 64)i, err := strconv.ParseInt("-42", 10, 64)u, err := strconv.ParseUint("42", 10, 64)
③FormatBool,FormatFloat,FormatInt 和 FormatUint将值转换为字符串 eg: s := strconv.FormatBool(true)s := strconv.FormatFloat(3.1415, 'E', -1, 64)s := strconv.FormatInt(-42, 16)s := strconv.FormatUint(42, 16)
22、结构体:类似于面向对象编程中无法方法的类一样,因为go中没有类这个概念,所以结构体在go语言中显得尤为重要。以下是一个定义简单结构体的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package
main
import
"fmt"
type
struct1
struct
{
a int
b float32
c string
}
function main(){
s1 := new(sruct1)
s1.a=1
s1.b=3.1
s1.c =
"hello world"
fmt.Printf(
"s1.a=$d"
,s1.a)
fmt.Printf(
"s1.b=$f"
,s1.b)
fmt.Printf(
"s1.c=$s"
,s1.c)<br> fmt.Println(s1)
} <br>
//以下是输出结果<br>s1.a=1 s1.b=3.1 s1.c=hello world &{1 3.1,hello world}
|
可以通过更简短和惯用的方式来比如:
1
2
3
4
|
var
s1 = &struct1{1,3.1,
"hello world"
}
或
var
s1 struct1
s1 :=struct1{1,3.1,
"hello world"
}
|
23、结构体工厂:在 Go 语言中常常像下面这样在工厂方法里使用初始化来简便的实现构造函数
1
2
3
4
5
6
7
8
9
10
11
12
|
type
File
struct
{
type
int
name string
}
fun NewFile(fid int name string){
if
fid<0 {
return
nil
}
return
&File{fid,name}
}<br><br>f :=NewFile(2,
"1.text"
)
|
24、结构体中的继承:Go 语言中的继承是通过内嵌或组合来实现的,结构体可以包含一个或多个 匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型就是字段的名字。匿名字段本身可以是一个结构体类型,即 结构体可以包含内嵌结构体。(这里得注意:在一个结构体中对于每一种数据类型只能有一个匿名字段)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package
main
import
"fmt"
type
father
struct
{
firstname string
homename string
}
type
son
struct
{
lastname string
int
//匿名字段
string
//匿名字段
father
//内嵌结构体,就相当于继承
}
func
main(){
xiaoming :=new(son)
xiaoming.firstname=
"xiao"
xiaoming.lastname=
"ming"
xiaoming.homename=
"jiangxi"
xiaoming.int=13
xiaoming.string=
"xm"
fmt.Printf(
"xiaoming.fastname=%s\n"
,xiaoming.firstname)
fmt.Printf(
"xiaoming.lastname=%s\n"
,xiaoming.lastname)
fmt.Printf(
"xiaoming.homename=%s\n"
,xiaoming.homename)
fmt.Printf(
"xiaoming.int=%d\n"
,xiaoming.int)
fmt.Printf(
"xiaoming.string=%s\n"
,xiaoming.string)
fmt.Println(xiaoming)
}
|
25、结构体中的方法:Go 方法是作用在接收者(receiver)上的一个函数,接收者是某种类型的变量。因此方法是一种特殊类型的函数。
接收者几乎可以是任何类型,不单单是结构体类型,任何类型都可以有方法,但是除了接口类型,接收者还不能是个指针类型。
Go中类型的代码和绑定在它类型中的方法代码不能放置在一起,可以存在不同的源文件中,但是唯一的要求是必须在一个包里面。定义方法的一般格式:func (recv receiver_type) methodName(parameter_list) (return_value_list) { ... }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package
main
import
"fmt"
type
instance
struct
{
//定义结构体
t1 int
t2 int
}
func
main(){
test := new(instance)
test.t1 = 1
test.t2 = 2
fmt.Printf(
"the sum is %d"
,test.addSum())
fmt.printf(
"the sum add user input param is %d"
,test.addParam(10))
}
func
(t *instance) addSum() int {
//结构体中的参数相加
return
t.t1+t.t2
}
func
(t *instance) addParam(param int) int{
//增加用户添加的参数
return
t.t1+t.t2+param
}
|
26、类型的String()方法:如果类型定义了 String()
方法,它会被用在 fmt.Printf()
中生成默认的输出:等同于使用格式化描述符 %v
产生的输出。还有 fmt.Print()
和 fmt.Println()
也会自动使用 String()
方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package
main
import
(
"fmt"
"strconv"
)
type
TwoInts
struct
{
a int
b int
}
func
main() {
two1 := new(TwoInts)
two1.a = 12
two1.b = 10
fmt.Printf(
"two1 is: %v\n"
, two1)
fmt.Println(
"two1 is:"
, two1)
fmt.Printf(
"two1 is: %T\n"
, two1)
fmt.Printf(
"two1 is: %#v\n"
, two1)
}
func
(tn *TwoInts) String() string {
return
"("
+ strconv.Itoa(tn.a) +
"/"
+ strconv.Itoa(tn.b) +
")"
}
//运行结果
two1 is: (12/10)
two1 is: (12/10)
two1 is: *main.TwoInts
two1 is: &main.TwoInts{a:12, b:10}
|
27、Go中的垃圾回收:在 Go 运行时中有一个独立的进程,即垃圾收集器(GC),会处理这些事情,它搜索不再使用的变量然后释放它们的内存。可以通过 runtime
包访问 GC 进程。调用 runtime.GC()
函数可以显式的触发 GC,但这只在某些罕见的场景下才有用,比如当内存资源不足时调用 runtime.GC()
,它会在此函数执行的点上立即释放一大片内存,此时程序可能会有短时的性能下降(因为 GC
进程在执行)。
1
2
3
4
5
6
7
|
//获取当前内存状态
var
m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf(
"%d Kb\n"
, m.Alloc / 1024)
//在一个对象 obj 被从内存移除前执行一些特殊操作
runtime.SetFinalizer(obj,
func
(obj *typeObj))
|
28、Go中的接口:接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。
(按照约定,只包含一个方法的)接口的名字由方法名加 [e]r
后缀组成,例如 Printer
、Reader
、Writer
、Logger
、Converter
等等。还有一些不常用的方式(当后缀 er
不合适时),比如 Recoverable
,此时接口名以 able
结尾,或者以 I
开头(像 .NET
或 Java
中那样)。
Go 语言中的接口都很简短,通常它们会包含 0 个、最多 3 个方法。
类型不需要显式声明它实现了某个接口:接口被隐式地实现。多个类型可以实现同一个接口。
实现某个接口的类型(除了实现接口方法外)可以有其他的方法。
一个类型可以实现多个接口。
接口类型可以包含一个实例的引用, 该实例的类型实现了此接口(接口是动态类型)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
package
main
import
"fmt"
type
Driver
interface
{
//定义接口
Driver() int
}
type
bike
struct
{
speed int
name string
}
type
train
struct
{
speed int
name string
}
func
(b bike) Driver() int{
//实现接口方法
return
b.speed*2
}
func
(t *train) Driver() int{
//实现接口方法
return
t.speed*5
}
func
main(){
// bikes :=&bike{10,"bike"}
// trains :=&train{40,"train"}
// cars :=[]Driver{bikes,trains} //声明是实现了方法
// for i,_ :=range cars{
// fmt.Printf("the speed is %d \n",cars[i].Driver())
// }
var
o Driver =bike{10,
"bike"
}
//这是另一种简约声明方式
fmt.Printf(
"the speed is %d \n"
,o.Driver())
o =&train{40,
"train"
}
fmt.Printf(
"the speed is %d \n"
,o.Driver())
}
|
29、检测和转换接口变量的类型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
package
main
import
(
"fmt"
"math"
)
type
Square
struct
{
side float32
}
type
Circle
struct
{
radius float32
}
type
Shaper
interface
{
Area() float32
}
func
main() {
var
areaIntf Shaper
sq1 := new(Square)
sq1.side = 5
areaIntf = sq1
// Is Square the type of areaIntf?
if
t, ok := areaIntf.(*Square); ok {
fmt.Printf(
"The type of areaIntf is: %T\n"
, t)
}
if
u, ok := areaIntf.(*Circle); ok {
fmt.Printf(
"The type of areaIntf is: %T\n"
, u)
}
else
{
fmt.Println(
"areaIntf does not contain a variable of type Circle"
)
}
}
func
(sq *Square) Area() float32 {
return
sq.side * sq.side
}
func
(ci *Circle) Area() float32 {
return
ci.radius * ci.radius * math.Pi
}
|
30、测定某个值是否实现了接口:v
是一个值,然后我们想测试它是否实现了 Stringer
接口
1
2
3
4
5
6
7
|
type
Stringer
interface
{
String() string
}
if
sv, ok := v.(Stringer); ok {
fmt.Printf(
"v implements String(): %s\n"
, sv.String())
// note: sv, not v
}
|
31、Go中的排序:对于一般性的排序,sort
包定义了一个接口
1
2
3
4
5
|
type
Interface
interface
{
Len() int
Less(i, j int) bool
Swap(i, j int)
}
|
其中的两个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package
sort
type
Sorter
interface
{
Len() int
Less(i, j int) bool
Swap(i, j int)
}
func
Sort(data Sorter) {
for
pass := 1; pass < data.Len(); pass++ {
for
i := 0; i < data.Len()-pass; i++ {
if
data.Less(i+1, i) {
data.Swap(i, i+1)
}
}
}
}
func
IsSorted(data Sorter) bool {
n := data.Len()
for
i := n - 1; i > 0; i-- {
if
data.Less(i, i-1) {
return
false
}
}
return
true
}
// Convenience types for common cases
type
IntArray []int
func
(p IntArray) Len() int {
return
len(p) }
func
(p IntArray) Less(i, j int) bool {
return
p[i] < p[j] }
func
(p IntArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
type
StringArray []string
func
(p StringArray) Len() int {
return
len(p) }
func
(p StringArray) Less(i, j int) bool {
return
p[i] < p[j] }
func
(p StringArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// Convenience wrappers for common cases
func
SortInts(a []int) { Sort(IntArray(a)) }
func
SortStrings(a []string) { Sort(StringArray(a)) }
func
IntsAreSorted(a []int) bool {
return
IsSorted(IntArray(a)) }
func
StringsAreSorted(a []string) bool {
return
IsSorted(StringArray(a)) }
|
排序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
package
main
import
(
"./sort"
"fmt"
)
func
ints() {
data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
a := sort.IntArray(data)
//conversion to type IntArray
sort.Sort(a)
if
!sort.IsSorted(a) {
panic(
"fails"
)
}
fmt.Printf(
"The sorted array is: %v\n"
, a)
}
func
strings() {
data := []string{
"monday"
,
"friday"
,
"tuesday"
,
"wednesday"
,
"sunday"
,
"thursday"
,
""
,
"saturday"
}
a := sort.StringArray(data)
sort.Sort(a)
if
!sort.IsSorted(a) {
panic(
"fail"
)
}
fmt.Printf(
"The sorted array is: %v\n"
, a)
}
type
day
struct
{
num int
shortName string
longName string
}
type
dayArray
struct
{
data []*day
}
func
(p *dayArray) Len() int {
return
len(p.data) }
func
(p *dayArray) Less(i, j int) bool {
return
p.data[i].num < p.data[j].num }
func
(p *dayArray) Swap(i, j int) { p.data[i], p.data[j] = p.data[j], p.data[i] }
func
days() {
Sunday := day{0,
"SUN"
,
"Sunday"
}
Monday := day{1,
"MON"
,
"Monday"
}
Tuesday := day{2,
"TUE"
,
"Tuesday"
}
Wednesday := day{3,
"WED"
,
"Wednesday"
}
Thursday := day{4,
"THU"
,
"Thursday"
}
Friday := day{5,
"FRI"
,
"Friday"
}
Saturday := day{6,
"SAT"
,
"Saturday"
}
data := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday}
a := dayArray{data}
sort.Sort(&a)
if
!sort.IsSorted(&a) {
panic(
"fail"
)
}
for
_, d :=
range
data {
fmt.Printf(
"%s "
, d.longName)
}
fmt.Printf(
"\n"
)
}
func
main() {
ints()
strings()
days()
}
|
32、Go中的读和写:io包中提供了读和写的接口
1
2
3
4
5
6
7
|
type
Reader
interface
{
Read(p []byte) (n int, err error)
}
type
Writer
interface
{
Write(p []byte) (n int, err error)
}
|
33、空接口:空接口或则最小接口不包含任何方法,它对实现没有任何要求。可以给一个空接口类型的变量 var val interface {}
赋任何类型的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
package
main
import
"fmt"
var
i = 5
var
str =
"ABC"
type
Person
struct
{
name string
age int
}
type
Any
interface
{}
func
main() {
var
val Any
val = 5
fmt.Printf(
"val has the value: %v\n"
, val)
val = str
fmt.Printf(
"val has the value: %v\n"
, val)
pers1 := new(Person)
pers1.name =
"Rob Pike"
pers1.age = 55
val = pers1
fmt.Printf(
"val has the value: %v\n"
, val)
switch
t := val.(
type
) {
case
int:
fmt.Printf(
"Type int %T\n"
, t)
case
string:
fmt.Printf(
"Type string %T\n"
, t)
case
bool:
fmt.Printf(
"Type boolean %T\n"
, t)
case
*Person:
fmt.Printf(
"Type pointer to Person %T\n"
, t)
default
:
fmt.Printf(
"Unexpected type %T"
, t)
}
}
//输出
val has the value: 5
val has the value: ABC
val has the value: &{Rob Pike 55}
Type pointer to Person *main.Person
|
34、Go中对变量类型的判断以及获取变量的值:引入reflect 包,使用reflect.TypeOf(变量) 判断变量的类型 使用reflect.ValueOf(变量) 获取变量的值。(了解更多:https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/11.10.md)
35、遍历出结构体中的字段值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
package
main
import
(
"fmt"
"reflect"
)
type
NotknownType
struct
{
s1, s2, s3 string
}
func
(n NotknownType) String() string {
return
n.s1 +
" - "
+ n.s2 +
" - "
+ n.s3
}
// variable to investigate:
var
secret
interface
{} = NotknownType{
"Ada"
,
"Go"
,
"Oberon"
}
func
main() {
value := reflect.ValueOf(secret)
// <main.NotknownType Value>
typ := reflect.TypeOf(secret)
// main.NotknownType
// alternative:
//typ := value.Type() // main.NotknownType
fmt.Println(typ)
knd := value.Kind()
// struct
fmt.Println(knd)
// iterate through the fields of the struct:
for
i := 0; i < value.NumField(); i++ {
fmt.Printf(
"Field %d: %v\n"
, i, value.Field(i))
// error: panic: reflect.Value.SetString using value obtained using unexported field
//value.Field(i).SetString("C#")
}
// call the first method, which is String():
results := value.Method(0).Call(nil)
fmt.Println(results)
// [Ada - Go - Oberon]
}
//结果
main.NotknownType
struct
Field 0: Ada
Field 1: Go
Field 2: Oberon
[Ada - Go - Oberon]
|
36、接口的继承:当一个类型包含(内嵌)另一个类型(实现了一个或多个接口)的指针时,这个类型就可以使用(另一个类型)所有的接口方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
type
Task
struct
{
Command string
*log.Logger
}
//这个类型的工厂方法像这样
func
NewTask(command string, logger *log.Logger) *Task {
return
&Task{command, logger}
}
//当 log.Logger 实现了 Log() 方法后,Task 的实例 task 就可以调用该方法
task.Log()
//类型可以通过继承多个接口来提供像 多重继承 一样的特性
type
ReaderWriter
struct
{
*io.Reader
*io.Writer
}
|
37、Go中的封装、继承、多态:
-
封装(数据隐藏):和别的 OO 语言有 4 个或更多的访问层次相比,Go 把它简化为了 2 层
1)包范围内的:通过标识符首字母小写,
对象
只在它所在的包内可见2)可导出的:通过标识符首字母大写,
对象
对所在包以外也可见类型只拥有自己所在包中定义的方法。
- 继承:用组合实现:内嵌一个(或多个)包含想要的行为(字段和方法)的类型;多重继承可以通过内嵌多个类型实现
- 多态:用接口实现:某个类型的实例可以赋给它所实现的任意接口类型的变量。类型和接口是松耦合的,并且多重继承可以通过实现多个接口实现。Go 接口不是 Java 和 C# 接口的变体,而且接口间是不相关的,并且是大规模编程和可适应的演进型设计的关键。
38、Sscanf()函数的使用:从一大长串的字符串截取我们需要的数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package
main
import
"fmt"
var
(
s string
i int
f float32
input =
"56/39.45/Go"
format =
"%d/%f/%s"
//规定想要数据的格式
)
func
main(){
fmt.Sscanf(input,format,&i,&f,&s)
//把对应数据的信息赋值给所对应变量
fmt.Printf(
"i=%d,f=%f,s=%s"
,i,f,s)
}
|
39、 使用bufio
包提供的缓冲读取(buffered reader)来读取数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package
main
import
(
"fmt"
"bufio"
"os"
)
var
readerInput *bufio.Reader
var
input string
var
err error
func
main(){
readerInput = bufio.NewReader(os.Stdin)
//创建一个读取器并将它与标准输入绑定
fmt.Println(
"please enter you input:"
)
input,err = readerInput.ReadString(
'\n'
)
//这是读取器中的一个方法,该方法从输入中读取内容,直到碰到 delim 指定的字符,然后将读取到的内容连同 delim 字符一起放到缓冲区。
if
err==nil{
fmt.Printf(
"the user input is:%s\n"
,input)
//输出用户读取的内容
}
}
|
40、读文件:文件使用指向 os.File
类型的指针来表示的,也叫做文件句柄。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package
main
import
(
"fmt"
"os"
"bufio"
"io"
)
func
main(){
content,err :=os.Open(
"1.html"
)
//打开文件
if
err!=nil {
fmt.Printf(
"maybe your file not exist or you dont't have the pormission which open the file"
)
return
}
defer
content.Close()
//在文件末尾关闭文件
fileContent:=bufio.NewReader(content)
//创建一个读取器并将它与文件读取内容绑定
for
{
fileString,err :=fileContent.ReadString(
'\n'
)
//遍历读取内容时按照遇见换行符停止
fmt.Printf(
"the file content is :%s"
,fileString)
//输出文件内容
if
err==io.EOF{
return
}
}
}
|
41、写文件:OpenFile
函数有三个参数:文件名、一个或多个标志(使用逻辑运算符“|”连接),使用的文件权限。
os.O_RDONLY
:只读os.O_WRONLY
:只写os.O_CREATE
:创建:如果指定文件不存在,就创建该文件。os.O_TRUNC
:截断:如果指定文件已存在,就将该文件的长度截为0。12345678910111213141516171819202122232425262728package
main
import
(
"os"
"bufio"
"fmt"
)
func
main () {
// var outputWriter *bufio.Writer
// var outputFile *os.File
// var outputError os.Error
// var outputString string
outputFile, outputError := os.OpenFile(
"output.dat"
, os.O_WRONLY|os.O_CREATE, 0666)
if
outputError != nil {
fmt.Printf(
"An error occurred with file opening or creation\n"
)
return
}
defer
outputFile.Close()
outputWriter := bufio.NewWriter(outputFile)
outputString :=
"hello world!\n"
for
i:=0; i<10; i++ {
outputWriter.WriteString(outputString)
}
outputWriter.Flush()
}
42、文件拷贝。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package
main
import
(
"fmt"
"io"
"os"
)
func
main() {
CopyFile(
"target.txt"
,
"source.txt"
)
fmt.Println(
"Copy done!"
)
}
func
CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if
err != nil {
return
}
defer
src.Close()
dst, err := os.Create(dstName)
if
err != nil {
return
}
defer
dst.Close()
return
io.Copy(dst, src)
}
|
0、值类型:变量直接存储值,内存通常在栈中分配:int、float、bool、string以及数组和struct
引用类型:变量存储的是一个地址,这个地址存储最终的值。内存通常在堆上分配。通过gc回收:指针、slice、map、chan等都是引用类型
使用&结构体{}就相当与使用new实例化了一次结构体
1、变量(或常量)包含数据,这些数据可以有不同的数据类型,简称类型。使用 var 声明的变量的值会自动初始化为该类型的零值。类型定义了某个变量的值的集合与可对其进行操作的集合。
类型可以是基本类型,如:int、float、bool、string;结构化的(复合的),如:struct、array、slice、map、channel;只描述类型的行为的,如:interface。
结构化的类型没有真正的值,它使用 nil 作为默认值(在 Objective-C 中是 nil,在 Java 中是 null,在 C 和 C++ 中是NULL或 0)。值得注意的是,Go 语言中不存在类型继承。
比如:定义变量的两种方式 var i int=15 或 i :=15
定义数组:arr1 :=[] int { } 数组名称 数组长度 数组里面的类型 数组值
2、如果设定的全局变量希望被外部的包使用,其首字母应该大写。
3、字符串:Strings.HasPrefix(string,prefix) 字符串是否以xx开头 Strings.HasSuffix(string,suffix) 字符串是否以xx结尾 eg:str="this is a example" String.HasPrefix(str,"th");]
4、时间和日期:获取当前时间:time.Now(),获取当前年份:time.Now().Year() 获取当前月份:time.Now().Month() 获取当前天:time.Now().Day()
5、代码块: a ,err :=strconv.Itoa(an) 注意这里的error是为了防止转换失败 if !err { fmt.Printf("输出成功:%d”,a)}
6、定义函数 func 函数名称 参数类型 返回参数值 eg:
1
2
3
4
5
6
|
func
func1(s string) (n int, err error) {
defer
func
() {
log.Printf(
"func1(%q) = %d, %v"
, s, n, err)
}()
return
7, io.EOF
}
|
7、利用闭包来调试函数:
1
2
3
4
5
6
7
8
9
|
where :=
func
() {
_, file, line, _ := runtime.Caller(1)
log.Printf(
"%s:%d"
, file, line)
}
where()
// some code
where()
// some more code
where()
|
8、记算代码执行时间:
1
2
3
4
5
|
start := time.Now()
longCalculation()
end := time.Now()
delta := end.Sub(start)
fmt.Printf(
"longCalculation took this amount of time: %s\n"
, delta)
|
9、使用new创建得数组和普通创建数组得不同
1
2
3
4
5
6
7
8
9
10
11
12
|
var
arr1=new([5]int)
for
i:=0;i<len(arr1);i++{
arr1[i]=1+i
}
var
arr2= *arr1
arr2[0]=7
for
i,_ :=
range
arr1{
fmt.Printf(
"this value is %d"
,arr1[i])
}
//注意使用new([5]int创建得数组是带有地址的 而普通创建的没有
//例如:使用new创建的类型是 *[5]int,而 使用var arr1=[5]int创建的类型是 [5]int。
//var arr2=*arr1 如果不带指针符号则修改arr2的值会使得arr1的值也改变
|
10、将一个大数组传递给函数会消耗很多内存,有两种方法可以避免:①传递数组的指针 ②使用数组的切片
11、切片(slice)是对数组一个连续片段的引用(该数组我们称之为相关数组,通常是匿名的),所以切片是一个引用类型(因此千万不能指针指向切片,因为它自己就是一个指针)
声明切片的格式是: var identifier []type
(不需要说明长度)
切片的初始化格式是:var slice1 []type = arr1[start:end]
还可以使用make创建:var slice1 []int = make([]int, 10)
var slice1 []type = arr1[:]
那么 slice1 就等于完整的 arr1 数组(所以这种表示方式是 arr1[0:len(arr1)]
的一种缩写)。另外一种表述方式是:slice1 = &arr1
。
arr1[2:]
和 arr1[2:len(arr1)]
相同,都包含了数组从第三个到最后的所有元素。
arr1[:3]
和 arr1[0:3]
相同,包含了从第一个到第三个元素(不包括第四个)。
如果你想去掉 slice1 的最后一个元素,只要 slice1 = slice1[:len(slice1)-1]
。
一个由数字 1、2、3 组成的切片可以这么生成:s := [3]int{1,2,3}[:]
(注: 应先用s := [3]int{1, 2, 3}
生成数组, 再使用s[:]
转成切片) 甚至更简单的 s := []int{1,2,3}
。
s2 := s[:]
是用切片组成的切片,拥有相同的元素,但是仍然指向相同的相关数组。
一个切片 s 可以这样扩展到它的大小上限:s = s[:cap(s)]
,如果再扩大的话就会导致运行时错误
12、数组与切片的应用:可以直接通过 c := []byte(s)
来获取一个字节的切片 c。另外,您还可以通过 copy 函数来达到相同的目的:copy(dst []byte, src string)
。可以使用 c := []int32(s)
语法,这样切片中的每个 int 都会包含对应的 Unicode 代码,因为字符串中的每次字符都会对应一个整数。len([]int32(s))
来获得字符串中字符的数量,但使用 utf8.RuneCountInString(s)
效率会更高一点。
13、将一个字符串添加到一个字节切片尾部:
var b []byte var s string b = append(b, s...)
14、获取字符串的某一部分:substr := str[start:end]
可以从字符串 str 获取到从索引 start 开始到 end-1
位置的子字符串。go中字符串是不可变的,srt[index]这种事不可以放在等号左侧的,如果想要修改字符串,就必须先把字符串转化为字符串切片,然后在进行变换,代码如下:
1
2
3
4
|
s :=
"hello"
c := []byte(s)
c[0] =
'c'
s2 := string(c)
// s2 == "cello"
|
15、对数组或者切片进行排序:可以使用sort包中的函数,sort.Ints(arr)来对int类型的数组或切片进行排序。为了检查某个数组是否已经被排序,可以通过函数 IntsAreSorted(a []int) bool
来检查,如果返回 true 则表示已经被排序。类似的可以使用sort.Float64s来对Float类型的数组或切片进行排序,使用sort.Strings对字符串类型的数组或切片进行排序。
16、对数组或切片进行搜索:该数组或切片必须先被排序(因为标准库的搜索算法使用的是二分法)。然后,可以使用函数 sort.SearchInts(a []int, n int),类似的还可以使用sort.
SearchFloat64s(a []int, n int),sort.
SearchStrings(a []int, n int)来对不同类型的数组或切片进行排序。
17、map类型:map是引用类型,可以使用make来声明(尽量使用make来声明),不能用new来声明否则的到的是一个空引用的指针。处于性能的考虑对于大容量的map和容易快速扩张的数组,也尽量规定它的容量。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
map2 := make(
map
[string]float32, 100)<br>
//定义值为整形,键为字符型的map<br>tmp := make(map[string]int)<br>
//下面代码证明了map的值可以是任意类型的
package
main
import
"fmt"
func
main() {
mf :=
map
[int]
func
() int{
1:
func
() int {
return
10 },
2:
func
() int {
return
20 },
5:
func
() int {
return
50 },
}
fmt.Println(mf)
}
|
18、在map中如果key不存在当 value1 := tmp[key]时,如果key不存在会返回空,如果它本来设置得就是空就无法区分是否存在该key,所以可以使用value,ispresent :=tmp[key] 如果存在则ispresent=true,如果不存在则ispresent=false。可以直接从map使用delete(map,key)删除某个key。map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序,所以遍历map时你会发现它得输出时随机的。
19、map类型的切片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package
main
import
"fmt"
func
main(){
//map类型的切片
//第一种定义值得方式Version A
items :=make([]
map
[int]int,5)
for
i,_ :=
range
items{
items[i] = make(
map
[int]int,1)
//定义items的第一个元素是一个切片
items[i][1]=2
}
fmt.Printf(
"version B:value of items:%v\n"
,items)
//第二种定义值得方式Version B
items2 := make([]
map
[int]int,5)
for
_,items :=
range
items2{
items =make(
map
[int]int)
items[1]=2
}
fmt.Println(items2)
}
//应当像 A 版本那样通过索引使用切片的 map 元素。
//在 B 版本中获得的项只是 map 值的一个拷贝而已,
//所以真正的 map 元素没有得到初始化。
|
20、对map类型进行排序:因为map 默认是无序的,不管是按照 key 还是按照 value 默认都不排序。为了达到这个效果需要将key保存到一个切片,再对切片进行排序。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package
main
import
(
"fmt"
"sort"
)
var
(
sortMap =
map
[string]int{
"cherry"
:4,
"apple"
:3,
"lemon"
:9,
"banana"
:5,
"durain"
:1}
)
func
main(){
fmt.Println(
"this is unsort map"
)
for
key,value :=
range
sortMap{
fmt.Printf(
"this key is:%v,this value is :%v \n"
,key,value)
}
fmt.Println(
"this is sort map"
)
keys :=make([]string,len(sortMap))
i :=0
for
key :=
range
sortMap{
keys[i]=key
i++
}
sort.Strings(keys)
for
_,value :=
range
keys{
fmt.Printf(
"this key is:%v,this value is:%v \n"
,value,sortMap[value])
}
}
|
21、strconv包的使用:
①字符串转整型,整型转字符串:这是最常见的两种,Atoi(string to int)和 Itoa(int to string)。eg:strconv.Atoi("-42") strconv.Itoa(-42)
②ParseBool,ParseFloat,ParseInt 和 ParseUint 将字符串转换为值 eg: b, err := strconv.ParseBool("true")f, err := strconv.ParseFloat("3.1415", 64)i, err := strconv.ParseInt("-42", 10, 64)u, err := strconv.ParseUint("42", 10, 64)
③FormatBool,FormatFloat,FormatInt 和 FormatUint将值转换为字符串 eg: s := strconv.FormatBool(true)s := strconv.FormatFloat(3.1415, 'E', -1, 64)s := strconv.FormatInt(-42, 16)s := strconv.FormatUint(42, 16)
22、结构体:类似于面向对象编程中无法方法的类一样,因为go中没有类这个概念,所以结构体在go语言中显得尤为重要。以下是一个定义简单结构体的例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package
main
import
"fmt"
type
struct1
struct
{
a int
b float32
c string
}
function main(){
s1 := new(sruct1)
s1.a=1
s1.b=3.1
s1.c =
"hello world"
fmt.Printf(
"s1.a=$d"
,s1.a)
fmt.Printf(
"s1.b=$f"
,s1.b)
fmt.Printf(
"s1.c=$s"
,s1.c)<br> fmt.Println(s1)
} <br>
//以下是输出结果<br>s1.a=1 s1.b=3.1 s1.c=hello world &{1 3.1,hello world}
|
可以通过更简短和惯用的方式来比如:
1
2
3
4
|
var
s1 = &struct1{1,3.1,
"hello world"
}
或
var
s1 struct1
s1 :=struct1{1,3.1,
"hello world"
}
|
23、结构体工厂:在 Go 语言中常常像下面这样在工厂方法里使用初始化来简便的实现构造函数
1
2
3
4
5
6
7
8
9
10
11
12
|
type
File
struct
{
type
int
name string
}
fun NewFile(fid int name string){
if
fid<0 {
return
nil
}
return
&File{fid,name}
}<br><br>f :=NewFile(2,
"1.text"
)
|
24、结构体中的继承:Go 语言中的继承是通过内嵌或组合来实现的,结构体可以包含一个或多个 匿名(或内嵌)字段,即这些字段没有显式的名字,只有字段的类型是必须的,此时类型就是字段的名字。匿名字段本身可以是一个结构体类型,即 结构体可以包含内嵌结构体。(这里得注意:在一个结构体中对于每一种数据类型只能有一个匿名字段)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
package
main
import
"fmt"
type
father
struct
{
firstname string
homename string
}
type
son
struct
{
lastname string
int
//匿名字段
string
//匿名字段
father
//内嵌结构体,就相当于继承
}
func
main(){
xiaoming :=new(son)
xiaoming.firstname=
"xiao"
xiaoming.lastname=
"ming"
xiaoming.homename=
"jiangxi"
xiaoming.int=13
xiaoming.string=
"xm"
fmt.Printf(
"xiaoming.fastname=%s\n"
,xiaoming.firstname)
fmt.Printf(
"xiaoming.lastname=%s\n"
,xiaoming.lastname)
fmt.Printf(
"xiaoming.homename=%s\n"
,xiaoming.homename)
fmt.Printf(
"xiaoming.int=%d\n"
,xiaoming.int)
fmt.Printf(
"xiaoming.string=%s\n"
,xiaoming.string)
fmt.Println(xiaoming)
}
|
25、结构体中的方法:Go 方法是作用在接收者(receiver)上的一个函数,接收者是某种类型的变量。因此方法是一种特殊类型的函数。
接收者几乎可以是任何类型,不单单是结构体类型,任何类型都可以有方法,但是除了接口类型,接收者还不能是个指针类型。
Go中类型的代码和绑定在它类型中的方法代码不能放置在一起,可以存在不同的源文件中,但是唯一的要求是必须在一个包里面。定义方法的一般格式:func (recv receiver_type) methodName(parameter_list) (return_value_list) { ... }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package
main
import
"fmt"
type
instance
struct
{
//定义结构体
t1 int
t2 int
}
func
main(){
test := new(instance)
test.t1 = 1
test.t2 = 2
fmt.Printf(
"the sum is %d"
,test.addSum())
fmt.printf(
"the sum add user input param is %d"
,test.addParam(10))
}
func
(t *instance) addSum() int {
//结构体中的参数相加
return
t.t1+t.t2
}
func
(t *instance) addParam(param int) int{
//增加用户添加的参数
return
t.t1+t.t2+param
}
|
26、类型的String()方法:如果类型定义了 String()
方法,它会被用在 fmt.Printf()
中生成默认的输出:等同于使用格式化描述符 %v
产生的输出。还有 fmt.Print()
和 fmt.Println()
也会自动使用 String()
方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package
main
import
(
"fmt"
"strconv"
)
type
TwoInts
struct
{
a int
b int
}
func
main() {
two1 := new(TwoInts)
two1.a = 12
two1.b = 10
fmt.Printf(
"two1 is: %v\n"
, two1)
fmt.Println(
"two1 is:"
, two1)
fmt.Printf(
"two1 is: %T\n"
, two1)
fmt.Printf(
"two1 is: %#v\n"
, two1)
}
func
(tn *TwoInts) String() string {
return
"("
+ strconv.Itoa(tn.a) +
"/"
+ strconv.Itoa(tn.b) +
")"
}
//运行结果
two1 is: (12/10)
two1 is: (12/10)
two1 is: *main.TwoInts
two1 is: &main.TwoInts{a:12, b:10}
|
27、Go中的垃圾回收:在 Go 运行时中有一个独立的进程,即垃圾收集器(GC),会处理这些事情,它搜索不再使用的变量然后释放它们的内存。可以通过 runtime
包访问 GC 进程。调用 runtime.GC()
函数可以显式的触发 GC,但这只在某些罕见的场景下才有用,比如当内存资源不足时调用 runtime.GC()
,它会在此函数执行的点上立即释放一大片内存,此时程序可能会有短时的性能下降(因为 GC
进程在执行)。
1
2
3
4
5
6
7
|
//获取当前内存状态
var
m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf(
"%d Kb\n"
, m.Alloc / 1024)
//在一个对象 obj 被从内存移除前执行一些特殊操作
runtime.SetFinalizer(obj,
func
(obj *typeObj))
|
28、Go中的接口:接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。
(按照约定,只包含一个方法的)接口的名字由方法名加 [e]r
后缀组成,例如 Printer
、Reader
、Writer
、Logger
、Converter
等等。还有一些不常用的方式(当后缀 er
不合适时),比如 Recoverable
,此时接口名以 able
结尾,或者以 I
开头(像 .NET
或 Java
中那样)。
Go 语言中的接口都很简短,通常它们会包含 0 个、最多 3 个方法。
类型不需要显式声明它实现了某个接口:接口被隐式地实现。多个类型可以实现同一个接口。
实现某个接口的类型(除了实现接口方法外)可以有其他的方法。
一个类型可以实现多个接口。
接口类型可以包含一个实例的引用, 该实例的类型实现了此接口(接口是动态类型)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
package
main
import
"fmt"
type
Driver
interface
{
//定义接口
Driver() int
}
type
bike
struct
{
speed int
name string
}
type
train
struct
{
speed int
name string
}
func
(b bike) Driver() int{
//实现接口方法
return
b.speed*2
}
func
(t *train) Driver() int{
//实现接口方法
return
t.speed*5
}
func
main(){
// bikes :=&bike{10,"bike"}
// trains :=&train{40,"train"}
// cars :=[]Driver{bikes,trains} //声明是实现了方法
// for i,_ :=range cars{
// fmt.Printf("the speed is %d \n",cars[i].Driver())
// }
var
o Driver =bike{10,
"bike"
}
//这是另一种简约声明方式
fmt.Printf(
"the speed is %d \n"
,o.Driver())
o =&train{40,
"train"
}
fmt.Printf(
"the speed is %d \n"
,o.Driver())
}
|
29、检测和转换接口变量的类型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
package
main
import
(
"fmt"
"math"
)
type
Square
struct
{
side float32
}
type
Circle
struct
{
radius float32
}
type
Shaper
interface
{
Area() float32
}
func
main() {
var
areaIntf Shaper
sq1 := new(Square)
sq1.side = 5
areaIntf = sq1
// Is Square the type of areaIntf?
if
t, ok := areaIntf.(*Square); ok {
fmt.Printf(
"The type of areaIntf is: %T\n"
, t)
}
if
u, ok := areaIntf.(*Circle); ok {
fmt.Printf(
"The type of areaIntf is: %T\n"
, u)
}
else
{
fmt.Println(
"areaIntf does not contain a variable of type Circle"
)
}
}
func
(sq *Square) Area() float32 {
return
sq.side * sq.side
}
func
(ci *Circle) Area() float32 {
return
ci.radius * ci.radius * math.Pi
}
|
30、测定某个值是否实现了接口:v
是一个值,然后我们想测试它是否实现了 Stringer
接口
1
2
3
4
5
6
7
|
type
Stringer
interface
{
String() string
}
if
sv, ok := v.(Stringer); ok {
fmt.Printf(
"v implements String(): %s\n"
, sv.String())
// note: sv, not v
}
|
31、Go中的排序:对于一般性的排序,sort
包定义了一个接口
1
2
3
4
5
|
type
Interface
interface
{
Len() int
Less(i, j int) bool
Swap(i, j int)
}
|
其中的两个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
package
sort
type
Sorter
interface
{
Len() int
Less(i, j int) bool
Swap(i, j int)
}
func
Sort(data Sorter) {
for
pass := 1; pass < data.Len(); pass++ {
for
i := 0; i < data.Len()-pass; i++ {
if
data.Less(i+1, i) {
data.Swap(i, i+1)
}
}
}
}
func
IsSorted(data Sorter) bool {
n := data.Len()
for
i := n - 1; i > 0; i-- {
if
data.Less(i, i-1) {
return
false
}
}
return
true
}
// Convenience types for common cases
type
IntArray []int
func
(p IntArray) Len() int {
return
len(p) }
func
(p IntArray) Less(i, j int) bool {
return
p[i] < p[j] }
func
(p IntArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
type
StringArray []string
func
(p StringArray) Len() int {
return
len(p) }
func
(p StringArray) Less(i, j int) bool {
return
p[i] < p[j] }
func
(p StringArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// Convenience wrappers for common cases
func
SortInts(a []int) { Sort(IntArray(a)) }
func
SortStrings(a []string) { Sort(StringArray(a)) }
func
IntsAreSorted(a []int) bool {
return
IsSorted(IntArray(a)) }
func
StringsAreSorted(a []string) bool {
return
IsSorted(StringArray(a)) }
|
排序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
package
main
import
(
"./sort"
"fmt"
)
func
ints() {
data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586}
a := sort.IntArray(data)
//conversion to type IntArray
sort.Sort(a)
if
!sort.IsSorted(a) {
panic(
"fails"
)
}
fmt.Printf(
"The sorted array is: %v\n"
, a)
}
func
strings() {
data := []string{
"monday"
,
"friday"
,
"tuesday"
,
"wednesday"
,
"sunday"
,
"thursday"
,
""
,
"saturday"
}
a := sort.StringArray(data)
sort.Sort(a)
if
!sort.IsSorted(a) {
panic(
"fail"
)
}
fmt.Printf(
"The sorted array is: %v\n"
, a)
}
type
day
struct
{
num int
shortName string
longName string
}
type
dayArray
struct
{
data []*day
}
func
(p *dayArray) Len() int {
return
len(p.data) }
func
(p *dayArray) Less(i, j int) bool {
return
p.data[i].num < p.data[j].num }
func
(p *dayArray) Swap(i, j int) { p.data[i], p.data[j] = p.data[j], p.data[i] }
func
days() {
Sunday := day{0,
"SUN"
,
"Sunday"
}
Monday := day{1,
"MON"
,
"Monday"
}
Tuesday := day{2,
"TUE"
,
"Tuesday"
}
Wednesday := day{3,
"WED"
,
"Wednesday"
}
Thursday := day{4,
"THU"
,
"Thursday"
}
Friday := day{5,
"FRI"
,
"Friday"
}
Saturday := day{6,
"SAT"
,
"Saturday"
}
data := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday}
a := dayArray{data}
sort.Sort(&a)
if
!sort.IsSorted(&a) {
panic(
"fail"
)
}
for
_, d :=
range
data {
fmt.Printf(
"%s "
, d.longName)
}
fmt.Printf(
"\n"
)
}
func
main() {
ints()
strings()
days()
}
|
32、Go中的读和写:io包中提供了读和写的接口
1
2
3
4
5
6
7
|
type
Reader
interface
{
Read(p []byte) (n int, err error)
}
type
Writer
interface
{
Write(p []byte) (n int, err error)
}
|
33、空接口:空接口或则最小接口不包含任何方法,它对实现没有任何要求。可以给一个空接口类型的变量 var val interface {}
赋任何类型的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
package
main
import
"fmt"
var
i = 5
var
str =
"ABC"
type
Person
struct
{
name string
age int
}
type
Any
interface
{}
func
main() {
var
val Any
val = 5
fmt.Printf(
"val has the value: %v\n"
, val)
val = str
fmt.Printf(
"val has the value: %v\n"
, val)
pers1 := new(Person)
pers1.name =
"Rob Pike"
pers1.age = 55
val = pers1
fmt.Printf(
"val has the value: %v\n"
, val)
switch
t := val.(
type
) {
case
int:
fmt.Printf(
"Type int %T\n"
, t)
case
string:
fmt.Printf(
"Type string %T\n"
, t)
case
bool:
fmt.Printf(
"Type boolean %T\n"
, t)
case
*Person:
fmt.Printf(
"Type pointer to Person %T\n"
, t)
default
:
fmt.Printf(
"Unexpected type %T"
, t)
}
}
//输出
val has the value: 5
val has the value: ABC
val has the value: &{Rob Pike 55}
Type pointer to Person *main.Person
|
34、Go中对变量类型的判断以及获取变量的值:引入reflect 包,使用reflect.TypeOf(变量) 判断变量的类型 使用reflect.ValueOf(变量) 获取变量的值。(了解更多:https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/11.10.md)
35、遍历出结构体中的字段值:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
package
main
import
(
"fmt"
"reflect"
)
type
NotknownType
struct
{
s1, s2, s3 string
}
func
(n NotknownType) String() string {
return
n.s1 +
" - "
+ n.s2 +
" - "
+ n.s3
}
// variable to investigate:
var
secret
interface
{} = NotknownType{
"Ada"
,
"Go"
,
"Oberon"
}
func
main() {
value := reflect.ValueOf(secret)
// <main.NotknownType Value>
typ := reflect.TypeOf(secret)
// main.NotknownType
// alternative:
//typ := value.Type() // main.NotknownType
fmt.Println(typ)
knd := value.Kind()
// struct
fmt.Println(knd)
// iterate through the fields of the struct:
for
i := 0; i < value.NumField(); i++ {
fmt.Printf(
"Field %d: %v\n"
, i, value.Field(i))
// error: panic: reflect.Value.SetString using value obtained using unexported field
//value.Field(i).SetString("C#")
}
// call the first method, which is String():
results := value.Method(0).Call(nil)
fmt.Println(results)
// [Ada - Go - Oberon]
}
//结果
main.NotknownType
struct
Field 0: Ada
Field 1: Go
Field 2: Oberon
[Ada - Go - Oberon]
|
36、接口的继承:当一个类型包含(内嵌)另一个类型(实现了一个或多个接口)的指针时,这个类型就可以使用(另一个类型)所有的接口方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
type
Task
struct
{
Command string
*log.Logger
}
//这个类型的工厂方法像这样
func
NewTask(command string, logger *log.Logger) *Task {
return
&Task{command, logger}
}
//当 log.Logger 实现了 Log() 方法后,Task 的实例 task 就可以调用该方法
task.Log()
//类型可以通过继承多个接口来提供像 多重继承 一样的特性
type
ReaderWriter
struct
{
*io.Reader
*io.Writer
}
|
37、Go中的封装、继承、多态:
-
封装(数据隐藏):和别的 OO 语言有 4 个或更多的访问层次相比,Go 把它简化为了 2 层
1)包范围内的:通过标识符首字母小写,
对象
只在它所在的包内可见2)可导出的:通过标识符首字母大写,
对象
对所在包以外也可见类型只拥有自己所在包中定义的方法。
- 继承:用组合实现:内嵌一个(或多个)包含想要的行为(字段和方法)的类型;多重继承可以通过内嵌多个类型实现
- 多态:用接口实现:某个类型的实例可以赋给它所实现的任意接口类型的变量。类型和接口是松耦合的,并且多重继承可以通过实现多个接口实现。Go 接口不是 Java 和 C# 接口的变体,而且接口间是不相关的,并且是大规模编程和可适应的演进型设计的关键。
38、Sscanf()函数的使用:从一大长串的字符串截取我们需要的数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package
main
import
"fmt"
var
(
s string
i int
f float32
input =
"56/39.45/Go"
format =
"%d/%f/%s"
//规定想要数据的格式
)
func
main(){
fmt.Sscanf(input,format,&i,&f,&s)
//把对应数据的信息赋值给所对应变量
fmt.Printf(
"i=%d,f=%f,s=%s"
,i,f,s)
}
|
39、 使用bufio
包提供的缓冲读取(buffered reader)来读取数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package
main
import
(
"fmt"
"bufio"
"os"
)
var
readerInput *bufio.Reader
var
input string
var
err error
func
main(){
readerInput = bufio.NewReader(os.Stdin)
//创建一个读取器并将它与标准输入绑定
fmt.Println(
"please enter you input:"
)
input,err = readerInput.ReadString(
'\n'
)
//这是读取器中的一个方法,该方法从输入中读取内容,直到碰到 delim 指定的字符,然后将读取到的内容连同 delim 字符一起放到缓冲区。
if
err==nil{
fmt.Printf(
"the user input is:%s\n"
,input)
//输出用户读取的内容
}
}
|
40、读文件:文件使用指向 os.File
类型的指针来表示的,也叫做文件句柄。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
package
main
import
(
"fmt"
"os"
"bufio"
"io"
)
func
main(){
content,err :=os.Open(
"1.html"
)
//打开文件
if
err!=nil {
fmt.Printf(
"maybe your file not exist or you dont't have the pormission which open the file"
)
return
}
defer
content.Close()
//在文件末尾关闭文件
fileContent:=bufio.NewReader(content)
//创建一个读取器并将它与文件读取内容绑定
for
{
fileString,err :=fileContent.ReadString(
'\n'
)
//遍历读取内容时按照遇见换行符停止
fmt.Printf(
"the file content is :%s"
,fileString)
//输出文件内容
if
err==io.EOF{
return
}
}
}
|
41、写文件:OpenFile
函数有三个参数:文件名、一个或多个标志(使用逻辑运算符“|”连接),使用的文件权限。
os.O_RDONLY
:只读os.O_WRONLY
:只写os.O_CREATE
:创建:如果指定文件不存在,就创建该文件。os.O_TRUNC
:截断:如果指定文件已存在,就将该文件的长度截为0。12345678910111213141516171819202122232425262728package
main
import
(
"os"
"bufio"
"fmt"
)
func
main () {
// var outputWriter *bufio.Writer
// var outputFile *os.File
// var outputError os.Error
// var outputString string
outputFile, outputError := os.OpenFile(
"output.dat"
, os.O_WRONLY|os.O_CREATE, 0666)
if
outputError != nil {
fmt.Printf(
"An error occurred with file opening or creation\n"
)
return
}
defer
outputFile.Close()
outputWriter := bufio.NewWriter(outputFile)
outputString :=
"hello world!\n"
for
i:=0; i<10; i++ {
outputWriter.WriteString(outputString)
}
outputWriter.Flush()
}
42、文件拷贝。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package
main
import
(
"fmt"
"io"
"os"
)
func
main() {
CopyFile(
"target.txt"
,
"source.txt"
)
fmt.Println(
"Copy done!"
)
}
func
CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if
err != nil {
return
}
defer
src.Close()
dst, err := os.Create(dstName)
if
err != nil {
return
}
defer
dst.Close()
return
io.Copy(dst, src)
}
|