散修带你入门鸿蒙应用开发基础第十六节:静态成员与单例模式——商品全局管控
ArkTS基础第十六节:静态成员与单例模式——商品全局管控
炼气十六重天
【学习目标】
- 理解静态成员(
static)的核心价值(类级共享、全局管控),掌握ArkTS中静态属性、静态方法的定义与调用规范,实现商品ID全局自增、全品类商品总数统计; - 掌握
readonly只读修饰符的使用规则,理解其与const的核心区别,为商品ID、创建时间等核心字段添加只读约束,杜绝非法修改; - 掌握单例模式的实现逻辑(私有构造+静态
getInstance方法),基于泛型与抽象类实现全局唯一的商品管理器,统一管控跨模块商品数据; - 实现静态成员、
readonly与抽象类、接口、泛型的深度整合,落地鸿蒙电商“全局管控+类型安全”的商品模块; - 识别静态成员滥用导致的状态混乱问题,掌握静态成员与实例成员的使用边界,规避开发中的常见坑点。
【学习重点】
- 静态成员核心语法:静态属性(商品全局ID、总数)、静态方法(统计、重置)的定义与调用,区分实例成员与静态成员的访问方式;
readonly关键规则:只读字段的赋值时机(构造函数内/初始化时)、与const的使用边界(字段vs变量、运行时vs编译时);- 单例模式实战:基于私有构造器+静态实例+泛型约束,实现全局唯一的
GoodsManager(放置于泛型工具层),统一管理所有商品数据; - 架构整合:静态层、只读层与抽象类、接口、泛型层的联动逻辑,保障代码复用性与数据安全性;
- 避坑指南:静态成员的生命周期(应用级)、静态方法中必须用类名访问静态属性、
readonly字段的赋值限制、ArkTS静态类型检查下多余的类型判断需移除。
一、工程结构
基于上一节的ClassObjectDemo_4工程,复制并重命名为ClassObjectDemo_5,核心修改/新增如下(调整GoodsManager至generic层,移除多余的static目录):
ClassObjectDemo_5/
├── entry/
│ ├── src/main/ets/
│ │ ├── pages/
│ │ │ └── Index.ets # 【修改】扩展静态成员+单例管理器实战功能
│ │ └── model/
│ │ ├── AbstractGoods.ets # 【核心修改】新增静态成员+readonly字段
│ │ ├── PhysicalGoods.ets # 【轻微修改】适配readonly字段
│ │ ├── VirtualGoods.ets # 【轻微修改】适配readonly字段
│ │ ├── SeckillGoods.ets # 【轻微修改】适配readonly字段
│ │ ├── capability/ # 【无修改】复用原有内容
│ │ └── generic/ # 【无修改+新增】复用原有内容,新增GoodsManager.ets
│ │ ├── GoodsList.ets
│ │ ├── GoodsTool.ets
│ │ └── GoodsManager.ets # 【新增】单例模式:全局商品管理器(泛型工具层)
│ ├── resources/ # 【无修改】复用
│ └── module.json5 # 【无修改】复用
└── hvigorfile.ts # 【无修改】复用
二、核心知识
2.1 静态成员(static):类级别的共享资源
静态成员(静态属性/静态方法)属于类本身,而非类的实例。所有该类的实例共享同一个静态成员,无需创建实例即可通过类名.成员直接调用。
核心作用
- 全局数据共享:统计商品总数、生成全局唯一商品ID;
- 全局方法调用:工具类通用方法,无需实例化即可使用;
- 类关联常量:定义商品默认分类,与类绑定更易维护。
关键规则
- 静态方法中必须使用类名访问静态属性,不能使用
this(避免语法歧义); - 静态方法只能访问静态成员,不能直接访问实例成员;
- 静态成员生命周期与应用一致,避免存储大量数据导致内存过高;
- 私有静态属性仅能在类内部的静态方法中访问,保证数据安全。
2.2 readonly修饰符:核心字段的安全保障
readonly用于修饰类的字段(实例/静态),表示只读,仅允许在初始化时或构造函数内赋值,后续无法修改。
与const的核心区别
| 特性 | readonly(只读字段) |
const(常量) |
|---|---|---|
| 作用对象 | 类的实例字段/静态字段 | 全局/局部变量、对象引用 |
| 赋值时机 | 初始化时/构造函数内(仅一次) | 声明时(仅一次) |
| 关联关系 | 与类关联,属于类的一部分 | 独立存在,与类无直接关联 |
| 可变性 | 对象字段:引用不可变,属性可变 | 对象变量:引用不可变,属性可变 |
适用场景
- 商品ID、创建时间等一旦生成就不能修改的核心字段;
- 类的静态常量(如默认分类、默认利润率)。
2.3 单例模式:全局唯一的实例
单例模式是一种设计模式,保证一个类在应用中只有一个实例,并提供一个全局访问点。
实现核心步骤
- 私有构造器:禁止外部通过
new创建实例; - 静态私有实例:存储唯一实例;
- 静态
getInstance方法:提供全局访问点,懒加载创建实例。
适用场景
- 全局商品管理器、全局状态管理器等需要统一管控数据的场景;
- 避免多模块创建多个实例导致数据分散、不一致。
三、核心语法实现(仅展示修改/新增部分)
3.1 抽象商品类扩展:静态成员+readonly字段(核心修改)
// model/AbstractGoods.ets
export abstract class AbstractGoods {
// 静态属性:私有,仅类内部可访问(全局商品ID计数器)
private static _globalGoodsId: number = 0;
// 静态属性:私有,仅类内部可访问(全局商品总数)
private static _globalGoodsCount: number = 0;
// 实例只读字段:商品唯一ID
public readonly goodsId: number;
// 实例只读字段:商品创建时间
public readonly createTime: Date = new Date();
// 静态只读字段:商品默认分类(公开,外部可访问)
public static readonly DEFAULT_CATEGORY: string = "鸿蒙周边";
// 原有实例成员
public name: string;
public category: string = AbstractGoods.DEFAULT_CATEGORY;
// protected修改成private 禁止外部直接访问修改底价,只能通过getter setter方法访问修改
// 其他的引用使用的地方如 SeckillGoods、VirtualGoods 也需改成使用get basePrice 方法 来访问(this.basePrice)
private _basePrice: number;
public profitRate: number;
// 构造函数
constructor(
name: string,
basePrice: number,
category?: string,
profitRate: number = 0.1
) {
// 为readonly字段赋值(调用静态方法获取全局ID)
this.goodsId = AbstractGoods.getNextGoodsId();
// 原有逻辑
this.name = name;
this._basePrice = basePrice > 0 ? basePrice : 0;
this.profitRate = profitRate > 0 ? profitRate : 0.1;
this.category = category?.trim() || this.category;
}
// 私有静态方法:自增商品ID(内部辅助方法)
private static incrementGoodsId(): number {
AbstractGoods._globalGoodsId += 1;
return AbstractGoods._globalGoodsId;
}
// 私有静态方法:自增商品总数(内部辅助方法)
private static incrementGoodsCount(): void {
AbstractGoods._globalGoodsCount += 1;
}
// 公开静态方法:获取下一个全局商品ID(对外暴露的接口)
public static getNextGoodsId(): number {
const newId = AbstractGoods.incrementGoodsId();
AbstractGoods.incrementGoodsCount(); // 联动更新总数,逻辑闭环
return newId;
}
// 公开静态方法:获取全局商品总数
public static getGlobalGoodsCount(): number {
return AbstractGoods._globalGoodsCount;
}
// 公开静态方法:重置全局计数器(测试用)
public static resetGlobalCounter(): void {
AbstractGoods._globalGoodsId = 0;
AbstractGoods._globalGoodsCount = 0;
console.log("【AbstractGoods】全局商品计数器已重置");
}
// 实例方法:打印基础信息
printBaseInfo(): void {
console.log(`
商品ID:${this.goodsId}(只读)
商品名称:${this.name}
商品分类:${this.category}
商品底价:${this._basePrice}元
利润率:${this.profitRate * 100}%
创建时间:${this.createTime.toLocaleString()}
`);
}
// 原有属性读写器(保留,用于访问私有_basePrice)
get basePrice() {
return this._basePrice;
}
set basePrice(newPrice: number) {
if (newPrice < 0) {
console.log(`【${this.name}】商品底价不能为负,修改失败`);
return;
}
this._basePrice = newPrice;
}
public abstract calculateSellingPrice(): number;
public abstract checkStock(amount: number): boolean;
}
3.2 商品子类适配:兼容readonly字段
说明:子类构造函数调用
super()时会自动继承父类的readonly字段赋值逻辑,无需额外修改,仅需保证原有构造函数参数传递正常即可。
3.3 【新增】单例模式:全局商品管理器(泛型+静态)
// model/generic/GoodsManager.ets
import { AbstractGoods } from '../AbstractGoods';
import { GoodsList } from './GoodsList';
import { GoodsTool } from './GoodsTool';
import { StockTool } from '../capability/GoodsComparators';
// 声明数据结构:商品数量信息
export interface GoodsCountInfo {
globalCount: number;
localCount: number;
}
/**
* 全局商品管理器(单例模式+泛型)
* 放置于generic层,作为泛型工具类统一管理商品数据
*/
export class GoodsManager<T extends AbstractGoods> {
// 静态私有实例:存储唯一实例
private static instance: GoodsManager<AbstractGoods>;
// 泛型商品列表:复用原有GoodsList逻辑
private readonly goodsList: GoodsList<T> = new GoodsList<T>();
// 私有构造器:禁止外部实例化
private constructor() {}
// 静态方法:获取全局唯一实例(直接返回实例,移除所有多余断言/转换)
public static getInstance<T extends AbstractGoods>(): GoodsManager<T> {
if (!GoodsManager.instance) {
GoodsManager.instance = new GoodsManager<AbstractGoods>();
console.log("【GoodsManager】全局商品管理器实例已创建(首次调用)");
}
// 返回实例
return GoodsManager.instance as GoodsManager<T>
}
// 添加单个商品
addGoods(goods: T): void {
this.goodsList.addGoods(goods);
console.log(`【GoodsManager】新增商品ID:${goods.goodsId},名称:${goods.name}`);
}
// 批量添加商品
addGoodsBatch(goodsArr: T[]): void {
this.goodsList.addGoodsBatch(goodsArr);
console.log(`【GoodsManager】批量新增完成,全局总数:${AbstractGoods.getGlobalGoodsCount()}`);
}
// 根据商品ID查询商品
getGoodsById(goodsId: number): T | undefined {
if (goodsId <= 0) {
console.warn("【GoodsManager】查询失败:商品ID必须大于0");
return undefined;
}
return this.goodsList.getGoodsList().find(item => item.goodsId === goodsId);
}
// 获取商品库存排名
getGoodsStockRank(isDesc: boolean = true): T[] {
const goodsList = this.goodsList.getGoodsList();
return goodsList.sort((a, b) => {
const stockA = StockTool.getStock(a);
const stockB = StockTool.getStock(b);
return isDesc ? stockB - stockA : stockA - stockB;
});
}
// 获取所有商品
getAllGoods(): T[] {
return this.goodsList.getGoodsList();
}
// 筛选有库存的商品
filterInStock(): T[] {
return GoodsTool.filterInStock(this.goodsList.getGoodsList());
}
// 获取商品数量信息(使用声明的接口返回)
getGoodsCountInfo(): GoodsCountInfo {
const countInfo: GoodsCountInfo = {
globalCount: AbstractGoods.getGlobalGoodsCount(),
localCount: this.goodsList.getCount()
};
return countInfo;
}
// 清空商品列表
clearGoods(): void {
this.goodsList.clear();
}
}
四、实战:Index页面扩展(完整展示核心修改部分)
// pages/Index.ets
import { LengthMetrics } from '@kit.ArkUI';
import { AbstractGoods } from '../model/AbstractGoods';
import { PhysicalGoods } from '../model/PhysicalGoods';
import { VirtualGoods } from '../model/VirtualGoods';
import { SeckillGoods } from '../model/SeckillGoods';
import { GoodsManager } from '../model/generic/GoodsManager';
import { StockAscComparator, StockDescComparator } from '../model/capability/GoodsComparators';
import { GoodsTool } from '../model/generic/GoodsTool';
@Entry
@Component
struct Index {
@State goodsList: AbstractGoods[] = [];
@State globalCount: number = 0;
// 获取单例管理器实例
private goodsManager = GoodsManager.getInstance<AbstractGoods>();
// 页面初始化
aboutToAppear(): void {
this.initGoodsByManager();
this.updateGlobalCount();
}
// 使用单例管理器初始化商品
private initGoodsByManager(): void {
AbstractGoods.resetGlobalCounter();
// 原有商品实例
const phone = new PhysicalGoods("鸿蒙Mate70手机", 6999, 200, 0.2, "智能手机", 0.12);
const emptyStockPhone = new PhysicalGoods("鸿蒙Mate60手机(无库存)", 5999, 0, 0.2, "智能手机", 0.12);
const vip = new VirtualGoods("鸿蒙VIP会员", 99, 365, true, "虚拟服务", 0.2);
const watch = new SeckillGoods("鸿蒙智能手表GT5(秒杀款)", 1299, 50, 0.7, new Date(), new Date(Date.now() + 7200000), "智能穿戴", 0.1);
// 单例管理器批量添加商品
this.goodsManager.addGoodsBatch([phone, emptyStockPhone, vip, watch]);
this.goodsList = this.goodsManager.getAllGoods();
}
// 更新全局商品总数
private updateGlobalCount(): void {
this.globalCount = AbstractGoods.getGlobalGoodsCount();
}
// 按商品ID查询
private searchGoodsById(id: number): void {
const goods = this.goodsManager.getGoodsById(id);
this.goodsList = goods ? [goods] : this.goodsManager.getAllGoods();
}
// 获取库存排名
private getStockRank(): void {
this.goodsList = this.goodsManager.getGoodsStockRank(true);
}
// 重置列表
private resetList(): void {
this.goodsList = this.goodsManager.getAllGoods();
}
// 原有核心逻辑方法
private callSubclassMethods(): void {
console.log("===== 商品核心逻辑执行 =====");
this.goodsList.forEach((goods, index) => {
console.log(`\n【商品${index + 1}:${goods.name}】`);
goods.printBaseInfo();
console.log(`最终售价:${goods.calculateSellingPrice()}元`);
const checkResult = goods.checkStock(2);
console.log(`购买2件校验结果:${checkResult ? "✅ 通过" : "❌ 失败"}`);
if (goods instanceof PhysicalGoods) {
goods.reduceStock(2);
} else if (goods instanceof SeckillGoods) {
goods.reduceSeckillStock(2);
}
});
this.goodsList = [...this.goodsList];
}
// 库存升序
private sortStockAsc(): void {
this.goodsList = GoodsTool.sortByComparator(this.goodsList, new StockAscComparator());
}
// 库存降序
private sortStockDesc(): void {
this.goodsList = GoodsTool.sortByComparator(this.goodsList, new StockDescComparator());
}
// 筛选有库存
private filterInStock(): void {
this.goodsList = GoodsTool.filterInStock(this.goodsList);
}
// 日期格式化
private formatDate(date: Date): string {
return date.toLocaleString('zh-CN', {
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit'
});
}
build() {
Column() {
// 全局商品统计展示
Text(`全局商品总数:${this.globalCount} | 默认分类:${AbstractGoods.DEFAULT_CATEGORY}`)
.fontSize(14)
.fontColor("#666")
.margin({ bottom: 10 });
// 操作按钮组
Flex({ space: { main: LengthMetrics.vp(10), cross: LengthMetrics.vp(10) }, wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Center }) {
// 原有按钮
Button("库存升序").onClick(() => this.sortStockAsc());
Button("库存降序").onClick(() => this.sortStockDesc());
Button("筛选有库存").onClick(() => this.filterInStock());
Button("重置列表").onClick(() => this.resetList());
Button("执行核心操作").onClick(() => this.callSubclassMethods());
// 新增按钮
Button("按ID查询(1)")
.backgroundColor("#9333EA")
.onClick(() => this.searchGoodsById(1));
Button("库存排名")
.backgroundColor("#F59E0B")
.onClick(() => this.getStockRank());
}
.margin({ bottom: 15 });
// 商品列表展示
Scroll() {
Column() {
ForEach(this.goodsList, (goods: AbstractGoods) => {
Column() {
// 展示readonly字段
Text(`商品ID:${goods.goodsId} | ${goods.name}`)
.fontSize(18)
.fontWeight(FontWeight.Medium);
Text(`创建时间:${goods.createTime.toLocaleString()}`)
.fontSize(12)
.margin(1)
.fontColor("#999");
// 原有信息展示
Text(`分类:${goods.category} | 底价:¥${goods.basePrice} | 售价:¥${goods.calculateSellingPrice().toFixed(2)}`)
.fontSize(14)
.margin(2)
.fontColor("#666");
// 子类专属信息
if (goods instanceof PhysicalGoods) {
Text(`库存:${goods.stock}件 | 重量:${goods.weight.toFixed(2)}kg`)
.fontSize(12)
.margin(2)
.fontColor(goods.stock === 0 ? "#ff0000" : "#999");
} else if (goods instanceof VirtualGoods) {
Text(`有效期:${goods.validDays}天 | 自动续费:${goods.isAutoRenew ? "开启" : "关闭"}`)
.fontSize(12)
.margin(2)
.fontColor("#999");
} else if (goods instanceof SeckillGoods) {
Text(`秒杀库存:${goods.seckillStock}件 | 折扣:${(goods.discountRate * 10).toFixed(1)}折`)
.fontSize(12)
.margin(2)
.fontColor(goods.seckillStock === 0 ? "#ff0000" : "#999");
Text(`秒杀时间:${this.formatDate(goods.promoStartTime)} - ${this.formatDate(goods.seckillEndTime)}`)
.fontSize(10)
.margin(2)
.fontColor("#ff6600");
}
// 子类专属按钮
Flex({ space: { main: LengthMetrics.vp(10) }, justifyContent: FlexAlign.Center }) {
if (goods instanceof PhysicalGoods) {
Button("扣减库存")
.fontSize(10)
.backgroundColor(Color.Green)
.fontColor(Color.White)
.borderRadius(4)
.enabled(goods.stock > 0)
.onClick(() => {
goods.reduceStock(1);
this.goodsList = [...this.goodsList];
});
} else if (goods instanceof SeckillGoods) {
Button("扣减秒杀库存")
.fontSize(10)
.backgroundColor(Color.Orange)
.fontColor(Color.White)
.borderRadius(4)
.enabled(goods.seckillStock > 0)
.onClick(() => {
goods.reduceSeckillStock(1);
this.goodsList = [...this.goodsList];
});
} else if (goods instanceof VirtualGoods) {
Button("购买VIP")
.fontSize(10)
.backgroundColor(Color.Pink)
.fontColor(Color.White)
.borderRadius(4)
.onClick(() => {
console.log(`${goods.name}购买成功!`);
});
}
}
.margin({ top: 10 });
}
.width("90%")
.padding(15)
.backgroundColor(Color.White)
.borderRadius(10)
.shadow({ radius: 5, color: "#ccc", offsetX: 2, offsetY: 2 })
.margin(10);
});
}
.width("100%");
}
.layoutWeight(1)
.width("100%");
}
.width("100%")
.height("100%")
.backgroundColor("#f5f5f5")
.padding(10);
}
}
运行效果以及日志

