{Java初级系列四}---继承、接口和抽象类
本人为自学Java系列,内容来自于中国大学mooc华东师范大学陈育良教授《Java核心技术》,在此感谢老师!
一:继承
- 面向对象和面向过程编程语言最大的特点就是变量类型的继承,通过extend去继承父类
- 继承能够解决很多类型的重复定义,类别内的对象属性和方法都具有一定的共同点。将共同点提取出来,即形成父类/基类/超类—Parent class/Base class/Super class
- 其他类型则自动生成子类/派生类-----Child class/Derived class
Human类把Man和Woman的height、weight成员属性和eat方法写成一个父类,两个子类就能继承Human,从而节约很多重复定义。
- 子类会继承父类所有的属性和方法,但是不能访问private成员。
- 子类会继承父类的父类所有的属性和方法,但是不能访问private成员
- 单根继承原则:每个类都只能继承一个类,如果不写extends,Java类都默认继承java.lang.object,Objecvt里面默认有clone equlas finalize getcalss hashCode toString等方法
- 同样方法名和参数情况下,本类的方法会比父类的方法优先级更高
父类:
- public class Base {
- private int num = 10;
- public int getNum() {
- return this.num;
- }
- }
子类:
- public class Derived extends Base{
- private int num = 20;
- public int getNum() {
- return this.num;
- }
- public static void main(String[] args) {
- Derived foo = new Derived();
- System.out.println(foo.getNum());
- }
- }
程序返回值为20。子程序会选择本类的方法进行执行。
解释下上章节的继承函数
- public class A {
- public A() {
- System.out.println("11111");
- }
- public A(int a) {
- System.out.println("22222");
- }
- }
- public class B extends A {
- public B() {
- //super();
- System.out.println("333333");
- }
- public B(int b) {
- //super(b);
- System.out.println("444444");
- }
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- B obj = new B();
- System.out.println("==========");
- B obj1 = new B(10);
- }
- }
输出结果:
- 11111
- 333333
- ==========
- 11111
- 444444
两个构造器都会默认调用父类的无参构造函数,输入11111.
当我们在每个构造器前面加上super函数,而且super语句必须放在第一条。
当去掉第二行super(b)注释,输出值:
- 11111
- 333333
- ==========
- 22222
- 444444
二:抽象类
抽象类:属性+方法组成,完整的类所有的方法都有实现(方法体),一个完整的类才可以被实例化,如果一个类暂时没有方法实现,就定义为抽象类,抽象类是无法被实例化的。
- 抽象类关键字abstract声明;
- 抽象类的组成
--optional 成员变量,个数不限
--optional具体方法,方式有实现,个数不限
-- optional 抽象方法,加abstract关键字,个数不限
同时对于抽象类我们还要遵循以下的规则
- 抽象类也是类,同样要满足单根继承原则。一个类只能继承一个抽象类
- 完整类要继承抽象类必须实现其所有的方法,否则只能定义为抽象类
抽象类:
- public abstract class Shape {
- int area;
- public abstract void calArea();
- }
完整类继承于抽象类,并实现抽象类中的方法体。
- public class Rectangle extends Shape {
- int width;
- int length;
- public void set(int length, int width) {
- this.width = width;
- this.length = length;
- }
- public void calArea() {
- System.out.println(this.length *this.width);
- }
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- Rectangle obj = new Rectangle();
- obj.set(10,5);
- obj.calArea();
- }
- }
三:接口
- 紧接继承的定义,如果类的所有方法都没有实现,那么这个类就是接口interface。类只可以继承-extends一个类,但是可以实现-implements多个接口,继承和实现可以同时进行
- 类可以继承多个接口,没有实现的方法将会叠加,即A继承与B C两个接口,B C两个接口所有方法体将会叠加,如果类没有实现接口所有方法,只能定义为抽象类
- 接口里可以定义变量,但是一般是常量,详情可以参考final节
这里给大家写几个简单的例子来描述: 类-抽象类-接口之间的关系
定义三个接口:
- public interface Aninmal {
- public void eat();
- public void move();
- }
接口2:
- public interface ClassTree {
- public void climb();
- }
接口3
- public interface CatFamliy extends ClassTree, Aninmal {
- // 继承多个接口相当于将没有实现的方法全部叠加
- // eat()
- // move()
- //climb()
- }
Cat类是实现与Animanl接口的,必须实现它所有的方法体
- public class Cat implements Aninmal {
- public void eat() {
- System.out.println("Cat can eat");
- }
- public void move() {
- System.out.println("Cat can move");
- }
- }
类LandAnimal由于只实现Animal中的move方法,没有实现eat方法,定义为abstract类
- public abstract class LandAnimal implements Aninmal {
- public void move() {
- System.out.println("LandAnimal cat move");
- }
- //public abstract void eat();
- }
Rabbit类是先继承LandAnimal抽象类再实现ClassTree接口
- public class Rabbit extends LandAnimal implements ClassTree {
- // 必须先继承再实现
- public void eat() {
- System.out.println("Rabbit cat eat");
- }
- public void climb () {
- System.out.println("Rabbit can climb");
- }
- }
Tiger类是实现CatFamily接口,所以要实现这个接口所有方法
- public class Tiger implements CatFamliy {
- public void move() {
- System.out.println("Tiger can move");
- }
- public void eat() {
- System.out.println("Tiger can move");
- }
- public void climb() {
- System.out.println("Tiger can move");
- }
- }
四:转型
基本类型转型
- 变量支持相互转化。比如 int a = (int)3.5 这里进行变量的强转换
基本类型转换:
从高精度往低精度转化,会损失信息,称为收缩变换。这种情况我们需要声明类型转化
从低精度往高精度转化,不损失信息,称为宽松变换。这种情况Java会自动转化。
- public class Test{
- public static void main(String[] args) {
- int a;
- a = (int)3.5; // 收缩变换
- System.out.println(a);
- double b = 3; // 宽松变换
- System.out.println(b);
- }
结果:
- 3
- 3.0
类转型
类型之间可以互相转型,但是只限制于有继承关系的类
---子类可以转化为父类,但是父类不能转化为子类。因为子类有更多的方法和变量
---子类继承父类所有的财产,子类转化为父类(从大到小,即向上转型);从父类直接变成子类(从小变大,即向下转型)
- public class Man extends Human {
- public void eat() {
- System.out.println("i cat eat more");
- }
- public void plough() {
- }
- public static void main(String[] arsg) {
- Man obj1 = new Man();
- obj1.eat();
- Human obj2 = new Man();// upcast向上转换
- obj2.eat();
- Man obj3 = (Man)obj2;
- obj3.eat();
- // Man obj3 = new Human(); Human是父类,Man拥有父类不具有的成员变量与方法。
- // 所以Human对象内存无法转型为Man类
- }
- }
父类:
package service;
public class Human {
int height;
int weight;
public void eat() {
System.out.println("I cat eat");
}
}
输出结果:
i cat eat more
i cat eat more
i cat eat more
如上序程序就有类转型,Man类转为Human类,即衍生类引用转为基类引用。类型转换的作用就是带来多态。
程序中有一个eat方法,和父类中的eat方法方法名和参数一致,我们把这个称为方法重写。(不同于重载,重载是方法名一致但是形参类型或者个数不一致)。且子类方法的优先级高于父类。
五:多态
多态拥有三个必要条件:
- 继承 extends
- 重写 overwrite
- 父类引用指向子类对象 Father obj = new Son()
当我们使用多态调用方法时,对象会检查父类是否有此方法,如果没有就会编译错误。如果有的话,就会调用子类同名方法。
多态的作用到底有什么呢:
- 以统一的接口来操纵某一类中不同对象的动态行为
- 对象之间的解耦
代码实例:
public interface Aninmal {
public void eat();
public void move();
}
public class Dog implements Aninmal {
public void eat() {
System.out.println("Dog can eat");
}
public void move() {
System.out.println("Dog can move");
}
}
public class Cat implements Aninmal {
public void eat() {
System.out.println("Cat can eat");
}
public void move() {
System.out.println("Cat can move");
}
}
- public class AnimalTest {
- public static void haveLunch(Aninmal a) {
- a.eat();
- }
- Aninmal a;
- AnimalTest c = new AnimalTest();
- public static void main(String[] args) {
- haveLunch(new Cat()); // 此处等于转型 Aninaml a = new Cat(); haveLunch(a)
- haveLunch(new Dog());
- haveLunch(new Aninmal() {
- @Override
- public void move() {
- // TODO Auto-generated method stub
- System.out.println("move Test");
- }
- @Override
- public void eat() {
- // TODO Auto-generated method stub
- System.out.println(" eat Test");
- }
- });
- }
- }
输出结果:
- Cat can eat
- Dog can eat
- eat Test
我们通过Animal接口,完成对于Dog和class两个不同对象的自己各自独有的行为。第三个是匿名类,重写补全Animal接口的方法,调用补全的方法。
我们haveLunch方法形参可以传入不同对象,这个称为对象的解耦。
六:契约设计
契约:通过接口定义了方法的名称,参数和返回值。规范了派生类的行为
基于接口,利用转型和多态,不影响真正方法调用,成功地将调用类和被调用类解耦。
- 被调用类:
- 调用类:
两个方法之间已经解耦设计,havelunch已经定义其方法名 参数名和方法体,调用类可以利用多态的功能实现多个对象的不同行为方法。