golang基础学习

ice_201507 · · 4869 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

刚找了个新工作,应公司的要求必须用go语言来开发服务器。特此将自己对于go语言的学习经历写出来,帮助那些正在golang学习路上挣扎的迷茫的小伙伴们。

本文只是讲解自己最基础的一个学习过程,从最基础的变量开始,到最后利用golang搭建一个简单的服务器项目的过程,肯定有不少的错误或瑕疵,千万别当做标准的文档去看,仅仅是自己的一个学习过程的总结。希望大家理解,也仅仅是自己对自己每周学习过程的一个总结,每周更新一次。带大家用代码学习golang,用开源项目学习整个项目的框架逻辑。抽象的东西最好用实例来理解



使用环境:ubuntu 14+go1.4+sublime 2(go插件gosublime)

遇到不会的函数---查看官方包文件 http://godoc.golangtc.com/pkg/


golang基础

变量,array/slice/map,struct/func/method/interface,channel


具体内容参考我的CSDN代码页


1.怎样让map[string][]string 根据key的值按顺序输出





const--type--array(创建,数组指针/指针数组,元素遍历,冒泡排序)
slice(2种创建/append/copy)
map(创建,值类型为map,交换键值顺序,range)
函数(调用/参数的值传递和址传递,不定参/defer,匿名函数作为值,匿名函数()问题,错误处理)
结构体(创建和使用赋值,值传递和址传递,嵌套结构)
方法(为类型添加方法,面向对象和面向过程区别,求矩形面积和周长,修改对象的值
接口赋值,)
接口(冒泡排序,)
并发(开启新线程,channel,close,select,)
web应用




----------------------------------------------------------------------------------------go基础




25个内置关键字:
package--import--type--struct--interface--const--func--var--if--else
switch--case--default--for--range--goto--break--continue--return--map
go--chan--select--defer--fallthrough


内置函数:15个
close--delete--len/cap--new--make--append--copy--print--println
pannic--recover--3复数函数


new(type) 用于各种类型的内存分配
make(map/slice/channel) 用于这3中类型的内存分配
var 声明  make初始化  都不需要赋值






1.变量
变量就是存储某种或者某些数据的存储器,对一块数据存储空间的命名,引用变量名来使用这块空间
变量类型
基础数据类型:
整型:int/uint int8/uint8 int16/uint16 int32/uint32 int64/uint64
浮点型: float32/float64
布尔型:true false    1字节
字节型:byte          ===uint8  UTF-8字符串单个字节
字符型:rune          Unicode字符
字符串:string        []byte
错误类型:error


复合类型:
指针:pointer  *i
数组:array   [n]int  [...]string
切片:slice   []int
字典:map     map[int]string
通道:chanel  chan int   c<-   -<c
结构体:struct  
接口:interface 
函数:func(){}


自定义类型  type




2.常量 constants
在编译时就已经确定的值
后面声明的局部变量和前面的常量名相同,则会覆盖前面的常量值




3.数组array
(1)数组是一系列同一类型的数据的集合,在内存上地址是连续的,且长度在定义后无法修改
(2)数组是值类型,所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作,
在函数体重无法修改传入的数组的内容,因为操作的只是该数组的一个副本




4.slice
(1)看起来类似一个指向数组的指针,作为参数传递是不会产生拷贝,
它的数据结构可以抽象为3个变量(指向原生数组的指针,len,cap)
可以随时动态的扩充存放空间
(2)基于数组和直接创建,


5.map
(1)map是一组键值对的未排序集合,有delete删除对应键的值
(2)类似map[2][1]的返回值


6.function
(1)go语言的函数有 不定参数,多返回值,匿名函数和闭包,函数也是一种类型
即是值类型又是引用类型,有匿名函数有无()的区别,有返回值的函数可以当做一个值
赋值给对应的变量,没有返回值的函数不能使用赋值操作
(2)函数的调用,包名.函数名   但是对应的函数名必须在包内是大字母开头,
go语言中没有封装的概念,没有private和public。
(3)go语言的函数可以像普通变量一样被传递或者使用,匿名函数即不需要定义函数名
的一种实现方式,只需要关键字func
(4)匿名函数就是一个闭包,闭包关键的是局部变量和全局变量
闭包的价值在于 可以将函数存储到变量中作为参数传递给其他函数,
而且能够被函数动态的创建和返回
(5)错误处理
go语言引入了一个关于错误处理的标准模式,定义了一个error接口类型,
里面有一个Error()方法,一般将函数的最后一个返回值作为异常信息
经典用法errors.New("error strings")
自定义错误处理函数func A() error{} 即返回值为错误类型




7.struct(类)
(1)GO语言的结构体实现面向对象的功能,一般用&来初始化结构体,这样在后面对于
结构的传递就是地址传递,不会产生拷贝。初始化类似于map,用: 也可以不用,
但一定要有{},数组也是{}。值和指针都是可以直接用 . 来操作
(2)嵌套结构的实现,类似于其他语言的继承,
go语言的面向对象编程,有结构和方法,接口组成




8.method
(1)方法就是有接收者的匿名函数,可以为任何类型(除指针和接口)添加别名成为新类型
然后为新定义的类型创建函数操作,即为类型添加方法
(2)强制性的第一个参数receiver,即要有接收者,
(3)method value和method expression




