前言

面向对象编程的核心思想包括分装、继承、多态和抽象。分装通过私有变量和访问控制提高数据安全性;继承减少代码冗余,通过父类子类关系实现;多态通过方法重载和重写实现同一方法不同实现;抽象通过抽象类和接口加强多态。仓颉和Java在面向对象编程思想上有诸多相似之处,但也有一些独特的概念如属性设计器,用于更灵活地控制数据访问。


一、仓颉流程控制

1.流程控制语句

if  else

while

for

do  while

switch case

二、仓颉面向对象

1.封装

对于面向对象来说,包含类中的数据的访问的安全性

封装对于代码来说(private),不能够直接让外部访问,提高了数据的访问安全性

public class Book {
    // 访问的可见性
    private var price: Float32 = 40.0
}
func main() -> Int64 {
    // 创建这个Book类对象
    var book: Book = Book()

    // 不让你直接访问,提高数据访问的安全性
   // println(book.price)//错误
    return 0
}

需要访问时,可以通过函数进行访问

// 函数,提供了访问的安全性的机制
public func getPrice(name: String) :String
 {
    if name.contains("管理员") 
    {
        return price.toString()
    } 
    else 
    {
        return "对不起,你的身份不符合访问的要求"
    }
}

也可以通过属性构造器访问

// 仓颉中,第二种方案 属性构造器
public mut prop propSalary: Int64 {
    get()

      {
        return salary
           }
    set(newSalary)

   {
        if (newSalary > 0 && newSalary < 20000)

       {
            this.salary = newSalary
        }

        else

        {
            println("该工资不能被系统认可,请确认...")
        }
    }
}

当仅获取时可以不用mut,存在设置时要用mut,并且使用mut时也要设置获取的get()

尽量保持属性和访问内容数据类型一致

public mut prop propSalary: Int64

2.继承

子类继承父类,作用是减少冗余重复的代码

但仓颉的继承是单继承不可以直接继承多个父类,降低程序的复杂度和维护

// 类之间有了重复的变量,也叫做属性,重复的函数,也就是方法
public class Manager {
    var name: String = ""
    var age: Int16 = 0
    
    public func eat() {
    }
}
public class Worker {
    var name: String = ""
    var age: Int16 = 0
    
    public func eat() {
    }
}

类与类之间存在重复部分,这是可以使用继承

// 在仓颉中父类需要用 open 关键字来表示,表示这个类可以被继承
public open class Person {
    var name: String = ""
    var age: Int16 = 0
    
    public func eat()

   {

     println(“都在吃饭”)
    }
}

// 继承 <:
class Manager <: Person

 {
 }

public func execPerson() {
    // 声明的是子类对象类型,实例化子类对象
    var m1: Manager  = Manager()
    m1.eat()

    // 有继承关系,p1是父类对象 = (指向) 实例化子类对象,动态绑定技术
    var p1: Person = Manager()
}

动态绑定通过容器获取具体对象

// 声明的是子类对象类型,实例化子类对象
var m1: Manager1 = Manager1()
//m1 = Worker1()  // 错误,因为这里不是动态绑定
m1.eat()       // 调用方法(动态绑定到实际对象类型)

// 有继承关系,p1是父类对象 = (指向) 实例化子类对象  动态绑定技术

//动态绑定只能存在于继承关系中
var p1: Person = Manager1()  // 父类引用指向Manager1子类对象
p1 = Worker1()               // 同一父类引用改为指向Worker1子类对象

子类构造函数会访问父类构造函数

//仓颉的继承关系中,子类的构造一定依赖于父类的构造

Son() {
    super() // 访问父类的构造函数
    println("4.子类Son的主构造函数")
}

值得注意的是,super()可以省略不写

书写后其中的参数也会带入到父类构造函数中

并且函数执行顺序中一定是静态函数优先被执行

顺序一般为

1.父类静态函数

2.子类静态函数

3.父类构造函数

4.子类构造函数

public open class Base {
    public open fun run(): Unit {
        println("1.爸爸喜欢慢跑")
    }
}

public class Son : Base() {
    // 在继承关系中,子类和父类具有相同的方法名,相同的参数列表,相同的返回值类型
    public override fun run(): Unit {
        println("2.儿子喜欢快跑")
    }
}

继承关系中的方法重写,子类和父类具有相同的方法名,相同的参数列表,相同的返回值类型

