整数与浮点数

  1. Go的数据类型分四大类:基础类型、聚合类型、引用类型和接口类型。基础类型包括数字、字符串和布尔型
  2. rune类型是int32类型的同义词,常用于指明一个值是Unicode码点,这两个名称可互换使用。byte类型同样是int8类型的同义词,强调一个值是原始数据而非量值
  3. 有符号整数以补码表示,保留最高位为符号位,n位数字的取值范围为-2n-1~2n-1-1,无符号整数由全部位构成其非负值,范围是0~2n-1
  4. Go中取模整数运算符%仅能用于整数,取模余数的正负号总是与被除数一致,除法运算的行为取决于操作数是否都为整型
  5. 运算符^作为二元运算符为异或,作为一元运算符则为按位取反,运算符&^是按位清除:表达式z=x&^y中,若y的某位为1,则z的对应位为0,否则等于x的相应位
  6. 左移以0填补右边空位,无符号整数右移同样以0填补左边空位,有符号整数右移是按符号位的值填补空位
  7. 如果使用fmt包输出数字,我们可以使用谓词%d、%o和%x指定进位制基数和输出格式。通常Printf的格式化字符串含有多个%谓词,这要求提供相同数量的操作数,而%后的谓词[1]告知Printf重复使用第一个操作数,而%o、%x或%x之前的副词#告知Printf输出相应的前缀0、0x或0X
  8. 用%c输出文字符号,如果希望输出带有单引号则用%q
  9. 浮点数能方便地通过谓词%g输出,该谓词会自动保持足够的精度,并选择最合适的表示方式,但是对于数据表。%e(有指数)和%f(无指数)的形式可能更合适,这三个谓词都能掌控输出宽度和数值精度
  10. 所有数字包括NaN自身与NaN的比较总不成立(除了!=,它总是与==相反)
  11. Go具备两种大小的复数complex64与complex128,二者分别由float32与float64构成,内置的complex函数根据给定的实部和虚部创建复数,而内置的real函数和imag函数则分别提取复数的实部或虚部。在浮点数或十进制整数后面紧接着写字母i,它就变成了一个虚数
  12. bool型的值或布尔值(boolean)只有两种可能:true和false,运算符&&和||都会引起短路行为,而且&&的优先级比   更高

字符串

  1. 文本字符串被解读为按UTF-8编码的Unicode码点(文字符号)序列,内置的len函数返回字符串的字节数(并非文字符号的数目)。字符串的第i个字节不一定就是第i个字符,因为非ascii字符的UTF-8码点需要两个字节或多个字节
  2. 字符串比较运算按字节进行,结果服从本身的字典排序。尽管可以将新值赋予字符串变量,但是字符串值无法改变:字符串值本身所包含的字节序列永不可变,因此无法通过下标的方式给字符串的某个字节赋值
  3. 原生的字符串字面量的书写形式是`…`,使用反括号而不是双引号,其中转义序列不起作用:实质内容与字面写法严格一致,包括反斜杠和换行符
  4. Go语言中,字符串字面量的转义让我们得以用码点的值来指明Unicode字符,有两种形式:\uhhhh表明16位码点值,\Uhhhhhhhh表示32位码点值
  5. 由于UTF-8的优良特性,许多字符串操作都无须解码,当[]rune转换作用于UTF-8编码的字符串时,返回该字符串的Unicode码点序列。如果把文字符号类型的slice转换成一个字符串,它会输出各个文字符号的UTF-8编码拼接结果,若将一个整数值转换成字符串,其值按文字符号类型解读,并且产生该文字符号的UTF-8码
  6. 字符串和字节slice可以相互转换。为了避免转换和不必要的内存分配,bytes包和strings包都预备了许多对应的应用函数,它们两两相对应
  7. 要将整数转换成字符串,一种选择是使用fmt.Sprintf,另一种做法是用函数strconv.Itoa,而strconv中的FormatInt和FormatUInt可以按不同的进位制格式化数字,而当要包含除数字以外的额外信息时Sprintf中谓词%b、%d、%x往往比Format函数更为方便
  8. strconv包内的Atoi函数或ParseInt函数用于解释表示整数的字符串,ParseUInt用于无符号整数