9.interface
(1)接口就是一个或多个方法签名的的集合,接口为定义,方法为实现
因此接收者不能是接口或者指针,接口名字为方面名加上er
要实现某个接口,只需要实现该接口要求的所有函数即可。
(2)当要修改对象时,才必须用指针,不是必须的,不是go语言的约束
有时对象很小(4字节),用指针传递并不划算
(3)接口赋值,将对象实例赋值给接口。对象实例即声明对应类型的变量
接口组合:ReadWriteer
(4)Any类型,空接口interface{},任何实例对象都满足空接口,可以指向任何对象




10.concurrency
(1)高并发是go语言的特点,goroutine是官方实现的超级线程池
占用4-5KB的栈内存,并发是由cpu切换时间片来实现同时运行,不是并行
同一个程序中的goroutine共享同一个地址空间。
一般通过睡眠的方式来编排线程之间的相互等待不方便,有管道
(2)channel,go语言的一个构件,引用类型,基于make函数来分配
通信模型是 消息机制即channel,不是共享内存的方式
提供一种机制用于goroutine之间通过传递指定类型的值进行通信
(3)默认的无缓冲管道,发送方会阻塞直到接收方从管道中接收了值,
创建的有缓冲管道,发送方会阻塞直到发送的值被拷贝到缓冲区,如果
缓冲已满,那么接收方在有值可以接收之前会一种阻塞
(4)close 函数标志着不会在往某个管道发送值并且在之前发送的值都被接收后
接收操作会返回一个类型零值,不会阻塞
(5)select, 监听管道上接收的数据,替代原来的<-chan接收数据的方式
(6)死锁。线程之间相互等待,其中任何一个都无法向前运行的情形
当没有任何goroutine能够前往执行的情况发送时,go会提供详细的错误信息
(7)互斥锁,数据竞争data race,避免它的唯一方式就是线程间同步访问所有的
共享可变数据。
处理并发数据访问的推荐方式是:使用管道从一个goroutine往下一个goroutine
传递实际的数据
对所有共享数据的访问,不管读写,仅当goroutine持有锁才能操作,
(8)共享内存:不同进程间的内存是相互独立的,没办法直接操作对方内的数据
内存映射机制,让不同进程的同一块地址空间映射到同一个虚拟内存区,


11.搭建一个http(WEB)服务器
(1)http.listenAndServer(":1234",nil)
开始监听服务,表示本机所有ip地址的1234端口,提示页面没找到表示http服务
已经开启,即第二个参数决定路径网页内容等
(2)myServeHttp(w http.ResponseWrite,r *http.Request)
注意,监听函数的第二个参数是Handle接口,里面实现了ServeHttp()方法,要写入监听函数
的第二个参数里面,方法名一定要是这个,可以定义一个新类型,添加这个方法名,具体
实现自己写,传参数时&a{}
(3)http.ResponseWrite   字面意思即服务器给客户端的回应,并写出来
用 io.WriteString(w,"要传递个客户端的内容")
(4)* http.Request    字面意思即客户端给服务器的请求,
如r.URL.String()即客户端的url地址
(5)http.HandleFunc("/",myServeHttp)注册一个myServeHTTP函数给"/",但浏览器浏览"/"
会自动调用myServeHTTP函数。注册路由,
(6)通过服务器传送文件给客户端
查看句柄,编辑句柄,保存句柄
(7)html文档直接写在打印语句(数据)里面,传送给客户端。
由html/template模板库创建html文件,执行对应句柄,并可以渲染模板,精简代码量
访问不存在的页面的处理方式,返回对应的状态码。尽量添加错误处理方式。
template.ParseFiles()经常调用很浪费资源,可以将所有要渲染的文件放在一个模板指针里
(8)安全问题




12.http协议
(1)我们输入URL后,我们的浏览器给Web服务器发送了一个Request, Web服务器接到Request后进行处理,生成相应的Response,然后发送给浏览器, 浏览器解析Response中的HTML,这样我们就看到了网页
(2)发送的一个url就是http request
URL详解
基本格式如下
schema://host[:port#]/path/.../[?query-string][#anchor]
http://www.mywebsite.com/sj/test/test.aspx?name=sviergn&x=true#stuff
(3)HTTP消息的结构
先看Request 消息的结构,   Request 消息分为3部分,第一部分叫Request line, 第二部分叫Request header, 第三部分是body。 header和body之间有个空行


Response消息的结构, 和Request消息的结构基本一样。 同样也分为三部分,第一部分叫Response line, 第二部分叫Response header,第三部分是body. header和body之间也有个空行
(4)r.header
服务器和客户端的r.header是不一样的,结构都是 map[string][]string
客户端:map[Content-Type:[text/plain;charset=utf-8],Date:[Fri,...],Content-Length:[25]]
服务器:map[User-Agent:[go 1.1 package http],Referer:[http://..],Accept-Encoding:[g-zip]]


自定义http header
(5)Http get,post,soap协议都是在http上运行的
1)get:请求参数是作为一个key/value对的序列(查询字符串)附加到URL上的
      查询字符串的长度受到web浏览器和web服务器的限制(如IE最多支持2048个字符),不适合传输大型数据集同时,它很不安全
2)post:请求参数是在http标题的一个不同部分(名为entity body)传输的,这一部分用来传输表单信息,因此必须将Content-type设置为:application/x-www-form-urlencoded。post设计用来支持web窗体上的用户字段,其参数也是作为key/value对传输


