黑马程序员——继承、抽象、接口

 继承

一、概述

  继承是面向对象最显著的一个特性。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。Java继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。这种技术使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用。比如可以先定义一个类叫车,车有以下属性:车体大小,颜色,方向盘,轮胎,而又由车这个类派生出轿车和卡车两个类,为轿车添加一个小后备箱,而为卡车添加一个大货箱。

  JAVA不支持多继承,单继承使JAVA的继承关系很简单,一个类只能有一个父类,易于管理程序,同时一个类可以实现多个接口,从而克服单继承的缺点。多继承容易出现问题。两个父类中有相同的方法,子类到底要执行哪一个是不确定的。

  Java支持多层继承(继承体系):C继承B,B继承A,就会出现继承体系。多层继承出现的继承体系中,通常看父类中的功能,了解该体系的基本功能,建立子类对象,即可使用该体系功能。

  注意:不要仅为了获取其他类中某个功能而去继承,类与类之间要有所属("isa")关系。

二、super关键字和函数覆盖

  1、变量

  super关键字和this很相似,this代表本类对象的引用,而supersuper代表父类的内存空间的标识。

  当本类的成员和局部变量同名用this区分。当子父类中的成员变量同名用super区分父类。

  2、函数

  当子父类中出现成员函数一模一样的情况,会运行子类的函数。这种现象,称为覆盖操作,这是函数在子父类中的特性。在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。

  这里涉及到一个函数重载和重写(覆盖)的区别: 

  重载是在Java中一个类的内部可以定义多个同名的方法,但是它们的方法参数要有所不同。重写的是与父类同名的方法,与返回值有关。重载是同名方法,与返回值无关,与参数有关,如:个数、类型、排列。

  重写是指重新实现基类中的方法。在运行过程中,如果将子类创建的对象赋值给子类的引用或父类的引用,则无论通过哪个类型的引用,真正调用的方法都将是在子类中重写的方法。如果希望调用父类中的方法,则需要通过父类创建类的实例,然后通过该实例才能访问父类定义的方法。重写方法是实现多态的一个重要表现。

   重写注意事项:

  (1)父类中的私有方法不可以被重写

  (2)子类方法访问权限一定要大于父类的访问权限

  (3)静态的方法只能被静态的方法重写,这个其实不能算对象的关系。

  这里我简单的写个小程序来验证一下:

 1 class MyExtends 
 2 {
 3     public static void main(String[] args) 
 4     {
 5         Zi z=new Zi();
 6         z.show(); //调用子类中重写过的show方法
 7         z.show(8);//调用从父类继承的带参数的show函数的重载        
 8     }
 9 }
