目录
一、概述
1、结构体
1.1、创建结构体变量和访问结构体变量
1.2、结构体的注意事项和使用细节
1.3、创建结构体变量时指定字段值
1.4、工厂模式
1.5、抽象
1.6、面向对象编程三大特性
2、方法
2.1、方法注意事项和细节讨论
3、接口(interface)
3.1、注意事项和细节
3.2、接口排序
3.3、接口VS继承
一个程序就是一个世界,有很多对象(变量)
package main
import (
"fmt"
)
type Cat struct {
Name string
Age int
Color string
}
func main(){
var cat1 Cat
cat1.Name="小白"
cat1.Age=3
cat1.Color="白色"
fmt.Println("cat1=",cat1)
fmt.Println("cat1 Name=",cat1.Name)
}
结构体是自定义的数据类型,代表一类事物
声明结构体
type 结构体名称 struct {
field1 type
field2 type
}
例:
type Cat struct {
Name string
Age int
Color string
}
package main
import (
"fmt"
)
func main(){
var p1 Person
fmt.Println("p1=",p1)
p1.Name="小明"
p1.Age=10
p1.slice= make([]int,10)
p1.map1 = make(map[string]string)
p1.map1["k1"] = "v1"
fmt.Println("p1=",p1)
}
type Person struct {
Name string
Age int
map1 map[string]string
slice []int
ptr *int //指针
}
方式1:
var person Person
方式2:
var person Person = Person{}
例:
package main
import (
"fmt"
)
type Person struct {
Name string
Age int
}
func main(){
p := Person{"mary",20}
fmt.Println(p)
}
方式3:
var person *Person = new(Person)
(*person).Name="tom" //等价 person.Name="tom" 因为go编译器底层对person.Name做了转化(*person).Name
(*person).Age=30
fmt.Println(*person)
方式4:
var person *Person = &Person{}//var person *Person = &Person{"tom",60}
(*person).Name="tom" //等价 person.Name="tom" 因为go编译器底层对person.Name做了转化(*person).Name
person.Name="tom--"
(*person).Age=30
person.Age=10
fmt.Println(*person)
说明:
1.第三种和第四种方式返回的是 结构体指针。
2.结构体指针访问字段的标准方式应该是:(*结构体指针).字段名 如:(*person).Name ="tom"
3.但go做了一个简化,也支持结构体指针.字段名,比如 person.Name = "tom" 。
更加符合程序员使用的习惯,go编译器底层对person.Name做了转化(*person).Name
请问可以这样写吗?
*person.Name:不可以,会报错。因为.的运算符优先级比*高。
package main
import (
"fmt"
)
type A struct {
Num int
}
type B struct {
Num int
}
func main(){
var a A
var b B
b = B(a)
fmt.Println(b)
}
import "encoding/json"
func Marshal(v interface{}) ([]byte, error):Marshal函数返回v的json编码。
======================================================
package main
import (
"fmt"
"encoding/json"
)
type C struct {
Name string `json:"name"` //`json:"name"`结构体标签
Age int `json:"age"`
Skill string `json:"skill"`
}
func main(){
c :=C{"孙悟空",1000,"金箍棒"}
cJson,err :=json.Marshal(c) //json.Marshal函数中使用到反射,把tag标签json名称替换掉本来的属性名
if err !=nil{
fmt.Println("err=",err)
}
fmt.Println("cJson",string(cJson))
}
Golang在创建结构体实例(变量)时,可以直接指定字段的值。
package main
import (
"fmt"
)
type Student struct {
Name string
Age int
}
func main(){
var stu1 Student=Student{"tom",10}
stu2 := Student{"tom",20}
var stu3 Student=Student{
Name:"tom",
Age:30,
}
stu4 :=Student{
Name:"tom",
Age:40,
}
var stu5 *Student=&Student{"stu5",50}
var stu6 *Student=&Student{
Name:"tom",
Age:60,
}
fmt.Println(stu1)
fmt.Println(stu2)
fmt.Println(stu3)
fmt.Println(stu4)
fmt.Println(stu5)
fmt.Println(stu6)
}
Golang的结构体没有构造函数,通常可以使用工厂模式来解决这个问题。
使用工厂模式实现跨包创建结构体实例(变量)
package model
import (
_ "fmt"
)
type student struct {
Name string
Score float64
}
func NewStudent(n string,s float64) *student {
return &student{
Name : n,
Score : s,
}
}
================================
package main
import (
"fmt"
"model" //model包下的代码我整体拷贝到GO安装环境目录下面,可调用
)
func main(){
var stu = model.NewStudent("张无忌",98.0)
fmt.Println(stu) //&{张无忌 98}
}
//如果Score改为小写开头score,则在其他包不可以直接访问 //提供一个对外的方法
package model
import (
_ "fmt"
)
type student struct {
Name string
score float64
}
func NewStudent(n string,s float64) *student {
return &student{
Name : n,
score : s,
}
}
//如果Score改为小写开头score,则在其他包不可以直接访问
//提供一个对外的方法
func (s *student) GetScore() float64 {
return s.score
}
-----------------
package main
import (
"fmt"
"model"
)
func main(){
var stu = model.NewStudent("张无忌",98.0)
fmt.Println(stu.GetScore())//98
}
定义一个结构体的时候,实际上就是把一类事物的共有的属性(字段)和行为(方法)提取出来,形成一个物理模型(模板)。这种研究问题的方法称为抽象。
Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现方式和其他OOP语言不一样。
封装
封装(encapsulation)就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其他包只有通过被授权的操作(方法),才能对字段进行操作。对电视机的操作就是典型的封装。
封装的理解和好处:
如何体现封装:
封装的实现步骤
func (var 结构体类型名) SetXxx(参数列表) (返回值列表){
//添加验证逻辑
var.字段=参数
}
func (var 结构体类型名) GetXxx() {
return var.字段
}
继承
继承可以解决代码复用,让我们的编程更加靠近人类思维
当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体,在该结构体中定义这些相同的属性和方法。
其他的结构体不需要重新定义这些属性和方法,只需要嵌套一个共通定义的匿名结构体即可。
也就是说,在Golang中,如果一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承特性。
type Goods struct{
Name string
Price int
}
type Book struct{
Goods //这里就是嵌套匿名结构体Goods
Write string
}
var book Book
book.Goods.Name="tom"
↓
book.Name="tom"
多态
变量(实例)具有多种形态。面向对象的第三大特征,在Go语言,多态特征是通过接口实现的。可以按照统一的接口来调用不同的实现。这时接口变量就呈现不同的形态。
参考下面接口中的Usb案例
类型断言
类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要使用类型断言。
package main
import (
"fmt"
)
func main(){
var t float32
var x interface{}
x=t
_,ok :=x.(float32)
if ok ==true{
fmt.Println("success")
}else{
fmt.Println("fail")
}
}
Golang中的方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅是struct。
方法的声明和调用
func (变量名 type) methodName(参数列表) (返回值列表) {
方法体
return 返回值
}
1.变量名 type:表示这个方法和type这个类型进行绑定,或者该方法作用于type类型。
2.返回值列表:表示返回的值,可以多个
3.return 语句不是必须的
type A struct {
Num int
}
func (a A) test(){
fmt.Println(a.Num)
}
例:
var t A
t.test()
方法的调用和传参机制和函数基本一样,不一样的地方是方法调用时,会将调用方法的变量,当作实参也传递给方法。
package main
import (
"fmt"
)
type Circle struct {
radius float64
}
func (c Circle) area() float64 {
return 3.14 * c.radius * c.radius
}
func main(){
var c Circle
c.radius =2.0
res := c.area()
fmt.Println("res=",res)
}
方法和函数的区别
USB插槽就是现实中的接口。
Golang中 多态特性主要是通过接口来体现的。
interface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量。到某个自定义类型要使用的时候,再根据具体情况把这些方法写出来。
基本语法:
type 接口名 interface{
method1(参数列表) 返回值列表
method2(参数列表) 返回值列表
...
}
package main
import (
"fmt"
)
type Usb interface {
Start()
Stop()
}
type Phone struct {
}
func (phone Phone) Start(){
fmt.Println("手机开始工作...")
}
func (phone Phone) Stop(){
fmt.Println("手机停止工作...")
}
type Camera struct {
}
func (camera Camera) Start(){
fmt.Println("相机开始工作...")
}
func (camera Camera) Stop(){
fmt.Println("相机停止工作...")
}
type Computer struct {
}
//实现了Usb接口就是指实现了Usb接口声明的所有方法
func (computer Computer) working(usb Usb){
usb.Start()
usb.Stop()
}
func main(){
computer :=Computer{}
phone :=Phone{}
camera := Camera{}
computer.working(phone)
computer.working(camera)
}
import "sort"
func Sort(data Interface):Sort排序data,需要实现接口下的三个方法
type Interface interface {
// Len方法返回集合中的元素个数
Len() int
// Less方法报告索引i的元素是否比索引j的元素小
Less(i, j int) bool
// Swap方法交换索引i和j的两个元素
Swap(i, j int)
}
=========================================
package main
import (
"fmt"
"sort"
)
func main(){
var intSlice = []int{0,-1,10,7,90,23}
sort.Ints(intSlice)
fmt.Println(intSlice)
}
干我们这行,啥时候懈怠,就意味着长进的停止,长进的停止就意味着被淘汰,只能往前冲,直到凤凰涅槃的一天!