服务器通过请求的指针里面 r.ParseForm()来获得客户端通过GET/POST方式传递的参数




13.go工具
go使用静态链接,所有
标准类库的packages可以使用短名字,如:fmt。对于你自己的Packages,最好提供一个base path(基路径),这可以避免命名冲突,下面是我的命名原则
happygo.codeplex.com/study/hello
package main //command executable(命令行可执行程序)必须使用main作为package名字


包名字(Package Name)
一些规则:
一个package(文件件)中的所有文件必须使用相同的名字。
Go语言约定import path(导入路径)的最后一个元素是包名字。
Executable Commands必须使用 package main。


import(加载模块,即寻找对应的.go文件)
标准库,相对路径,绝对路径,点操作,别名操作,_操作
import "fmt" fmt是标准库,它是去goroot下加载改模块
加载自己写的模块,默认是$GOPATH/src
import "./model"  当前文件的同一目录的model子目录
import "/project/model" 加载 $GOPATH/src/project/model


_操作,表示对包的引用,而不是直接使用包里面的函数,只是调用了该包里面的init()函数,自己在新建自己的包也要定义相关的init函数
程序初始化和执行都起始于main包,执行import,再对包级常量和变量初始化,再执行init()函数,最后执行下面的内容


go语言编译与链接
go build --在当前目录下生成一个bin文件,不是main包的文件什么都不会生成
go install --自动生成bin pkg文件夹,在里面生成bin文件和.a文件。是针对包不用加文件名直接后面跟的是文件夹名称,有个main包,里面有其他的自己新建的包,
导入格式为相对路径或者绝对路径
且只有再 goinstall后,生成了.a文件,才能在调用包的时候成功的看到包内大写字母开头的函数,才能.处理,否则是看不到的
链接是将link,将主程序与库合成一个可执行的程序。改变了某个包后,必须要重新编译,这是go语言的特点,但是编译快


把自己写的包打包成.a格式  在src目录下运行,go install project/sort1   也可以直接去对应目录的对.go文件,直接go install,不加文件名
自己写的包只有在go install 打包后才能用。也可以在main包中import对应的包,再去main.go下面直接go install
并且只有main包的main函数才会生成bin文件,多次import同一个包,只会导入一次
GO的引用是  一级包/二级包/包名 比如example/newmath


测试是针对包,不用再写个main去执行对应的函数,直接在Testxx函数里面实现
测试代码中,函数可以用如下方式命名:Test_T_M,其中T为类型名,M为方法名,这样容易区分,但这不是Go语言的强制要求。
go test -run=Test_Add




大环境搞明白了,语法就快了,再学习一下语法,然后开发个应用试试


14.git
flag包
对命令行参数的解析
定义flags的2种方式
var ip=flag.Int("flagname",1234,"help message")
var flagvar int---初始化函数
flag.IntVar(&amp,"flagVal",1324,"helpmessage")
命令行参数使用形式的
./hello --a "pa" -age=132 


通过flag学标准库的制定方法
flag.Args()----------返回的是一个 []string,是string数组
flag.Arg(i)----------返回的是对应参数i的输入的参数,单个的string
flag.NArg()----------返回的是输入的参数的个数


git add test.go---git commit -m "message"---git push name1 master
git remote 查看当前配置有哪些远程仓库,默认为origin,
添加 git remote add name1 http://github.com/jllei/basic或者是git://github.com/.. 
git branch 列出本地分支,-r/a,
创建 git branch newbranch1  删除 git branch -d |-D branchname  删除远程分支 git branch -d -r branchname
切换分支  git checkout branchname
推送本地分支到远程分支  git push origin branchname
合并分支  git checkout master --- git diff --- git merge b0702合并 ---  删除git branch -d b0702 


master分支是非常稳定的,仅用来发布新版本,平时工作都在自己新建的分支 dev上,再建自己的分支,最后合并到dev上


15.golang操作mysql
go get github.com/go-sql-driver/mysql


16.markov
马尔科夫链算法生成任意文本
一个链由一个前缀和后缀组成 模拟 map[string][]string
前缀是一个字符串数组,处理后为: 加了空格的,拥有prefixlen个单词的字符串 "i am s" 长度为3
后缀是一个单词,一个前缀可以有多个后缀,模拟为{"a","not"}
前缀 []string{"i","am"} --- 映射键 "i am"  将字符串连同空格一起加入到其中生成映射键


前缀类型 type Prefix []string ----
并添加2个方法func (p *Prefix) str() string{} 将前缀作为字符串(映射键)返回,
func (p *Prefix) Shift(word string){} 将前缀中移除第一个单词并追加上给定的单词 "i am" --"am not"


链初始化函数 / 构造函数 func NewChain(prefixlen int) *Chain{}


Chain结构体类型,包含了前缀到后缀的映射map[string][]string 和前缀长度prefixlen---
添加2个方法  func(c *Chain)Build(r io.Reader){} 构建链,从Reader接口中读取文本,用缓存输入,这里是os.stdin,解析并储存
func (c *Chain)Generate(n int)string{} 返回一个从Chain中生成的,最多有n个单词的字符串




