抽象类和接口【java】

目录

前言

一、抽象类详解

1.1 什么是抽象类

1.2 抽象类的语法

1.3 抽象类的特点与作用

二、接口详解

2.1 接口的概念

2.2 接口的实现

2.3 多接口的实现 

2.4 接口的继承

2.5 接口的使用

2.5.1 数组的比较 

2.5.2 比较器

2.5.3 克隆

2.5.4 浅拷贝

2.5.5 深拷贝

三、object类详解

3.1 equals方法

3.2 hashcode方法

总结


前言

通过前面的知识,已经打下了基础,下面还是围绕着类这个话题展开学习。

各位看官请指教!


一、抽象类详解

1.1 什么是抽象类

Java是面向对象的语言,他的存在就是对世界中的实体进行描述;而所有的对象都是通过类实例化,我们可以对对象进行细致化的描述,但是呢,并不是所有的对象都用来描述对象,如果一个类中没有能力去具体的描述某一个对象,这样的类就是抽象类。下面我们用一段代码来解答:

//整个抽象类的代码都整合在一起

/*class GoSchool{
    public void goSchool(){
        System.out.println("怎样的方式去上学!");
    }
}*/
abstract class GoSchool{
    //抽象类的方法直接用分号结尾
    public abstract void goSchool();
    //public static abstract void goSchool();//err
    //public final abstract void goSchool();//err
    //private abstract void goSchool();//err
}
class RideBike extends GoSchool{
    @Override
    public void goSchool() {
        System.out.println("骑自行车去上学");;
    }
}
class ByCar extends GoSchool{
    @Override
    public void goSchool() {
        System.out.println("坐车去上学");
    }
}
public class Test1 {
    public static void goSchool(GoSchool goSchool){
        goSchool.goSchool();
    }
    public static void main(String[] args) {
        //GoSchool goSchool = new GoSchool();err
        goSchool(new RideBike());//打印骑自行车去上学
        
    }
}

上面父类中的我们隐藏的goSchool()不能具体的描述怎样上学,没有实际的意义,不能具体的实现goSchool()方法,都是子类在完成相应的描述,所以方法里面的内容System.out.println("怎样的方式去上学!");是可有可无的。对于这样的方法,我们可以直接将里面的内容省略掉,只留下一个“空壳”,如何来实现呢,下面就来为您解答。

1.2 抽象类的语法

Java中我们使用abstract来修饰这样的方法,称之为抽象方法;含有抽象方法的类我们也要用abstract修饰,叫做抽象类;抽象的方法不用给出具体的实现,只需要一个“空壳”就可以。请看下面的代码:

abstract class GoSchool{
    //抽象类的方法直接用分号结尾
    public abstract void goSchool();
}

上面的代码段就给我演示如何创建一个抽象类,在抽象类中,除了不能够实例化对象外,其余的功能都是与普通类一样,抽象类也是有构造方法的,用途就是供被继承后,子类用来初始化父类中的成员。 

1.3 抽象类的特点与作用

(一)抽象类是不能够直接实例化对象的,否则会出错,只能被继承(抽象类的最大的意义就是用来继承的)。

GoSchool goSchool = new GoSchool();//err

(二)抽象类的方法不能被private、static、final修饰,private只能在当前类中使用,static,final不能被重写(抽象类的方法是要被重写的)。

public static abstract void goSchool();//err
public final abstract void goSchool();//err
private abstract void goSchool();//err

(三)抽象类是必须被继承的,这是他出现的意义,并且继承后子类要重写父类中的抽象方法,如果不想被重写,那么子类也修饰为抽象类,使用 abstract 修饰。但是呢在后面的继承中,如果出现一个普通的类继承,那么普通类就需要重写前面所有抽象类中没有被重写的抽象方法。

(四)没有抽象类方法的类也可以被abstract修饰。

abstract class GoSchool{}

 (五)抽象类虽不能实例化对象,但是他可以接收子类(普通类)的对象。

public static void goSchool(GoSchool goSchool){
        goSchool.goSchool();
    }
    public static void main(String[] args) {
        goSchool(new RideBike());//打印骑自行车去上学
    }

 (六)抽象类的作用就是用来校验我们的代码,让我们更早的知道我们的错误,而不是在编译的时候才知道我们写的代码报错了;就比如我们把一串字符串定义为int型,编译器就会马上提示你这个地方写错了,这就是编译器的校验。

int a = "dased";//err

二、接口详解

2.1 接口的概念

信息化的时代,我们已经离不开生活中的接口了,比如电脑、手机:

 电脑上有好几个USB的接口,我们可以插入鼠标、键盘、U盘、手机、usb风扇等等。

