00 推荐阅读
李文周golang:https://www.liwenzhou.com/posts/Go/golang-menu/
https://www.cnblogs.com/smartrui/p/11425822.html
https://studygolang.com/articles/17179
https://www.jianshu.com/p/6ca70382bb42
对一个新的类型进行定义,我们称之为“类型声明”。对于某个类型声明可以直接使用,也可以使用type关键字对这个“类型声明”进行命名操作。
比如我们有一个类型声明,定义如下:
struct {
name string
age int
}
下面直接对该类型进行实例化对象:
func b() {
s := struct {
name string
age int
}{"ljz", 15}
fmt.Printf("%#v",s)
}
而我们常用的方式,是先对类型声明进行命名:
type persion struct {
name string
age int
}
func c() {
s := persion{"ljz", 15}
fmt.Printf("%#v",s)
}
type关键字的作用:为“类型声明”命名。通过type关键字,可以对各种类型进行命名,使之成为“命名类型(defined type)”
语法:type T 类型声明:此后,T为“类型声明”的名字,可以直接使用T来引用该类型。
golang中的各种数据类型:
int 、int32、int64、floating32、string、bool //GO中预先声明类型
数组,切片,map,通道,指针,结构体,接口,函数 // 组合类型
struct{...} // 结构体类型:通过”关键字struct+大括号“定义
interface{...} // 接口类型:通过”关键字interface+大括号“定义
func(参数) 返回值 //函数类型
以上所有类型都可以通过type关键字进行"类型声明"的命名,(为字面量类型做一个命名),从而简化引用。
int // 是一种类型
type myint int
map[string]int // 是一种类型
type mm map[string]int
struct{} // 是一种类型
struct{
name string
age int
} // 是一种类型
type person struct{
name string
age int
}
interface{} // 是一种类型。称之为“空接口”类型
type nilinterface interface{} // 为类型interface{}做了个命名操作,其名称为nilinterface。
func(a int ,b int) int // 是一种类型
type myfunc func(a int ,b int) int
类型(T)如果实现了接口(S),则类型T 的实例对象(obj)也是接口类型S的对象。即obj既是T类型,也是S类型。
函数也是一种数据类型,函数的签名就是函数的类型。
//定义函数类型
type handler func (name string) int
//实现函数类型方法
func (h handler) add(name string) int {
return h(name) + 10
}
//另一个函数定义
func doubler(name string) int {
return len(name) * 2
}
func main() {
fmt.Println(handler(doubler).add("taozs")) //doubler函数显式转换成handler函数对象然后调用对象的add方法
}
//1、定义结构体
//结构体定义
type person struct {
name string //注意后面不能有逗号
age int
}
func main() {
//结构体初始化
p := person{
name: "taozs", //注意后面要加逗号
age: 18, //或者下面的}提到这儿来可以省略逗号
}
fmt.Println(p.name)
}
//初始化字段不一定要全部指定,比如下面也是可以的,name默认取长度为0的空字符串
p := person{
age: 18,
}
//2、定义一个新的类型
type Str string
func main() {
var myname Str = "taozs" //其实就是字符串类型
l := []byte(myname) //字符串转字节数组
fmt.Println(len(l)) //字节长度
}
// 新定义的类型,可以定义方法.上面的Str类型可以像下面这样定义方法:
type Str string
func (n Str) len() int {
return len(n)
}
func main() {
var myname Str = "taozs" //其实就是字符串类型
l := []byte(myname) //字符串转字节数组
fmt.Println(len(l)) //字节长度
fmt.Println(myname.len()) //调用对象的方法
}
//3、结构体内嵌匿名成员
//结构体内嵌匿名成员定义
type person struct {
string //直接写类型,匿名
age int
}
func main() {
//结构体匿名成员初始化
p := person{string: "taozs", age: 18}//可以省略部分字段,如:person{string: "taozs"}。也可以这样省略字段名:person{“taozs”, 18},但必须写全了,不可以省略部分字段
//结构体匿名成员访问
fmt.Println(p.string) //注意不能用强制类型转换(类型断言):p.(string)
}
以下是只有单个匿名成员的例子。
//结构体内嵌匿名成员定义
type person struct {
string
}
func main() {
//结构体匿名成员初始化
p := person{string: "taozs"} //也可这样:person{"taozs"}
//结构体匿名成员访问
fmt.Println(p.string) //注意不能用强制类型转换(类型断言):p.(string)
}
//4、定义接口类型
package main
import (
"fmt"
)
//接口定义
type Personer interface {
Run()
Name() string
}
//实现接口,注意实现接口的不一定只是结构体,也可以是函数对象,参见下面第5条
type person struct {
name string
age int
}
func (person) Run() {
fmt.Println("running...")
}
//接收参数person不可以是指针类型,否则不认为是实现了接口
func (p person) Name() string {
return p.name
}
func main() {
//接口类型的变量定义
var p Personer
fmt.Println(p) //值<nil>
//实例化结构体,并赋值给interface
p = person{"taozs", 18} //或者:&person{"taozs", 18}
p.Run()
fmt.Println(p.Name())
var p2 person = p.(person) //类型断言,接口类型断言到具体类型
fmt.Println(p2.age)
}
//另外,类型断言返回值也可以有第二个bool值,表示断言是否成功,如下:
if p2, ok := p.(person); ok {//断言成功ok值为true
fmt.Println(ok)
fmt.Println(p2.age)
}
//5、定义函数类型
//以下是定义一个函数类型handler
type handler func (name string) int
//针对这个函数类型可以再定义方法,如:
func (h handler) add(name string) int {
return h(name) + 10
}
// 这种定义方式,和实现接口的方法定义差不多,感觉这种就是让代码更清晰,如果声明太复杂,不用看上去全是很乱的这种定义。
// 6、别名定义:定义和原来一样的类型,就是一个别名alias
type nameMap = map[string]interface{}
func main() {
m :=make(nameMap)
m["name"] = "golang"
fmt.Printf("%v", m)
}
下面让我们详细看一下例子,其中涉及了函数、函数的方法、结构体方法、接口的使用。
package main
import (
"fmt"
)
//定义接口
type adder interface {
add(string) int
}
//定义函数类型
type handler func (name string) int
//实现函数类型方法
func (h handler) add(name string) int {
return h(name) + 10
}
//函数参数类型接受实现了adder接口的对象(函数或结构体)
func process(a adder) {
fmt.Println("process:", a.add("taozs"))
}
//另一个函数定义
func doubler(name string) int {
return len(name) * 2
}
//非函数类型
type myint int
//实现了adder接口
func (i myint) add(name string) int {
return len(name) + int(i)
}
func main() {
//注意要成为函数对象必须显式定义handler类型
var my handler = func (name string) int {
return len(name)
}
//以下是函数或函数方法的调用
fmt.Println(my("taozs")) //调用函数
fmt.Println(my.add("taozs")) //调用函数对象的方法
fmt.Println(handler(doubler).add("taozs")) //doubler函数显式转换成handler函数对象然后调用对象的add方法
//以下是针对接口adder的调用
process(my) //process函数需要adder接口类型参数
process(handler(doubler)) //因为process接受的参数类型是handler,所以这儿要强制转换
process(myint(8)) //实现adder接口不仅可以是函数也可以是结构体
}
具有名称的类型:例如:int, int64, float32, string, bool等。这些已经是GO中预先声明好的类型。
通过类型声明语句(type declaration )创建的所有类型都是命名类型。type T 类型声明
一个命名类型一定和其它类型不同!
例如:
type myInt int // myInt是一个命名类型,所以myInt类型与其他类型(如int)不同。
type mymap map[string]string // named type
组合类型:数组,切片,map,通道,指针,结构体,接口,函数, 都是未命名类型。
未命名类型虽然他们没有名字,但却有一个类型字面量(type literal)来描述他们由什么构成(即未命名类型使用类型字面量表示)。
[]string // unnamed type
map[string]string // unnamed type
[10]int // unnamed type
任何类型T都有基本类型。
如果T是预先声明类型:boolean, numeric, or string(布尔,数值,字符串)中的一个,或者是一个类型字面量(type literal),他们对应的基础类型就是T自身。否则,T的基础类型就是T所引用的那个类型的类型声明(type declaration)。
即:
基础类型要么是预先声明类型,要么是组合类型的类型字面量,不能是自定义的命名类型的名称。(即如果T是一个类型字面量,它的基础类型就是T自身)

