OO语法基础 练习 #practice #oo-syntax #abap-objects
关键词
答案
类定义
CLASS … DEFINITION / CLASS … IMPLEMENTATION
可见性
PUBLIC SECTION, PROTECTED SECTION, PRIVATE SECTION
构造函数
CONSTRUCTOR 方法,在 CREATE OBJECT 时自动调用
引用变量
TYPE REF TO,指向对象的引用
题目1 - [记忆] 类定义的基本结构
请写出ABAP中定义和实现一个类的完整语法框架。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 " 类定义(声明) CLASS lcl_example DEFINITION. PUBLIC SECTION. " 公有属性和方法声明 PROTECTED SECTION. " 受保护的属性和方法声明 PRIVATE SECTION. " 私有属性和方法声明 ENDCLASS. " 类实现 CLASS lcl_example IMPLEMENTATION. METHOD method_name. " 方法实现代码 ENDMETHOD. ENDCLASS.
关键点:
DEFINITION 部分声明类的接口(属性、方法、事件)
IMPLEMENTATION 部分实现所有在定义中声明的方法
类名约定:局部类以 LCL_ 开头,全局类以 CL_ 开头
题目2 - [记忆] 三种可见性区域
请解释ABAP类中 PUBLIC SECTION、PROTECTED SECTION 和 PRIVATE SECTION 的区别。
可见性
访问范围
用途
PUBLIC SECTION
所有外部程序、子类和类本身均可访问
定义外部接口,提供公共服务
PROTECTED SECTION
仅限类本身及其子类(继承者)访问
为子类提供可复用的内部方法和属性
PRIVATE SECTION
仅限类本身访问,子类也无法直接访问
隐藏内部实现细节,保护数据完整性
设计原则:
属性通常设为 PRIVATE,通过公有方法提供受控访问(getter/setter)
供子类重定义的方法放在 PROTECTED SECTION
对外提供的服务接口放在 PUBLIC SECTION
遵循最小可见性原则 :尽可能使用最严格的可见性
题目3 - [记忆] 实例属性与静态属性
请说明实例属性(Instance Attribute)和静态属性(Static Attribute)的区别。
特性
实例属性
静态属性
声明
DATA: attr TYPE type
CLASS-DATA: attr TYPE type
存在方式
每个对象实例各有一份独立的副本
整个类只有一份,所有实例共享
访问方式
通过对象引用访问:oref->attr
通过类名访问:lcl_class=>attr,也可通过引用访问
生命周期
随对象的创建和销毁
类被加载时创建,程序运行期间一直存在
典型用途
存储对象的状态数据(如订单金额)
计数器、配置信息、共享常量
示例:
1 2 3 4 5 CLASS lcl_counter DEFINITION. PUBLIC SECTION. DATA: count TYPE i. " 实例属性 CLASS-DATA: total TYPE i. " 静态属性 ENDCLASS.
题目4 - [记忆] 构造函数的作用和特点
请说明ABAP中构造函数(CONSTRUCTOR)的特点和用途。
构造函数的特点:
名称固定 :必须命名为 CONSTRUCTOR(实例构造函数)或 CLASS_CONSTRUCTOR(静态构造函数)
自动调用 :在使用 CREATE OBJECT 创建对象时自动被调用
初始化 :用于设置对象的初始状态,初始化属性值
参数支持 :可以定义 IMPORTING 参数和异常,在创建对象时传入初始值
不可继承 :每个类必须定义自己的构造函数(但子类构造函数会隐式调用父类构造函数)
语法:
1 2 3 4 METHODS: constructor IMPORTING iv_name TYPE string. " 创建对象时传入参数 CREATE OBJECT lo_obj EXPORTING iv_name = 'Test'.
静态构造函数(CLASS_CONSTRUCTOR) :在类首次被访问时自动调用一次,仅用于初始化静态属性,不能有参数。
题目5 - [记忆] 引用变量和对象创建
请说明ABAP中如何声明引用变量、创建对象以及访问对象的属性和方法。
声明引用变量 :
1 DATA: lo_customer TYPE REF TO lcl_customer. " 声明引用变量
创建对象 :
1 2 3 4 5 CREATE OBJECT lo_customer. " 创建对象实例 " 或带参数: CREATE OBJECT lo_customer EXPORTING iv_name = 'John'.
NEW 操作符(内联创建) :
1 DATA(lo_customer) = NEW lcl_customer( iv_name = 'John' ).
访问属性和方法 :
1 2 3 4 5 6 " 实例成员使用 -> (对象组件选择器) lo_customer->set_name( 'John' ). DATA(lv_name) = lo_customer->get_name( ). " 静态成员使用 => (类组件选择器) DATA(lv_count) = lcl_customer=>instance_count.
关键概念:引用变量存储的是指向对象的指针(引用),而非对象本身。
题目6 - [记忆] 方法定义的参数类型
请列出ABAP方法中可以使用的四种参数类型(参数传递方式)及其特点。
参数类型
关键字
特点
导入参数
IMPORTING
从调用者传入方法,方法内只读
导出参数
EXPORTING
从方法返回给调用者,方法内写入
变更参数
CHANGING
从调用者传入并可能被方法修改,可读写
返回参数
RETURNING
返回单个值,支持函数式调用链式写法
语法示例:
1 2 3 4 5 METHODS: calculate_price IMPORTING iv_quantity TYPE i iv_price TYPE p EXPORTING ev_total TYPE p RETURNING VALUE(rv_result) TYPE p.
注意:
RETURNING 参数只能有一个,且不能与 EXPORTING 或 CHANGING 同时使用
RETURNING 参数使用 VALUE() 语法,表示按值传递
推荐使用 RETURNING 以支持函数式编程风格
题目7 - [应用] 编写类定义
请根据以下需求编写一个ABAP类的完整定义和实现:
类名:lcl_product(商品类)
有私有的商品名称和价格属性
构造函数接收名称和价格参数进行初始化
公有方法 get_price_with_tax 返回含税价格(税率10%)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 CLASS lcl_product DEFINITION. PUBLIC SECTION. METHODS: constructor IMPORTING iv_name TYPE string iv_price TYPE p DECIMALS 2, get_price_with_tax RETURNING VALUE(rv_price) TYPE p DECIMALS 2. PRIVATE SECTION. DATA: mv_name TYPE string, mv_price TYPE p DECIMALS 2. ENDCLASS. CLASS lcl_product IMPLEMENTATION. METHOD constructor. mv_name = iv_name. mv_price = iv_price. ENDMETHOD. METHOD get_price_with_tax. rv_price = mv_price * '1.10'. ENDMETHOD. ENDCLASS.
使用示例:
1 2 3 DATA(lo_product) = NEW lcl_product( iv_name = '笔记本电脑' iv_price = '5999.00' ). DATA(lv_tax_price) = lo_product->get_price_with_tax( ). " lv_tax_price = 6598.90
题目8 - [应用] 方法调用分析
分析以下代码的执行结果。请逐步追踪 lo_ref 引用变量的变化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 CLASS lcl_demo DEFINITION. PUBLIC SECTION. METHODS: set_value IMPORTING iv_val TYPE i. METHODS: get_value RETURNING VALUE(rv_val) TYPE i. PRIVATE SECTION. DATA: mv_value TYPE i. ENDCLASS. CLASS lcl_demo IMPLEMENTATION. METHOD set_value. mv_value = iv_val. ENDMETHOD. METHOD get_value. rv_val = mv_value. ENDMETHOD. ENDCLASS. START-OF-SELECTION. DATA: lo_ref1 TYPE REF TO lcl_demo. DATA: lo_ref2 TYPE REF TO lcl_demo. CREATE OBJECT lo_ref1. lo_ref1->set_value( 10 ). lo_ref2 = lo_ref1. lo_ref2->set_value( 20 ). WRITE: / lo_ref1->get_value( ).
输出结果:20 逐步分析:
CREATE OBJECT lo_ref1 — 创建一个 lcl_demo 对象,lo_ref1 指向该对象
lo_ref1->set_value( 10 ) — 将该对象的 mv_value 设置为 10
lo_ref2 = lo_ref1 — 关键步骤 :将 lo_ref1 的引用赋值给 lo_ref2,此时 lo_ref1 和 lo_ref2 指向同一个对象 (不是创建新对象)
lo_ref2->set_value( 20 ) — 修改了同一对象的 mv_value 为 20
lo_ref1->get_value( ) — 返回该对象的 mv_value,此时值为 20
核心概念:引用赋值(lo_ref2 = lo_ref1)不创建新对象,而是让两个引用变量指向同一块内存。通过任一引用修改对象,另一个引用也能看到变化。
题目9 - [应用] 静态成员使用场景
以下代码试图实现一个对象计数器。请找出代码中的错误并修正。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 CLASS lcl_counter DEFINITION. PUBLIC SECTION. METHODS: constructor. DATA: mv_count TYPE i. PRIVATE SECTION. CLASS-DATA: gv_instance_count TYPE i. ENDCLASS. CLASS lcl_counter IMPLEMENTATION. METHOD constructor. gv_instance_count = gv_instance_count + 1. mv_count = gv_instance_count. ENDMETHOD. ENDCLASS.
代码中的问题:
mv_count 可见性不当 :mv_count 被定义在 PUBLIC SECTION,外部代码可以随意修改它,破坏了计数器的封装性。应移到 PRIVATE SECTION
设计问题 :mv_count(实例属性)在构造函数中被赋值为当前实例编号,但外部可以随意修改。如果目的是为每个实例分配唯一编号,应该通过公有方法提供只读访问
修正版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 CLASS lcl_counter DEFINITION. PUBLIC SECTION. METHODS: constructor, get_instance_number RETURNING VALUE(rv_num) TYPE i, get_total_instances RETURNING VALUE(rv_count) TYPE i. PRIVATE SECTION. DATA: mv_instance_number TYPE i. CLASS-DATA: gv_instance_count TYPE i. ENDCLASS. CLASS lcl_counter IMPLEMENTATION. METHOD constructor. gv_instance_count = gv_instance_count + 1. mv_instance_number = gv_instance_count. ENDMETHOD. METHOD get_instance_number. rv_num = mv_instance_number. ENDMETHOD. METHOD get_total_instances. rv_count = gv_instance_count. ENDMETHOD. ENDCLASS.
题目10 - [分析] 方法参数传递方式选择
请分析以下三种方法签名,讨论它们的适用场景以及哪种方式是推荐的最佳实践。
1 2 3 4 5 6 7 8 9 10 11 " 方式A METHODS: get_full_name EXPORTING ev_name TYPE string. " 方式B METHODS: get_full_name CHANGING cv_name TYPE string. " 方式C METHODS: get_full_name RETURNING VALUE(rv_name) TYPE string.
三种方式的对比分析:
特性
方式A (EXPORTING)
方式B (CHANGING)
方式C (RETURNING)
语义
方法输出结果给调用者
方法修改调用者传入的变量
方法返回一个值
调用方式
需要先声明接收变量
需要先初始化传入变量
可直接内联使用
链式调用
不支持
不支持
支持
参数数量
可有多个
可有多个
仅限一个
推荐:方式C(RETURNING)是最佳实践
理由:
函数式风格 :支持链式调用和内联表达式1 2 " RETURNING 支持这种写法 lo_person->get_full_name( )->to_upper( ).
意图清晰 :明确表示方法返回一个值,不修改任何已有变量
代码简洁 :无需预先声明接收变量1 2 3 4 5 6 " RETURNING DATA(lv_name) = lo_person->get_full_name( ). " EXPORTING 需要更多代码 DATA: lv_name TYPE string. lo_person->get_full_name( IMPORTING ev_name = lv_name ).
何时用其他方式 :
EXPORTING:方法需要返回多个值时
CHANGING:方法需要读取并修改调用者的变量时
一般规则:返回单个值用 RETURNING,返回多个值用 EXPORTING
关键词
答案
类定义结构
DEFINITION(声明)+ IMPLEMENTATION(实现)
可见性三级
PUBLIC(所有)> PROTECTED(类+子类)> PRIVATE(仅类本身)
实例 vs 静态
DATA(每对象一份)vs CLASS-DATA(全类共享)
构造函数
CONSTRUCTOR自动调用,初始化对象状态
引用赋值
赋值引用不创建新对象,两个引用指向同一实例
参数传递
RETURNING推荐(支持链式),EXPORTING用于多返回值
组件选择器
-> 实例成员,=> 静态成员