继承(五)

继承

继承,是面向对象三大特征之一,继承的出现,是为了减少很多的冗余代码,因为是把各个类中,把相同特征和行为收集到另一个类中,然后这些类继承于这个集中类,可以把这个集中类的所有的特征和行为都继承过来。然后使用。

说到类了,则应该提到子类和父类,这两者是同时存在的,不能说我是父类,也不能说我是子类,两者相互依存。谁是谁的父类,谁是谁的子类。

子类继承父类,是单向的,两者之间不能相互继承。

继承还具有传递性,用白话来说就是:B类如果继承于A类,C类继承于B类,此时B类就是A类的子类,C类就是B类的子类;A是B的父类,B是C的父类。这样的话,B(子类)就可以继承了A(父类)的所有实例变量和方法,这里说得是所有的实例变量和方法,也包括private修饰的实例变量,但是private修饰的实例变量,继承过来后不能直接使用。C(子类)继承了B(父类)的所有实例变量和方法,包括private修饰的实例变量。

在OC中,所有的类的最终父类都是NSObject(这个类没有父类了,叫做根类)。

子类继承过来的父类方法,可以重写。这里注意的是,1、重写父类方法,不需要再声明,就是不需要在.h文件里声明。2、重写父类方法,必须与父类方法名一样。

一个练习:

Animal.h:继承于NSObject


#import <Foundation/Foundation.h>

@interface Animal : NSObject{

NSString *_kind;

NSString *_color;

@private

NSString *_name;

}

- (void)sayHi;

- (NSString *)kind;

- (NSString *)color;

- (void)setKind :(NSString *)kind;

- (void)setColor :(NSString *)color;

@end

Animal.m


#import "Animal.h"

@implementation Animal

- (void)sayHi{

NSLog(@"动物乱叫...");

}

- (NSString *)kind{

return _kind;

}

- (NSString *)color{

return _color;

}

- (void)setKind :(NSString *)kind{

_kind = kind;

}

- (void)setColor :(NSString *)color{

_color = color;

}

@end

Cat.h:继承于Animal类


//继承的时候,使用#import引入父类头文件

#import "Animal.h"

