Golang学习笔记

Golang

基础语法


Golang执行流程分析

如果是对源码编译后,再执行,Go的执行流程如下图

在这里插入图片描述

如果是对源代码直接 执行 go run 源码,Go的执行流程如下图

在这里插入图片描述

编译

go build main.go

go build -o 自己命名 main.go -o 参数自定义文件名

Windows下是生成二进制exe可执行程序

Go程序开发注意事项

1)Go源文件以"go"为拓展名

2)Go应用程序的执行入口是main()方法

3)Go语言严格区分大小写

4)Go方法由一条条语句构成,每个语句不需要加分号(每行会自动加分号)

5)Go编译器是一行行进行编译的,因此我们一行只能写一行语句,不能多条语句写在同一个,否则报错

6)Go语言定义的变量或者import的包如果没有用到,代码不能编译通过

7)打括号都是成对出现 缺一不可

Go语言转义符

常用转义字符

1)\t 对齐

2)\n 换行符

3)\\ \

4)\" "号

5)\r 回车

变量

变量相当于内存中一个数据存储空间的表示

变量使用的基本步骤

1)声明变量

2)赋值

3)使用

变量使用的三种方式

1)指定变量类型,声明后若不赋值,使用默认值

2)根据值自行判定变量类型(类型推导)

3)省略var 注意:=左侧的变量不应该是已经声明过的,否则会导致编译错误

数据类型

基本数据类型

数值型:整数类型 (int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64,byte)int类型默认是int32

浮点类型 (float32,float64)

字符型:没有专门的字符型,使用byte来保存单个字母字符 需要用’'来包裹 如 var a byte = ‘a’

布尔型:bool

字符串:string

派生/复杂数据类型

1.指针(pointer)

2.数组

3.结构体(struct)

4.管道(channel)

5.函数(也是一种类型)

6.切片(slice)

7.接口(interface)

8.map

基本类型转换

Golang在不同类型的变量之间赋值时需要显式转换,也就是说Golang中数据类型不能自动转换

基本语法

表达式T(v) 将值v转换成T类型

T:type类型

v:变量

package main

import (
	"fmt"
	"reflect"
)

func main() {
	//数据类型转换
	var i int = 100
	//将i转换为float类型
	var i1 = float32(i)
	fmt.Println(i1, reflect.TypeOf(i1))
}

基本类型转换string类型 fmt.Sprintf(“格式化动作”,变量)

package main

import (
	"fmt"
	"strconv"
)

func main() {
	// 基本数据类型转string类型
	var num1 int = 99
	var num2 float64 = 23.456
	var b bool = true
	var myChar byte = 'h'
	var str string

	//使用第一种方式来转换
	str = fmt.Sprintf("%d", num1)
	fmt.Printf("str type %T str=%q\n", str, str)
	str = fmt.Sprintf("%f", num2)
	fmt.Printf("str type %T str=%q\n", str, str)
	str = fmt.Sprintf("%t", b)
	fmt.Printf("str type %T str=%q\n", str, str)
	str = fmt.Sprintf("%c", myChar)
	fmt.Printf("str type %T str=%q\n", str, str)

}
//第二种 strconv函数
	var num3 int = 99
	var num4 float64 = 23.456
	var b2 bool = true

	str = strconv.FormatInt(int64(num3), 10)
	fmt.Printf("str type %T str=%q\n", str, str)
	str = strconv.FormatFloat(num4, 'f', 10, 64)
	/*	str = strconv.FormatFloat(num4, 'f', 10, 64)
		说明:'f' 格式10:表示小数位保留10位 64:表示这个浮点数是float64
	*/
	fmt.Printf("str type %T str=%q\n", str, str)
	str = strconv.FormatBool(b2)
	fmt.Printf("str type %T str=%q\n", str, str)
//strconv包中有一个函数Itoa
	var num5 int64 = 99
	str = strconv.Itoa(int(num5)) // Itoa只能转换int类型 此处可以转成int类型
	fmt.Printf("str type %T str=%q\n", str, str)

spring类型转为基本数据类型

package main

import (
	"fmt"
	"strconv"
)

func main() {
	//string转基本类型
	//string 转bool类型
	var str string = "true"
	var b bool
	b, _ = strconv.ParseBool(str)
	// b, _ = strconv.ParseBool(str)
	//说明:strconv.ParseBool(str) 函数会返回两个值(value bool,err error) 使用,_忽略后面的值 只获取value bool
	fmt.Printf("b type %T b=%v\n", b, b)
	// string转int类型
	var str2 string = "123"
	var n1 int64
	n1, _ = strconv.ParseInt(str2, 10, 8) //ParseInt方法返回int64 ,8代表此处你所转换的数值不能满足bitSize范围大小,会报错比方超过int64
	fmt.Printf("n1 type %T n1=%v\n", n1, n1)
	//string转浮点类型
	var str3 string = "22.22"
	var f float64
	f, _ = strconv.ParseFloat(str3, 64)
	fmt.Printf("f type %T f=%v\n", f, f)

}

数组

定义数组

var 数组名[数组大小] 数组类型

var hens [i]int

