Java基础学习-抽象类和接口

抽象类

在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果
一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类
。 比如:动物类,形状类

  1. 抽象类 使用abstract修饰类。
  2. 抽象类当中,可以包含普通类所能包含的成员。
  3. 抽象类当中可以包含抽象方法,也可以不包含。
  4. 抽象方法是使用abstract修饰的,这个方法没有具体的实现
  5. 抽象类不能实例化
  6. 抽象类存在的最大意义:被继承
  7. 如果一个普通类继承了抽象类,必须重写抽象方法。
  8. 如果一个抽象类A继承一个抽象类B,此时A当中不需要重写B当中的抽象方法,但如果A在被普通类继承 ,就需要重写抽象方法。(如果想要类B继承抽象类A,但是又不想在类B中重写A中的抽象类方法,方法:将类B写成抽象类)
  9. 抽象方法不能是私有的【抽象方法的出现就是为了重写】。要满足重写的规则。
  10. finalabstract矛盾的,不可共存 ;staticabstract是矛盾的,不可共存 。【抽象方法不能被finalstatic修饰,因为抽象方法要被子类重写,而final修饰的类称为静态类,不能被继承】
  11. 抽象类当中可以有构造方法,为了方便子类能够调用,来初始化抽象类当中的成员。【因为抽象类不能实例化,提供了构造方法后,相当于子类可以帮助抽象类来初始化成员。

比如说,我们定义了一个Shape类,用来描述形状,
定义了三角形类,正方形类等形状,让他们继承shape类,重新抽象类的抽象方法,也就是draw()方法。

abstract class Shape {
    public static int a = 10;
//    抽象方法,没有具体的实现
    public abstract void draw();
    public void func() {
    }
}

class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("this is a rect.");
    }
}

接 口

接口概念

在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。

接口规则

  • 1.使用 interface 修饰接口
  • 2.接口当中的成员方法不能有具体的实现 默认都是【public】
    1. 抽象方法:默认是public abstract,这个写不写都一样
    1. JDK1.8开始,允许有可以实现的方法,但是只能由default修饰
    1. 可以实现静态方法
  • 3.成员变量默认是public static final修饰,写不写都一样
  • 4.接口不能被实例化
  • 5.类和接口之间采用implements实现多个接口
  • 6.子类重写抽象方法,必须使用public
  • 7.接口中不能含有静态代码块和构造方法。
  • 8.如果一个类继承了接口,但是不想实现接口的方法,那么就把这个类定义为抽象类。但是如果这个类被其他类继承,那么必须重写。
  • 9.一个类可以实现多个接口。使用implements,用逗号隔开。【可以解决多继承问题】

接口使用

提示:

  1. 创建接口时,接口的命名一般以大写字母I开头
  2. 接口命名一般使用形容词的词性
  3. 阿里编程规范中约定,接口中的方法和属性不要加任何的修饰符号【不用写public abstract】,保持代码的简洁。
  4. 一个接口就回生成一个.class文件。
  5. 一般一个接口写在一个Java文件中。
// USB接口
interface USB {
    void openDevice();
    void closeDevice();
}

// 鼠标类,实现USB接口
class Mouse implements USB {

    @Override
    public void openDevice() {                                                                                                                                            
        System.out.println("打开鼠标");
    }

    @Override
    public void closeDevice() {
        System.out.println("关闭鼠标");
    }

    public void click(){
        System.out.println("鼠标点击");
    }
}


// 键盘类,实现USB接口
class KeyBoard implements USB {

    @Override
    public void openDevice() {
        System.out.println("打开键盘");
    }

    @Override
    public void closeDevice() {
        System.out.println("关闭键盘");
    }

    public void inPut(){
        System.out.println("键盘输入");
    }
}


// 笔记本类:使用USB设备
class Computer {

    public void powerOn(){
        System.out.println("打开笔记本电脑");
    }

    public void powerOff(){
        System.out.println("关闭笔记本电脑");
    }

    public void useDevice(USB usb){
        usb.openDevice();
        if(usb instanceof Mouse){
            Mouse mouse = (Mouse)usb;   //强制类型转换 (向下转型
            mouse.click();              //方法只在子类中定义,通过usb无法调用它
        }else if(usb instanceof KeyBoard){
            KeyBoard keyBoard = (KeyBoard)usb;
            keyBoard.inPut();
        }
        usb.closeDevice();
    }
}

