JavaSE-抽象类和接口

(一)抽象类

概念

当这个类不能被具体表达某个对象时,我们把这个类定义为抽象类,被abstract修饰。

//抽象类:被abstract修饰的类
public abstract class Shape{
    //抽象方法:被abstract修饰的方法,没有方法体
    abstract public void draw();
    
    //抽象类也是类,也可以增加普通方法和属性
    public double getArea(){
        return area;
    }
    
    protected double area;
}

 特性

  1. 被abstract修饰的方法,叫做抽象方法,没有具体的实现的
  2. 如果一个类包含了这个抽象方法,此时这个类也必须得用abstract修饰。这个类也被叫做抽象类
  3. 抽象类必须被继承
  4. 抽象类当中,可以有和普通类当中一样的成员变量和成员方法
  5. 不能被实例化 (存在构造方法,因为可以被子类继承后,调用这个构造方法初始化抽象类的成员)👇
  6. 当一个普通类继承了这个抽象类,那么这个普通类必须重写抽象方法
  7. 抽象方法不能被private,final,static修饰
  8. 当一个普通类继承抽象类后,必须要重写抽象类的抽象方法和基类的抽象方法👇

作用

抽象类本身不能实例化,要想使用,只能创建该抽象类的子类,然后让子类重写抽象类中的抽象方法。那么,普通类可以被继承,普通方法也可以被重写,为啥非要用抽象类和抽象方法?

📍抽象类相当于多了一重编译器的校验

使用抽象类在实际工作中不应由父类完成,而应由子类完成,如果此时不小心误用成父类,使用普通编译器是不会报错的,但是父类是抽象类就会在实例化时提示错误,提醒我们的程序猿。(很多语法存在的意义时为了"预防出错")

(二)接口

概念

被interface修饰的,是一种引用数据类型。它是一种标准规范,多个类的公共规范。       

 创建接口时,接口的命名一般以大写字母I开头;一般使用形容词词性的单词。 

public interface 接口名称{
    //抽象方法
    public abstract void method();//默认是abstract public,可以不写
    public void method2();
    abstract void method3();
    void method4();//更推荐这种写法
}
 
    

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

子类和父类之间是extends继承关系,类和接口之间是implements实现关系。 

public class 类名 implements 接口名称{
    //...
}

特性

  1. 定义接口用关键字interface定义
  2. 接口当中的方法,如果没有被实现,那么默认是一个抽象方法
  3. 接口当中的方法不能有具体的实现(不能在接口中实现,只能由实现接口的类来实现)
  4. 如果非要有具体的实现,要用default或static修饰
  5. 接口当中定义成员变量,默认都是public static final的(“常量”)
  6. 接口当中的抽象方法,默认都是public abstract修饰
  7. 接口不可以被实例化
  8. 类和接口之间的关系,可以使用implements来关联
  9. 接口也可以产生字节码文件的
  10. 一个类可以继承一个抽象类/普通类,同时还可以实现这个接口(顺序:先继承,再实现)

 注意:

接口和抽象类都可以发生向上转型的。 

接口使用

(1)

IDEA中,ctrl + i 可以快速生成接口

首先创建一个demo3的包,把所有的类放到该包底下

IUSB接口:

public interface IUSB {
    void openDevice();
    void closeDevice();
}

 Keyboard类,实现IUSB接口

public class KeyBoard implements IUSB{

    @Override
    public void openDevice() {
        System.out.println("打开键盘!");
    }
    public void inPut() {
        System.out.println("键盘输入");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭键盘!");
    }
}

Mouse类,实现IUSB接口:

public class Mouse implements IUSB{
    @Override
    public void openDevice() {
        System.out.println("打开鼠标!");
    }

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

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

笔记本类,使用IUSB设备:

public class Computer {
    public void powerOn() {
        System.out.println("打开电脑!");
    }
    public void powerOff() {
        System.out.println("关闭电脑!");
    }

    public void useDevice(IUSB iusb) {
        iusb.openDevice();

        if(iusb instanceof Mouse) {
            Mouse mouse = (Mouse)iusb;//向下转型
            mouse.click();
        }else if(iusb instanceof KeyBoard) {
            KeyBoard keyBoard = (KeyBoard)iusb;
            keyBoard.inPut();
        }
        iusb.closeDevice();
    }
}

Test类的main方法中:

public class Test {
    public static void main(String[] args) {
        Computer computer =new Computer();
        computer.powerOn();

        computer.useDevice(new Mouse());

        computer.useDevice(new KeyBoard());

        computer.powerOff();
    }
}

运行结果:

 (2)实现多个接口

 第十点,还可以补充 👉

一个类实现多个接口时,每个接口中的抽象方法都要实现,否则必须设为抽象类。

public class 子类类名 extends 父类类名 implements A,B,C,D{

    //...

}// 此时的A,B,C,D都是接口,中间用英文逗号隔开

 在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口。

当他实现接口后,必须重写里面的抽象方法。

继承表达的含义是is-a的关系,而接口表达的是has-a的关系. 

而设置接口的好处就在于,时刻牢记多态的好处,让程序猿忘记类型。有了接口之后,类的使用者就不必再关注具体类型,而只关注某个类是否具备某种能力。

(3)接口间的继承

在Java中,类和类之间是单继承,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的。

使用extends关键词,当接口A拓展了接口B之后,就可以拥有B中的方法。

interface IRuning {
    void run();
}

interface ISwimming {
    void swim();
}

//两栖动物,既能游,也能跑
interface IAmphibious extends ISwimming,IRuning{   
}

class Frog implements IAmphibious {
}

通过接口继承创建一个新的接口IAmphibious表示”两栖“的,此时实现接口创建的Frog类,就继续要实现run、swim方法。 

接口和抽象类的区别