package main
import "fmt"
func main() {
	//定义数组
	var hens [6]float64
	//给数组的每个元素赋值
	hens[0] =3.1
	hens[1] =1
	hens[2] =2
	hens[3] =6.6
	hens[4] =8.7
	//遍历数组
	arrSum:=0.0
	for i := 0; i < len(hens); i++ {
		fmt.Println(hens[i]) //默认值为0
		arrSum+=hens[i]
	}
	//求和与平均值
	ageArr := fmt.Sprintf("%.2f",arrSum/6) //保留两位小数
	fmt.Printf("该数组的和为:%v\n",arrSum)
	fmt.Printf("该数组的平均值为:%v\n",ageArr)
	
}

默认情况下,数组的每个元素都被初始化为元素类型对应的零值,对于数字类型来说就是0

四种初始化数组的方式

//五种初始化数组的方式
var myArrays1 = []int{1,2,3}
fmt.Println(myArrays1)
var myArrays2 [3]int= [3]int{1,2,3}
fmt.Println(myArrays2)
var myArrays3 = [...]int{1,2,3} //...规定的写法
fmt.Println(myArrays3)
var myArrays4 = [...]int{0:200,1:100,2:300}
fmt.Println(myArrays4)
//类型推导
myArrays5:= [...]string{"小明","大毛","二毛"}
fmt.Println(myArrays5)

数组遍历

for-range结构遍历

fmt.Println(myArrays5)
for index,value := range myArrays5{
	fmt.Println(index,value)
}

数组练习

package main
import 
(
"fmt"
"math/rand"
"time"

)
func main() {
	//打印A-Z的字母
	var letters [26]byte
	letters[0]='A'

	for i := 0; i < 26; i++ {
		letters[i] = letters[0]+byte(i)
	
		
	}
	
	for i := 0; i < 26; i++ {
	
		fmt.Printf("%c ",letters[i])
	}
 	fmt.Println()
	//找出数组中整数的最大值 非冒泡查询
	numArrays:=[...]int {10,2,3,4,5}
	
	maxNum:=numArrays[0]
	iArray:=0
	for i := 0; i < len(numArrays); i++ {
		if numArrays[i]>maxNum {
            maxNum=numArrays[i]
			iArray=i
        }
	}
	fmt.Printf("numArray a Max: %v\n",maxNum)
    fmt.Printf("numArrays a Index: numArrays[%v]\n",iArray)
	//随机生成五个数,进行反转
	var numArrays2 [5]int
	rand.Seed(time.Now().UnixNano())
	for i := 0; i < len(numArrays2); i++ {
		numArrays2[i]=rand.Intn(100)
		
	}
	fmt.Printf("numArrays2替换前: %v\n",numArrays2)
	temp := 0
	temp = numArrays2[4]
	numArrays2[4]=numArrays2[0]
	numArrays2[0]=temp

	fmt.Printf("numArrays2: %v\n",numArrays2)
	
}
切片

Slice(切片)代表变长的序列,序列中每个元素都有相同的类型。一个slice类型一般写作[]T,其中T代表slice中元素的类型;slice的语法和数组很像,只是没有固定长度而已。

var 切片名[]类型

var testSlice[] int

切片的内存图

在这里插入图片描述

定义切片的三种方式

1)定义一个切片,使用切片去引用一个数组

var myArray = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	slice := myArray[1:3] // 前闭后开 只包含前面的 也就是不包含数组下标为3的

2)通过make来创建切片

基本语法:var切片名[]type=make([],len,[cap])

参数说明:type数据类型 len大小 cap 指定切片容量

指针

基本介绍

1) 每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。使用 &字符放在变量前面对变量进行“取地址”操作。 Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:*int、*int64、*string 等。

Pointer

2)获取变量的地址,用&,比如:var i int ,获取i的地址,&i

在这里插入图片描述

3)指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值,比如

var ptr *int = &i

在指针类型前面加上*号(前缀)来获取指针所指向的内容

在这里插入图片描述

键盘输入语句

在编程中,需要接收用户输入的内容,通过fmt.Scanln()或fmt.Scanf()来接受内容

package main

import "fmt"

func main() {
	//使用Scanln方式接收数据
	var name string
	var age byte
	var sal float32
	var isPass bool
	// fmt.Println("请输入你的姓名:")
	// fmt.Scanln(&name)
	// fmt.Println("请输入你的年龄:")
	// fmt.Scanln(&age)
	// fmt.Println("请输入你的薪水:")
	// fmt.Scanln(&sal)
	// fmt.Println("是否通过考试:")
	// fmt.Scanln(&isPass)
	// fmt.Printf("姓名:%v\n年龄:%v\n 薪水:%v \n 是否通过考试:%v \n",name,age,sal,isPass)
	//使用Scanf方式接收数据
	fmt.Printf("请输入你的姓名、年龄、薪水、是否通过考试 用空格分隔\n")
	fmt.Scanf("%s %d %f %t",&name,&age,&sal,&isPass)
	fmt.Printf("姓名:%v\n 年龄:%v\n 薪水:%v\n 是否通过考试:%v\n",name,age,sal,isPass)
}
流程控制

1)顺序控制

package main

import "fmt"

func main() {
	var age int64
	fmt.Println("请输入你的年龄:")
	fmt.Scanln(&age)
	if age>18{
		fmt.Println("你年龄大于18岁要对自己的行为负责")
	}else {
		fmt.Println("小屁孩")
	}
}

2)分支控制

Switch分支

package main

import "fmt"