10 
11 class Fu
12 {
13     public void show()
14     {
15         System.out.println("fu show!");
16     }
17     public void show(int num)//重载show方法
18     {
19         System.out.println("num:"+num);
20     }
21 }
22 
23 class Zi extends Fu
24 {
25     public void show()//重写父类的show方法
26     {
27         System.out.println("{
16     public Fu()
17     {
18         System.out

  3、构造函数

    继承中构造函数的特点:

    在子类构造函数执行时,发现父类构造函数也运行了。原因:在子类的构造函数中,第一行有一个默认的隐式语句:super();。如果显式地通过super使用父类的构造函数,super(4);,那么默认的父类构造函数将不会再被调用。

    构造函数我也用一个小例子来巩固一下记忆

 1 class MyExtends 
 2 {
 3     public static void main(String[] args) 
 4     {
 5         Zi z1=new Zi();
 6         System.out.println();
 7         Zi z2=new Zi(2);
 8         System.out.println();
 9         Zi z3=new Zi(3,"测试");
10         System.out.println();
11     }
12 }
13 
14 class Fu
15 {
16     public Fu()
17     {
18         System.out.println("父类无参数的构造方法!");
19     }
20     public Fu(int num)
21     {
22         System.out.println("父类带参数的构造方法! "+num);
23     }
24 }
25 
26 class Zi extends Fu
27 {
28     public Zi()
29     {
30         System.out.println("子类无参数的构造方法!");
31     }
32     public Zi(int num)
33     {
34         //super()这里自动会调用父类无参的构造函数
35         System.out.println("子类有参数的构造方法!"+num);
36     }
37     public Zi(int num,String str)
38     {
39         super(num);//显示
40         System.out.println("子类有两个参数的构造方法!"+num+" "+str);
41     }
42 }

 输出的结果为:

  可以看到子类在调用自己的构造函数的时候确实会先去调用父类的构造函数。

四、子类的实例化过程

  下面以Personp=newPerson();为例说明一个对象实例化过程。

  1.JVM会读取指定的路径下的Person.class文件,并加载进内存,并会先加载Person的父类(如果有直接的父类的情况下)。

  2.在内存中开辟空间,并分配地址。

  3.并在对象空间中,对对象的属性进行默认初始化。

  4.调用对应的构造函数进行初始化。

  5.在构造函数中,第一行会先到调用父类中构造函数进行初始化。

  6.父类初始化完毕后,再对子类的属性进行显示初始化。

   7.再进行子类构造函数的特定初始化。

  8.初始化完毕后,将地址值赋值给引用变量。

五、final关键字

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

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

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

  final修饰的变量是一个常量,只能被赋值一次。为什么要用final修饰变量,其实,在程序中如果一个数据是固定的。那么直接使用这个数据就可以了,但是这种阅读性差,所以应该给数据起个名称。而且这个变量名称的值不能变化,所以加上final固定。

  写法规范:常量所有字母都大写,多个单词,中间用_连接。

 

 抽象

 

一、概述 

  使用了关键词abstract声明的类叫作“抽象类”。如果一个类里包含了一个或多个抽象方法,类就必须指定成抽象。抽象方法属于一种不完整的方法,只含有一个声明,没有方法主体。

  抽象的由来:多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。例如:狼和狗都有吼叫的方法,可是吼叫内容是不一样的。所以抽象出来的犬科虽然有吼叫功能,但是并不明确吼叫的细节。

二、抽象类的特点

  1、抽象类和抽象方法必须用abstract关键字来修饰。

   2、抽象方法只有方法声明,没有方法体,定义在抽象类中。格式:修饰符abstract返回值类型函数名(参数列表);

   3、抽象类不可以被实例化,也就是不可以用new创建对象。因为:1.抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:犬科是一个抽象的概念,真正存在的是狼和狗。2.而且抽象类即使创建了对象,调用抽象方法也没有意义。2.而且抽象类即使创建了对象,调用抽象方法也没有意义。3.抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。

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

  题目如下:假如我们在开发一个系统时需要对员工进行建模,员工包含 3 个属性:姓名、工号以及工资。经理也是员工,除了含有员工的属性外,另为还有一个 奖金属性。请使用继承的思想设计出员工类和经理类。要求类中提供必要的方法进行属性访问。

  首先分析一个题目,题目要求使用继承的思想,题目中的名词指出经理也是员工,这里的员工有两个意思,一个是抽象的代指公司的每一个人,另外一个是具体的指代每一个普通的工作人员,这里为了区分,把这具体的员工叫做普通员工。这里可以把员工抽象成一个抽象类,普通员工和经理都继承这个抽象类。那么代码就很好写了

 1 //描述抽象的员工
 2 abstract class Employee
 3 {
 4     private String name;
 5     private String id;
 6     private double salary;
 7 
 8     public Employee(String name,String id,Double salary)
 9     {
10         this.name=name;
11         this.id=id;
12         this.salary=salary;
13     }
14     public abstract void work();
15     public void setName(String name)//以那么为例来说类属性的访问方法,别的类似
16     {
17         this.name=name;
18     }
19     public String getName()
20     {
21         return name;
22     }
23 }
24 
25 //员工类
26 class Worker extends Employee
27 {
28     Worker(String name,String id,Double salary)
29     {
30         super(name,id,salary);
31     }
32     public void work()
33     {
34         System.out.println("working...");
35     }
36 }
37 
38 //经理类
39 class Manager extends Employee
40 {
41     private int bonus;
42     Manager(String name,String id,Double salary)
43     {
44         super(name,id,salary);
45     }
46     public void work()
47     {
48         System.out.println("managing...");
49     }
50 }

  从上面的代码中我们能很好的理解抽象类和抽象方法给我们在定义类和定义变量的时候带来的便利。

  那么抽象类和一般类有什么关系呢?

    相同点:抽象类和一般类都是用来描述事物的,都在内部定义了成员。

    不同点:

      1.一般类有足够的信息描述事物。

       抽象类描述事物的信息有可能不足。

      2.一般类中不能定义抽象方法,只能定义非抽象方法。

       抽象类中可定义抽象方法,同时也可以定义非抽象方法。

      3.一般类可以被实例化。

       抽象类不可以被实例化。

接口

  当一个抽象类中的方法都是抽象的时候,这时可以将该抽象类用另一种形式定义和表示,就是接口。Java中接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。

  接口的格式:interface{}

  接口中的成员修饰符是固定的:成员常量:public static final,成员函数:public abstract接口中的成员都是公共属性。接口是对外暴露的规则,接口是程序的功能拓展。

  在java中不直接支持多继承,因为会出现调用的不确定性。所以,java将多继承机制进行改良,在java中变成了多实现,一个类可以实现多个接口。接口的出现避免了单继承的局限性。一个类在继承另一个类的同时,还可以实现多个接口。但是继承类要下载实现接口的前面。如:abstract classA extends classB implements IC,ID;。

  注意:接口与接口之间是继承关系,而且接口可以多继承

  抽象类和接口的异同点?

    相同点:都是不断向上抽取而来的。

    不同点:

      1、抽象类需要被继承,而且只能单继承。

       接口需要被实现,而且可以多实现。

      2、抽象类中可以定义抽象方法和非抽象方法,子类继承后,可以直接使用非抽象方法

       接口中只能定义抽象方法,必须由子类去实现。

      3、抽象类的继承,是isa关系,定义该体系的基本共性内容。

       接口的实现是likea关系。

  接下来也用一个小实例来说明一下接口的使用。

   

 1 class MyInterface 
 2 {
 3     public static void main(String[] args) 
 4     {
 5         useUSB(new Upan());//使用接口
 6 
 7     }
 8     static void useUSB(USB u)// 接口类型的引用,用于接收(指向)接口的子类对象
 9     {
10         if(u!=null)
11         {
12             u.open();
13             u.close();
14         }
15     }
16 }
17 /**
18 *为了拓展笔记本的功能,但日后出现什么功能的设备并不清楚,
19 *因此,需要定义一个规则,只要日后出现的设备符合这个规则就可以用
20 *那么这个规则就是接口。接下来代码模拟一下笔记本对外的接口USB
21 */
22 interface USB
23 {
24     public abstract void open();
25     public void close();//可以简化书写为这种形式
26 }
27 //u盘实现了USB接口所以电脑能认识U盘
28 class Upan implements USB
29 {
30     public void open()
31     {
32         System.out.println("U盘 open!");
33     }
34     public void close()
35     {
36         System.out.println("U盘 close!");
37     }
38 } 
39 //鼠标实现了USB接口所以电脑能认识鼠标
40 class UsbMouse implements USB
41 {
42     public void open()
43     {
44         System.out.println("鼠标 open!");
45     }
46     public void close()
47     {
48         System.out.println("鼠标 close!");
49     }
50 } 

  输出的结果为:

  

  上面就能看到我们的电脑能用U盘了。这就是接口带给我们的方便。主函数不用做任何改变,我们只用实现USB接口就能拓展电脑的功能。

  至此继承、抽象、接口就总结完了,这一部分的内容是实现多态的基础,非常的重要。有些概念很相近,但是要能明确这些概念的区别。

  继续努力加油!为了明天更好的自己。

 

原文地址:https://www.cnblogs.com/dengzhenyu/p/4829876.html