值得注意的是静态函数具有相同的方法名,相同的参数列表,相同的返回值类型,不是方法重写

同时存在关键字

父类函数需要有open关键字,子类函数需要有override关键字(可以省略)

// 动态绑定技术:一个父类绑定多个子类的现象  
// Base是个父类,是个大类型  
// 它有多个子类  
var f: Base  

f = Son()  
f = gin1()

继承中的动态绑定技术,只能是父类绑定多个子类

public func exec(f: Base) { // f对象可以动态绑定子类
    f.run()
}

public func execBase2() {
 
    // 运行
    exec(gin1())
}

由于可由父类动态绑定子类,实际运行时,exec()可填入不同子类函数,得到不同结果

根据用户的需求,可获取不同的对象,从而执行相对应函数,实现了运行时多态

代码级绑定,代码中写了具体的创建对象

访问修饰符的范围:private < default < protected < public

子类重写方法的访问修饰符必须大于或等于父类被重写方法的访问修饰符

父类中被重写的方法不能使用private修饰符(因为private方法不能被重写)

public fun execOne() {
    var base: Base
    // 绑定了儿子
    base = Son()
    
    // base是父类声明的对象,父类想调用子类的扩展的方法
    // base.playGame()  // 直接调用会报错,因为父类类型没有playGame方法
    
    // 父类转换子类:引用类型继承关系的转换
    var s1 = (base as Son).getOrThrow()
    s1.playGame()  // 转换后可以调用子类特有的方法
}

父类不能直接调用子类函数中扩展的方法,需要通过转换才可以调用

3.多态

同一方法名,不同的实现结果

编译时多态(方法重载)
在一个类中,方法名相同但参数列表不同,称为方法重载。调用哪个重载方法由参数列表决定。由于参数列表在编译期间即可确定,因此称为编译时多态。

public class Emp {
    public func eat() {
        println("一般吃正常的工作食品")
    }
    
    public func eat(manageName: String) {
        println("管理者吃丰富一点的食品")
    }
}

public func execEmp() {
    var emp: Emp = Emp()
    emp.eat()
}

运行时多态(方法重写)
在继承体系中,子类重写父类的方法(方法名、参数列表、返回值类型均相同)。调用哪个重写方法由实际创建的对象决定,而对象创建发生在运行期间,因此称为运行时多态。

public open fun run(): Unit {
    println("父类base的run方法-正常跑")
}

public class Manager : Emp() {
    public override fun run(): Unit {
        println("子类Manager的run方法-慢慢跑")
    }
}

// 方法重写,调用哪个具体的方法,是由创建的对象来决定的
var m1: Emp = Manager()  // 父类类型引用指向子类对象
m1.run()  // 实际调用的是子类Manager重写的run方法

一个类的组成:

同struct类型,

定义的变量是分为:静态变量和实例变量(可以用修饰符var 可变变量,修饰符let,不可变变量)

构造函数:

静态构造函数:只能有一个,初始化的是静态变量

主构造函数,名字和类名一致,只能由一个,在内部不能使用this()调用其它的构造函数

普通构造函数:可以有多个,必须参数不一样,名字是init(),在内部可以使用this()调用其它的构造函数

属性设计器 public mut prop 属性设计器名称:类型

函数:静态函数和实例函数 func

4.抽象

抽象类(abstract)与接口(interface)
抽象类和接口(完全抽象的类)的核心作用是为多态服务(主要是方法重写),尤其是增强运行时多态的能力。


总结

本次学习着重于封装和继承

封装是面向对象编程中的一种重要概念,指的是将数据和对数据的操作方法捆绑在一起,形成一个独立的单元。封装可以保护数据,防止外部直接访问,只能通过提供的接口方法来访问和操作数据。这样可以隐藏实现细节,提高代码的安全性和可维护性。在封装中,通过使用访问修饰符(如public、private、protected)来限制对对象的访问权限,同时提供公共方法供外部调用。通过封装,可以实现信息隐藏和代码复用,是面向对象编程中的重要特性之一。

继承是面向对象编程中一种机制,允许一个类(子类)从另一个类(父类)继承属性和行为。子类可以使用父类的属性和方法,同时也可以定义自己独有的属性和方法。继承可以提高代码的重用性,减少重复编码的工作量。在继承关系中,子类可以重写父类的方法以实现方法的定制化,也可以调用父类的方法。

但值得注意的所有操作的核心都是为了多态服务

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