一、继承

1.概念语法

继承 :代码可以复用,允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承主要解决共性的抽取,实现代码复用的问题

//狗类
public class Dog {
    String name;
    int age;
    float weight;

    public void eat(){
        System.out.println(name+"正在吃饭");
    }

    public void sleep(){
        System.out.println(name+"正在睡觉");
    }

    void Bark(){
        System.out.println(name+"汪汪汪");
    }
}

//猫类
public class Cat {
    String name;
    int age;
    float weight;

    public void eat(){
        System.out.println(name+"正在吃饭");
    }

    public void sleep(){
        System.out.println(name+"正在睡觉");
    }

    void mew(){
        System.out.println(name+"喵喵喵");
    }
}

java 连续赋值语法 java连续继承_java 连续赋值语法

        Dog和Cat都继承了Animal类,其中Animal类称为父类/基类或超类Dog和Cat可以称为Animal的子类/派生类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员

java 连续赋值语法 java连续继承_System_02

在Java中如果要表示类之间的继承关系,需要借助extends关键字

public class Animal {
    String name;
    int age;

    public  void eat(){
        System.out.println(name+"正在吃饭");
    }

    public void sleep(){
        System.out.println(name+"正在睡觉");
    }
}

public class Dog1 extends Animal{
    void bark(){
        System.out.println(name+"汪汪汪");
    }
}

public class Cat1 extends Animal{
    void mew(){
        System.out.println(name+"喵喵喵");
    }
}

public class TestExtend {
    public static void main(String[] args) {
        Dog1 dog1 = new Dog1();
        // dog类中并没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
        System.out.println(dog1.name);
        System.out.println(dog1.age);

        // dog访问的eat()和sleep()方法也是从Animal中继承下来的
        dog1.eat();
        dog1.sleep();
        dog1.bark();
    }
}

 运行结果:   

java 连续赋值语法 java连续继承_父类_03

【 注意】

  •  子类会将父类中的成员变量或者成员方法继承到子类中
  • 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了

父类成员访问

当子类和父类不存在同名成员变量和子类和父类成员变量同名时子类中如何访问父类的成员变量

public class Base {
    int a;
    int b;
}

//子类和父类不存在同名成员变量
public class Derived extends Base{
    int c;
    public void method(){
        a = 10;// 访问从父类中继承下来的a
        b = 20;// 访问从父类中继承下来的b
        c = 30;// 访问子类自己的c
    }
}
public class Base1 {
    int a;
    int b;
    int c;
}

//子类和父类成员变量同名
public class Derived1 extends Base1{
    int a;// 与父类中成员a同名,且类型相同
    char b;// 与父类中成员b同名,但类型不同

    public void method(){
        a = 100;
        b = 101;
        c = 102;// 与父类中成员b同名,但类型不同
        //d = 103;// 编译失败,因为父类和子类都没有定义成员变量b
    }
}

成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找

在子类方法中或者通过子类对象访问成员时】

  • 如果访问的成员变量子类中有,优先访问自己的成员变量
  • 如果访问的成员变量子类中没有,则访问父类继承下来的,如果父类也没有定义,则编译报错
  • 如果访问的成员变量与父类中成员变量同名,则优先访问自己的

成员方法名字不同成员方法名字相同时子类中如何访问父类的成员方法

public class Base2 {
    public void methodA(){
        System.out.println("Base中的methodA()");
    }
}

//成员方法名字不同
public class Derived2 extends Base2{
    public void methodB(){
        System.out.println("Derived中的methodB()方法");
    }

    public void methodC(){
        methodB();// 访问子类自己的methodB()
        methodA();// 访问父类继承的methodA()
        //methodD();// 编译失败,在整个继承体系中没有发现方法methodD()
    }
}
public class Base3 {
    public void methodA(){
        System.out.println("Base中的methodA()");
    }

    public void methodB(){
        System.out.println("Base中的methodB()");
    }
}

//成员方法名字相同
public class Derived3 extends Base3{
    public void methodA(int a){
        System.out.println("Derived中的method(int)方法");
    }

    public void methodB(){
        System.out.println("Derived中的methodB()方法");
    }

    public void methodC(){
        methodA();// 没有传参,访问父类中的methodA()
        methodA(20);// 传递int参数,访问子类中的methodA(int)
        methodB();// 直接访问,则永远访问到的都是子类中的methodB(),基类的无法访问到
    }
}

【注意

  • 通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
  • 通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同,根据调用方法适传递的参数选择合适的方法访问,如果没有则报错;如果父类和子类同名方法的原型一致,则只能访问到子类的,父类的无法通过派生类对象直接访问到

在子类方法中访问父类的成员时使用Java 提供的 super 关键字

public class Base4 {
    int a;
    int b;

    public void methodA(){
        System.out.println("Base中的methodA()");
    }

    public void methodB(){
        System.out.println("Base中的methodB()");
    }
}

public class Derived4 extends Base4{
    int a;// 与父类中成员变量同名且类型相同
    char b;// 与父类中成员变量同名但类型不同

    public void methodA(int a){
        System.out.println("Derived中的method()方法");
    }

    public void methodB(){
        System.out.println("Derived中的methodB()方法");
    }

    public void methodC(){
        a = 100;//访问子类
        b = 101;//访问子类
        // 访问父类的成员变量时,需要借助super关键字
        // super是获取到子类对象中从基类继承下来的部分
        super.a = 200;
        super.b = 201;
        methodA();//访问父类中的methodA()
        methodA(20);//访问子类中的methodA(int)
        methodB();//直接访问子类中的methodA()
        super.methodB();// 访问基类的methodB()
    }
}

 【注意】

  • 只能在非静态方法中使用
  • 在子类方法中,访问父类的成员变量和方法

