Go语言学习之路

Posted 果子哥丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言学习之路相关的知识,希望对你有一定的参考价值。

Go语言学习之路(二)

面对对象编程思想

抽象

  • 抽象:定义一个结构体时候,实际上就是把一类事物的公有的属性(字段)或行为(方法)提取出来,形成一个物理模型(模板)。这种研究问题的方法称为抽象。

    加 * 的意思是定义为指针类型,后面好对内存中真实的数据进行实际修改

go的交互 数据输入方式
https://blog.csdn.net/weixin_45794641/article/details/124645893
第一种:这个可以

var cmdString string
fmt.Scanf("%s", &cmdString)
fmt.Println(cmdString)

第二种:字节类型 这个不能用数字来

reader := bufio.NewReader(os.Stdin)
cmdString, err := reader.ReadString('\\n')
cmdString = strings.TrimSuffix(cmdString, "\\n")
// cmd := exec.Command(commandString)
// cmd.Stderr = os.Stderr
// cmd.Run()

封装

封装就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作(方法),才能对字段进行操作。

封装的理解和好处
(1)隐藏实现细节
(2)可以对数据进行验证,保证安全合理

如何体现封装
(1)对结构体中的属性进行封装
(2)通过方法,包 实现封装

小写的变量作为struct则只能本包实现

封装的实现步骤
1)将 结构体、字段(属性)的首字母小写 (不能导出,其它包不能使用,类似private)
2)给结构体所在包提供一个工厂模式的函数,首字母大写。类似一个构造函数
3)提供一个首字母大写的Set方法(类似其它语言的public),用于对属性判断并赋值

funcvar结构体类型名)SetXxox(参数列表)(返回值列表)
	//加入数据验证的业务逻辑
	var.字段=参数

4)提供一个首字母大写的Get方法(类似其它语言的public),用于获取属性的值

funcvar结构体类型名)Getxxx)
	return var.字段;

模板:
person.go

package model
import "fmt"

type person struct 
	Name string
	age int   //小写其它包不能直接访问..
	sal float64


//写一个工厂模式的函数,相当于构造函数
//func 函数名(参数列表) (返回值列表) 
func NewPerson(name string) *person 
	return &person
		Name : name,
	


//为了访问age 和 sal 我们编写一对SetXxx的方法和GetXxx的方法
func (p *person) SetAge(age int) 
	if age >0 && age <150 
		p.age = age
	 else 
		fmt.Println("年龄范围不正确..")
		//给程序员给一个默认值
	


// 这里的int表示返回值类型,()表示不传参
// func (参数列表) Get方法名(传参) 返回值类型 
func (p *person) GetAge() int 
	return p.age



func (p *person) SetSal(sal float64) 
	if sal >= 3000 && sal <= 30000 
		p.sal = sal
	 else 
		fmt.Println("薪水范围不正确..")
		
	


func (p *person) GetSal() float64 
	return p.sal


main.go

package main
import (
	"fmt"
	"go_code/chapter11/encapsulate/model"
)

func main() 

	p := model.NewPerson("smith")
	p.SetAge(18)
	p.SetSal(5000)
	fmt.Println(p)
	fmt.Println(p.Name, " age =", p.GetAge(), " sal = ", p.GetSal())
	


main.go

package main

import (
	"fmt"
	"go_code/chapter11/encapexercise/model"
)

func main() 
	//创建一个account变量
	account := model.NewAccount("jzh11111", "000", 40)
	if account != nil 
		fmt.Println("创建成功=", account)
	 else 
		fmt.Println("创建失败")
	

account.go

package model

import (
	"fmt"
)
//定义一个结构体account
type account struct 
	accountNo string
	pwd string
	balance float64


//工厂模式的函数-构造函数
func NewAccount(accountNo string, pwd string, balance float64) *account 

	if len(accountNo) < 6 || len(accountNo) > 10 
		fmt.Println("账号的长度不对...")
		return nil
	

	if len(pwd) != 6 
		fmt.Println("密码的长度不对...")
		return nil
	

	if balance < 20 
		fmt.Println("余额数目不对...")
		return nil
	

	return &account
		accountNo : accountNo,
		pwd : pwd,
		balance : balance,
	



