Golang基础-面向对象篇

文章目录

    • struct结构体
    • 类的表示与封装
    • 类的继承
    • 多态的基本要素与实现
    • interface空接口
    • 反射
      • 变量的内置pair
      • reflect包
      • 解析Struct Tag
      • Struct Tag在json中的应用

struct结构体

在Go语言中,可以使用type 关键字来创建自定义类型,这对于提高代码的可读性和可维护性非常有用。如type myint int,myint 是一个基于内置类型 int 创建的自定义类型。你可以在代码中使用 myint 类型,并对其进行操作,而它实际上是基础的 int 类型。

struct的定义如下:

package mainimport "fmt"// Book 定义一个结构体
type Book struct {title stringauth  string
}func main() {var book1 Bookbook1.title = "Golang"book1.auth = "zhangsan"fmt.Println(book1)
}
// {Golang zhangsan}

需要注意的是 Go 语言中函数参数默认是值传递的。如果将book1对象传入函数中时,仅仅只是传进去一个副本,在函数中对book1的任何修改都是无效的。如果想在函数中修改原始变量,可以传递指向结构体的指针。如以下代码所示。

package mainimport "fmt"// Book 定义一个结构体
type Book struct {title stringauth  string
}func changeBook(book *Book) {book.auth = "777"
}func main() {var book1 Bookbook1.title = "Golang"book1.auth = "zhangsan"changeBook(&book1)fmt.Println(book1)
}
// {Golang 777}

类的表示与封装

Go语言(通常称为Golang)不使用传统的类(class)和继承(inheritance)的概念,而是通过结构体(struct)来实现面向对象编程的特性。

go中没有public和private的关键字,如果类名大写,则其他包也能够访问;如果标识符以小写字母开头,则它是未导出的,只能在同一个包内访问。导出的标识符(大写字母开头)不仅仅限于结构体名字,还包括结构体中的字段名、函数名等。这种命名规范有助于代码的可维护性和封装性。

package mainimport "fmt"type Hero struct {Name  stringAd    intLevel int
}func (this Hero) Show() {fmt.Println("name =", this.Name)fmt.Println("Ad =", this.Ad)fmt.Println("Level =", this.Level)
}func (this Hero) GetName() string {return this.Name
}func (this Hero) SetName(newName string) {this.Name = newName
}func main() {hero := Hero{Name: "zhangsan", Ad: 100, Level: 1}hero.Show()hero.SetName("lisi")nowName := hero.GetName()fmt.Println("nowName =", nowName)
}
/*输出结果
name = zhangsan
Ad = 100
Level = 1
nowName = zhangsan
*/

可以看出,setName并没有把名字改为lisi。因为setName方法中使用的是值接收者(receiver),这意味着在该方法内部对Hero实例的修改不会影响到实际的hero变量。要使setName方法正确地修改Hero实例,需要将其改为指针接收者。如以下代码所示。

package mainimport "fmt"type Hero struct {Name  stringAd    intLevel int
}func (this *Hero) Show() {fmt.Println("name =", this.Name)fmt.Println("Ad =", this.Ad)fmt.Println("Level =", this.Level)
}func (this *Hero) GetName() string {return this.Name
}func (this *Hero) SetName(newName string) {this.Name = newName
}func main() {hero := Hero{Name: "zhangsan", Ad: 100, Level: 1}hero.Show()hero.SetName("lisi")nowName := hero.GetName()fmt.Println("nowName =", nowName)
}
/*输出结果
name = zhangsan
Ad = 100
Level = 1
nowName = lisi
*/

类的继承

现在有如下的一个Human类

package mainimport "fmt"type Human struct {name stringsex  string
}func (this *Human) Eat() {fmt.Println("Human.Eat()...")
}func (this *Human) Walk() {fmt.Println("Human.Walk()...")
}func main() {h := Human{name: "zhangsan", sex: "male"}h.Eat()h.Walk()
}

现在有一个SuperMan类需要继承Human类,还有自己的level字段,并重写其中的Eat方法,还要能够写子类的新方法。代码如下:

type SuperMan struct {Human // SuperMan继承了Human类的方法level int
}// 重写父类的Eat()方法
func (this *SuperMan) Eat() {fmt.Println("SuperMan.Eat()...")
}// 添加子类新方法
func (this *SuperMan) Fly() {fmt.Println("SuperMan.Fly()...")
}

在继承完成后,main函数中定义子类的对象有两种方法。
第一种:一气通贯式

