Java——继承、覆写与多态

目录

1.继承的定义与使用

1.1继承的实现

1.2 继承的限制

2.覆写

2.1 方法的覆写

2.2 属性的覆写(了解)

2.3 super关键字

3.final关键字——终结器

3.1 final修饰类(String类以及8大基本数据类型的包装类,Integer)

3.2 final修饰方法

3.3 final修饰属性——常量

4.多态


1.继承的定义与使用

  • 继承的主要作用在于:在已有基础上继续进行功能的扩充(可重用)
  • 继承能够消除结构定义上的重复
  • 1.1继承的实现

  • 继承使用extends关键字来实现
class 子类 extends 父类
  • Java是单继承 : 一个子类只有一个父类
  • 子类也称为派生类,父类也成为超类 / 基类
  • 1.2 继承的限制

子类对象实例化前一定会首先调用父类的构造方法,实例化父类对象后再调用子类构造方法,进行子类对象初始化。

  • 实际上在子类的构造方法之中,相当于隐含了一个语句 super();子类和父类都有默认的构造方法时,不调用。
  • super( )调用父类的构造方法,this( )调用当前对象的构造方法
public class Super{
    public static void main(String[] args){
        new Son("abc");
    }
}

class Father{
    //无参数的构造方法——默认的构造方法  public Father(){}
}

class Son extends Father{
    public Son(String name){
        //Father super();
    }
}
  • Java不允许多重继承,但是允许多层继承
  • 子类会继承父类的所有结构(包含私有域与其他属性、方法),但不一定能访问所有的内容
  • 显式继承:子类能够使用所有非private操作,可直接调用。
  • 隐式继承:所有的private操作无法被直接调用,需要通过其他形式调用

代码示例:

public class Inherit{
    public static void main(String[] args){
        //学生继承了Person类,而Person类由于有自定义的构造方法,因此没有默认的构造方法
        //所以必须在Student中定义构造方法
        Student student = new Student("Jack","男","1","bit");
        //调用Student.toString,而Student.toString是继承Person.toString
        //因此打印的是Person.toString
        System.out.println(student);
    }
}

//学生在Person的基础上增加学号、学校
//Person是Student的父类
//Studnt是Person的子类
class Student extends Person{
    private String num;
    private String school;

    public Student(String name,String gender,String num,String school){
        //虽然Student继承了Person,但是由于name、gender是私有的,因此Student不能访问
        //this.name=name;   //error

        //直接赋值 ->能够访问到父类的属性(属性不是私有的,父类提供无参的构造方法)
        //通过构造方法 -> 父类提供构造方法(带参数)
        //通过setter方法 ->能够访问到父类的setter方法
        super(name,gender);  //调用父类的构造方法
        this.num=num;
        this.school=school;
        System.out.println("这是子类的构造方法");
    }
    public String getNum(){
        return this.num;
    }
    public String getSchool(){
        return this.school;
    }

    //方法覆写
    public String toString(){
        return super.toString()+" 学号:"+this.num+" 学校:"+this.school;
    }

    // public String toString(){
    // return " 姓名:"+this.getName()+" 性别:"+this.getGender()+" 学号:"+this.num+" 学校:"+this.school;
    // }

}

//面向对象的一个特性——封装,对方封装好的部分进行扩展,
//开闭原则:对扩展开放,对修改关闭
class Person{
    private String name;
    private String gender;

    //构造方法
    public Person(String name,String gender){
        this.name=name;
        this.gender=gender;
        System.out.println("这是父类的构造方法");
    }

    //getter方法

    public String getName(){
        return this.name;
    }

    public String getGender(){
        return this.gender;
    }

    public String toString(){
        return " 姓名:"+this.name+" 性别:"+this.gender;
    }
}

2.覆写

如果子类定义了与父类完全相同(不算权限)的方法或者属性的时候,这样的操作称为覆写。

  • 2.1 方法的覆写

子类定义了与父类方法名称、参数列表、返回值完全相同的方法。被覆写的方法不能拥有比父类更为严格的访问控制权限。

  • 判断调用的是父类方法或子类方法:

a.看new在哪儿(当前使用的对象是通过哪个类new的)