type I int32 // numeric
type II float64 // numeric ,所以I和II具有相同的基础类型numeric
type person struct{
name string
age int
}
// person类型的基础类型为:
struct{
name string
age int
}
type HandlerFunc func(ResponseWriter, *Request) // 基础类型:func(ResponseWriter, *Request)
所以:
第3,8行:他们的类型声明为 string的预先声明的类型,所以他们的基础类型就是T它自身: string
第5,7行:他们有类型字面量,所以他们的基础类型也是T它自身:map[string]int和 *N 指针。注意:这些类型字面量还是 未命名类型(unnamed type)
第4,6,10行:T的基本类型是T所引用的那个类型的类型声明(type declaration)。
- 4行:
B引用了A,因此B的基础类型是A的类型声明:string, - 6行:
N引用了M, 因此N的基础类型是M的类型声明:map[string]int
需要注意的是第9行:
type T map[S]int. 由于s的基础类型是string,那么是否type T map[S]int的基础类型应该是 map[string]int 而并非map[S]int呢?确定吗? 在此,我们讨论的是基础未命名类型map[S]int,因为基础类型只追溯到它的未命名类型的最外层,所以map[S]int内层的S就不再追溯(或者就像说明上说的:如果T是一个类型字面量,它的基础类型就是T自身),所以U的基础类型是map[S]int。
什么情况下变量x可以赋值给另一个类型T

