21天从Java转向Go之第三天——初出茅庐

名称

Go中25个关键字 只能在语法允许的地方使用,不能做为名称

  • break
  • default
  • func
  • interface
  • select
  • case
  • defer
  • go
  • map
  • struct
  • chan
  • else
  • goto
  • pacakage
  • switch
  • const
  • if
  • range
  • type
  • continue
  • for
  • import
  • return
  • var

内置的预声明的常量、类型和函数名称。这些名称不是预留的,可以对其重声明,但这会有冲突的风险。

常量

  • true false iota nil

类型

  • int int8 int16 int32 int64
  • uint uint8 uint16 uint32 uint64 uintptr
  • float32 float64 complex128 complex64
  • bool byte rune string error

函数

  • make len cap new append copy close delete
  • complex real imag
  • panic recover

命名及访问权限

  • 在函数体中的声明,只在函数局部有效。
  • 声明在函数外的实体,则对相同包里面的所有源文件可见。
  • 如果名称开头字母是大写,则是导出的。意味这它对包外是可见和可访问的,可以被自己包之外的其他程序所引用,像fmt包的Printf函数。包名总是小写。
  • 这一点简化了像Java的权限修饰符 private protected public。权限以包为单位,不是像Java一样,以类为基础单位。
  • go语言的命名本身没有长度限制。Go的编程风格倾向于短名称,特别是作用域较小的局部变量,更喜欢叫i,而不是theLoopIndex。
  • 风格上使用驼峰命名,作用域越大,名称命名相应更长更有意义。

声明

  • 有四个主要的声明,变量,常量,类型,函数
  • Go程序存储在一个或多个以.go为后缀的文件里。每个go文件以package声明开头,表明文件属于哪个包。
  • package声明后面是import声明,然后是--包级别--的常量,变量,类型,函数,不区分顺序。

代码说明

package main

//导入包的两种形式 单个导入 多个导入
//导入如果不使用,会报错,需要使用_空标识符匿名导入,会执行包内的init函数
//另外可指定别名,包名是从$GOPATH/src/后开始计算的,使用/进行路径分隔
import (
	"fmt"
	"os"
)
import (
	_ "bufio"
	_ "image"
	_ "io/ioutil" //从$GOPATH/src/开始
)

//常量的定义,单个定义 多个定义 如果多个,使用const()包起来
const (
	LARGE = 42
	MID   = 40
	SMALL = 38
)
const PI = 3.14

//变量的声明 var name type = expression
//类型和表达式可以省略其中一个,不能都省略
//如省略表达式,其初始值是对应类型的零值。
//如果是数字类型,零值是0。如果是string类型,零值是""。
//对于接口和引用类型(slice、map、函数、通道、指针)则是nil。
//如果是数组或结构体复合类型的,其零值是里面元素或者成员的零值。

//如省略类型,会根据表达式推断变量的类型
var name = "Tom"
var age int = 1
var height, weight float32

//忽略类型可以一次性声明多个不同的变量
var x, y, z = "hello", false, 1

var clothingSize, clothingColor = LARGE, "black"

//init函数  类似Java的静态代码块,在包初始化时会被调用
func init() {
	height = 178.88
	fmt.Printf("my name is %s,height = %v\n", name, height)
}

// People 结构体 类似于Java类
type People struct {
	age    int
	name   string
	height float32
}

