面向对象的三大特性:封装、继承、多态
1. 封装
将属性和方法封装在一个对象中,只给外界公开访问接口,而把具体的实现隐藏。
主要目的是提高程序的可维护性和可扩展性,增加可读性。
在OC中,把需要公开的属性、方法定义或者声明在interface部分,而把不需要公开的属性、方法定义在implementation部分,隐藏起来。
OC中实例变量的处理:
一般情况下,实例变量是不能公开的。所以实例变量应该定义在implementation部分,除非你需要在子类中直接访问实例。如果在子类中需要访问父类的实例变量,那么就定义在interface部分。
可以用一下语法声明实例变量的访问范围:
@public 可以在任何位置访问
@package 可以在包内部访问,一个项目一定是在同一个包下
@protected 可以在本类内部和子类内部访问
@private 只可以在本类内部访问
//
// TRStudent.h
// day04-2
//
// Created by tarena on 14-3-20.
// Copyright (c) 2014年 tarena. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface TRStudent : NSObject
{
int _age; //默认是protected
@public
int _height; //所有文件都可以访问
@package
int _weight; //只能在当前项目中访问
@protected
BOOL gender; //只能在本类或子类访问
@private
float salary; //只能在本类访问
}
@end
//
// TRStudent.m
// day04-2
//
// Created by tarena on 14-3-20.
// Copyright (c) 2014年 tarena. All rights reserved.
//
#import "TRStudent.h"
@implementation TRStudent
@end
//
// main.m
// day04-2
//
// Created by tarena on 14-3-20.
// Copyright (c) 2014年 tarena. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TRStudent.h"
int main(int argc, const char * argv[])
{
@autoreleasepool {
TRStudent* stu = [[TRStudent alloc]init];
stu->_age = 10; //不能访问,报错
stu->_height = 170;
stu->_weight = 65;
stu->gender = YES; //不能访问,报错
}
return 0;
}
2. 继承(Inheritance)
2.1 概念
继承是一种代码复用技术,是类与类之间的一种关系。
类A继承了类B,类A中就直接拥有B中的所有属性和方法。我们把A类叫B类的子类(派生类),把B类叫A类的父类(基类)。
2.2 继承的方式
单继承
一个类只有一个父类。
OC java C#
多继承
一个类可以有多个父类。
C++
2.3 OC的继承语法
@interface 类 : 父类
@end
2.4 继承是一种关系
继承是类与类之间的关系,是一种"is a"关系。
狗是动物 狗 : 动物
Computer has a CPU, has a 有一个 不能用继承描述。
2.5 方法的重写(覆盖) override
子类对继承来的父类方法不满意,可以重新在子类定义一个相同的方法,进行覆盖掉从父类中继承来的方法。叫方法的重写(或覆盖)
重写需要遵守如下原则:
1)方法名相同
2)参数类型相同
3)返回值相同
2.6 初始化方法的继承和对象创建过程
创建一个对象的过程:
1)分配内存空间
2)递归的创建父类对象(调用父类对象的初始化方法)
3)初始化当前对象
dealloc方法的调用与初始化方法的顺序正好相反。
在dealloc方法的最后一行,一定要调用父类的dealloc方法,[super dealloc]方法是必须的。
练习:
写出以下类,简单实用之。(括号里是方法或者属性)
TRShape 形状类 (peremeter area show)
|
----------------------------------------------
| |
TRRectange (width,height) TRCircle(radius)
|
TRSquare (side)
3. 继承的缺陷
1)提高程序的复杂度,维护性和扩展性会降低
2)破坏类的封装性
慎用继承
4. 为什么用继承
1)代码复用
2)指定规则
3)为了多态
5. 组合或聚合(Combination and Aggregation )
复用代码更好的方式是组合或聚合,而不是继承。
组合和聚合描述是类与类之间"has a"的关系。
Computer has a CPU
在一个类中定义另一个类的属性的方式来完成组合或聚合。
//创建项目Inheritance:
//创建类TRAnimal
@interface TRAnimal : NSObject
@property (copy) NSString * name;
@property int size;
- (void)eat;
- (void)sleep;
- (id)initWithName:(NSString*)name andSize:(int)size;
@end
@implementation TRAnimal
- (void)eat
{
NSLog(@"动物%@吃", self.name);
}
- (void)sleep
{
NSLog(@"动物%@睡", self.name);
}
- (id)initWithName:(NSString*)name andSize:(int)size
{
if(self = [super init])
{
self.name = name;
self.size = size;
}
return self;
}
-(void)dealloc
{
[_name release]; //对象name是copy的
NSLog(@"狗对象被销毁",self.name);
[super dealloc];
}
@end
//创建类TRDog
#import "TRAnimal.h"
@interface TRDog : TRAnimal
@property int age;
- (void)watchHome;
- (void)bark: (int)n;
- (id)initWithName:(NSString *)name andSize:(int)size age:(int)age;
@end
@implemantation TRDog
- (id)initWithName:(NSString *)name andsize:(int)size age:(int)age //初始化方式
{
if(self = [super initWithName:name andSize:size]) //调用父类初始化方法
{
self.age = age;
NSLog(@"狗对象被创建",self.name);
}
return self;
}
- (void)watchHome
{
NSLog(@"狗狗%@在看家", self.name);
}
- (void) eat //方法的覆盖(重写)
{
NSLog(@"狗狗%@吃骨头",self.name);
}
- (void)bark: (int)n
{
for(int i = 0; i<n; i++)
{
NSLog(@"狗狗%@在旺旺的叫",self.name);
}
}
- (void) dealloc()
{
NSLog(@"狗对象被销毁",self.name);
[super dealloc];
}
@end
@interface TRPet : TRDog
- (id)initWithName:(NSString *)name size:(int)size age:(int)age;
@end
@implementation TRPet
- (id)initWithName:(NSString *)name size:(int)size age:(int)age
{
if(self = [super initWithName:name size:size age:age])
{
NSLog(@"宠物狗对象被创建",self.name);
}
}
- (void)bark:(int)n
{
for(int i = 0; i<n; i++)
{
NSLog(@"小狗狗%@很好听地叫",self.name);
}
}
- (void)dealloc
{
NSLog(@"宠物狗对象被销毁",self.name);
[super dealloc];
}
@end
//main.m中:
#import "TRDog.h"
void test()
{
TRDog *dog = [[TRDog alloc]init];
dog.name = @"旺财";
dog.size = 30;
[dog eat];
[dog sleep];
[dog watchHome];
NSString* desc = [dog description]; //任何类都有此方法,继承Object
NSLog(@"desc:%@", desc); //NSLog(@"%@", dog);
[dog release];
}
void test2()
{
TRDog *dog = [[TRDog alloc]initWithName:@"阿黄" andSize:100];
[dog catchMouse];
[dog release];
}
void test3()
{
TRPet *pet = [[TRPet alloc]initWithName:@"小黄" size:30 age:3]
NSLog(@"pet:%@,%d,%d", pet.name, pet.size, pet.age);
[pet release]
}
int main(int argc, const char * argv[])
{
@autoreleasepool{
//test();
test2();
}
return 0;
}
3. 多态(Polymorphism)
3.1 概念
多种形态,变量的多种形态。
对于一个引用(指针)变量,可以指向任何子类的对象。也可以指向本类型的对象。
对于同一个指针,指向的对象类型不同,当通过该指向发送消息时,调用的方法会不同,此时,方法的调用是多态的。
NSObject *obj = @"abc";
NSString *desc = [obj description];
obj = [[TRPoint alloc]init];
desc = [obj description];
3.2 编译期类型和运行期类型
在多态下,父类的引用可以指向子类的对象。但当编译器编译程序时,编译器无法确定引用指向的对象时什么类型,所以编译会将引用当做父类型做编译检查,如果调用的方法父类中没有,则编译警告。但程序运行期间,通过父类的引用向子类对象发消息时,调用的方法却是子类的方法。
TRAnimal *a = [[TRDog alloc]init];
编译将a当做TRAnimal类型来看,叫编译器类型
运行时,系统将a当做TRDog类型来看,叫运行期间类型。
3.3 多态的好处
可以让我们设计出更加合理的架构,写出更加通用的程序,更加提高程序的可维护性和扩展性。
3.4 多态的表现
1)多态在方法的参数的表现
2)方法的返回值
3)数组或集合体现多态
//创建项目Ploymorphism
@interface TRAninal : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int size;
- (void)eat;
-(void)sleep;
- (id)initWithName:(NSString*)name andSize:(int)size;
@end
@implemenatation TRAnimal
- (void)eat
{
NSLog(@"动物吃");
}
-(void)sleep
{
NSLog(@"动物吃");
}
- (id)initWithName:(NSString*)name andSize:(int)size
{
if(self = [super init])
{
self.name = name;
self.size = size;
}
return self;
}
@end
//创建子类TRDog
@interface TRDog : NSAnimal
- (void)watchHome;
@end
@implementation
- (void)watchHome
{
NSLog(@"狗看家");
}
- (void)eat
{
NSLog(@"狗吃骨头");
}
@end
//main.m中:
void testPoly()
{
TRAnimal *a = [[TRDog alloc]initWith:@"阿黄" andSize:50]; //父类型指针指向子类型对象
[a eat]; //调用子类的方法 打印了"狗吃骨头"
[a sleep]; //调用父类的方法 打印了"动物睡"
//[a watchHome];
[a release];
}
//参数具有多态特征
void show(TRAnimal* a)
{
[a eat];
[a sleep];
}
void testParameter()
{
TRDog *dog = [[TRDog alloc]init];
show(dog); //a = dog;
TRCat *cat = [[TRCat alloc]init];
show(cat);
[dog release];
[cat release];
}
typedef enum Animals{
ANIMAL,
DOG,
CAT,
}Animals;
//返回值上的多态
TRAnimal* get(Animals type)
{
if(type == ANIMAL)
{
return [[[TRAnimal alloc]init]autorelease];
}
if(type == DOG)
{
return [[[TRDog alloc]init]autorelease];
}
if(type == CAT)
{
return [[[TRCat alloc]init]autorelease];
}
}
//数组或集合体现多态
void testArrayPoly()
{
TRAnimal* as[5];
as[0] = [[TRDog alloc]init];
as[1] = [[TRCat alloc]init];
as[2] = [[TRAnimal alloc]init];
as[3] = [[TRDog alloc]init];
as[4] = [[TRCat alloc]init];
for (int i = 0; i<5; i++)
{
[as[i] eat];
}
}
int main(int argc, const char * argv[])
{
@autoreleasepool{
testPoly();
}
return 0;
}