s := SuperMan{Human{"lisi", "female"}, 5}

第二种:守旧派

var s SuperMan
s.name = "lisi"
s.sex = "female"
s.level = 5

完整代码如下,都能成功运行。

package mainimport "fmt"type Human struct {name stringsex  string
}func (this *Human) Eat() {fmt.Println("Human.Eat()...")
}func (this *Human) Walk() {fmt.Println("Human.Walk()...")
}// ===========================================type SuperMan struct {Human // SuperMan继承了Human类的方法level int
}// 重写父类的Eat()方法
func (this *SuperMan) Eat() {fmt.Println("SuperMan.Eat()...")
}// 添加子类新方法
func (this *SuperMan) Fly() {fmt.Println("SuperMan.Fly()...")
}func (this *SuperMan) Show() {fmt.Println("name =", this.name)fmt.Println("sex =", this.sex)fmt.Println("level =", this.level)
}func main() {h := Human{name: "zhangsan", sex: "male"}h.Eat()h.Walk()fmt.Println("=============")//方法一:定义子类新对象//s := SuperMan{Human{"lisi", "female"}, 5}//方法二var s SuperMans.name = "lisi"s.sex = "female"s.level = 5s.Walk() //父类方法s.Eat()s.Fly()s.Show()
}
/*运行结果
Human.Eat()...
Human.Walk()...
=============
Human.Walk()...
SuperMan.Eat()...
SuperMan.Fly()...
name = lisi
sex = female
level = 5
*/

多态的基本要素与实现

Go语言中用继承是无法实现多态的,需要用接口(interface)实现,它的本质是一个指针。
基本要素:

  • 有一个父类(有接口)
  • 有子类(实现了父类的全部接口方法)
  • 父类类型的变量(指针)指向(引用)子类的具体数据变量

以下代码展示了一个使用接口的例子,定义了一个 AnimalIF 接口和两个实现该接口的类型 Cat 和 Dog。然后,通过 showAnimal 函数展示了如何使用接口进行多态性调用。

package mainimport "fmt"type AnimalIF interface {Sleep()GetColor() stringGetType() string
}type Cat struct {color string
}func (this *Cat) Sleep() {fmt.Println("cat is sleeping")
}func (this *Cat) GetColor() string {return this.color
}func (this *Cat) GetType() string {return "cat"
}//=================type Dog struct {color string
}func (this *Dog) Sleep() {fmt.Println("dog is sleeping")
}func (this *Dog) GetColor() string {return this.color
}func (this *Dog) GetType() string {return "Dog"
}func showAnimal(animal AnimalIF) {animal.Sleep()fmt.Println("color =", animal.GetColor())fmt.Println("kind =", animal.GetType())
}func main() {//var animal AnimalIF //接口的数据类型,父类指针//animal = &Cat{"Green"}//animal.Sleep()////animal = &Dog{"yellow"}//animal.Sleep()cat := Cat{"green"}dog := Dog{"yellow"}showAnimal(&cat)showAnimal(&dog)
}

main 函数中,我们创建了 Cat 和 Dog 的实例,并通过showAnimal 函数调用展示它们的信息。这里利用了接口的多态性,通过相同的接口来处理不同的类型。这种设计使得代码更加灵活,可以方便地扩展和添加新的类型。

interface空接口

在Go语言中,空接口(empty interface)是一种特殊的接口,它不包含任何方法签名。由于不包含任何方法,空接口可以表示任意类型。在Go中,空接口的声明形式是interface{}
空接口的特点是它可以保存任意类型的值,因为任何类型都至少实现了零个方法,因此都满足空接口的要求。

package mainimport "fmt"func myFunc(arg interface{}) {fmt.Println("myFunc is called...")fmt.Println(arg)
}type Books struct {auth string
}func main() {book := Books{"zhangsan"}myFunc(book)myFunc(100)myFunc("haha")myFunc(3.14)
}
/*输出结果
myFunc is called...
{zhangsan}
myFunc is called...
100
myFunc is called...
haha
myFunc is called...
3.14
*/

在这个例子中,myFunc 函数接受一个空接口类型的参数,因此它可以接受任何类型的值。然后创建了一个 Books 结构体的实例 book,并将其作为参数传递给 myFunc 函数。

但是,interface{}如何区分此时引用的底层数据类型是什么?主要是通过断言机制来实现的。

func myFunc(arg interface{}) {fmt.Println("myFunc is called...")fmt.Println(arg)value, ok := arg.(string)if !ok {fmt.Println("arg is not string")} else {fmt.Println("value =", value)fmt.Println("arg is string")}
}

