golang类型系统笔记

一、常量:
  const Pi float64 = 3.14159265358979323846
  const zero = 0.0 // 无类型浮点常量
  const (
   size int64 = 1024
   eof = -1 // 无类型整型常量
  )
  const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重赋值
  const a, b, c = 3, 4, "foo"
 
二、Go语言预定义了这些常量:【true】、【false】和【iota】。
iota比较特殊,可以被认为是一个可被编译器修改的常量,在每一个const关键字出现时被重置为0,然后在下一个const出现之前,每出现一次iota,其所代表的数字会自动增1。
从以下的例子可以基本理解iota的用法:
  const ( // iota被重设为0
    c0 = iota // c0 == 0
    c1 = iota // c1 == 1
    c2 = iota // c2 == 2
  )
 
三、枚举:
const (
 Sunday = iota
 Monday
 Tuesday
 Wednesday
 Thursday
 Friday
 Saturday
 numberOfDays // 这个常量没有导出, 因为他是小写开头, 仅包内可见
)

四、整型: 既然整型有平台相关性, 还是少用int uint好一些。

类 型 长度(字节) 值 范 围
int8 1 ?128 ~ 127
uint8(即byte) 1 0 ~ 255
int16 2 ?32 768 ~ 32 767
uint16 2 0 ~ 65 535
int32 4 ?2 147 483 648 ~ 2 147 483 647
uint32 4 0 ~ 4 294 967 295
int64 8 ?9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807
uint64 8 0 ~ 18 446 744 073 709 551 615
int 平台相关 平台相关
uint 平台相关 平台相关
uintptr 同指针 在32位平台下为4字节,64位平台下为8字节

五、浮点型:

Go语言定义了两个类型float32和float64,其中float32等价于C语言的float类型,float64等价于C语言的double类型。
在Go语言里,定义一个浮点数变量的代码如下:
 var fvalue1 float32
 fvalue1 = 12
 fvalue2 := 12.0 // 如果不加小数点,fvalue2会被推导为整型而不是浮点型
浮点数比较:
因为浮点数不是一种精确的表达方式,所以像整型那样直接用==来判断两个浮点数是否相等是不可行的,这可能会导致不稳定的结果。下面是一种推荐的替代方案:
import "math"
// p为用户自定义的比较精度,比如0.00001
func IsEqual(f1, f2, p float64) bool {
 return math.Fdim(f1, f2) < p
}

六、【字符串】

  声明: var str string
      str = “hello”
    或者: str := “hello”
 
  操作: ch := str[0] // 取字符串的第一个字符
 
  字符串的内容不能在初始化后被修改:   str[0] = 'X' // 编译错误
 
  x + y 字符串连接 "Hello" + "123" // 结果为Hello123
 len(s) 字符串长度 len("Hello") // 结果为5
 s[i] 取字符 "Hello" [1] // 结果为'e'
 
 更多的字符串操作,golang标准库strings包。
 
七、数组:
  声明:以下为一些常规的数组声明方法:
  [32]byte // 长度为32的数组,每个元素为一个字节
  [2*N] struct { x, y int32 } // 复杂类型数组
  [1000]*float64 // 指针数组
  [3][5]int // 二维数组
  [2][2][2]float64 // 等同于[2]([2]([2]float64)) 
 
  循环:
    for i := 0; i < len(array); i++ {
    fmt.Println("Element", i, "of array is", array[i])
   } 
    for i, v := range array {
    fmt.Println("Array element[", i, "]=", v)
   }
     
   【★ 值类型】所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作。如果将数组作为函数的参数类型,则在函数调用时该参数将发生数据复制。
需要特别注意的是,在Go语言中数组是一个值类型(value type)。因此,在函数体中无法修改传入的数组的内容,因为函数内操作的只是所
传入数组的一个副本。
     
     
八、数组切片(slice)

声明:
   基于数组创建一个数组切片                             var mySlice []int = myArray[:5]     
   基于myArray的所有元素创建数组切片:                  mySlice = myArray[:]
   创建一个初始元素个数为5的数组切片,元素初始值为0:   mySlice1 := make([]int, 5)
   创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间:mySlice2 := make([]int, 5, 10)
   直接创建并初始化包含5个元素的数组切片:              mySlice3 := []int{1, 2, 3, 4, 5}
  
   数组切片支持Go语言内置的cap()函数和len()函数,代码清单2-2简单示范了这两个内置函数的用法。
   可以看出,cap()函数返回的是数组切片分配的空间大小,而len()函数返回的是数组切片中当前所存储的元素个数。
  
添加元素:
 mySlice = append(mySlice, 1, 2, 3)
 
 mySlice2 := []int{8, 9, 10}
 // 给mySlice后面添加另一个数组切片
 mySlice = append(mySlice, mySlice2...) 。注意需要三个点...
 
 复制数组切片:slice1 := []int{1, 2, 3, 4, 5}
               slice2 := []int{5, 4, 3}
               copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中
               copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
              
  【引用传递】  数组切片是引用传递。
 
