Golang 语言面向对象编程说明
1、Golang 也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说 Golang 支持面向对象编程特性是比较准确的。
2、Golang 没有类(class),Go 语言的结构体(struct)和其它编程语言的类(class)有同等的地位,可以理解 Golang 是基于 struct 来实现 OOP 特性的。
3、Golang 面向对象编程非常简洁,去掉了传统 OOP 语言的继承、方法重载、构造函数和析构函数、隐藏的 this 指针等等
4、Golang 仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它 OOP 语言不一样,比如继承 :Golang 没有 extends 关键字,继承是通过匿名字段来实现。
5、Golang 面向对象(OOP)很优雅,OOP 本身就是语言类型系统(type system)的一部分,通过接口(interface)关联,耦合性低,也非常灵活。在 Golang 中面向接口编程是非常重要的特性。

对上图的说明:
1、将一类事物的特性提取出来(比如猫类), 形成一个新的数据类型, 就是一个结构体。
2、通过这个结构体,我们可以创建多个变量(实例/对象)
3、事物可以猫类,也可以是 Person , Fish 或是某个工具类。。。
代码如下:
package main
import "fmt"
// 定义一个Cat结构体,将Cat的各个字段/属性信息,放入到Cat结构体中进行管理
type Cat struct {
Name string
Age int
Color string
Hobby string
}
func main() {
// 创建一个Cat的变量
var cat1 Cat
cat1.Name = "binglecat"
cat1.Age = 18
cat1.Color = "yellow"
cat1.Hobby = "eat and sleep"
fmt.Println("cat1 = ", cat1)
fmt.Println("小猫信息如下:")
fmt.Println("name is ", cat1.Name)
fmt.Println("age is ", cat1.Age)
fmt.Println("color is ", cat1.Color)
fmt.Println("hobby is ", cat1.Hobby)
}

结构体和结构体变量(实例)的区别和联系

如何声明结构体
基本语法:
type 结构体名称 struct {
field1 type
field2 type
}
举例:
type Cat struct {
Name string
Age int
Color string
Hobby string
}
基本介绍
1、从概念或叫法上看: 结构体字段 = 属性 = field
2、字段是结构体的一个组成部分,一般是基本数据类型、数组,也可是引用类型。比如我们前面定义猫结构体 的 Name string 就是属性
package main
import "fmt"
// 如果结构体的字段类型是: 指针,slice,和map的零值都是 nil ,即还没有分配空间
// 如果需要使用这样的字段,需要先make,才能使用.
type Person struct {
Name string
Age int
Scores [5]float64
ptr *int //指针
slice []int //切片
map1 map[string]string //map
}
func main() {
// 定义结构体变量
var p1 Person
fmt.Println(p1)
if p1.ptr == nil {
fmt.Println("ok1")
}
if p1.slice == nil {
fmt.Println("ok2")
}
if p1.map1 == nil {
fmt.Println("ok3")
}
//使用slice, 再次说明,一定要make
p1.slice = make([]int, 10)
p1.slice[0] = 100 //ok
//使用map, 一定要先make
p1.map1 = make(map[string]string)
p1.map1["key1"] = "bingle~"
fmt.Println(p1)
}
package main
import "fmt"
type Monster struct {
Name string
Age int
}
func main() {
//不同结构体变量的字段是独立,互不影响,一个结构体变量字段的更改,
//不影响另外一个, 结构体是值类型
var monster1 Monster
monster1.Name = "牛魔王"
monster1.Age = 500
monster2 := monster1 //结构体是值类型,默认为值拷贝
monster2.Name = "青牛精"
monster2.Age = 888
fmt.Println("monster1=", monster1) //monster1= {牛魔王 500}
fmt.Println("monster2=", monster2) //monster2= {青牛精 888}
}

var person Person
func main() {
var person Person
person.Name = "bingle"
person.Age = 18
fmt.Println("name is ",person.Name)
fmt.Println("age is ",person.Age)
}var person Person = Person{}func main() {
person := Person{
Name: "bingle",
Age: 18,
}
fmt.Println("name is ", person.Name)
fmt.Println("age is ", person.Age)
}var person *Person = new (Person)
func main() {
var person *Person= new(Person)
//因为person是一个指针,因此标准的给字段赋值方式
//(*person).Name = "bingle" 也可以这样写 person.Name = "bingle"
//原因: go的设计者 为了程序员使用方便,底层会对 person.Name = "bingle" 进行处理
//会给 person 加上 取值运算 (*person).Name = "bingle"
(*person).Name = "bingle"
person.Name = "bingle" //
(*person).Age = 30
person.Age = 100
fmt.Println(*person)
fmt.Println("name is ", person.Name)
fmt.Println("age is ", person.Age)
}var person *Person = &Person{}func main() {
//下面的语句,也可以直接给字符赋值
//var person *Person = &Person{"bingle", 60}
var person *Person = &Person{}
//因为person 是一个指针,因此标准的访问字段的方法
// (*person).Name = "bingle"
// go的设计者为了程序员使用方便,也可以 person.Name = "bingle"
// 原因和上面一样,底层会对 person.Name = "bingle" 进行处理, 会加上 (*person)
(*person).Name = "bingle"
person.Name = "bingle~~"
(*person).Age = 88
person.Age = 10
fmt.Println("name is ", person.Name)
fmt.Println("age is ", person.Age)
}package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
var person1 Person
person1.Age = 18
person1.Name = "bingle1111"
var person2 Person = person1
fmt.Println(person2.Age)
person2.Name = "bingle2222"
fmt.Printf("person2.Name is %v,person1.Name is %v", person2.Name, person1.Name)
}
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
var person1 Person
person1.Age = 18
person1.Name = "bingle1111"
var person2 *Person = &person1
fmt.Println(person2.Age)
person2.Name = "bingle2222"
fmt.Printf("person2.Name is %v,person1.Name is %v\n", person2.Name, person1.Name)
fmt.Printf("person1 的地址 = %p\n",&person1)
fmt.Printf("person2 的地址 = %p\n",&person2)
}