这种方式在运行时进行了类型检查,以确保转换的安全性。如果 arg 的实际类型不是 string,那么 ok 将为 false,并输出相应的提示信息。

反射

变量的内置pair

在Go中,如果定义了一个变量,那么它内部实际构造由两部分组成:变量类型type和值value。type可以划分成两类:static type和concrete type。

  • static type指常见的数据类型,如int、string…
  • concrete type指interface所指向的具体数据类型,是系统看得见的类型

变量类型type和值value组成了pair,反射主要是通过变量找到当前变量的具体类型或值。具体结构如下图所示。
请添加图片描述

package mainimport "fmt"func main() {var a string// pair<static_type:string,value:"aceld">a = "aceld"//pair<type:string,value:"aceld">var alltype interface{}alltype = astr, _ := alltype.(string)fmt.Println(str)
}

这段代码中,alltype.(string) 尝试将 alltype转为字符串类型,并返回两个值,一个是转换后的值 str,另一个是一个布尔值 ok 表示转换是否成功。在这里使用了 _ 来忽略不需要的第二个返回值。
如果转换成功,oktrue,则会打印出字符串的值。如果转换失败,okfalse,则会输出相应的提示信息。这种模式在处理接口类型时常用于确保类型安全。

reflect包

reflect主要包含了两个关键的接口,ValueOf实现输入任意数据类型返回数据的值,TypeOf实现输入任意数据类型,动态获取这个数据类型是static type还是concrete type。
请添加图片描述
可以用以下代码来判断:

package mainimport ("fmt""reflect"
)func reflectNum(arg interface{}) {fmt.Println("type:", reflect.TypeOf(arg))fmt.Println("value:", reflect.ValueOf(arg))
}func main() {var num float64 = 3.14159reflectNum(num)
}
/*输出结果
type: float64
value: 3.14159
*/

下面的代码演示了如何使用反射(reflection)获取结构体实例的字段和方法信息。

package mainimport ("fmt""reflect"
)type User struct {Id   intName stringAge  int
}func (this User) Call() {fmt.Println("User is called...")fmt.Printf("%v\n", this)
}func DoFiledAndMethod(input interface{}) {// 获取input的typeinputType := reflect.TypeOf(input)fmt.Println("inputType is:", inputType)// 获取input的valueinputValue := reflect.ValueOf(input)fmt.Println("inputValue is:", inputValue)// 通过type获取里面的字段// 1.获取interface的reflect.Type,通过Type得到NumField,进行遍历// 2.得到每个field,数据类型// 3.通过field有一个interface()方法得到对应valuefor i := 0; i < inputType.NumField(); i++ {field := inputType.Field(i)value := inputValue.Field(i).Interface()fmt.Printf("%s:%v=%v\n", field.Name, field.Type, value)}// 通过type获取里面的方法、调用for i := 0; i < inputType.NumMethod(); i++ {m := inputType.Method(i)fmt.Printf("%s:%v\n", m.Name, m.Type)}
}func main() {user := User{1, "Aceld", 18}DoFiledAndMethod(user)
}
/*输出结果
inputType is: main.User
inputValue is: {1 Aceld 18}
Id:int=1
Name:string=Aceld
Age:int=18
Call:func(main.User)
*/

解析Struct Tag

在 Go 语言中,可以为结构体的字段添加标签(tag),这些标签可以在运行时通过反射获取。标签通常用于提供额外的元数据,如字段的注释、验证规则等。

package mainimport ("fmt""reflect"
)type resume struct {Name string `info:"name" doc:"我的名字"`Sex  string `info:"sex"`
}func findTag(str interface{}) {t := reflect.TypeOf(str).Elem() //当前结构体的全部元素for i := 0; i < t.NumField(); i++ {taginfo := t.Field(i).Tag.Get("info")tagdoc := t.Field(i).Tag.Get("doc")fmt.Println("info:", taginfo)fmt.Println("doc:", tagdoc)}
}func main() {var re resumefindTag(&re)
}

在以上代码中,resume 结构体的字段 NameSex 都有标签信息。在 findTag 函数中,通过 reflect.TypeOf(str) 获取结构体的类型,并使用 Elem() 方法获取实际的类型。然后,通过 t.Field(i).Tag.Get("info")t.Field(i).Tag.Get("doc") 获取每个字段的 "info" "doc" 标签的值。

Struct Tag在json中的应用

定义如下代码

