Go_方法

方法

基本介绍

  • 使用情景:如在Person结构体中,除了一些字段外(姓名、年龄等),还需要一些行为(如:可以说话,会做算数体等),这时候就需要用方法才能完成
  • Golang中的方法是作用在指定的数据类型上的(即方法与数据类型绑定),因此自定义类型都可以有方法,而不仅仅是struct

方法的声明和调用-快速入门

  • 快速入门案例
package main

import "fmt"

type Person struct {
	Name string
}

func (p Person) test() {
	p.Name = "anger"
	fmt.Println("p.test()中p.Name的值:", p.Name)   // 输出  p.test()中p.Name的值: anger
}
func main() {
	//定义一个person实例
	var p Person
	p.Name = "tom"
	p.test() //调用方法
	fmt.Println("main函数中p.Name的值:", p.Name)  //输出   main函数中p.Name的值: tom
}
  • 对上述代码块的说明:
    • func (p Person) test() :表示A结构体有一个方法,方法名为test
    • test方法和Person类型绑定
    • test方法只能通过Person类型的变量来调用,而不能直接调用,也不能使用其他类型变量来调用
    • func (p Person) test():中的p 表示那个Person变量调用,这个p就是它的副本,这点和函数传参非常相似(像形参)
    • p 这个名字,有程序员指定,不是固定的,比如改成person

方法快速入门

  • 给Person结构体添加speak方法,输出 xxx是一个好人
  • 给Person结构体添加jisuan方法,可以计算从1 +…+1000的结果
  • 给Person结构体jisuan2方法,该方法可以接收一个数n,计算从1+…+n的结果
  • 给Person结构体添加getSum方法,可以计算两个数的和,并返回结果
package main

import "fmt"

/*
给Person结构体添加speak方法,输出  xxx是一个好人
给Person结构体添加jisuan方法,可以计算从1 +.....+1000的结果
给Person结构体jisuan2方法,该方法可以接收一个数n,计算从1+......+n的结果
给Person结构体添加getSum方法,可以计算两个数的和,并返回结果
*/
type Person struct {
	Name string
}

func (p Person) speak() {
	fmt.Println(p.Name, "是一个好人")
}

func (p Person) jisuan() {
	sum := 0
	for i := 0; i < 1000; i++ {
		sum += i
	}
	fmt.Println(p.Name, "计算的结果是", sum)
}

func (p Person) jisuan2(n int) {
	sum := 0
	for i := 0; i < n; i++ {
		sum += i
	}
	fmt.Println(p.Name, "计算的结果是", sum)
}

func (p Person) getSum(n1, n2 int) int {
	return n1 + n2

}
func main() {
	//定义一个person实例
	var p Person
	p.Name = "tom"
	p.speak() //方法调用
	p.jisuan()
	p.jisuan2(10)
	n1 := 3
	n2 := 4
	res := p.getSum(n1, n2)
	fmt.Println(res)
}

方法的调用和传参机制

  • 下面画出上面代码getSum方法
    内存机制

  • 说明:

    • 在通过一个变量去调用方法时,其调用机制和函数一样
    • 不一样的地方时,变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,则进行值拷贝;如果变量是引用类型,则进行引用拷贝)
  • 案例2:请编写一个程序,要求如下:

    • 声明一个结构体Cricle,字段为radius
    • 声明一个方法area和Circle绑定,可以返回面积
    • 提示:画出area执行过程+说明
package main

import "fmt"

/*
- 声明一个结构体Circle,字段为radius
- 声明一个方法area和Circle绑定,可以返回面积
- 提示:画出area执行过程+说明
*/
type Circle struct {
	radius float64
}

func (c Circle) area() float64 {
	return 3.14 * c.radius * c.radius
}

func main() {
	//定义一个person实例
	var c Circle
	c.radius = 4.0
	res := c.area()
	fmt.Println("面积是=", res)
}

方法声明-深度理解

  • 方法声明的完整定义:
func (receiver) methodName(参数列表)(返回值列表){
	方法体
	return 返回值
}
  • receiver type:表示这个方法和type这个类型进行绑定,或者说该方法作用于type类型
  • receiver type:type可以是结构体,也可以是其他的自定义类型
  • receiver:就是type类型的一个变量(实例),比如:Person结构体的一个变量(实例)
  • 参数列表:表示方法输入
  • 返回值列表:表示返回的值,可以多个
  • 方法主体:表示为了实现某一功能代码块
  • return语句不是必须的,但是有返回值列表的时候一定要有return语句

方法注意事项和细节

  • 结构体类型默认值传递;如果程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理
package main

import "fmt"

