JAVA面向对象(下 )(一、继承和方法重写)

一、继承

1.1 什么是继承

生活中继承是指:

  • 继承财产=>延续财产

  • 继承/遗传 父母的长相,基因 => 延续上一代的基因

  • 继承技能、专业、职位 =>延续

  • 继承中华民族的传统文化 => 延续

  • 青出于蓝而胜于蓝 或 长江后浪推前浪,前浪被拍在沙滩上 => 下一代比上一代更厉害

Java中继承是指:

  • 代码的复用:子类可以继续使用父类已经声明过的代码

  • 代码的扩展:子类比父类对事物的描述更具体,更丰富,(属性或功能更多)

  • is-a 的关系:子类是父类事物的一个分支

示例代码
父类Person
package com.atguigu.inherited;

public class Person {//父类
    //属性
    public String name;
    public int age;

    //方法
    public void eat(){
        System.out.println(name+"现在是" + age +"岁,在吃东西");
    }
}
子类Student
package com.atguigu.inherited;

//Student是子类
//Person是父类
//子类继承了父类的成员,代码的复用
//子类和父类不同,子类的成员比父类多,子类更具体
public class Student extends Person{
    //属性
    public int score;//成绩

    //方法
    public void exam(){
        System.out.println(name+"现在是" + age +"岁,在在考试");
    }
}
测试类TestStudent
package com.atguigu.inherited;

public class TestStudent {
    public static void main(String[] args) {
        //Person对象只能访问name,age属性,调用eat()方法
        Person p = new Person();
        p.name = "李四";
        p.age = 24;
        p.eat();

        System.out.println("=========================");

        //Student对象可以访问name,age,score属性,调用eat()和exam()方法
        Student s = new Student();
        s.name = "张三";
        s.age = 23;
        s.score = 99;//父类已经声明过了,从父类继承的

        s.eat();//父类已经声明过了,从父类继承的
        s.exam();
    }
}

1.2 如何继承?

语法格式:

【修饰符】 class 父类名{
    
}
【修饰符】 class 子类名 extends  父类名{ //extends是关键字
    
}

父类:SuperClass,又称为超类,基类。

子类:SubClass,又称为派生类。

extends:扩展

1.3 继承有什么特点或要求

1、Java中只支持单继承

比喻:每一个人只有一个亲生父亲。

【修饰符】 class 子类名 extends  父类名1, 父类名2{ //错误
    
}

2、Java中支持多层继承

比喻:代代相传。

解释:父类也可以有父类,父类的父类对于这个子类来说也是父类。

public class A{
    
}
public class B extends A{
    
}
public class C extends B{
    
}
//B是C的父类,它是C的直接父类。
//A也是C的父类,它是C的间接父类。
//B会继承A的成员,然后C会继续继承B的成员。C的成员最多。

3、同一个父类可以同时有多个子类

比喻:支持多胎。

public class A{
    
}
public class B extends A{
    
}
public class C extends A{
    
}
//B和C同时都是A的子类,而且是并列关系。B和C是兄弟类关系。

4、父类的所有成员变量、成员方法会继承到子类吗?

父类的所有成员变量、成员方法,都会继承到子类中。但是,父类中私有的成员变量,成员方法,子类不能直接使用,如果子类需要使用,直接通过间接的方式使用,例如:通过get/set方式。

Father类
package com.atguigu.inherited;

public class Father {//父类
    //这四个属性的权限修饰符是不同的
    public int a;
    int b;
    protected int c;
    private int d;

    public int getD(){
        return d;
    }

    public void setD(int d) {
        this.d = d;
    }
}
Son类
package com.atguigu.inherited;

//Son是子类,Father是父类
public class Son extends Father {
    private int e;

    public void setE(int e) {
        this.e = e;
    }

    public String getInfo() {
//        return "a = " + a + ",b = " + b + ",c= " + c + ",d = " + d + ",e = " + e;
        //d因为在父类中是private修饰,所以在子类中无法直接使用
        return "a = " + a + ",b = " + b + ",c= " + c + ",d = " + getD() + ",e = " + e;
    }
}
测试类TestSon
package com.atguigu.inherited;

public class TestSon {
    public static void main(String[] args) {
        Son s = new Son();
        s.a = 1;
        s.b = 1;
        s.c = 1;
//        s.d = 1;
//        s.e = 1;
        s.setD(1);
        s.setE(1);
        System.out.println(s.getInfo());
    }
}

