1. 变量

变量的声明有四种方式:

var a intvar a int = 100var a = 100:=a := 100
a := 100

多个变量一起声明的写法:

var (
	a int = 100
	b string = "abc"
)

匿名变量

_

go支持函数多返回值,而当我们对于某个函数的返回值是不关心的时候,可以使用匿名变量来接收

fd, _ := os.Open(xxx)_

2. 常量

const
varconst:=

比如:

const a int = 100
const (
	a = 10
	b = 20
)
iota
iotaconst
iotaconst
const (
	RED = iota
    BLUE
    BLACK
    ....
)
constiota
RED=0,BLUE=1,BLACK=2
RED=5 * 0=0,BLUE=5 * 1=5,BLACK=5 * 2=10
iota
iota
const (
	_ = iota // 赋值给_忽略这个值
    B = 1 << (10 * iota)
    KB
    MB
    GB
    TB
    ...
)

4.函数

go函数是允许有多个返回值的。go的函数定义可以有以下几种写法:

func test(a string, b int) (int, int) {
    ....
    
    return 100, 200
}
func test(a string, b int) (c, d int) {
    ...
    c = 100
    d = 200
    
    return
}

5. init函数

init函数是go在每个包初始化后自动执行的,而且在main函数之前执行

因此,init函数常用来:对变量初始化,注册等。

init函数的几个特点:

package xxxxmain
package main

import "fmt"

func int() {
    fmt.Println("init ok")
}

func main() {
    fmt.Println("main...")
}

6. import 导包

import
import _ "fmt"_init()import aa "fmtaa.Println()import . "fmt"

7. defer

defer关键字是go独有的,是一种延迟语句,在函数return前执行defer。

一个函数中可以添加多个defer语句,执行顺序是逆序的,先定义的defer最后执行

一般defer用于资源的关闭操作比较多。

结论就是:return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出。

8. 数组

func add(array [4]int) {
	fmt.Println(array[0], array[1], array[2], array[3])
}
func main() {
	arr := [5]int{1, 2, 3, 4}
	add(arr)
}

9. 数组切片(slice)

数组切片slice,也叫动态数组。

创建数组切片有两种方式:基于数组和直接创建

func main() {
    // 先定义一个数组
    var myArray [10]int = [10]int{1,2,3,4,5,6,7,8,9,10}
    // 基于数组创建一个数组切片
    var mySlice []int = myArray[:5]
}

元素的遍历

for i := 0; i < len(mySlice); i++ {
    ....
}
for i, v := range mySlice {
    ....
}// i 是index v是元素值

动态增减元素:

mySlice2 := []int{8, 9, 10}
mySlice = append(mySlice, mySlice2...)
slice1 := []int{1, 2, 3, 4, 5}
slice2 := []int{6, 7, 8}

copy(slice2, slice2) // 只会复制slice1的前三个元素到slice2中
// slice2 = {1,2,3}  slice1 = {1,2,3,4,5}

copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
// slice2 = {6,7,8} slice1 = {6,7,8,4,5}
func printArray(myArray []int) {
    ...
}

10. map

var myMap map[int]string
myMap = make(map[int]string, 10)
myMap[0] = "java"
myMap[1] = "Go"
myMap := make(map[int]string)
myMap[0] = "java"
myMap[1] = "Go"
myMap := map[int]string{
    0: "java",
    1: "Go",
}
value, ok := myMap[1]
if ok { // 找到了
    ....
}

11. 面向对象

我们都知道面向对象三个特点:封装,继承,多态。

但是go中并不像其他面向对象语言那样有很多的概念,go语言的面向对象编程是基于语言类型系统的,整个类型系统通过接口串联。

1. 类型系统

go语言中的类型是可以添加方法的,可以给任何类型,包括内置类型增加新方法。比如:

type Integer int

func (a Integer) Less(b Integer) bool {
    return a < b
}

// 可以这样使用
func main() {
    var a Integer = 1
    if a.Less(2) {
        fmt.Println(a, "Less 2")
    }
}
typeInteger

新增方法这个语法可以以java类的概念来理解为:Integer就是一个类,而a就相当于类中的this,而Less是类里的一个方法,当然a就可以调用到类里的成员了,但是这里的类实质是一个int,所以也就成员变量就是自身int值变量了,但如果a是一个结构体那就有成员变量了。

func (a *Integer) Add(b Integer) {
    *a += b
}

其实上面用指针和不用指针的具体原因,归根结底就是:Go语言的类型是基于值传递的,要修改变量的值,就需要传递指针。

2. 结构体

结构体的定义很简单,基本和C一样:

type Person struct {
    name string
    age int
}

// 新增一个方法
func (p *Person) setAge(age int) {
    p.age = age
}
func (p Person) getAge() {
    return p.age
}

