zoukankan      html  css  js  c++  java
  • 十、面向对象编程_上

    10.1 结构体

    10.1.1 结构体

    看一个问题

    t2bcPf.png

    10.1.2 一个程序就是一个世界,有很多对象(变量)

    10.1.3 Golang语言面向对象编程说明

    t2qBSU.png

    t2LM7R.png

    10.1.4 结构体与结构体变量(实例/对象)的关系示意图

    43672846238468.PNG

    对上图的说明

    1、将一类事物的特性提取出来(比如:猫类),形成一个新的数据类型,就是一个结构体

    2、通过这个结构体,我们可以创建多个变量(实例对象)

    3、事物可以是猫类,也可以是person,也可以是鱼类

    t2OIZd.png

    10.1.5 快速入门-面向对象的方式(struct)解决养猫的问题

    t2jmjS.png

    10.1.6 结构体和结构体变量(实例)的区别联系

    • 结构体是自定义的数据类型,代表一类事物
    • 结构体变量(实例)是具体的,实际的,代表一个具体变量

    10.1.7 结构体变量(实例)在内存中的布局—重要

    结构体是值类型

    tREowt.png

    10.1.8 如何声明结构体

    type 结构体名称 struct {
        field1 type
        field2 type
        .....
    }
    
    // 举例
    
    type Cat struct {
    	Name string   //Name 大写则表示可以被其他包引用
    	Age int 
    	Color string
    	Hobby string
    }
    

    10.1.9 字段/属性

    基本介绍

    • 从概念或叫法上:结构体=属性=field
    • 字段是结构体的一个组成部分,一般是基本数据类型、数组、也可是引用类型,比如我们前面定义猫的结构体的Name string就是属性

    注意事项

    • 字段声明语法同变量,示例: 字段名 字段类型
    • 字段的类型可以为:基本类型、数组或引用类型
    • 在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值),规则同前面说的一样
    • 布尔类型是 false 、整型是 0 、 字符串是 “”
      • 数组类型的默认值和它的元素类型相关,比如:socore [3]int 则为[0,0,0]
      • 指针,slice和map的零值都是nil,即还没有分配空间
    • 不同结构体变量的字段是独立的,互不影响,一个结构体变量字段的更改,不影响另外一个,结构体是值类型

    43672846238468.PNG

    上述代码的内存示意图

    43672846238468.PNG

    10.1.10 创建结构体变量和访问结构体字段

    10.1.10.1 方式1

    直接声明

    var person Person

    10.1.10.2 方式2

    var person Person = Person {}

    43672846238468.PNG

    10.1.10.3 方式3

    43672846238468.PNG

    43672846238468.PNG

    10.1.10.4 方式4

    43672846238468.PNG

    说明

    • 第三种和第四种方式返回的是 结构体指针

    • 结构体指针访问字段的标准方式应该是:(*结构体指针).字段名,

      • (*person).Name = "tom"
        
    • 但go做了一个简化,也支持 结构体指针.字段名 比如:person.Name = "tom" 更加符合程序员使用的习惯,go编译器底层对 person.Name做了转化(*person).Name

    10.1.11 struct类型的E内存分配机制

    思考?

    43672846238468.PNG

    变量总是在内存中的,那么结构体变量在内存中究竟是怎样存在的?

    结构体在内存中示意图

    43672846238468.PNG

    43672846238468.PNG

    上述代码内存图

    43672846238468.PNG

    43672846238468.PNG

    10.1.12 结构体的注意事项和使用细节

    1、结构体的所有字段在内存中是连续的

    tRxWPs.png

    tROdXD.png

    2、结构体是用户单独定义的类型,和其他类型进行转换时需要有完全相同的字段(名字、个数和类型)

    987654345678.PNG

    3、结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以强转

    987654345678.PNG

    10.1.13 结构体注意事项

    • struct的每个字段上,可以写上一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列号和反序列化

    序列化的使用场景

    tWnX7j.png

    代码

    tWnRne.png

    10.2 方法

    10.2.1 基本介绍

    ​ 在某些情况下,我们需要声明/定义(方法),比如:Person结构体,除了有一些字段外(年龄,姓名...)Person结构体还有一些行为,这时就要用方法才能完成

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

    10.2.2 方法的声明和调用

    type A struct {
        Num int
    }
    func (a A) test() {
        fmt.Println(a.Num)
    }
    

    对上面语法的说明

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

    2、(a A) 体现方法是和A类型绑定的

    举例

    tWc2Js.png

    对上面代码说明

    1、 test方法和Person类型绑定

    2、test方法只能通过Person类型的变量来调用,也不能使用其他类型变量来调用

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

    4、func (p Person) test(){....} p这个名字,有程序员固定,不是固定不变的

    10.2.3 方法快速入门

    1、给Person结构体添加 speak方法,输出 xxx是一个好人

    =

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

    tWoGcQ.png

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

    tWTKKJ.png

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

    tWT5aq.png

    5、方法调用的几种形式

    tW7pi6.png

    10.2.4 方法的调用和传参机制原理(重要!!)

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

    案例1

    前面getSum方法的执行过程+说明

    tWbIII.png

    上述代码图示

    tWjmnO.png

    说明

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

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

    案例2

    编写一个程序,要求如下

    • 声明一个结构体Circle,字段为radius
    • 声明一个方法area和Circle绑定,可以返回面积

    tWvcRI.png

    上述代码图示

    tWvLQ0.png

    10.2.5 方法的声明/定义

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

    10.2.6 方法注意事项和细节讨论

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

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

    tWvcRI.png

    tWvLQ0.png

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

    987654345678.PNG

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

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

    987654345678.PNG

    10.2.7 方法练习题

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

    987654345678.PNG

    2、编写一个方法,提供m和n两个参数,方法中打印一个 m*n的矩形

    987654345678.PNG

    3、编写一个方法,算该矩形的面积(可以接收长len,和宽width),将其作为方法返回值,在main方法中调用该方法,接收返回的面积并打印

    987654345678.PNG

    4、编写一个方法,判断一个数是奇数还是偶数

    987654345678.PNG

    5、根据行、列、字符打印对应行数和列数的字符,比如:行:3、列:2、字符:*

    987654345678.PNG

    6、定义小小计算器结构体(Calcuator),实现加减乘除四个功能

    • 实现形式1:分四个方法完成,分别计算 + - * /
    package main 
    import (
    	"fmt"
    )
    type Calcuator struct {
    	Num1 float64
    	Num2 float64
    }
    
    func (calcuator *Calcuator) getSum() float64 {
    	return calcuator.Num1 + calcuator.Num2
    }
    
    func (calcuator *Calcuator) jianfa() float64 {
    	return calcuator.Num1 - calcuator.Num2
    }
    
    func (calcuator *Calcuator) chengfa() float64 {
    	return calcuator.Num1 * calcuator.Num2
    }
    
    func (calcuator *Calcuator) chufa() float64 {
    	return calcuator.Num1 / calcuator.Num2
    }
    
    func main() {
    	var calcuator Calcuator
    	calcuator.Num1 = 1.2
    	calcuator.Num2 = 2.2
    	fmt.Println("加法=", calcuator.getSum())
    	// 下一行的  fmt.Sprintf("%.2f")  是格式化处
    	fmt.Printf("减法= %v
    ", fmt.Sprintf("%.2f",calcuator.jianfa())) 
    	fmt.Println("乘法=", calcuator.chengfa())
    	fmt.Println("除法=", calcuator.chufa())
    }
    
    • 实现形式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 calcuator Calcuator
    	calcuator.Num1 = 1.2
    	calcuator.Num2 = 2.2
    	res := calcuator.getRes('+')
    	fmt.Println("res=", res)
    }
    

    10.3 方法和函数区别

    1、调用方式不一样

    ​ 函数的调用方式: 函数名(实参列表)

    ​ 方法的调用方式: 变量.方法名(实参列表)

    2、对于普通函数,接受者为值类型时,不能将指针类型的数据直接传递,反之亦然

    3、对于方法(如struct的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以

    10.4 面向对象编程实例

    10.4.1 步骤

    • 声明(定义)结构体,确定结构体名
    • 编写结构体的字段
    • 编写结构体的方法

    10.4.2 案例

    学生个人信息案例

    package main 
    import (
    	"fmt"
    )
    
    type Student struct {
    	name string
    	gender string
    	age int
    	id int
    	score float64
    }
    
    func (student *Student) say() string {
    	infoStr := fmt.Sprintf("Student的信息 name=[%v] gender=[%v]  age=[%v] id=[%v] score=[%v]",
    	student.name, student.gender, student.age, student.id, student.score)
    	return infoStr
    }
    
    func main() {
    	// 测试
    	// 创建一个Student实例变量
    	var stu = Student{
    		name : "tom",
    		gender : "male",
    		age : 18,
    		id : 10000,
    		score : 99.99,
    	}
    	fmt.Println(stu.say())
    }
    

    10.4.3 盒子案例

    • 编程创建一个Box结构体,在其中声明三个字段表示一个立方体的长、宽、高、这三者要从终端获取
    • 声明一个方法获取立方体的体积
    • 创建一个Box结构体变量,打印给定尺寸的立方体的体积

    tf6YS1.png

    10.4.4 景区门票案例

    • 一个景区根据游人的年龄收取不同价格的门票,比如年龄大于18,收费20元,其他情况门票免费
    • 请编写Visitor结构体,根据年龄段决定能购买的门票价格并输出
    package main 
    import (
    	"fmt"
    )
    // 创建一个 Visitor 结构体,定义了长宽高
    type Visitor struct {
    	Name string
    	Age int
    }
    
    func (visitor *Visitor) showprice() {
    	if visitor.Age > 90 || visitor.Age <= 8 {
    		fmt.Println("考虑到安全,请不要去玩耍")
    		return
    	}
    	if visitor.Age > 18 {
    		fmt.Printf("游客的名字为%v 年龄为%v  收费20元 
    ", visitor.Name, visitor.Age)
    	} else {
    		fmt.Printf("游客的名字为%v 年龄为%v  免费 
    ", visitor.Name, visitor.Age)
    	}
    }
    
    func main() {
    	// 测试
    	var v Visitor 
    	for {
    		fmt.Println("请输入你的名字:")
    		fmt.Scanln(&v.Name)
    		if v.Name == "n" {
    			fmt.Println("谢谢、退出程序")
    			break
    		}
    		fmt.Println("请输入你的年龄:")
    		fmt.Scanln(&v.Age)
    		v.showprice()
    	}
    }
    

    10.5 创建结构体变量时指定字段值

    ​ Golang在创建结构体实例(变量)时,可以直接指定字段的值。

    创建结构体变量时指定字段值方式

    方式1

    package main 
    import (
    	"fmt"
    )
    
    type Student struct {
    	Name string
    	Age int
    }
    
    func main() {
    	//方式1
    	// 在创建结构体变量时,就直接指定字段的值
    	var stu1 = Student{"小明", 19} // stu1 --> 结构体数据空间
    	stu2 := Student{"小明~", 22}
    
    	// 方式2
    	//  在创建结构体变量时,把字段名和字段值写在一起
    	// 这种写法,就不依赖字段的定义顺序
    	var stu3 = Student{
    		Name : "Jack",
    		Age : 9,
    	}
    	stu4 := Student {
    		Age : 30,
    		Name : "Marry",
    	}
    
    	fmt.Println(stu1, stu2, stu3, stu4)
    	fmt.Println()
    }
    

    方式2

    package main 
    import (
    	"fmt"
    )
    
    type Student struct {
    	Name string
    	Age int
    }
    
    func main() {
    	// 方式2 返回结构体的指针类型!!!!!!!!!!
    	var stu5 = &Student{"小王", 29} //stu5 --> 地址 --> 结构体数据
    	stu6 := &Student{"小王", 33}
    	// 在创建结构体指针变量时,把字段名和字段值写在一起
    	// 这种写法,就不依赖字段的定义顺序
    	var stu7 = &Student {
    		Name : "小李",
    		Age : 49,
    	}
    	stu8 := &Student {
    		Name : "小李~",
    		Age : 49,
    	}
    	fmt.Println(*stu5, *stu6, *stu7, *stu8)
    }
    

    10.6 工厂模式

    10.6.1 说明

    ​ Golang的结构体没有构造函数,通常可以使用工厂模式来解决这个问题。

    10.6.2 看一个需求

    一个结构体的声明是这样的:

    package model
    type Student struct {
        Name string....
    }
    

    ​ 因为这里的Student的首字母S是大写的,如果我们想在其它包创建Student的实例(比如:main包),引入model包后,就可以直接创建Student结构体的变量,但是问题来了,如果首字母是小写的,比如:type strdent struct {....} 就不行了,怎么办,------> 工厂模式解决

    student.go

    987654345678.PNG

    main.go

    987654345678.PNG

    如果model包的student 的结构体的字段 Score改成score,我们还能正常访问吗?又该如何解决这个问题?

    解决办法

    987654345678.PNG

    987654345678.PNG

  • 相关阅读:
    吴裕雄--天生自然--Go 语言学习笔记--Go 语言指针
    SVN报错“Failed to run the WC DB work queue associated with”解决办法
    【Oracle学习笔记】Oralce随机数与随机取样
    [CUDA]CUDA编程资源汇总
    [算法竞赛]目标检测常用技巧总结
    [CUDA]CUDA编程实战五——dot向量点积运算
    [CUDA]CUDA编程实战四——矩阵乘法
    [CUDA]CUDA编程实战三——矩阵加法的实现
    [CUDA]CUDA编程实战一——了解CUDA及获取GPU信息
    [CUDA]CUDA编程实战二——向量加法
  • 原文地址:https://www.cnblogs.com/jiaxiaozia/p/13061582.html
Copyright © 2011-2022 走看看