--------------------------------------------------------------------------------------------code
--------------------------------------------------------------------------------------------code
--------------------------------------------------------------------------------------------code


------------------------------------------------------const/type


const a "ss"
const(
a=3
b
f="yes"
c=iota
d
)


type a int
type(
b string
c []byte
d bool
e fun(int) (string,error){}
)






-------------------------------------------------------array


func main() {
var arr1 [5]int
arr1 = [5]int{1, 3, 45, 8, 21}
arr2 := [...]string{"go", "lang"}
arr3 := new([4]int)
*arr3 = [4]int{1, 2, 3, 4}
arr3[0] = 99
fmt.Println(arr1, arr2, arr3)
var p *[5]int = &arr1
x, y := 2, 3
arr4 := [3]*int{&x, &y}
fmt.Println(p, arr4)
arr5 := [3][2]int{{3, 4}, {}}
fmt.Println(arr5)
}


---------------------------------元素遍历


func main() {
a := [5]int{3, 23, 45, 23, 77}
for i := 0; i < len(a); i++ {
log.Printf("a[%d]=%d\n", i, a[i])
}
b := []string{"aa", "sfsf", "papa", "dkdk"}
for i, ok := range b {
log.Printf("a[%d]=%c\n", i, ok)
log.Println(i, ok)
}
c := [5]int{99, 99, 99, 99, 99}
for i, ok := range c {
log.Println(i, ok)
}
}


---------------------------------冒泡排序
func main() {
arr := []int{22, 15, 25, 18, 23}
BubbleSort(arr)
}


func BubbleSort(a []int) {
if len(a) < 1 {
return
}
for i := 0; i < len(a); i++ {
for j := i + 1; j < len(a); j++ {
if a[i] > a[j] {
a[i], a[j] = a[j], a[i]
}
}
}
fmt.Println(a)
}




-------------------------------------------------------slice




func main() {
arr := [9]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
var s1 []int
s1 = []int{2, 3, 4, 5}
s2 := arr[1:6]
s3 := arr[6:]
s4 := s2[0:3]
fmt.Println(s1, s2, s3, s4)
s4[0] = 99
fmt.Println(s1, s2, s3, s4)


s5 := make([]string, 2, 5)
s5 = []string{"hello", "world", "!"}
s6 := new([]int)
*s6 = []int{2, 3, 4, 5, 6, 9}
//s6[3] = 4 数组切片指针不支持[]操作
fmt.Println(s5, s6)


s2 = append(s2, 33, 44, 55, 55, 6)
//arr = append(arr, 3, 4)  只有切片才能用append
fmt.Println(s2, arr, len(s2), cap(s2))


copy(s2, s3)
fmt.Println(s2, s3, arr)
}




--------------------------------------------------------map操作
func main() {
var m1 map[int]int
m1 = map[int]int{1: 1, 2: 2, 3: 3}
m2 := map[int]string{1: "a", 2: "b"}
m3 := make(map[int]int, 4)
m3[1] = 33
fmt.Println(m1, m2, m3)
a := m2[2]
fmt.Println(a)
delete(m1, 2)
fmt.Println(m1)


m4 := make(map[int]map[int]string)
fmt.Println(m4)
//m4[1][1] = "OK" 必须先初始化外层第一个键的map
m4[1] = make(map[int]string) //初始化内层的map
m4[1][1] = "OK 1"
m4[1][2] = "OK 2"
fmt.Println(m4)


m5 := make(map[int]map[int]string)
a, ok := m5[2][3] 
//返回2个值,一个是键对应的值,一个是是否返回值,bool型
fmt.Println(a, ok)
if !ok {
m5[2] = make(map[int]string)
}
m5[2][3] = "value and bool"
a, ok = m5[2][3]
fmt.Println(a, ok, m5)


//-------------迭代操作,拷贝,改变值
sm1 := make([]map[int]string, 5)
for _, v := range sm1 {
v = make(map[int]string, 1)
v[1] = "OK"
fmt.Println(v)
}
fmt.Println(sm1)


sm2 := make([]map[int]string, 5)
for i := range sm2 {
sm2[i] = make(map[int]string)
sm2[i][1] = "OK"
fmt.Println(sm2[i])
}
fmt.Println(sm2)
//----------------交换键和值的顺序
m6 := map[int]string{1: "a", 2: "b", 3: "c", 4: "D", 5: "F"}
m7 := make(map[string]int)
for k, v := range m6 {
m7[v] = k
fmt.Println(k, v, m7)
}
fmt.Println(m7)
}


----------------------------------------------------------函数
func main() {
var1 := 99
A(var1)              
//无返回值的函数就是一个表示式,不能使用赋值操作c:=A(var1)错误
b := B(var1)         //B有返回值可以作为一个值赋给变量,函数的调用
fmt.Println(b, var1) //函数AB也是值传递,对实参没影响
C(&var1)             //传递的是实参的地址,修改他的内容*
fmt.Println(var1)
var s error
fmt.Println(s)
f1(2, 4, 5, 7, 8)


}


func A(a int) {
a = 1
fmt.Println("func A", a)//值传递和指针传递
}


func B(a int) int {
a = 2
fmt.Println("func B", a)
return a
}


func C(a *int) {
*a = 9
fmt.Println("func C", *a)
}


