Java基础-学习笔记(十一)——类的继承性

1、为什么会有继承性

 1 class Person
 2 {
 3     String name;
 4     int age;
 5     String getInfo(){...}
 6 }
 7 class Student
 8 {
 9     String name;
10     int age;
11     String school;
12     String getInfo(){...}
13     String study(){...}
14 }

在编写代码过程中,可能会出现如上所示的情况,Student类中包含了Person类中的方法和属性。我们针对这种情况,就引入了继承这个概念,只要表明Student类继承了Person类中的所有属性和方法,就不用再在Student中重写Person类中的属性和方法,也就是简化了类的定义,如下所示:

class Person
{
    String name;
    int age;
    void getInfo()
    {
        System.out.println("name="+name+" "+"age="+age);
    }
}
class Student extends Person
{
    String school;
    void study()
    {
        System.out.println("school="+school);
    }
    public static void main(String [] args)
    {
        s1.name="Jane";
        s1.age=23;
        s1.school="清华";
        s1.getInfo();
        s1.study();
    }
}
/*
F:\java_example\lesson5>java Student
name=Jane age=20
school=清华*/

 

2、继承性的特点

1)可以简化类的定义;

2)java只支持单继承,不允许多重继承。也就是说,一个类不能同时继承多个类;

3)java支持多层继承。即,A类可以继承B类,B类可以继承C类,...,重复再多层也支持。

4)子类只继承父类(或称为基类、超类)所有的成员和方法,不能继承父类的构造函数,需要通过子类构造方法中使用语句super(参数列表)调用父类的构造函数;

 1 class Person
 2 {
 3     String name;
 4     int age;
 5     Person(String name,int age)
 6     {
 7         this.name=name;
 8         this.age=age;
 9     }
10     void getInfo()
11     {
12         System.out.println("name="+name+" "+"age="+age);
13     }
14 }
15 class Student extends Person
16 {
17     String school;
18     Student(String name,int age,String school)
19     {
20         super(name,age);
21         this.school=school;
22     }
23     void study()
24     {
25         System.out.println("school="+school);
26     }
27     public static void main(String [] args)
28     {
29         Student s1=new Student("Jane",20,"清华");
30         s1.getInfo();
31         s1.study();
32     }
33 }
34 /*
35 F:\java_example\lesson5>java Student
36 name=Jane age=20
37 school=清华*/

5)子类对象实例化过程

注意事项: 子类对象创建时需要调用父类的构造函数

如下代码在编译过程中出错

 1 class Person
 2 {
 3     String name;
 4     int age;
 5     Person(String name,int age)
 6     {
 7         this.name=name;
 8         this.age=age;
 9     }
10         //Person(){}
11     void getInfo()
12     {
13         System.out.println("name="+name+" "+"age="+age);
14     }
15 }
16 class Student extends Person
17 {
18     String school;
19     void study()
20     {
21         System.out.println("school="+school);
22     }
23     public static void main(String [] args)
24     {
25         Student s1=new Student();
26         s1.name="Jane";
27         s1.age=23;
28         s1.school="清华";
29         s1.getInfo();
30         s1.study();
31     }
32 }
View Code

报错如下:

F:\java_example\lesson5>javac lesson5.java
lesson5.java:19: 错误: 无法将类 Person中的构造器 Person应用到给定类型;
class Student extends Person
^
需要: String,int
找到: 没有参数
原因: 实际参数列表和形式参数列表长度不同
1 个错误

解决方法:将无参数的Person构造函数注释去掉。以后只要是定义了类有参数的构造函数,需要把其无参数的构造函数同样写上,避免类似错误。

现象说明:在构造Student类的s1对象的过程中,需要调用父类Person的无参数构造函数,但是由于,父类已存在的构造函数是有参数的,那么编译器不会再给Person自动生成无参数的构造函数,所以s1对象就无法创建成功

 1 class Person
 2 {
 3     String name="unknown";
 4     int age=-1;
 5     Person(String name,int age)
 6     {
 7         this.name=name;
 8         this.age=age;
 9     }
10     Person()
11     {
12         System.out.println("Is calling");
13     }
14     void getInfo()
15     {
16         System.out.println("name="+name+" "+"age="+age);
17     }
18 }
19 class Student extends Person
20 {
21     String school="unknown";
22     Student(String name,int age,String school)
23     {
24         super(name,age);
25         this.school=school;
26     }
27     /*Student(String name,int age)
28     {
29         super(name,age);
30     }
31     Student(String name,int age,String school)
32     {
33         this(name,age);
34         this.school=school;
35     }
36     */
37     void study()
38     {
39         System.out.println("school="+school);
40     }
41     public static void main(String [] args)
42     {
43         Student s1=new Student("Jane",20,"清华");
44         s1.getInfo();
45         s1.study();
46     }
47 }
View Code

这段代码说明了,子类对象创建时会按需选择父类中对应的构造函数。

具体实例化步骤:

第一步 分配成员变量的内存空间并进行默认的初始化,就是在new对象的过程中,会按照系统的默认值给各个成员赋初值,例如,String类型赋值为“null”,int类型为0等等;

第二步 绑定构造方法参数,就是将new Student("Jane",20,"清华")中的值传给对应的构造函数中的形参

第三步 不会立马将构造函数形参的值赋值给实参,而是,检查有没有this()方法调用。如果有,则调用相应的重载构造函数(被调用的重载构造函数,又从第二步开始执行),该过程结束后,回到当前的构造方法,直接转到第六步;如果没有,则执行下一步骤;

