Java中的继承以及接口的基础

记录自己的Java学习过程

        从类的继承开始

目录

记录自己的Java学习过程

一、继承

1.修饰词与继承的关系

 1.public

 2.protected 

 3.default

 2. 继承与构造函数 

  3.继承与静态区函数

4.super的用法

 5.组合

二、多态

三、抽象类

四、接口

1.接口的一般使用方法

2.接口与类的继承同时使用 

 3.接口之间的调用(继承)

4.一些常用的内置接口实例

Cloneable 接口

Comparator 接口

五、Object类


一、继承

1.修饰词与继承的关系

 

*图片来自哔站比特博哥

    现在inherit包中创建一个Animal类

package inherit;
public class Animal {
    public String name;
    public int age;

    String def;        //默认为default类型
    protected String pro;
}
 1.public

 在不同包不同类之间都可以直接引用

//public在不同包不同类都可以使用(任何地方)
  Animal animal = new Animal();
  animal.name = "public";
  animal.age = 3;
  animal.eat();
 2.protected 

 在同一个包中的不同类可以使用

 也可以在不同包中的子类中使用(不同包的非子类不可以引用)

 在同一个包中可以随便调用

package inherit; 
public class Test2 {
    public static void main(String[] args) {
        //protect在同一个包中的不同类也可以使用
        Animal animal1 = new Animal();
        animal1.pro = "Protected";
    }
}

 在不同包中只能通过继承才能调用

package combination;
import inherit.Animal;
//也可以在不同包中的子类使用
public class Test1 extends Animal{
    public void main(String[] args) {
        this.pro = "aaa";
    }
 3.default

在同一个包中的不同类引用(不同包之间不可以引用)

package inherit;
public class Test2 {
    public static void main(String[] args) {
        //default在同一个包中的不同类可以使用
        Animal animal2 = new Animal();
        animal1.def = "default";
    }
}

 2. 继承与构造函数 

父类存在有参或无参构造时,子类必须也进行有参或无参构造,子类在构造时,可以用super对父类进行构造,再对自己独有的成员进行构造。

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

    //父类存在有参构造,想使用无参构造时必须进行无参构造
    public Animal() {

    }
    
    //父类有参构造
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

class Dog extends Animal {
    public boolean silly;

    //父类存在有参构造,想使用无参构造时必须进行无参构造
    public Dog() {
        super();
    }
    
    //如果父类存在带参构造,子类必须进行有参构造
    public Dog(String name, int age, boolean silly) {
        super(name, age);//先对父类进行构造
        this.silly = silly;//在对子类进行构造
    }
}

  3.继承与静态区函数

静态区的方法在实例化时自动执行,先执行父类,再执行子类,且只能执行一次

public class Animal {
    public String name;
    public int age;
      
    //父类静态区
    static{
        System.out.println("Animal::static");
    }
}

class Dog extends Animal {
    public boolean silly;

    //子类静态区
    static {
        System.out.println("Dog::static");
    }
}

public class Test2 {
    public static void main(String[] args) {
        Dog dog = new Dog();//先调用Dog,会先执行父类的static区的方法,再执行子类static区的方法
        Dog dog1 = new Dog();//但静态区的都只会执行一次,在第二次调用的时候就不执行了
    }
}

 运行结果: 

Animal::static
Dog::static

4.super的用法

 super会达到易读的效果
 this会访问子类以及父类的变量(从子类到父类寻找)
 super会直接从父类中寻找

super用法
 1. super.data   访问父类的普通成员变量
 2. super.func() 调用父类的普通成员方法
 3. super()      调用父类的构造方法

      注意:只能调用非静态成员,静态成员必须用类名访问

class Base {
    int a;
    public void methodBase() {
        System.out.println("methodBase");
    }
}

class Child extends Base {
    int a;
    
    public void func1() {
        a = 10;//在子类和父类有同名的变量时会先访问子类的
        super.a = 100;//利用super直接访问父类的成员
        System.out.println(a);//10
        System.out.println(super.a);//super可以直接访问父类的变量 100
        
    }

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

    public void methodBase() {
        System.out.println("Child_methodBase");

    }

    public void func2() {
        methodChild();//现在子类中找,找到就引用
        methodBase();//子类中存在找,直接引用
        super.methodBase();//利用super来直接访问父类的对象
    }
}

 5.组合

