您的位置:首页 > 职场人生

黑马程序员---继承,抽象,接口

2015-06-30 11:08 525 查看
-----------android培训java培训、java学习型技术博客、期待与您交流!---------

继承(extends)

继承是面向对象的一个重要特征。

当多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继那个类即可。

这时,多个类可以称为子类,单独的这个类称为父类或者超类。

例如:猫和豹子都是猫科动物, 那么就可以说描述猫的类,是子类;而描述所有猫科动物的类,就是父类。

继承的好处:

1. 提高了代码的重用性。

2. 继承让类与类之间产生了关系,才有了多态的特性,事物可以被描述,而事物之间会有关系,继承就是关系中的一种。

(在继承前要分析是否真的有继承关系)

注意:千万不要为了获取其他类的功能,简化代码而继承, 必须是类与类之间所属关系才可以继承,所属关系:is
a (谁是谁中的一种)


父类是子类中不断抽取而来的,而抽取不是抽取代码,而是分析问题。


问题领域不同,思考方式和问题中对象之间的关系体现也不一样。


判断所属关系的方式

有没有关系先继承以下,继承完看父类中的内容是不是子类都应具备的,如果父类中有的功能不是子类所具备的,那么他们之间就不应该继承,

在Java中只支持单继承,不支持多继承(但Java保存了这种机制,用多实现来体现,后面会学到)

因为多继承容易带来安全隐患

当多个父类中定义了相同的功能,但功能内容不同时,

子类对象不确定要运行哪一个。

Java支持多层继承,也就是一个继承体系,如儿子继承父亲,父亲继承爷爷

代码体现:

class a{}

class b extends a {}

class
c extends b {}

如何使用一个继承体系中的功能呢?

想要使用继承体系,先查阅体系中父类的描述, 因为父类中定义的是该体系中共性的功能,

通过了解共性功能就可以知道该体系的基本功能,那么这个体系已经可以基本使用了。

那么在具体调用时,要创建最子类对象,

一是因为父类有可能不能创建对象,

二是创建子类对象可以使用更多的功能,包括父类的基本功能,也包括子类的特有功能。

简单一句话:查阅父类的功能,创建子类对象使用功能。

注意:Java中一个孩子只能有一个父亲。

聚集关系

1. 聚合:球队里面有球员,球员是球队中的一个,球队中少一个球员也可以。

2.
组合:事物的联系比聚合更紧密,身体和心脏的关系,不能分割。

下面我们来看一个继承程序示例

/*
继承代码演示
*/

//定义一个Person类
class Person
{
//定义姓名和年龄属性
String name;
int age;

//定义构造函数,对姓名和年龄进行初始化
Person(String name , int age)
{
this.name = name;
this.age = age;
}
//人有睡觉功能
void sleep()
{
System.out.println("睡觉了,呼呼呼~");
}
//人具备说话功能,说出自己的姓名年龄
void speak()
{
System.out.println("我叫:"+name+",今年"+age+"岁。");
}
}

//定义一个学生类,继承Person类
class Student extends Person
{
//定义构造函数
Student(String name , int age)
{
//super关键字代表父类,因为姓名和年龄在父类中进行了初始化动作,在这里可以被直接调用
super(name , age);
}

//学生有特有的学习功能
void study()
{
System.out.println("我爱学习~");
}
}

class   ExtendsDemo
{
public static void main(String[] args)
{
//建立学生对象,并传入姓名和年龄
Student s = new Student("潘机智",20);
//子类调用父类的speak功能
s.speak();
//子类可以单独设置name的值,所以不安全,还是建议把属性私有化
s.name = "张三";
s.speak();
//子类调用父类睡觉功能
s.sleep();
//子类调用自己特有的学习功能
s.study();
}
}


运行结果如图



子父类出现后,类中成员的特点:

类中成员:变量,函数,构造函数。

1. 变量

如果子父类中出现非私有的同名成员变量时,子类要访问本类中的变量,用this。

