JAVASE----抽象类和接口总结

本文详细介绍了Java中的抽象类和接口,包括它们的概念、语法、特性及应用场景。抽象类主要用于被继承,不能直接实例化,其抽象方法需在子类中重写。接口则定义了多个类的公共规范,类通过implements接口并实现其所有抽象方法。文章还讨论了实现多个接口、接口间的继承以及抽象类与接口的区别,并给出了实际使用示例。

一:抽象类

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

2:语法

一个类如果被 abstract修饰称为抽象类,抽象类中被abstract修饰的方法称为抽象方法,抽象方法不用给出具体的实现体。

注意:抽象类也是类,内部可以包含普通方法和属性,甚至构造方法。

3.抽象类特性

(1).抽象类不能直接实例化对象

abstract class Shape{
abstract public void draw();
}
public static void main(String[] args) {
    Shape shape =new Shape();    //Shape 是抽象的,无法实例化。
}

(2).抽象方法不能是private的

abstract class Shape{
abstract private void draw();//非法的修饰组合,abstract和private
}

*抽象方法没有加范根限定符时,默认是public

(3):抽象方法不能被finalstatic修饰,因为抽象方法要被子类重写

 abstract class Shape{
    abstract final void funcA();
    abstract public static funcB();
 }

(4):抽象类必须被继承,并且继承后子类要重写父类中的抽象方法

class Rect extends Shape {
    private double length;
    private double width;

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

    @Override
    public void draw() {   //重写父类的draw()方法
        System.out.println("矩形:length=" + length + "width=" + width);
    }
}

如果不重写父类中的抽象方法,则子类必须为抽象类,否则报错。

class Triangle extends Shape {     //此时报错
    private double a;
    private double b;
    private double c;
}
abstract class Triangle extends Shape {   //加上abstract不报错
     private double a;
     private double b;
     private double c;
 }

(5)抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类

(6)抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量

4.抽象类的作用

抽象类的作用就是被继承。

普通的类也可以被继承,普通的方法也可以被重写,为什么要引入一个抽象类的概念呢?------

实际工作中有些代码不应该由父类完成,而应该由子类完成,如果此时不小心误用成父类了,使用普通类编译器是不会报错的,但是父类抽象类就会在实例化的时候提示错误,以便于早些发现问题

二:接口

1.概念

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

2.语法规则

接口的定义格式与定义类的格式基本相同,将class关键字换成interface关键字,就定义了一个接口

interface IA{
    public abstract void methodA();//public abstract 是固定搭配,可以不写
    public void methoddB();
    abstract void methodC();
    void methodD();     //推荐使用该书写方法
}

提示:

(1)创建接口时,接口的命名一般已大写字母 开头

(2)接口的命名一般使用“形容词”磁性的单词

(3)接口的方法和属性不要任何修饰符号,保持代码的简洁性

3.接口使用

接口不能直接使用,必须要有一个“实现类”来实现该接口,实现接口中的所有抽象方法。

 class B implements IA{
//..重写接口中的方法
}

注意:子类和父类之间是extends 继承关系,类与接口之间是  implements关系。

例:请实现笔记本电脑使用USB鼠标,USB键盘的例子

1.USB接口:包含打开设备,关闭设备功能。

2.笔记本类:包含开机功能,关机功能,使用USB设备功能

3.鼠标类:实现USB接口,并具备点击功能

4.键盘类:实现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;//必须把usb强转为Mouse类型,因为usb中没有具体的鼠标点击等方法
            mouse.click();
        } 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 Mouse());
        //使用键盘设备
        computer.useDevice(new KeyBoard());
        computer.powerOff();
    }
}

打开笔记本电脑
打开鼠标
鼠标点击
关闭鼠标
打开键盘
键盘输入
关闭键盘
关闭笔记本电脑

 4.接口特性

(1)接口类型是一种引用类型,不能直接new接口的对象

(2)接口中每一个方法都是public的抽象方法,接口中的方法会被隐式指定为public abstract,其他的修饰符会出错。

interface ITest{
    private void testDemo() ;//接口中不允许引用private
    void testDemo1();
}

(3)接口中的方法是不能在接口中实现的,只能由实现接口的类来实现

interface ITest{
    void testDemo() ;
    void testDemo1(){               //接口abstract方法不能有主体
        System.out.println("试图在接口中实现");
    }
}

