javase学习第八天(继承和多态)

继承和多态

继承

定义

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

通过extends关键字可以实现类与类的继承:

格式: class 子类名 extends 父类名{}

被继承的类称为父类或基类、超类。

好处

提高代码的复用性:多个类相同的成员可以放到同一个类中;

提高代码的可维护性:如果功能的代码需要修改,修改一处即可,继承的类中自动被修改。

特点

1、只能单继承,不支持多继承:即一个类只能有一个父类,不能有多个父类;

2、可以多层继承: class A{};--> cass B extends A();-->class C extends B{}.

注意事项

1、子类只能继承父类中所有非私有成员(成员方法和成员变量);

2、子类不能继承父类的构造方法,但可通过super关键字去访问父类的构造方法;

3、不要为了部分功能而去继承。

案例:

 1 class Father{
 2     private int num1=10;
 3     public int num2=20;
 4     public void show(){
 5         System.out.println(num2);
 6         System.out.println(num1);
 7     }
 8     private void method(){
 9         System.out.println(num2);
10         System.out.println(num1);
11         
12     }
13 }
14 class Son extends Father{
15     public void function(){
16         // System.out.println(num1);//不能继承私有的num1
17         System.out.println(num2);
18     }
19 }
20 class Extends1Demo{
21     public static void main(String[]args){
22         Son s=new Son();
23         s.show();
24         // s.method();//不能继承私有方法
25         s.function();
26     }
27 }

 

继承中成员变量关系

在子类方法中访问一个变量 :

1、首先在子类局部范围找,即方法内部;

2、然后在子类的成员范围找,也就是子类的成员变量 ;

3、最后在父类的成员变量范围找(不能访问父类的局部范围) ;

4、如果还没有就报错。

 1 class Father {
 2     public int num = 10;
 3 }
 4 
 5 class Son extends Father {
 6     public int num = 20;
 7     public void show() {
 8         int num = 30;
 9         System.out.println(num);        //30(就近原则)
10         System.out.println(this.num);    //20(this修饰访问成员变量)
11         System.out.println(super.num);    //10
12     }
13 }

 

super关键字

和this使用相识, this 代表本类当前对象的引用 ,super 代表父类存储空间的标识(也可理解为父类对象的引用)

使用方式:

访问成员变量:

this.成员变量 ;super.成员变量(访问父类的成员变量,但不能访问父类的private变量)

访问静态成员时,也可用:父类名.静态成员

访问成员方法,(注意没有"."): this(...)访问本类的构造方法,根据括号内参数来区分访问哪个;

super(...)访问父类的构造方法,根据括号内参数来区分访问哪个。

访问成员方法: this.成员方法(),super.成员方法()

继承中构造方法的关系

1、子类中的所有的构造方法默认都会访问父类中空参数的构造方法,除非显示使用super/this调用了父类或者本类的其他构造方法;

2、在类中对本类或者父类构造方法的调用,只能是在构造方法中,不能在实例方法中调用构造方法(更不能在类方法中),原因:

-->1、实例方法被调用时,说明实例对象已经被创建完了,此时不能再使用this、super去初始化本地实例或是父类实例

-->2、类方法是在本类加载的时候就已经加载了,这时候实例对象还没有被创建出来。

子类初始化之前,一定要先完成父类数据的初始化(一个对象的创建意味着它的所有的父类都会被创建出来);

子类构造方法的第一条语句:

如果是this(...)表明调用的是本类的另一个构造方法,在另一个构造方法中还可以继续使用this(...)调用本类其他的构造方法,如果有多个构造方法的话,可以继续调用下去,但是不能递归调用,最终总会有一个构造方法,第一条语句不是this()了。

构造方法不能递归调用:如下错误

1 class A {
2     public A(int i){
3         this(1 ,2);
4     }
5     public A(int a ,int b){
6         this(2);
7     }
8 }

 

子类中所有的构造方法默认都会访问父类中空参数的构造方法(注意:子类每一个构造方法的第一条语句默认都是:super();除非显示this、super);

如果父类没有空参构造,子类的构造方法中必须显示调用父类带参构造super(...);

且super(...)或者this(...)必须出现在构造方法第一条语句上,否则,会出现父类数据的多次初始化。

父类,子类代码块的执行顺序

构造代码块顺序:

父类的静态代码块-->子类的静态代码块-->父类的构造代码块-->父类构造方法-->子类构造代码块-->子类的构造方法

 1 class Father{
 2     int age;
 3     static{
 4         System.out.println("static code block");
 5     }
 6     {
 7         System.out.println("code block 1");
 8     }
 9     public Father(){
10         System.out.println("no arguments" );
11     }
12     public Father(int age){
13         System.out.println("hava arguments");
14     }
15     {
16         System.out.println("code block 2");
17     }
18 }
19 class Son extends Father{
20     int age;
21     static{
22         System.out.println("son static code block");
23     }
24     {
25         System.out.println("son code block 1");
26     }
27     public Son(){
28         System.out.println("son -no arguments" );
29     }
30     public Son(int age){
31         System.out.println("son hava arguments");
32     }
33     {
34         System.out.println("son code block 2");
35     }
36 }
37 class FatherDemo{
38     public static void main(String[]args){
39         
40     Son s=new Son();
41     }
42 }

结果:

类加载过程和对象初始化过程

类加载的时候执行的是类的初始化(静态代码块、有继承关系的类,从上到下) 对象初始化过程

1、先执行的是父类成员变量的初始化(如果父类成员是另一个对象的引用的话,则先对引用的类加载,然后成员变量初始化,然后成员变量初始化,然后构造代码块、然后构造方法;完成后,将引用赋给成员变量),再父类构造代码块,再父类构造方法。