func main() {
	var key byte
	fmt.Println("请输入一个字符:a,b,c,d,e,f,g")
	fmt.Scanf("%c",&key)
	switch key{
		case 'a':
			fmt.Println("周一")
		case 'b':
			fmt.Println("周二")
		case 'c':
			fmt.Println("周三")
		case 'd':
			fmt.Println("周四")
		case 'e':
			fmt.Println("周五")
		case 'f':
			fmt.Println("周六")
		case 'g':
			fmt.Println("周天")
		default:
			fmt.Println("编译错误")

	}
}

case后面不需要带break,程序匹配到一个case后就会执行对应的代码块,然后退出switch,如果一个都匹配不到,则执行default

default不是必须的

3)循环控制

代码循环

package main

import "fmt"

func main() {
	for i := 1; i <= 10; i++ {
		fmt.Println("我好帅!",i)
	}
}

遍历

package main

import "fmt"

func main() {
	//遍历方式1
	var str string = "hello,world"
	for i := 0; i < len(str); i++ {
		fmt.Printf("%c\n",str[i])
	}
	//遍历方式2 for-range
	str = "abc"
	for index,val:=range str{
		fmt.Printf("index=%v val=%c\n",index,val)
	}
}

传统遍历中如果遍历的字符有中文,会出现乱码,因为遍历是单字节遍历的方式,utf-8编码对应的是3个字节

使用string转[]rune切片的方式来解决

str :=[]rune(str)//string转切片类型
函数

func 函数名(行参列表) (返回值列表){

​ 执行语句…

​ 返回值列表

}

1)函数的行参列表可以是多个,返回值列表也可以是多个

2)行参列表和返回值列表的数据类型可以是值类型和引用类型

3)函数命名需要像java public 首字母大写

4)函数内部的变量是局部的,函数外不生效

5)基本数据类型和数组默认都是值传递的,即进行值拷贝,在函数内修改,不会影响原来的值

6)如果希望函数内的变量能修改函数外的变量,可以穿入变量的地址&,函数内以指针的方式操作变量

7)Go函数不支持重载

package main

import "fmt"

func main() {
	//主函数
	var a int64 = 3
	var b int64 = 1
	var operator byte = '-'
	result := cal(a, b, operator)
	fmt.Println("result:", result)

}

func Cal(a int64, b int64, operator byte) int64 {
	//自定义函数
	var res int64
	switch operator {
	case '+':
		res = a + b
	case '-':
		res = a - b
	case '*':
		res = a * b
	case '/':
		res = a / b
	default:
		fmt.Println("符号错误")
	}
	return res

}

打包基本语法:

package util

引入包:

Import “titl” (""内包的路径)

注意如果

GO111MODULE=“off”

默认使用GOPATH 环境变量路径(设置成项目路径)

GO111MODULE=“on”

在根目录下 go mod init 项目名

函数递归

1)执行一个函数时,就创建一个新的受保护的独立空间(新函数栈)

2)函数的局部变量是独立的,不会相互影响

3)递归必须向退出递归的条件逼近,否则就是无限递归,死龟了:)

4)当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时函数调用结束或返回后,该函数本身也会被销毁

init函数

每一个源文件都有一个init函数,该函数在main函数调用前会被go运行框架调用,init在main函数前调用

匿名函数

如果函数只使用一次,可以考虑使用匿名函数,匿名函数也可以多次被调用

package main
import "fmt"

//全局匿名函数
var TestFunc2 =func (n1 int,n2 int)int{
		return n1 + n2
	}
		
func main() {

	//匿名函数
testFunc := func (a int,b int) int {
	
return a+b
   
}(1,2)
fmt.Println("result:", testFunc)

result := TestFunc2(1,2)

fmt.Println("result:", result)


}  

全局匿名函数变量需要首字母大写

闭包

闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)

package main
import "fmt"
	//累加器
func addUpper() func(int)int{
	var n int =10
	return func(x int)int{
		n +=1
		return n
	}
}



func main() {
	result := addUpper()
	fmt.Println("n=",result(1))
	fmt.Println("n=",result(2))
	fmt.Println("n=",result(3))
	
	
}

闭包的本质就是调用了外层变量并返回一个函数

函数defer

释放资源 延时机制

当执行到defer时,暂时不执行,会将defer后面的语句压入到独立的栈(defer栈)

当函数执行完毕后,再从defer栈,按照先入后出的方式出栈执行

package main
import "fmt"

func sum(n1 int, n2 int) int {
	defer fmt.Println("n1=",n1) //3  //先存起来,最后执行回来保留原结果不受外界干扰
	defer fmt.Println("n2=",n2) //2 //先存起来,最后执行回来保留原结果不受外界干扰
	n1++ 
	n2++
	res := n1+n2
	fmt.Println("res=",res) //1
		return res  
	

}
func main() {
	fmt.Println(sum(100, 100))	//4
}

项目案例:

func test(){
    //关闭文件资源
    file = openfile("文件名")
    defer file.close
    //向下执行,执行后关闭
}
函数参数的传递方式

1)值传递 基本数据类型int 、float、bool、string,数组和结构体struct

2)引用传递 (地址) 指针、slice切片、map、管道chan、interface

不管是值传递还是引用传递,传递给函数的都是变量的副本,不同的是,值传递的是值拷贝,引用传递是地址的拷贝,一般来说,第值拷贝效率高,因为数据量小,而值拷贝决定拷贝的数据大小,数据越大,效率越低

