我们在学习Java的时候都知道,类有三大特性:继承,封装,多态,这也是面向对象的三大特征。OC学习篇之---类的三大特性(封装,继承,多态)
1、封装(Encapsulation)是指将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问(该隐藏的隐藏,该暴露的暴露)。封装的主要目的是:
- 隐藏类的实现细节
- 让使用者只能通过预先定义好的方法来访问数据,从而可以在该方法中加入控制逻辑,限制对成员变量的不合理访问
- 可进行数据检查,从而有利于保证对象信息的完整性
- 便于修改,提高代码可维护性
2、封装的具体实现通过4个访问控制负来实现变量和方法不同级别的访问权限:@private、@package、@protected和@public。这里的每一个权限跟Java中的基本一致。
- @private(当前类访问权限):如果类中的成员变量用@private访问控制符来限制,则这个成员变量只能在当前类的内部访问。在类的实现部分定义的成员变量默认是@private
- @package(相同映像访问权限):如果类中的成员变量用@package访问控制符来限制,则这个成员变量可以在当前类以及当前类的同一个映像的任意地方访问。所谓的统一映像指的是编译后生成的同一个框架或同一个执行文件,编译器可以将一些类编译成一个框架库,那么这个库中的类之间可以互相直接访问@package修饰的成员变量。
- @protected(子类访问权限):如果类中的成员变量用@protected访问控制符来限制,则这个成员变量可以再当前类、当前类的子类的任意地方访问。在类的接口部分定义的成员变量默认是@protected
- @public(公共访问权限):如果类中的成员变量用@public访问控制符来限制,则这个成员变量可以在任何地方进行访问。
@interface Car : NSObject{ @public float _capcity; //油量属性 } - (void)run:(float)t; @end
值得注意的是,OC中的方法是没有修饰符的概念的,一般都是公开访问的,即public的,但是我们怎么做到让OC中的一个方法不能被外界访问呢?OC中是这么做的,如果想让一个方法不被外界访问的话,只需要在.m文件中实现这个方法,不要在头文件中进行定义,说白了就是:该方法有实现,没定义,这样外界在导入头文件的时候,是没有这个方法的,但是这个方法我们可以在自己的.m文件中进行使用。
3、在OC2.0之后,它自动合成了setter方法和getter方法,这样就可以避免重复写这些无聊的setter、getter方法。让系统自动合成setter、getter方法需要如下两步:
- 在类接口部分使用@property指令定义属性,使用@property定义属性时无需放在类接口部分的花括号里,而是直接放在@interface、@end之间定义。@property指令放在属性定义的最前面。此外,还可以在@property和类型之间用括号添加一些额外的指示符,可使用的指示符如下:
- assign:该指示符只是对属性进行简单复制,不更改对所赋的值的引用计数。这个指示符主要用于NSInteger等基本类型以及short、float、结构体等各种C数据类型
- atomic(nonatomic):制定合成的存取方法是否为原子操作。所谓原子操作就说指是否是线程安全的。atomic表示是线程安全的,但是会造成性能下降。
- copy:表示当调用setter方法对成员变量进行赋值时,会将被赋值的对象复制一个副本,再将该副本赋值给成员变量。copy指示符会将原成员变量所引用计数减1。当成员变量的类型是可变类型或其子类是可变类型时,被赋值的对象有可能在赋值之后发生变化,如果程序不需要这种修改影响setter方法设置的成员变量的值,就可以考虑使用copy指示符。
- 此步是可选的。如果程序需要改变getter、setter方法对应的成员变量的变量名,则可以在类的实现部分使用@synthesize指令
4、继承是面向对象的三大特征之一,也是实现软件重用的重要手段。OC的继承与Java一样,具有单继承的特点,每个子类由且仅有一个直接父类。当子类继承自父类时,子类可以继承得到父类的如下内容:
- 全部成员变量
- 全部方法,包括初始化方法
5、子类扩展了父类,在大部分时候,子类总是以父类为基础,额外增加新的成员变量和方法。但有一种情况例外:子类需要重写父类的方法。
- 无论父类接口部分的成员变量使用何种访问控制符的限制,子类接口部分定义的成员变量都不允许与父类接口部分定义的成员变量重名。
- 需要指出的是,在类的实现部分定义的成员变量将被限制在该类的内部,因此,父类在类实现部分定义的成员变量对子类没有任何影响。
1 #import <Foundation/Foundation.h> 2 3 //父类Car的接口部分 4 @interface Car : NSObject{ 5 NSString *_brand; 6 NSString *_color; 7 } 8 9 - (void)setBrand:(NSString *)brand; 10 - (void)setColor:(NSString *)color; 11 - (void)brake; 12 - (void)quicken; 13 14 @end
1 #import "Car.h" 2 3 //父类Car的实现部分 4 @implementation Car 5 - (void)setBrand:(NSString *)brand{ 6 _brand = brand; 7 } 8 - (void)setColor:(NSString *)color{ 9 _color = color; 10 } 11 - (void)brake{ 12 NSLog(@"刹车"); 13 } 14 - (void)quicken{ 15 NSLog(@"加速"); 16 } 17 @end
1 #import "Car.h" 2 3 //继承自Car的子类Taxi的接口部分 4 @interface Taxi : Car{ 5 NSString *_company;//所属公司 6 } 7 8 //打印发票 9 - (void)printTick; 10 11 @end
1 #import "Taxi.h" 2 3 //继承自Car的子类Taxi的实现部分 4 @implementation Taxi 5 6 - (void)printTick{ 7 [super brake]; 8 [self brake]; 9 NSLog(@"%@出租车打印了发票,公司为:%@,颜色为:%@",_brand,_company,_color); 10 } 11 12 @end
对方法的实现,这里我们看到实现文件中是不需要导入父类Car的头文件的,因为可以认为,Taxi.h头文件中已经包含了Car的头文件了。而且,这里可以使用super关键字来调用父类的方法,同时这里我们也是可以用self关键字来调用,这里看到其实这两种方式调用的效果是一样的,当我们在子类重新实现brake方法的时候(Java中的重写概念),那么这时候super关键字调用的还是父类的方法,而self调用的就是重写之后的brake方法了。同样,我们也是可以使用父类中的属性。
6、OC指针类型的变量有两个:一个是编译时类型,一个是运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象所决定。如果编译时类型和运行时类型出现不一致,就可能出现所谓的多态(Ploymorphism)。和Java中多态的概念是一样的,主要体现在继承过程中方法重写时的调用上。具体代码就不放了,需要程序员好好体会