3.子类构造方法 

子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法

public class Base5 {
    public Base5() {
        System.out.println("Base5()");
    }
}

public class Derived5 extends Base5{
    public Derived5() {
        //super();//默认调用基类构造方法
        System.out.println("Derived5()");
    }
}

public class Test {
    public static void main(String[] args) {
        Derived5 derived5 = new Derived5();
    }
}

执行结果

java 连续赋值语法 java连续继承_父类_04

【注意】 

  • 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用
  • 如果父类构造方法是带有参数的,此时编译器不会再给子类生成默认的构造方法,此时需要用户为子类显式定义构造方法
  • 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句。
  • super(...)只能在子类构造方法中出现一次,并且不能和this同时出现

 4.继承关系上的代码块执行顺序

public class Person {
    public String name;
    public int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person:构造方法执行");
    }

    {
        System.out.println("Person:实例代码块执行");
    }

    static {
        System.out.println("Person:静态代码块执行");
    }
}

public class Student extends Person{
    public Student(String name, int age) {
        super(name, age);
        System.out.println("Student:构造方法执行");
    }

    {
        System.out.println("Student:实例代码块执行");
    }

    static {
        System.out.println("Student:静态代码块执行");
    }

}

public class Test1 {
    public static void main01(String[] args) {
        Student student1 = new Student("张三",19);
        System.out.println("=====================");
        Student student2 = new Student("mulin",20);
    }

    public static void main(String[] args) {
        Person person1 = new Person("hit",10);
        System.out.println("========================");
        Person person2 = new Person("mulin",20);
    }
}

执行结果

java 连续赋值语法 java连续继承_java_05

1、父类静态代码块优先于子类静态代码块执行(最早执行)

2 、父类实例代码块和父类构造方法紧接着执行

3 、子类的实例代码块和子类构造方法紧接着再执行

4 、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行

5.finnal关键字 

1). 修饰变量或字段,表示常量(即不能修改)

2). 修饰类:表示此类不能被继承

3). 修饰方法:表示该方法不能被重写

二、组合

组合和继承相似是一种表达类之间关系的方式,是将一个类的实例作为另外一个类的字段

继承表示对象之间是is-a的关系        比如:狗是动物,猫是动物

组合表示对象之间是has-a的关系        比如:汽车和其轮胎、发动机、方向盘、车载系统等的关系就应该是组合

//轮胎类
class Tire {
    //...
}

//发动机类
class Engine{
    //...
}

//车载系统类
class VehicleSystem{
    //...
}

class Car{
    private Tire tire;// 可以复用轮胎中的属性和方法
    private Engine engine;// 可以复用发动机中的属性和方法
    private VehicleSystem vs;// 可以复用车载系统中的属性和方法

    //...
}

// 奔驰是汽车
class Benz extends Car{
    // 将汽车中包含的:轮胎、发送机、车载系统全部继承了下来
}

三、多态 

多态的概念:通俗来说,就是多种形态,同一件事情,发生在不同对象身上,就会产生不同的结果

【在java中要实现多态,必须要满足如下几个条件】

  • 必须在继承体系下
  • 子类必须要对父类中方法进行重写
  • 通过父类的引用调用重写的方法

多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法

public class Animal1 {
    String name;
    int age;

    public Animal1(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void eat(){
        System.out.println(name+"吃饭");
    }
}

public class Cat2 extends Animal1{
    public Cat2(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(name+"吃鱼");
    }
}

public class Dog2 extends Animal1{
    public Dog2(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(name+"吃骨头");
    }
}

public class TestAnimal1 {
    public static void eat(Animal1 animal1){
        animal1.eat();
    }

    public static void main(String[] args) {
        Cat2 cat2 = new Cat2("yuanbao",2);
        Dog2 dog2 = new Dog2("xiaoqi",1);

        eat(cat2);
        eat(dog2);
    }
}

执行结果

java 连续赋值语法 java连续继承_java 连续赋值语法_06

 

重写(override):是子类对父类非静态、非private修饰,非fifinal修饰,非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变即外壳不变,核心重写!重写的好处在于子类能够根据需要实现父类的方法

方法重写的规则

  • 子类在重写父类的方法时,一般必须与父类方法原型一致:修饰符 返回值类型 方法名(参数列表) 要完全一致
  • 被重写的方法返回值类型可以不同,但是必须是具有父子关系的
  • 访问权限不能比父类中被重写的方法的访问权限更低
  • 父类被static、private修饰的方法、构造方法都不能被重写
  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 fifinal 的方法。
  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 fifinal 方法。
  • 重写的方法, 可以使用 @Override 注解来显式指定

重写和重载的区别

java 连续赋值语法 java连续继承_父类_07


避免在构造方法中调用重写的方法

  • 构造 D 对象的同时, 会调用 B 的构造方法.
  • B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func
  • 此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0.
public class B {
    public B() {
        //do nothing
        func();
    }

    public void func(){
        System.out.println("B.func()");
    }
}

public class D extends B{
    private int num = 1;

    @Override
    public void func() {
        System.out.println("D.func()"+num);
    }
}

public class Test3 {
    public static void main(String[] args) {
        D d = new D();
    }
    //执行结果
    //D.func()0
}
  • 构造 D 对象的同时, 会调用 B 的构造方法
  • B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func
  • 此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0