项目初始

IDEA配置

-o bin.main

引用其他包文件

package calc
func Add(a, b int) int {
	return a + b
}

* Add 函数必须要大写,大写属于public 才能够让其他包引用 ,不是大写 属于私有方法
* 同一个文件下面的函数变量名称不能重复
* 
  • GIF 动态画面
package main

import (
	"image"
	"image/color"
	"image/gif"
	"io"
	"log"
	"math"
	"math/rand"
	"net/http"
	"os"
	"time"
)

var palette = []color.Color{color.White, color.Black}

const (
	whiteIndex = 0 // 画板中第一种颜色
	blackIndex = 1 // 画板中第二种颜色
)

func lissajous(out io.Writer) {
	const (
		cycles  = 5
		res     = 0.001
		size    = 100
		nframes = 64
		delay   = 8
	)
	freq := rand.Float64() * 3.0
	anim := gif.GIF{LoopCount: nframes}
	phase := 0.0
	for i := 0; i < nframes; i++ {
		rect := image.Rect(0, 0, 2*size+1, 2*size+1)
		img := image.NewPaletted(rect, palette)
		for t := 0.0; t < cycles*2*math.Pi; t += res {
			x := math.Sin(t)
			y := math.Sin(t*freq + phase)
			img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5), blackIndex)
		}
		phase += 0.1
		anim.Delay = append(anim.Delay, delay)
		anim.Image = append(anim.Image, img)
	}
	err := gif.EncodeAll(out, &anim)
	if err != nil {
		return
	}
}
func main() {
	rand.Seed(time.Now().UTC().UnixNano())
	if len(os.Args) > 1 && os.Args[1] == "web" {
		handler := func(w http.ResponseWriter, r *http.Request) {
			lissajous(w)
		}
		http.HandleFunc("/", handler)
		log.Fatal(http.ListenAndServe("localhost:8080", nil))
		return
	}
	lissajous(os.Stdout)
}

  • 构建简单 统计的服务器
package main

import (
	"fmt"
	"log"
	"net/http"
	"sync"
)

// 构建一个简单的 web 服务 能够流量计数统计
var mu sync.Mutex
var count int

func handler(w http.ResponseWriter, r *http.Request) {
	mu.Lock()
	count++
	mu.Unlock()
	_, err := fmt.Fprintf(w, "URL.path = %q\n", r.URL.Path)
	if err != nil {
		return
	}
}

func counter(w http.ResponseWriter, r *http.Request) {
	mu.Lock()
	_, err := fmt.Fprintf(w, "Count %d\n", count)
	if err != nil {
		return
	}
	mu.Unlock()

}

func main() {
	// 回声请求的调用处理函数
	http.HandleFunc("/", handler)
	http.HandleFunc("/counter", counter)
	log.Fatal(http.ListenAndServe("localhost:8080", nil))
}
  • 变量第一区别
var :多用于全局变量
:= 简短声明多用于局部变量
new() 分配内存函数 返回函数地址
make() 只使用于 map,chan, slice
  • init()函数
1. 同一个包下面可以有多个init 函数
2. 自动执行,不需要调用 类似于Java中构造器函数
3. 方法名称 init() ,init()在main() 函数前执行, 多个init() 初始化化原则(依赖顺序初始化)
  • go 语言中异常处理 通过是使用 if 方式
if f,err := os.Open(fname); err != nil {
    return err
}else {
	f.Stat()
	f.Close()
}

  • 常量
4. 使用const 定义,如果在定义过程中没有初始化值 自动找前一个赋值常量
const (
	a = 1
	b
	c = 2
	d 
) // 1 1 2 2
5. 常量生成器: iota :从0 开始取值 逐项+1 
使用在枚举类中数字表示
type Weekday int
const (
	Sunday Weekday = iota
	Monday
	Tuesday
)
  • Go 中函数有多个返回值
func op(x, y int) (int, error) {
	if (0 == y){
		return -1, errors.New("ch除数不能为0")
}else {
	return x /y, nil
}
}

Errors.New(""), fmt.Errorf("parseing %s")Go语言有异常机制,但是针对程序BUG导致的预料之外的错误,对于常规的错误方法初始化通过返回字符串类型进行处理
6. s使用 fmt.Errorf 格式化一条错误消息并且返回一个新的错误值,在原始的错误消息不断添加额外的上下文信息 创建一个可读的错误描述,-- 清晰的因果链;
7. 错误消息频繁串联起来,消息字符串首字母 不应该大写并且避免换行

Go语言中错误的消息设计时候 慎重,最好包含充足的相关信息,并且保持一致性,保持统一的形式和错误的处理方式

  • 对于不固定或者不可预测的错误
比如对服务器在短时间内进行 指数退避策略进行重试
if err := WaitForSrver(url); err != nil {
	fmt.Fprintf(os.Stderr, "site is down: %v\n", err)
	os.Exit(1)
}
Go 语言错误处理有特定的规律, 成功的逻辑不会放在else 块,在外层的作用域,一般看到 if err 之后,不会有太多的else

如果不定义一个临时变量进行存储,最终删除知识最后一次
var rmdirs [] func()
for _, d := range tempDirs(){
	dir :=d // 必须要使用局部变量 声明内部的dir ,并且外部的dir 进行初始化
	os.MkdirAll(dir, 0775)
	rmdirs = append(rmdirs, func() {
		os,RemoveAll(dir)
})
}
defer go中一种延迟调用的机制,defer 函数只有在当前函数执行完毕后才会执行,遵守的栈的结果 场景在于 释放资源, 保留是入栈时候的值
8. go 中 return 语句并不是原子性操作, defer 在1之后 2之前
 1. 将返回值赋值个一个变量
 2. 执行RET指令
9.  -- 输出结果 10 保存的是入栈那一刻的值
	 x := 10
	defer func(a int) {
		fmt.Printf("%d", a)
	}(x)
	x++
10. 输出结果 11 传入的是地址
	x := 10
		defer func(a *int) {
			fmt.Printf("%d", *a)
		}(&x)
		x++
11. 输出结果 11 是具名函数。即返回值带有名字。这样我们在执行defer的时候相当于修改了返回值的值。所以为11
	func test()(x int)  { -- 属于具体名称函数 ,返回值带有函数名称
	 x = 10
	 defer func() {
	 	x++
	 }()
12. 闭包函数 返回值 10
13. func test1() int {
	x := 10
	defer func() {
		x++
	}()
	// ans = x
	// -------- defer x = x+1
	// return x
	return x
}
倒序方式打印出堆栈函数recorver 终止当前的paninc 状态并且返回paninc 函数, 函数在paninc地方跳转到recover函数 ,与 Java try..catch 有不一样
package main

import "fmt"

func main() {

	defer func() {
		err := recover()
		// 恢复
		fmt.Println("recorver error:", err)
	}()

	for i := 0; i < 10; i++ {
		if i == 5 {
			// painc 程序退出
			panic("i == 5")
		}
	}
}
-- recover 函数 捕获到 paninc 函数 ,与 Java try..catch 不一样,在 i == 5 跳出主函数 ,执行 延迟函数中 recover(),没有执行 i= 6; 不会执行for 循环
组合封装
方法声明
  1. 方法声明与普通函数相似,只是与普通的函数多了一个参数, 将 Distance 方法属于某个类型方式, 普通函数属于 包级别方法
func Distance(p, q Point) float64 {
	return p * q
}
// Point类型函数, p 称为方法接收者, 使用类型的简写
func (p Point) Distance(q Point) float64 {
		return p.x-q.x + p.Y- q.Y
}
类型方法调用: p:= Point{1,2}
			q:= Point{3,4}
			p.Distance(q) -- 选择子 ,注意 方法和名称属于同一个命名空间,在Point类型中声明一个与属性相同的方法名称会报错
类型任何一个方法使用指针接收者,其他方法使用指针接收者
 主函数会赋值每一个实参变量,如果想更新一个变量,或者变量太大不像时刻复制实参,需要使用指针传递变量地址, 这样使用 指针类型的接收者;
 1. 定义一个指针 & 取其地址
 2. p Point 类型普通变量 Run 属于指针接收者
    p.Run() // 编译器 隐式转换 (&p).Run() -- 普通类型变量调用指针方法
 3. p3 = &Point{"jack", 1} p3.Say() ,其中 Say() 普通的方法
	p3.Say() // 编译器隐式转换 (*p3).Say()  -- 指针调用非指针函数
	P3:实参类型,Run()方法 形参类型
3. 变量类型与方法 属于同一类型 调用OK
类型中如果允许nil 接收者需要在注释中 显示的表明
type IntList struct {
	value int
	tail *IntList
}

func (list *IntList) Sum() int {
	if nil ==list {
		return 0
 }
 return list.value + list.tail.sum()
}
  • 封装
    在Go中丹云是包 不是类型也不是类, 封装类似Java中 私有变量,protect变量
变量和方法不能通过对象方位到,这种方式叫做封装,也叫数据隐藏Go 只有一种方式控制命名可见性,定义时候 首字母大写的标识符,是可以从包中导出, 同样也适用于结构体内字段和类型中方法, 要封装一个对象,必须使用结构体
// 只能在定义InSet 包内使用
type InSet struct {
	words [] unit64
}
// 其他包内被使用,重新定义 IntSet 为 一个Slice 类型 功能基本相同,想要修改 words值 需要使用 *s 指针类型
type InSet [] unit64

// bytes.Buffer 类型 结构体有 buf 首字母不是大写,其他包不能引用,这块空间额外的[64]byte ,使用者感觉性能提升但是不会关系内部实现
type Bffer struct {
	buf [] byte
	initial [64] byte
}

接口

Go 一个具体类型,无需进行接口声明,只需要提供实现接口方法即可
1. B 类型需要实现A类型的所有方法,才能认为B类型实现A接口
2. 传入任何的参数,需要通过接口 进行接口断言
3. flag.value进行参数解析,进行命令行参数的解析
动态类型和动态值
	var w io.Writer
	w = os.Stdout // 动态类型:os.File 类型描述符, 动态值为os.Stdout 副本
	w= new(bytes.Buffer)
分配了指针但是其值没有调用使用发生paninc动态值是nil,但是其动态类型存在 在判断时候发现是正确的解决方案: 保持接口的动态类型 一致; 不要 定义的变量接口类型和函数动态类型不一致,导致发生空指针的非空接口, w != nil 比较动态类型
  • 类型断言
    类型断言作用在接口值上操作,x(T) ,X表示一个接口类型的表达式,断言检查 动态类型是否满足指定的断言类型
var w io.Writer
w = os.Stdout
// 判断接口的类型断言
f := w.(*os.File) // 成功 f == os.Stdout 

var w io.Writer
	w = os.Stdout
	// T 是具体类型, 检查X的动态类型是否就是T
	// x.(T) 如果T 是接口类型,检查 x 的动态类型是否满足 T
	f := w.(*os.File)
	fmt.Println("%T", f.Name())
  • 使用类型断言识别错误
4. 使用字符串方式判断是否包含某种错误字符串 不建议
5.