public class test3 {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.powerOn();
        computer.useDevice(new KeyBoard());
        computer.useDevice(new Mouse());
        computer.powerOff();
    }
}

接口特性

  1. 接口类型是一种引用类型,但是不能直接new接口的对象
USB usb = new USB();   //错误 
  1. 接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是
    public abstract,其他修饰符都会报错,public abstract可不写,因为默认就是public abstract
interface USB {
    void openDevice();
    public abstract void closeDevice();
}
  1. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现。
interface USB {
    void openDevice();
    // 编译失败:因为接口中的方法默认为抽象方法
    //接口中的抽象方法不能带有主体
    void closeDevice() {
        System.out.println("test");
    }
}
  1. 重写接口中的方法的时候,不能使用默认的访问修饰权限。
interface USB {
    void openDevice();
    // 编译失败:因为接口中的方法默认为抽象方法
    //接口中的抽象方法不能带有主体
    void closeDevice();
}

class Mouse implements USB {
    @Override
    public void openDevice() {    //重写时, 需要加上public,子类的访问修饰权限必须大于等于父类是权限
        System.out.println("打开鼠标");
    }
}
  1. 接口中可以含有变量,会被隐式的指定为public static final
  2. 接口中不能含有静态代码块和构造方法。【因为接口本身不能被实例化】
  3. 接口虽然不是类,但是接口编译完成后的字节码文件后缀格式也是.class
  4. 如果类没有实现接口中的所有抽象方法,则类必须设置为抽象类
  5. jdk8中,接口可以包含default方法。

实现多个接口

一个类可以实现多个接口。使用implements,用逗号隔开。【可以解决多继承问题】
Java当中只能继承一个类,但是可以实现多个接口。接口解决了Java中无法多继承的问题。

interface A1 {
    void func1();
}
interface B1 {
    void func2();
}
interface D extends A1,B1 {   //接口D继承了接口A1,B1 【接口和接口之间】 把多个接口合并在一起,扩展的意思。
    void func3();
}


class E implements A1,B1 {

    @Override
    public void func1() {
        
    }

    @Override
    public void func2() {

    }
}


class C implements D {    //类 C 使用接口 D   【类和接口之间】

    @Override
    public void func1() {

    }

    @Override
    public void func2() {

    }

    @Override
    public void func3() {

    }
}

class Animal {
    public String name;
    public int age;
    public void eat() {
        System.out.println("eat");
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
//这里没有写成类的原因是,Java当中只能继承一个类,不能继承多个类。  但是可以实现多个接口。接口解决了Java无法多继承的问题
interface IFlying {
    void flying();
}

interface ISwimming {
    void swimming();
}

interface IRunning{
    void running();
}


class Dog extends Animal implements IRunning,ISwimming{

    public Dog(String name, int age) {    //构造方法
        super(name, age);
    }

    @Override
    public void swimming() {
        System.out.println(name + "dog swimming");
    }

    @Override
    public void running() {
        System.out.println(name + "dog running");
    }

    @Override
    public void eat() {
        System.out.println(name + "eat dog food");
    }
}

class Bird extends Animal implements IFlying {   //一定是先继承再实现
    public Bird(String name, int age) {
        super(name, age);
    }

    @Override
    public void flying() {

    }
}


class Duck extends Animal implements IFlying,ISwimming,IRunning {

    public Duck(String name, int age) {
        super(name, age);
    }

    @Override
    public void flying() {
        System.out.println(name + "Duck flying");
    }

    @Override
    public void swimming() {
        System.out.println(name + "Duck swimming");
    }

    @Override
    public void running() {
        System.out.println(name + "Duck running");

    }
    @Override
    public void eat() {
        System.out.println(name + "eat Duck food");
    }
}


public class Test5 {

    //有了接口之后就不关注类型了,只要实现了接口 重写了方法,表现的行为就不一样了,就可以实现多态
    public static void walk(IRunning iRunning) {   //多态
        iRunning.running();
    }

    public static void func(Animal animal){
        animal.eat();
    }

    public static void main(String[] args) {
        walk(new Dog("aa",12));
        walk(new Duck("bb",34));

        func(new Dog("aa",12));
        func(new Duck("bb",34));

    }
}

接口间的继承

interface A1 {
    void func1();
}
interface B1 {
    void func2();
}
interface D extends A1,B1 {   //接口D继承了接口A1,B1
    void func3();
}

抽象类和接口的区别

抽象类和接口都是Java中多态的常见使用方式。
核心区别:抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不用重写),而接口中不能包含普通方法,子类必须重写所有的抽象方法。

区别抽象类接口
结构组成普通类+抽象方法抽象方法+全局变量
权限各种权限public
子类使用使用extends关键字继承抽象类使用implements关键字实现抽象类
关系一个抽象类可以实现若干接口接口不能继承抽象类,但是接口可以使用extends关键字继承多个父接口
子类限制一个子类只能继承一个抽象类一个子类可以实现多个接口

Object类

Object类是所有类的父类,我们自己写的类,就算没有显示extends Object ,都是默认继承的

equals

Object 的equals判断的是引用变量的里面存储的地址是否一样。如果有自己的判断相等的规则,需要重写equals方法。
如果写了自定义类型,要注意重写equals方法。
在这里插入图片描述

import java.util.Objects;

class Person{
    private String name ;
    private int age ;
    public Person(String name, int age) {
        this.age = age ;
        this.name = name ;
    }