type Circle struct {
	radius float64
}

// 为了提高效率,通常我们方法和结构体的指针类型绑定
func (c *Circle) area2() float64 {
	//因为c是一个指针,标准的访问方式应该这样写:(*c).radius,也可以等价c.radius
	fmt.Printf("area2()方法中变量c,类型指针的地址是:%p\n", c)
	c.radius = 10
	return 3.14 * (*c).radius * (*c).radius
}
func main() {
	//定义一个person实例
	var c Circle
	c.radius = 7.0
	fmt.Printf("Circle结构体中变量c的地址是:%p\n", &c)
	res2 := c.area2()
	//编译器底层做了优化,(&c).area2()等价 c.area2()
	//因为编译器会自动加上&c
	fmt.Println("面积是=", res)
}

指针传递情况

  • Golang中的方法作用在指定的数据类型上的(即,指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅是struct,比如 int、float32等都可以有方法
package main

import "fmt"

type integer int

func (i integer) print() {
	fmt.Println("i=", i)
}

// 请编写一个方法,改变i的值
func (i *integer) change() {
	*i = *i + 1
}

func main() {
	var i integer = 10
	i.print()
	i.change()
	fmt.Println("i=", i)
}
  • 方法的访问范围控制的规则,和函数一样。方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其他包访问
  • 如果一个类型实现了String()这个方法,那么fmt.Println默认会调用这个变量的String()进行输出
package main

import "fmt"

type Student struct {
	Name string
	Age  int
}

func (stu *Student) String() string {
	str := fmt.Sprintf("Name=[%v] Age=[%v]", stu.Name, stu.Age)
	return str
}

func main() {
	//
	stu := Student{
		Name: "tom",
		Age:  20}
		//如果实现了*Student类型的String方法,就会自动调用
	fmt.Println(&stu)
}

练习

  • 1、编写结构体(MethodUtils),编一个方法,方法不需要参数,在方法中打印一个10*8的矩形,在main方法中调用该方法
package main

import "fmt"

type MethodUtils struct {
	//Len int
	//Width  int
}

func (rect MethodUtils) Print() {
	for i := 1; i <= 10; i++ {
		for j := 1; j <= 8; j++ {
			fmt.Print("*")
		}
		fmt.Println()
	}
}

func main() {
	var mu MethodUtils
	mu.Print()
}
  • 2、编写一个方法,提供m和n两个参数,方法中打印一个m*n的矩阵
package main

import "fmt"

type MethodUtils struct {
	//Len int
	//Width  int
}

func (rect MethodUtils) Print(m int, n int) {
	for i := 1; i <= m; i++ {
		for j := 1; j <= n; j++ {
			fmt.Print("*")
		}
		fmt.Println()
	}
}

func main() {
	var mu MethodUtils
	mu.Print(3, 4)
}
  • 3、编写一个方法算该矩形的面积(可以接收长len、和宽width),将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印
package main

import "fmt"

type MethodUtils struct {
	//Len int
	//Width  int
}

func (rect MethodUtils) Area(len float64, width float64) float64 {
	return len * width
}

func main() {
	var mu MethodUtils
	m := 5.3
	n := 2.4
	res := mu.Area(m, n)
	fmt.Println("面积为:", res)
}

  • 编写方法:判断一个数是奇数还是偶数
package main

import "fmt"

type MethodUtils struct {
	//Len int
	//Width  int
}

func (ret *MethodUtils) JudgeNum(num int) {
	if num%2 == 0 {
		fmt.Println(num, "是偶数")
	} else {
		fmt.Println(num, "是奇数")
	}
}

func main() {
	var mu MethodUtils
	mu.JudgeNum(13)
}
  • 根据行、列、字符打印对应行数和列数的字符,比如:行:3,列:2,字符 *,则打印相应的效果
package main

import "fmt"

type MethodUtils struct {
	//Len int
	//Width  int
}

func (ret MethodUtils) Print(n int, m int, key string) {
	for i := 1; i <= n; i++ {
		for j := 1; j <= m; j++ {
			fmt.Print(key)
		}
		fmt.Println()
	}
}

func main() {
	var mu MethodUtils
	mu.Print(3, 4, "&")
}
  • 定义小小计算器结构体(Calcuator),实现加减乘除四个功能,实现形式1:分四个方法完成;实现形式2:用一个方法搞定
// 实现形式1:分四个方法,分别计算加减乘除;通过方法传递
package main

import "fmt"

type Calcuator struct {
}

func (ret Calcuator) Add(n int, m int) {
	fmt.Println("两数的和为:", n+m)
}
func (ret Calcuator) Sub(n int, m int) {
	fmt.Println("两数的差为:", n-m)
}
func (ret Calcuator) Multiply(n int, m int) {
	fmt.Println("两数的乘积为:", n*m)
}
func (ret Calcuator) Mod(n int, m int) {
	fmt.Println("两数的除法为:", n/m)
}
func main() {
	var mu Calcuator
	mu.Add(4, 2)
	mu.Sub(4, 2)
	mu.Multiply(4, 2)
	mu.Mod(4, 2)

}

// 实现形式1:分四个方法,分别计算加减乘除;通过结构体传递
package main

import "fmt"

type Calcuator struct {
	Num1 int
	Num2 int
}

func (ret Calcuator) Add() {
	fmt.Println("两数的和为:", ret.Num1+ret.Num2)
}
func (ret Calcuator) Sub() {
	fmt.Println("两数的差为:", ret.Num1-ret.Num2)
}
func (ret Calcuator) Multiply() {
	fmt.Println("两数的乘积为:", ret.Num1*ret.Num2)
}
func (ret Calcuator) Mod() {
	fmt.Println("两数的除法为:", ret.Num1/ret.Num2)
}
func main() {
	var mu Calcuator
	mu.Num1 = 4
	mu.Num2 = 2
	mu.Add()
	mu.Sub()
	mu.Multiply()
	mu.Mod()

}
// 实现形式2:用一个方法,需要接受两个数,还有一个运算符
package main

import "fmt"

type Calcuator struct {
	Num1 float64
	Num2 float64
}

func (calcuator *Calcuator) getRes(operator byte) float64 {
	res := 0.0
	switch operator {
	case '+':
		res = calcuator.Num1 + calcuator.Num2
	case '-':
		res = calcuator.Num1 - calcuator.Num2
	case '*':
		res = calcuator.Num1 * calcuator.Num2
	case '/':
		res = calcuator.Num1 / calcuator.Num2
	default:
		fmt.Println("运算符输入有误")
	}
	return res
}

func main() {
	var mu Calcuator
	mu.Num1 = 4.0
	mu.Num2 = 2.0
	res := mu.getRes('+')
	fmt.Println("res=", res)

}

课后题

  • 1、在MethodUtils结构体变革方法,从键盘接受整数(1-9),打印对应乘法表
import "fmt"

type MethodUtils struct {
}

// 从键盘接受整数(1-9),打印对应乘法表
func (method MethodUtils) Print(n int) {
	for i := 1; i <= n; i++ {
		for j := 1; j <= i; j++ {
			fmt.Printf("%d * %d = %d\t", j, i, i*j)
		}
		fmt.Println()
	}

}

func main() {
	var method MethodUtils
	//var num int
	//fmt.Println("请输入你想打印的乘法表,输入数范围1-9")
	//fmt.Scanln(&num)
	method.Print(5)

}
  • 2、编写一个方法,使给定的一个二维数组(3*3)转置

如下图

package main

import "fmt"

type MethodUtils struct {
}

// 从键盘接受整数(1-9),打印对应乘法表
func (method MethodUtils) Trun(arr [3][3]int) {
	var temp int
	for i := 0; i < 3; i++ {
		for j := 0; j < i; j++ {
			temp = arr[i][j]
			arr[i][j] = arr[j][i]
			arr[j][i] = temp
		}

	}
	fmt.Println()
	fmt.Println("转置后:")
	for i := 0; i < 3; i++ {
		for j := 0; j < 3; j++ {
			fmt.Printf("%v\t", arr[i][j])
		}
		fmt.Println()
	}

}

func main() {
	var method MethodUtils
	var arr = [3][3]int{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}
	fmt.Println("转置前")
	for _, v := range arr {
		for _, v2 := range v {
			fmt.Printf("%d\t", v2)
		}
		fmt.Println()
	}
	method.Trun(arr)

}

方法和函数的区别

  • 调用方式不同
    • 函数的调用方式:函数名(实参列表)
    • 方法的调用方式:变量.方法名(实参列表)
  • 对于普通函数,接受者为值类型时,不能将指针类型的数据直接传递,反之亦然
  • 对于方法(如struct的方法),接受者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以
//结构体中的方法调用中,真正决定 是 值拷贝还是地址拷贝,看这个方法是和那个类型绑定的
package main

import "fmt"

type Person struct {
	Name string
}

func (person Person) test() {
	person.Name = "jack"
	fmt.Println("test输出:", person.Name)
}

func main() {
	var person Person
	person.Name = "tom"
	person.test()
	(&person).test()
	fmt.Println("main输出:", person.Name)

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值