b.调用的方法是否被子类覆写,如果被覆写,调用的一定是被覆写后的方法。

  • private < default—包访问权限
  • 方法覆写不能出现private关键字(隐式继承影响)

练习:

解释方法重载与方法重写的区别

    a.概念上:

方法重载:方法名相同,参数列表不同,与返回值无关。

方法重写:子类定义了和父类方法名称、参数列表、返回值完全相同的方法,只是权限不同。

    b.范围上:

方法重载:在同一个类中

方法重写:在有继承关系的类之间

    c.权限要求:

方法重载:没有权限要求

方法重写:被覆写的方法不能拥有比父类更为严格的访问控制权限


  • 2.2 属性的覆写(了解)

当子类定义了和父类属性名称完全相同的属性的时候,就称为属性的覆写。

  • 属性覆写不对访问控制权限有要求,因为属性是通过内部来进行访问。

/**
 * 方法覆写
 * Author:qqy
 */
public class Test11{
    public static void main(String [] args){
        Person person=new Person();
        person.print();

        Student student=new Student();
        //就近原则
        System.out.println(student.getName());
    }
}

class Person{
    public String name="Jack";

    //成员方法
    public void print(){
        System.out.println("这是Person的print方法");
    }

    private void hello(){
        System.out.println("这是Person的hello方法");
    }

}

class Student extends Person{
    //属性覆写
    private String name="Tom";

    //default——包私有
    //public>protected>[default]>private
    public void print(){
        System.out.println("这是Student的print方法");
    }

    //此时,子类的hello方法并不是方法覆写
    //因为父类中的hello方法是私有的,子类已经看不见父类的hello方法
    //因此,这只是子类的普通方法,与父类的方法没有任何关系
    public void hello(){
        System.out.println("这是Student的hello方法");
    }

    public String getName(){
        return name;
    }
}
  • 2.3 super关键字

  • 2.3.1 super用于方法

       1.  用于构造方法,表示调用父类构造方法  super(参数列表)

a.当子类调用父类无参构造时,super( )可写可不写,表示父类无参构造;

   当子类调用父类有参构造时,super(参数列表)必须要写,告诉编译器当前调用的是哪个有参构造

b.子类构造方法中调用父类构造必须是第一行语句

c. this与super不能同时调用

       2.  用于普通方法,super.方法名(参数)

用于在子类中,明确调用父类被覆写的方法

  • 2.3.2 super用于属性(了解)

super.属性名  表示调用父类中被覆写的属性(权限不是private)

代码示例:

class Person{
    public String str="daddy";
    public void fun(){
        System.out.println("父类");
    }
}

class Student extends Person{
    public String str="child";
    public void fun(){
        //明确调用父类被覆写的fun方法
        super.fun();
        System.out.println("子类");
        //明确调用父类被覆写的属性
        System.out.println(super.str);
    }
}

public class Test1{
    public static void main(String [] args){
        System.out.println(new Student().str);
        new Student().fun();
    }
}