变量作用域

局部变量 作用域仅限函数内部

全局变量 整个包有效

系统函数

字符串常用函数

len(str) 判断字符串长度,根据字节判断,中文为3个字节

package main
import "fmt"
func main() {
	//统计字符串的长度 按字节len(str)
	str :="hello"
	fmt.Println(len(str))
}

遍历是有中文字符需要转为[]rune()类型

package main
import "fmt"
func main() {
	//统计字符串的长度 按字节len(str)
	str :="hello你好"
	fmt.Println(len(str))
	str1 :=[]rune(str)
	for i := 0; i < len(str1); i++ {
		fmt.Printf("%c\n",str1[i])
	}
}

字符串转整数,n,err :=strconv.Atoi(“12”)

	n,err := strconv.Atoi("123")
	if err != nil {
		fmt.Println("错误",err)
	}else{
		fmt.Println("转成的结果",n)
	}

整数转字符串, n,err :=strconv.Itoa(123)

str2 := strconv.Itoa(123)
	if(err != nil){
		fmt.Println("错误",err)
	}else{
		fmt.Printf("str类型:%T 转成的结果%v",str2,str2)
	}

字符串转成[]byte

//字符串转为[]byte
	str3 := []byte("hello")
    fmt.Printf("%c",str3)

字符串替换

str4:=strings.Replace("go go hello", "go","go语言",n)
	fmt.Printf("%v\n",str4)

字符串分隔


  str5:=strings.Split("hello,1223,hell",",")

  fmt.Printf("%v\n",str5)

大小写转换

	//ToLower转小写
	str6:=strings.ToLower("Go")
	fmt.Printf("%v\n",str6)
	// ToUpper转大写
	str7:=strings.ToUpper("go")
	fmt.Printf("%v\n",str7)

…剩下的看官方文档

时间日期相关函数
package main
import 
(
	"fmt"
	"time"
)
func main() {
	//日期时间相关函数
	//获取当前时间
	now :=time.Now()
	fmt.Printf("类型是%T %v\n",now,now)
    //通过now获取年月日,时分秒
	fmt.Printf("年=%v\n",now.Year())
	fmt.Printf("月=%v\n",now.Month())
	fmt.Printf("年=%v\n",now.Day())
	fmt.Printf("时=%v\n",now.Hour())
	fmt.Printf("分=%v\n",now.Minute())
	fmt.Printf("秒=%v\n",now.Second())

	
}

格式化日期时间

//格式化输出

fmt.Printf("%d-%d-%d %d:%d:%d",now.Year(),now.Month(),now.Day(),now.Hour(),now.Minute(),now.Second())

fmt.Println(now.Format("2006-01-02 15:04:05"))

时间戳

func (t Time) Unix() int64
func (t Time) UnixNano() int64
testNow := time.Now()
	sj:=testNow.Unix()
	sj2:=testNow.UnixNano()
	fmt.Println(sj)
	fmt.Println(sj2)
	
内置函数

len(str) 判断字符串长度,根据字节判断,中文为3个字节

new() 用来分配内存,主要用来分配值类型,比如int、float32,struct…返回的是指针

num2 :=new(int)
	*num2 = 200
	fmt.Printf("num2的类型是:%T num2: %v", num2,*num2)

make 用来分配 内存,主要用来分配引用类型,比如chan、map、slice

错误处理

处理错误

package main
import 
("fmt"
"time"
)

func test(){
	//使用defer +recover 来捕获异常处理异常
	defer func() {
        err:=recover()
		if err !=nil{
			fmt.Println("异常错误是:",err)
	
		}
    }()
	a:=10 
	b:=0
	result:=a/b
	fmt.Println(result)
}
func main() {
	test()
	for  {
		fmt.Println("下面的代码")
		time.Sleep(time.Second)
	}
	
	
}

自定义错误

使用errors.New和panic内置函数

1)errors.New(“错误说明”),会返回一个error类型的值,表示一个错误

2)panic内置函数,接受一个interface{}类型的值(也就是任何值)作为参数。可以接受error类型的变量,输出错误信息,并退出程序

package main

import 
(
    "fmt"
    "errors"
)
//读取ini.conf配置信息
//如果文件名传入不正确,我们就返回一个自定义错误
func readConfig(name string)(err error){
	if name == "ini.conf"{
        //读取...
        return nil
    }else {
		//返回一个自定义错误
		return errors.New("读取文件错误")
	}

}

func myErr(){
	err:=readConfig("ini.ff")
	if err!=nil{
		//如果发生错误,输出错误,并终止程序
		panic(err)
		
	}
	fmt.Println("ok")
	
}
func main() {
	//测试自定义错误的使用
	myErr()
	fmt.Println("下面的代码")

}
map

无序的,只可使用for range进行遍历

