- 面向对象的操作使用编程语言实现就是叫做面向对象编程Object-Oriented Programming
在编程语言中一般使用class来实现面向对象,但并不是说没有class就不能或者不是面向对象
Go中使用结构体struct来代替类class对对象的描述
- 可以实现OOP的编程语言叫做面向对象语言
- 实现面向对象这一过程叫做面向对象过程
语法层面上讲就是将一组相关的数据变量(属性)和操作函数,整合到一个对象。
package main
import "fmt"
//Go中没有class,一般使用struct代替实现oop
//对象的声明 定义就是类
type Computer struct { //OOP对象的声明
name string
price int
module []string
}
//this *value这里是使用了一个接收器和对象进行关联
//*的目的是为了指明这是一个Computer类型的指针,后面的实例化使用&也是为了取其内存地址
//因为在OOP中,推荐使用指针的引用类型来进行传递,否则默认将会是值传递。就不满足面向对象的思想了
func (this *Computer)GetComType(a bool)string{ //OOP对象的方法
if a {
return this.name+ "--台式机"
} else {
return this.name+ "--便携机"
}
}
//函数和方法是一个东西,只不过带有接收器的函数一般被叫做方法,一般用来处理oop中的对象,由对象调用
func New(n string, p int, m []string)(*Computer, error){
//可以实例化对象,并返回的叫做构造函数
//一般构造函数内用来完成初始化操作和实例化对象
obj := new(Computer)
obj.name = n
obj.price = p
obj.module = m
return obj, nil
}
func main() {
//实例化
//:= &Computer这种带有字面量语法的实例方式必须赋其值,不常用。
PC1 := &Computer{ //OOP对象的实例化
name: "surface pro3",
price: 2999,
module: []string{"CPU", "RAM", "ROM"},
}
//常用的都是使用new来实例化对象
PC2 := new(Computer) //实例化初始,默认所有值都为零值
PC2.name = "Mac book pro"
PC2.price = 4999
//也可以使用函数来实例化,用来实例化的函数叫做构造函数,他没有特殊的语法,就是一种普通函数
PC3, err := New("Web-server", 15000, []string{"CPU", "ROM", "RAM"})
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("PC3 name:%v \n%v\n", PC3.GetComType(true), PC3)
}
fmt.Printf("PC1 name:%v \n", PC1.GetComType(false))
fmt.Printf("PC1 price:%v \n", PC1.price)
//成员的访问,属性和方法
fmt.Printf("PC2 name:%v \n", PC2.GetComType(false))
fmt.Printf("PC2 price:%v \n", PC2.price)
}
输出:
PC3 name:Web-server--台式机
&{Web-server 15000 [CPU ROM RAM]}
PC1 name:surface pro3--便携机
PC1 price:2999
PC2 name:Mac book pro--便携机
PC2 price:4999
Process finished with exit code 0
指针传递
分别试验:构造函数是否使用指针和对象方法是否使用指针
代码:
package main
import "fmt"
//对于指针引用类型和值拷贝类型的练习
type article struct {
name string
price int
}
//对象的定义
func (a *article)zz(n string)string{
return a.name+ "---"+ n
}
//对象的方法
func (a article)zz1(n string)string{
return a.name+ "---"+ n
}
//如果不使用指针引用类型
func New(n string, p int)(*article, error){
obj := new(article)
obj.name = n
obj.price = p
return obj, nil
}
//对象实例化
func New1(n string, p int)(article, error){
var obj article //new返回的是一个指针类型,这里使用var初始化
obj.name = n
obj.price = p
return obj, nil
}
//不使用指针引用类型
func main() {
wp1, err := New("水杯", 10)
if err != nil {panic(err)}
fmt.Printf("物品:%v\n%v\n", wp1.zz("china"), wp1)
//标准流程(都使用引用类型)
wp2, err := New1("铅笔", 1)
if err != nil {panic(err)}
fmt.Printf("物品:%v\n%v\n", wp2.zz("china"), wp2)
//实例化不使用引用类型
wp3, err := New1("电池", 4)
if err != nil {panic(err)}
fmt.Printf("物品:%v\n%v\n", wp3.zz1("china"), wp3)
//对象方法和实例化都不使用引用类型
wp4, err := New("本子", 2)
if err != nil {panic(err)}
fmt.Printf("物品:%v\n%v\n", wp4.zz1("china"), wp4)
//对象方法不使用引用类型
}
输出:
物品:水杯---china
&{水杯 10}
物品:铅笔---china
{铅笔 1}
物品:电池---china
{电池 4}
物品:本子---china
&{本子 2}
Process finished with exit code 0
结果如下表:
仅进行一次流程初始化
指针试验 | 构造函数* | 构造函数 |
---|---|---|
对象方法* | 实例化的对象值可以同步 | 实例化的对象值正常输出,但是构建函数返回的是值拷贝 |
对象方法 | 也是正常输出,但是方法返回的是值拷贝的数据,会增加内存占用 | 实例化的值和方法返回的内容都正常,不过都是值拷贝。 |
结论:
在第一次初始化操作时,是否使用指针传递并不会产生不一样的输出结果。但是因为是值拷贝 ,值传递相对来说会多占用内存空间。
继承与重写
package main
import "fmt"
//OOP 继承,用go的实现方式是结构体嵌套
type headset struct {
name string
price int
}
//对象的定义
type wireless struct {
headset
//嵌套了ej的对象属性
yc int
}
//典型的嵌套方法
type wired struct {
ej headset
//增加了一个headset类型的ej成员变量
jk string
}
//另一种嵌套方法
func (a *headset)manufacturer(n string)string{
return a.name + "---" + n
}
//对象的方法
//继承-重写
type headphones struct {
headset
price string
//如果出现和父结构体成员变量相同时,优先会输出子值。这个过程叫做重写 见:54行
jz int
}
//同样如果父结构体中有关联的对象方法,和子类冲突时,会优先使用子类的方法。这个过程也是重写 见:57行
func (a *headphones)manufacturer(n string)string{
return a.name + "制造商:"+ n
}
type lyyx struct {
headset
wireless
//这种使用俩个结构体以上的嵌套叫做 多嵌套
yl int
}
//但是多嵌套如果要重写也是遵循子类最优先,但是!如果想多重写,不允许出现在headset和wireless类中都有个相同的成员变量,编译器将会报错。
type A struct {
}
type B struct {
A
}
type C struct {
B
}
//这种像链式的嵌套称为嵌套链,嵌套链的多重写,允许不同层级的成员变量名相同,最终以子类优先。
//链式嵌套可以和多嵌套同时出现,也是遵循相应规则。
//嵌套链不允许产生循环结构 比如 C嵌套B,B嵌套A,A最后嵌套C,这种编译器也会报错!
func main() {
//实例化
akg := new(wired)
hw := new(wireless)
//当使用字面量语法实例化嵌套的结构体时,如果要实例化嵌套内的成员变量必须使用下面这种方式。
xm := &wireless{
headset: headset{"小米", 998},
yc: 250,
}
sx := new(headphones)
sx.price = "390"
fmt.Printf("%v %T", sx.price, sx.price) //重写后会输出子类中的price
fmt.Println(sx.manufacturer("shenzhen"))
fmt.Println(xm)
fmt.Println(hw.price)
//访问对象继承属性的典型方式,和下面那种方法没什么本质区别 只是go提供的一种语法糖
fmt.Println(akg.ej.price)
//标准访问继承对象属性的方式,但是如果是使用在子结构体中新加一个父结构体类型的成员变量,就必须使用这种方式访问,上面那种语法糖就不支持了。
}
输出:
390 string制造商:shenzhen
&{{小米 998} 250}
0
0
Process finished with exit code 0