常量

  1. 常量是一种表达式,其可以保证在编译阶段就计算出表达式的值,并不需要等到运行时,所有变量本质上都属于基本类型
  2. 对于常量操作数,所有数学运算、逻辑运算和比较运算的结果依然是常量,常量的类型转换结果和某些内置函数的返回值,比如len、cap、real等,同样是常量
  3. 常量声明可以同时指定类型和值,如果没有显式指定类型,则类型根据右边的表达式推断。若同时声明一组常量,除了第一项外,其他项在等号右侧的表达式可以省略,这意味着会复用前面一项的表达式及其类型
  4. 常量的声明可以使用常量生成器iota,它创建一系列相关值,而不是逐个值显式写出,常量声明中,iota从0开始取值,逐项加1
  5. 许多常量并不从属某一具体类型,编译器将这些从属类型待定的常量表示成某些值,这些值比基本类型的数字精度更高,且算术精度高于原生的机器精度
  6. 从属类型待定的常量共有6种,分别为无类型布尔、无类型整数、无类型文字符号、无类型浮点数、无类型复数、无类型字符串。
  7. 借助推迟从属类型,无类型常量不仅能暂时维持更高的精度,与类型已确定的常量相比,它们还能写进更多表达式而无需转换类型
  8. 只有常量才可以是无类型的,若将无类型常量声明为变量或在类型明确的变量赋值的右方出现无类型常量,则常量会被隐式转换为该变量的类型
  9. 无论隐式显式,常量从一种类型转换成另一种,都要求目标类型能够表示原值,实数和复数允许舍入取值
  10. 变量声明(包括短变量声明)中,假如没有显式指定类型,无类型常量会隐式转换成该变量的默认类型。要将变量转换成不同的类型,我们必须将无类型常量显式转换为期望的类型,或在声明变量时指明想要的类型
  11. Go语言中,只有大小不明确的int类型,不存在大小不确定的float类型和complex类型,因为如果浮点数的大小不明,就很难写出正确的数值算法

数组和slice

  1. 数组是拥有固定长度且拥有零个或多个相同数据类型元素的序列。默认情况下一个新数组中的元素初始值为元素类型的零值,也可使用数组字面量根据一组值来初始化一个数组
  2. 在数组字面量中,如果省略号“…”出现在数组长度的位置,那么数组的长度由初始化数组的元素个数决定
  3. 数组的长度是数组类型的一部分,因此元素类型相同但是大小不同的数组不是同一类型,数组的长度必须是常量表达式
  4. 数组初始化中没有指定值的索引位置的元素默认被赋予数组元素类型的零值。如果数组元素类型是可比较的,数组也是可比较的,比较结果是两边元素的值是否完全相同,大小不同的数组不能进行比较
  5. Go把数组和其他类型都看成值传递,如需进行修改则需要显式传递数组和其他类型的指针
  6. slice表示一个拥有相同类型元素的可变长度的序列,通常写成[]T,元素类型是T,看上去像没有长度的数组类型
  7. slice用来访问数组的部分或全部元素,这个数组称为slice的底层数组
  8. slice有三个属性:指针、长度和容量,指针指向数组中第一个可以从slice中访问的元素,不一定是数组的第一个元素,长度是指slice中元素的数量,不能超过容量,容量的大小通常是从slice的起始元素到底层数组的最后一个元素间元素的个数,内置的len和cap函数可以返回slice的长度和容量
  9. 如果slice的引用超过了被引用对象的容量,即cap(s),那么会程序报错,如果slice的长度超出了被引用对象slice的长度len(s),那么最终slice会比原slice长
  10. slice包含了指向数组元素的指针,所以将一个slice传递给函数的时候,可以在函数内部修改底层数组的元素
  11. 和数组不同,slice无法做比较,检查一个slice是否为空,可以通过len(s)来检查,因为slice!=nil的情况下也有可能为空
  12. 可以使用make函数创建一个具有指定元素类型、长度和容量的slice,容量参数省略的情况下长度和容量相等
    make([]T,len)
    make([]T,len,cap)//和make([]T,cap)[:len]功能相同
    
  13. 内置函数append用来将元素追加到slice的后面
  14. 对于任何函数,只要有可能改变slice的长度或容量,抑或是使得slice指向不同的底层数组,都需要更新slice变量