五、架构整合:静态/readonly与原有体系的联动逻辑
graph LR
%% 基础层:抽象类+readonly+静态成员
subgraph 1.基础核心层
A[AbstractGoods(抽象类:readonly字段+静态成员)]
end
%% 能力层:接口(复用)
subgraph 2.能力接口层
B[IReturnable(可退换)]
C[ISeckill(可秒杀)]
D[IGoodsComparator(泛型比较器)]
end
%% 泛型工具层(复用+新增GoodsManager)
subgraph 3.泛型工具层
E[GoodsList(泛型列表)]
F[GoodsTool(泛型工具)]
J[GoodsManager(单例模式+泛型,全局商品管理)]
end
%% 商品业务层(复用+适配readonly)
subgraph 4.商品业务层
G[PhysicalGoods(实体商品)]
H[VirtualGoods(虚拟商品)]
I[SeckillGoods(秒杀商品)]
end
%% 联动逻辑(调整GoodsManager至泛型工具层)
A -->|继承(含readonly字段)| G
A -->|继承(含readonly字段)| H
A -->|继承(含readonly字段)| I
G -->|实现| B
I -->|实现| B
I -->|实现| C
A -->|泛型约束:T extends AbstractGoods| D
A -->|泛型约束| E
A -->|泛型约束| F
E -->|被整合| J
F -->|被整合| J
A -->|静态成员调用| J
J -->|全局管控| G
J -->|全局管控| H
J -->|全局管控| I
六、常见问题解答(避坑指南)
6.1 静态成员与实例成员的核心区别?
| 特性 | 静态成员(static) |
实例成员(非static) |
|---|---|---|
| 归属 | 类本身(所有实例共享) | 单个实例(每个实例独立) |
| 访问方式 | 类名.成员 | 实例名.成员 |
访问this |
不支持 | 支持 |
| 生命周期 | 应用启动至销毁(全局) | 实例创建至销毁(局部) |
| 适用场景 | 全局统计、ID生成、常量定义 | 实例属性、实例方法 |
6.2 为什么不能滥用静态成员?
- 静态成员全局存在,存储大量数据会导致内存占用过高,甚至内存泄漏;
- 静态成员状态全局共享,多模块修改易导致状态混乱;
- 解决方案:使用单例模式封装静态成员,提供统一修改接口(如
GoodsManager)。
6.3 readonly字段的赋值限制有哪些?
- 实例
readonly字段:仅能在初始化时或构造函数内赋值; - 静态
readonly字段:仅能在初始化时赋值(ArkTS暂不支持静态代码块); - 错误示例:
goods.goodsId = 999(编译报错,readonly字段不可修改)。
七、仓库代码
工程名称:ClassObjectDemo_5
代码地址:https://gitee.com/juhetianxia321/harmony-os-code-base.git
八、课堂小结
- 静态成员通过
类名.成员访问,静态方法中操作静态属性时直接使用类名,保证语法严谨性,同时将_basePrice改为私有,仅通过getter/setter访问,强化封装性; readonly修饰符保障核心字段(如商品ID)不可篡改,与const的核心区别在于作用对象和赋值时机;- 单例模式的
GoodsManager放置于泛型工具层,通过私有构造器+静态getInstance实现全局唯一实例; - ArkTS静态类型检查保障参数类型合法性,方法内仅保留业务逻辑校验(如商品ID>0),移除冗余的类型判断;
- 静态/readonly与抽象类、接口、泛型的五层架构整合,实现商品模块的基础规范、能力扩展、通用管理与全局管控。
九、下节预告
下一节将学习静态成员的实战进阶——HiLogUtil日志工具封装,掌握静态工具类封装与单例模式基础优化的核心技巧:
- 静态工具类的封装思想,理解为何工具类适合用静态成员实现(无需实例化);
- 基于静态成员封装HiLogUtil,替代console并实现日志分级(debug/info/warn/error);
- 鸿蒙HiLog的基础规范适配,统一管理日志的domain与tag,保留console的便捷性;
- 单例模式的基础优化,掌握饿汉式单例的静态只读实例最佳实践;
- 静态成员与实例成员的使用边界,规避静态成员滥用导致的状态混乱问题。
十、鸿蒙开发者学习与认证指引
(一)、官方学习班级报名(免费)
- 班级链接:HarmonyOS赋能资源丰富度建设(第四期)
- 学号填写规则:填写个人手机号码即可完成班级信息登记
(二)、HarmonyOS应用开发者认证考试(免费)
-
考试链接:HarmonyOS开发者能力认证入口
-
认证等级及适配人群
- 基础认证:适配软件工程师、移动应用开发人员,需掌握HarmonyOS基础概念、DevEco Studio基础使用、ArkTS及ArkUI基础开发等能力;
- 高级认证:适配项目经理、工程架构师,需掌握系统核心技术理念、应用架构设计、关键技术开发及应用上架运维等能力;
- 专家认证:适配研发经理、解决方案专家,需掌握分布式技术原理、端云一体化开发、跨端迁移及性能优化等高级能力。
-
认证权益:通过认证可获得电子版证书以及其他专属权益。
浙公网安备 33010602011771号