------------------------不定参,defer


func f1(arg ...int) { 
//接受不定数量的参数,在函数体中,变量arg是一个int型的slice
for i, v := range arg {
fmt.Println(i)
fmt.Println(v)
fmt.Printf("arg index:", i, "arg value:", v)//打印的值有点怪
}
}


func main() {
a := func() { //匿名函数,赋值给a,后面没有(),函数作为一个值
fmt.Println("hello world")
}
a()                   //调用
fmt.Printf("%T\n", a) //%T打印变量的类型
f1()
f()
}


func f1() {
for i := 0; i < 5; i++ {
defer fmt.Println(i) //延迟代码,后进先出的顺序,
}
}


func f() (ret int) {
defer func() { //defer访问返回值
ret++
}()
return 0
}


------------------函数体内使用匿名函数作为值传递给变量
func main() {
var k = 4
a := func(i int) func() {
return func() {
fmt.Println("i + k=", i+k)
}
}
fmt.Printf("%T\n", a)
b := a(3)
b()
}


func main() {
var k = 4
a := func(i int) func() {
return func() {
fmt.Println("i + k=", i+k)
}
}(4)//这里加了()后表示已经调用了,a的类型已经变成了func()
//但是不会打印任何东西,因为返回值是个闭包,里面的内容外部是看不见的
//只有调用了内部的函数,闭包后才能显示打印,但不要再加参数
fmt.Printf("%T\n", a)
a()
}


------------------------匿名函数()问题
func main() {
f := func(i, j int) (result int) {
result = i + j
return
} //--------创建匿名函数不执行,加了()就直接执行,这里要调用
fmt.Fprintf(os.Stdout, "f=%v,f=%T,f(1,3)=%v\n", f, f, f(1, 3))
//匿名函数调用,这里f的类型是func(int,int)int,和下面x,y不一样
x, y := func(i, j int) (m, n int) {
return j, i
}(1, 9) //直接执行的匿名函数就可以当做一个值来传递,x,y的类型是int
fmt.Fprintf(os.Stdout, "x=%d,x=%T,y=%d\n", x, x, y)


}


func main() {
f := close(4) //调用了函数后,就得到了返回值,这个参数是x
//故而f的类型是func(int) int,是close函数的返回值类型,即匿名函数类型,
fmt.Printf("%T\n", f)
b := f(1) //调用匿名函数后的返回值类型为int,这个参数是y
fmt.Printf("b=%d,type=%T\n", b, b)
}


func close(x int) func(int) int { 
//调用close函数,return并不会执行打印的语句
fmt.Println(x)
return func(y int) int {
fmt.Println(x, y)
return x + y
}
}




----------------------------------------------------------结构体




type person struct {
Name string
Age  int
}


func main() {
p1 := &person{"joe", 22}
p2 := person{Name: "jim", Age: 25}
p3 := &person{
Name: "jack",
Age:  20,
}
p4 := person{}
fmt.Println(p1, p2, p3, p4)
//A(*p1)
B(p1)
fmt.Println(p1, p2, p3, p4)
}


func A(p person) {
p.Age = 33
p.Name = "change p1"
fmt.Println(p)
}


func B(p *person) {
p.Age = 33
p.Name = "change p1"
fmt.Println(p)
}


---------------------匿名结构
func main() {
a := &struct {
Name string
Age  int
}{
Name: "joe",
Age:  19,
}
fmt.Println(a)
}


---------------------嵌套embedding结构
type person struct {
sex string
}


type teacher struct {
person
Name string
Age  int
}


type student struct {
person
teacher
Name string
Age  int
}


func main() {
a := &person{}
b := &teacher{}
c := &student{}
fmt.Println(a, b, c)
a.sex = "male"
b.person.sex = "female"
b.Age = 22
b.Name = "jack"
c.Age = 99
//遇到不同结构体的同名变量时,默认是第一层的变量的值
fmt.Println(a, b, c)
c.sex = "female"
c.teacher.Name = "joe"
fmt.Println(a, b, c)
}




----------------------------------------------------------方法


type person struct {
Name string
Age  int
}


func (p *person) change() {
//改为func (p person)后就是值拷贝,调用该方法后对实参是没有影响的
p.Name = "has been changed name"
fmt.Println(p)
}


func main() {
a := &person{}//若将这里改为a:=person{},则后面的值就变为{},而不是&{}
fmt.Println(a)
a.change()
fmt.Println(a)//因为传递的是指针,所有a的值被改变
}


-----------------------面向对象和面向过程的区别


type interger int


func (*interger) compare(a, b int) bool {
return a < b
}
func (a interger) less(b interger) bool {
return a < b
}


func it_less(a, b interger) bool {
return a < b
}


func main() {
//面向对象的用法
var c interger
fmt.Println(c.compare(5, 3)) //用不到接收者就省略,但是类型一定要有
var d interger = 1
fmt.Println(d.less(3)) //d就是方法的接收者,
//面向过程的用法
fmt.Println(it_less(5, 3))
}


---------------------------求矩形的面积和周长
type rect struct {
width, height int
}


func (r *rect) area() int {
return r.width * r.height
}


func (r rect) perim() int {//若不需要用到实参,则值址传递无所谓,尽量用&
return 2*r.width + 2*r.height
}