2、然后子类成员变量的初始化(如果是另一个对象的引用的话,过程如上),子类构造代码块,子类构造方法

一个类中不可包含自身实例对象的引用。 当此类对象初始化的时候,先加载类,然后对成员变量赋初始值,然后是显式赋值,在显式赋值的时候发现是本类的构造方法的调用,再去调构造方法; 而本类的构造方法还是对成员变量赋值,在赋值的时候还去调用构造方法,造成了构造方法的迭代

继承中成员方法的使用

子父类中存在同名和不同名的成员方法 通过子类对象去访问一个实例方法:

1、首先在子类中找(是否子类进行了重写,或者是子类特有的方法)

2、然后在父类中找(子类没有重写,而是从父类继承而来的)。

方法的重写

定义:子类中出现和父类一模一样的方法声明,称为方法覆盖(override)/重写(overwrite).

应用条件: 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样既沿袭父类的功能,又有子类特有的内容;

方法的重写时多态实现的条件。

重写注意事项:

1、父类中私有方法不能被重写,编译报错

2、子类重写父类方法时,访问权限不能更低(后面讲),否则编译报错

3、子类重写父类方法,返回值类型可以相同,或者是父类返回值类型的子类型

4、父类的实例方法(非静态方法),子类不能重新定义为静态方法

5、父类静态方法(类方法),子类也必须通过静态方法进行“重写”(虽然编译和使用都不报错,但其实这个算不上方法重写)

6、子类中重写父类方法,按照重写的原则(访问权限不能变小,返回值同类或者子类,方法名相同,形参列表相同);否则子类中定义的同名方法就是方法的重载(继承而来的方法和子类定义的方法构成重载),重载就必须让参数列表不同。如果子类方法只与继承自父类的方法返回值不同,不能构成重载

7、子类如果想重写父类的方法,最好是让方法的签名一模一样

(重写方法的一个重要用途就是:父类的引用能够指向子类的方法,但是静态方法的“重写”,在多态中依然调用的是父类的方法,所以,从这个角度上来讲,子类对父类的静态方法的重写不能算是真正方法的重写)。

注意

父类的私有变量子类不可继承但在子类的构造方法中,使用super调用父类的构造方法那么,对成员变量的赋值,到底是赋给了父类的成员变量?还是子类的成员变量?

可以理解为:整个父类的对象都在子类对象中,值确实是赋给了父类的成员变量,但是子类通过方法可以使用,和自己的变量没有区别。这就是继承的好处。

final关键字

final关键字是最终的意思,可以修饰类、成员变量、成员方法

特点:

修饰类:类不能被继承(不能放在extends)之后 修饰变量,变量变成常量,只能被赋值一次,不论子类还是本类,都不能被修改;

修饰方法:方法不能被重写(子类有使用权,没有修改权);

修饰局部变量: 在方法内部,该变量不可以被改变;

在方法声明上,分基本类型和引用类型两种:

基本类型:这个参数的值不能被改变;

引用类型:这个参数指向的地址值不能被改变。

1 public void testFinalVar(final int i){
2     //i = 1; // error,不能再对final变量赋值
3 }
4 public void testFinalVar(final String str){
5     //str = null; //error不能再对final变量赋值
6 }

 

赋值情况:

类中非static的final变量(实例final变量)可以在声明的时候赋值,如果声明的时候没有赋值的话,就必须在以下两个地方赋值,一个是构造代码块中,一个是构造方法中。如果这两个地方也没有赋值的话,编译报错。

如果是static修饰的final变量(类变量)的话,则只能在两个地方赋值:声明的时候,或者是在静态代码块中。

经过验证: 若类中的final成员变量在声明时没有赋值,并且存在多个构造方法的话,则在每个构造方法中都应该显示的为这个final变量赋值(可以不同),或者可以抽取到构造代码块中进行赋值; 多个构造方法中,不能互相调用。

多态(polymorphism)

定义

多态:某一个事物,在不同时刻表现出来的不同状态。

事物的引用分为两种类型:编译时类型,声明时指定的类型;运行时类型,实际赋给这个变量的对象的类型。

如: Student s = new Student();

等号左边就是编译时的类型,s在编译时指定的类型是Student,等号右边是运行时实际赋值给这个变量的值。 如果等号左右两边的类型不一致,就有可能出现了多态。

父类引用不能调用子类特有的方法,因为使用了父类的引用,就代表站在父类的角度来看待当前的子类对象,只能看到从父类继承而来的特性,或者是子类重写的父类的方法。成员变量没有多态性,只能看到父类的成员变量。

多态的前提条件

1、有继承关系(没有继承关系的话,不同类型的变量是不能赋值的)

2、有方法重写(没有方法的重写的话,始终调用的是父类的方法)

3、有父类引用指向子类对象(不使用父类的引用的话,始终调用的是子类的方法)

成员访问特点:

成员变量

1、编译看左边,运行看左边(父类的引用始终访问的是父类的变量,不论是不是static);

2、即使子类有同名的变量,访问的也是父类的变量

成员方法

1、编译看左边,运行看右边(父类的引用运行时访问的是子类重写的方法)

静态方法

1、编译看左边,运行看左边(父类的引用始终访问的是父类的静态方法)

2、所以前面说静态方法不能算方法的重写

总结:

成员变量和静态方法没有多态性

只有被子类重写的成员方法才有多态性

posted on 2017-03-25 12:49  艺海浮台  阅读(153)  评论(0编辑  收藏  举报