手机只有一个USB接口,可以用来充电或者导数据等等......

由此我们可以知道:接口就是公共的行为规范标准,只要符合规范标准,就可以通用(就比如上面的手机,只要是能用的数据线,就能给手机充电)Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。

2.2 接口的实现

下面分几点讲解接口的实现:

//这是第二章节全部的代码

interface IGoSchool{
    public static final int a = 10;//与int a = 10;一模一样;public static final是默认的
    public abstract void test();//接口当中的方法是不能有具体实现的,都是默认public abstract修饰的,默认为抽象方法
    default void defaultTest(){//静态和默认的方法都是默认public修饰的(接口是拿来用的)
        System.out.println("接口默认的方法");
    }
    static void staticTest(){
        System.out.println("静态的方法");
    }
}
//class ByCar implements IGoSchool{}//不想重写抽象方法
class ByCar implements IGoSchool{
    @Override
    public void test() {
        System.out.println("坐车去学校");
    }
    //public和default共存了
    public void defaultTest(){//静态和默认的方法都是默认public修饰的(接口是拿来用的)
        System.out.println("接口默认的方法!!!");
    }
}
public class Test {
    public static void goSchool(IGoSchool iGoSchool){
        iGoSchool.test();//打印坐车去学校
    }
    public static void main(String[] args) {
        //IGoSchool iGoSchool = new ByCar();//发生向上转型,类实现了接口,引用了具体的实现类
        goSchool(new ByCar());//
    }
}

(一)接口的定义格式和定义类的格式是差不多的,将class换成interfaces即可。

interface IGoSchool

 (二)接口是不能直接使用的,必须要一个“实现类”来实现接口,重写接口中的所有抽象方法,雷和接口之间叫做“实现”。

class ByCar implements IGoSchool

(三)接口中的方法是不能有实现的,除非加default。 

default void defaultTest(){//静态和默认的方法都是默认public修饰的(接口是拿来用的)
        System.out.println("接口默认的方法");
    }

(四)重写抽象方法后,要注意子类里面的访问限定符,接口的访问限定符是默认的,子类的访问限定符就不能是默认的了(子类的访问限定符要大于等于父类的)。 

(五)抽象方法中是不能有构造方法、实例代码块和静态代码块的。

(六)一个类继承接口,是需要重写接口里面所有的抽象方法的,不然这个类也需要修饰为抽象类

class ByCar implements IGoSchool{}//不想重写抽象方法

(七)接口当中的成员变量默认public static final修饰,成员方法默认public abstract修饰。

public static final int a = 10;//与int a = 10;一模一样;public static final是默认的
public abstract void test();//接口当中的方法是不能有具体实现的,都是默认public abstract修饰的,默认为抽象方法

2.3 多接口的实现 

Java中不支持多继承,但是可以有多个接口的存在。为什么会有多接口呢,就动物世界来说鸟会飞,鱼会游泳,老虎会跑等等,那我们就可以吧可以游泳的、能飞的、能上树的归为一个接口,因为一个类中创建是可能被继承的,而且一个类中我们不能把所有动物的生活方式全部写在一起,所以才会出现接口。

class Animal{
    public String name;
    public Animal(String name){
        this.name = name;
    }
}
interface IFlying{//能飞的
    void fly();
}
interface ISwimming{//能游泳的
    void swimming();
}
interface IRunning{//能跑的 
    void running();
}
//一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。
//一个类只能被单继承,但可以实现多个接口。
class Tiger extends Animal implements IRunning,IFlying,ISwimming{
    @Override
    public void fly() {
        System.out.println(name+"我可不会飞!");
    }
    @Override
    public void swimming() {
        System.out.println(name+"我可不会游泳!");
    }

    public Tiger(String name){
        super(name);
    }
    @Override
    public void running() {
        System.out.println(name+"正在飞快的跑!");
    }
}
public class Test {
    public static void func(Tiger tiger){
        tiger.fly();
        tiger.swimming();
        tiger.running();
    }
    public static void main(String[] args) {
        func(new Tiger("老虎"));
    }
}

🚀接口是用来描述某个类具备某种能力的,程序员写代码的时候,就不必关心具体的类型。

🚀一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。
🚀一个类只能被单继承,但可以实现多个接口。

2.4 接口的继承

Java中,不只有类才有继承,接口也是能继承的,就像多态一样,可以实现代码的复用,也是使用extends关键字来发生继承关系,也叫做扩展功能。