//方法
//1. 存款
func (account *account) Deposite(money float64, pwd string)  

	//看下输入的密码是否正确
	if pwd != account.pwd 
		fmt.Println("你输入的密码不正确")
		return 
	

	//看看存款金额是否正确
	if money <= 0 
		fmt.Println("你输入的金额不正确")
		return 
	

	account.balance += money
	fmt.Println("存款成功~~")



//取款
func (account *account) WithDraw(money float64, pwd string)  

	//看下输入的密码是否正确
	if pwd != account.pwd 
		fmt.Println("你输入的密码不正确")
		return 
	

	//看看取款金额是否正确
	if money <= 0  || money > account.balance 
		fmt.Println("你输入的金额不正确")
		return 
	

	account.balance -= money
	fmt.Println("取款成功~~")



//查询余额
func (account *account) Query(pwd string)  

	//看下输入的密码是否正确
	if pwd != account.pwd 
		fmt.Println("你输入的密码不正确")
		return 
	

	fmt.Printf("你的账号为=%v 余额=%v \\n", account.accountNo, account.balance)


继承

go是没有继承的,但面对相同的代码,如何避免代码冗余? (匿名结构体)

  • 当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体(比如Student),在该结构体中定义这些相同的属性和方法。
  • 通过一个struct嵌套另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承的特性

嵌套匿名结构体的基本语法

type Goods struct 
	Name string
	Price int


type Book struct 
	Goods // 这里就是嵌套匿名结构体Goods
	Writer string

匿名结构体

package main

import (
	"fmt"
)

//编写一个学生考试系统
type Student struct 
	Name  string
	Age   int
	Score int


//将Pupil 和 Graduate 共有的方法也绑定到 *Student
func (stu *Student) ShowInfo() 
	fmt.Printf("学生名=%v 年龄=%v 成绩=%v\\n", stu.Name, stu.Age, stu.Score)

func (stu *Student) SetScore(score int) 
	//业务判断
	stu.Score = score


//给 *Student 增加一个方法,那么 Pupil 和 Graduate都可以使用该方法
func (stu *Student) GetSum(n1 int, n2 int) int 
	return n1 + n2


//小学生
type Pupil struct 
	Student //嵌入了Student匿名结构体


//这时Pupil结构体特有的方法,保留
func (p *Pupil) testing() 
	fmt.Println("小学生正在考试中.....")


//大学生
type Graduate struct 
	Student //嵌入了Student匿名结构体


//这时Graduate结构体特有的方法,保留
func (p *Graduate) testing() 
	fmt.Println("大学生正在考试中.....")


//代码冗余.. 高中生....

