HOME/Articles/

Java中的面向对象的简单介绍(三)继承及abstract抽象类

Article Outline

Java中的面向对象的简单介绍(三)继承及abstract抽象类

<!--more-->

继承 extends

多个类有共同的成员变量和成员方法,可以抽取到另外一个类(父类),再让多个类去继承这个父类,这些类就可以获取到父类中的成员。

public class ExtendsDemo {
    public static void main(String[] args) {
        Dota dead = new Dota();
        dead.start(); // 启动游戏
        Lol l = new Lol();
        l.start(); // 启动游戏
    }
}

class Game {
    String name;
    double version;
    String agent;

    public void start() {
        System.out.println("启动游戏");
    }

    public void exit() {
        System.out.println("退出游戏");
    }
}

class Dota extends Game {} // Dota继承Game类
class Lol extends Game {} // Lol继承Game类

Java中继承的特点

  • 只支持单继承,不支持多继承(即只可以有一个父类) class sub extends Demo{} // ok class sub extends Demo1,Demo2{} // error

  • 支持多层继承(继承体系)

    class A{}
    class B extends A{}
    class C extends B{}

    Java中成员变量继承的特点

  • 子类中只能获取父类中非private的成员变量

public class ExtendsDemo { public static void main(String[] args) { Child c = new Child(); c.sayName(); c.sayAge(); } } class Dad { String name = "dad"; private int age = 40; }

class Child extends Dad { public void sayName () { System.out.println(name); } public void sayAge () { System.out.println(age); } }

上述例子中,提示`Dad.age`这个字段不可见。所以子类中只能获取父类中**非private的成员变量**
- 如果子类和父类中的成员变量名字一样,那么遵循**就近原则**

