Golang基础-面向对象篇

news2024/11/15 19:36:09

文章目录

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

struct结构体

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

struct的定义如下:

package main

import "fmt"

// Book 定义一个结构体
type Book struct {
	title string
	auth  string
}

func main() {
	var book1 Book
	book1.title = "Golang"
	book1.auth = "zhangsan"
	fmt.Println(book1)
}
// {Golang zhangsan}

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

package main

import "fmt"

// Book 定义一个结构体
type Book struct {
	title string
	auth  string
}

func changeBook(book *Book) {
	book.auth = "777"
}

func main() {
	var book1 Book
	book1.title = "Golang"
	book1.auth = "zhangsan"
	changeBook(&book1)
	fmt.Println(book1)
}
// {Golang 777}

类的表示与封装

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

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

package main

import "fmt"

type Hero struct {
	Name  string
	Ad    int
	Level 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 main

import "fmt"

type Hero struct {
	Name  string
	Ad    int
	Level 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 main

import "fmt"

type Human struct {
	name string
	sex  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 main

import "fmt"

type Human struct {
	name string
	sex  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 SuperMan
	s.name = "lisi"
	s.sex = "female"
	s.level = 5
	s.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 main

import "fmt"

type AnimalIF interface {
	Sleep()
	GetColor() string
	GetType() 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 main

import "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 main

import "fmt"

func main() {
	var a string
	// pair<static_type:string,value:"aceld">
	a = "aceld"

	//pair<type:string,value:"aceld">
	var alltype interface{}
	alltype = a

	str, _ := alltype.(string)
	fmt.Println(str)
}

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

reflect包

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

package main

import (
	"fmt"
	"reflect"
)

func reflectNum(arg interface{}) {
	fmt.Println("type:", reflect.TypeOf(arg))
	fmt.Println("value:", reflect.ValueOf(arg))
}

func main() {
	var num float64 = 3.14159
	reflectNum(num)
}
/*输出结果
type: float64
value: 3.14159
*/

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

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Id   int
	Name string
	Age  int
}

func (this User) Call() {
	fmt.Println("User is called...")
	fmt.Printf("%v\n", this)
}

func DoFiledAndMethod(input interface{}) {
	// 获取input的type
	inputType := reflect.TypeOf(input)
	fmt.Println("inputType is:", inputType)

	// 获取input的value
	inputValue := reflect.ValueOf(input)
	fmt.Println("inputValue is:", inputValue)
	// 通过type获取里面的字段
	// 1.获取interface的reflect.Type,通过Type得到NumField,进行遍历
	// 2.得到每个field,数据类型
	// 3.通过field有一个interface()方法得到对应value
	for 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 main

import (
	"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 resume
	findTag(&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 main

import (
	"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.coloradmin.cn/o/1234650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安防监控视频云存储平台EasyCVR页面播放卡顿的优化方法

视频监控平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;TSINGSEE青犀视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多路视频流&#xff0c;也能支持视…

Milvus 2.3.功能全面升级,核心组件再升级,超低延迟、高准确度、MMap一触开启数据处理量翻倍、支持GPU使用!

Milvus 2.3.功能全面升级&#xff0c;核心组件再升级&#xff0c;超低延迟、高准确度、MMap一触开启数据处理量翻倍、支持GPU使用&#xff01; 1.Milvus 2.3版本全部升级简介 Milvus 2.3.0 不仅包含大量的社区呼声很高的新功能&#xff0c;还带来了诸如 GPU 支持、Query 架构…

使用Python处理ADC激光测距数据并绘制为图片(二)

使用Python处理ADC激光测距数据并绘制为图片 说明一、定义全局变量变二、保存和清空原始数据三、拆分原始数据为键值对四、获取标题、FigText、更新统计信息文件五、生成图片六、处理原始数据文件七、主函数入口八、测试结果 说明 1. 主要是将ADC激光测距叠加后的1024Byte数据绘…

vue-admin-template改变接口地址

修改登录接口 1.f12查看请求接口 模仿返回数据写接口 修改方式1 1.在env.devolopment修改 修改方式2 vue.config.js 改成本地接口地址 配置转发 后端创建相应接口&#xff0c;使用map返回相同的数据 修改前端请求路径 修改前端返回状态码 utils里面的request.js

Joern安装与使用

环境准备 Joern需要在Linux环境中运行&#xff0c;所以在Windows系统中需要借助WSL或虚拟机安装。 JDK安装 Joern的运行需要JAVA环境的支持&#xff0c;本次采用的是JDK17&#xff0c;其他版本建议看一下Joern官方文档。 apt install openjdk-17-jre-headless 配置JAVA环境变…

集成仿真软件 PLEXOS 9.0 授权永久完美

PLEXOS是由能源示例发布的能源市场集成仿真软件。能源市场是一个充满活力和活力的市场&#xff0c;在这个大市场中有各种各样的数据和信息模型。观看每日市场发展和大量数据可能会使您感到困惑&#xff0c;并难以进行未来的投资和分析。使用集成和全面的方法是应对这一挑战的唯…

【深度学习实验】注意力机制(四):点积注意力与缩放点积注意力之比较

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 理论介绍a. 认知神经学中的注意力b. 注意力机制 1. 注意力权重矩阵可视化&#xff08;矩阵热图&#xff09;2. 掩码Softmax 操作3. 打分函数——加性注意力模型3. 打分函数——点积注意力与缩放…

ky10 server aarch64 离线安装openssl3.1.4

离线程序 https://gitcode.net/zengliguang/ky10_aarch64_openssl_install.git 输入下面命令执行离线安装脚本 source openssl_offline_install.sh 安装完成

仪表盘:pyecharts绘制

一、仪表盘 在数据分析中&#xff0c;仪表盘图&#xff08;dashboard&#xff09;的作用是以一种简洁、图表化的方式呈现数据的关键指标和核心信息&#xff0c;以帮助用户快速理解数据的情况&#xff0c;并从中提取关键见解。 仪表盘图通常由多个图表、指标和指示器组成&…

南京--ChatGPT/GPT4 科研实践应用

2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车…

杨氏矩阵解法

每日一言 「 人生如逆旅&#xff0c;我亦是行人。 」--临江仙送钱穆父-苏轼题目 杨氏矩阵 有一个数字矩阵&#xff0c;矩阵的每行从左到右是递增的&#xff0c;矩阵从上到下是递增的&#xff0c;请编写程序在这样的矩阵中查找某个数字是否存在。 解法思路 法一&#xff1a;…

Vue3 provide 和 inject 实现祖组件和后代组件通信

provide 和 inject 能够实现祖组件和其任意的后代组件之间通信&#xff1a; 一、provide 提供数据 我们在祖组件中使用provide 将数据提供出去。 使用provide 之前需要先进行引入&#xff1a; import { provide } from "vue"; 语法格式如下&#xff1a; provide(&q…

米哈游大数据云原生实践

云布道师 近年来&#xff0c;容器、微服务、Kubernetes 等各项云原生技术的日渐成熟&#xff0c;越来越多的公司开始选择拥抱云原生&#xff0c;并将企业应用部署运行在云原生之上。随着米哈游业务的高速发展&#xff0c;大数据离线数据存储量和计算任务量增长迅速&#xff0c…

SqlServer_idea连接问题

问题描述&#xff1a; sqlServer安装之后可以使用navicat进行连接idea使用账户密码进行登录连接失败 问题解决&#xff1a; 先使用sqlServer管理工具进行登录 使用window认证连接修改账户密码 启用该登录名 这时idea还是无法连接&#xff0c;还需要如下配置 打开sqlserve…

论文阅读:“基于特征检测与深度特征描述的点云粗对齐算法”

文章目录 摘要简介相关工作粗对齐传统的粗对齐算法基于深度学习的粗对齐算法 特征检测及描述符构建 本文算法ISS 特征检测RANSAC 算法3DMatch 算法 实验结果参考文献 摘要 点云对齐是点云数据处理的重要步骤之一&#xff0c;粗对齐则是其中的难点。近年来&#xff0c;基于深度…

java项目之社区互助平台(ssm+vue)

项目简介 社区互助平台实现了以下功能&#xff1a; 1、一般用户的功能及权限 所谓一般用户就是指还没有注册的过客,他们可以浏览主页面上的信息。但如果有中意的社区互助信息时&#xff0c;要登录注册&#xff0c;只有注册成功才有的权限。2、管理员的功能及权限 用户信息的添…

第十一章 目标检测中的NMS

精度提升 众所周知&#xff0c;非极大值抑制NMS是目标检测常用的后处理算法&#xff0c;用于剔除冗余检测框&#xff0c;本文将对可以提升精度的各种NMS方法及其变体进行阶段性总结。 总体概要&#xff1a; 对NMS进行分类&#xff0c;大致可分为以下六种&#xff0c;这里是依…

为什么 Django 后台管理系统那么“丑”?

哈喽大家好&#xff0c;我是咸鱼 相信使用过 Django 的小伙伴都知道 Django 有一个默认的后台管理系统——Django Admin 它的 UI 很多年都没有发生过变化&#xff0c;现在看来显得有些“过时且简陋” 那为什么 Django 的维护者却不去优化一下呢&#xff1f;原文作者去询问了多…

股票指标信息(六)

6-指标信息 文章目录 6-指标信息一. 展示股票的K线图数据,用于数据统计二. 展示股票指标数据,使用Java处理,集合形式展示三. 展示股票目前的最新的指标数据信息四. 展示股票指标数据,某一个属性使用Java处理五. 展示股票的指标数据,用于 Echarts 页面数据统计六. 展示股票指标数…

做好性能测试计划的4个步骤!全都是精华!【建议收藏】

如何做好一次性能测试计划呢&#xff1f;对于性能测试新手来说&#xff0c;也许你非常熟悉Jmeter的使用&#xff0c;也许你清楚的了解每一个系统参数代表的意义&#xff0c;但是想要完成好一次性能测试任务&#xff0c;并不仅仅是简单的写脚本&#xff0c;加压力&#xff0c;再…