1、继承概述
- 继承:解决代码复用,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义相同的属性和方法,所有子类不需要重新定义这些属性和方法,只需要通过extends来生命继承父类
 
(1)继承语法
class 子类 extends 父类{
}
/*
1、子类就会自动拥有父类定义的属性和方法;
2、父类又叫超类,基类;
3、子类又叫派生类
*/
 
- 继承示意图
 

(2)继承入门范例
- 目录如下:
 

①创建父类
package com.taobao;
public class Person {
    public String name;     //名字公开
    private int age;        //age私有化
    private double salary;  //..
    //alt+insert
    public Person() {
    }
    public Person(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
        //将set方法写在构造器中,这样仍然可以验证
        setSalary(salary);
        setName(name);
        setAge(age);
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        //1、加入对数据的校验,相当于增加了默认逻辑
        if(name.length() >=2 && name.length()<=6) {
            this.name = name;
        } else {
            System.out.println("名字的长度不对,需要2到6字符,默认名字");
            this.name = "青春重新输入";
        }
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        if(age>=1 && age <= 120) {
            this.age = age;
        }else{
            System.out.println("设置年龄不对,需要在(1-120),给默认年龄18");
            this.age = 18;    //给一个默认年龄
        }
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    //写一个方法,返回属性信息
    public String info(){
        return "信息为 name =" + name + "age = " + age + "薪水为 =" + salary;
    }
}
 
②创建子类
package com.taobao;
public class Man extends Person {
    public void testing(){
        System.out.println(name);
    }
}
 
③main方法调用子类
package com.taobao;
public class hello {
    public static void main(String[] args) {
        Man man = new Man();
        man.name = "张三";
        man.testing();
    }
}
 
- 运行结果
 

2、继承细节
2.1、子类继承了所有的属性和方法
①非私有的属性和方法可以在子类中直接访问;
②私有属性和方法不能在子类直接访问,要通过公共方法访问;
- 范例:目录结构如下
 

(1)父类Person创建
public class Person {
    public String name;
    private double salary;  //..
    //alt+insert
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    //写一个公共方法,返回属性信息
    public String info(){
        return "薪水为 =" + salary;
    }
    //写一个私有方法
    private void info2(){
        System.out.println("姓名:" + name);
    }
    //callTest
    public void callInfo2(){
        info2();
    }
}
 
(2)子类Man继承Person父类
public class Man extends Person {
    public void testing(){
        System.out.println(name);
    }
}
 
(3)main方法使用子类
public class hello {
    public static void main(String[] args) {
        Man man = new Man();
        //1、子类继承非私有属性 - 可以直接访问
        man.name = "张三";
        //2、子类继承私有属性 - 通过父类提供公共方法(get)访问属性
        man.setSalary(1000);
        //3、之类继承父类公共方法 - 可以直接访问
        System.out.println(man.info());
        //4、子类继承父类的私有方法 - 通过在父类提供公共方法访问私有方法
        man.callInfo2();
        //5、直接访问子类的方法
        man.testing();
    }
}
 
- 运行结果
 

2.2、子类必须调用父类构造器,完成父类的初始化
-  
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会调用父类的无参构造器
- 子类的无参构造器中默认有个 super(),代表调用父类的无参构造器;
 - 当创建子类对象时,不管使用子类的哪个构造器,默认情况下都会调用父类的无参构造器
 