package mainimport ("encoding/json""fmt"
)type Movie struct {Title  string   `json:"title"`Year   int      `json:"year"`Price  int      `json:"rmb"`Actors []string `json:"actors"`
}func main() {movie := Movie{"喜剧之王", 2000, 10, []string{"xingye", "zhangbozi"}}
}

对movie编码,即从结构体->json。从运行结果可以看出json中的键就是上面定义的Struct Tag

jsonStr, err := json.Marshal(movie)
if err != nil {fmt.Println("error:", err)return
}
fmt.Printf("jsonStr = %s\n", jsonStr)
//jsonStr = {"title":"喜剧之王","year":2000,"rmb":10,"actors":["xingye","zhangbozi"]}

对jsonStr解码,即从json->结构体

my_movie := Movie{}
err = json.Unmarshal(jsonStr, &my_movie)
if err != nil {fmt.Println("error:", err)return
}
fmt.Printf("%v\n", my_movie)
// {喜剧之王 2000 10 [xingye zhangbozi]}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/200683.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

win11,引导项管理

1&#xff0c;打开cmd,输入msconfig 2,进入引导选项卡 3&#xff0c;删除不需要的引导项

ETL-使用kettle批量复制sqlserver数据到mysql数据库

文章标题 1、安装sqlserver数据库2、下载kettle3、业务分析4、详细流程&#xff08;1&#xff09;转换1&#xff1a;获取sqlserver所有表格名字&#xff0c;将记录复制到结果&#xff08;2&#xff09;转换2&#xff1a;从结果设置变量&#xff08;3&#xff09;转换3&#xff…

【Linux】:共享内存

共享内存 一.原理二.创建共享内存1.shmget2.写一个共享内存代码 三.进行通信1.各种接口2.各接口使用代码3.一次简单的通信四.共享内存的特点 一.原理 直接原理 共享内存顾名思义就是共同使用的一块空间。 很明显操作系统需要对这块内存进行管理&#xff0c;那么就避免不了先描…

Servlet执行流程Servlet 生命周期

Servlet 生命周期 对象的生命周期指一个对象从被创建到被销毁的整个过程 import javax.servlet.*; import javax.servlet.annotation.WebServlet; import java.io.IOException; WebServlet(urlPatterns "/demo",loadOnStartup 10) public class ServletDemo imple…

华为ac+fit漫游配置案例

Ap漫游配置: 其它配置上面一样,ap管理dhcp和业务dhcp全在汇聚交换机 R1: interface GigabitEthernet0/0/0 ip address 11.1.1.1 255.255.255.0 ip route-static 12.2.2.0 255.255.255.0 11.1.1.2 ip route-static 192.168.0.0 255.255.0.0 11.1.1.2 lsw1: vlan batch 100 200…

存储日志数据并满足安全要求

日志数据是包含有关网络中发生的事件的记录的重要信息&#xff0c;日志数据对于监控网络和了解网络活动、用户操作及其动机至关重要。 由于网络中的每个设备都会生成日志&#xff0c;因此收集的数据量巨大&#xff0c;管理和存储所有这些数据成为一项挑战&#xff0c;日志归档…

Windows系统如何安装与使用TortoiseSVN客户端,并实现在公网访问本地SVN服务器

文章目录 前言1. TortoiseSVN 客户端下载安装2. 创建检出文件夹3. 创建与提交文件4. 公网访问测试 前言 TortoiseSVN是一个开源的版本控制系统&#xff0c;它与Apache Subversion&#xff08;SVN&#xff09;集成在一起&#xff0c;提供了一个用户友好的界面&#xff0c;方便用…

2023年以就业为目的学习Java还有必要吗?

文章目录 1活力四射的 Java2从零开始学会 Java3talk is cheap, show me the code4结语写作末尾 现在学 Java 找工作还有优势吗&#xff1f; 在某乎上可以看到大家对此问题的热议&#xff1a;“2023年以就业为目的学习Java还有必要吗&#xff1f;” 。有人说市场饱和&#xff0c…

关于lenra你需要了解的

monorepo&#xff1a;项目代码管理方式&#xff0c;单个仓库中管理多个项目是一种设计思想 lenra&#xff1a;是一种工具&#xff0c;对于使用npm和git管理多软件包代码仓库的工作流程进行优化 使用这些工具的优点&#xff1a; 公共依赖只要安装一次&#xff0c;Monorepo 中…

C/C++内存管理(1):C/C++内存分布,C++内存管理方式