func main() {
r := rect{width: 10, height: 5}
fmt.Println("area=", r.area(), "perim=", r.perim())


rp := &r
fmt.Println("area=", rp.area(), "perim=", rp.perim())
}


--------------修改对象的值
type Interger int


func (a *Interger) add(b Interger) { 
//接收者为指针,可以修改实参的值,若是a Interger则不能改变,拷贝
*a += b
}


func main() {
var a Interger = 1
fmt.Println(a)
a.add(4)
fmt.Println(a)
}




------------------------------------------------------接口


--------------------------接口实现排序


package main


import "fmt"


type Sorter interface {
len() int
less(i, j int) bool
swap(i, j int)
}


type xi []int
type xs []string


func (p xi) len() int {
return len(p)
}
func (p xi) less(i, j int) bool {
return p[i] > p[j]
}
func (p xi) swap(i, j int) {
p[i], p[j] = p[j], p[i]
}


func (p xs) len() int {
return len(p)
}
func (p xs) less(i, j int) bool {
return p[i] > p[j]
}
func (p xs) swap(i, j int) {
p[i], p[j] = p[j], p[i]
}


func Sort(x Sorter) {
for i := 0; i < x.len(); i++ {
for j := i + 1; j < x.len(); j++ {
if x.less(i, j) {
x.swap(i, j)
}
}
}
}


--------------------------接口赋值
type Interger int


func (a Interger) Less(b Interger)bool{
return a<b
}
func (a *Interger) Add(b Interger){
*a=b
}//为新类型添加方法
type LessAdder interface{
Less(b Interger) bool
Add(b Interger)
}//定义我们自己的接口er,怎样给接口赋值?对象实例和接口


var a Interger =1
//Interger类型的对对象实例,新建一个接口类型实例
//var file package.LessAdd=new(File)
var b LessAdder=&a
var b LessAdder=a
//利用第一个语句&a,go语言可以根据定义的Less方法,
自动生成一个新的Less()方法:
func (a *Interger) Less(b Interger) bool{
return (*a).Less(b)
}//这样*Interger就即存在Less()方法也存在Add()方法,满足接口。
但是,根据func(a Interger)Add(b Interger)
无法自动生成下面这个成员方法
func (a Interger)Add(b Interger){
(&a).Addr
}//因为,它改变的只是函数参数a,对外面的实参没有影响
//因此类型Interger只存在Less()方法,缺少Add()方法,不满足接口。
--------------------------------
type Lesser interface{//新定义一个接口
Less(b Interger) bool
}
var a Interger=1
var b1 Lesser=a
var b1 Lesser=&a
这两种赋值方法都可以编译通过,都实现了接口里面的方法
-----------------------------接口查询,是否实现了接口






------------------------------------------------------并发


func main() {
a := xi{15, 2, 66, 24, 19}
b := xs{"sf", "pa", "dk"}
Sort(a)
Sort(b)
fmt.Println(a, b)
}


func main() {
go func() {
fmt.Println("one thread")
//go开启一个新的线程,和main执行顺序不确定
}()
time.Sleep(time.Second * 5)
fmt.Println("main thread")


}


--------------------channel
var c chan int //可用来发送和接收int类型的值
//var s chan <-string  仅可用来发送string类型的值
//var f <-chan float32  仅可用来接收int类型的值
//var s1 <-chan struct{}  空结构体管道,仅用于发送信号,不是传递数据


func main() {
c = make(chan int)
//c1 :=make(chan *byte,10) 带缓冲的byte类型指针管道
go func() {
fmt.Println("one thread")
c <- 1
}()
go func() {
fmt.Println("Second thread")
time.Sleep(time.Second)
c <- 2
}()
go func() {
fmt.Println("third thread")
c <- 3
}()


fmt.Println("main thread")
<-c
<-c
<-c
close(c)
}


-----------------------close
func main() {
ch := make(chan string)
go func() {
ch <- "hello"//只发送一次
close(ch)//标志着不会再向管道里发送值
}()
fmt.Println(<-ch)//输出字符串"hello"
fmt.Println(<-ch)//输出字符串零值,为空""
fmt.Println(<-ch)再次打印空值
v, ok := <-ch//v的值为空"",ok的值为false
fmt.Println(v, ok)
}


----------------------select


func main() {
c1 := make(chan string)
c2 := make(chan string)


go func() {
//time.Sleep(time.Second * 2)
c1 <- "one"
}()
go func() {
//time.Sleep(time.Second * 2)
c2 <- "two"
}()


for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println(msg1)
case msg2 := <-c2:
fmt.Println(msg2)
}
}
}


---------------------------共享数据
func main() {
wait := make(chan int)
n := 0 //所有的goroutine共享的数据
//不应该通过共享数据来通信,而要通过通信来共享数据
go func() {
s := 99 //仅为一个goroutine可见的局部变量
n++
wait <- n //数据从一个goroutine离开,发送,也起到同步作用
wait <- s
close(wait)
fmt.Println(<-wait)
}()
go func() {
n++
<-wait //从一个管道接收
<-wait
close(wait)
}()
s := <-wait
fmt.Println(n)
fmt.Println(s)
}


----------------------------------互斥锁
import (
"fmt"
"sync"
)


