关于Go的知识:
默认情况下都会使用静态链接,编译完的go程序都是二进制文件,有非常好的便捷性,可以拷贝到不同的机器上运行
并且go与其他主流语言相比,它的关键字比较少只有25个关键字,c有37个,c++的84个
与其他语言的差异:
Go中main函数不支持任何返回值
通过os.Exit来返回状态
func main() { //func main () int{
fmt.Println("hello word")
os.Exit(-1) //return 1 -1 状态值
}
main函数不支持传入参数
func main (arg []string)
在程序中直接通过os.Args获取命令行参数
if len(os.Args)>1{
fmt.Println("Hello word",os.Args[1])
}
os.Exit(-1)
1.变量定义
var a int = 1 // var a,b,c =1,"2",4
编译器可以自动识别类型
a,b := 1,2(只能初次定义使用,包外部不可用)
var (
a = '1'
)
与其他主要编程语言的差异:
赋值可以进行自动类型推断
在一个赋值语句可以对多个变量进行同时赋值
a,b=b,a //可以这样使用交换值
2.内建变量类型
bool、string
无符号:(u)int,(u)int8,(u)int16,(u)int32,(u)int64,uintptr 指针
byte(8位),rune (字符型)32位
float32,float64,complex64(复数,实部和虚部分别是32位)、complex128(复数,实部和虚部64位)
复数:
欧拉公式:
与其他主要编程语言的差异:
1、GO语言不允许隐士类型转换(主流语言一般潜在的规则就是小的类型可以往大的类型进行转换)
2、别名和原有类型也不能进行隐士类型转换
type MyInt int64
func Test(t *testing.T){
var a int 32=1
var b int64
b=int64(a)
var c MyInt
c= b//错误,不支持别名的因式转换
}
类型的预定义值:
math.MaxInt64
math.MaxFloat64
math.MaxUint32
指针类型:
go语言和java一样支持了垃圾回收的机制,但是作为非常高效的语言,也支持使用指针访问内存空间,但是是有一些限制的
与其他编程语言的差异:
1、不支持指针运算
a:=1
aptr:=&a
aptr=aptr+1 //因为平常可以使用指针的自增来访问连续的存储空间例如数组
t.Log(a,aptr)
t.Logf("%T %T",a,aptr)
编译结果:
2、string是值类型,其默认的初始化为空字符串,而不是nil
if s==" "{ //不能判断 s== nil
}
3、常量
const a,b = 3,5
var c = int
fmt.Print(int(math.Sqrt(a*b+c+d)))
4、枚举类型:
go语言没有特殊的枚举关键字,所以用一组const来定义表示
普通枚举:
const(
cpp =0
java = 1
py = 2
)
自增枚举:
const (
Modoy=iota +1
Tuesday
Wednesday
)
5、复杂运算:
go语言没有前置的++,--
用==比较数组:在主流的语言中数组是个引用类型不是值类型,用等号比较其实是比较两个数组的引用,不是比较值是否相同,这点在go中完全相反的,如果维数相等且含有相同个数元素的数组是可以比较的,每个元素都相同的才相等
//b,kb,mb,gb,tb,pb
const(
b = 1<<(10*iota)
kb
mb
gb
)
可以作为一个自增值的种子
6、位运算符:
与其他主要编程语言的差异
7、判断语句 if
if content,err := ioutil.ReadFile(filename);err=null{
fmt.PrintIn(string(content))
}else{
fmt=PrintIn("content is err",err)
}
与其他语言的差异:
1、condition表达式结构必须为布尔值
2、支持变量赋值
if var declartion; condition{
// if a:=1==1; a{
}
//平常用到: 因为go语言函数支持多返回值,所以这样写
if v,err:=someFun(); err==nill{
t.Log()
}else{
t.Log()
}
8、switch条件
func grade(score int)string{
g := ""
switch{
case score<0||score>100:
panic(fmt.Sprint("Wrong scroe : %d", score))
case score < 60:
g = "F"
case score< 80:
g ="C"
}
return g
}
go语言的switch自带berak
与其他语言的差异:
实例:
func TestSwitch(t *testing.T){
for i:=0;i<5;i++{
switch i {
case 0,2:
t.Log("Even")
case 1,3:
t.Log("Odd")
default:
t.Log("it is not 0-3")
}
}
}
9、循环:for
for ; ; {}
func printFile(){
filname := "123.txt"
file,err := os.Open(filename)
if err!=nill{
panic(err)
}
scanner=bufio.NewScanner(file)
for scanner.Scan(){
fmt.PrintIn(scanner.Text())
}
}
//条件循环
n:=0
for n<5{
}
//无限循环
//while(true)
n:=0
for{}
10、指针
var a int =2
var pa *int =&a
*pa =3
指针不能运算
Go语言只有值传递一种方式
当 func f(pa *int)的时候,相当于把地址进行传递
当 func f(cache Cache)的时候,相当于直接把cache里面pDate指针拷贝过去,都是指向data
func swap(a,b *int){
*b,*a=*a,*b
}
swap(&a,&b)
11、数组:
var arr1 [3][4]int
arr2 :=[4]int {3,4,5,6}
arr3 :=[...]int{123,23,4,5}
var arrs [3][4] int
数组遍历:
for i:=0;i<len(arr3);i++{
t.Log(arr3[i])
}
for idx,e:=range arr3{ //idx 索引值 或者 _,e 或者 e
t.Log(idx,e)
}
i是数组下标,v为值 类似于foreach
数组是值类型
[10]int 和[20]int 是不同类型
调用func f(arr [10]int)会拷贝数组
在go语言不直接使用数组,而使用切片
数组截取:也就是切片
12、切片:
slice本身是没有数据的,是对底层array的一个视图,相对容易造成大量GC,是一个连续数据存储结构,表明上是一个可变长的数组,其实内部是一个结构体
其中ptr指针指向一个连续的存储空间
这个时候报错,其中len代表可以访问的元素,cap内部容量,其中len个元素被初始化为默认零值,未初始化元素不可以访问
实例一:切片如何实现可变长
cpa呈2倍增长,在append时候为什么要赋值,是因为这里的连续存储空间的地址发生了变化,并不是总是在原有的内存空间上添加这个值,当初存储空间需要扩展的时候,会创建一个新的存储空间并且把原有的数组拷贝过来
实例二:
arr := [...]int{0,2,3,4,5,6}
fmt.Println(arr[2:6],arr[:6],arr[2:],arr[:])
还可以resilic
s:=arr[2:6]
s = s[:3]
s=s[1:]
s=arr[:] //和指针相同
slice的扩展:
问题一:
arr:=[]int{0,1,2,3,4,5,6}
s1:=arr[2,6]
s2:=s1[3:5]
s1=[2,3,4,5] s2=[5,6]
其中len会改变,但是cap从截取的位置开始到末尾,所以s1指向后端连续存储空间,以下实例验证
当我们修改summer的时候,Q2也发生改变
cap=cap(s1)
添加元素如果超过cap,系统会重新分配更大的底层数组
由于值传递的关系,必须接受append的返回值
s=append(s,val)
var s []int //zero value for slice is nil
s = append(s,2*i+1)//当cap每次装不下的时候会*2
还可以这样创建:
s2 :=make([]int,16) //len=6
s3 :=make([]int,10,32) //len=10,cap=32
copy: copy (s2,s1) //把s1拷贝到s2
del:中间的下标3的元素: s2=append(s2[:3],s2[4:]...)
从头和从尾去掉:
front :=s2[0]
s2=s2[1:]
tail := s2[len(s2)-1]
s2=s2[:len(s2)-1]
如果创建一个长度为16的slice:
s2 := make([]int,16)
创建一个有10个元素,但是数组有32个长度
s3 :=make([]int,10,32)
删除下标为3的元素
s2=append(s2[:3],s2[4:]...)
len会改变,cap不会改变
删除首尾:
s2=s2[1:]
s2=sw[:len(s2)-1 ]
数组和切片:
1、容量可以伸缩
2、是否可以比较,数组只要维数相同,长度相同可以进行比较,只要里面元素都相同,那么数组相同,切片不可以比较
13、Map:
map[K]V,map[K1]map[K2]V
声明实例:
创建:
map := map[string]string{
"name":"123",
}
var m2 map[string] int //m2=nil
m3 :=make(map[string]int) //m3=empty map
遍历:k在map中是无序的,是hash map,需要给key进行排序,把key取出来放到slice中,排序完再去遍历map
for k,v/_,v/k :=range m{
}
func TestTMap(t *testing.T) {
m1:=map[int]int{1:1,2:4,3:9}
for k,v :=range m1{
t.Log(k,v)
}
}
取值:
if name,ok:=m["name"];ok{
fmt.Println(name,ok)
}else{
fmt.Println(name,ok)
}
删除:
delete(m,"name")
map的key:
1>map使用哈希表,必须可以比较相等
2>除了slice,map,function的内建类型都可以作为key
3>Struct类型不包括上述字段也可以作为key
与其他编程语言的差异:
在访问的key不存在的时候,仍然返回零值,不能通过返回nill来判断元素是否存在
其中主要的判断方式:
面向对象
go语言仅支持封装,不支持继承和多态,没有class只有struct
创建结构体:
type treeNode struct{
value int
left,right *treeNode
}
func (node treeNode) print(){ //treeNode结构体内的方法,go语言都是传值的
fmt.Println(node.value)
}
实例化:
root :=treeNode{}
root.left=&treeNode{}
root.right.left=new(treeNode)
nodes:=[]treeNode{
{value:2}
{}
{1,nil,nil},
}
工厂函数:
funct createNode(value int )*treeNode{
return &treeNode{value:value} //返回局部变量的地址
}
接口:
go语言是面向接口的编程语言,没有继承和多态,通过接口完成
duck typing:描述事务的外部行为而非内部结构
严格说go属于结构化类型系统,类似duck typing
go语言的接口是由使用者定义
接口类:
type Retriever interface {
Get(url string) string //在interface中不用加func
}
func download(r Retriever) string{ //使用者
return r.Get("http://www.imooc.com")
}
func main() {
var r Retriever
r=mock.Retrievers{"this is a fake imooc.com"}
//r=mock.Retrievers{"this is a fake imooc.com"}
r=real.Retriever{}
fmt.Println(download(r))
//fmt.Println(download(r))
}
实现类:
type Retriever struct {
UserAgent string
TimeOut time.Duration
}
func(r Retriever) Get(url string) string{
resp,err:=http.Get(url)
if err!=nil{
panic(err)
}
result,err:=httputil.DumpResponse(resp,true)
resp.Body.Close()
if err!=nil{
panic(err)
}
return string(result)
}
在r的里面有两个内容,一个类型,一个值
func inspect(r Retriever){
fmt.Printf("%T %v\n",r,r)
//第二种打印方式
switch v:=r.(type) {
case mock.Retrievers:
fmt.Println("content:",v.Contentst)
case *real.Retriever:
fmt.Println("UserAgent:",v.UserAgent)
}
}
func main() {
var r Retriever
inspect(r)
r=mock.Retrievers{"this is a fake imooc.com"}
//r=mock.Retrievers{"this is a fake imooc.com"}
inspect(r)
r=&real.Retriever{UserAgent:"Mozilla/5.0",TimeOut:time.Minute}
//fmt.Printf("%T %v\n",r,r) 第一种打印方式
inspect(r)
//fmt.Println(download(r))
//fmt.Println(download(r))
//第三种打印方式
realRetriever := r.(*real.Retriever)
fmt.Println(realRetriever.TimeOut)
if mockRetriever,ok:=r.(mock.Retrievers);ok{
fmt.Println(mockRetriever.Contentst)
}else{
fmt.Println("not a mock retriever")
}
}
接口变量里面有:实现者的类型、实现者的值(或者是实现者的指针指向实现者)
接口变量自带指针,接口变量统一采用值传递,几乎不需要使用接口的指针
指针接受者实现只能以指针方式使用,值接收者都可以
func的返回类型如果是interface的话不限制返回类型,但是可以在返回值时添加类型限制,比如 return head.(int )
常用的接口:
string:
func (r *Retrievers)String()string{
return fmt.Sprintf(
"Retriever:{Contents=%s}",r.Contentst)
}
Read:实现的人读取文件给你的[]byte里面
Write:写入到文件里面
函数式编程:
函数是一等公民:参数,变量,返回值都可以是函数
高阶函数:函数的参数也可以是函数
func adder() func(int)int{
sum:=0
fmt.Println(sum)
return func(v int) int {
sum+=v
return sum
}
}
func main() {
a:=adder()
fmt.Println("............")
for i:=0;i<10;i++{
fmt.Println(a(i))
}
}