5、父类的构造器会继承到子类吗?

父类的构造器不会继承到子类中,但是,子类的构造器中必须调用父类的构造器。

  • 不会继承的原因:父类的构造器是用来创建父类的对象的

  • 必须调用的原因:父类的构造器中 编写了 为父类中声明的这些属性初始化的代码,那么子类会继承这些属性,就需要“复用”父类构造器的这些代码为它们初始化。

说明:

  • 子类的构造器==默认==去找父类的无参构造。

  • 当然,子类构造器也可以通过 super(); 或 super(实参列表); 来明确子类找父类的哪个构造器

    • super(); 明确调用父类的无参构造

    • super(实参列表); 明确调用父类的有参构造

父类Animal
package com.atguigu.inherited;

public class Animal {//父类,Animal:动物
    private String name;
    private int age;

    public Animal() {
        System.out.println("父类Animal的无参构造");
    }

    public Animal(String name, int age) {
        System.out.println("父类Animal的有参构造");
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
子类Dog
package com.atguigu.inherited;

//Dog是子类
//Animal是父类
public class Dog extends Animal{//Dog:狗
    private double weight;//重量

    public Dog() {
        super();//这句话可以省略,表示默认调用父类的无参构造
        //super("小黑",1);
        System.out.println("子类Dog的无参构造");
    }

    public Dog(String name, int age, double weight) {
        super(name, age);//明确说明调用父类的有参构造,为name和age属性初始化
        //super();
        System.out.println("子类Dog的有参构造");
        this.weight = weight;
    }
}
测试类TestDog
package com.atguigu.inherited;

public class TestDog {
    public static void main(String[] args) {
        Dog d1 = new Dog();
        Dog d2 = new Dog("小白",3,5.6);
    }
}
思考题1

问:子类的无参构造一定找父类的无参构造,子类的有参构造一定找父类的有参构造?没有

思考题2

问:父类没有无参构造,会怎么样?

6、子类可以重写父类的方法

请看$1.4小节。

1.4 方法的重写

如果父类的某个成员方法,继承到子类后,子类认为该方法的方法体功能实现不适合子类,子类可以选择对其进行重写(Override)。

1.4.1 重载与重写的区别

方法的重载(Overload)方法的重写(Override)
2个或多个方法的位置在同一个类中 或 父子类中父子类中
修饰符不看必须满足> 或 =的关系
返回值类型不看void:必须相同 基本数据类型:必须相同 引用数据类型:必须满足 < 或 = 的关系
方法名必须相同必须相同
(形参列表)必须不同。个数、类型、顺序不同,和形参名无关。必须相同。个数、类型、顺序相同,和形参名无关。
{方法体}不看必须重写实现

示例代码1
父类Fu1
package com.atguigu.override;

public class Fu1 {//父类
    public void m1(){
        System.out.println("Fu.m1方法");
    }

    protected void m2(){
        System.out.println("Fu.m2方法");
    }

     void m3(){
        System.out.println("Fu.m3方法");
    }

    private void m4(){
        System.out.println("Fu.m4方法");
    }
}
子类Zi1
package com.atguigu.override;

//Zi是子类,Fu是父类
public class Zi1 extends Fu1{
    //因为父类Fu1的m1方法的权限修饰符是public
    //子类Zi1的m1方法,权限修饰符必须是public
    @Override
    public void m1(){
        System.out.println("Zi.m1方法");
    }

    //因为父类Fu1的m2方法的权限修饰符是protected
    //子类Zi1的m2方法,权限修饰符可以是public 或 protected
    @Override
    public void m2(){
        System.out.println("Zi.m2方法");
    }

    //因为父类Fu1的m3方法的权限修饰符是缺省
    //子类Zi1的m3方法,权限修饰符可以是public 或 protected 或缺省
    @Override
    void m3(){
        System.out.println("Zi.m3方法");
    }

    //不是重写,因为父类Fu1的m4方法,在子类中不可见
    //@Override //加它会报错,因为现在的m4方法不是重写,它只是子类Zi1自己定义的一个方法
    private void m4(){
        System.out.println("Zi.m4方法");
    }
}
父类Fu2
package com.atguigu.override;

public class Fu2 {
    public void m1(){
        System.out.println("Fu.m1的方法");
    }

    public int m2(){
        System.out.println("Fu.m1的方法");
        return 0;
    }

    public Object m3(){
        System.out.println("Fu.m3的方法");
        return new Object();
    }

    public String m4(){
        System.out.println("Fu.m4的方法");
        return new String();
    }
}
子类Zi2
package com.atguigu.override;

public class Zi2 extends Fu2{
    //因为Fu2类的m1方法的返回值类型是void
    //Zi2的m1方法的返回值类型只能是void
    public void m1(){
        System.out.println("Zi.m1的方法");
    }

    //因为Fu2类的m2方法的返回值类型是int
    //Zi2的m2方法的返回值类型只能是int
    public int m2(){
        System.out.println("Zi.m2的方法");
        return 1;
    }

    //因为Fu2类的m3方法的返回值类型是Object
    //Zi2的m3方法的返回值类型可以是Object,或 Object的子类
    //String < Object
    public String m3(){
        System.out.println("Zi.m3的方法");
        return  new String();
    }

    //因为Fu2类的m4方法的返回值类型是String
    //Zi2的m4方法的返回值类型可以是String,或String的子类
    //很遗憾,String没有子类,它是太监类
    public String m4(){
        System.out.println("Zi.m4的方法");
        return new String();
    }
}
父类Fu3
package com.atguigu.override;

public class Fu3 {
    public void m1(){
        System.out.println("Fu.m1");
    }

    public void m2(int a, int b){
        System.out.println("Fu.m2");
    }

    public void m3(int a, int b){
        System.out.println("Fu.m3");
    }
}
子类Zi3
package com.atguigu.override;

public class Zi3 extends Fu3{
    //因为Fu3的m1方法的形参列表是(),
    //那么Zi3的m1方法如果是()就是重写,如果是(非空)就是重载
    public void m1(int a){
        System.out.println("Zi.m1");
    }

    //因为Fu3的m2方法的形参列表是(int a, int b),
    //那么Zi3的m2方法的形参列表(int x, int y)
    //它们是重写关系,不看形参的名字,
    //只看类型,个数,顺序
    public void m2(int x, int y){
        System.out.println("Zi.m2");
    }

    //因为Fu3的m3方法的形参列表是(int a, int b),
    //那么Zi3的m3方法的形参列表(int x)
    //它们是重载关系,不看形参的名字,
    //只看类型,个数,顺序
    public void m3(int x){
        System.out.println("Zi.m3");
    }
}

1.4.2 @Override有什么用

@Override是一个注解。是对代码进行注释。这个注释是可以给编译器看的,
编译器看到某个方法上面加了@Override,就会对这个方法按照重写的要求,
进行格式检查,看他是否满足重写的要求,不满足就编译报错,如果满足就
不报编译错。
但是,如果一个重写的方法,没有违反重写的要求,那么加@Override
和不加它没有任何区别。

总结:@Override只是对重写方法起到一个格式检查的作用,不影响重写的本质。

建议:重写的方法都加上@Override。

1.5 super关键字

super可以调用父类声明的xxx成员。

1、super() 或 super(实参列表)

它们会出现在子类构造器的首行。表示调用父类的构造器。它只能找直接父类的。

  • super():找父类无参构造。

  • super(实参列表):找父类的有参构造。

示例代码
GrandFather爷爷类
package com.atguigu.keyword;

public class GrandFather {//爷爷类
    private int a;

    public GrandFather() {
        System.out.println("GrandFather的构造器");
    }

    public GrandFather(int a) {
        this.a = a;
    }
}

 

Father父类
package com.atguigu.keyword;

public class Father extends GrandFather{//父类
    public Father(){
        System.out.println("Father的构造");
    }
}

 

GrandSon孙子类

 


public class GrandSon extends Father{
    public GrandSon() {
        super();
        System.out.println("GrandSon的构造器");
    }

    public GrandSon(int a){
//        super(a);//报错,因为Father类没有有参构造
    }
}

测试类TestGrandSon

package com.atguigu.keyword;

public class TestGrandSon {
    public static void main(String[] args) {
        GrandSon g = new GrandSon();

        /*
        从构造器的角度来说,
        孙子要先找爸爸,爸爸再找爷爷。

        但是不能孙子直接找爷爷的构造器。
         */
    }
}

2、super.方法

当子类重写了父类的某个方法,但是在子类中又要调用父类被重写的方法,就必须用“super.被重写的方法”,否则就会自己调用自己的。

如果子类没有重写父类的某个方法,在子类中需要调用这个父类的方法,加不加super.都可以。

3、super.成员变量

super.成员变量是表示使用父类的成员变量。

当父类的成员变量,与子类的成员变量重名了,可以用"super.成员变量"表示使用父类的成员变量。

如果父类的成员变量,与子类的成员变量没有重名问题,可以直接使用父类的成员变量。

虽然我们讲了这种使用方式,但是开发中一定要避免父子类的成员变量重名。

示例代码

父类Fu

package com.atguigu.keyword;

public class Fu {
    public int a = 1;
    public  int b = 1;
}

 

子类Zi

package com.atguigu.keyword;

public class Zi extends Fu{
    public int a = 2;

    public void test(int a){
        System.out.println("a = " + a);//有重名问题,就近原则,使用局部变量
        System.out.println("this.a = " + this.a);//有重名问题,明确说明使用本类的
        System.out.println("super.a = " + super.a);//有重名问题,明确说明使用父类的

        System.out.println("b = " + b);//没有重名问题,直接使用父类的
    }
}

 

测试类TestZi

package com.atguigu.keyword;

public class TestZi {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.test(3);
    }
}

4、注意

无论在子类中调用父类的构造器,还是父类的方法,还是父类的成员变量,都要求被调用的父类成员不能是private。就算加super也不能调用。

1.6 根父类Object类

1.6.1 根父类的概念

java.lang.Object类是所有Java类的父类,它是老祖宗。所有Java类都是它的子类。

如果一个类没有明确说明它继承(extends)哪个类,那么它的父类就是Object。

1.6.2 toString方法

Object类中声明了public String toString()方法,所有Java类(或者说所有引用数据类型)都会从Object继承这个方法。

如果子类不重写toString方法,默认返回的是 对象的实际类型(new它的类型) @ 对象的hashCode值的十六进制形式。

官方API文档说明了,建议所有子类都重写toString方法,用于返回对象的详细信息,一般都是返回对象的属性值拼接。等价于我们之前做练习用的getInfo方法。

package com.atguigu.api;

//Person的直接父类就是Object
public class Person {
}

 

package com.atguigu.api;

public class TestPerson {
    public static void main(String[] args) {
        Person p = new Person();
        System.out.println(p.toString());
        //这里toString方法,就是从Object类继承的
        //默认打印的是 com.atguigu.api.Person@4eec7777

        System.out.println(p.hashCode());//1324119927
        //1324119927是p对象的hash值的十进制形式
        //4eec7777是 hash值的十六进制形式
        //关于hash值是什么,干什么用的,今天先不讨论,等后面讲哈希表等数据结构的时候再说。
    }
}

toString()非常特殊,当我们用System.out.println() 或 System.out.print()方法打印对象时,默认就会调用这个对象的toString,不用程序员手动调用。或者当我们把一个Java对象与字符串进行拼接时,也会自动调用这个对象的toString。

toString()方法的重写有两个快捷键: 

  • Alt + Insert:属性拼接的模板

  • @Override
    public String toString() {
        return "Rectangle{" +
            "length=" + length +
            ", width=" + width +
            '}';
    }

  • Ctrl + O:重写父类的哪些方法。使用调用父类toString方法的模板。

@Override
public String toString() {
    return super.toString();
}

1.6.3 示例代码1

Rectangle矩形类
package com.atguigu.api;

public class Rectangle {
    private double length;
    private double width;

    public Rectangle() {
    }

    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    public double getLength() {
        return length;
    }

    public void setLength(double length) {
        this.length = length;
    }

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        this.width = width;
    }

    //下面的toString方法,使用快捷键Alt + Insert自动生成
    @Override
    public String toString() { 
        return "Rectangle{" +
                "length=" + length +
                ", width=" + width +
                '}';
    }
}

测试类
package com.atguigu.api;

public class TestRectangle {
    public static void main(String[] args) {
        Rectangle r = new Rectangle(8,4);
        System.out.println(r.toString());
        System.out.println(r);
        //打印r对象时,自动会调用r的toString
    }
}

  • 38
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值