子类要访问父类中的同名变量用super(super和this的使用几乎一致)

this代表本类对象的引用,super代表父类对象的引用。

2. 函数

当子父类中出现和父类函数一摸一样的函数时,子类对象调用该函数会运行子类函数内容,如同父类的函数被覆盖一样。

这种情况是函数的另一个特征:重写(覆盖)

当子类继承父类,沿袭了父类的功能到子类中,子类虽具备该功能,但功能的内容却和父类不一致,

这是没有必要定义新功能, 而是使用覆盖特性,保留父类的功能定义,并重写功能内容。

(可以用覆盖的特性给程序提高扩展性)

注意:1.
子类覆盖父类,必须保证子类权限大于等于父类权限才可以覆盖

2. 静态只能覆盖静态(一般不用但要作为了解)

3. 子类中的私有方式不能被重写。

记住:重载只看同名函数的参数列表,重写还要看子父类重名函数是否一模一样(包括返回值类型)

3. 构造函数

在对子类对象进行初始化时,父类的构造函数也会运行,

那是因为子类的构造函数默认第一行有一条隐式的语句
super();

会访问父类中空参数的构造函数,而且子类中所有构造函数第一行都是super();

为什么子类一定要访问类中的构造函数呢?

因为父类中的数据子类可以直接获取,所以子类对象建立时,需要先查看父类是如何对这些数据进行初始化的,

所以子类在对象初始化时,要先访问一下父类中的构造函数。如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。

注意:super语句一定定义在子类构造函数第一行(谁用了super谁就是子类)

在子类的构造函数中至少有一个访问父类。

子类的实例化过程

结论:

子类所有的构造函数,默认都会访问父类中的空参数构造函数,因为子类每一个构造函数第一行都有一句隐式super()。

当父类中没有空参数构造函数时,子类必须手动通过super语句的形式来指定要访问的构造函数。

当然:

子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数,(意思就是子类中每一个构造函数不一定都会访问父类中的构造函数)

子类中至少会有一个构造函数会访问父类中的构造函数。

为什么this()和super()不能在同一个构造函数中?

因为它俩都必须要在第一行,因为初始化动作要先做。它俩都是初始化语句。

集成的弊端,打破了封装。

final关键字(最终)

因为有了继承,所以子类可以随意的复写父类中的内容,这就出现了安全隐患,那么就需要用final关键字来避免这个隐患的发生。

final作为一个修饰符具有以下的特点

1. final可以修饰类,方法,变量。

2. final修饰的类不可以被继承。

这样就避免被继承、被子类复写功能。

3. final修饰的方法不可以被覆盖。

4. final修饰的变量是一个常量,只能被赋值一次。

final既可修饰成员变量,又可以修饰局部变量。

当描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性, 给这个值起个名字,方便阅读,

而这个值不需要改变,所以先加上final修饰。

作为常量:常量的书写规范,所有的字母都大写,如果有多个单词组成,单词间通过“-”下划线连接。(写代码阅读性很重要)

5. 内部类只能访问被final修饰的局部变量。

抽象(abstract)

Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。

抽象类的由来:

多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,

只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。

特点:

1. 抽象方法一定在抽象类中

2. 抽象方法和抽象类都必须被abstract关键字修饰。

3. 抽象类不可以用new创建对象,因为调用抽象方法没有意义。

4. 抽象类中的抽象方法要被使用,必须由子类复写其所有的抽象方法后,建立对象调用。

如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。

(所以如果不想子类为抽象类,那么就复写所有抽象方法)

下面通过一个实例来说明抽象类的使用