    //重写equals方法
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if(this == obj) {
            return true ;
        }
        // 不是Person类对象
        if (!(obj instanceof Person)) {
            return false ;
        }


        Person person = (Person) obj ; // 向下转型,比较属性值
        if (this.name.equals(person.name) && this.age==person.age) {
            return true;
        } else {
            return false;
        }
    }
}

public class Test6 {
    public static void main(String[] args) {
        Person p1 = new Person("gaobo", 20) ;
        Person p2 = new Person("gaobo", 20) ;
        int a = 10;
        int b = 10;
        System.out.println(a == b); // 输出true
        System.out.println(p1 == p2); // 输出false
        System.out.println(p1);
        System.out.println(p2);
        System.out.println(p1.equals(p2)); // 输出false
    }
}

hashcode

重写hashcode

import java.util.Objects;

class Person{
    private String name ;
    private int age ;
    public Person(String name, int age) {
        this.age = age ;
        this.name = name ;
    }

    @Override
    public int hashCode() {
    //计算对象的位置
        return Objects.hash(name, age);
    }


}
public class Test6 {
    public static void main(String[] args) {
        Person p1 = new Person("gaobo", 20) ;
        Person p2 = new Person("gaobo", 20) ;

        //重写hashcode前,不在同一个地方
        //重写后,一样的数据生成的hashcode一样
        System.out.println(p1.hashCode());
        System.out.println(p2.hashCode());
    }
}


comparable接口

对类的侵入性很强,定好了以后的比较都是用什么比较
总结:如果以后自定义的类型,一定要记得,如果要比较大小,必须要让这个类具备可以比较的功能。此时可以选择实现接口comparable

import java.util.Arrays;

class Student implements Comparable<Student>{
    public String name;
    public int age;

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

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public int compareTo(Student o) {  //谁调用compareTo方法谁就是this,
        if(this.age - o.age > 0)  {
            return 1;
        } else if (this.age - o.age < 0){
            return -1;
        } else {
            return 0;
        }
    }

}

public class Test {
    //总结:如果以后自定义的类型,一定要记得,如果要比较大小,必须要让这个类具备可以比较的功能。
    //此时可以选择实现接口comparable

    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("bit1",8);
        students[1] = new Student("bit2",2);
        students[2] = new Student("bit3",3);

        //如何对学生类数组进行排序  向平常数组一样Arrays.sort(students) 是不可行的
        //自定义的学生类,需要具备可以比较的功能【让student类实现一个Comparable接口,重写compareTo方法】
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));

        Student student1 = new Student("a", 11);
        Student student2 = new Student("b", 12);
        if(student1.compareTo(student2) > 0) {    //谁调用compareTo方法谁就是this,在这里student1就是this,student2就是o
            System.out.println("student1 > student2");
        }
        if(student1.compareTo(student2) < 0) {
            System.out.println("student1 < student2");
        }

    }
}