func Map() {
	var m1 map[string]string
	fmt.Println("m1==nil?", m1 == nil)
	m1 = make(map[string]string, 2) //make(Type,初始Size) size可以省略,默认为1
	m1["早晨"] = "敲代码"
	m1["中午"] = "送外卖"
	m1["晚上"] = "按摩店" // 如果按照新的key进行赋值,如果key存在就是修改,不存在就是新增key
	fmt.Println("m1=", m1)
	m2 := map[string]string{
		"凌晨": "吃夜宵",
		"梦里": "磨枪",
	}
	fmt.Println("m2=", m2)
  //查找操作
	v, ok := m2["早上"]
	if ok {
		fmt.Println("v=", v)
	} else {
		fmt.Println("vlaue不存在")
	}
  //删除操作
	delete(m1, "晚上")
	fmt.Println("m1=\n", m1)
	// m1 = nil
	m2 = make(map[string]string)
	fmt.Println("m2=\n0", m2)
	for k, v := range m1 {
		fmt.Printf("m1[k]=%v \n m1[v]=%v", k, v)
	}
}

delete操作

1)如果我们要删除map所有的key,没有专门一次性全部删除的方法,可以使用遍历key进行删除

2)或者map = make(…),make一个新的map让原来的成为垃圾,被gc删除

自定义数据类型

自定义数据类型

属于不同类型,需要类型转换

	fmt.Println("自定义数据类型")
	type mesType uint16
	var u1000 uint16 = 1000
	var textMes mesType = mesType(u1000)
	fmt.Printf("textMesType= %T textMes= %v\n", textMes, textMes)

类型别名

属于相同类型,混用无需类型转换

fmt.Println("类型别名")
	type myUint16 = uint16
	var myu16 myUint16 = u1000
	fmt.Printf("myu16Type= %T myu16= %v\n", myu16, myu16)

结构体

结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员。用结构体的经典案例是处理公司的员工信息,每个员工信息包含一个唯一的员工编号、员工的名字、家庭住址、出生日期、工作岗位、薪资、上级领导等等。所有的这些信息都需要绑定到一个实体中,可以作为一个整体单元被复制,作为函数的参数或返回值,或者是被存储到数组中,等等。

结构体字段

结构体可以没有字段

结构体字段通过“.”访问

结构体指针

// 结构体
type User struct {
	Name string
	Id   uint32
}
type Account struct {
	User
	password int64
}
type Contact struct {
	*User
	Remark string
}

func Struct() {
	var u1 User = User{
		Name: "张三",
	}
	u1.Id = 1000
	var u2 *User = &User{
		Name: "李四",
	}
	u2.Id = 1001 //(*u2).Id=10001
	var a1 = Account{
		User: User{
			Name: u1.Name,
		},
		password: 123456,
	}
	var c1 *Contact = &Contact{
		User:   &User{},
		Remark: "王麻子",
	}
	c1.Name = "王五"
	fmt.Printf("Account Type: %T Account Value: %v\n", a1, a1)
	fmt.Printf("Contact Type: %T Contact Value: %v c1.Name: %v\n", c1, c1, c1.Name)

}

字段/属性

注意事项和细节说明

1)字段声明语法同变量,示例:字段名 字段类型。

2)字段的类型可以为:基本类型、数组或引用类型。

3)在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值),规则前面讲的一样:

布尔类型是false,数值是0,字符串是""

数组类型的默认值和他的元素类型相关,比如score [3]int 则为[0,0,0]

指针,slice,和map的零值都是nil,即还没有分配空间。

匿名函数

如果函数只使用一次,可以考虑使用匿名函数,匿名函数也可以多次被调用

方法

在函数声明时,在其名字之前放上一个变量,即是一个方法。这个附加的参数会将该函数附加到这种类型上,即相当于为这种类型定义了一个独占的方法。

//方法

type TestStru struct {
	Name string
}

// 定义一个函数
func (p TestStru) TestFcun() {
	p.Name = "jack"
	fmt.Println("Celebrity()", p.Name)

}

func (p TestStru) Calculation() {
	res := 0
	for i := 0; i <= 1000; i++ {
		res += i
	}
	fmt.Println(p.Name, "你的计算结果是", res)

}
func (p TestStru) Calculation1(n int) {
	res := 0
	for i := 1; i <= n; i++ {
		res += i

	}
	fmt.Println(p.Name, "你的计算结果是", res)
}

func Celebrity() {
	var p TestStru
	p.Name = "tom"
	p.TestFcun() // 只能使用这种方式调用
	fmt.Println("Celebrity()", p.Name)
	p.Calculation()
	p.Calculation1(20)
}

// 方法案例  求面积
type Circle struct {
	radius float64
}

// 生命一个方法area和Circle绑定,可以返回面积
func (c Circle) Area() float64 {
	return 3.14 * c.radius * c.radius
}

//生命一个结构体circle,字段为 radius
//生命一个方法 area和circle绑定,可以返回面积
//创建一个Circle变量

func TestCircle() {
	var c Circle
	c.radius = 4.0
	res := c.Area()
	fmt.Println("面积是:", res)
}

方法调用案例

// 方法案例
type MethodUtils struct {
}

func (mu MethodUtils) Printf(m int, n int) {
	for i := 1; i <= m; i++ {
		for j := 1; j <= n; j++ {
			fmt.Print(("*"))

		}
		fmt.Println()

	}

}

// 编写结构体(MethodUtils),编程一个方法,方法不需要参数,在方法中打印10*8的矩形
func TestMu() {
	var mu MethodUtils
	mu.Printf(10, 8)
}

面向对象

封装

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

封装的实现过程

请添加图片描述

type person struct {
	Name string
	age  int //其他包不能进行访问
	sal  float64
}