/*
抽象类使用示例

雇员示例
需求:
公司中程序员有姓名,工号,薪水,工作内容。
项目经理除了程序员的所有功能外还有特有的奖金功能。
对给出的需求进行数据建模

分析:
先找出设计的对象,通过名词提炼法来发现他们之间的联系。
程序员
属性:姓名,工号,薪水
行为:工作
经理
属性:姓名,工号,薪水,奖金
行为:工作

程序员和经理不存在直接继承关系,虽然经理有程序员的属性,奖金是多出来的属性,可以后加进去
但是经理和程序员的工作内容不同,无法继承。

经理和程序员都是公司的雇员,所以可以将程序员和经理相同的地方进行抽取,建立体系
*/

/*
描述雇员,因为不知道雇员具体的工作内容,所以工作行为是抽象方法,雇员类也会是抽象类
*/
abstract class Employee
{
//定义雇员的属性,因为是雇员内部资料,所以要私有化
//雇员姓名
private String name;
//雇员工号
private String id;
//雇员工资
private double pay;

//定义构造函数,初始化雇员
Employee(String name , String id , double pay)
{
this.name = name;
this.id = id;
this.pay = pay;
}

//定义抽象的工作方法,因为不知道具体工作是什么,所以只有功能声明,没有功能主体
public abstract void work();
}

//定义类描述程序员,继承雇员类
class Programmer extends Employee
{
//定义构造函数,初始化程序员
Programmer(String name , String id , double pay)
{
//使用super语句,访问父类的构造方法
super(name , id ,pay);
}

//重写父类的工作抽象方法,并定义程序员特有的功能主体
public void work()
{
System.out.println("程序员 工作");
}
}

//定义类描述经理,继承雇员类
class Manager extends Employee
{
//定义经理特有的奖金属性,也是私有的
private double bouns;
//定义构造函数,初始化经理
Manager(String name , String id , double pay , double bouns)
{
//使用super语句访问父类构造函数
super(name , id , pay);
//奖金是经理特有的,所以要单独定义
this.bouns = bouns;
}

//重写父类的工作抽象方法,并定义经理特有的功能主体
public void work()
{
System.out.println("经理-------工作");
}
//重写输出方法
}

class AbstractDemo
{
public static void main(String[] args)
{
//创建程序员和经理对象,并调用工作方法
new Programmer("潘机智" , "18" , 8000).work();
new Manager("张经理" , "5" , 20000 , 5000).work();

}
}


抽象类与一般类的区别

1. 抽象类和一般类没有太大不同,该如何描述事物就如何描述事物,只不过该事物出现了一些看不懂的东西,

这些不确定的部分也是该事物的功能,需要明确出来。无法定义主体就通过抽象方法来表示。

2. 抽象类比一般类多了个抽象函数,就是在类中可以定义抽象方法。

3. 抽象类不可以实例化。

4. 抽象类虽然不能创建对象,但是也有构造函数。可以让子类实例化调用。

abstract修饰的函数不可以同时被一些关键词修饰

1. private

如果把抽象方法私有了,那子类就不能发现它,不能完成复写,而抽象方法就是用来复写的。

2. static

用static修饰过的抽象方法可以被类名直接调用,就不用创建对象, 抽象方法内容就没有意义了。

3. final

final就是用来防止父类被子类随意覆盖的,如果在抽象方法被final修饰,那么就无法完成覆盖。

抽象有一个特殊的地方

抽象类中可以不定义抽象方法,这样做仅仅是不让该类创建对象。

也可用于模板方法设计模式

什么是模板方法呢?

在定义功能时,功能的一部分是确定的,但是有一部分是不确定的,

而确定的部分在使用不确定的部分,那么这时就将不确定的部分暴露出去, 由该类的子类完成。

我们可以用模板方法完成一个小程序

/*
模板方法代码示例

需求获取一段程序的运行时间。

原理:将程序开始执行到之行结束的时间相减,得到运行时间。

思路:
1.想要获取运行时间的程序未知,可以将其抽象出去,通过继承重写的方法获得
2.利用java中提供的当前时间功能,得到程序开始和结束的时间,相减得到运行时间
*/