func main() {
var person1 Person
person1.Age = 18
person1.Name = "bingle1111"
var person2 *Person = &person1
fmt.Println(*person2.Age)
}package main
import "fmt"
// 结构体
type Point struct {
x int
y int
}
// 结构体
type Rect struct {
leftUp, rightDown Point
}
func main() {
r1 := Rect{
Point{1, 2},
Point{3, 4},
}
// r1有四个int, 在内存中是连续分布
// 打印地址
fmt.Printf("r1.leftUp.x 地址=%p r1.leftUp.y 地址=%p r1.rightDown.x 地址=%p r1.rightDown.y 地址=%p \n",
&r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y)
}package main
import "fmt"
// 结构体
type Point struct {
x int
y int
}
// 结构体
type Rect struct {
leftUp, rightDown Point
}
//结构体
type Rect2 struct {
leftUp, rightDown *Point
}
func main() {
r1 := Rect{
Point{1, 2},
Point{3, 4},
}
// r1有四个int, 在内存中是连续分布
// 打印地址
fmt.Printf("r1.leftUp.x 地址=%p r1.leftUp.y 地址=%p r1.rightDown.x 地址=%p r1.rightDown.y 地址=%p \n",
&r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y)
//r2有两个 *Point类型,这个两个*Point类型的本身地址也是连续的,
//但是他们指向的地址不一定是连续
r2 := Rect2{&Point{10,20}, &Point{30,40}}
//打印地址
fmt.Printf("r2.leftUp 本身地址=%p r2.rightDown 本身地址=%p \n",
&r2.leftUp, &r2.rightDown)
//他们指向的地址不一定是连续..., 这个要看系统在运行时是如何分配
fmt.Printf("r2.leftUp 指向地址=%p r2.rightDown 指向地址=%p \n",
r2.leftUp, r2.rightDown)
}package main
import "fmt"
type A struct {
Num int
}
type B struct {
Num int
}
func main() {
var a A
var b B
a = A(b) // ? 可以转换,但是有要求,就是结构体的的字段要完全一样(包括:名字、个数和类型!)
fmt.Println(a, b)
}
package main
import "fmt"
type Student struct {
Name string
Age int
}
type Stu Student
func main() {
var stu1 Student
var stu2 Stu
// stu2 = stu1// 这样写是错误的,可以修改为 stu2 =Stu(stu1)
stu2 =Stu(stu1)
fmt.Println(stu1,stu2)
}package main
import (
"encoding/json"
"fmt"
)
type Person struct {
Name string `json:"name"` // `json:"name"` 就是 struct tag
Age int `json:"age"`
}
func main() {
// 创建一个Person变量
person := Person{
Name: "bingle",
Age: 18,
}
// 将person变量序列化为 json格式字串
// json.Marshal 函数中使用反射
jsonStr, err := json.Marshal(person)
if err != nil {
fmt.Println("json处理错误,", err)
}
fmt.Println("jsonStr is ", string(jsonStr))
}
type A struct {
Num int
}
func (a A) test() {
fmt.Println(a.Num)
}package main
import "fmt"
type Person struct {
Name string
}
func (this Person) test() {
fmt.Println("person.Name is ", this.Name)
}
func main() {
var person Person
person.Name = "bingle"
person.test()
}func (this Person) speak() {
fmt.Println(this.Name, "是一个好人")
}
func main() {
var person Person
person.Name = "bingle"
person.speak()
}func (this Person) jisuan() {
res := 0
for i := 1; i <= 1000; i++ {
res += i
}
fmt.Println(this.Name, "计算的结果是=", res)
}func (this Person) jisuan2(n int) {
res := 0
for i := 1; i <= n; i++ {
res += i
}
fmt.Println(this.Name, "计算的结果是=", res)
}func main() {
var person Person
person.Name = "bingle"
person.test()
person.speak()
person.jisuan()
person.jisuan2(10)
}
func (this Person) getSum(n1, n2 int) int {
return n1 + n2
}
func main() {
var person Person
person.Name = "bingle"
n1 := 10
n2 := 20
res := person.getSum(n1, n2)
fmt.Println("res = ", res)
}
说明:
1、在通过一个变量去调用方法时,其调用机制和函数一样
2、不一样的地方时,变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,则进行值拷贝,如果变量是引用类型,则进行地质拷贝)
func (recevier type) methodName(参数列表) (返回值列表) {
方法体
return 返回值
}
func (this Person) showName() {
this.Name = "bingle2222"
fmt.Println("name in showName is ", this.Name)
}
func main() {
var person Person
person.Name = "bingle"
person.showName()
fmt.Println("name in main is ", person.Name)
}// 为了提高效率,通常我们方法和结构体的指针类型绑定
func (this *Person) showName() {
//因为 this 是指针,因此我们标准的访问其字段的方式是 (*this).Name
// (*this).Name 等价 this.Name
this.Name = "bingle2222"
fmt.Println("name in showName is ", this.Name)
}
func main() {
var person Person
person.Name = "bingle"
(&person).showName()
// 编译器底层做了优化 (&person).showName() 等价 person.showName()
// 因为编译器会自动的给加上 &c
//person.showName()
fmt.Println("name in main is ", person.Name)
}package main
import "fmt"
type integer int
func (i integer) print() {
fmt.Println("i=", i)
}
//编写一个方法,可以改变i的值
func (i *integer) change() {
*i = *i + 1
}
func main() {
var i integer = 10
i.print()
i.change()
fmt.Println("i=", i)
}
package main
import "fmt"
type Student struct {
Name string
Age int
}
//给*Student实现方法String()
func (stu *Student) String() string {
str := fmt.Sprintf("Name==[%v] Age==[%v]", stu.Name, stu.Age)
return str
}
func main() {
//定义一个Student变量
stu := Student{
Name: "tom",
Age: 20,
}
//如果你实现了 *Student 类型的 String方法,就会自动调用
fmt.Println(&stu)
}package main
import (
"fmt"
)
type Person struct {
Name string
}
//函数
//对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然
func test01(p Person) {
fmt.Println(p.Name)
}
func test02(p *Person) {
fmt.Println(p.Name)
}
//对于方法(如struct的方法),
//接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以
func (p Person) test03() {
p.Name = "bingle03"
fmt.Println("test03() =", p.Name) // bingle03
}
func (p *Person) test04() {
p.Name = "bingle04"
fmt.Println("test04() =", p.Name) // bingle04
}
func main() {
p := Person{"bingle"}
test01(p)
test02(&p)
p.test03()
fmt.Println("main() p.name=", p.Name) // bingle03
(&p).test03() // 从形式上是传入地址,但是本质仍然是值拷贝
fmt.Println("main() p.name=", p.Name) // bingle03
(&p).test04()
fmt.Println("main() p.name=", p.Name) // bingle04
p.test04() // 等价 (&p).test04 , 从形式上是传入值类型,但是本质仍然是地址拷贝
}
type Stu struct {
Name string
Age int
}
func main() {
//方式1
//在创建结构体变量时,就直接指定字段的值
var stu1 = Stu{"bingle1111", 18} // stu1---> 结构体数据空间
stu2 := Stu{"bingle2222~", 18}
//在创建结构体变量时,把字段名和字段值写在一起, 这种写法,就不依赖字段的定义顺序.
var stu3 = Stu{
Name :"bingle3333",
Age : 18,
}
stu4 := Stu{
Age : 18,
Name : "bingle4444",
}
fmt.Println(stu1, stu2, stu3, stu4)
}func main() {
//方式2, 返回结构体的指针类型(!!!)
var stu5 *Stu = &Stu{"bingle5555", 18} // stu5--> 地址 ---》 结构体数据[xxxx,xxx]
stu6 := &Stu{"bingle6666~", 18}
//在创建结构体指针变量时,把字段名和字段值写在一起, 这种写法,就不依赖字段的定义顺序.
var stu7 = &Stu{
Name : "bingle7777",
Age :18,
}
stu8 := &Stu{
Age :18,
Name : "bingle8888~",
}
fmt.Println(*stu5, *stu6, *stu7, *stu8) //
}
package model
type Student struct {
Name string
...
}
package model
// 定义一个结构体
type Student struct {
Name string
}package main
import (
"fmt"
model "go_code/test/models"
)
func main() {
stu :=model.Student{
Name: "bingle",
}
fmt.Println(stu)
}package model
type person struct {
Name string
}
// 因为student结构体首字母是小写,因此是只能在model使用
// 我们通过工厂模式来解决
func NewPerson(n string) *person {
return &person{
Name : n,
}
}package main
import (
"fmt"
model "go_code/test/models"
)
func main() {
person := model.NewPerson("bingle")
fmt.Println(person)
fmt.Println(*person)
}