一、C/C内存分布 1.1 1.2 二、C内存管理方式 C可以通过操作符new和delete进行动态内存管理。 2.1 new和delete操作内置类型 int main() {int* p1 new int;// 注意区分p2和p3int* p2 new int(10);// 对*p2进行初始化 10int* p3 new int[10];// p3 指向一块40个字节的int类…

网络运维与网络安全 学习笔记2023.11.21

网络运维与网络安全 学习笔记 第二十二天 今日目标 端口隔离原理与配置、路由原理和配置、配置多路由器静态路由 配置默认路由、VLAN间通信之路由器 端口隔离原理与配置 端口隔离概述 实现报文之间的2层隔离&#xff0c;除了使用VLAN技术以后&#xff0c;还可以使用端口隔…

Linux socket编程(5):三次握手和四次挥手分析和SIGPIPE信号的处理

在我之前写的Wireshark抓包&#xff1a;理解TCP三次握手和四次挥手过程中&#xff0c;通过抓包分析了TCP传输的三次握手和四次挥手的过程。在这一节中&#xff0c;将分析在Linux中的三次握手和四次挥手的状态和过程&#xff0c;另外还有一个在我们编程过程中值得注意的SIGPIPE信…

gitBash中如何使用Linux中的tree命令

文章目录 在gitBash中安装tree的目的如何安装安装完成,就可以直接完美适配Linux系统了在gitBash中安装tree的目的 如下图,powershell虽然可以看做是window下的Linux系统,但是根本就不适配很多Linux中的命令 如何安装 tree.exe安装网址 下载 tree 命令的 二进制包,安装 tr…

OAK相机通过振动测试!

编辑&#xff1a;OAK中国 首发&#xff1a;oakchina.cn 喜欢的话&#xff0c;请多多&#x1f44d;⭐️✍ 内容可能会不定期更新&#xff0c;官网内容都是最新的&#xff0c;请查看首发地址链接。 Hello&#xff0c;大家好&#xff0c;这里是OAK中国&#xff0c;我是助手君。 当…

【python】Python生成GIF动图,多张图片转动态图,pillow

pip install pillow 示例代码&#xff1a; from PIL import Image, ImageSequence# 图片文件名列表 image_files [car.png, detected_map.png, base64_image_out.png]# 打开图片 images [Image.open(filename) for filename in image_files]# 设置输出 GIF 文件名 output_g…

java算法学习索引之数组矩阵问题

一 将正方形矩阵顺时针转动90 给定一个NN的矩阵matrix&#xff0c;把这个矩阵调整成顺时针转动90后的形式。 顺时针转动90后为&#xff1a; 【要求】额外空间复杂度为O&#xff08;1&#xff09;。 public void rotate(int[][] matrix) {int tR 0; // 左上角行坐标int tC 0;…

香港科技大学广州|机器人与自主系统学域博士招生宣讲会—同济大学专场!!!(暨全额奖学金政策)

在机器人和自主系统领域实现全球卓越—机器人与自主系统学域 硬核科研实验室&#xff0c;浓厚创新产学研氛围&#xff01; 教授亲临现场&#xff0c;面对面答疑解惑助攻申请&#xff01; 一经录取&#xff0c;享全额奖学金1.5万/月&#xff01; &#x1f559;时间&#xff1a;…

STM32 Flash

FLASH简介 Flash是常用的用于存储数据的半导体器件&#xff0c;它具有容量大&#xff0c;可重复擦写&#xff0c;按“扇区/块”擦除、掉电后数据可继续保存的特性。 常见的FLASH主要有NOR FLASH和NAND FLASH两种类型。NOR和NAND是两种数字门电路&#xff0c;可以简单地认为FL…

多线程的概念

点击链接返回标题-> 什么是进程&#xff1f; 进程&#xff08;Process&#xff09;&#xff0c;是程序的基本执行实体。 在早期面向进程设计的计算机结构中&#xff0c;进程是程序的基本执行实体&#xff1b; 在当代面向线程设计的计算机结构中&#xff0c;进程是线程的容器…

【阿里云】图像识别 摄像模块 语音模块

USB 摄像头模块测试及配置 一、首先将 USB 摄像头插入到 Orange Pi 开发板的 USB 接口中二、然后通过 lsmod 命令可以看到内核自动加载了下面的模块三、通过 v4l2-ctl 命令可以看到 USB 摄像头的设备节点信息为 /dev/video0四、使用 fswebcam 测试 USB 摄像头五、使用 motion …