---------------------- android培训、java培训、期待与您交流! ----------------------
第七天
4 继承
(1)提高代码的复用性。
(2)让类与类之间产生了关系。有了这个关系,才有了多态的特性。
注意:
千万不要为了获取其他类的功能,简化代码而继承。
必须是类与类之间有所属关系才能继承。所属关系 :is a;谁是谁中的一种。
Java语言中只支持单集成,不支持多继承。(即是说:一个类只能继承一个父类,不能继承多个父类。)因为多继承带来安全隐患:
当多个父类中定义了相同功能时,但功能内容不同时,子类对象不确定要运行哪一个。
class A
{
void show()
{
System.out.println("a");
}
}
class B
{
void show()
{
System.out.println("b");
}
}
class C extends A,B
{
C c=new C();
c.show();
}
子类对象c不确定要打印a还是b。
Java中有多实现(C++中的多继承)
Java支持多层继承。就是B继承A,C继承B。
如何使用一个集成体系中的功能呢?
先查阅体系父类的描述,因为父类中定义的是该体系中的共性功能。
通过了解共性功能,就可以知道该体系的基本功能。
那么在具体调用时,要创建最子类的对象,为什么呢?
(1)因为有可能父类不能创建对象。
(2)创建子类对象可以使用更多的功能,包括基本的和特有的方法。
聚合:
组合:
聚合:表示两个对象之间是整体和部分的弱关系,部分的生命周期可以超越整体。如电脑和鼠标,就可以用一下图形表示:
组合:表示两个对象之间是整体和部分的强关系,部分的生命周期不能超越整体,或者说不能脱离整体而存在。组合关系的“部分”,是不能在整体之间进行共享的。如人和眼睛的关系:
不过,如果你要说,眼睛可以移植,是不是说可以脱离人而存在,它们就不是组合关系了?其实,UML中对象的关系都是在相应的软件环境或实际场景下定义的,这里区别聚合和组合的关系,关键还是在于它们之中整体和部分的关系强、弱,以及它们之间的依附关系。如果刚才说眼睛可以移植给别人,那你也可以把它认为是聚合,这都要结合实际场景来说明。
另外两个比较重要的关系是关联和依赖:
关联:简单的可以认为,如果一个类作为了另外一个类的属性,那一定是关联关系。但你要知道,聚合是一种特殊的关联,而组合又是一种特殊的聚合。一般的关联我们认为是平级的、无整体部分关系的,如朋友关系。
依赖:对象之间的临时关系,临时性体现在不超越方法的周期。简单的说,在方法使用到的,如参数类,就一定是依赖关系。
最后想说一句,实际工作中,我并没有发现区分这四种关系有多么大的用处,也许自己还没有碰到,只是觉得可能每个学习它的同学总想把它搞清楚,但实际工作中作用并不明显。
1 组合和聚合是有很大区别的,这个区别不是在形式上,而是在本质上:
比如A类中包含B类的一个引用b,当A类的一个对象消亡时,b这个引用所指向的对象也同时消亡(没有任何一个引用指向它,成了垃圾对象),这种情况叫做组合,反之b所指向的对象还会有另外的引用指向它,这种情况叫聚合。
在实际写代码时组合方式一般会这样写:
A类的构造方法里创建B类的对象,也就是说,当A类的一个对象产生时,B类的对象随之产生,当A类的这个对象消亡时,它所包含的B类的对象也随之消亡。
聚合方式则是这样:
A类的对象在创建时不会立即创建B类的对象,而是等待一个外界的对象传给它
传给它的这个对象不是A类创建的。
现实生活中:
人和人和手,脚是组合关系,因为当人死亡后人的手也就不复存在了。人和他的电脑是聚合关系。class Hand{
2 }
3 class Computer{
4 }
5 组合:
6 class Person{
7 private Hand hand;
8 public Person(){
9 hand = new Hand();
10 }
11 }
12 聚合:
13 class Person{
14 private Computer computer;
15 public setComputer(){
16 computer = new Computer();
17 }
18 }
子父类中变量的特点:
如果子类中出现非私有的同命成员变量时。
子类要访问本类中的变量,用this。
子类要访问父类中的同命变量,用super。
Super的使用和this基本一致。
子父类中函数的特点-----覆盖
当子类出现和父类一模一样的函数时,
当子类对象调用该函数时,会运行子类函数的内容。
如同父类函数被覆盖一样。
这就是函数的另一个特性-----重写(覆盖)。
当子类继承父类,但是子类虽然具备了父类的某个功能,但是该功能的内容却和父类的不一样,这时,就没有必要定义新功能,而使用覆盖,保留父类的功能定义,并重写功能内容。
class ExtendsDemo2
{
public static void main(String[] args)
{
Zi z=new Zi();
z.speak();
}
}
class Fu
{
void show()
{
System.out.println("fu show");
}
void speak()
{
System.out.println("vb");
}
}
class Zi
{
void speak()
{
System.out.println("java");
}
void show()
{
System.out.println("zi show");
}
}
结果:
java
注意:
(1)子类覆盖父类,必须保证子类的权限大于等于父类的权限,才能覆盖,否则编译失败。
(2)静态只能覆盖静态。
注意:
重载:只看同名函数的参数列表。
重写:子父类方法要一模一样。
子父类中构造函数的特点----子类实例化过程
class ExtendsDemo3
{
public static void main(String[] args)
{
Zi z=new Zi();
}
}
class Fu
{
Fu()//这是构造函数
{
System.out.println("fu run");
}
}
class Zi extends Fu
{
Zi()
{
//这里隐藏了super();
System.out.println("zi run");
}
}
结果:
fu run
zi run
在对子类对象进行初始化时,父类的构造函数也会运行。
那是因为子类的构造函数默认第一行有一条隐式的语句 super();
super():会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是 super()。
为什么子类一定要访问父类中的构造函数。
因为父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。
所以子类在对象初始化时,要先访问一下父类中的构造函数。
如果要访问父类中指定的构造函数,可以通过手动定义super语句方式来指定。
注意:super语句一定定义在子类构造函数的第一行。
结论:
子类中的所有的构造函数,默认都会访问父类中空参数的构造函数。
因为子类每一个构造函数内的第一行都有隐式的super();
当父类中没有空参数的构造函数时,子类必须手动通过super语句来指定要访问父类的构造函数。
当然,子类的构造函数第一行也可以手动指定this语句来访问本类的构造函数。
子类中至少有一个构造函数访问父类中的构造函数。
为什么super和this不能同时存在?
因为他们都需要写在第一行
为什么super和this都需要写在第一行?
因为初始化动作需要先做
4.6 final(最终)关键字
(1)final可以修饰类、方法、变量。
(2)final修饰的类不可以被继承。避免父类被子类复写。
(3)final修饰的方法不可以被覆盖。
(4)final修饰的变量是一个常量,只能被赋值一次。
(5)内部类只能访问被final修饰的局部变量。
常量的书写规范:所有字母都大写,如果有多个单词组成,则多个单词间通过“_”连接。