9.继承、抽象类

继承

概念:

继承就是把多个类中共同的成员提取出来,定义到某个独立的类中,

然后让这个独立类和多个类之间产生一个关系,多个类就具有独立类中的内容,它们之间的关系就是继承。

Java中继承实现

通过extends关键字

class 子类 extends 父类

//Person类
public class Person {
    String name;
    int age ;
    String gender;

    public void show(){
        System.out.println("我的名字是:" + name
                + ",我的性别是:" + gender
                + ",我的年龄是:" + age
                );
    }
}
//Student类
public class Student extends Person {

    String schoolName;

    @Override
    public void show() {
       super.show(); //通过super访问父类的show方法
       System.out.println("我的学校是:" + schoolName);
    }
}

//Teacher类
public class Teacher extends Person{

    String courseName;

    @Override
    public void show() {
        super.show();
        System.out.println("我教的课程是:" + courseName);
    }
}


package com.day9.extend;
//Test类
public class PersonTest {
    public static void main(String[] args) {

        //创建学生对象
        Student s = new Student();
        //给属性赋值
        s.name = "jack";
        s.age = 20;
        s.gender = "男";
        s.schoolName = "南京邮电大学";
        //调用方法
        s.show();


        Teacher t = new Teacher();
        t.name = "tom";
        t.age = 30;
        t.gender = "男";
        t.courseName = "数学";
        t.show();
    }
}

继承父类私有化属性

package com.day9.extend1;

public class Pet {
    private String name;
    private int age;
    private String breeds;
    private String owner;
//构造方法
    public Pet() {
    }

    public Pet(String name, int age, String breeds, String owner) {
        this.name = name;
        this.age = age;
        this.breeds = breeds;
        this.owner = owner;
    }
//set/get方法
    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;
    }

    public String getBreeds() {
        return breeds;
    }

    public void setBreeds(String breeds) {
        this.breeds = breeds;
    }

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
    }
//普通方法show()
    public void show(){
        System.out.println("名字:"+name+",年龄:"+age+",品种:"+breeds+",主人:"+owner);
    }

    @Override
    public String toString() {
        return "Pet{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", breeds='" + breeds + '\'' +
                ", owner='" + owner + '\'' +
                '}';
    }
}
package com.day9.extend1;

import com.day9.extend1.Pet;

public class Dog extends Pet {
    String food;

    public Dog() {
    }

    public Dog(String name, int age, String breeds, String owner, String food) {
        //这里的super表示访问父类的全参构造
        super(name, age, breeds, owner);
        this.food = food;
    }

    public String getFood() {
        return food;
    }

    public void setFood(String food) {
        this.food = food;
    }

    public void show(){
        super.show();
        System.out.println("食物:"+food);
    }

    @Override
    public String toString() {
        return "Dog{" +
                "food='" + food + '\'' +
                "} " + super.toString();
    }
}
package com.day9.extend1;

public class PetTest {
    public static void main(String[] args) {
        Dog d = new Dog("dd",2,"柴犬","Amy","狗粮");
        //子类继承父类的的私有属性,是间接继承,通过构造方法去给属性赋值
        System.out.println(d.getName());
        System.out.println(d.getAge());
        System.out.println(d.getBreeds());
        System.out.println(d.getOwner());
        System.out.println(d.getFood());

        //通过父类的set方法给间接继承的私有化属性赋值
        Dog d1 = new Dog();
        d1.setName("11");
        d1.setAge(1);
        d1.setBreeds("田园犬");
        d1.setOwner("Tom");
        d1.setFood("狗粮a");
        System.out.println(d.getName());
        System.out.println(d1);

    }

}

继承的好处

1.提高代码的复用性和可维护性

2.继承让类和类产生了关系,是多态性的前提

继承的弊端

1.增加了类的耦合性(耦合:类和类之间的关系),某个类改变的话,也会改变其他类,

将来程序设计的原则是:低耦合、高内聚(内聚:某个类解决问题的能力)

2.打破了封装性

继承的特点

1.Java中继承只能是单继承,只能继承一个类,但是可以多重继承

2.如果没有继承,那么系统会自动的让这个类继承Object类

3.子类不能直接继承父类的私有成员属性,但是可以通过构造、继承父类的set方法,间接的使用父类的私有成员属性

4.子类不能继承父类的构造方法,但可以通过super去访问父类的构造方法

什么时候使用继承?

如果一个类和另一个类有is-a的关系,就可以让他们产生继承

继承中的成员关系及访问特点

1.成员属性

子类属性和父类属性不同名,调用谁,就找谁的

子类属性和父类属性同名,优先访问子类的,可以同super访问父类的非私有属性

2.成员方法

子类有,父类也没有,子类将来访问子类的

子类有,父类也有,子类将来访问子类的

子类没有,父类有,就访问父类的

3.构造方法

子类构造方法会默认访问父类的无参构造

如果父类没有无参构造,怎么继承访问?

1)父类没有无参构造,那么必定存在有参构造,这个时候,可以在子类中,通过super调用父类的有参构造