map

  1. map散列表是一个拥有键值对的无序集合,在这个集合中,键的值是唯一的。键的类型一定是可以通过操作符==来进行比较的数据类型,所以map可以检测一个键是否已经存在
  2. 可以使用内置函数make来创建一个map,从字典中根据键移除一个元素可以使用内置函数delete
  3. map使用给定的键来查找元素,对应的元素不存在就返回值类型的零值
  4. map元素不是一个变量,无法获取它的地址,一个原因是map的增长可能会导致已有的元素被重新散列到新的存储位置,这样就使得原来获取的地址无效
  5. 可以使用for循环结合range关键字来遍历map中所有键和值,map是无序的,如果需要按照某种顺序来遍历map中的元素,我们必须显式地给键排序
  6. 设置元素之前必须初始化map,想零值map中设置元素会导致错误
  7. 通过下标方式访问map中的元素输出两个值,第二个值是一个布尔值,用来报告该元素是否存在
  8. 和slice一样,map不可比较,唯一合法的比较就是和nil比较

结构体

  1. 结构体是将零值或者多个任意类型的命名变量组合在一起的聚合数据类型,每个变量都叫做结构体的成员
  2. 结构体变量和结构体指针都可以通过.号来访问结构体成员
  3. 结构体的成员变量通常一行写一个,变量的名称在类型的前面,但是相同类型的连续成员变量可以写在一行上
  4. 成员变量的顺序对于结构体同一性很重要,成员变量类型相同只是顺序不同的两个结构体也认为是不同的类型
  5. 如果一个结构体的成员变量名称是首字母大写的,那么这个变量是可导出的,一个结构体可以同时包含可导出和不可导出的成员变量
  6. 命名结构体类型不能定义一个拥有相同结构体类型S的成员变量,但是可以定义一个S的指针类型*S
  7. 结构体初始化时,如果指定了成员变量的名称,顺序是无所谓的,如果在这种初始化方式中某个成员变量的没有指定,那么它的值就是该成员变量类型的零值。指定成员变量名称和不指定按照顺序初始化这两种方式不可以混用
  8. 可以通过一种简单的方式来创建、初始化一个struct类型的变量并获取它的地址:pp:=&Point{1,2}等价于:pp:=new(Point) *pp=Point{1,2}
  9. 如果结构体的所有成员变量都可比较,那么这个结构体就是可以比较的
  10. Go中的结构体嵌套机制可以让我们将一个命名结构体当作另一个结构体类型的匿名成员使用,可以使用简单的表达式就可以代表连续成员
  11. Go允许我们定义不带名称的结构体成员,只需要指定类型即可;这种结构体成员称作匿名成员,这个结构体成员的类型必须是一个命名类型或者指向命名类型的指针
  12. 因为匿名成员拥有隐式的名字,所以不能在一个结构体里定义两个相同类型的结构体成员,否则会引起冲突
  13. 匿名成员中的导出性由它们自身类型所决定,即使匿名成员结构体不可导出,只要本身可导出那么也就是可导出的。但是显式指定中间匿名成员的方式在声明circle和point的包之外是不允许的,因为中间匿名成员是不可导出的

JSON

  1. JSON是JavaScript值的Unicode编码,这些值包括字符串、数字、布尔值、数组和对象
  2. JSON的对象用来编码Go里面的map(键为字符串类型)和结构体,把Go的数据结构转换为JSON称为marshal,通过json.Marshall来实现,Marshall生成一个字节slice,包含一个不带有任何多余空白字符的很长的字符串,为了方便阅读可以使用json.MarshalIndent输出整齐格式化过的结果
  3. Marshal使用结构体成员名称以及成员标签值作为JSON对象里面字段的名称,只有可导出的成员可以转换为JSON字段
  4. 将JSON字符串解码为Go数据结构,这个过程称为unmarshal,由json.unmarshal实现
  5. 通过合理地定义Go的数据结构,我们可以选择将哪部分JSON数据解码到结构体对象中,哪些数据可以丢弃
  6. 即使对应的JSON字段的名称不是首字母大写,结构体的成员名称也必须首字母大写,因为在unmarshal阶段,JSON字段的名称关联到Go结构体成员的名称是忽略大小写的
  7. text/template包和html/template包提供了一种机制,可以将程序变量的值代入到文本或者HTML模板中,将格式和代码彻底分离
  8. 模板是一个字符串或者文件,它包含一个或者多个两边用双大括号包围的单元——``,这称为操作
  9. 在操作中,符号|会将前一个操作的结果当作下一个操作的输入,类似shell的管道
  10. html/template包和text/template包使用一样的API和表达式语句,并且额外地对在HTML、JavaScript、CSS和URL中的字符串进行自动转义,这些功能可以避免生成的HTML引发长久以来都会有的安全问题,比如注入攻击
  11. 我们通过使用命名的字符串类型template.HTML类型而不是字符串类型避免模板自动转义受信任的HTML数据,同样的命名类型适用于受信任的JavaScript、CSS和URL