// 写一个工厂模式的函数,相当于构造函数
func newPerson(name string) *person {

	return &person{
		Name: name,
	}

}

// 为了访问age和sal 编写GET SET方法
func (p *person) SetAge(age int) {
	if age > 0 && age < 150 {
		p.age = age
	} else {
		fmt.Println("年龄范围不正确")
		//给程序员给个默认值
	}
}
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
}
func EncapsulationDemo() {
	p := newPerson("smith")
	p.SetAge(18)
	p.SetSal(5000)
	fmt.Println(p)
	fmt.Println(p.Name, p.GetAge(), p.GetSal())
}

继承

代码复用 ,减少代码冗余

// 继承案例
// 编写一个学生考试系统
// 学生
type Student struct {
	Name  string
	Age   int
	Score int
}
type Pupil struct {
	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

}

func (p *Pupil) testing() {
	fmt.Println("小学生正在考试中")
}

//大学生

type Graduate struct {
	Student //匿名结构体
}

func (g *Graduate) testing() {
	fmt.Println("大学生正在考试中")
}
func Inherit() {
	pupil := &Pupil{}
	pupil.Student.Name = "tom"
	pupil.Student.Age = 8
	pupil.testing()
	pupil.Student.SetScore(90)
	pupil.Student.ShowInfo()

	graduate := &Graduate{}
	graduate.Student.Name = "jack"
	graduate.Student.Age = 20
	graduate.testing()
	graduate.Student.SetScore(80)
	graduate.Student.ShowInfo()
}

接口
// 声明一个接口
type Usb interface {
	//声明两个方法
	Start()
	Stop()
}
type Phone struct {
}

func (p Phone) Start() {
	fmt.Println("手机开始工作")

}
func (Phone) Stop() {
	fmt.Println("手机停止工作")
}

type Camera struct {
}

func (c Camera) Start() {
	fmt.Println("手机开始工作")

}
func (c Camera) Stop() {
	fmt.Println("手机停止工作")
}

type Computer struct{}

// 编写一个方法working 方法,接受一个接口类型变量
// 只要是实现了usb接口
func (c Computer) Working(usb Usb) {
	usb.Start()
	usb.Stop()
}
func InterfaceDemo() {
	computer := Computer{}
	phone := Phone{}
	camera := Camera{}
	computer.Working(phone)
	computer.Working(camera)
}

多态
多态特征是通过接口实现的。可以按照统一的借口调用不同的实现。这时接口变量就呈现不同的形态

以上代码usb变量会根据传入的实惨,来判断到底是phone还是Camra

请添加图片描述

类型断言

// 类型断言
type Point struct {
	x int
	y int
}

func Assert() {
	var a interface{}
	var point Point = Point{}
	a = point //ok
	//如何将a赋给一个Point变量?
	var b Point
	//b =a 不可以
	//b =a.(Point)//可以
	b = a.(Point)
	fmt.Println(b)

	//类型断言的其它类型
	var x interface{}
	var b2 float32 = 1.1
	x = b2 //空接口 ,可以接受任意类型
	//x=>float32[使用类型断言]
	y := x.(float32)
	fmt.Printf("y的类型是%T 值是=%v", y, y)

}

在进行类型断言时,如果类型不匹配,就会报panic,因此进行类型断言时,要确保原来的

//项目

// 模拟实现基于文本界面的《家庭记账软件》。

//该软件能够记录家庭的收入、支出,并能够打印收支明细表

//项目采用分级菜单方式,主菜单如下

// -----------家庭收支记账软件-----------

// 1.收支明细

​ // 2 登记收入

​ // 3 登记支出

​ // 4 退 出

面向过程编程

package main

import "fmt"

func main() {
	//声明一个变量,保存接收用户输入的选项
	key := ""
	//声明一个变量,控制是否退出for
	loop := true
	//定义账户的余额
	balance := 0.0
	//每次收支的金额
	money := 0.0
	//每次收支的说明
	note := ""
	//收支详情使用字符串来记录
	datails := "收支\t账户金额\t收支金额\t说  明"
	falg := false
	// 显示主菜单
	for {
		fmt.Println("------------------家庭收支记账软件------------------")
		fmt.Println("               	1 收支明细")
		fmt.Println("               	2 登记收入")
		fmt.Println("               	3 登记支出")
		fmt.Println("               	4 退   出")
		fmt.Print("请选择(1-4):")
		fmt.Scanln(&key)
		switch key {
		case "1":

			fmt.Println("\n------------------当前收支明细------------------")
			if falg {
				fmt.Println(datails)
			} else {
				fmt.Println("当前没有收支明细。。。来一笔吧!")
			}

		case "2":
			fmt.Println("本次收入金额:")
			fmt.Scanln(&money)
			balance += money // 修改账户余额
			fmt.Println("本次收入说明:")
			fmt.Scanln(&note)

			//将这个收入情况,拼接到details变量
			datails += fmt.Sprintf("\n收入\t%v     \t%v      \t%v", balance, money, note)
			falg = true
		case "3":
			fmt.Println("本次支出金额:")
			fmt.Scanln(&money)
			if money > balance {
				fmt.Println("支出金额大于余额")
				break
			}
			balance = balance - money //修改账户余额
			fmt.Println("本次支出说明:")
			fmt.Scanln(&note)

			datails += fmt.Sprintf("\n支出\t%v     \t%v      \t%v", balance, money, note)
			falg = true
		case "4":
			fmt.Println("你确定要退出吗?y/n")
			choice := ""
			for {
				fmt.Scanln(&choice)
				if choice == "y" || choice == "n" {
					break
				}
				fmt.Println("你输入的命令有误")
				fmt.Println("你确定要退出吗?y/n")
			}
			if choice == "y" {
				loop = false
			}

		default:
			fmt.Println("请输入正确的选项")

		}
		if !loop {
			break
		}
	}
}