2)在子类中通过this关键字,调用自己的构造方法,被调用的自身的构造方法中,必须要调用父亲的构造

package com.day9.extend2;

//父类
public class Father {
    //父类的成员属性
    public int number1 = 10;
    int number2 = 20;
    protected int number3 = 30;
    private int number4 = 40;
    int number5 = 50;

//    public Father() {
//        System.out.println("这是父类的无参构造!");
//    }

    public Father(int number1) {
        this.number1 = number1;
        System.out.println("父类的有参构造");
    }


    public void say(){
        System.out.println("这是父类的say方法");
    }
    public void hello(){
        System.out.println("这是父类的hello方法");
    }

}

//子类
public class Son extends Father {
    public int number1 = 100;
    int number2 = 200;
    protected int number3 = 300;
    private int number4 = 400;

    public Son() {
        super(10);
        System.out.println("子类的无参构造!");
    }
    public Son(int number1) {
//        super(20);
        this();
        this.number1 = number1;
    }
    public Son(int number1, int number2) {
//        super(30);
        this();
        this.number1 = number1;
        this.number2 = number2;
    }

    public void show(){
        //子类属性和父类属性名相同,
        //子类访问默认是访问自己的属性
        System.out.println(number1);
        System.out.println(number2);
        System.out.println(number3);
        //子类没有number5,访问的是父类的
        System.out.println(number5);
        //通过super访问父类和子类同名的属性
        System.out.println(super.number1);
        System.out.println(super.number2);
        System.out.println(super.number3);
        //子类无法直接访问父类的私有属性
        //System.out.println(super.number4);
    }

   @Override
    public void say(){
//        super.say();
        System.out.println("这是子类的say方法");
        super.hello(); //这个访问的是父类的hello方法
       hello(); //这里访问的是自己类中的hello方法
    }

    @Override
    public void hello(){
        System.out.println("这是子类的hello方法");
    }
}


package com.iweb.day6.test.extend2;
//测试类
public class Test {
    public static void main(String[] args) {
        Son son = new Son();
        Son son1 = new Son(10);
        Son son2 = new Son(10,20);
//        System.out.println(son.number1);
//        System.out.println(son.number2);
//        System.out.println(son.number3);
       // son.show();

       // son.say();

        //son.hello();
    }
}


继承总结

继承关系中,被继承的类,称为父类、超类、基类,继承的类称为子类。

一个类如果没有显式的继承,默认继承object类,object类是所有父类的创始类。

类和类之间只能单继承,可以多重继承。

子类可以直接继承父类非私有属性、方法,私有属性可以间接使用,不能直接访问。

子类可以扩展父类信息,可以拥有自己的属性和方法。

继承可以提升代码复用性。

super关键字的用法

1.在子类中调用父类的属性,不能调用私有的 super.属性名

2.在子类中调用父类的方法 super.方法名()

3.在子类中调用父类的构造方法,使用super调用构造方法,必须写在构造方法中的第一行

调用无参 super();

调用有参 super(参数);

在构造方法中super是否可以与this同时出现?

不可以,super和this在构造方法中,都只能在第一行出现。

面试题:this和super的区别

1.super表示父类的对象,this表示当前类的对象

2.super在子类调用,this在当前类调用

方法重写

在继承关系中,子类重写父类的方法

规范是:

方法名相同、参数列表相同、返回值类型相同或者是父类方法返回值的子类,访问修饰符不能严于父类,不能抛出比父类方法更多的异常。

方法重写的特点

1.子类不能将父类的非静态方法,重写为静态的方法

2.子类不能重写父类的静态方法,但是可以定义和父类同名的静态方法,这个方法就是子类自己的静态方法

package com.day9.extend3;

public class Father {

    //测试返回值类型
    public Father show(String name){
        System.out.println("show"+name);
        return new Father();
    }
    protected void test(){
        System.out.println("父类的test");
    }
    //父类的静态方法
    public static void method01(){
        System.out.println("父类的静态方法method01");
    }

    public void method02(){
        System.out.println("父类的私有方法method02");
    }
}
package com.day9.extend3;

public class Son extends Father{

    //@Override注解,用来检查重写的方法是否符合规范
    @Override
    public Son show(String name){
        System.out.println("这是子类重写父类的方法");
        System.out.println("show"+name);
        return new Son();
    }
    @Override
    public void test(){
        System.out.println("子类的test");
    }
    //子类不能重写父类的静态方法,但是可以定义和父类同名的静态方法
    //这个方法是子类自己的静态方法
    public static void method01(){
        System.out.println("子类的method01方法");
    }
    //父类的私有方法无法重写
    public void method02(){
        System.out.println("子类私有的方法method02");
    }

}

方法重写的应用

重写equals()方法

package com.day9.extend3;

import java.util.Objects;

public class Student {
    private String sid;
    private String name;
    private int age;
    private String className;

    public Student() {
    }

