go语言学习笔记(4)程序实体

283 阅读8分钟
1.声明变量的两种方式

第一种方式通过var 来声明变量:
例子:var name string 或者 var name = "nshu" 后面这种方式是利用了Go语言自身的类型推断。
第二种方式通过短变量声明方法:
例子 name :=“nshu”这种方式只能在函数体内部使用

2.go语言类型推断带来的好处

Go语言类型推断可以明显提升程序的灵活性,使得代码重构变得更加容易,因为当你修改函数时,改变了函数的返回值类型,但main函数里面的赋值是通过语言类型推断来实现的,所以不需要修改main函数,正因如此代码的维护也变得更加建议,同时因为go语言是静态类型语言,它的类型推断是在编译期实现的,所以不像它动态语言是通过牺牲程序的可维护性和运行效率实现的。

3.代码块

在Go语言中代码块一般就是一个由花括号括起来的区域。Go语言本身以及我们编写的代码共同形成了一个大代码块,也叫全局代码块。只要是公开的全局变量,就可以被所有代码使用,相对于小一点的代码块就是代码包,再小一点就是一个源码文
件,最后就是函数,if,swich,select之类的。

4.变量重声明

由于变量的类型在其初始化时就已经确定了,所以对它再次声明赋予的类型必须与其原本的类型相同,否则会产生编译错误。
变量的重声明只能发生在同一个代码块中。如果是与外层代码块中的变量重名,在本代码块中会发生变量的覆盖,但在外层代码块中,外层变量的值并不会改变。
变量的重声明只有在使用短变量声明时才会发生,否则也无法通过编译。例如
不能同时出现 var name = "nshu" var name = "nshu"
但是可以出现var name = "nshu" name := "nshu"

5.怎样判断一个变量的类型

通过类型断言表达式能够判断一个变量的类型,例如:value,ok:=interface{}(container).([]string)
这个类型断言表达式包括了把container变量的值转换为空接口值的inertface{}(container) 以及一个用于判断前者的类型是否为切片类型[]string的.(string),这个表达式的结果可以被赋值给两个变量,在这里由ok和value代表。变量ok时候bool类型的,他将代表类型的判断结果,如果为true被判断的值会转化为[]string类型,并赋值给变量value,否则value的值会被赋予nil。
注意在赋值的时候记得带上ok不然当判断为否的时候程序会panic。
类型断言表达式的语法形式是x.(T),前面之所以要做类型转换,是因为x只能是接口类型的,在Go语言中interface{}是空接口,任何类型都是它的实现类型。

6.如果一个变量与其外层变量重名会出现什么问题

go语言变量查找过程:首先代码引用变量的时候总会最先查找当前代码块中的变量,其次如果当前代码块没有声明以此为名的变量,那么程序会沿着代码块的嵌套关系,从当前代码块开始向外层一层层查找,直到找到那个变量,如果找不到编译器就会报错。
注意点,当前源码文件中导入了其他代码包,那么引用其中的程序实体时是需要以限定符为前缀的。因为不加限定符的话程序是不会去引入的代码包中查找的。除了(import . XXX)的形式。
采用import . xxx如文章所说,基本上就会认为引入的代码包的代码,如同在本包中一样,那作用域其实是同一个,自然不允许重复声明。

7.不同代码块中的重名变量与变量重声明中的变量区别
变量重声明中的变量一定是在某一个代码块内的,而不同代码块中的重名变量是指多个代码块之间的由相同的标识符代表的变量。
变量重声明是指对一个变量的多次声明,而不同代码块中的重名变量,它们指的是多个变量。
不论对变量重声明多少次它们的类型必须始终与第一次声明时的类型一致。而可重名变量不需要遵循此条规定
不同代码块中的重名变量,在不同代码块之间存在屏蔽关系,而变量重声明就不会出现。

6.类型转换需要注意的地方

第一,对于整数类型的值、整数常量质检的类型转换,原则上只要源值在目标类型的可表示范围内就是合法的。

例子,之所以uint(255)能把无类型的常量255转换为uint8类型的值,是因为255在[0,255]的范围内
但是当源数类型的可表示范围较大,而目标类型的可表示范围较小时,要十分注意。
例子:
package main

import (
"fmt"
)

func main() {
var a int16 = -255
b := int8(a)
fmt.Println(b)
}
输出结果为 1
解析:
Go语言以及计算机中都是以补码的形式存储的。这主要是为了简化计算机对整数的运算过程。
原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值。
反码的表示方法是:正数的反码是其本身;负数的反码是在其原码的基础上, 符号位不变,其余各个位取反。
补码的表示方法是:正数的补码就是其本身;负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1。 (即在反码的基础上+1)
在这里int16类型的值255的补码是1111111100000001,如果我们把改值转换为int8类型的值,那么Go语言会把在较高位置八位二进制数直接截掉,从而得到00000001。又因为它的符号位为0,说明它是个正整数,由于正整数的补码就等于其源码,所以b最后的值为1。
当整数值类型的有效范围由宽变窄时,只需要在补码的形式下截掉一定数量的高位二进制数即可,当把浮点数转换为整数类型时,前者的小数部分会被全部截掉。

第二,虽然直接把一个整数值类型转换为一个string类型的值是可行的,但是被转换的整数值应该可以代表一个有效的Unicode代码点,否者转换的结果将会是�。�的Unicode代码点是U+FFFD。它是Unicode标准中定义的Replacement Character专门用于替换那些未知的、不被认可的以及无法展示的字符。
第三,string类型与各种类型之间的互转。一个值在从string类型向[]byte类型转换时,代表着以UTF-8编码的字符串会被拆分成零散独立的字节。除了与ASCII编码兼容的那部分字符集,以UTF-8编码的某个单一字节是无法代表一个字符的。
例如:
string([]byte{'\xe4', '\xbd', '\xa0', '\xe5', '\xa5', '\xbd'}) // 你好
UTF-8 编码的三个字节\xe4、\xbd和\xa0合在一起才能代表字符'你',而\xe5、\xa5和\xbd合在一起才能代表字符'好'。
其次,一个值在从string类型向[]rune类型转换时代表着字符串会被拆分成一个个 Unicode 字符。
string([]rune{'\u4F60', '\u597D'}) // 你好


7.类型别名,潜在类型

我们可以用关键值type声明自定义的各种类型。这些类型必须在Go语言基本类型和高级类型的范围之内。我们可以像下面这样声明它:
type Mystring = string
这条语句代表着,MyString是string类型的别名类型。意思是这两种类型区别只在名字上,它们是完全相同的。Go语言内建的基本类型中就存在两个别名类型。byte是uint8的别名类型,而rune是int32的别名类型。但是如果这样声明:
type Mystring2 string
Mystring2 和string就是两个不同的类型了。这里的Mystring2是一个新的类型,不同于其他任何类型,这种方式叫做类型的再定义,在这里string可以被称为Mystring2的潜在类型。潜在类型的含义是某个类型在本质上是哪个类型或者哪个类型的集合。潜在类型相同的不同类型值之间是可以互相转换的,因此Mystring2类型与String类型是可以互相转换的。但是[]Mystring2与[]string之前是不可以互相转换的,因为它们的潜在类型分别是Mystring2 string。
注意点潜在类型相同的两个类型之间不能进行比较,赋值。