搞定Go语言之第四天

指针

任何程序数据载入内存后,在内存都有他们的地址,这就是指针。而为了保存一个数据在内存中的地址,我们就需要指针变量。

地址:就是内存地址(用字节来描述的内存地址)

指针:指针是带类型的。

&和*

&:表示取地址

*:根据地址取值

定义一个变量:

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)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kuokay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值