(4)重写接口中方法时,不能使用默认的访问权限修饰

例如:

interface ITest{
    void testDemo() ;
}
class A implements ITest{
    @Override
   public void testDemo() {   //此处的public若是省略掉则会报错
    }
}

(5)接口中可以含有变量,但是接口中的变量会被隐式的指定为public static final 变量

interface ITest {
    double a = 3.0;  //默认被  public static final修饰
}

public class Test4 {
    public static void main(String[] args) {
        System.out.println(ITest.a);      //此处可以通过接口名直接访问,说明是静态的
    }
    ITest.a=2.0;     //此处报错,说明变量具有final属性
}

 (6)接口中不能有静态代码快和构造方法

interface ITest {
    public ITest{

    }    //编译失败
    
    {}  // 编译失败
}

(7)接口虽然不是类,但是接口编译完成后字节码文件的后缀也是.class

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

 class Frog extends Aniaml implements IRunning,ISwimming{
    public Frog(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(name+"正在跳着跑");
    }

    @Override
    public void swim() {
        System.out.println(name+"正在蛙泳");
    }
}   // 此处若只实现一个接口,则会报错。如果必须只实现一个接口,那么必须设置成abstract类

三:实现多个接口

JAVA中不支持多继承,但是一个类可以实现多个接口。

class Aniaml {
    protected String name;

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

    void eat() {
        System.out.println("正在吃饭");
    }
}

interface IFlying {
    void fly();
}

interface IRunning {
    void run();
}

interface ISwimming {
    void swim();
}

//猫可以跑
class Cat extends Aniaml implements IRunning {
    public Cat(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(name + "正在用四条腿跑");
    }

    @Override
    void eat() {
        System.out.println(name + "正在吃猫粮");
    }
}

//鱼可以游泳
class Fish extends Aniaml implements ISwimming {
    public Fish(String name) {
        super(name);
    }

    @Override
    public void swim() {
        System.out.println(name + "正在用尾巴游泳");
    }

    void eat() {
        System.out.println(name + "正在吃鱼粮");
    }
}

//青蛙 能跑,能游泳
class Frog extends Aniaml implements IRunning, ISwimming {
    public Frog(String name) {
        super(name);
    }

    @Override
    public void run() {
        System.out.println(name + "正在跳着跑");
    }

    @Override
    public void swim() {
        System.out.println(name + "正在蛙泳");
    }

    void eat() {
        System.out.println(name + "正在吃蛙粮");
    }
}

//鸭子可以跑,游泳,飞
class Duck extends Aniaml implements IRunning, ISwimming, IFlying {
    public Duck(String name) {
        super(name);
    }

    @Override
    public void fly() {
        System.out.println(name + "正在用翅膀飞");
    }

    @Override
    public void run() {
        System.out.println(name + "正在用两条腿跑");
    }

    @Override
    public void swim() {
        System.out.println(name + "正在飘在水上");
    }

    void eat() {
        System.out.println(name + "正在吃鸭粮");
    }
}

class Robot implements IRunning {

    @Override
    public void run() {
        System.out.println("机器人正在跑");
    }
}

public class Test5 {
    public static void walk(IRunning iRunning) {
        iRunning.run();
    }

    public static void swim(ISwimming iSwimming) {
        iSwimming.swim();  //这两个方法不关注是哪个类型的
    }

    public static void eat(Aniaml aniaml) {
        aniaml.eat();    //这个方法必须是Animal类型的
    }

    public static void main(String[] args) {
        walk(new Duck("小黄鸭"));
        walk(new Frog("小青蛙"));
        eat(new Duck("大鸭子"));
        walk(new Robot());
        swim(new Frog("青蛙二号"));
        swim(new Fish("鲤鱼"));
    }
}

小黄鸭正在用两条腿跑
小青蛙正在跳着跑
大鸭子正在吃鸭粮
机器人正在跑
青蛙二号正在蛙泳
鲤鱼正在用尾巴游泳
 

这样做的好处是可以使人忘记类型,有了接口之后,类的使用者就可以不必关注具体类型,而只关注某个类是否具备某种能力。

四:接口间的继承

接口与接口之间可以多继承,达到复用的效果,使用extends 关键字

interface IRunning {
    void run();
}