//main方法
func main() {
	//main方法执行时传入的参数  args类型是slice切片,第一个元素是程序的名称
	//Args hold the command-line arguments, starting with the program name.
	args := os.Args
	fmt.Println(args)
	//
	//stdin := os.Stdin
	//if all, err := ioutils.ReadAll(stdin); err == nil {
	//	fmt.Println(all)
	//}
	//new(T)返回的是*T,该类型对象对应的指针
	newP := new(People)
	newP.age = 66
	newP.height = 155
	newP.name = "大爷"
	fmt.Println(newP)

	//短变量声明,用来初始化局部变量,可同时声明多个不同的变量
	m, n := 1, 2
	fmt.Println(m, n)

	//短变量声明最少得声明一个新变量 open是新声明的,error是重新赋值。
	create, err := os.Create("")
	open, err := os.Open("")
	fmt.Println(create, open, err)

	//指针,用来存储一个变量的地址。一个指针,指示值所保存的位置。
	//不是所有的值都有地址。但是所有的变量都有地址
	// pi := &PI  Cannot take the address of 'PI'   PI这个常量无法获取地址
	//使用指针,可以在无须知道变量名字的情况下,间接读取变量或者更改变量的值。

	x := 1
	p := &x                            ////使用&符号取地址  表达式&x(x的地址)获取一个指向整形变量的指针,类型是整形指针*int
	fmt.Println(p)                     //0xc00000a0f0
	fmt.Println(*p)                    //表达式*p获取变量的值
	fmt.Println("p != nil:", p != nil) //true
	ptr := &x
	fmt.Println("p == ptr:", p == ptr) //true
	//指针的指针是这个指针变量ptr的地址
	i := &ptr
	fmt.Println(i) //0xc000006088
	//针对指针(变量)的指针取值 是该指针的变量地址值
	fmt.Println(*i) //0xc00000a0f0   与p的地址相同

	p2 := People{height: 162, name: "大娘"}
	fmt.Println(p2)
	fmt.Println(height)
	fmt.Println(weight)

	var p3 People = People{height: 155, name: "大妈", age: 88}
	fmt.Println(p3)
	fmt.Println(p3.height)
	fmt.Println(p3.age)

	changePeopleName(&p3)  //传入指针,修改了实际对象
	changePeopleHeight(p3) //只修改了副本
	fmt.Println(p3)

	//赋值
	q := 1
	q++
	q += 1
	q *= 2
	fmt.Println(q) //6

	//交换赋值,不需要借助临时变量
	x, y := 1, 2
	y, x = x, y
	fmt.Println(x, y) //2 1

	//类型声明
	type newFloat32 float32
	var s newFloat32 = 1
	var g float32 = 1
	//fmt.Println(s == g) //不同类型无法比较 Invalid operation: s == g (mismatched types newFloat32 and float32)

	//类型转换 如果两个类型具有相同的底层类型 或者 二者都指向相同类型的未命名变量指针,则二者是可以相互转换的(这句话暂且没搞懂)
	fmt.Println("类型转换之后相同类型才能判断是否相等:",s == newFloat32(g)) //true 通过类型转换T(x)
	//sPtr:=&s
	//gPtr:=&g
	////fmt.Println(sPtr == gPtr)
	////fmt.Println(sPtr == newFloat32(gPtr))

}

//函数的声明 func关键字、函数名称  参数列表、 返回列表(可返回多个值)
func floatToInt(f float64) int {
	return int(f)
}

func changePeopleName(ptr *People) {
	fmt.Println("Before changePeopleName:", ptr.name)
	ptr.name = "你大爷"
	fmt.Println("After changePeopleName:", ptr.name)
}

func changePeopleHeight(p People) {
	fmt.Println("Before changePeopleHeight:", p.height)
	p.height = 226.00
	fmt.Println("After changePeopleHeight:", p.height)
}

变量的生命周期

  • 生命周期是指在程序执行过程中变量存在的时间段。

  • 包级别变量的生命周期是整个程序的执行时间。

  • 局部变量有一个动态的生命周期,每次执行声明语句时创建一个新的实体,变量一直生存到变得不可访问,这时它占用的存储空间被回收。函数的参数和返回值也是局部变量,它们在其闭包函数被调用的时候创建。

  • 垃圾回收器如何知道一个变量是否应该被回收?基本思路是每一个包级别的变量,以及每一个当前执行函数的局部变量,可以作为追溯该变量的路径的源头,通过指针和其他方式的引用可以找到变量。如果变量的路径不存在,那么该变量变得不可访问。(可达性分析)

  • 局部变量可在包含它的循环的一次迭代之外继续存活,即使包含它的循环已经返回,它的存在还可能延续。因为变量的生命周期是通过它是否可达来确定的。

包的导入和初始化、作用域

  • 包的导入路径从$GOPATH/src开始
  • 包的初始化,按照在程序中导入的顺序来进行,依赖顺序优先,每次初始化一个包。如果包p导入了包q,那么先初始化p,后初始化q。main包最后初始化。
  • 声明的作用域是指声明在程序文本中出现的区域,是一个编译时属性。变量的生命周期是指变量在程序执行期间能被程序的其他部分所引用的起止时间,是一个运行属性。

你可能感兴趣的