Comparator接口

对类的侵入性较弱

import java.util.Arrays;
import java.util.Comparator;

class Student {
    public String name;
    public int age;

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

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}


//TODO:Comparator接口当中,不止一个抽象方法,那为什么只需要实现一个compare方法就好了
class AgeComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}

class NameComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}

public class Test {

	public static void main(String[] args) {
        Student student1 = new Student("a", 11);
        Student student2 = new Student("b", 12);
        AgeComparator ageComparator = new AgeComparator();
        if(ageComparator.compare(student1, student2) > 0) {
            System.out.println("student1 > student2");
        }
        if(ageComparator.compare(student1, student2) < 0) {
            System.out.println("student1 < student2");
        }
    }

    public static void main1(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("bit1",8);
        students[1] = new Student("bit2",2);
        students[2] = new Student("bit3",3);

//        AgeComparator ageComparator = new AgeComparator();
//        Arrays.sort(students, ageComparator);   //ageComparator的第二个参数是实现了Comparator接口的类的对象
//        System.out.println(Arrays.toString(students));

        NameComparator nameComparator = new NameComparator();
        Arrays.sort(students, nameComparator);
        System.out.println(Arrays.toString(students));
    }
}

compareTo 和equals

equals返回布尔类型,判断是否相等。
compareTo返回整数,比较的是两个之间的大小关系。

clone

一个对象 想被克隆:

  1. 添加克隆接口
class Person implements Cloneable
  1. 重写克隆方法
  protected Object clone() throws CloneNotSupportedException {
    return super.clone();
}
  1. 弹出异常,强转类型
public static void main(String[] args) throws CloneNotSupportedException {
Person person2 = (Person) person.clone();
//Cloneable (空接口 / 标记接口) 表示当前类可以被克隆
//implements Cloneable 让Person具备可克隆的功能
class Person implements Cloneable{
    public int id;

    //重写 Object 克隆方法
    //不同包中,需要通过super去访问。
    @Override   
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                '}';
    }
}

public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person.id = 1;
        Person person2 = (Person) person.clone();
        System.out.println(person);
        System.out.println(person2);   //得到的结果都是Person{id=1}
    }
}

Clonable 接口和深拷贝

浅拷贝
在这里插入图片描述
深拷贝
在这里插入图片描述
浅拷贝

class Money{
    public double m = 12.5;
}

//Cloneable (空接口 / 标记接口) 表示当前类可以被克隆
//implements Cloneable 让Person具备可克隆的功能
class Person implements Cloneable{
    public int id;
    public Money money = new Money();

    //重写 Object 克隆方法
    //不同包中,需要通过super去访问。
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();      //原来只是克隆出一个对象,在money中,没有克隆出money指向的值   浅拷贝
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", money=" + money +
                '}';
    }
}

public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person.id = 1;
        Person person2 = (Person) person.clone();

        //问题:person.money.m 和 person2.money.m 指向了同一个值
        //浅拷贝
        //改变person2 之后 pearson也变了
        person2.money.m = 100;
        System.out.println(person.money.m);
        System.out.println(person2.money.m);
    }
}

深拷贝

class Money implements Cloneable{
    public double m = 12.5;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

//Cloneable (空接口 / 标记接口) 表示当前类可以被克隆
//implements Cloneable 让Person具备可克隆的功能
class Person implements Cloneable{
    public int id;
    public Money money = new Money();

    @Override
    protected Object clone() throws CloneNotSupportedException {
//        return super.clone();      //原来只是克隆出一个对象,在money中,没有克隆出money指向的值   浅拷贝
        //深拷贝
         Person tmp = (Person) super.clone();  //克隆出了对象。但是他money指向的还是原来的对象的money值 【克隆person】
        tmp.money = (Money) this.money.clone(); //克隆当前对象的money  谁调用clone 谁就是当前对象   【克隆money】
        return tmp;  //
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", money=" + money +
                '}';
    }
}

public class Test2 {

    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person.id = 1;
        Person person2 = (Person) person.clone();
        //问题:person.money.m 和 person2.money.m 指向了不同值
        person2.money.m = 100;
        System.out.println(person.money.m);
        System.out.println(person2.money.m);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值