 组合就是同一个类面实例化其他类,相比于继承更加常用

与C语言的结构体类似

class Student {

}

class Teacher {

}


//以后使用组合的情况比较多
class School {
    public Student[] students;
    public Teacher[] teachers;

}

二、多态

重写

快捷键 Alt + Insert   ->    ctrl + o


1.private修饰的方法不能重写
2.static修饰的方法不能重写
3.final(密封)修饰的方法不能重写
4.返回值可以不同,但必须有父子关系
5. 重写的参数列表相同

创建一个Animal父类 ,有一个eat的成员函数

class Animal {
    public String name;
    public int age;

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

 再定义两个子类,Dog类和Cat类

class Dog extends Animal {
    //Dog类独有的狗叫
    public void bark() {
        System.out.println(name + "在叫");
    }

    //有特殊需求需要重写
    //原理  子类访问的优先权大于父类
    //对eat进行重写
    public void eat() {
        System.out.println(name + "吃骨头");
    }

}

class Cat extends Animal {
    //Cat类独有的抓老鼠
    public void catchMouse() {
        System.out.println(name + "在抓老鼠");
    }
    //有特殊需求需要重写
    //对eat进行重写
    public void eat() {
        System.out.println(name + "在吃鱼");
    }
}

 发生多态

public class Main {
    public static void function(Animal animal) {
        //接收父类对象,可以是子类类型
        //方法传参时可以进行向上转型
        animal.eat();
        //判断传入进来的animal具体是Dog还是Cat,然后强转后可以调用他们独有的方法
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            dog.bark();
        } else {
            Cat cat = (Cat) animal;
            cat.catchMouse();
        }
    }

    public static void main(String[] args) {
        //向下转型
        Animal dog = new Dog();
        dog.name = "狗";
        
        Animal cat = new Cat();
        cat.name = "咪咪";
        function(dog);//传入父类类型的子类对象
        function(cat);
    }
}

三、抽象类

1. 抽象类,使用abstract修饰类

2. 抽象类当中,可以包含普通类所能包含的成员

3. 抽象类和普通类不一样的是,抽象类当中可以包含抽象方法

4. 抽象方法是使用abstract修饰的,这个方法,没有具体实现

5. 不能实例化抽象类

6. 抽象类存在的最大意义就是被继承

7. 如果一个普通类继承了一个抽象类,必须重写所有抽象类的方法

8. 如果一个抽象类A继承了一个抽象类B,此时A当中 不需要重写B中的抽象方法,但如果A再被普通类继承,就必须重写A和B的所有抽象类

9. 抽象方法不能是私有的,static? 也就是要满足重写的规则

10.final不可以,他和abstract是矛盾的

11.抽象类当中 可以有构造的方法,为了方便子类能够调用,来初始化抽象类的成员

1.创建抽象类 (使用abstract修饰整个类,该抽象类的内部成员不能被实例化) 

abstract class Shape {
    //抽象方法,不用实现
    public int a;
    //抽象函数也可有public的构造方法,但不能有abstract修饰
    public Shape(int a) {
        this.a = a;
    }

    public abstract void draw();

    public abstract void eat();

    //抽象类的普通方法可以实现,并可以被继承,不必要进行重写,必要时也可以重写
    public void func() {
        System.out.println("func()");
    }
}

 2.创建第二个抽象类,继承第一个抽象类(抽象类之间可以有继承关系)

abstract class Shape2 extends Shape {
    public int b;
    
    //对父类以及自己进行构造
    public Shape2(int a, int b) {
        super(a);
        this.b = b;
    }
    public abstract void brak();
}

 3.创建一个普通类来继承抽象类(继承抽象子类)

//如果继承了抽象子类,那么孙子必须重写父类和子类的所有抽象方法
class Rect2 extends Shape2 {
    //对父类都进行构造
    public Rect2(int a,int b) {
        super(a,b);
    }
    
    //对父亲的重写
    public void brak() {
        System.out.println("Rect2::brak()");
    }
    //对爷爷的重写
    public void draw() {
        System.out.println("Rect2::draw()");
    }
    //对爷爷的重写
    public void eat() {
        System.out.println("Rect2::eat()");
    }
}

进行实例化

public class Main {
    public static void main(String[] args) {
        Rect2 rect2 = new Rect2(10, 20);
        rect2.func();
        rect2.brak();
        rect2.eat();
        rect2.draw();
    }
}

 运行结果如下

func()
Rect2::brak()
Rect2::eat()
Rect2::draw()

四、接口

1.使用interface来修饰接口 2.接口当中的成员方法,不能有具体的实现

        a.抽象方法:默认是public abstract的方法