    public Student(String sid, String name, int age, String className) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.className = className;
    }

    //重写equals()方法
    //自己写的
    //instanceof 是 Java 的保留关键字。
    //它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
   /* @Override
    public boolean equals(Object o) {
        //如果比较的对象地址相同,表示他们就是同一个对象
        if (this == o) {
            return true;
        }
        //如果传进来的参数是一个学生对象
        if (o instanceof Student) {
            //将传进来的参数,转回Student,因为参数是Object类型
            //这个时候不能直接使用,转回Student之后才能使用
            Student obj = (Student) o;

            if (this.sid.equals(obj.sid) &&
                    this.name.equals(obj.name) &&
                    this.age == obj.age &&
                    this.className.equals(obj.className)){
                return true;
            }else {
                return false;
            }
        }
        return false;
    }

    */
    //系统生成的
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        if (sid != null ? !sid.equals(student.sid) : student.sid != null)
            return false;
        if (name != null ? !name.equals(student.name) : student.name != null)
            return false;
        return className != null ? className.equals(student.className) : student.className == null;
    }

    @Override
    public int hashCode() {
        int result = sid != null ? sid.hashCode() : 0;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + age;
        result = 31 * result + (className != null ? className.hashCode() : 0);
        return result;
    }
}
package com.day9.extend3;

public class StudentTest {
    public static void main(String[] args) {
        //创建学生对象
        Student s1 = new Student("1001","jack",20,"1");
        Student s2 = new Student("1001","jack",20,"1");
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s1==s2);//false

        //系统类Object,有一个equals()方法,用来比较对象是否相同
        //任何类,都会继承这个方法
        //Object类中的,底层默认还是 ==
        //如果类中想要调用equals()方法,去比较对象是否相同,必须要先重写equals()方法
        System.out.println(s1.equals(s2));//false  //其实还是 return s1==s2
                                          //重写equals()方法后,返回true
        String s3="hello";
        String s4=new String("hello");
        System.out.println(s3.equals(s4));//true

    }
}
面试题:== 和 equals() 的区别

1. ==是用来比较基本类型值是否相同,引用类型的地址值是否相同

2. equals() 是Object类中的一个方法,用来比较引用类型是否是同一个对象的,但是Object类中的equals()方法底层还是==

所以,如果想要实现比较功能,将来在类中要重写equals()方法。

面试题:为什么重写equals()方法的时候同时要重写hashCode()方法

1. 一般的话,同一个对象的hashCode()值也是相同的

2. 如果只重写方法,而不重写hashCode()方法,会出现对象相同,但是hashCode()值不同的情况,这种情况一般是不允许的。

toString()方法

Object类中的一个方法,用来返回对象的字符串表示

如果直接将一个对象输出的话,默认就会以此对象,调用toString()方法,

如果没有重写toString()方法的话,对象就默认调用Object类中的toString()方法,

Object类中的toString()方法,返回的内容是

return getClass().getName() + "@" + Integer.toHexString(hashCode());

当前类的全路径名 + "@" + 对象的hashCode()值的16进制表示

重写之后,对象会调用自己类中重写的toString()方法

abstract关键字

抽象类

概念

在类的继承关系中,继承把共性的东西做了抽取,有的时候,抽取的方法相同,但是每个子类中的方法的具体实现(功能)是不一样的, 这个时候,方法相同,但是实现不同,父类中的方法就不能给出具体的实现。这种没有实现的方法,就称为抽象方法。

如果一个类中,有抽象方法,那么该类就必须要定义为抽象类

写法:通过abstract关键字修饰

public abstract class{

public abstract void method();

}

抽象类的特点

1.抽象类中可以没有抽象方法,如果有抽象方法的类,一定是抽象类

2.抽象类不可以实例化

3.抽象类子类,要么子类也是抽象类,要么子类重写父类中的所有抽象方法

抽象类中的成员特点

1.成员变量

可以有变量,也可以有常量

2.构造方法

可以有构造方法,构造方法不用来实例化,一般用来给子类访问

3.成员方法

可以有抽象方法,也可以有普通方法、静态方法

普通方法和静态方法的使用,可以和普通类的用法一样。

package com.day9.abstract1;

public abstract class Animal {
    private String name;
    private int age;

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

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

    //普通方法
    //抽象方法,写法就是普通方法上,加上abstract,并且去掉方法的大括号
    public abstract void eat();

    //抽象类中除了抽象方法,其他的方法用法跟普通类中的继承的用法没区别

    //抽象类中,可以定义普通方法
    public void show(){
        System.out.println("父类的普通方法");
    }

    //抽象类中的静态方法,可以被类名直接调用访问,
    //也可以被子类继承后,子类名访问
    public static void hello(){
        System.out.println("父类的静态方法");
    }
}
package com.day9.abstract1;
//子类继承抽象父类
//1.要么子类也是抽象类
//2.要么子类重写父类中的所有抽象方法
public class Dog extends Animal{
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }
}
package com.day9.abstract1;

public class Test {
    public static void main(String[] args) {
        //创建对象
        Dog dog = new Dog();
        dog.eat();
        dog.show();
        dog.hello();

        Animal.hello();
        Dog.hello();

        //创建Animal对象
        //抽象类不能被实例化
        //Animal animal = new Animal();
    }
}
  • 22
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值