- x的类型与T相同
- T是一个接口类型,而且x的类型实现了接口T
- x的类型V与T具有相同的基础类型,而且V和T其中至少有一个不是命名类型
- nil值可以赋值给如下类型:指针类型、函数类型、slice、map、channel、接口类型
具有相同的基础类型的2种类型(T1,T2)的变量是可以相互赋值的,前提是有可能需要做类型转换。
- 如果T1、T2二者都是命名类型,则需要做类型转换。
- 如果T1、T2二者都是非命名类型(类型字面量),或二者一个是命名类型一个是类型字面量,而可以直接赋值。
双方应该具有相同的基础类型,而且其中至少有一个不是命名类型(至少有一个是未命名变类型)。
package main
import "fmt"
type aInt int
func main() {
var i int = 10
var ai aInt = 100
i = ai // Error: i和ai都是命名类型,无法直接赋值,需要做类型转换:i= int(ai)
printAiType(i)
}
func printAiType(ai aInt) {
fmt.print(ai)
}
所以,上面的代码将不会 通过编译并且会报以下编译错误:
8:4: cannot use ai (type aInt) as type int in assignment 9:13: cannot use i (type int) as type aInt in argument to printAiType因为
i是命名类型int,ai是命名类型aInt, 虽然他们的基础类型是相同的,但是没有未命名类型,所以不能赋值。
package main
import "fmt"
type MyMap map[int]int
func main() {
m := make(map[int]int) // m是非命名类型(类型字面量)
var mMap MyMap // mMap是命名类型(类型的名称是MyMap)
mMap = m // m和mMap具有相同的基础类型,且m是非命名类型,所以可以直接赋值,无需类型转换。
printAiType(mMap)
fmt.Print(m)
}
func printAiType(mMap MyMap) {
fmt.print(mMap)
}
编译通过,因为
m是未命名类型,同时m和mMap有相同的基础类型。