当然,结构体也是go的一种类型,也是可以添加方法的,按我的理解,其实结构体就相当于是面向对象的类,添加的方法就是成员方法,而本身的成员变量就是类中的成员变量。

结构体初始化:

结构体初始化有以下几种实现:

p := new(Person)p := &Person{}p := &Person{"zhangsan", 18}p := &Person{name: "zhangsan", age: 20}
NewXXX
func NewPerson(name string, age int) *Person {
    return &Person{name, age}
}
3. 封装

回到面向对象三要素,封装,其实结构体就已经是封装的实现了。

这里有个注意的点就是:

类名,属性名,方法名,首字母大写表示对外(也就是其他包)可以访问,否则只能在本包内访问

4. 继承

go语言其实也是提供了继承的,只是采用的是组合的写法,比如以下例子:

// 定义父类
type Animal struct {
    name string
    age int
}

// 父类方法
func (a *Animal) Say() {
    fmt.Println("animal say...")
}

// 定义子类继承父类
type Dog struct {
    Animal
    weight int
}

func main() {
    d := &Dog{}
    
    d.Say()
    
    d.name = "旺财"
    
    fmt.Println(d) 
}

输出:

animal say...
&{{旺财 0} 0}

没有初始化值的变量会默认为对应类型的零值。

5. 多态

在理解go语言的多态之前,得先了解go语言的接口类型。

先来了解下其他语言的接口,在java中,对于接口的实现是必须在实现类中声明要实现的接口的,如果要实现一个接口,需要像下面代码这样编写代码:

// 定义一个接口类
public interface Person {
    // 接口方法
    public void say();
}

// 定义实现类,需要使用关键字implements显式的说明实现哪一个接口
class Teacher implements Person {
    public void say() {
        system.out.println("Hello 我是老师")
    }
}

而在go语言中,一个类只要实现了接口要求的所有函数,就可以说这个类实现了这个接口,当然go中接口使用的关键字还是interface

比如:

有一个File类,并且该类有四个方法,Read(),Write(),Seek(),Close()

type File struct {
    // ...
}
func (f *File) Read(buf []byte) (n int, err error)
func (f *File) Write(buf []byte) (n int, err error)
func (f *File) Seek(off int64, whence int) (pos int64, err error)
func (f *File) Close() error

然后有以下一些接口:

type IFile interface {
    Read(buf []byte) (n int, err error)
	Write(buf []byte) (n int, err error)
	Seek(off int64, whence int) (pos int64, err error)
	Close() error
}

type IReader interface {
    Read(buf []byte) (n int, err error)
}

type IWriter interface {
    Write(buf []byte) (n int, err error)
}

type ICloser interface {
    Close() error
}

代码中可以看出,File类并没有明确表示从这些接口中继承,甚至对于File类来说都不知道有这些接口的存在,但是在go里,认为File类实现了这些接口。

因此可以这样子进行赋值:

var file1 IFile = new(File)
var file2 IReader = new(File)
var file3 IWriter = new(File)
var file4 ICloser = new(File)

实质上,这样子不就是多态么!

接口的赋值:

go语言中接口赋值分为以下两种情况:

type Writer interface {
    Write(buf []byte) (n int, err error)
}

type ReadWriter interface {
    Read(buf []byte) (n int, err error)
    Write(buf []byte) (n int, err error)
}
var file ReadWriter = new(File)
// 接口ReadWriter 赋值给 接口Writer
var file1 Writer = file
// 这样子是可以的,这样file1是Writer接口的实例,只有一个Write方法可以调用是正常的
var file Writer = new(File)
// 接口ReadWriter 赋值给 接口Writer
var file1 ReadWriter = file
// 这样子是不可以的,这样file1是ReadWriter接口的实例,当file1调用read方法时候,并没有这个方法,因为他实质是Writer接口类型

接口查询

接口查询可以检查接口所指向的对象实例是否实现了某个接口,从而进行接口转换,比如:

var file Writer = new(File)
if file1, ok := file.(ReadWriter); ok {
    ...
}

这里是Writer接口所指向的对象实例是File类,是实现了ReadWriter的,所以这里ok会为true,file1是ReadWriter接口的实例,所以相当于是从Writer接口转为了ReadWriter接口了。

万能类型

type any = interface{}interface{}
var a interface{} = new(int)
var b interface{} = new(string)
var c interface{} = struct{X int}{1}
a = 10
b = "hello"
fmt.Println(a, b, c) // 输出:10 hello {1}
interface{}interface{}

类型查询(类型断言)

interface{}xxx.(type)
func test(arg interface{}) {
	switch arg.(type) {
	case int:
		fmt.Println("int type")
	case string:
		fmt.Println("string type")
	default:
		fmt.Println("unknown type")
	}
}

func main() {
    var v1 interface{} = "hello"
	var v2 int = 100
	v3 := struct{ X int }{1}

	test(v1)
	test(v2)
	test(v3)
}

12. 学习资料

《Go语言编程》