继承:
好处:
1,继承的出现,提高了代码的复用性。
2,继承的出现,让类与类之间产生了关系。而这个关系出现,就导致面向对象的第三个特征,多态的产生。
简单说:继承就是多态的前提。
Java中支持单继承,不支持多继承(其实更确切的说,是java将多继承进行改良,避免了安全隐患的产生)。
class A{void show(){System.out.println("a");}
class B{}{void show(){System.out.println("b");}
class C extends A,B{}//这就是多继承,一个子类同时有多个父类。在java中是允许的。
C c = new C();
c.show();//到底是运行a,还是运行b呢?所以这就是安全隐患。为了避免,所以java不支持这样继承。
简单说:一个儿子只能有一个爹,不能同时有多个爹。
Java中可以存在多层(多重)继承。
class A{}
class B extends A{}
class C extends B{}
这时就出现了继承体系。
记住一个原则:
1,A类中定义是这个体系中的最共性的功能。
2,要使用这个体系的功能,建议建立最子类的对象来完成调用。
父类的由来都是子类不断向上抽取而来的。
就代表着,A中定义的功能是该体系中最共性的功能。
所以要了解这个体系的功能,只要参考A类中的功能即可。
了解后,需要建立对象来调用这些功能,那么建立哪个类的对象好呢?
建议建立C的对象,因为C中可以使用A类中的共性功能,也可以使用C类中的特有功能。
简单说:
要使用一个继承体系,原则:
1,参阅父类功能,
2,建立最子类对象。
什么时候定义继承呢?
继承是用于程序设计的。
只要一个事物是另一个事物的一种。就可以用继承体现。
猫或者虎都是猫科这类事物中的一种。就是可以进行继承。
狗或者狼都是犬科,也可以继承。
猫科,犬科,都是哺乳动物中的一种。也可以进行继承。
简答的分析:怎么判断是另一个事物的中的一种呢?
那么可以先视为可以继承,用更简单的方式判断,一个类如果继承了另一个类,那么这个类是否应该具备另一个类的所有的成员。如果可以。继承有效,如果不可以,那么无法继承。
注意的是:不要为了获取另一个类中的部分功能,而继承。这样是仅仅为了提高复用性而继承,并没有判断事物之间的关系。这样不可取。
class A
{
void method1(){}
void method2(){}
}
class B //extends A//如果为了提高复用,让B继承A这样,B就不用在定义method1()方法了,
//但是B也获取到它不应该具备的method2().
//那么这样的继承不可以存在。
//但是我们发现,虽然A和B之间没有继承关系,但是他们有共性的内容,
//那么就可以向上抽取。。
{
void method1(){}
void method3(){}
}
class C
{
void method1(){}
}
class A extends C
{
void method2(){}
}
class B extends C
{
void method3(){}
}
*/
class Person
{
String name;
int age;
}
继承出现后,在子父类中的成员特点。
成员:
成员变量。
成员函数。
构造函数。
//在子父类中成员变量的特点。
当子父类中出现同名的成员变量时,
这是为了区分两个变量,
在子类,用this调用的是子类的变量。
用super调用的是父类中的变量。
super其实和this的用法很相似。
this代表是本类对象的引用
super代表的是父类的存储空间。
this可以区分局部变量和成员变量重名的情况。
super可以用于区分子父类中成员变量重名的情况。
这个部分作为了解。这是引出一个关键字super。
不是重点,因为,属性在类中通常都是私有的,而且父类定义完了以后,
子类一般是不会定义同名变量的。而是直接使用父类的变量即可
所以开发并不常见。而面试却很常见。
注意:在访问上,子类不可以直接访问父类中的私有成员。
可以通过间接的形式访问。
子父类中成员函数的特点:
特殊情况:
当子父类中出现一模一样的函数时,
子类对象在调用该函数时,运行的是子类中的函数。
父类中的函数好像被覆盖一样。
这就是函数的另一个特性:覆盖(复写,重写)override 函数的另一个特性: 重载 overload
覆盖:在子父类中,如果出现一模一样的函数时,就会发生覆盖操作。
子父类中的构造函数的特点:
通过结果发现,子类的构造函数运行前,父类的构造函数先运行了?
为什么呢?
原因在于在子类的构造函数的第一行,其实就一条默认的隐式语句 super();
super():和this():用法是一样的。
this();调用了本类中的构造函数。
super():调用了父类中的构造函数。
子类的构造函数中为什么都有一个默认的super()语句呢?
子类在实例化的时候,因为子类继承了父类的成员数据,
所以必须要先看父类是如何对自己的成员进行初始化的。
#################################################################
#子类的所有构造函数默认都会访问父类中空参数的构造函数。 #
#当父类中没有定义空参数的构造时, #
#子类必须通过super语句或者this语句 #
#明确指定要访问的子类中或者父类中的构造函数。 #
# #
#简单说:子类的构造函数无论如何,都必须要访问父类中的构造函数。 #
#要明确父类的初始化过程. #
#################################################################
super语句用于访问父类的初始化,而初始化动作要先完成,所以super语句必须定义在构造函数的第一行。
那么就和曾经的this语句冲突了。因为this语句也要定义在构造函数的第一行。
所以一个构造函数中,只能有一个要么this语句,要么super语句。
而且不冲突,因为子类中至少会有一个构造函数回去访问父类中的构造函数。一样可以完成父类的初始化。
这个就是子类的实例化过程。
继承的弊端:打破的封装性。
如果恶意继承并进行不正确的覆盖,会导致原功能的错误。打破了封装性。
不让其他类继承可以解决这个问题。
这就需要一个关键字来完成 :final(最终)
final:作为一个修饰符。
1,它可以修饰类,可以修饰方法,可以修饰变量。
2,final修饰的类是一个最终类,不可以被继承。
3,final修饰的方法不可以被覆盖。
4,final修饰的变量的是一个常量,只能被赋值一次。这个赋值指的是显示初始化赋值。
什么时候将变量修饰成final的呢?
通常在程序中会使用一些不会变化的数据。也就是常见的一些常量值。比如 3.14
那么这个数据直接使用是可以的,但是不利于阅读,所以一般情况下,都会被该数据起个容易阅读的名称。
final double PI = 3.14;
final修饰的常量定义一般都有规范书写,被final修饰的常量名称,所有字母都大写。
如果由多个单词所组成,每一个单词间用_连接。