[go学习笔记.第十章.面向对象编程] 4.方法介绍,使用,调用,传参机制和方法的声明,定义剖析以及注意事项

1.基本介绍

        在某些情况下,需要声明(定义)方法,比如:Person结构体,除了有一些字段(年龄,名字,...),Person结构体还有一些行为,比如:可以说话,跑步,...通过学习,还可以做算术题,这个时候就要用到方法才能够完成.

        go中的方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型都可以有方法,而不仅仅是struct

2.方法的声明和调用

type A struct {

        Num int

func (a A) test(){

        fmt.Println(a.Num)

}

对上面语法的说明:

(1).func(a A) test(){} 表示: A结构体有一方法,方法名为test

(2).(a A)体现 test方法和A类型绑定的

举例说明

package main

import(
    "fmt"
)

//结构体
type Person struct{
    Name string
}

type Dog struct {

}
//给Person类型绑定一个方法
func (p Person) test(){
    p.Name = "jack"
    fmt.Println("test() name=", p.Name) //jack
}
func main()  {
    //定义一个结构体
    var p Person
    p.Name = "tom"
    p.test()//调用方法test(): test() name= tom
    //下面的使用都是错误的
    var dog Dog
    //dog.test()// dog.test undefined (type Dog has no field or method test)
    //test()//undefined: test
}

 对上面代码总结

(1).test方法和Person类型绑定

(2).test方法只能通过Person类型的变量来调用,而不能直接调用,也不能使用其他类型变量来调用

(3).func (p Person) test(){} p表示哪个Person变量调用,这个p就是它的副本,这个和函数传参非常相似

(4).p这个名字是由程序员指定的,不是固定的,比如修改成person也是可以的

3.方法快速入门

(1).给Person结构体添加speak方法,输出 xxxxxx牛逼

//结构体
type Person struct{
    Name string
}

//给person添加一个方法,speak,输出:xxx牛逼
func (p Person) speak(){
    fmt.Println(p.Name, "牛逼")   //tom
}
func main()  {
    //定义一个结构体
    var p Person
    p.Name = "tom"
    p.speak()
}

(2).给Person结构体添加jisuan方法,可以计算从1+...+1000的结果,说明方法体内可以和函数一样,进行各种运算

//给person添加一个方法:计算1~1000的和,并输出
func (p Person) jisuan(){
    sum := 0
    for  i := 1; i <= 1000; i++ {
        sum += i
    }
    fmt.Println(sum)
}

(3).给Person结构体添加方法jisuan2,该方法可以接收一个数n,计算从1+...+n的结果

//给person添加一个方法:计算1~n的和,并输出
func (p Person) jisuan2(n int){
    sum := 0
    for  i := 1; i <= n; i++ {
        sum += i
    }
    fmt.Println(sum)    
}
p.jisuan(2)

(4).给Person结构体添加getSum方法,可以计算两个数的和,并返回结果

//给person添加一个方法:计算两个数的和,并返回
func (p Person) getSum(n1 int, n2 int) int{
    return n1 + n2
}
res := p.getSum(10,20)

4.方法的调用和传参机制

说明:

        方法的调用和传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当做实参也传递给方法,举例说明

案例1:画出前面getSum方法的执行过程+说明

对上面执行流程图说明

(1).在通过一个变量去调用方法时,其调用机制和函数一样

(2).不一样的地方是:变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,就进行值拷贝,如果变量是引用类型,就进行地址拷贝) 

 案例2:

编写一个程序,要求:

1.声明一个结构体Circle,字段为radius

2.声明一个方法area和circle绑定,返回面积

3.画出area执行过程+说明

package main

import(
    "fmt"
)

//声明一个结构体Circle,字段为radius
//声明一个方法area和circle绑定,返回面积
type Circle struct{
    radius float64
}
func (circle Circle) area() float64 {
    //计算面积
    return 3.14 * circle.radius *  circle.radius
}
func main()  {
    var circle Circle
    circle.radius = 10
    area := circle.area()
    fmt.Println(area)
}

 5.方法的声明和定义剖析

语法:

        func (recevier type) methodName (参数列表) (返回值列表){

                方法体

                return 返回值

        }

说明:

(1).参数列表:表示方法输入

(2).recevier type:表示这个方法和type这个类型进行绑定,或者说该方法作用于type类型

(3).recevier type:type可以是结构体,也可以是其他自定义类型

(4).recevier:就是type类型的一个变量(实例),比如:Person结构体的一个变量(实例)

(5).返回值列表:表示返回的值,可以多个

(6).方法主体:表示为了实现某一功能的代码块

(7).return语句不是必须的

6.方法的注意事项和细节讨论

(1).结构体类型是值传递,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式

(2).如果程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方法来处理

package main

import(
    "fmt"
)

//声明一个结构体Circle,字段为radius
//声明一个方法area和circle绑定,返回面积
type Circle struct{
    radius float64
}
func (circle Circle) area() float64 {
    //计算面积
    return 3.14 * circle.radius *  circle.radius
}

//为了提高效率,通常我们方法和结构体的指针类型绑定
func (circle2 *Circle) area2() float64 {
    //计算面积
    //因为circle2是指针,所有我们标准的访问字段的方式:(*circle2).radius
    // return 3.14 * (*circle2).radius *  (*circle2).radius
    //(*circle2).radius等价 circle2.radius
    return 3.14 * circle2.radius *  circle2.radius
}
func main()  {
    // var circle Circle
    // circle.radius = 10
    // area := circle.area()
    // fmt.Println(area)
    // 创建一个circle变量
    var circle Circle
    circle.radius = 10
    // area := (&circle).area2()
    //编辑器底层做了优化:(&circle).area()等价于circle.area(),编译器会自动加上&circle
    area := circle.area2()
    fmt.Println(area)
}

 

(3).go中的方法作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅是struct,比如:int,float32等都可以有方法, 

package main

import(
    "fmt"
)

/**
 * go中方法作用在指定的数据类型上(即:和指定的数据类型绑定),
 * 因此:自定义类型,都可以有方法,不仅仅是struct, 比如"int float64等都可以有方法
 */

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()   //10
    i.change()
    fmt.Println("i=", i)    //11
}

 (4).方法的访问范围控制的规则,和函数一样,方法名首字母小写,只能在本包访问,方法名首字母大写,可以在本包和其他包访问

(5).如果一个类型出现了String()这个方法,那么fmt.Println()默认会调用这个变量的String()进行输出

package main

import(
    "fmt"
)

type Student struct{
    Name string
    Age int
}
//给*Student 实现String()
func (stu *Student) String() string {
    str := fmt.Sprintf("Name=%v \t Age=%v", stu.Name, stu.Age)
    return str
}
func main()  {
    //定义一个student
    stu := Student{
        Name: "Jack",
        Age: 11,
    }
    fmt.Println(stu)//{Jack 11}
    //如果实现了*Student类型的String方法,就会自动调用String方法
    fmt.Println(&stu)//Name=Jack        Age=11
}

[上一节][go学习笔记.第十章.面向对象编程] 3.结构体内存分配机制以及注意事项和使用细节

[下一节][go学习笔记.第十章.面向对象编程] 5.方法的练习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值