@interface Cat : Animal{

- (void)test;

@end

Cat.m


#import "Cat.h"

@implementation Cat

- (void)test{

_kind = @" ";

_color = @" ";

//    _name = @" ";

}

//重写父类继承过来的方法。

//1、不用声明

//2、方法名必须与父类的相同

- (void)sayHi{

NSLog(@"喵了个咪的..");

}

@end

SmallCat.h:继承于Cat类


#import "Cat.h"

@interface SmallCat : Cat

@end

SmallCat.m


#import "SmallCat.h"

@implementation SmallCat

@end

main.m


#import <Foundation/Foundation.h>

#import "Cat.h"

#import "SmallCat.h"

int main(int argc, const char * argv[])

{

@autoreleasepool {

Cat *c = [[Cat alloc]init];

[c sayHi];//2015-04-15 10:47:46.528 OCLesson3_继承[951:41625] 动物乱叫...

[c setKind:@"哈士奇.."];

[c setColor:@"白色.."];

NSLog(@"%@",c.kind);//2015-04-15 10:54:16.185 OCLesson3_继承[1045:44417] 哈士奇..

NSLog(@"%@",c.color);//2015-04-15 10:54:16.186 OCLesson3_继承[1045:44417] 白色..

c.kind = @"蓝猫..";

c.color = @"白色的...";

NSLog(@"%@",c.kind);//2015-04-15 10:54:16.186 OCLesson3_继承[1045:44417] 蓝猫..

NSLog(@"%@",c.color);//2015-04-15 10:54:16.186 OCLesson3_继承[1045:44417] 白色的...

[c sayHi];

SmallCat *sc = [[SmallCat alloc] init];

[sc sayHi];

}

return 0;

}

self:

self是一个指向自己的指针(可以打印出来看地址),一般用在类内,用来调用自己的方法,当然这里得说明白,self如果在实例方法里,只能调实例方法,在类方法里只能调类方法,不允许在实例方法里调类方法,在类方法里调实例方法。

self调自身的方法形式为:[self 本类方法名]。

super:

super 的出现,是因为在子类里,self调了自己重写父类的方法时,执行的是本类中的重写后的方法(若是本类中没有调用的方法,系统会自动去父类找,若是父类没有这个方法,就会去父类的父类找,一直找到NSObject根类,若是还没有,则程序崩溃),此时若是想执行父类的那个方法,那么就用到了super来调用父类的方法。

self是指针,super不是指针,也不是变量,super是编译器的指令。专门用来访问父类的方法。若是在本类中使用super(将其当成变量赋值或者当成指针打印地址等),都会抱一个错误,叫做“未定义super”。

super与self一样,在实例变量里只能调用实例方法,在类方法里只能调用类方法,不允许在实例方法里调类方法,在类方法里调实例方法。

一个例子:

Person.h:继承NSObject


#import <Foundation/Foundation.h>

@interface Person : NSObject

- (void) sayHi;

- (void)test;

+ (void)test2;

+ (void)test3;

@end

Person.m


#import "Person.h"

@implementation Person

- (void) sayHi{

NSLog(@"吃了吗?");

}

//测试self

- (void)test{

//self就是一个指向自己的指针。

//self作用:在类内调用自己的方法。

//self在实例方法中可以调用实例方法,

NSLog(@"%p",self);//2015-04-15 14:50:24.972 OCLesson3_Self_Super[1733:91671] 0x100114040

[self sayHi];

//self在实例方法中,不能调用类方法。

//    [self test2];

}

+ (void)test2{

//self在类方法中可以调用类方法。

[self test3];

//self在类方法中,不能调用实例方法。

//    [self test];

}

+ (void)test3{

}

@end

Student.h:继承于Person


#import "Person.h"

@interface Student : Person

- (void)stuTest;

+ (void)stuTest1;

@end

Student.m


#import "Student.h"

@implementation Student

- (void)sayHi{

NSLog(@"约吗?");

}

//测试super

- (void)stuTest{

//super可以调用父类的方法。

//super是一条编译器指令(不是变量,也不是指针),用来访问父类方法。

//    NSLog(@"%p",super);//报错super未定义

[super sayHi];

[super test];//实例方法里可以调实例方法。

//    [super test2];//实例方法不能调类方法。

}

+ (void)stuTest1{

[super test2];

[super test3];//super在类方法中调用类方法

//    [super test];//super在类方法不能调用实例方法。

}

@end

main.m


#import <Foundation/Foundation.h>

#import "Person.h"

#import "Student.h"

int main(int argc, const char * argv[])

{

@autoreleasepool {

//        Person *p1 = [[Person alloc] init];

//        NSLog(@"%p",p1);//2015-04-15 14:50:24.972 OCLesson3_Self_Super[1733:91671] 0x100114040

////        [p1 sayHi];

//        [p1 test];//2015-04-15 14:49:07.067 OCLesson3_Self_Super[1710:90956] 吃了吗?

Student *stu = [[Student alloc] init];

[stu sayHi];//2015-04-15 15:04:35.318 OCLesson3_Self_Super[1798:96571] 约吗?

[stu stuTest];//2015-04-15 15:05:43.319 OCLesson3_Self_Super[1811:97083] 吃了吗?

}

return 0;

}

完整的初始化方法:

完整的初始化方法,要加上一个判断,就是遵循一个顺序,先对父类进行初始化,如果父类初始化成功后,就给子类返回一个地址,子类接收到地址后,再给自己初始化。如果在父类初始化的时候,失败了,就会给子类返回一个空值,子类接收到这个空值后,不再执行自己的初始化方法,而是把空值返回出去,让程序停止下来。

上面实现过程代码如下:

Person.h继承于NSObject


#import <Foundation/Foundation.h>

@interface Person : NSObject{

NSString *_name;

NSInteger _age;

}

- (instancetype)initWithName :(NSString *)name age:(NSInteger)age;

@end

Person.m


#import "Person.h"

@implementation Person

- (instancetype)initWithName :(NSString *)name age:(NSInteger)age{

//初始化从父类继承过来的东西。

//1.初始化失败,返回值为nil(空)

//2.初始化后会换地址,(NSMutableArray)类簇

self = [super init];

//一旦从父类继承的东西初始化失败,就不要再给自己的实例变量赋值了。

if(self){

_name = name;

_age = age;

}

//如果初始化失败,返回nil

//如果初始化成功返回地址。

return self;

}

@end

//指定初始化方法(课后找找)

这里,先用super 调用父类(NSObject)的init初始化方法,然后用self指针来接收[super init]返回的地址。如果父类的初始化失败,返回一个空给self,当判断self的值,如果不为空,才为自己的类进行初始化,否则返回空出去。

值得注意的是:[super init]指的是super调用的是父类的的初始化方法,不一定是init,也可能initXXX。(一般是继承之后再有一个子类继承它,才会出现这种状况)。

一个例子:

Student.h继承于Person

自己声明了一个初始化方法,在实现初始化方法时,先用self接收[super initXXX ]父类初始化的结果,如果成功,返回地址后自己继续进行初始化。


#import "Person.h"

@interface Student : Person{

NSString *_num;

}

- (instancetype)initWithName:(NSString *)name age:(NSInteger)age num:(NSString *)num;

@end

Student.m


#import "Student.h"

@implementation Student

- (instancetype)initName:(NSString *)name age:(NSInteger)age num:(NSString *)num{

self = [super initName:name age:age];

if (self) {

_num = num;

}

return self;

}

@end

self = [super initName:name age:age];这一句就是说,super调了父类(Person)的初始化方法,如果成功,self接收地址,后继续执行_num = num;这个自己的初始化。

相同的:

CollageStudent.h继承于Student


#import "Student.h"

@interface CollageStudent : Student{

NSString *_major;

}

- (instancetype)initName:(NSString *)name age:(NSInteger)age num:(NSString *)num major: (NSString *)major;

@end

CollageStudent.m


#import "CollageStudent.h"

@implementation CollageStudent

- (instancetype)initName:(NSString *)name age:(NSInteger)age num:(NSString *)num major: (NSString *)major{

self = [super initName:name age:age num:num];

if (self) {

_major = major;

}

return self;

}

@end

都是一样的执行方式,一步一步往上,从最高的父类开始初始化,子类self接收,判断后才对自己初始化。

便利构造器:

便利构造器是一个类方法,目的是为了方便使用者的使用,但是在写便利构造器的时候,会稍微麻烦。

注意的是:

1、便利构造器的出现,必须与对应的初始化方法一起出现;

2、方法名是以类名开头(类名必须全部小写),后面跟着初始化方法init后面的全部东西。如:初始化方法是:

- (instancetype ) initName :(NSString *)name age :(NSInteger)age;

则便利构造器为:(Person类)

+ (instancetype)personName :(NSString *)name age :(NSInteger)age;

便利构造器的实现,是把alloc这个内存分配的步骤放在构造器里,在main.m等里使用便利构造器的时候,不需要再alloc。

例子:

Person.h


#import <Foundation/Foundation.h>

@interface Person : NSObject{

NSString *_name;

NSInteger _age;

}

- (instancetype ) initName :(NSString *)name age :(NSInteger)age;

//便利构造器(类方法)

//写便利构造器前提要有一个对应的初始化方法

//方法名以类名开头,类名小写,后面跟着init后面的所有东西。

+ (instancetype)personName :(NSString *)name age :(NSInteger)age;

@end

Person.m


#import "Person.h"

@implementation Person

- (instancetype ) initName :(NSString *)name age :(NSInteger)age{

self = [super init];

if (self) {

_name = name;

_age = age;

}

return self;

}

//便利构造器实现

+ (instancetype)personName :(NSString *)name age :(NSInteger)age{

Person *p = [[Person alloc]initWithName:name age:age];

return p;

}

@end

Person *p = [[Person alloc]initName:name age:age];这句的意思是,把本该在main里alloc的步骤,移到.m实现文件中。

main.m


#import <Foundation/Foundation.h>

#import "Person.h"

int main(int argc, const char * argv[])

{

@autoreleasepool {

Person *p = [[Person alloc]initName:@"小黑" age:18];

Person *p2 = [Person personName:@"小白" age:18];

}

return 0;

}

Person *p = [[Person alloc]initName:@"小黑" age:18]; 这个语句是直接调用初始化方法,需要alloc;

Person *p2 = [Person personName:@"小白" age:18];这个语句是调用便利构造器,不需要alloc。相对于alloc,方便很多。

时间: 2024-10-27 07:47:47

继承(五)的相关文章

C++ 类的继承五(多继承的二义性--虚基类)

//多继承的二义性--虚基类(了解为主) #include<iostream> using namespace std; /* 多继承在现在的项目开发中一般不使用,他会增加项目的复杂度 */ class Point{ public: Point(){ x = 1; y = 1; } int x; int y; }; class PointA :virtual public Point{ public: PointA(){ a = 2; } int a; }; class PointB :vir

10分钟带你光速入门运维工具之-Puppet

一.简介 当服务器数量达到一定的规模时,仅依靠人为完成批量部署服务器个资源的配置,运维工作将变得繁琐且容易出错,为了解决这一问题,我们应该怎么办呢?我们可以引入一批工具,这批工具可编写相应的manifests代码,运行它便可以自动完成所有的工作,目前比较流行的运维工具主要有:puppet,ansible, slackstack等,在这我们主要以puppet来展开话题. 在一些大型互联网企业中,运维自动化管理着几百甚至上千台服务器,它可以针对多台服务器进行统一操作,例如部署统一软件.进行统一上线维

《Java从0开始的成长之路》

大纲 这篇博文是我整理寒假一个月来的总结 作用一:主要是方便我以后复习,并尝试对Java虚拟机深度挖掘,希望各位前辈给予指点,我会潜心钻研,只希望水平更进一步. 作用二:闭关锁国终将遭遗弃,希望汇聚网络的开源力量,欢迎各位对我进行纠错和探讨,使得我这个井底之蛙窥得半片青空! 作用三:我会以教程+JDK源码分析+Java虚拟机内存分析+实例分析的方式进行回顾,希望可以起到给一些尚未涉及Java的朋友指路的作用,帮一些朋友少走点弯路,当然也欢迎指正交流,这对我来说意味着进步. 我于学海,仿若须臾!

PowerDesigner概念模型详解

PowerDesigner概念模型详解 环境 PowerDesigner 12.5 Windows XP 中文版 一.概念模型的重要性 PowerDeigner是最强大.最优秀的数据建模工具,是Sybase公司最伟大的产品.目前最新版本是12.5,从9一直用到现在,对PD的认识也是在逐步加深. 常常在工作中,看到大家用PD,都是用来建几个表,实际上是做PDM,上来就干这个,实际上,这么用PD,是对强悍的PD一种侮辱.PD仅仅是这么玩的吗? 数据库设计的步骤是什么,难道上来稍稍想一下就搞个pdm出

小程序02 wxml和wxss

微信小程序的排版就跟wxml和wxss有关,它们两者相当于HTML和CSS,其中wxml指定了界面的框架结构,而wxss指定了界面的框架及元素的显示样式. 一.wxml 界面结构wxmL比较容易理解,主要是由八大类基础组件构成: 一.视图容器(View Container): 二.基础内容(Basic Content) 组件名 说明 组件名 说明 view 视图容器 icon  图标 scroll-view 可滚动视图容器 text 文字 swiper 可滑动的视图容器 progress 进度条

C++对象模型——&quot;无继承&quot;情况下的对象构造(第五章)

5.2 继承体系下的对象构造 当定义一个object如下: T object; 时,实际上会发生什么事情呢?如果T有一个constructor(不论是由user提供或是由编译器合成),它会被调用.这很明显,比较不明显的是,constructor的调用真正伴随了什么? constructor可能内带大量的隐藏码,因为编译器会扩充每一个constructor,扩充程度视 class T的继承体系而定.一般而言,编译器所做的扩充操作大约如下: 1.记录在member initialization li

【《Objective-C基础教程 》笔记ch04】(五)OC中的继承inheritance机制

一.为什么需要继承 使用继承一方面继承了父类的特性,另一方便解决了重复代码维护问题. 二.继承之语法 1. @interface 子类名:父类名 2. OC只支持单继承,不支持继承多个父类. 3. 重构--移植和优化代码. 三. 继承的工作机制 1. 方法调度 子类拥有一个指向它父类的引用指针,消息传递时,OC的方法调度机制使用该信息来找到正确的实现方法,查找过程是现在子类中找,找不到再到父类中继续找. 2. 实例变量 1)继承实例源码 @interface Shape : NSObject {

Java从零开始学十五(继承)

一.继承作用 继承使用复用以前的代码非常容易,能够大大的缩短开发周期,降低开发成本,同时增加程序的易维护性 继承使重一个类A能够直接使用另外一个类B的属性和方法的一种途径 类A可以有自己的属性和方法 二.继承实现 类的继承格式 在Java中使用extends关键字完成类的继承关系,操作格式: class 父类{} // 定义父类 class 子类 extends 父类{} // 使用extends关键字实现继承 package com.pb.person; /* * 父类 */ public c

Swift学习——类的定义,使用,继承,构造等(五)

Swift学习--类的定义,使用.继承,构造等(五) 类的使用说明 1 使用class和类名来创建一个类名,比如: class student 2 类中属性的声明和常量和变量一样,唯一的差别就是他们的上下文是类 3 方法和函数声明也一样 // 1 ---- 简单的类介绍 class Student{ //变量学号初始化为0 var num = 0; //成员函数 func GetNum()->Int{ return num } } //创建类的实例对象 var student = Student

继承PagerAdapter类需要重写五个方法和ViewPager的点击事件方法小结

继承PagerAdapter类需要重写五个方法: public int getCount() { return pages.size(); } 返回page的长度 public boolean isViewFromObject(View arg0, Object arg1) { return arg0==arg1; } 判断instantiateItem(ViewGroup container, int position)返回的要加载的pager对象是不是view视图,是则返回true并显示,不