interface IFlying{
    void fly();
}
interface ISwimming{
    void swimming();
}
interface IRunning extends ISwimming,IFlying{
    void running();
}

2.5 接口的使用

2.5.1 数组的比较 

//public interface Comparable<T>  <T>是泛型 T代表类型 Comparable<T>是编译器的接口
//public int compareTo(T o);接口的方法
class Dog implements Comparable<Dog>{
    public String name;
    public int age;
    public double weight;

    public Dog(String name, int age, double weight) {
        this.name = name;
        this.age = age;
        this.weight = weight;
    }

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

    @Override
    public int compareTo(Dog o) {
        if(this.age > o.age)//this表示当前引用dog,o接收dog1
            return 1;
        else if(this.age == o.age)
            return 0;
        else
            return -1;
        //return this.age - o.age;
    }
}
//尤拉比较器,就可以删除Comparable<Dog>接口
//名字比较器
class NameComparator implements Comparator<Dog>{//根据用户的需求来定义,不用直接写死
    @Override
    public int compare(Dog o1, Dog o2) {//实现重写的快键键 按住ALT+回车+回车
        return o1.name.compareTo(o2.name);
        //return o1.name.length()-o2.name.length();//main3
    }
    //equals为什么不重写了
}
//年龄比较器
class AgeComparator implements Comparator<Dog>{
    @Override
    public int compare(Dog o1, Dog o2) {//实现重写的快键键 按住ALT+回车+回车
        return o1.age-o2.age;
    }
    //equals为什么不重写了
}
public class Test1 {
    public static void main3(String[] args) {
        AgeComparator ageComparator = new AgeComparator();
        NameComparator nameComparator = new NameComparator();
        Dog dog = new Dog("aaa",3,36.3);
        Dog dog1 = new Dog("b",6,66.3);
        int ret = ageComparator.compare(dog,dog1);
        System.out.println(ret);//打印的是两个age之间的差值:-3
        int ret1 = nameComparator.compare(dog,dog1);
        System.out.println(ret1);//打印的是两个name长度之间的差值:2
    }

    public static void main2(String[] args) {
        Dog[] dogs = new Dog[2];
        dogs[0] = new Dog("哈士奇",3,36.3);
        dogs[1]= new Dog("牧羊犬",6,66.3);
        Arrays.sort(dogs);//排序的原理是把每一个元素强转成comparable
        System.out.println(Arrays.toString(dogs));//如果没有Comparable<Dog>这个接口,那代码会报错

        Arrays.sort(dogs,new AgeComparator());
        System.out.println(Arrays.toString(dogs));
    }

    public static void main1(String[] args) {
        Dog dog = new Dog("哈士奇",3,36.3);
        Dog dog1 = new Dog("牧羊犬",6,66.3);
        //比较两个自定义数组之间的大小有两个步骤:1.指定比较的方法2.告诉编译器怎么做
        if(dog.compareTo(dog1) < 0)
            System.out.println("dog < dog1");
    }
}

🚀自定义的数据进行比较时,需要 public interface Comparable<T> ;

🚀比较大小有两个步骤:1.指定比较的方法2.告诉编译器怎么做

2.5.2 比较器

比较器的出现可以根据用户的需求来自定义,使用Comparator<T>接口,上面代码中已有了名字和年龄两个比较器的用法,这里就不做说明了。

2.5.3 克隆

首先我们要实现一个克隆接口,克隆就是产生对象的一个副本,如何来产生呢?实现Cloneable接口,这个接口里面没有抽象方法,所谓空接口(标记接口),意味可以被克隆,需要重写object类的克隆方法。

class Clone implements Cloneable{//空接口:现了这个接口的类可以被克隆
    public int a = 0;
    @Override
    protected Object clone() throws CloneNotSupportedException {//不同包的子类
        return super.clone();//调用object类
    }
}
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {//异常(后面详解)
        CClone clone = new Clone();
        System.out.println(clone.a);
        Clone clone1 = (Clone)clone.clone();//克隆Clone类型的,克隆的返回值是object类,向上转型需要强制类型转换
        System.out.println(clone1.a);
    }
}

图解克隆: 

2.5.4 浅拷贝

什么是浅拷贝呢?就是拷贝的时候,没有拷贝到对象里面所引用的对象(我拷贝的对象里面还存了一份对象的地址,原对象里面存的对象没有被拷贝)下面就来解释:

//为了方便,这里的代码加上上面克隆的代码就是全部的代码
class A{
    public int b = 10;
}
class Clone implements Cloneable{//空接口:现了这个接口的类可以被克隆
    public int a = 0;
    public A c = new A();//存了A对象的地址
}