3.final关键字——终结器

  • 3.1 final修饰类(String类以及8大基本数据类型的包装类,Integer)

  • 被final修饰的类不能被extends(编译无法通过)

  • final修饰内部类,只能被子类使用

  • final修饰的属性、变量(引用)、参数,一旦初始化后就不能再赋值了

  • 一旦一个类被final修饰,该类的所有方法都会默认加上final(成员变量不会加final

  • 3.2 final修饰方法

  • 被final修饰的方法,不允许被覆写
  • 被private修饰的方法,相当于加了一个final关键字
  • 3.3 final修饰属性——常量

  • 3.3.1 修饰普通数据类型的成员变量(最主要用途)
  • 被final修饰的成员变量必须在声明时初始化(构造块或构造方法中初始化),并且在初始化后值无法修改。
  • 被final修饰的全局变量必须在声明时初始化(静态块初始化),并且在初始化后值无法修改。

练习:

a和b的区别:

class Person{
    public final int a=5;
    public static final int B=10;
}

a :  final变量——常量(值不能改变,每个对象都有自己的final变量,在对象产生时初始化)(对象)

b :  static  final变量——全局常量(所有对象共享此变量,类加载时初始化,效率较高,通过类名调用),存储于全局数据区。字母全大写,下划线分隔。(类)

  • 3.3.2 修饰引用数据类型的成员变量(值不能改变)

代码示例:

class Person{
    public String name="daddy";
    public int age=20;
}

public class Test1{
    public static void main(String [] args){
        final Person p=new Person();
        //p的地址不变
        //p=new Person();  //error
        //内容可以改变
        p.name="hello";
        p.age=30;
        System.out.println(p.name);
        System.out.println(p.age);
    }
}
  • 两个操作数都为:byte、 short、int 、char,两个数都会被转换成int类型,但是final修饰的类型不会发生变化。

   练习:

public class Test1{
    byte b1=2,b2=2,b3,b4;
    final byte b5=4,b6=5,b7=8;
    public void test(){
        //假设下列语句没有关联性

        //b3是byte型,b1+b2的结果是int型,需强转
        b3=b1+b2;  //error

        //b4是byte型,b5、b6有final修饰,相加结果仍为byte型
        b4=b5+b6;

        //b5被final修饰,值不可修改
        b5=b1+b3;  //error

        //b3是byte型,b4是int型,b4+b5结果为int型,需强转
        b3=b5+b4;  //error
    }
}

4.多态

  • 方法多态性:1.方法重载  2.方法覆写
  • 对象多态性的核心本质:方法的覆写
     
  • 向上转型(自动):用于参数统一化

父类  父类引用 = new 子类( );

  • 向下转型(强制):当父类引用需要调用子类扩充方法时,才需要向下转型

子类  子类引用 = (子类)父类实例;

  • 要发生向下转型,必须先发生向上转型,否则在转型时会出现运行时异常:ClassCastException  (类型转换异常)
public class Test1{
    public static void main(String[] args){
        //向上转型

        //父类  父类引用 = new 子类( );
        Person per=new Student();
        per.fun();   //子类
        System.out.println(per.getPersonInfo());  //Person info
        //Student给的是子类对象,但变量类型是Person
        //使用时,将一个Student变量当Person来用
        //我们所看到的属性、方法,是Person中定义的
        //Person中没有定义getStudentInfo(),因此下行代码错误
        //System.out.println(per.getStudentInfo());  //error

        //向下转型

        //子类  子类引用 = (子类)父类实例;
        Student stu=(Student)per;
        //1.  Student stu=(Student)new Person();  //编译通过,无法运行(CCE)
        //2.  Person per1=new Person();
        //    Student stu1=(Student)per1;     1、2等价
        stu.fun();    //子类
        System.out.println(stu.getPersonInfo());  //Person info
        System.out.println(stu.getStudentInfo()); //Student info
    }
}

class Person{
    public void fun(){
        System.out.println("父类");
    }

    public String getPersonInfo(){
        return "Person info";
    }
}

class Student extends Person{
    public void fun(){
        System.out.println("子类");
    }

    public String getStudentInfo(){
        return "Student info";
    }
}
  • 引用名 instanceof 类:表示该引用是否能表示该类实例
public class Test6{
    public static void main(String[] args){
        Person per=new Person();
        //per可以由Person类创建,返回true
        System.out.println(per instanceof Person);
        //如果per不能由Student创建
        if(!(per instanceof Student)){
            per=new Student();
            System.out.println(per instanceof Student);
        }
    }
}

class Person{
    public void fun(){
        System.out.println("父类");
    }
}

class Student extends Person{
    public void fun(){
        System.out.println("子类");
    }
}
  • 向上转型作用:参数统一化
class Person{
    public void fun(){
        System.out.println("人类");
    }
}

class Student extends Person{
    public void fun(){
        System.out.println("学生");
    }
}

class Worker extends Person{
    public void fun(){
        System.out.println("工人");
    }
}

public class Test1{
    public static void main(String[] args){
        Test1(new Person());   //人类
        //向上转型
        Test1(new Student());  //学生
        //向上转型
        Test1(new Worker());   //工人
    }
    public static void Test1(Person per){
        per.fun();
    }
}

 

你可能感兴趣的:(JavaSE)