九、map
声明:var personDB map[string] PersonInfo
   personDB = make(map[string] PersonInfo)               
   
   // 往这个map里插入几条数据
  personDB["12345"] = PersonInfo{"12345", "Tom", "Room 203,..."}
  personDB["1"] = PersonInfo{"1", "Jack", "Room 101,..."}
  // 从这个map查找键为"1234"的信息
  person, ok := personDB["1234"]
  // ok是一个返回的bool型,返回true表示找到了对应的数据
  if ok {
    fmt.Println("Found person", person.Name, "with ID 1234.")
  } else {
   fmt.Println("Did not find person with ID 1234.")
  }
  
  创建了一个初始存储能力为100的map:  myMap = make(map[string] PersonInfo, 100)
  
  赋值 : myMap["1234"] = PersonInfo{"1", "Jack", "Room 101,..."}
  删除 : delete(myMap, "1234")
  查找 : value, ok := myMap["1234"]
      if ok { // 找到了
       // 处理找到的value
      }
      
    【引用传递】 map是引用传递。
   
【★★★ 引用语义】 golang中的引用语义类型有: 数组切片、map、channel、interface。
                    struct(类)对象往往通过new创建出来,这时会是一个引用传递,new出来的本身就是一个指针。
                    直接声明struct得到的变量传递时是按值传递。★★★★★
 
   
十、 结构体struct

定义新类型:      type Integer int : 定义个Integer 类型, 与int相同。
           type Header map[string][]string
          
结构体:           
 type Rect struct {
  x, y float64
  width, height float64
 }
 
 func (r *Rect) Area() float64 {
  return r.width * r.height
 }
 实例化对象:
 rect1 := new(Rect)
 rect2 := &Rect{}
 rect3 := &Rect{0, 0, 100, 200}
 rect4 := &Rect{width: 100, height: 200}
  也可以rect5 := Rect{}.
 
  加&符号和new的是指针对象,没有的则是值对象,这点和php、java不一致,在传递对象的时候要根据实际情况来决定是要传递指针还是值。
  现在看还是用引用声明方式好一点。
 
构造函数:   查看官方文档,golang并没有构造函数一说。如果一定要在初始化对象的时候进行一些工作的话,可以自行封装产生实例的方法。
   func NewPoem(param string, p ...interface{}) *Poem
   示例:
   func NewPoem(author string) (poem *Poem) {
     poem = &Poem{}
     poem.Author = author
     return
   }
  
   struct是组合。
  
   【TIPS】: 传统的继承,有两方面的作用: 一是复用,即子类复用父类的一部分方法,自己再添加一些特殊的。  二是表示,即父类提供
   一个通用的表示方式,不同的子类去实现,这种也很常见比如抽象类。
       那么第一种应该完全的切换到组合的思考方式, 而第二种则通过interface实现。 java已经没有了virtual关键字,引入了interface,go也保留了
   interface,解决了第二个问题。 第一个go直接通过组合实现。
  

十一、 接口 interface

 在Go语言出现之前,接口主要作为不同组件之间的契约存在。对契约的实现是强制的,你必须声明你的确实现了该接口。
 为了实现一个接口,你需要从该接口继承。
 
 在Go语言中,一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口。 就可以赋值给这个接口。
 
 【接口赋值】: var b LessAdder = &a ... (1)  var b LessAdder = a ... (2)   应该用1的写法, 2会有问题。
 
 【接口查询】:
 var file1 Writer = ...
  if file5, ok := file1.(two.IStream); ok {
  ...
 }
 这个if语句检查file1接口指向的对象实例是否实现了two.IStream接口,如果实现了,则执行特定的代码。
 
 在Go语言中,你可以询问接口它指向的对象是否是某个类型,比如:
  var file1 Writer = ...
  if file6, ok := file1.(*File); ok {
   ...
  }
 这个if语句判断file1接口指向的对象实例是否是*File类型,如果是则执行特定代码。
 
 【类型查询】:
 var v1 interface{} = ...
  switch v := v1.(type) {
   case int: // 现在v的类型是int
   case string: // 现在v的类型是string
   ...
  }

  接口可以给接口赋值。  接口是引用传递。
 
  -------------------------------------------
   var i itest                                                                            |
  sp := new(stest)                                                               |
  i = sp
  fmt.Println("point i", unsafe.Sizeof(i))                                       |
  fmt.Println("point s", unsafe.Sizeof(sp))                                      |
  i.do()                                                                         |
  ss := stest{}
  i = ss                                         |
  fmt.Println("struct i", unsafe.Sizeof(i))                                      |
  fmt.Println("struct s", unsafe.Sizeof(ss))                                     |
 输出:
   point i 8                |
   point s 4                |
   struct i 8
   struct s 40
  可见: 接口类型永远大小是8,不管赋值给他的是new出来的指针还是结构体,都是可以的。      |结构体的指针大小是4。 
    -------------------------------------------
十二、 channel :

声明:var chanName chan ElementType。 如var ch chan int 或者,我们声明一个map,元素是bool型的channel:var m map[string] chan bool 

定义:定义一个channel也很简单,直接使用内置的函数make()即可:   ch := make(chan int)

要创建一个带缓冲的channel,其实也非常容易:  c := make(chan int, 1024)
在调用make()时将缓冲区大小作为第二个参数传入即可,比如上面这个例子就创建了一个大小为1024的int类型channel,
即使没有读取方,写入方也可以一直往channel里写入,在缓冲区被填完之前都不会阻塞。

单向channel:
var ch1 chan int // ch1是一个正常的channel,不是单向的
var ch2 chan<- float64// ch2是单向channel,只用于写float64数据
var ch3 <-chan int // ch3是单向channel,只用于读取int数据
应该比较适合用于函数参数中。

关闭: close(ch)。
 x, ok := <-ch 这个用法与map中的按键获取value的过程比较类似,只需要看第二个bool返回值即可,如果返回值是false则表示ch已经被关闭。
 
【引用传递】 channel是引用传递。

 
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值