public class Test1 {
    public static void main(String[] args) throws CloneNotSupportedException {
        clone.c.b = 20;
        System.out.println(clone.c.b);
        System.out.println(clone1.c.b);

    }
}

图解: 

 由上面的图和代码可知,A对象没有被拷贝,clone和clone1里面都存了对象A的地址,同时指向了A,当我们任意改变一个clone和clone1里面所存对象A里面b的值时,两个clone和clone1都会被改变,这样的现象就叫做浅拷贝。

2.5.5 深拷贝

有了上面的基础,我们就可以轻易的得出深拷贝的原理了,请看下面的图解:

 深拷贝就是只要是被拷贝的,都要单独的占用一份空间,而类A想要被克隆的话,也是要使用克隆接口的,下面的代码是修改后的代码,当我们去任意改变clone或clone1中引用A中的值b的话,两个是互不干扰的:

//照着上面的代码修改
class A implements Cloneable{
    public int b = 10;

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

    @Override
    protected Object clone() throws CloneNotSupportedException {//不同包的子类
        Clone clone = (Clone)super.clone();//克隆clone变成clone1
        clone.c = (A) this.c.clone();//克隆A的对象 (clone在栈区中作为局部变量传递地址)
        return clone;
        //return super.clone();//调用object类
    

三、object类详解

Object Java 默认提供的一个类。 Java 里面除了 Object 类,所有的类都是存在继承关系的。默认会继承 Object 父类,可以访问里面的任何方法。即所有类的对象都可以使用Object 的引用进行接收。
class Dog{//默认继承父类object
    public String a="asd";

    @Override
    public String toString() {
        return "Dog{" +
                "a='" + a + '\'' +
                '}';
    }
}
class Tiger{}
public class test {
    public static void func(Object o){
        String ret = o.toString();
        System.out.println(ret);
        //System.out.println(o);
    }

    public static void main(String[] args) {
        func(new Tiger());//打印demo4.Tiger@1b6d3586 demo4代表的我的包
        func(new Dog());//打印Dog{a='asd'}
    }
}

上面的代码我们没有手动的去继承,那么所有的类就会默认继承object类。

3.1 equals方法

equals方法就是判断两个对象或者对象里面的值是否相等。

//只有核心的代码块,加上面的代码就是全部
public boolean equals(Object obj){
        if(obj == null)//传过来的参数时候为空
            return false;
        if (this == obj) // 两个对象的地址是否相同
            return true;
        Dog dog = (Dog) obj;
        return this.a.equals(dog.a);
    }

public static void main(String[] args) {
        Dog dog = new Dog("qwe");
        Dog dog1 = new Dog("qwe");;
        System.out.println(dog1.equals(null));

        //System.out.println(dog.equals(null));//对应上面的第一个if
        //Dog dog1 = dog;//对应上面第二个if

    }

当我们没有重写equals方法的时候,equals默认是object类的方法。

3.2 hashcode方法

hashcode方法用来确定对象在内存中存储的位置是否相同(数据结构会详解,这里使用编译器生成)

//重写hashCode()
@Override
    public int hashCode() {
        return Objects.hash(a);
    }

public static void main(String[] args) {
        Dog dog = new Dog("qwe");
        Dog dog1 = new Dog("qwe");;
        System.out.println(dog.hashCode());//字符串一样会返回同样的哈希值
        System.out.println(dog1.hashCode());
    }

总结

抽象类:

1.使用abstract修饰的方法叫做抽象方法,有抽象方法的类,必须为抽象类。(没有抽象方法也可以修饰为抽象类)

2.抽象类不能够实例化,其余与普通的类一样,可以被继承(最大的意义就是被继承)。

3.抽象类被继承后,子类需要重写抽象类的所有抽象方法,不想被重写,可以将子类也修饰为抽象类,但是又被一个不是抽象类继承,那他需要重写所有的抽象方法。

 接口:

1.和类的定义格式一样,将class换成interface,接口名的前面加上大写字母I(例如IFlower),看起来更加的规范。

2.接口当中的成员变量都是默认public static final修饰的;成员方法都是public abstract修饰的

3.接口当中的普通方法是不能有具体实现的,除非加上default修饰【从JDK8开始】。

4.接口当中的静态方法和default方法都是public默认修饰的。

5.接口也是不可以进行实例化的。

6.类和接口的关系用implements关联,关联后,必须重写接口里所有的方法。

7.接口可以引用接口具体的实现类,想当于向上转型。

8.不能有实例代码块、静态代码块、构造方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霄百

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值