interface ISwimming {
    void swim();
}
interface Icanswimandrun extends IRunning,ISwimming{
    //...
}

接口间的继承相当于把多个接口合并 在一起

五:接口使用实例

1.给对象数组排序

class Student {
    private String name;
    private int score;

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

    public String toString() {
        return "[" + this.name + ":" + this.score + "]";
    }
}
public class Test6 {
    public static void main(String[] args) {
        Student[] students = new Student[]{
                new Student("张三", 20),
                new Student("张四", 15),
                new Student("张五", 18),
                new Student("张六", 22),
        };
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

当我们需要给上述学生对象排序时,使用sort方法,会报错,因为不知道是根据年龄排还是姓名排。

所以此时需要用Student类实现Comparable接口,并实现其中的compareTo方法

class Student implements Comparable {
    private String name;
    private int score;

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

    public String toString() {
        return "[" + this.name + ":" + this.score + "]";
    }

    @Override
    public int compareTo(Object o) {
        Student student =(Student) o;
        if(this.score> student.score){
            return -1;
        } else if (this.score<student.score) {
            return 1;
        }else {
            return 0;
        }
    }
}

六:抽象类和接口的区别

抽象类中可以包含普通方法和普通字段,这样的普通方法和普通字段可以被子类直接使用(不必重写),而接口中不能包含普通方法,子类必须重写所有的抽象方法。

七:Object类

1.对象比较equals方法

在java中,==进行比较时:

a.如果==左右两侧是基本类型变量,比较的是变量中的值是否相同

b.如果==左右两侧是引用类型变量,比较的是引用变量地址是否相同

c.如果要比较对象中的内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的

class Person2 {
    private String name;
    private int age;

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

public class Test8 {
    public static void main(String[] args) {
        Person2 p1 = new Person2("jialin", 21);
        Person2 p2 = new Person2("jialin", 21);
        int a = 10;
        int b = 10;
        System.out.println(a == b);
        System.out.println(p1 == p2);
        System.out.println(p1.equals(p2));
    }
}

输出:

true
false
false

Person类重写equals方法后,然后比较:

class Person2 {
    private String name;
    private int age;

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

    @Override
    public boolean equals(Object obj)                      
 //----------------------------------------------------------------------------
        if (obj == null) {                       
            return false;
        }
        if (this == obj) {
            return true;                            //此区域为对obj做出判断
        }
        //不是Person2类对象
        if (!(obj instanceof Person2)) {
            return false;
        }
//-----------------------------------------------------------------------------
        Person2 person = (Person2) obj;             //此区域为重写的内容
        if (this.name.equals(person.name) && this.age == person.age) {
            return true;
        }
        return false;
    }
}

public class Test8 {
    public static void main(String[] args) {
        Person2 p1 = new Person2("张三", 21);
        Person2 p2 = new Person2("张三", 21);
        int a = 10;
        int b = 10;
        System.out.println(a == b);
        System.out.println(p1 == p2);
        System.out.println(p1.equals(p2));   //重写后为 true
    }
}

结论:比较对象中内容是否相同时,一定要重写equals方法

2.hashcodde方法    (计算一个对象的位置)

class People {
    public String name;
    public int age;

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

public class Test9 {
    public static void main(String[] args) {
        People people1 = new People("小红", 20);
        People people2 = new People("小红", 20);
        System.out.println(people1.hashCode());
        System.out.println(people2.hashCode());
    }
}


执行结果:
460141958
1163157884

注意:两个完全一样的对象的hash值不一样。

当我们重写hashCode()方法时

class People {
    public String name;
    public int age;

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

    @Override
    public int hashCode() {
        return Objects.hash(name,age);
    }
}

public class Test9 {
    public static void main(String[] args) {
        People people1 = new People("小红", 20);
        People people2 = new People("小红", 20);
        System.out.println(people1.hashCode());
        System.out.println(people2.hashCode());
    }
}

//执行结果

23653826
23653826    注意事项:哈希值一样。

结论:

1,hashcode方法用来确定对象中内存存储的位置是否相同

2.事实上hashCode()在散列表中才有用,在其他情况下没用。在散列表中hashCode()的作用时获取对象的散列码,进而确定该对象在散列表中的位置。

equals()方法和 hashCode()方法一般来说都使用编译器默认生成的,不需要我们自己书写。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值