  • 抽象类当中,可以包含和普通类一样的成员变量和成员方法,但是接口当中的成员变量只能是public 是static final的,方法只能是public abstract的
  • 一个类只能继承一个抽象类,但可以同时实现多个接口,所以解决了Java当中不能进行多继承的特性

(三)内部类

(1)概念

当一个事物的内部,还有一部分需要有一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么这个内部的完整结构最好使用内部类。在Java中,可以将一个类定义在另一个类或一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。

上图,火车可以看作一个外部类,车厢看作一个内部类。

 注意:

1.定义在class{}花括号外部的,即使在同一个文件,也能称为内部类

2.内部类和外部类共用同一个Java源文件,经过编译后,内部类会形成单独的字节码文件-->外部类名$内部类类名.class

(2)分类 

静态内部类

语法规则:

public class OutClass {
    static class InnerClass { };//被static修饰的成员内部类
}
    

 实例:

class OutClass {
    private int a;
    static int b;
    public void methodA() {
        a = 10;
        System.out.println(a);
    }
    public static void methodB() {
        System.out.println(b);
    }
    
    //静态内部类
    static class InnerClass {
        //在内部类中只能访问外部类的静态成员
        public void methodinner() {
           // a = 100;//error a不是类的成员变量
            b = 200;
           // methodA();//error 它不是类的成员方法
            methodB();
        }
    }
    public static void main(String[] args) {
        OutClass.InnerClass innerClass = new OutClass.InnerClass();//获取静态内部类对象 点好优先级最高,先跟后面结合
        innerClass.methodinner();//成员访问
    }
}

 在静态内部类中只能访问外部类中的静态成员

创建静态内部类对象时,不需要先创建外部类对象

实例内部类

语法规则:

public class OutClass {
    public class InnerClass { };//未被static修饰的成员内部类
}
    

实例:

class OutClass2{
    private int a = 2;
    static int b = 3;
    int c = 4;
    public void methodA() {
        a = 10;
        System.out.println(a);
    }
    public static void methodB() {
        System.out.println(b);
    }

    //实例内部类
    class InnerClass {
        int c = 5;
        public static final int d = 6;//在实例内部类中 被static修饰的成员变量要加 final并且赋值,这时候把它看作是一个常量。常量是在编译的时候确定的
        public void methodinner() {
            //在实例内部类中可以直接访问外部类中任意访问限定符修饰的成员
            a = 100;
            b = 200;
            methodA();
            methodB();

            //如果外部类和实例内部类有相同名字的成员,优先访问内部类自己的
            System.out.println(c);

            //如果要访问外部类同名成员,必须:外部类名.this.同名的成员名
            OutClass2.this.c = 400;
            System.out.println(OutClass2.this.c);
         
        }

    }

    public static void main(String[] args) {
        OutClass2 outClass2 = new OutClass2();
        System.out.println(outClass2.a);
        System.out.println(OutClass2.b);
        System.out.println(outClass2.c);
        outClass2.methodA();
        outClass2.methodB();
        
        //创建实例内部类对象
        OutClass2.InnerClass innerClass = new OutClass2().new InnerClass();
        innerClass.methodinner();
    }
}

实例内部类当中,有两个this

1.实例内部类自己的this 

2.外部类的this

匿名内部类

首先,什么是匿名对象?👇

匿名内部类也会生成自己的字节码文件。在以后的学习中更多是要学会使用匿名内部类

(四)Object类

Object类是所有类的父类。它包含以下的方法。

当我们写好一个类Person,其实它默认就是继承了父类Object。并且Object类也可以发生向上转型。

(1)toString方法 

(2)对象比较equals方法 

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

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

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

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

main方法中:

   public static void main(String[] args) {

        Person person1  = new Person("zhangsan",18);
        Person person2  = new Person("zhangsan",18);

        int a = 10;
        int b = 10;

        System.out.println(a == b);//true
        System.out.println(person1 == person2);//false  直接用 == 比较,比较的是地址 
        System.out.println(person1.equals(person2));//true  重写equals方法了
    }

Person类重写了Object的equals方法:

class Person {
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;//不是person类对象
        Person person = (Person) o;//向下转型,比较属性值
//      return age == person.age && Objects.equals(name, person.name);
        return this.name.equals(person.name) && this.age == person.age;//这样写也可以
    }

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

(3)hashcode方法  

toString源码:

 

 hashcode()这个方法,帮我们算了一个具体的对象位置,因为涉及到数据结构,这里只能简单的理解为它是个内存地址,然后调用Integer.toHexString()方法,将这个地址以16进制输出。

hashcode源码:

该方法是一个native方法,底层是由C/C++实现的,我们无法清楚看到。

我们认为,两个名字相同,年龄相同的对象将存储在同一个位置,但是如果不重写hashcode方法,那么两个对象的hash值是不一样的。

因此,也需要在Person类中重写hashcode()方法:

main方法调用

System.out.println(person1.hashCode());
System.out.println(person2.hashCode());

输出结果 哈希值一样:


寄语:不要忘了你希望的远方~

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值