type s struct { //一个并发的数据结构
mu sync.Mutex //锁,一次只能被一个goroutine访问
n  int
}


//为该结构添加2个方法Add(int)  value() int
func (a *s) Add(n int) {
a.mu.Lock() //等待锁释放,然后持有它
a.n += n
a.mu.Unlock() //释放锁
}
func (a *s) value() int {
a.mu.Lock()
n := a.n
a.mu.Unlock()
return n
}


func main() {
wait := make(chan struct{})
var n s
go func() {
n.Add(1) //一次访问
close(wait)
}()
n.Add(1) //另一个并发访问
<-wait
fmt.Println(n.value())
}






---------------------------------------------------------HTTP
func main() {
    http.ListenAndServe(":8080", nil)
}//当第二个参数是nil时,http内部会自己建立一个叫DefaultServeMux
//的ServeMux,要向这个mux里面注册的话,就要用http.HandleFunc("/",sayhello)
-------------------------
type a struct{}


func (*a) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "hello world version 1.")
}


func main() {
    http.ListenAndServe(":8080", &a{})
    //第2个参数需要实现Handler的struct,a满足
}


---------------------------
type b struct{}


func (*b) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "hello")
}
func main() {
    mux := http.NewServeMux()
    //ServeMux有一张map表,key是r,URL.String(),value记录的是一个方法
    //类似ServeHTTP,别名叫HandlerFunc
    mux.Handle("/h", &b{})
    //用来注册HandlerFunc
    //ServeMux里面实现了2个方法Handle,ServeHTTP,这样实现了Handler接口
    //才能当监听函数的第二个参数传递
    http.ListenAndServe(":8080", mux)
}
----------------------------
func main() {//跳过结构b,用mux.HandleFunc()来注册func到map表中
    mux := http.NewServeMux()
    mux.HandleFunc("/h", func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "hello")
    })
    mux.HandleFunc("/bye", func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "byebye")
    })
    mux.HandleFunc("/hello", sayhello)
    http.ListenAndServe(":8080", mux)
}
func sayhello(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "hello world")
}


------------------------------
func myhandle(w http.ResponseWriter, r *http.Request) {
path := r.URL.String()
fmt.Fprintf(w, "hi there,i love %s!\n", path[1:])
}
func main() {
http.HandleFunc("/", myhandle)
http.ListenAndServe(":1000", nil)
}
-----------------------通过服务器给客户端发送文件
type Page struct {
Title string
Body  []byte
}
func (p *Page) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}


func loadpage(title string) (*Page, error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Page{Title: title, Body: body}, nil
}


func myhandle(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[4:]
p, _ := loadpage(title)
fmt.Fprintf(w, "<h1>%s</h1><div>%s<div>", p.Title, p.Body)
}
func main() {
http.HandleFunc("/my/", myhandle)
http.ListenAndServe(":1000", nil)
}


---------------------------一个简单的传送html数据


package main


import (
"fmt"
"io/ioutil"
"net/http"
)


type Page struct {
Title string
Body  []byte
}


func loadpage(title string) (*Page, error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Page{Title: title, Body: body}, nil
}


func viewhandle(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/view/"):]
p, _ := loadpage(title)
fmt.Fprintf(w, "<h1>%s</h1><div>%s<div>", p.Title, p.Body)
}


func edithandle(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
p, err := loadpage(title)
if err != nil {
p = &Page{Title: title} //如果文件不存在就创建
}
fmt.Fprintf(w, "<h1>Editing %s</h1>"+
"<form action=\"/save/%s\" method=\"POST\">"+
"<textarea name=\"body\">%s</textarea><br />"+
"<input type=\"submit\" value=\"save1\">"+
"</form>",
p.Title, p.Title, p.Body)
}


func main() {
http.HandleFunc("/view/", viewhandle)
http.HandleFunc("/edit/", edithandle)
http.HandleFunc("save", savehandle)
http.ListenAndServe(":1000", nil)
}


--------------------利用html模板/渲染模板
http://godoc.golangtc.com/doc/articles/wiki/


import (
"html/template"
"io/ioutil"
"net/http"
)


type Page struct {
Title string
Body  []byte
}


func (p *Page) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}
func loadpage(title string) (*Page, error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Page{Title: title, Body: body}, nil
}


func viewhandle(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/view/"):]
p, err := loadpage(title)
if err != nil {
p = &Page{Title: title}//访问不存在的网页时,显示空内容
//http.Redirect(w, r, "/edit/"+title, http.StatusFound)
return
}
t, _ := template.ParseFiles("view.html")
t.Execute(w, p)
}


func edithandle(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
p, err := loadpage(title)
if err != nil {
p = &Page{Title: title} //如果文件不存在就创建
}
t, _ := template.ParseFiles("edit.html")
t.Execute(w, p)
}


func savehandle(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/save/"):]
body := r.FormValue("body")
p := &Page{Title: title, Body: []byte(body)}
p.save()
//http.Redirect(w, r, "/view/"+title, http.StatusFound)
}


func main() {
http.HandleFunc("/view/", viewhandle)
http.HandleFunc("/edit/", edithandle)
http.HandleFunc("/save/", savehandle)
http.ListenAndServe(":1000", nil)
}


view.html
<h1>{{.Title}}</h1>
<p>[<a href="/edit/{{.Title}}">edit</a>]</p>
<div>{{printf "%s" .Body}}</div>