func main() 

	//当我们对结构体嵌入了匿名结构体使用方法会发生变化
	pupil := &Pupil
	pupil.Student.Name = "tom~"
	pupil.Student.Age = 8
	pupil.testing()
	pupil.Student.SetScore(70)
	pupil.Student.ShowInfo()
	fmt.Println("res=", pupil.Student.GetSum(1, 2))


	// 匿名结构体 简写写法,编译器会自己去找本地的,如果不存在就去嵌入的匿名结构体
	graduate := &Graduate
	graduate.Name = "mary~"
	graduate.Age = 28
	graduate.testing()
	graduate.SetScore(90)
	graduate.ShowInfo()
	fmt.Println("res=", graduate.GetSum(10, 20))

  • 当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分。 先查自己有没有,逐级的查,查到了距离自己最近的,就屏蔽其他的

  • 结构体嵌入两个(或多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时就必须明确指定匿名结构体名字,否则编译报错

  • 有名结构体,必须指定才能调用,如果D中是一个有名结构体,则访问有名结构体的字段时,就必须带上有名结构体的名字,d.a.Name

  • 嵌套多个匿名结构体(多重继承,尽量不使用),直接指定各个匿名结构体字段的值

接口

文件

1、文件的基本介绍:
文件在程序中以流的形式来操作的。
文件——》(输入流)——》Go程序(内存)
流:数据在数据源(文件)和程序(内存)之间经历的路径
输入流(读文件):数据从数据源(文件)到程序(内存)的路径
输出流(写文件):数据从程序(内存)到数据源(文件)的路径

os.File封装所有文件相关操作,File是一个结构体

type File struct 
    // 内含隐藏或非导出字段

https://studygolang.com/static/pkgdoc/pkg/os.htm#File

2、打开文件和关闭文件

  1. 打开一个文件进行读和写操作:
func Open(name string) (file *File, err error)
Open打开一个文件用于读取。如果操作成功,返回的文件对象的方法可用于读取数据;对应的文件描述符具有O_RDONLY模式。如果出错,错误底层类型是*PathError。

func (f *File) Close() error
Close关闭文件f,使文件不能用于读写。它返回可能出现的错误。
package main
import (
	"fmt"
	"os" 
)
func main() 
	//打开文件
	//概念说明: file 的叫法,三个叫法都是一个意思
	//1. file 叫 file对象
	//2. file 叫 file指针
	//3. file 叫 file 文件句柄
	file , err := os.Open("d:/test.txt")
	if err != nil 
		fmt.Println("open file err=", err)
	
	//输出下文件,看看文件是什么, 看出file 就是一个指针 *File
	fmt.Printf("file=%v", file)
	//关闭文件
	err = file.Close()
	if err != nil 
		fmt.Println("close file err=", err)
	

3、读文件:读取文件的内容并显示在终端(带缓冲区的方式 bufio.NewReader)
使用os.Open,file.Close,bufio.NewReader(),reader.ReadString函数和方法

package main
import (
	"fmt"
	"os"
	"bufio"
	"io" 
)
func main() 
	//打开文件
	//概念说明: file 的叫法
	//1. file 叫 file对象
	//2. file 叫 file指针
	//3. file 叫 file 文件句柄
	file , err := os.Open("d:/test.txt")
	if err != nil 
		fmt.Println("open file err=", err)
	

	//defer 当函数退出时,要及时的关闭file
	defer file.Close() //要及时关闭file句柄,否则会有内存泄漏.

	// 创建一个 *Reader  ,是带缓冲的
	/*
	const (
	defaultBufSize = 4096 //默认的缓冲区为4096
	)
	*/
	reader := bufio.NewReader(file)
	//循环的读取文件的内容
	for 
		str, err := reader.ReadString('\\n') // 每读到一个换行符就结束
		if err == io.EOF  // io.EOF表示文件的末尾
		//如果文件读到最后一行readstring就会返回一个io.Eof
			break
		
		//输出内容
		fmt.Printf(str)
	

	fmt.Println("文件读取结束...")

defer:处理业务或逻辑中涉及成对的操作是一件比较烦琐的事情,比如打开和关闭文件、接收请求和回复请求、加锁和解锁等。在这些操作中,最容易忽略的就是在每个函数退出处正确地释放和关闭资源。
defer 语句正好是在函数退出时执行的语句,所以使用 defer 能非常方便地处理资源释放问题。

4、读文件:读取文件的内容并显示在终端(使用ioutil一次将整个文件读入到内存中)
这种方式适用于文件不大的情况。

func ReadFile(filename string) ([]byte, error)
ReadFile 从filename指定的文件中读取数据并返回文件的内容。成功的调用返回的err为nil而非EOF。因为本函数定义为读取整个文件,它不会将读取返回的EOF视为应报告的错误。
package main
import (
	"fmt"
	"io/ioutil" 
)
func main() 

	//使用ioutil.ReadFile一次性将文件读取到位
	file := "d:/test.txt"
	content, err := ioutil.ReadFile(file)
	if err != nil 
		fmt.Printf("read file err=%v", err)
	
	//把读取到的内容显示到终端
	//fmt.Printf("%v", content) // []byte
	fmt.Printf("%v", string(content)) // []byte
	
	//我们没有显式的Open文件,因此也不需要显式的Close文件
	//因为,文件的Open和Close被封装到 ReadFile 函数内部

5、写文件:基本应用实例(一)

func OpenFile(name string, flag int, perm FileMode) (file *File, err error)

OpenFile是一个更一般性的文件打开函数,大多数调用者都应用Open或Create代替本函数。它会使用指定的选项(如O_RDONLY等)、指定的模式(如0666等)打开指定名称的文件。如果操作成功,返回的文件对象可用于I/O。如果出错,错误底层类型是*PathError。

第二个参数:

const (
    O_RDONLY int = syscall.O_RDONLY // 只读模式打开文件
    O_WRONLY int = syscall.O_WRONLY // 只写模式打开文件
    O_RDWR   int = syscall.O_RDWR   // 读写模式打开文件
    O_APPEND int = syscall.O_APPEND // 写操作时将数据附加到文件尾部
    O_CREATE int = syscall.O_CREAT  // 如果不存在将创建一个新文件
    O_EXCL   int = syscall.O_EXCL   // 和O_CREATE配合使用,文件必须不存在
    O_SYNC   int = syscall.O_SYNC   // 打开文件用于同步I/O
    O_TRUNC  int = syscall.O_TRUNC  // 如果可能,打开时清空文件
)

第三个参数是权限777控制

应用实例:
1)创建一个新文件,写入内容5句"hello,Gardon"
os.O_WRONLY | os.O_CREATE
2)打开一个存在的文件中,将原来的内容覆盖成新的内容10句“你好,尚硅谷!"
os.O_WRONLY | os.O_TRUNC
3)打开一个存在的文件,在原来的内容追加内容’ABC!ENGLISH!!"
os.O_WRONLY | os.O_APPEND
4)打开一个存在的文件,将原来的内容读出显示在终端,并且追加5句“hello,北京!"
os.O_RDWR | os.O_APPEND