 -  
范例:目录结构如下
 

①创建父类
package com.taobao;
public class Person {
    //父类构造器
    public Person() {
        System.out.println("调用父类的构造器...");
    }
}
 
②创建子类
package com.taobao;
public class Man extends Person {
    //子类构造器
    public Man() {
        //super();                 //默认调用父类的无参构造器
        System.out.println("子类的无参构造器...");
    }
    public Man(String name){
        //super();
        System.out.println("参数"+name);
    }
}
 
③main方法创建子类对象
package com.taobao;
public class hello {
    public static void main(String[] args) {
        Man man = new Man();
    }
}
 
- 运行结果:先创建父类,后创建子类
 

2.3、父类没有提供无参构造器
-  
注意事项:必须在子类的构造器中用super去指定使用哪个父类的构造器完成对父类的初始化工作,否则编译不会通过;
 -  
范例:目录结构如下
 

①创建父类
package com.taobao;
public class Person {
    public Person(String name) {
        System.out.println("调用父类的构造器...");
    }
}
 
②创建子类
package com.taobao;
public class Man extends Person {
    public Man() {
        super("张三");
        System.out.println("子类的无参构造器...");;
    }
    public Man(String name) {
        super("张三");
        System.out.println("子类的有参构造器...");;
    }
}
 
③创建对象,分别调用子类的无参构造器和带参构造器
package com.taobao;
public class hello {
    public static void main(String[] args) {
        System.out.println("调用子类无参构造器!");
        Man man = new Man();
        System.out.println("调用子类的带参构造器!");
        Man man1 = new Man("lizhi");
    }
}
 
- 运行结果
 

- 注意事项 
  
- ①如果希望指定去调用父类的某个构造器,则显式的调用一下 :super(参数列表)
 - ②super在使用时,需要放在构造器的第一行
 - ③super()和this()都只能放在构造器第一行,因此两个方法不能存在同一个构造器
 - ④java所有类都是object的父类,object是所有类的基类;
 - ⑤父类构造器的调用不限于直接父类!将一直往上追溯知道Object类(顶级父类)
 - ⑥子类最多继承一个父类,即java中是单继承方式:如何让A类继承B和C,先让B类继承C,再让A类继承B
 - ⑦不能滥用继承,子类和父类之间必须满足is-a的逻辑关系;
 
 
3、继承的本质
- 范例
 
(1)新建类GradPa
package com.taobao;
public class GrandPa {
    String name = "爷爷";
    String hobby = "旅游";
}
 
(2)新建子类Father继承GradPa类
package com.taobao;
public class Father extends GrandPa {
    String name = "大头爸爸";
    int age = 39;
}
 
(3)新建子类Son继承Father
package com.taobao;
public class Son extends Father{
    String name = "大头儿子";
}
 
(4)main方法创建子类对象
package com.taobao;
public class hello {
    public static void main(String[] args) {
        Son son = new Son();
        System.out.println("调用Son子类name:"+son.name);
        System.out.println("调用Father父类age:"+son.age);
        System.out.println("调用GrandPa父类hobby:"+son.hobby);
    }
}
 
- 运行结果
 

- 属性查找顺序 : 按照查找关系来返回信息 
  
- ①首先看子类是否有该属性;
 - ②如果子类有这个属性,并且可以访问,则返回信息;
 - ③如果子类没有这个属性,就看父类有没有属性(如果父类有该属性,并且可以访问,就返回信息)
 - ④如果父类没有就按照(3)的规则,继续找上级父类,知道Object
 - ⑤向上找遇上私有属性的则调用get方法;
 
 
4、super关键字
- super:代表父类的引用,用于访问父类的属性,方法,构造器 
  
- 在子类的构造方法中显式的调用父类构造方法
 - 访问父类的成员方法和变量。
 
 
(1)super基本语法
 ①访问父类的属性,但不能访问父类的private属性;
super.属性
 
②访问父类的方法,但不能访问父类的private方法;
super.方法名(参数列表)
 
③访问父类的构造器,但只能放在构造器第一句,和this冲突;
super(参数列表)
 
(2)super调用父类构造方法
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会调用父类的无参构造器,
    子类的无参构造器中默认有个  super(),代表调用父类的无参构造器;
	当创建子类对象时,不管使用子类的哪个构造器,默认情况下都会调用父类的无参构造器
 
(3)super调用父类属性
- 当父类和子类具有相同的数据成员时,JVM 可能会模糊不清,需要用super指定
 - 范例1 - 不加super就近查找
 
//1、创建父类
class Person {
    int age = 12;
}
//2、创建子类
class Student extends Person {
    int age = 18;
    void display() {
        System.out.println("学生年龄:" + age);
    }
}
//3、创建测试类
class Test {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.display();
    }
}
 
- 运行结果
 

- 范例2 : 加上super则跟随父类
 
//1、创建父类
public class Person {
    int age = 12;
}
//2、创建子类
public class Student extends Person {
    int age = 18;
    void display() {
        System.out.println("学生年龄:" + super.age);
    }
}
//3、创建测试类
public class Test {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.display();
    }
}
 
- 运行结果
 

(4)super调用成员方法
-  
当父类和子类都具有相同的方法名时,可以使用 super 关键字访问父类的方法;
 -  
范例:
 
//1、创建父类
class Person {
    void message() {
        System.out.println("This is person class");
    }
}
//2、创建子类
class Student extends Person {
    void message() {
        System.out.println("This is student class");
    }
    void display() {
        message();        // 调用当前类的message方法
        super.message();  // 调用父类的message方法
    }
}
//3、main调用子类
class Test {
    public static void main(String args[]) {
        Student s = new Student();
        s.display();
    }
}
 
- 运行结果 
  
- 只调用方法 message( ),是当前的类 message( ) 被调用;
 - 使用 super 关键字时,是父类的 message( ) 被调用。

 
 
(5)super和this区别
| 区别点 | this | super | |
|---|---|---|---|
| 1 | 访问属性 | 访问本类中的属性,如果本类中没有此属性则从父类中继续查找 | 直接访问父类中的属性 | 
| 2 | 调用方法 | 访问本类中的方法,如果本类中没有此方法则从父类继承 | 直接访问父类中的方法 | 
| 3 | 调用构造器 | 调用本类构造器,必须放在构造器首行 | **直接调用父类构造器,**必须放在构造器首行 | 
| 4 | 特殊 | 表示当前对象 | 子类中访问父类对象 | 
5、方法重写(override)
(1)方法重写概述
- 方法重写:子类中有一个方法,和父类的某个方法名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的方法;
 
//1、创建父类
public class Animal {
    public  void cry(){
        System.out.println("动物汪汪叫...");
    }
}
//2、创建子类
public class Dog extends Animal {
    public  void cry(){
        System.out.println("小狗汪汪叫...");
    }
}
//3、main方法调用子类
public class OverrideMain {
    public static void main(String[] args) {
        //演示方法重写
        Dog dog = new Dog();
        dog.cry();
    }
}
 
- 运行结果
 

(2)方法重写细节
Ⅰ、子类方法的参数,方法名称要和父类方法的参数,方法名称完全一样;
Ⅱ、子类方法的返回类型
-  
父类的方法返回类型一样;
 -  
父类的返回类型的子类;
 
//父类
public object getInfo()
    
//子类
public String getInfo()
 
Ⅲ、子类方法不能缩小父类方法的访问权限
//父类
void sayOk()
//子类
public void sayOk()
 
(3)方法重写和方法重载区别
| 名称 | 发生范围 | 方法名 | 参数列表 | 返回类型 | 修饰符 | 
|---|---|---|---|---|---|
| 重载 | 本类 | 必须一样 | 类型、个数、顺序至少一个不同 | 无要求 | 无要求 | 
| 重写 | 父子类 | 必须一样 | 必须相同 | 一致/父子类 | 子类>父类访问范围 | 