面向对象编程
封装工具类

package utils

import "fmt"

type FamilyAccount struct {
	// 声明一个字段,保存接收用户输入的选项
	key string
	// 声明一个字段,控制是否退出for
	loop bool
	// 定义账户的余额
	balance float64
	// 每次收支的金额
	money float64
	// 每次收支的说明
	note string
	// 收支详情使用字符串来记录
	datails string
	// 定义记录收支行为字段,
	flag bool
}

// 编写要给工厂模式的构造方法,返回一个*FamilyAccount实例
func NewFamilyAccount() *FamilyAccount {
	return &FamilyAccount{
		key:     "",
		loop:    true,
		balance: 0.0,
		money:   0.0,
		note:    "",
		flag:    false,
		datails: "收支\t账户金额\t收支金额\t说  明 ",
	}
}

// 将显示明细封装到一个方法
func (this *FamilyAccount) ShowDetails() {
	fmt.Println("\n------------------当前收支明细------------------")
	if this.flag {
		fmt.Println(this.datails)
	} else {
		fmt.Println("当前没有收支明细。。。来一笔吧!")
	}
}

// 将登记收入封装到一个方法
func (this *FamilyAccount) Income() {
	fmt.Println("本次收入金额:")
	fmt.Scanln(&this.money)
	this.balance += this.money // 修改账户余额
	fmt.Println("本次收入说明:")
	fmt.Scanln(&this.note)

	// 将这个收入情况,拼接到details变量
	this.datails += fmt.Sprintf("\n收入\t%v     \t%v      \t%v", this.balance, this.money, this.note)
	this.flag = true
}

// 将收入支出封装成一个方法
func (this *FamilyAccount) Pay() {
	fmt.Println("本次支出金额:")
	fmt.Scanln(&this.money)
	if this.money > this.balance {
		fmt.Println("支出金额大于余额")
		return
	}
	this.balance = this.balance - this.money // 修改账户余额
	fmt.Println("本次支出说明:")
	fmt.Scanln(&this.note)

	this.datails += fmt.Sprintf("\n支出\t%v     \t%v      \t%v", this.balance, this.money, this.note)
	this.flag = true
}

// 将退出封装成方法
func (this *FamilyAccount) Exit() {
	fmt.Println("你确定要退出吗?y/n")
	choice := ""
	for {
		fmt.Scanln(&choice)
		if choice == "y" || choice == "n" {
			break
		}
		fmt.Println("你输入的命令有误")
		fmt.Println("你确定要退出吗?y/n")
	}
	if choice == "y" {
		this.loop = false
	}
}

// 给该结构体绑定相应的方法
func (this *FamilyAccount) MainMenu() {
	// 显示主菜单
	for this.loop {
		fmt.Println("------------------家庭收支记账软件------------------")
		fmt.Println("                1 收支明细")
		fmt.Println("                2 登记收入")
		fmt.Println("                3 收入支出")
		fmt.Println("                4 退   出")
		fmt.Print("请选择(1-4):")
		fmt.Scanln(&this.key)
		switch this.key {
		case "1":
			this.ShowDetails()

		case "2":
			this.Income()
		case "3":
			this.Pay()
		case "4":
			this.Exit()

		default:
			fmt.Println("请输入正确的选项")

		}

	}
}

编写主函数

package main

import (
	"fmt"

	"github.com/mylib/utils"
)

func main() {
	fmt.Println("这个是面向对象的方式完成~~")
	utils.NewFamilyAccount().MainMenu()
}

文件操作

文件在程序中是以流的形式来操作的。

请添加图片描述

流:数据在数据源(文件)和程序(内存)之间经历的路径

输入流:数据从数据源(文件)到程序(内存)的路径

输出流:数据从程序(内存)到数据源(文件)的路径

常用的文件操作函数和方法

1)打开一个文件进行读操作

os.Open(name string) (*File,error)

函数:

请添加图片描述

2)关闭一个文件:

File.Close()

函数:

请添加图片描述

3)其它的函数和方法

读文件操作应用实例

package main

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

func main() {
	file, err := os.Open("文件路径")
	if err != nil {
		fmt.Println("open file err", err)
	}
	//当打开一个文件的时候 要及时关闭
	defer file.Close() // 防止内存泄露

	//创建一个Reader, 带缓冲
	reader := bufio.NewReader(file)
	for {
		str, err := reader.ReadString('\n') //读到一个换行就结束
		if err == io.EOF {                  //io.EOF表示读到文件的末尾
			break

		}
		//输出内容
		fmt.Print(str)
	}
	fmt.Println("文件读取结束")
}

读取文件内容

filePath := "filePath"
	content, err := os.ReadFile(filePath)
	if err != nil {
		fmt.Println("读取文件失败", err)
		return
	}
	fmt.Println("文件内容", string(content))

1)创建一个新文件,写入内容5句“ hello,tom“