使用os.OpenFile(),bufio.NewWriter(),*Writer的WriterString

package main
import (
	"fmt"
	"bufio"
	"os" 
)
func main() 
	//创建一个新文件,写入内容 5句 "hello, Gardon"
	//1 .打开文件 d:/abc.txt
	filePath := "d:/abc.txt"
	file, err := os.OpenFile(filePath, os.O_WRONLY | os.O_CREATE, 0666)
	if err != nil 
		fmt.Printf("open file err=%v\\n", err)
		return 
	
	//及时关闭file句柄
	defer file.Close()
	//准备写入5句 "hello, Gardon"
	str := "hello,Gardon\\r\\n" // \\r\\n 表示换行
	//写入时,使用带缓存的 *Writer
	writer := bufio.NewWriter(file)
	for i := 0; i < 5; i++ 
		writer.WriteString(str)
	
	//因为writer是带缓存,因此在调用WriterString方法时,其实
	//内容是先写入到缓存的,所以需要调用Flush方法,将缓冲的数据
	//真正写入到文件中, 否则文件中会没有数据!!!
	writer.Flush()

6、写文件:基本应用实例(二)
编程一个程序,将一个文件的内容,写入到另外一个文件。注:这两个文件已经存在了.
说明:使用ioutil.ReadFile/ioutil.WriteFile完成写文件的任务.
代码实现:

package main
import (
	"fmt"
	"io/ioutil" 
)
func main() 
	//将d:/abc.txt 文件内容导入到  e:/kkk.txt
	//1. 首先将  d:/abc.txt 内容读取到内存
	//2. 将读取到的内容写入 e:/kkk.txt
	file1Path := "d:/abc.txt" 
	file2Path := "e:/kkk.txt" 
	data, err := ioutil.ReadFile(file1Path)
	if err != nil 
		//说明读取文件有错误
		fmt.Printf("read file err=%v\\n", err)
		return
	
	err = ioutil.WriteFile(file2Path, data, 0666)
	if err != nil 
		fmt.Printf("write file error=%v\\n", err)
	

7、写文件:基本应用实例(三)拷贝文件

package main
import (
	"fmt"
	"os"
	"io"
	"bufio" 
)

//自己编写一个函数,接收两个文件路径 srcFileName dstFileName
func CopyFile(dstFileName string, srcFileName string) (written int64, err error) 

	srcFile, err := os.Open(srcFileName)
	if err != nil 
		fmt.Printf("open file err=%v\\n", err)
	
	defer srcFile.Close()
	//通过srcfile ,获取到 Reader
	reader := bufio.NewReader(srcFile)

	//打开dstFileName
	dstFile, err := os.OpenFile(dstFileName, os.O_WRONLY | os.O_CREATE, 0666)
	if err != nil 
		fmt.Printf("open file err=%v\\n", err)
		return 
	
	//通过dstFile, 获取到 Writer
	writer := bufio.NewWriter(dstFile)
	defer dstFile.Close()

	

以上是关于Go语言学习之路的主要内容,如果未能解决你的问题,请参考以下文章

Go语言之路—博客目录

go语言(golang)学习之路

《Go语言精进之路》读书笔记 | 汇总

Go语言学习之路

Go语言学习之路

Go语言学习之路