public class ExtendsDemo { public static void main(String[] args) { Child c = new Child(); c.sayName(); // child 就近原则 输出子类中的成员属性 } } class Dad { String name = "dad"; }

class Child extends Dad { String name = "child"; public void sayName () { System.out.println(name); } }

- 对于上述的就近原则,可以通过`super`关键字来获取父类中的成员变量和方法

public class ExtendsDemo { public static void main(String[] args) { Child c = new Child(); c.sayName(); // dad
} } class Dad { String name = "dad"; }

class Child extends Dad { String name = "child"; public void sayName () { System.out.println(super.name); // super.name 获取父类中的成员 } }

- 当涉及到局部变量和成员变量重名时,得使用`this`来获取当前类中的成员

public class ExtendsDemo { public static void main(String[] args) { Child c = new Child(); c.sayName();
} } class Dad { String name = "dad"; }

class Child extends Dad { String name = "child"; public void sayName () { String name = "testName"; System.out.println("父类:" + super.name); // super.name 获取父类中的成员 System.out.println("当前类:" + this.name); // this.name 获取当前类中的成员 System.out.println("局部变量:" + name); // name 获取当前作用域中的局部变量 } }


### Java中成员方法继承的特点

方法继承支持重写(不是重载),子类中的方法和父类中。的方法完全一致时,可以在子类中重写这个方法。子类的对象调用时调用的是子类重写的方法

关于重写有一些注意点:
- 在子类重写的方法中,可以使用`super.methodName()`来执行父类中的方法,这么写既可以使用父类的功能,也可以追加子类的逻辑。
- 重写前可以添加注解`@Override`加强规范
- 不能重写父类中private的成员变量,子类中方法的权限(如:public)必须要比父类中的权限等级高。

*定义方法的时候,可以不写权限修饰(比如: public), 那么就代表默认权限(如`void show() {}`)*

### Java中构造方法的执行顺序

1. 如果子类的构造方法中的**第一行代码中没有调用父类或者子类的构造方法时**,那么会**默认调用父类的无参构造方法**

public class ExtendsDemo { public static void main(String[] args) { Child c = new Child();// Super Constructor---no Args Sub Constructor---no Args } } class Dad { public Dad () { System.out.println("Super Constructor---no Args"); } public Dad (int num) { System.out.println("Super Constructor---have Args"); } }

class Child extends Dad { public Child () { System.out.println("Sub Constructor---no Args"); // 第一行没有调用父类构造方法,默认调用父类无参构造 } public Child (int num) { System.out.println("Sub Constructor---have Args");


2. 可以使用`super()`来在子类的构造方法的第一行中调用父类的构造方法

public class ExtendsDemo { public static void main(String[] args) { Child c = new Child();// Super Constructor---have Args Sub Constructor---no Args } } class Dad { public Dad () { System.out.println("Super Constructor---no Args"); } public Dad (int num) { System.out.println("Super Constructor---have Args"); } }

class Child extends Dad { public Child () { super(1); // 调用了父类的构造方法 System.out.println("Sub Constructor---no Args"); } public Child (int num) { System.out.println("Sub Constructor---have Args"); } }

3. 不管有没有参数,父类的构造方法总是优先于子类的构造方法被执行

public class ExtendsDemo { public static void main(String[] args) { Child c = new Child();// Super Constructor---no Args Sub Constructor---have Args Sub Constructor---no Args } } class Dad { public Dad () { System.out.println("Super Constructor---no Args"); } public Dad (int num) { System.out.println("Super Constructor---have Args"); } }

class Child extends Dad { public Child () { this(1); // 调用了子类的有参构造方法 System.out.println("Sub Constructor---no Args"); } public Child (int num) { System.out.println("Sub Constructor---have Args");//有参构造第一行中没调用父类的构造方法,于是默认调用父类的无参构造 } }



###this 和 super的区别
this: 当前对象的引用
super:子类对象的父类引用

在构造方法中的第一行中,直接写`this()`或者`super()`就是默认调用构造方法。

*如果子类中没有制定的成员属性,而只有父类中有的时候,调用`this.xxx`就是访问的父类的成员属性*

###继承的优缺点
优点:
- 提高代码的复用性和可维护性

缺点:
- 类的耦合性增强了
- 开发的原则:高内聚低耦合
- 内聚:就是自己完成某件事的能力
- 耦合:类和类的关系

### 抽象类

abstract: 关键字,用于修饰方法和类

抽象方法:不同类的方法是相似的,但是具体逻辑不太一样,只能抽取声明,没有具体的方法体。 即没有方法体的方法就是抽象方法,抽象方法只能在抽象类中使用。

抽象类: 有抽象方法的类必须是抽象类。

如果有其他类继承了抽象类,那**必须要重写其中的抽象方法**,**或者这个类必须也是抽象类**。

public class ExtendsDemo { public static void main(String[] args) {} }

abstract class Animal { // 有抽象方法的类必须是抽象类 public abstract void eat(); // abstract修饰的是方法是抽象方法 没有方法体 }

class Cat extends Animal { // 如果一个类继承了抽象类,而且同时还不也是一个抽象类的话,那么必须要重写抽象类中的抽象方法 @Override public void eat() { // 重写抽象类中的抽象方法 System.out.println("猫吃鱼"); } }

abstract class Dog extends Animal {} // 抽象类的Dog 本身也是一个抽象类 就不需要重写抽象方法了

抽象类的特点:
- 抽象方法只能在抽象类中
- 抽象类不能实例化(不能new)
- 抽象类可以有非抽象类(非抽象类被继承不需要强制重写)

抽象类的成员的特点:
1. 成员变量
    - 可以有成员变量
    - 可以有常量(被final修饰)
2. 成员方法
    - 可以有抽象方法和非抽象方法
3. 构造方法
    - 可以有构造方法(但是不能创建对象),因为需要对类中的成员变量进行初始化


**final**: 修饰类、成员变量、成员方法,修饰了之后不能被继承

抽象类的细节:
- abstract 不可以和 final, private 共存
- 可以有构造函数(因为有成员变量,要初始化成员变量,同时还要被继承,子类继承父类也要调用父类的构造函数)
- 内部也可以没有抽象方法(当不想让这个不可以类实例化的时候,当然也可以使用private构造函数来达到一样的效果)