edit.html
<h1>Editing {{.Title}}</h1>
<form action="/save/{{.Title}}" method="POST">
<div><textarea name="body" id="t1" cols="80" rows="20">{{printf "%s" .Body}}</textarea></div>
<div><input type="submit" value="save"></div>
</form>


--------------------------------增加安全性


import (
"errors"
"html/template"
"io/ioutil"
"net/http"
"regexp"
)


var templates = template.Must(template.ParseFiles("edit.html", "view.html"))
var validPath = regexp.MustCompile("^/(edit|save|view).([a-zA-Z0-9]+)$")


//这个函数会解析和编译这个正则表达式,返回一个指针和错误值
type Page struct {
Title string
Body  []byte
}


func (p *Page) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}
func loadpage(title string) (*Page, error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Page{Title: title, Body: body}, nil
}


func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
err := templates.ExecuteTemplate(w, tmpl+".html", p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = t.Execute(w, p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}


func getTitle(w http.ResponseWriter, r *http.Request) (string, error) {
m := validPath.FindStringSubmatch(r.URL.Path)
if m == nil {
http.NotFound(w, r)
return "", errors.New("Invalid page title")
}
return m[2], nil //title的值在第二个表达式里面
}


func viewhandle(w http.ResponseWriter, r *http.Request) {
title, err := getTitle(w, r)
if err != nil {
return
}
p, err := loadpage(title)
if err != nil {
http.Redirect(w, r, "/edit/"+title, http.StatusFound)
return
}
renderTemplate(w, "view", p)
}


func edithandle(w http.ResponseWriter, r *http.Request) {
title, err := getTitle(w, r)
if err != nil {
return
}
p, err := loadpage(title)
if err != nil {
p = &Page{Title: title} //如果文件不存在就创建
}
renderTemplate(w, "edit", p)
}


func savehandle(w http.ResponseWriter, r *http.Request) {
title, err := getTitle(w, r)
if err != nil {
return
}
body := r.FormValue("body")
p := &Page{Title: title, Body: []byte(body)}
err = p.save()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/view/"+title, http.StatusFound)
}


func main() {
http.HandleFunc("/view/", viewhandle)
http.HandleFunc("/edit/", edithandle)
http.HandleFunc("/save/", savehandle)
http.ListenAndServe(":1000", nil)
}


---------------标准web代码框架
package main


import (
"flag"
"html/template"
"io/ioutil"
"log"
"net"
"net/http"
"regexp"
)


var (
addr = flag.Bool("addr", false, "find open address and print to final-port.txt")
)


type Page struct {
Title string
Body  []byte
}


func (p *Page) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}


func loadPage(title string) (*Page, error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Page{Title: title, Body: body}, nil
}


func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := loadPage(title)
if err != nil {
http.Redirect(w, r, "/edit/"+title, http.StatusFound)
return
}
renderTemplate(w, "view", p)
}


func editHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}


func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
body := r.FormValue("body")
p := &Page{Title: title, Body: []byte(body)}
err := p.save()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/view/"+title, http.StatusFound)
}


var templates = template.Must(template.ParseFiles("edit.html", "view.html"))


func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
err := templates.ExecuteTemplate(w, tmpl+".html", p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}


var validPath = regexp.MustCompile("^/(edit|save|view)/([a-zA-Z0-9]+)$")


func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
m := validPath.FindStringSubmatch(r.URL.Path)
if m == nil {
http.NotFound(w, r)
return
}
fn(w, r, m[2])
}
}


func main() {
flag.Parse()
http.HandleFunc("/view/", makeHandler(viewHandler))
http.HandleFunc("/edit/", makeHandler(editHandler))
http.HandleFunc("/save/", makeHandler(saveHandler))


if *addr {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
log.Fatal(err)
}
err = ioutil.WriteFile("final-port.txt", []byte(l.Addr().String()), 0644)
if err != nil {
log.Fatal(err)
}
s := &http.Server{}
s.Serve(l)
return
}


http.ListenAndServe(":8080", nil)
}






--------------------------------------------------input/output
type Reader interface{
Read(p []byte)(n int,err error)
}
type Write interfaace{
Write(p []byte)(n int,err error)
}
//将len(p)个字节读取到p中,返回读取的字节数,0<=n<=len(p)
//所有实现了Read方法的类型都实现了io.Reader接口




--------------------------------------------------读写文件
type Page struct {
Title string
Body  []byte
}


func (p *Page) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}


func loadPage(title string) (*Page, error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Page{Title: title, Body: body}, nil
}


func main() {
p1 := &Page{Title: "Testpage", Body: []byte("This is a simple Page.")}
p1.save()
p2, _ := loadPage("Testpage")
fmt.Println(string(p2.Body))
}


----------------
func main() {
buf := make([]byte, 1024)
f, _ := os.Open("E:\\ljl_doc\\study\\victory.txt")// linux上是/etc/passwd
n, _ := f.Read(buf)
os.Stdout.Write(buf[:n])
}


版权声明:本文为博主原创文章,未经博主允许不得转载。


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:ice_201507

查看原文:golang基础学习

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

4869 次点击  
加入收藏 微博
上一篇:golang的跨域
1 回复  |  直到 2000-01-01 00:00:00
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传