2)打开一个存在的文件中,将原来的内容覆盖成新的内容10句 ”你好,tom!“

3)打开一个存在的文件中,将原来的内容追加内容“ABC“

4)打开一个存在的文件,将原来的内容读出显示在终端,并且追加5句 ”hello,tom!“

使用os.OpenFile(),bufio.NewWriter(),*Writer的方法WriterString完成上面的任务

第一个

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	//创建一个新文件,写入内容5句“ hello,tom“
	//1.打开文件
	filePath := "path"
	file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666) //只读,创建
	if err != nil {
		fmt.Printf("open file err= %v\n", err)
		return

	}
	//及时关闭文件,防止句柄泄露
	defer file.Close()
	str := "hello,tom\n"
	//写入时,使用带缓存的 *Writer
	writer := bufio.NewWriter(file)
	for i := 0; i < 5; i++ {
		writer.WriteString(str)
	}
	//因为writer是带缓存的,因此在调用WriterString方法时,其实内容是先写入到缓存的,所以需要调用Flush这个方法
	//将缓存的数据真正写入文件中,否则文件中会没有数据!
	writer.Flush()
}

第二个

file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC, 0666) // O_TRUNC 清空内容

第三个

file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND, 0666) // O_TRUNC 添加内容

第四个

package main

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

func main() {
	//创建一个新文件,写入内容5句“ hello,tom“
	//1.打开文件
	filePath := "path"
	//file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC, 0666) //清空 重新写
	//file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND, 0666) //末尾添加
	file, err := os.OpenFile(filePath, os.O_RDWR|os.O_APPEND, 0666) // 读写
	if err != nil {
		fmt.Printf("open file err= %v\n", err)
		return

	}
	//及时关闭文件,防止句柄泄露
	defer file.Close()
	//先读取原来文件的内容,并显示在终端
	reader := bufio.NewReader(file)
	for {
		str, err := reader.ReadString('\n')
		if err == io.EOF { //	如果读到文件末尾
			break

		}
		//继续
		fmt.Print(str)

	}
	//写入
	str := "hello world\n"
	//写入时,使用带缓存的 *Writer
	writer := bufio.NewWriter(file)
	for i := 0; i < 10; i++ {
		writer.WriteString(str)
	}
	//因为writer是带缓存的,因此在调用WriterString方法时,其实内容是先写入到缓存的,所以需要调用Flush这个方法
	//将缓存的数据真正写入文件中,否则文件中会没有数据!
	writer.Flush()
}

复制文件内容至另一文件

package main

import (
	"fmt"
	"os"
)

func main() {
	//将文件中的内容导入到 test2.txt中
	//首先将这个文件的内容读取到内存
	// 将读取到的内容 写入test2.txt
	fiel1Path := "path"
	file2Path := "path"
	content, err := os.ReadFile(fiel1Path)
	if err != nil {
		fmt.Println("读取文件错误:", err)
		return
	}
	err = os.WriteFile(file2Path, content, 0666)
	if err != nil {
		fmt.Println("write file err", err)
	}
}

判断文件是否存在

首先调用os.Stat()函数,并将文件路径作为参数传递给它。然后,通过检查返回的错误值来确定文件是否存在。

如果errnil,则表示文件存在。如果erros.ErrNotExist,则表示文件不存在。如果err为其他非空错误,则表示在判断文件是否存在时发生了一些错误。

请注意,os.Stat()函数还可以用于获取文件的其他属性,例如文件大小、修改时间等。您可以通过FileInfo结构体的相应方法来获取这些信息

go
package main

import (
	"fmt"
	"os"
)

func main() {
	filePath := "example.txt" // 文件路径

	_, err := os.Stat(filePath)
	if err == nil {
		fmt.Println("文件存在")
	} else if os.IsNotExist(err) {
		fmt.Println("文件不存在")
	} else {
		fmt.Println("发生错误:", err)
	}
}

拷贝文件

package main

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

// 自己编写一个函数,接受两个文件路径,srcFileName dstFileName
func CopeFile(dstFileName string, srcFileName string) (written int64, err error) {
	srcFile, err := os.Open(srcFileName)
	if err != nil {
		fmt.Printf("openfile err%v\n", err)
		return
	}
	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("openfile err%v\n", err)
		return
	}
	defer dstFile.Close()

	//通过dstFile,获取到Writer
	writer := bufio.NewWriter(dstFile)

	written, err = io.Copy(writer, reader)
	if err != nil {
		fmt.Printf("copy file err%v\n", err)
		return
	}

	err = writer.Flush()
	if err != nil {
		fmt.Printf("flush writer err%v\n", err)
		return
	}

	return written, nil
}

func main() {
	srcFile := "目标路径"
	dstFile := "原文件路径"

	_, err := CopeFile(dstFile, srcFile)
	if err == nil {
		fmt.Printf("拷贝完成\n")
	} else {
		fmt.Printf("拷贝错误 err=%v\n", err)
	}
}

命令行参数

请编写一段代码,可以获取命令行各个参数

func main() {
	fmt.Println("命令执行的参数有", len(os.Args))
	//遍历os.Args切片,就可以得到所有的命令行输入参数值
	for i, v := range os.Args {
		fmt.Printf("args[%v]=%v\n ", i, v)
	}
}

flag包解析命令行参数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值