将代码中Student的重载构造函数换成如下

Student(String name,int age)
{
  super(name,age);
}
Student(String name,int age,String school)
{
  this(name,age);
  this.school=school;
}

第四步 显示(就是执行super语句)或者隐式追溯调用父类的构造方法(根据继承性,一直追溯到最上层的父类)。在这个过程中,也是从第二步开始,全部结束后,继续下一步骤;

第五步 进行实例变量的显式初始化操作,也就是执行在定义成员变量时对它赋初值的语句。即,将unknown赋给name。没有则跳过该步骤

第六步 执行当前构造方法中的程序代码,即执行this.school=school。super或this已经在之前的步骤中执行过了,注意区别,上文提到的this()和this.school=school语句,前者调用的是构造方法,后者只是一个普通的语句

super()能和this()放同一个函数中么?我们通过this()间接来调用父类的构造方法,作用是和super()一致的,所以没意义,程序也不允许

super()、this()可以放在方法体中任意位置么?NO,只能放在对应构造方法体中的第一句,要不然就和上面的流程冲突了

3、覆盖父类的方法

在子类中可以根据需要更改从父类继承过来的方法---方法的覆盖或重写

覆盖的方法和被覆盖的方法两者具有相同的名称、参数列表和返回值类型

 1 class Person
 2 {
 3     String name="unknown";
 4     int age=-1;
 5     public Person(String name,int age)
 6     {
 7         this.name=name;
 8         this.age=age;
 9     }
10     public Person()
11     {
12         System.out.println("Is calling");
13     }
14     public void getInfo()
15     {
16         System.out.println("name="+name+" "+"age="+age);
17     }
18 }
19 class Student extends Person
20 {
21     String school="unknown";
22     public Student(String name,int age,String school)
23     {
24         super(name,age);
25         this.school=school;
26     }
27     public void getInfo()//覆盖父类中的getInfo()
28     {
29         System.out.println("name="+name+" "+"age="+age+" "+"school="+school);
30         //super.getInfo();可以直接调用父类的getInfo()
31     }
32     public void study()
33     {
34         System.out.println("I'm studing");
35     }
36     public static void main(String [] args)
37     {
38         Student s1=new Student("Jane",20,"清华");
39         s1.getInfo();
40         s1.study();
41     }
42 }
43 /*
44 F:\java_example\lesson5>java Student
45 name=Jane age=20 school=清华
46 I'm studing
View Code

如果将子类的访问修饰符换成protected,那么编译时会报如下错

F:\java_example\lesson5>javac lesson5.java
lesson5.java:27: 错误: Student中的getInfo()无法覆盖Person中的getInfo()
protected void getInfo()//覆盖父类中的getInfo()
^
正在尝试分配更低的访问权限; 以前为public
1 个错误

这说明了,如果要覆盖父类的方法,则子类的该方法的权限修饰符等级要比父类的高或者是同级;但两者均不能用private来进行修饰,super需要调用父类的getInfo()

4、final关键字

a 在java中声明类、属性、方法时,可以用关键字final来修饰

b final标记的类不能被继承

c final标记的方法不能被子类重写

d final标记的变量(成员变量或局部变量)则为常量,只能赋值一次,并且,只能在声明或者该类的所有构造方法中显式赋值再来使用,还有,赋值后的变量只能在该类中直接使用,在类的外部不能直接使用

java中定义常量,常用public static fianl的组合方式进行标识

e 方法中定义的内部类只能访问该方法的final类型的局部变量,用final定义的局部变量相当于是一个常量,它的生命周期超出方法运行的生命周期

 1 class Person
 2 {
 3     public final String;// name=“Jane”;
 4     //要么在声明时初始化,要么在所有显式的构造函数中都进行初始化
 5     int age=-1;
 6     public Person(String name,int age)
 7     {
 8         this.name="Jane";
 9         this.age=age;
10     }
11     public Person()
12     {
13         this.name="Jane";//如果不写这句,会报错“可能尚未初始化变量name”
14         System.out.println("Is calling");
15     }
16     public void getInfo()
17     {
18         System.out.println("name="+name+" "+"age="+age);
19     }
20 }
21 class Student extends Person
22 {
23     String school="unknown";
24     public Student(String name,int age,String school)
25     {
26         super(name,age);
27         this.school=school;
28     }
29     public void getInfo()//覆盖父类中的getInfo()
30     {
31         System.out.println("name="+name+" "+"age="+age+" "+"school="+school);
32         super.getInfo();
33     }
34     public void study()
35     {
36         System.out.println("I'm studing");
37     }
38     public static void main(String [] args)
39     {
40         Student s1=new Student("Chen",20,"清华");//name已经是常量,且值为Jane
41         s1.getInfo();
42         s1.study();
43     }
44 }
45 /*
46 F:\java_example\lesson5>java Student
47 name=Jane age=20 school=清华
48 I'm studing

如果将public final String name;写成public static final String name;只是替换这一句,程序会报错,因为用static修饰的变量,可以直接用类名来引用。那么就存在了一种隐患,我如果是通过类名来调用name,那我就不用new一个Person对象,我既然不用创建对象,那也不会调用其构造函数,从而,我的name也就没有进行初始化。所以,在通过publicstatic final修饰的变量,只能在声明时赋值

posted @ 2015-11-09 12:32  巅峰之旅  阅读(403)  评论(0编辑  收藏  举报