具有相同的基础类型,就可以做类型转换。
忽略结构体的tags,只要结构体X和T拥有相同的基础类型,就可以转换。
x和T都是非命名的指针类型,并且指针的base类型具有相同的基础类型。
两种类型要么相同,要么不相同。
- 一个命名类型一定和其它类型都不同。(如果要相互赋值,就需要进行类型转换)
- 如果他们其中至少有一个为未命名类型,且基础类型的字面量在结构上是等价的,他们就是相同的类型。
因此,预先声明的命名类型 int, int64, 等都是不一致的。
忽略结构体的tags,只要结构体X和T拥有相同的基础类型,就可以转换。
package main
type Meter struct {
value int64
}
type Centimeter struct {
value int32
}
func main() {
cm := Centimeter{
value: 1000,
}
var m Meter
m = Meter(cm)
print(m.value)
cm = Centimeter(m)
print(cm.value)
}
注意这个词:相同的基础类型,因为字段 Meter.value 的基础类型是 int64 , 字段Centimeter.value 的基础类型是 int32 ,因为一个命名类型一定和其它任意类型都不同。所以他们不一致,不是相同的基础类型,所以不能转换。我们会得到编译错误。
因为Meter的基础类型为:
struct {
value int64
}
Centimeter的基础类型为:
struct {
value int32
}
以上两个基础类型不相同,所以不能相互转换。
package main
type Meter struct {
value int64
}
type Centimeter struct {
value int64
}
func main() {
cm := Centimeter{
value: 1000,
}
var m Meter
m = Meter(cm)
print(m.value)
cm = Centimeter(m)
print(cm.value)
}
字段 Meter.value 的基础类型是 int64 ,字段Centimeter.value 的基础类型是 int64。一致的基础类型,所以可以相互转换,编译通过。
首先,Meter和Centimeter类型都是命名类型,2种命名类型肯定是2种不同的类型,不能直接赋值,需要做类型转换后方可赋值。
其次,因为类型Meter和类型Centimeter的基础类型都是:
struct {
value int64
}
所以,基础类型是一致的,是可以相互转换的。
error与errors:Go语言error类型详解:https://blog.csdn.net/fwhezfwhez/article/details/79175376
包:https://www.liwenzhou.com/posts/Go/11_package/
依赖:https://www.liwenzhou.com/posts/Go/go_dependency/
导入本地包:https://www.liwenzhou.com/posts/Go/import_local_package_in_go_module/
vender本地包:https://blog.csdn.net/test1280/article/details/120855865
https://www.liwenzhou.com/posts/Go/go_mysql/
https://www.liwenzhou.com/posts/Go/sqlx/
https://blog.csdn.net/wschq/article/details/80114036
https://www.cnblogs.com/ricklz/p/13659397.html
https://www.cnblogs.com/zhangyafei/p/13276705.html
【推荐使用】https://www.jianshu.com/p/fee89fbb5f27
https://my.oschina.net/claymore/blog/3042078
源码分析启动流程:https://www.jianshu.com/p/2c2bfe80bd91
源码分析:https://www.cnblogs.com/f-ck-need-u/p/9832538.html
【推荐】浅析 Go IO 的知识框架(https://blog.csdn.net/slphahaha/article/details/119792661)
缓冲区机制详解 和 带缓冲 IO 与不带缓冲 IO 的区别与联系

ioutil.ReadFile(filename string) ([]byte,error)
io.WriteString(w io.Writer, str string) # 向Writer中写入字符串
io.WriteFile(filename string,data []byte, perm fs.FileMode) # 向filename中写入data
https://blog.csdn.net/qq_42305808/article/details/121931574
https://www.jianshu.com/p/d86119d19d77
https://blog.csdn.net/inthat/article/details/123527784
https://www.jianshu.com/p/753bc2f9d2df
github.com/urfave/cli
filepath.Walk会变量指定目录下的所有文件和目录,并将每个文件或目录的文件路径path、文件信息info、文件错误err传递给walkFunc来进行回调,回调函数可以自定义实现。
import (
"fmt"
"os"
"path/filepath"
)
func walkFunc(path string, info os.FileInfo, err error) error {
if info.IsDir() {
fmt.Printf("----------> dir :%s\n", path)
}else {
fmt.Printf("%s\n", path)
}
return nil
}
func main() {
//遍历打印所有的文件名
filepath.Walk("/Users/lijuzhang/Downloads", walkFunc)
}