ArkTs面向对象编程
1.1 面向对象编程概述
1.1.1 什么是面向对象编程
面向对象编程是一种编程范式,它使用“对象”来设计软件和创建可重用的程序设计
对象是包含数据和方法的实体,可以与其他对象进行交互
面相对象编程鼓励使用已有的对象来组合或修改以适应新的需求,而不是重新编写代码
打一比方来说,比如我们洗衣服这件事,面向过程就是你需要一步一步的去操作,比如先泡衣服,然后导入洗衣液,然后在一步步的去洗,而面向对象则是,你只需要准备好洗衣液,把衣服分好类,然后放进洗衣机里,到底如何去洗交给洗衣机这个对象,通过调用它的“方法”帮你实现洗衣服这个功能,你并不需要知道内部是如何帮你洗衣服的
1.1.2 面向对象编程的特点
- 封装
- 继承
- 多态
1.1.3 封装
封装:将数据属性和方法封装在一个对象中,形成一个独立的实体,将某些信息隐藏在类内部,通过访问修饰符(public、private、protected)控制成员变量的访问权限,并且封装还可以把相同功能的代码封装到一个类中
- 提高代码的安全性和可维护性
- 提高了代码的复用性
我们程序设计追求“高内聚,低耦合
高内聚:类的内部数据操作细节自己完成,不允许外部干涉
低耦合:仅对外部暴露少量的方法用于使用
通俗的说:封装就是把该隐藏的隐藏起来,该暴露的暴露出来,这就是封装的设计思想
1.1.4 案例练习
class Animal{
//封装动物特征属性
private name:string;
private age:number;
private species:string;
constructor(name:string,age:number,species:string) {
this.name=name
this.age=age
this.species=species
}
//封装动物行为的方法
public eat():string {
console.log(`${this.name} is eating`)
return `${this.name} is eating`
}
public getName() {
return this.name
}
public getAge() {
return this.age
}
public getSpecies() {
return this.species
}
public sleep():string {
console.log(`${this.name} is sleeping`)
return `${this.name} is sleeping`
}
}
class Cat extends Animal{
hobby:string
address:string
constructor(name:string,age:number,species:string,hobby:string,address:string) {
super(name,age,species)
this.hobby = hobby
this.address = address
}
swimming() {
console.log(`${this.getName} is swimming`)
}
running() {
console.log(`${this.getName()} is runing`)
}
}
let animal1 = new Animal("cat",18,"小橘猫")
console.log(animal1.getName())
console.log(animal1.sleep())
1.1.5 继承
- 继承:允许一个子类继承另一个父类的所有属性和方法,同时,使得子类可以复用父类的代码,提高代码的可重复性
-
继承的特点
- 使用extends关键字
- 相当于子类把父类的功能复制了一份
- ArkTs只支持单继承
- 继承可以传递(爷爷,儿子,孙子的关系)
- 不能继承父类的私有成员
- 继承多用于功能的修改,子类可以拥有父类的功能的同时,进行功能扩展
-
构造方法的使用
- 子类创建对象时,默认都会去访问父类的无参构造方法
- 在构造方法的第一行,都有一条默认的雨具:super()
- 父类没有无参构造时,可以用super调用父类的其他构造:super(参数列表)
-
继承的要求
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
- 子类重写的方法返回值类型不能大于父类被重写的方法返回类型
- 子类重写的方法使用权限不能小于被重写的方法的访问权限
- 子类方法抛出的异常不能大于父类方法被重写的异常
注意子类与父类中同名参数的方法必须同时声明为非static得,或者同时声明为static得
- 多态:允许使用不同的对象对同一消息做出不同的相应,提高代码的可重用性
-
多态是指子类可以重写父类的方法,使得在调用时可以根据实际对象类型执行相应的方法
方法重写:子类提供与父类同名但参数列表不同的方法,称之为方法重写
-
多态的好处:提高代码的灵活性和可扩展性,方便程序的维护和修改
1.1.6 案例练习
class Animal {
name:string
age:number
species:string
//构造方法
constructor(name:string,age:number,species:string) {
this.name=name;
this.age=age;
this.species=species;
}
bark() {
console.log(`它叫${this.name},今年${this.age},它的品种是${this.species},`)
}
}
class Cat extends Animal{
hobby:string
snake:string
constructor(name:string,age:number,species:string,hobby:string,snake:string) {
super(name,age,species)
this.hobby = hobby
this.snake = snake
}
}
let cat1 = new Cat("花花",13,"小橘猫","玩毛球","fish")
cat1.bark()
1.1.7 面向对象编程的优点
- 提高代码可重用性:通过继承和多态,可以重用已有的代码,减少重复工作
- 易于维护和扩展:面向对象编程使得代码结构变得更清晰,更容易理解和维护。同时,也更容易扩展功能和添加新特性
- 提高软件可扩展性:面相对象编程使得软件更容易扩展和适应新的需求,因为可以通过添加新的类或对象来扩展功能
2.1 类与对象
- 类(class):类是对象的抽象描述,它定义了对象的属性(成员变量)和方法(成员函数)
类的声明使用class关键字,类名与变量、函数等命名规则类似,这里要首写字母大写,类名后跟上一对花括号可以理解为类的主体,类的主题三部分组成:成员属性、构造函数、成员方法
class 类名 {
成员属性:在类中可以直接声明变量,也称为成员属性,另外在类中声明成员属性我们还可以使用关键字 private、public、protected来修饰
构造方法:主要用于在创建对象时初始化对象、即对对象的成员变量进行赋值,并在创建对象的语句中与“new"运算符一起用
在类中直接声明函数称为成员方法,注意这里函数时不需要加function关键词,成员方法要和对象有关联,例如eat方法(每个人都需要吃饭的)另外方法也可以使用public、private、protected等关键词声明
}
代码实例
- 定义了一个Person类,类包含的结构,成员属性:年龄、名字;构造函数constructor();成员方法;公有的etc方法
class Person {
//成员属性
name:string
age:number
//构造函数进行对成员属性初始化
constructor(name:string,age:number) {
this.name=name
this.age=age
}
sayHi():string {
return `大家好,我叫`+this.name+",我现在"+this.age+"岁了"
}
}
//对比Java: Person p1 = new Person("小程",20)
let p1=new Person("小程",20)
console.log(p1.name)
console.log(p1.sayHi())
- 对象(Object):对象是类的实例,它具有类所定义的属性和方法
- 实例化:通过类创建对象的过程称为实例化
2.1.1 如何实例化对象
类对象包含的是类的所有成员对象和类的实例化过程。这些成员对象可以是实例变量、实例方法、类变量和类方法等,它们构成了类对象的主体方法等,它们构成了类对象的主体部分,是类的具体实现。类对象还包含类的属性、关系和实例化过程等,这些内容共同构成了类的完整结构和功能
let 对象名 = new 类名()
let person =new Person()
没有对象,就可以使用new关键字new一个出来
2.1.2 成员访问修饰符
成员属性和成员函数都可以用关键词private、public、protected来修饰
- public:声明的属性/函数具有公有权限,在类的外部是可以被访问的,public这个关键词默认是可以不用写的
- private:声明的属性/函数具有私有权限,仅在当前类的内部可以被访问
class Person {
//成员属性
name:string
age:number
private sex:string
//构造函数进行对成员属性初始化
constructor(name:string,age:number,sex:string) {
this.name=name
this.age=age
this.sex='男'
}
sayHi():string {
return `大家好,我叫`+this.name+",我现在"+this.age+"岁了"
}
// public getSex():string {
// return this.sex
// }
}
let p1=new Person("小程",20,"")
console.log(p1.name)
console.log(p1.sayHi())
提供一个set方法,把sex属性返回出去
public getSex():string {
return this.sex
}
console.log(p1.getSex())
- Protected:protected 成员对于该类及其子类是可见的,但对于该类的实例或外部代码是不可见的。这意味着子类可以访问和修改 protected 成员,但类外部的代码不能。声明的属性/函数具有保护权限,在类的派生内部可以被访问
class MyClass {
protected myProperty: string;
constructor() {
this.myProperty = "Hello, protected!";
}
}
class MySubClass extends MyClass {
public displayProperty() {
console.log(this.myProperty); // 正确:'MySubClass' 可以访问 'MyClass' 中的 'protected' 成员
}
}
const obj = new MyClass();
console.log(obj.myProperty); // 错误:'myProperty' 是受保护的,不能在类外部访问
const subObj = new MySubClass();
subObj.displayProperty(); // 正确:'MySubClass' 的方法内部可以访问 'protected' 成员
访问权限的总结
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | √ | × | × | × |
protected | √ | × | √ | × |
public | √ | √ | √ | √ |
2.1.3 案例练习
案例练习一:创建Person类,有四个属性和方法,分别有一个私有和受保护
class Person {
private name:string
protected age:number
Studentclass:string
sex:string
constructor(name:string,age:number,Studentclass:string,sex:string) {
this.name=name
this.age=age
this.Studentclass=Studentclass
this.sex=sex
}
public getName(): string{
return this.name
}
public getAge():number{
return this.age
}
private eating():string{
return "I am eating snack"
}
protected drinking():string{
return "I am drinking juice"
}
playing():void{
console.log("I am playing baseball")
}
joying():void{
console.log("I am feeled very excited")
}
public getPlaying():string{
return this.eating()
}
public getEating():string{
return this.drinking()
}
}
let student1=new Person("小张",13,"软件一班","女")
console.log(student1.getName()+"今年"+student1.getAge()+"岁了,我的班级是"+student1.Studentclass+",我的性别是"+student1.sex)
console.log(student1.getEating())
console.log(student1.getEating())
student1.playing()
student1.joying()
案例练习2:创建Person类,有四个属性和方法,分别有一个私有和受保护
class Animal {
breed:string
age:number
private name:string
protected sex:string
constructor(breed:string,age:number,name:string,sex:string) {
this.breed=breed
this.age=age
this.name=name
this.sex=sex
}
public getName(): string{
return this.name
}
public getSex():string{
return this.sex
}
private swimming():string{
return "I am eating snack"
}
protected walking():string{
return "I am drinking juice"
}
running():void{
console.log("I am running")
}
jumping():void{
console.log("I am jumping")
}
public Getswimming():string{
return this.swimming()
}
public Getwalking():string{
return this.walking()
}
}
let Cat= new Animal("小橘猫",3,"球球","母")
let Cat= new Animal("小橘猫",3,"球球","母")
console.log(Cat.getName()+"今年"+Cat.age+"岁了,我的品种是"+Cat.breed+",我的性别是"+Cat.getSex())
console.log(Cat.Getswimming())
console.log(Cat.Getwalking())
Cat.running()
Cat.jumping()
3.1 接口与抽象
3.1.2 接口概念
接口的作用:在面向对象的编程中,接口是种规范的定义, 它定义了行为和动作的规范, 在程序设计里面,接口起 到一种限制和规范的作用。接口定义了某-批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方,提供这些方法的类就可以满足实际需要。
ArkTs接口(interface)定义是一种在HarmonyOs系统中用于定义对象之间交互规则的方式。通过使用ArkTS接口,开发者可以明确的规范一个类必须实现的方法和属性,从而确保代码的健壮性和可维护性
3.1.2 ArkTs接口定义的核心概念
方法定义:在ArkTs接口中,你可以定义一个或多个方法的签名,包括方法名、参数列表和返回类型
interface MyInterface {
myMethod(Parameter:number):void
}
属性定义:除了方法之外,ArkTs接口中还可以定义属性
interface MyInterface {
myProperty:number;
}
实现接口:一旦你定义了一个接口,你可以让类来实现该接口,一个类实现一个接口意味着该必须提供接口中定义的所有方法的具体实现
class MyClass implements MyInterface {
myMethod(param:number) {
console.log(`Called myMethid with param ${param}`);
}
}
3.1.3 ArkTS接口操作
-
定义接口interface,实现接口用implements,以下是定义接口和实现接口的用例
-
定义Style接口,包含接口属性,方法
interface Style {
colorValue:string //属性
color(x:stirng) //方法
}
- 定义StyledRectangle类,实现Style接口方法
class StyledRectangle implements Style {
private colorValue:string = ""
color(x:string)
this.color = x
}
案例练习
interface StyleC {
colorValue:string
color(c:string):void//方法
}
interface ExtendedStyle extends StyleC {
setWidth(x:number):void //设置宽度
//获取宽度
getWidth():string
}
//接口实现
class TestStyle implements ExtendedStyle{
colorValue: string
mWidth:number
constructor(colorValue:string,_Width:number) {
this.colorValue=colorValue;
this.mWidth=_Width
}
color(c:string):void {
// this.colorValue = c
console.log("嘻嘻嘻嘻嘻")
}
setWidth(x: number): void {
// this.mWidth = x
console.log("哈哈哈哈哈")
}
getWidth():string{
return '宽度为'+this.mWidth+",颜色为"+this.colorValue
}
}
//创建实例化对象
let Style = new TestStyle('blue',500)
console.log(Style.getWidth())
3.1.4 ArkTs抽象类概述
我们知道,类用来模拟现实事物。一个类可以模拟一类事物,而某个类的一个实例化对象可以模拟某个属于该类的具体的事物。类中描绘了该类所有对象共同的特性,当一个类中给出的信息足够全面时,我们就可以实例化该类;比方说,在Dog类中定义了name,age,等属性,以及play,eat等行为时,我们就可以创建一个Dog类对象,来模拟某个具体的Dog,比如你家的宠物狗,或者是神犬小七等。但是,当一个类中给出的信息不够全面时,(比方说有无法确定的行为),它给出的信息不足以描绘出一个具体的对象,这时我们往往不会实例化该类,这种类就是抽象类。打个比方,对于Animal类,是,所有的动物都有吃喝的行为,定义eat方法可以描述某一种动物“吃”这一行为,但是每种动物吃的都不一样,因此一个eat方法并不能准确描述吃什么,怎么吃。这时Animal给出的信息就不足够描述一个对象,我们就不能去实例化Animal类。如下面代码演示
- 首先定义一个抽象类
abstract class Animal {
abstract makeSound():void
abstract move():void
baseInfo() :void{
console.log("我是一只动物")
}
}
- 实现这个抽象类(可以是任意动物)
class Dog extends Animal{
makeSound(): void {
console.log("小狗在汪汪叫");
}
move(): void {
console.log("小狗再跑");
}
}
class Cat extends Animal{
makeSound(): void {
console.log("小猫在汪汪叫");
}
move(): void {
console.log("小猫再跑");
}
}
3.1.5 多态的定义
多态是什么?
- 多态是同一个行为具有不同的表现形式或形态的能力
- 同一方法可以根据发送对象的不同而采用不同的行为方式
比如:打印机分为黑白打印机和彩色打印机,在黑白打印机情况下打出来为黑白,在彩色打印机的情况下打印出来为彩色
多态就是事务的多种形态,一个对象在不同条件下所表现的形式
3.1.6 多态的存在条件
多态存在的三个必要条件
- 继承或实现:在多态中必须存在有继承或实现关系的子类和父类
- 方法的重写:子类对父类中的某些方法进行重新定义
- 基类引用指向派生类对象,即父类引用子类对象
- 方法的重写
重写(override):也称覆盖。重写是子类对父类非静态、非private修饰,非构造方法等的实现过程进行重新编写,返回值和形参都不能改变。重写的目的也就是根据自己的需要,定义特定于自己的行为
-
重写的条件
- 参数列表相同
- 方法名相同
- 返回类型相同
-
重写和重载的区别
区别 重写 重载 参数列表 一定不能被修改 必须修改 返回类型 一定不能修改除非可以构成父子关系 可以修改 访问修饰符 一定不能修改更严格的限制 可以修改
3.1.7 案例练习
- 类实现多态
子类可以继承父类的属性和方法,并且可以重写父类的方法
//类实现多态
class Animal {
name:string;
constructor(name:string) {
this.name=name;
}
speak():string{
console.log(`${this.name} is speaking`);
return `${this.name} is speaking`
}
}
class Dog extends Animal {
//重写父类的方法
speak(): string {
console.log(`${this.name} is barks and wags its tail`)
return`${this.name} barks and wags its tail`
}
}
let dog = new Dog("花花")
dog.speak()
class Person {
name:string
constructor(name:string) {
this.name = name
}
speak() {
console.log(`${this.name}在发言`)
}
}
let person1 = new Person("小红")
class Student extends Person{
speak(): string {
console.log(`${this.name}使用英语对自己进行发言`)
return `${this.name}使用英语对自己进行发言`
}
}
class App{
message:string
constructor() {
this.message=""//没有传值
}
aboutToAppear() {
//创建实例化对象,并调用实例对象的方法
let s1 = new Student("小张")
this.message = s1.speak()
}
}
let app1 =new App()
app1.aboutToAppear()
console.log(app1.message)
- 接口实现多态
interface ISspeakable {
speak():string;//不提供方法
}
class Animal implements ISspeakable {
name:string;
constructor(name:string) {
this.name=name
}
speak(): string {
console.log(`${this.name} makes a noise`)
return `${this.name} make a noise`
}
}
let animal = new Animal("花花")
animal.speak()
interface ISpeakable {
speak(): string;//定义了有speak方法
}
//定义了一个动物类,实现了ISpeakable
class Animal implements ISpeakable {
name: string;
message: string
constructor(name: string, message: string) {
this.name = name
this.message = message
}
speak(): string {
console.log(`${this.name} make noise`)
return `${this.name} make noise.\n`
}
}
class App {
message:string
constructor() {
this.message=""
}
aboutToAppear() {
const animal = new Animal("小猫咪","喵喵喵")
this.message = animal.speak()
}
}
let app =new App()
app.aboutToAppear()
console.log(app.message)