        b.允许可以使用可实现的方法,但这种只能是由default修饰的

        c.可以实现有一个静态方法(只能通过接口名称进行调用)

3.成员变量默认是由public static final 修饰的

4.接口不能被实例化

5.类和接口是利用implements来链接,可以一个类对应多个接口,用逗号来隔开

6.接口名称一般以I开头,接一个形容词

7.子类重写抽象方法时必须是public修饰

8.接口中不能有静态代码块和构造方法

9.如果类没有实现接口中所有的抽象方法,则类必须设置为抽象类

10.接口可以解决Java中不能多继承的问题 java不能继承多个类,但可以继承多个接口

1.接口的一般使用方法

定义两个接口

//第一个接口
interface IShape {
    int size = 10;
    //即public static final int size = 10;
    
    //抽象的方法
    void draw();

    //具体实现的方法必须有default修饰
    default public void func() {
        System.out.println("default方法");
    }
    
    静态方法可以实现(只能通过接口名称进行调用)
    public static void func2() {
        System.out.println("public static");
    }
}

//第二个接口
interface IShape2 {
    void draw2();

}

 一个普通类可以调用多个接口(可以解决Java中不能多继承的问题)

class Person implements IShape,IShape2 {
    //默认的方法必须重写,即抽象方法必须重写
    //且必须重写所有接口的抽象方法
    public void draw() {
        System.out.println("Person 的重写");
    }
    public void draw2() {
        System.out.println("重写draw2()");
    }

 在实例化时可以做到多个普通类同时调用同一个接口,此时会发生多态,可以使用与继承相同的方法,如下

//新定义一个普通类来调用 第一个接口
class Student implements IShape {
    public void draw() {
        System.out.println("Student 的重写");
    }
}


public class Main {

    //接口也可以向下转型
    public static void drawMap(IShape iShape) {

        //使用此方法也可以单独的调用子类独有的方法
        if (iShape instanceof Person) {
            Person person = (Person) iShape; //和继承中的强转是一个道理
            person.draw();
        } else {
            Student student = (Student) iShape;
            student.draw();
        }
    }

    public static void main(String[] args) {
        //定义一个Person类
        Person person = new Person();
      //这里也可以使用
      //IShape person = new Person();
        drawMap(person);   //输出Person 的重写
        
        //定义一个Student类
        Student student = new Student();
      //这里也可以使用
      //IShape student = new Student();
        drawMap(student);  //输出Student 的重写

        IShape.func2();  //通过接口名来调用接口的静态方法
    }

}

2.接口与类的继承同时使用 

接口可以解决Java中不能多继承的问题(即在继承父类的同时可以调用其他多个接口)

java不能继承多个类,但可以继承多个接口

 定义两个接口和一个父类

//游泳的接口
interface ISwimming {
    void swimming();
}

//飞的接口
interface IFly {
    void fly();
}

//抽象Animal类
abstract class Animal {
    public String name;

    public Animal() {
    }
    public Animal(String name) {
        this.name = name;
    }
    public abstract void eat();
}

 再定义两个子类Dog和Bird都继承父类Animal,并让他们分别调用ISwimming接口和Ifly接口

这样可以分别使Dog类调用ISwimming接口,Bird类调用IFly接口

class Dog extends Animal implements ISwimming {
    public Dog() {
    }

    public Dog(String name) {
        this.name = name;
    }

    //重写父类
    public void eat() {
        System.out.println(name + "在吃狗粮");
    }
    //重写接口
    public void swimming() {
        System.out.println(name + "在游泳");
    }
}

class Dog extends Animal implements ISwimming {
    public Dog() {
    }

    public Dog(String name) {
        this.name = name;
    }

    //重写父类
    public void eat() {
        System.out.println(name + "在吃狗粮");
    }
    //重写接口
    public void swimming() {
        System.out.println(name + "在游泳");
    }
}

class Bird extends Animal implements IFly {
    public Bird() {
    }

    public Bird(String name) {
        this.name = name;
    }

    //重写父类
    public void eat() {
        System.out.println(name + "在吃虫子");
    }
    //重写接口
    public void fly() {
        System.out.println(name + "在飞");
    }
}

进行实例化

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog("狗");
        dog.eat();
        dog.swimming();

        Bird bird = new Bird("鸟");
        bird.eat();
        bird.fly();
    }
}

 运行结果如下

狗在吃狗粮
狗在游泳
鸟在吃虫子
鸟在飞

 3.接口之间的调用(继承)