//定义获取运行时间的类,因为要获取运行时间的程序未知,所以是抽象类
abstract class GetTime
{
//定义获取运行时间方法,因为不想被复写,所以加final修饰符
public final void getTime()
{
//利用java中已有的方法获取程序开始执行时间
long start = System.currentTimeMillis();
//调用程序执行方法
program();
//获取程序结束执行的时间
long end = System.currentTimeMillis();
//结束时间-开始时间得到程序执行时间,并输出
System.out.println("毫秒"+(end - start));
}
//定义程序执行方法,因为程序未知,所以是抽象方法
abstract void program();
}

//定义程序类
class GetProgram extends GetTime
{
//复写program方法,写入要获取时间的程序
void program()
{
for(int x=0 ; x<1000 ; x++)
{
System.out.println(x+" ");
}
}
}

class  TemolateDemo
{
public static void main(String[] args)
{
//创建程序类对象
GetProgram gp = new GetProgram();

//调用获取程序运行时间方法
gp.getTime();
}
}


运行结果



接口(interface)

当一个抽象类中的方法都是抽象的时候,这时可以将该抽象类用另一种形式定义和表示,就是接口。

接口的出现将“多继承”通过另一种方式体现出来,即多实现。

格式

interface接口名{}

class用来定义类,interface用来定义接口(可理解为特殊类)

接口格式的特点

1. 接口中常见的定义:常量,抽象方法

2. 接口中的成员修饰符是固定的

成员常量:public static final

成员函数:public abstract

3. 接口中的成员都是公共的权限,都用public修饰

在使用中,常量可以不写public static final,方法可以不写public abstract,编译时Java会自动添加这些修饰符

因为这是固定的格式修饰符。但是为了阅读性,一般都要写上。

接口的特点

1. 接口是对外暴露的规则

2. 接口是程序的功能扩展。

3. 接口可以用来多实现

4. 接口之间是实现关系,而且类可以继承一个类的同时实现多个接口

5. 接口与接口之间可以有继承关系

接口与抽象类

共性:都是不断向上抽取出来的抽象的概念。

区别:

1. 抽象类体现继承关系,一个类只能单继承。

接口体现实现关系,一个类可以多实现。同时接口与接口之间有继承关系。

2. 抽象类是继承,是 "is a "关系。

接口是实现,是 "like a"关系。

3. 抽象类中可以定义非抽象方法,供子类直接使用。

接口的方法都是抽象,接口中的成员都有固定修饰符。

4. 抽象类中可以私有变量或方法。

接口中的常量和方法都是public修饰的权限。

接口的实例小程序

/*
接口使用演示示例
*/

//定义一个抽象类,用于描述汽车
abstract class Car
{
//定义每个车都有的run方法,但不知道具体是怎么跑,所以是抽象的
abstract void run();
//汽车都有的加油方法,汽车加油的动作是一样的,所以是一般方法
void jiaYou()
{
System.out.println("汽车加油");
}
}

//定义一个接口,敞篷
interface Changpeng
{
//有的汽车具有敞篷功能
public abstract void chang();
}

//定义一个类,描述宝马汽车,继承汽车类,实现敞篷接口
class BMW
{
//复写汽车的run方法,定义自己特有的功能主体
void run()
{
System.out.println("宝马——提速快!");
}
//复写敞篷方法,定义自己特有的主体功能
public void chang()
{
System.out.println("宝马*****敞篷");
}
}

//定义一个类描述奥拓,继承汽车类,但是奥拓不能敞篷
class AoTuo extends Car
{
//复写run方法,定义自己特有的功能主体
void run()
{
System.out.println("奥拓——提速太慢");
}
}

class CarDemo
{
public static void main(String[] args)
{
//创建宝马对象
BMW b = new BMW();
//调用敞篷,跑方法
b.chang();
b.run();

//创建奥拓对象,调用run方法
new AoTuo().run();
}
}


运行结果



谢谢大家的观看~

-----------android培训java培训、java学习型技术博客、期待与您交流!---------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: