类型检查:编译时
运行环境:编译成机器代码直接运行
编程范式:面向接口,函数式编程,并发编程
Go语言并发编程
采用CSP(Communication Sequential Process)模式
不需要锁,不需要callback
并发编程vs并行计算
其中(u)int的位数由操作系统决定;
uintptr是指针;
byte和rune是int的别名,byte是8位rune是32位;
complex64的实部和虚部都是float32,complex128的实部和虚部都是float64。
go没有隐式类型转换,都要做显示的强制转换。
定义:
const filename = “abc.txt”
const数值可作为各种类型使用(无需强制类型转换)
const a,b = 3,4
var c int = int(math.Sqrt(a * a + b * b))
const组用作枚举
iota关键字用作初值,后续枚举值自动递增,使用_可以占一个数,如下例中,a b c d值分别为0 2 3 4
const(
a = iota
_
b
c
d
也可以使用公式,如下:
const (
b = 1 << (10 * iota)
kb
mb
gb
tb
pb
)
func grade(score int) string {
g := ""
switch {
case score < 0 || score >100:
panic(fmt.Sprintf("wrong score: %d", score))
case sore < 60:
g = "F"
case sore < 70:
g = "C"
case sore < 90:
g = "B"
case sore < 100:
g = "A"
}
return g
}
for的条件里不需要括号
for的条件里可以省略初始条件,结束条件,递增表达式
go语言没有while
函数返回值类型放在后面
函数可以作为参数传递
没有默认参数,可选参数,有变长参数
函数可以返回多个值
可以为返回的多个值起名字,但最好用于简单函数;起不起名字对调用者来说没区别
func div(a, b int) (int, int) {
return a / b, a % b
}
func div2(a, b int) (q, r int) {
return a / b, a % b
}
func div3(a, b int) (q, r int) {
q = a / b
r = a % b
return
}
一般来说,函数的第二个返回值用来返回error,以便调用者处理
func eval2(a, b int, op string) (int, error) {
switch op {
case "+":
return a + b, nil
case "-":
return a - b, nil
case "*":
return a * b, nil
case "/":
return a / b, nil
default:
return 0, fmt.Errorf("Invalid operation: %s", op)
}
}
函数可以作为函数的入参
func apply(op func(int, int) int, a, b int) int {
p := reflect.ValueOf(op).Pointer()
opName := runtime.FuncForPC(p).Name()
fmt.Printf("Calling Function %s with args(%d, %d)\n", opName, a, b)
return op(a, b)
}
只有值传递一种方式,即将参数进行拷贝传入函数
指针不能运算
var arr1 [5]int
var arr2 := [3]int{3, 4, 5}
var arr3 := [...]int{1, 2, 3, 4, 5, 6} // 缺省数组长度,由编译器确定
var grid [4][[5]int
len、range、省略变量可以用_代替
numbers := [6]int {1, 2, 3, 4, 5, 6}
for i := 0; i < len(numbers); i++ {
fmt.Println(numbers[i])
}
maxi := -1
maxValue := -1
for i,v := range numbers {
if v > maxValue {
maxi, maxValue = i, v
}
}
fmt.Println(maxi, maxValue)
sum := 0
for _,v := range numbers {
sum += v
}
fmt.Println(sum)
for i := range numbers {
fmt.Println(i)
}
func printArray(arr *[6]int) {
arr[1] = 100
for _, v := range arr {
fmt.Println(v)
}
}
func main() {
numbers := [6]int {1, 2, 3, 4, 5, 6}
printArray(&numbers)
fmt.Println(numbers)
}
slice本身没有数据,是对底层array的一个view
slice的内部:
ptr,指向slice开头的元素,
len,说明slice的长度,方括号取值时只能取到length里面的值,下标超出就报错,
cap(capacity:容量),从ptr开始到结束的整个array的长度;
扩展时,只要不超过capacity就可以扩展,只能向后扩展;
cap(s):slice的容量, len(s):slice的长度
添加元素时如果超过cap,系统会重新分配更大的底层数组
由于是值传递,必须有参数接收append的返回值
s = append(s, val)
var s []int /* 此处定义slice的初值为nil */
for i := 0; i < 10; i++ {
s = append(s, i*2+1)
}
s1 := []int{2, 4, 6, 8}
s2 := make([]int, 16)
s3 := make([]int, 10, 32) /* slice长度、容量 */
fmt.Println("Copying slice")
copy(s2, s1)
printSlice(s2)
fmt.Println("Deleting elements from slice")
s2 = append(s2[:3], s2[4:]...)
printSlice(s2)
fmt.Println("Poping from front")
front := s2[0]
s2 = s2[1:]
fmt.Println("Poping from back")
back := s2[len(s2)-1]
s2 = s2[:len(s2)-1]
fmt.Println(front, back)
printSlice(s2)
m := map[string]string{
"name": "xyz",
"sex": "famale",
"high": "167cm",
"weight": "53kg",
}
mm := map[string]map[string]string {
"xx": {"name": "xyz",},
}
m2 := make(map[string]int) /* m2 == empty map */
var m3 map[string]int
fmt.Println("Traversing Map")
for k, v := range m {
fmt.Println(k, v)
}
if namey, flagy := m["namey"]; flagy {
fmt.Println("namey value", namey)
} else {
fmt.Println("key does not exist")
}
fmt.Println("Deleting Element")
delete(m, "name")
namez, flagz := m["name"]
fmt.Println(namez, flagz)
package main
import (
"unicode/utf8"
"fmt"
)
func main() {
s := "kitty咘咘和臭臭" // UTF-8
fmt.Println(s)
for _, b := range []byte(s) {
fmt.Printf("%X ", b)
}
fmt.Println()
for i, ch := range s {
fmt.Printf("(%d %c)", i, ch)
}
fmt.Println()
fmt.Println("Rune count in string:",
utf8.RuneCountInString(s))
bytes := []byte(s)
for len(bytes) > 0 {
ch, size := utf8.DecodeRune(bytes)
bytes = bytes[size:]
fmt.Printf("%c ", ch)
}
fmt.Println()
for i, ch := range []rune(s) {
fmt.Printf("(%d %c)", i, ch)
}
}
打印结果
kitty咘咘和臭臭
6B 69 74 74 79 E5 92 98 E5 92 98 E5 92 8C E8 87 AD E8 87 AD
(0 k)(1 i)(2 t)(3 t)(4 y)(5 咘)(8 咘)(11 和)(14 臭)(17 臭)
Rune count in string: 10
k i t t y 咘 咘 和 臭 臭
(0 k)(1 i)(2 t)(3 t)(4 y)(5 咘)(6 咘)(7 和)(8 臭)(9 臭)
其他字符串操作
- Fields, Split, Join
- Contains, Index
- ToLower, ToUpper
- Trim, TrimRight, TrimLeft
仅支持封装,不支持继承和多态
没有class,仅有struct
不论是地址还是结构本身,一律使用.来访问成员
没有构造函数,使用自定义工厂函数,函数返回的是局部变量地址
go语言的编译系统会视具体情况将内存分配在栈上或者堆上,当堆上的变量不再使用时,便会启用垃圾回收,回收这块内存,程序员不需要关心。
type treeNode struct {
value int
left, right *treeNode
}
/* 显示定义和命名结构体方法 */
func (node treeNode) print() {
fmt.Print(node.value, " ")
}
/* 只有使用指针才可以改变结构内容 */
func (node *treeNode) setValue(value int) {
node.value = value
}
/* 中序遍历 */
func (node *treeNode) traverse() {
if node == nil {
return
}
node.left.traverse()
node.print()
node.right.traverse()
}
func createTreeNode(value int) *treeNode {
return &treeNode{value:value}
}
func main() {
var root treeNode
root = treeNode{value:1}
root.left = &treeNode{}
root.right = &treeNode{11, nil, nil}
root.left.left = new(treeNode)
root.right.right = createTreeNode(111)
nodes := []treeNode{
{value:1},
{},
{11, nil, &root},
}
fmt.Println(root)
fmt.Println(nodes)
root.print()
root.right.right.print()
root.left.left.setValue(100)
root.left.left.print()
/* nil也可以访问指针接收者 */
var pRoot *treeNode
pRoot.setValue(100)
pRoot = &root
pRoot.setValue(1111)
pRoot.print()
}
{1 0xc042060400 0xc042060420}
[{1 <nil> <nil>} {0 <nil> <nil>} {11 <nil> 0xc0420603e0}]
值接收者 vs 指针接受者
- 要改变内容必须使用指针接收者
- 结构过大也考虑使用指针接受者
- 一致性:如有指针接收者,最好都是指针接受者
值接收者是go语言特有
值/指针接受者均可接收值/指针
type myTreeNode struct {
node *tree.Node
}
func (myNode *myTreeNode) postOrder() {
if myNode == nil || myNode.node == nil {
return
}
left := myTreeNode{myNode.node.Left}
right := myTreeNode{myNode.node.Right}
left.postOrder()
right.postOrder()
myNode.node.Print()
}
func main() {
var root tree.Node
root = tree.Node{Value:1}
root.Left = &tree.Node{}
root.Left.Left = new(tree.Node)
fmt.Print("\nMy own post-order traversal: ")
myRoot := myTreeNode{&root}
myRoot.postOrder()
fmt.Println()
}
package queue
type Queue []int
func (q *Queue) Push(v int) {
*q = append(*q, v)
}
func (q *Queue) Pop() int {
head := (*q)[0]
*q = (*q)[1:]
return head
}
func (q *Queue) IsEmpty() bool {
return 0 == len(*q)
}
go get获取第三方库
使用gopm来获取无法下载的包
go get -v github.com/gpmgo/gopm
gopm get -g -v golang.org/x/tools/cmd/goimports
go bulid golang.org/x/tools/cmd/goimports
go install golang.org/x/tools/cmd/goimports
Gopath下包含src pkg bin;文件在src中,pkg是编译衍生物,编译生成的可执行文件在bin中
go语言的duck typing同时完成多种功能
同时具有python,c++的duck typing的灵活性
又具有java的类型检查
接口由使用者定义
接口的实现是隐式的
只要struct实现接口声明的方法,就可以传递给接口参数使用
package queue
type Queue []interface{}
func (q *Queue) Push(v interface{}) {
*q = append(*q, v)
//*q = append(*q, v.(int))
}
//func (q *Queue) Pop() int {
func (q *Queue) Pop() interface{} {
head := (*q)[0]
*q = (*q)[1:]
return head
//return head.(int)
}
func (q *Queue) IsEmpty() interface{} {
return 0 == len(*q)
}
package main
import (
"fmt"
"mypractice.com/practice/queue"
)
func main() {
q := queue.Queue{1}
q.Push(2)
q.Push(3)
q.Push("abc")
fmt.Println(q.Pop())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
fmt.Println(q.Pop())
fmt.Println(q.IsEmpty())
}
type Retriever struct {
Contents string
}
func (r *Retriever) String() string {
return fmt.Sprintf(
"Retriever: {Contents=%s}", r.Contents)
}
func (r *Retriever) Get(url string) string {
return r.Contents
}
func (r *Retriever) Post(url string,
form map[string]string) string {
r.Contents = form["contents"]
return "OK"
}
type Retriever interface {
Get(url string) string
}
type Poster interface {
Post(url string,
form map[string]string) string
}
func download(r Retriever) string {
return r.Get("https://www.csdn.net")
}
func post(poster Poster) {
poster.Post("https://www.csdn.net",
map[string]string {
"name": "qifangyuan",
"blog": "Blog",
})
}
//组合接口
type RetrieverPoster interface {
Retriever
Poster
}
func session(s RetrieverPoster) string{
s.Post("fake https://www.csdn.net",
map[string]string {
"contents": "another test interface",
})
/*s.Post("https://www.csdn.net",
map[string]string {
"name": "qqq",
"blog": "Blog",
})*/
return s.Get("https://www.csdn.net")
}
func inspect(r Retriever) {
fmt.Println("Inspecting", r)
fmt.Printf(" > Type:%T Value:%v\n", r, r)
fmt.Print(" > Type switch: ")
// .(type)只能配合switch case使用,用来判断变量类型
// 还可以使用.(bufio.Reader)
switch v := r.(type) {
case *mock.Retriever:
fmt.Println("Contents:", v.Contents)
case *real.Retriever:
fmt.Println("UserAgent:", v.UserAgent)
}
fmt.Println()
}
func main() {
var r Retriever
mockRetriever := mock.Retriever{
Contents:"test interface"}
r = &mockRetriever
inspect(r)
r = &real.Retriever{
UserAgent: "Mozilla/5.0",
TimeOut: time.Minute,
}
inspect(r)
realRetriever := r.(*real.Retriever)
fmt.Println(realRetriever.UserAgent)
if mockRetriever, ok := r.(*mock.Retriever); ok {
fmt.Println(mockRetriever.Contents)
}else {
fmt.Println("not a mock retriever")
}
fmt.Println(
"Try a session with mockRetriever")
fmt.Println(session(&mockRetriever))
}
常用的系统接口:
Stringer
Reader/Writer
应用举例:
斐波那契额
函数接口
二叉树遍历
go语言闭包的应用:
更为自然,不需要修饰如何访问自由变量
没有lambda表达式,但是有匿名函数
主要是防止程序出错中断,一般是直接返回
- 可以处理预期内的错误
- 也可以实现error接口
func writeFile(filename string) {
file, err := os.OpenFile(filename,
os.O_EXCL|os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
if pathError, ok := err.(*os.PathError); !ok {
panic(err)
} else {
fmt.Printf("%s, %s, %s\n",
pathError.Op,
pathError.Path,
pathError.Err)
}
return
}
}
使用函数式编程,将错误返回给外层错误处理函数统一处理
综合示例:
- defer + panic + recover
- Type Assertion
- 函数式编程的应用
package filelisting
import (
"fmt"
"io/ioutil"
"net/http"
"os"
)
func HandleFilelist(writer http.ResponseWriter,
request *http.Request) error {
path := request.URL.Path[len("/list/"):]
fmt.Println(path)
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
all, err := ioutil.ReadAll(file)
if err != nil {
return err
}
writer.Write(all)
return nil
}
/****************************************************************/
package main
import (
"log"
"net/http"
"os"
"mypractice.com/practice/errhandling/filelistingserver/filelisting"
)
type apphandler func(writer http.ResponseWriter,
request *http.Request) error
func errWrapper(
handler apphandler) func(
http.ResponseWriter, *http.Request) {
return func(writer http.ResponseWriter,
request *http.Request) {
defer func() {
if r := recover(); r != nil {
log.Printf("Panic: %v", r)
http.Error(writer,
http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError)
}
}()
err := handler(writer, request)
if err != nil {
log.Printf("Error occurred "+
"handling request: %s",
err.Error())
if userErr, ok := err.(userError); ok {
http.Error(writer,
userErr.Message(),
http.StatusBadRequest)
return
}
code := http.StatusOK
switch {
case os.IsNotExist(err):
code = http.StatusNotFound
case os.IsPermission(err):
code = http.StatusForbidden
default:
code = http.StatusInternalServerError
}
http.Error(writer,
http.StatusText(code), code)
}
}
}
func main() {
http.HandleFunc("/list/",
errWrapper(filelisting.HandleFilelist))
err := http.ListenAndServe(":8888", nil)
if err != nil {
panic(err)
}
}
测试用例文件命名:*_test
可以进入文件所在目录,命令行使用命令:go test . 运行测试文件
testing.T
package main
import "testing"
func TestSubstr(t *testing.T) {
tests := []struct {
s string
ans int
}{
// Normal cases
{"abcabcbb", 3},
{"pwwkew", 3},
// Edge cases
{"", 0},
{"b", 1},
{"bbbbbbbbb", 1},
{"abcabcabcd", 4},
// Chinese support
{"哇哈哈哈哇", 6},
{"一二三二一", 3},
{"黑化肥挥发发灰会花飞灰化肥挥发发黑会飞花", 8},
}
for _, tt := range tests {
actual := lengthOfNonRepeatingSubStr(tt.s)
if actual != tt.ans {
t.Errorf("got %d for input %s; "+
"expected %d",
actual, tt.s, tt.ans)
}
}
}
IDEA里按钮
命令行:go test -coverprofile=c.out
go tool cover 查询用法
go tool cover -html=c.out
func BenchmarkSubstr(b *testing.B) {
s := "黑化肥挥发发灰会花飞灰化肥挥发发黑会飞花"
for i := 0; i < 13; i++ {
s = s + s
}
b.Logf("len(s) = %d", len(s))
ans := 8
/*除去测试数据准备时间,只计算实际代码运行时间*/
b.ResetTimer()
for i := 0; i < b.N; i++ {
actual := lengthOfNonRepeatingSubStr(s)
if actual != ans {
b.Errorf("got %d for input %s; "+
"expected %d",
actual, s, ans)
}
}
}
IDEA里运行BenchmarkSubstr
或者命令行里用:go test -bench .
go test -bench . -cpuprofile cpu.out
go tool pprof cpu.out
交互式命令行:
(pprof)web (需要先安装Graphviz)
(pprof)quit