接口之间是可以调用的,普通类在调用辈分最小的接口时需要重写族内所有的方法

interface A {
    void func1();
}

interface B {
    void func2();
}

//接口之间可以继承,甚至多继承
interface C extends A, B {
    void func3();
}

//若调用一个子类接口,则必须将所有父类以及该接口的方法都重写
class Perosn1 implements C{
    @Override
    public void func3() {

    }

    @Override
    public void func1() {

    }

    @Override
    public void func2() {

    }
}

4.一些常用的内置接口实例

Cloneable 接口

import java.util.Comparator;

 类可以通过调用Java内置的Cloneable接口来重写clone方法进行对类变量的拷贝

可以实现浅拷贝或深拷贝

class Money {
    public int m;
    public Money(int m) {
        this.m = m;
    }
}

//Cloneable 可克隆的
class Student3 implements Cloneable{
    public String name;
    public int age;
    public Money money;

    public Student3(String name, int age) {
        this.name = name;
        this.age = age;
        money = new Money(10);
    }

    //相当于重写父类的克隆,再返回父类的克隆 
    //(因为是protected修饰,在不同包之间不可以直接引用,所以需要重写
    //直接通过ctrl + Insert  ->  ctrl + o 进行添加
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //浅拷贝(直接返回父类的clone方法,进行浅拷贝)
        //return super.clone();

        //深拷贝(对于自己需要创建新空间的成员变量需要强制转换父类返回的clone值并对其开辟新空间
        Student3 temp = (Student3) super.clone();
        temp.money = new Money(temp.money.m);
        return temp;
    }

    @Override
    public String toString() {
        return "Student3{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", money=" + money.m +
                '}';
    }
}

 实例化

public class Test3 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student3 student3 = new Student3("zhiasd", 10);
        
        //因为clone返回的是object类型,所以需要强制转换一下
        Student3 student31 = (Student3) student3.clone();
        student31.money.m = 100000; //若是浅拷贝,则两个students对象的money指向的m是同一个
                                    //深拷贝之后就会指向不同空间
        System.out.println(student3);
        System.out.println(student31);
    }
}

运行结果

 [Student3{name='aasd', age=5, money=10},

Student3{name='basd', age=10, money=10},

Student3{name='bfsd', age=7, money=10}]

Comparator 接口

Comparator 接口 用类创建比较器,对类数组进行排序

import java.util.Comparator;

 创建两个类,都调用Comparator接口

//比较器 (比较年龄大小)按年龄排序 //Comparator对类的侵入性比较弱
class AgeComparator implements Comparator<Student3> {
    @Override
    public int compare(Student3 o1, Student3 o2) {
        return o1.age - o2.age;
    }
}

//比较器(比较名字大小)按姓名排序
class NameComparator implements Comparator<Student3> {
    @Override
    public int compare(Student3 o1, Student3 o2) {
        return o1.name.compareTo(o2.name);//compareTo是String中自带的类,返回一个整数
    }
}

 利用连个比较类进行排序

public class Test3 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student3[] student3s = new Student3[3];
        student3s[0] = new Student3("basd",10);
        student3s[1] = new Student3("aasd", 5);
        student3s[2] = new Student3("bfsd", 7);
        AgeComparator ageComparator = new AgeComparator();//以年龄进行排序
        NameComparator nameComparator = new NameComparator();//以姓名排序
        
        //必须传入实例化之后的比较类变量
        //按年龄排序
        Arrays.sort(student3s, ageComparator);
        System.out.println(Arrays.toString(student3s));
        
        //按姓名排序
        Arrays.sort(student3s, nameComparator);
        System.out.println(Arrays.toString(student3s));
    }
}

 运行结果

[Student3{name='aasd', age=5, money=10},

Student3{name='bfsd', age=7, money=10},

Student3{name='basd', age=10, money=10}]


[Student3{name='aasd', age=5, money=10},

Student3{name='basd', age=10, money=10},

Student3{name='bfsd', age=7, money=10}]

五、Object类

Object类即所有类的父类

class Person {
    public void func1() {

    }
}

class Student {
    public void func2() {
        
    }
}

public class Main {
    //默认接收所有类的父类
    public static void Test(Object object) {
        if (object instanceof Person) {
            Person person = (Person) object;
            person.func1();
        } else {
            Student student = (Student) object;
            student.func2();
        }
    }

    public static void main(String[] args) {
        Test(new Person());//可以传入匿名对象
        Test(new Student());
    }
}

  • 28
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值