指针
任何程序数据载入内存后,在内存都有他们的地址,这就是指针。而为了保存一个数据在内存中的地址,我们就需要指针变量。
地址:就是内存地址(用字节来描述的内存地址)
指针:指针是带类型的。
&和*
&:表示取地址
*:根据地址取值
定义一个变量:
var a int
取变量a的内存地址:
b := &a
取地址对应的值:
fmt.Println(*b)
定义一个修改数组第一个元素为100的函数:
func modifyArray(a1 [3]int) {
a1[0] = 100 //只是修改的内部的a1这个数组
}
定义一个修改数组第一个元素为100的函数,接收的参数是一个数组的指针
func modifyArray2(a1 *[3]int) {
// (*a1)[0] = 100 //只是修改的内部的a1这个数组
//语法糖:因为Go语言中指针不支持修改
a1[0] = 100 //只是修改的内部的a1这个数组
}
new和make
二者都是内存的分配(堆上),但是make只用于slice、map以及channel的初始化(非零值);
而new用于类型的内存分配,并且内存置为零。所以在我们编写程序的时候,就可以根据自己的需要很好的选择了。
make返回的还是这三个引用类型本身;而new返回的是指向类型的指针。
new()
该方法的参数要求传入一个类型,而不是一个值,它会申请一个该类型大小的内存空间,并会初始化为对应的零值,返回指向该内存空间的一个指针。如下:
func new(Type) *Type
make()
make也是用于内存分配,但是和new不同,它只用于slice、map和channel的内存创建,它返回的类型就是类型本身,而不是它们的指针类型。
func make(t Type, size ...IntegerType) Type
案例:
package main
import "fmt"
func main() {
// 以下是错误的写法
// var a *int //a是一个int类型的指针
// var b *string
// var c *[3]int
// 以上是错误的写法
var a = new(int) //得到一个int类型的指针
fmt.Println(a)
*a = 10
fmt.Println(a)
fmt.Println(*a)
var c = new([3]int)
fmt.Println(c)
c[0] = 1
fmt.Println(*c)
}
panic和recover
其实,Go语言是不支持 try…catch…finally 这种异常处理的,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,甚至一个小小的错误都抛出一个异常。
在Go语言中,使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的情况下,才使用Go中引入的Exception处理:defer, panic, recover。
panic:运行时异常
panic用法挺简单的, 其实就是throw exception。
panic是golang的内建函数,panic会中断函数F的正常执行流程, 从F函数中跳出来, 跳回到F函数的调用者. 对于调用者来说, F看起来就是一个panic, 所以调用者会继续向上跳出, 直到当前goroutine返回. 在跳出的过程中, 进程会保持这个函数栈. 当goroutine退出时, 程序会crash。
要注意的是, F函数中的defered函数会正常执行, 按照上面defer的规则。
同时引起panic除了我们主动调用panic之外, 其他的任何运行时错误, 例如数组越界都会造成panic
panic用法:
package main
import (
"fmt"
)
func main() {
test()
}
func test() {
defer func() { fmt.Println("打印前") }()
defer func() { fmt.Println("打印中") }()
defer func() { fmt.Println("打印后") }()
panic("触发异常")
fmt.Println("test")
}
-------output-------打印后 打印中 打印前 panic: 触发异常 goroutine 1 [running]: main.test() D:/Go_Path/go/src/logDemo/main.go:15 +0x98 main.main() D:/Go_Path/go/src/logDemo/main.go:8 +0x27 exit status 2```
**recover:用来将函数在panic时恢复回来,用于做一些资源回收的操作**
recover也是golang的一个内建函数, 其实就是try catch。
不过需要注意的是:
1. recover如果想起作用的话, 必须在defered函数中使用。
2. 在正常函数执行过程中,调用recover没有任何作用, 他会返回nil。如这样:fmt.Println(recover()) 。
3. 如果当前的goroutine panic了,那么recover将会捕获这个panic的值,并且让程序正常执行下去。不会让程序crash。
**recover 用法:**
```go
func main() {
fmt.Println("c")
defer func() { // 必须要先声明defer,否则不能捕获到panic异常
fmt.Println("d")
if err := recover(); err != nil {
fmt.Println(err) // 这里的err其实就是panic传入的内容
}
fmt.Println("e")
}()
f() //开始调用f
fmt.Println("f") //这里开始下面代码不会再执行
}
func f() {
fmt.Println("a")
panic("异常信息")
fmt.Println("b") //这里开始下面代码不会再执行
}
-------output-------
c
a
d
异常信息
e
结构体(struct)和方法
type关键字用来在Go语言中定义新的类型。
创造一个新类型
type NewInt int
类型别名(软链)
var MyInt = int
byte: uint8 和 rune:int32是Go语言内置的别名。
类型别名只在代码编写过程中生效,编译完不存在。
结构体的定义
//结构体
// 创在新的类型要使用type关键字
type student struct {
name string
age int
gender string
hobby []string
}
func main() {
var haojie = student{
name: "豪杰",
age: 19,
gender: "男",
hobby: []string{"篮球", "足球", "双色球"},
}
//结构体支持.访问属性
fmt.Println(haojie)
fmt.Println(haojie.name)
fmt.Println(haojie.age)
fmt.Println(haojie.gender)
fmt.Println(haojie.hobby)
}
结构体的实例化
var haojie = student{
name: "豪杰",
age: 19,
gender: "男",
hobby: []string{"篮球", "足球", "双色球"},
}
结构体支持.访问属性
fmt.Println(haojie)
fmt.Println(haojie.name)
fmt.Println(haojie.age)
fmt.Println(haojie.gender)
fmt.Println(haojie.hobby)
实例化方法1
// struct是值类型的
// 如果初始化时没有给属性(字段)设置对应的初始值,那么对应属性就是其类型的默认值
var wangzhan = student{}
fmt.Println(wangzhan.name)
fmt.Println(wangzhan.age)
fmt.Println(wangzhan.gender)
fmt.Println(wangzhan.hobby)
实例化方法2 new(T) T:表示类型或结构体
var yawei = new(student)
fmt.Println(yawei)
// (*yawei).name
yawei.name = "亚伟"
yawei.age = 18
fmt.Println(yawei.name, yawei.age)
// 实例化方法3
var nazha = &student{}
fmt.Println(nazha)
nazha.name = "沙河娜扎"
fmt.Println(nazha.name)
结构体初始化
var stu1 = student{
"豪杰",
18,
"男",
[]string{"男人", "女人"},
}
fmt.Println(stu1.name, stu1.age)
//键值对初始化
var stu2 = &student{
name: "豪杰",
gender: "男",
}
fmt.Println(stu2.name, stu2.age, stu2.gender)
}
结构体的内存布局
// 内存是以字节为单位的十六进制数
// 1字节 = 8位 = 8bit
func main() {
type test struct {
a int16
b int16
c int16
}
var t = test{
a: 1,
b: 2,
c: 3,
}
fmt.Println(&(t.a))
fmt.Println(&(t.b))
fmt.Println(&(t.c))
}
方法
方法就是某个具体的类型才能调用的函数,Go的方法是在函数前面加上一个接收者。
type people struct {
name string
gender string
}
//函数指定接受者之后就是方法
// 在go语言中约定成俗不用this也不用self,而是使用后面类型的首字母的小写
func (p *people) dream() {
p.gender = "男"
fmt.Printf("%s的梦想是不用上班也有钱拿!\n", p.name)
}
func main() {
var haojie = &people{
name: "豪杰",
gender: "爷们",
}
// (&haojie).dream()
haojie.dream()
fmt.Println(haojie.gender)
}
package main
import "fmt"
// 可以给任意类型追加方法
// 不能给别的包定义的类型添加方法
type MyInt int
func (m *MyInt) sayHi() {
fmt.Println("Hello MyInt~")
}
func main() {
var a MyInt
fmt.Println(a)
a.sayHi()
}
构造函数
func newStudent(n string, age int, g string, h []string) *student {
return &student{
name: n,
age: age,
gender: g,
hobby: h,
}
结构体的嵌套
package main
import "fmt"
// 结构体内嵌模拟“继承”
type animal struct {
name string
}
//定义一个动物会动的方法
func (a *animal) move() {
fmt.Printf("%s会动~\n", a.name)
}
//定义一个狗的结构体
type dog struct {
feet int
animal
}
//定义了一个狗的方法 wangwang
func (d *dog) wangwang() {
fmt.Printf("%s 在叫:汪汪汪~\n", d.name)
}
func main() {
var a = dog{
feet: 4,
animal: animal{
name: "旺财",
},
}
a.wangwang() //调用狗的方法
a.move() //调用动物的方法
}
结构体的匿名字段
package main
import "fmt"
// 匿名字段
type student struct {
name string
string
int
}
func main() {
var stu1 = student{
name: "豪杰",
}
fmt.Println(stu1.name)
fmt.Println(stu1.string)
fmt.Println(stu1.int)
}

被折叠的 条评论
为什么被折叠?



