GO-接口
1. 接口
在Go语言中接口(interface)是一种类型,一种抽象的类型。
interface是一组method的集合,接口做的事情就像是定义一个协议(规则),只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法)。
接口(interface)是一种类型
接口类型是对其它类型行为的抽象和概括;因为接口类型不会和特定的实现细节绑定在一起,通过这种抽象的方式我们可以让我们的函数更加灵活和更具有适应能力。
接口是双方约定的一种合作协议。接口实现者不需要关心接口会被怎样使用,调用者也不需要关心接口的实现细节。接口是一种类型,也是一种抽象结构,不会暴露所含数据的格式、类型及结构。
1.2 接口定义
Go语言提倡面向接口编程。
每个接口类型由数个方法组成。接口的形式代码如下:
type 接口类型名 interface{方法名1( 参数列表1 ) 返回值列表1方法名2( 参数列表2 ) 返回值列表2…
}
对各个部分的说明:
- 接口类型名:使用 type 将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加 er,如有写操作的接口叫 Writer,有字符串功能的接口叫 Stringer,有关闭功能的接口叫 Closer 等。
- 方法名:当方法名首字母是大写时,且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
- 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以被忽略
type Writer interface {//接口名和方法首字母大写,意味着可以被其他包访问Write([]byte)string
}
1.3 接口实现条件
如果一个任意类型 T 的方法集为一个接口类型的方法集的超集,则我们说类型 T 实现了此接口类型。
T 可以是一个非接口类型,也可以是一个接口类型。
实现关系在Go语言中是隐式的。两个类型之间的实现关系不需要在代码中显式地表示出来。Go语言中没有类似于 implements 的关键字。 Go编译器将自动在需要的时候检查两个类型之间的实现关系。
接口定义后,需要实现接口,调用方才能正确编译通过并使用接口。
接口的实现需要遵循两条规则才能让接口可用:
- 接口的方法与实现接口的类型方法格式一致在类型中添加与接口签名一致的方法就可以实现该方法。签名包括方法中的名称、参数列表、返回参数列表。也就是说,只要实现接口类型中的方法的名称、参数列表、返回参数列表中的任意一项与接口要实现的方法不一致,那么接口的这个方法就不会被实现。
// 定义一个数据写入器
type DataWriter interface {Write(interface{}) error
}// 定义文件结构,用于实现DataWriter
type file struct {
}// 实现DataWriter接口的WriteData方法
func (f *file) Write(b interface{}) error {return fmt.Sprintf("writer:", b)
}func main() {// 实例化filef := new(file)// 声明一个DataWriter的接口var write DataWriter// 将接口赋值f,也就是*file类型write = f// 使用DataWriter接口进行数据写入write.Write("hhhhhhhh")
}
- 当类型无法实现接口时,编译器会报错:
-
- 函数名不一致导致的报错
- 实现接口的方法签名不一致导致的报错
- 接口中所有方法均被实现当一个接口中有多个方法时,只有这些方法都被实现了,接口才能被正确编译并使用。
// 定义一个数据写入器
type DataWriter interface {Write(interface{}) error//上述代码中新增一个方法Content() bool
}
运行结果
.\main.go:28:10: cannot use f (variable of type *file) as DataWriter value in assignment: *file does not implement DataWriter (missing method Content)
Go语言的接口实现是隐式的,无须让实现接口的类型写出实现了哪些接口。
这个设计被称为非侵入式设计。
1.4 类型与接口的关系
在Go语言中类型和接口之间有一对多和多对一的关系
一个类型可以实现多个接口
一个类型可以同时实现多个接口,而接口间彼此独立,不知道对方的实现。
例如,狗可以叫,也可以动。
我们就分别定义Sayer接口和Mover接口,如下:
type Sayer interface {Say()
}type Mover interface {Move()
}type Dog struct {name string
}// dog实现say和move接口
func (d Dog) Say() {fmt.Println(d.name, " saying......")
}func (d Dog) Move() {fmt.Println(d.name, "moving ......")
}func main() {var x Sayervar y Movervar dog = Dog{"wangwang"}x = dogy = dogx.Say() //wangwang saying......y.Move() //wangwang moving ......}
多个类型实现同一接口
type Mover interface {Move()
}type Dog struct {name string
}type Car struct {name string
}//dog 和 car都实现mover接口func (d Dog) Move() {fmt.Println(d.name, "moving,....")
}func (c Car) Move() {fmt.Println(c.name, "moving .....")
}func main() {var d = Dog{"旺财"}var c = Car{"小米"}var move Movermove = dmove.Move() //旺财 moving,....move = cmove.Move() //小米 moving .....}
接口嵌套
接口与接口间可以通过嵌套创造出新的接口
// Sayer 接口
type Sayer interface {say()
}// Mover 接口
type Mover interface {move()
}// 接口嵌套
type animal interface {SayerMover
}
嵌套得到的接口的使用与普通接口一样,这里我们让cat实现animal接口:
type cat struct {name string
}func (c cat) say() {fmt.Println("喵喵喵")
}func (c cat) move() {fmt.Println("猫会动")
}func main() {var x animalx = cat{name: "花花"}x.move()x.say()
}
1.5 空接口
空接口是指没有定义任何方法的接口。
因此任何类型都实现了空接口。
空接口类型的变量可以存储任意类型的变量。
func main() {var x interface{}var i = 100x = ifmt.Println(x) //100var name = "hhhhh"x = namefmt.Println(x) //hhhhh
}
1.5.1 空接口的应用
空接口作为函数的参数
使用空接口实现可以接收任意类型的函数参数。
func show(a interface{}) {fmt.Println(a)
}func main() {//空接口作为函数参数show("空接口传参") //空接口传参}
空接口作为map的值
使用空接口实现可以保存任意值的字典。
func main() {var student = make(map[string]interface{}, 3)student["小明"] = 100student["小红"] = "hahah"student["小高"] = falsefmt.Printf("%+v", student) //map[小明:100 小红:hahah 小高:false]}
1.5.2 类型断言
空接口可以存储任意类型的值,那我们如何获取其存储的具体数据呢?
接口值
一个接口的值(简称接口值)是由一个具体类型和具体类型的值两部分组成的。
这两部分分别称为接口的动态类型和动态值。
想要判断空接口中的值这个时候就可以使用类型断言,其语法格式:
x.(T)
1
其中:
- x:表示类型为interface{}的变量
- T:表示断言x可能是的类型。
该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败。
func main() {var student = make(map[string]interface{}, 3)student["小明"] = 100student["小红"] = "hahah"student["小高"] = falsefmt.Printf("%+v\n", student) //map[小明:100 小红:hahah 小高:false]_, ok := student["小明"].(bool)if ok != true {fmt.Println("student[\"小明\"]不是bool") }
}
2. I/O操作
I/O操作也叫输入输出操作。其中I是指Input,O是指Output,用于读或者写数据的,有些语言中也叫流操作,是指数据通信的通道。
Golang 标准库对 IO 的抽象非常精巧,各个组件可以随意组合,可以作为接口设计的典范。
io包中提供I/O原始操作的一系列接口。
它主要包装了一些已有的实现,如 os 包中的那些,并将这些抽象成为实用性的功能和一些其他相关的接口。
由于这些接口和原始的操作以不同的实现包装了低级操作,客户不应假定它们对于并行执行是安全的。
io库比较常用的接口有三个,分别是Reader,Writer和Closer。
2.1 Reader
Reader接口的定义,Read()方法用于读取数据。
type Reader interface {Read(p []byte) (n int, err error)
}
io.Reader 表示一个读取器,它将数据从某个资源读取到传输缓冲区。在缓冲区中,数据可以被流式传输和使用。
- 对于要用作读取器的类型,它必须实现 io.Reader 接口的唯一一个方法 Read(p []byte)。
- 换句话说,只要实现了 Read(p []byte) ,那它就是一个读取器。
- Read() 方法有两个返回值,一个是读取到的字节数,一个是发生错误时的错误。
通过 string.NewReader(string) 创建一个字符串读取器,然后流式地按字节读取:
func main() {reader := strings.NewReader("this is a reader")// 每次读取4个字节p := make([]byte, 4)for {n, err := reader.Read(p)if err != nil {if err == io.EOF {log.Println("读完了")break}log.Fatalln("read error", err)os.Exit(2)}log.Println("读取到的字节数:", n)}}
- 最后一次返回的 n 值有可能小于缓冲区大小。
- io.EOF 来表示输入流已经读取到头
2.1.1 文件操作相关API
func Create(name string) (file *File, err Error)
1
-
- 根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666
func NewFile(fd uintptr, name string) *File
1
-
- 根据文件描述符创建相应的文件,返回一个文件对象
func Open(name string) (file *File, err Error)
1
-
- 只读方式打开一个名称为name的文件
func OpenFile(name string, flag int, perm uint32) (file *File, err Error)
1
-
- 打开名称为name的文件,flag是打开的方式,只读、读写等,perm是权限
func (file *File) Write(b []byte) (n int, err Error)
1
-
- 写入byte类型的信息到文件
func (file *File) WriteAt(b []byte, off int64) (n int, err Error)
1
-
- 在指定位置开始写入byte类型的信息
func (file *File) WriteString(s string) (ret int, err Error)
1
-
- 写入string信息到文件
func (file *File) Read(b []byte) (n int, err Error)
1
-
- 读取数据到b中
func (file *File) ReadAt(b []byte, off int64) (n int, err Error)
1
-
- 从off开始读取数据到b中
func Remove(name string) Error
1
-
- 删除文件名为name的文件
2.1.2 读文件
type Closer interface {Close() error
}
os.Open()函数能够打开一个文件,返回一个*File和一个err。对得到的文件实例调用Close()方法能够关闭文件。
文件读取可以用file.Read(),读到文件末尾会返回io.EOF的错误
func main() {// 打开文件file, err := os.Open("C:\\Users\\Administrator\\Desktop\\新建 文本文档 (2).txt")if err != nil {log.Println("打开失败")}defer file.Close()// 定义接收文件读取的字节数组buff := make([]byte, 128)var content []bytefor {_, err := file.Read(buff)if err == io.EOF {log.Println("读完了")break}if err != nil {log.Println("读取失败:", err)return}}content = append(content, buff...)fmt.Sprintln(content)
}
Writer
type Writer interface {//Write() 方法有两个返回值,一个是写入到目标资源的字节数,一个是发生错误时的错误。Write(p []byte) (n int, err error)
}
- io.Writer 表示一个写入器,它从缓冲区读取数据,并将数据写入目标资源。
- 对于要用作编写器的类型,必须实现 io.Writer 接口的唯一一个方法 Write(p []byte)
- 同样,只要实现了 Write(p []byte) ,那它就是一个编写器。
func main() {// 打开文件file, err := os.Open("C:\\Users\\Administrator\\Desktop\\新建 文本文档 (2).txt")if err != nil {log.Println("打开失败")}defer file.Close()// 定义接收文件读取的字节数组buff := make([]byte, 128)var content []bytefor {_, err := file.Read(buff[:])if err == io.EOF {log.Println("读完了")break}if err != nil {log.Println("读取失败:", err)return}}content = append(content, buff...)fmt.Println(string(content))
}
2.2 Writer
type Writer interface {//Write() 方法有两个返回值,一个是写入到目标资源的字节数,一个是发生错误时的错误。Write(p []byte) (n int, err error)
}
- io.Writer 表示一个写入器,它从缓冲区读取数据,并将数据写入目标资源。
- 对于要用作编写器的类型,必须实现 io.Writer 接口的唯一一个方法 Write(p []byte)
- 同样,只要实现了 Write(p []byte) ,那它就是一个编写器。
写文件:
func main() {file, err := os.Create("test.txt")if err != nil {log.Println("create error")}defer file.Close()b := make([]byte, 0)for i := 0; i < 10; i++ {b = append(b, byte(i))}file.WriteString(string(b))
}
2.3 bufio
- bufio包实现了带缓冲区的读写,是对文件读写的封装
- bufio缓冲写数据
模式 | 含义 |
os.O_WRONLY | 只写 |
os.O_CREATE | 创建文件 |
os.O_RDONLY | 只读 |
os.O_RDWR | 读写 |
os.O_TRUNC | 清空 |
os.O_APPEND | 追加 |
bufio读写数据
func wr() {// 参数2:打开模式,所有模式d都在上面// 参数3是权限控制// w写 r读 x执行 w 2 r 4 x 1//特殊权限位,拥有者位,同组用户位,其余用户位file, err := os.OpenFile("./xxx.txt", os.O_CREATE|os.O_WRONLY, 0666)if err != nil {return}defer file.Close()// 获取writer对象writer := bufio.NewWriter(file)for i := 0; i < 10; i++ {writer.WriteString("hello\n")}// 刷新缓冲区,强制写出writer.Flush()
}func re() {file, err := os.Open("./xxx.txt")if err != nil {return}defer file.Close()reader := bufio.NewReader(file)for {line, _, err := reader.ReadLine()if err == io.EOF {break}if err != nil {return}fmt.Println(string(line))}}func main() {re()
}
2.5 实现一个cat命令
使用文件操作相关知识,模拟实现linux平台cat命令的功能。
相关文章:

GO-接口
1. 接口 在Go语言中接口(interface)是一种类型,一种抽象的类型。 interface是一组method的集合,接口做的事情就像是定义一个协议(规则),只要一台机器有洗衣服和甩干的功能,我就称它…...

【C语言】动态内存管理常用函数
前言 我们在之前学习的数组开辟的空间是固定不变的,有时候我们需要的空间⼤⼩在程序运⾏的时候才能知道~ c语言中的动态内存开辟,让程序员⾃⼰可以根据实际需求申请和释放相应空间,这使得空间的开辟变得灵活了许多。 欢迎关注个人主页&#x…...

【OpenGL】(1) 专栏介绍:OpenGL 库 | 3D 计算机图形应用 | GPGPU 计算 | 3D 建模和 3D动画 | 渲染技术介绍
🔗 《C语言趣味教程》👈 猛戳订阅!!! 💭 写在前面:本专栏主要内容是关于 3D 计算机图形技术的学习,重点是学习与此技术相关的 3D 实时渲染 (3D real-time rendering) 技术。我们会以…...
SPI总线知识总结
1 SPI的时钟极性CPOL和时钟相位CPHA的设置 1.1 SPI数据传输位数 SPI传输数据过程中总是先发送或接收高字节数据,每个时钟周期接收器或发送器左移一位数据。对于小于16位的数据,在发送前必须左对齐,如果接收的数据小于16位,则采用软…...
【异常关闭clas*h,导致无法访问任何网页_解决办法】
各位经常使用Clash Scientific浏览的朋友们,我要建议大家不要在开启Clash代理的情况下直接关机或者重启电脑,这样的操作会导致网络配置出现严重问题,带来不必要的麻烦。 这是我亲身体验的一次痛苦教训。不管是我在关闭or开启Clas*h代理后&am…...

STL-map和set
目录 一、关联式容器 二、键值对 三、树形结构的关联式容器 3.1 set 3.1.1 set介绍 3.1.2 set的使用 1. set的模板参数列表 2. set的构造 3. set的迭代器(类型是双向迭代器) 4. set的容量 5.set修改操作 6、operator 7. set的使用举例 3.2 map 3.2.1map介绍 3.2.2map的…...
[蓝桥杯 2020 省 B1] 整除序列
[蓝桥杯 2020 省 B1] 整除序列 题目描述 有一个序列,序列的第一个数是 n n n,后面的每个数是前一个数整除 2 2 2,请输出这个序列中值为正数的项。 输入格式 输入一行包含一个整数 n n n。 输出格式 输出一行,包含多个整数…...

【Android】View 的滑动
View 的滑动是 Android 实现自定义控件的基础,同时在开发中我们也难免会遇到 View 的滑动处理。其实不管是哪种滑动方式,其基本思想都是类似的:当点击事件传到 View 时,系统记下触摸点的坐标,手指移动时系统记下移动后…...

基于ZYNQ的PCIE高速数据采集卡的设计(一)
作为信息处理的第一步,数据采集的作用越来越重要。目前,数据采集已经在航 空、民用、军事、医疗等领域得到广泛应用。随着相关技术的不断发展,信号频率越 来高,带宽越来越大,使得数据采集技术逐渐向高速大数据的方向…...
渗透测试工具 nmap 详解
官网:Nmap: the Network Mapper - Free Security Scanner -p<端口范围>:仅扫描指定的端口 用于扫描指定端口是否开放,在 -p 后输入指定的端口,以英文","进行拼接多个指定端口。 nmap -p 80&…...

Ubuntu下安装Scala
前言 弄了一下终于成功装上了,这里对此进行一下总结 安装虚拟机 VMware虚拟机安装Ubuntu(超详细图文教程)_vmware安装ubuntu-CSDN博客https://blog.csdn.net/qq_43374681/article/details/129248167Download Ubuntu Desktop | Download | …...

无法启动报,To install it, you can run: npm install --save @/components/iFrame/index
运行的过程中后台报错 npm install --save /components/iFrame/index,以为是安装三方依赖错误,经过多次重装node_modules依然没有用。 没办法,只能在项目中搜索 components/iFrame/index这个文件。。突然醒悟。。。 有时候,犯迷…...

深入理解现代JavaScript:从语言特性到应用实践
💂 个人网站:【 海拥】【神级代码资源网站】【办公神器】🤟 基于Web端打造的:👉轻量化工具创作平台💅 想寻找共同学习交流的小伙伴,请点击【全栈技术交流群】 JavaScript作为一门动态、解释性脚本语言&…...

ThreadPoolExecutor 学习
ThreadPoolExecutor 是开发中最常用的线程池,今天来简单学习一下它的用法以及内部构造。 1、线程池存在的意义? 一般在jvm上,用户线程和操作系统内核线程是1:1的关系,也就是说,每次创建、销毁线程的时候&am…...
深入理解计算机操作系统书籍阅读感悟(一)
1.sp:表示为空格,ASCII为32 2.在我们写的每行程序结尾都有一个隐藏的\n(ASCII码值为10) 3.在书上的P2页上说:文本文件是指以ASCII码字符构成的文件,其余都是二进制文件 除了这种理解,更常见的…...

使用query请求数据出现500的报错
我在写项目的时候遇到了一个问题,就是在存商品id的时候我将它使用了JSON.stringify的格式转换了!!!于是便爆出了500这个错误!!! 我将JSON.stringify的格式去除之后,它就正常显示了&…...
PostgreSQL教程(二十一):服务器管理(三)之服务器设置和操作
本章讨论如何设置和运行数据库服务器,以及它与操作系统的交互。 一、PostgreSQL用户账户 和对外部世界可访问的任何服务器守护进程一样,我们也建议在一个独立的用户账户下运行PostgreSQL。这个用户账户应该只拥有被该服务器管理的数据,并且…...
Linux运维_Bash脚本_编译安装GNU-Tools
Linux运维_Bash脚本_编译安装GNU-Tools Bash (Bourne Again Shell) 是一个解释器,负责处理 Unix 系统命令行上的命令。它是由 Brian Fox 编写的免费软件,并于 1989 年发布的免费软件,作为 Sh (Bourne Shell) 的替代品。 您可以在 Linux 和 …...
leetcode 121.买卖股票的最佳时机
声明:以下仅代表个人想法,非官方答案或最优题解! 题目: 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的…...

javaWebssh酒店客房管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计
一、源码特点 java ssh酒店客房管理系统是一套完善的web设计系统(系统采用ssh框架进行设计开发),对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。开发环境为TOMCAT7.0…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...