栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

抽象类与接口的难点解析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

抽象类与接口的难点解析

目录
      • 抽象类
          • 1. 抽象类
          • 2. 抽象方法
          • 3. 抽象类的子类
          • 4. 抽象类的构造方法
      • 接口
          • JDK内置的常用接口
            • 1)Comparable接口
            • 2)Cloneable接口
      • 接口与抽象类的区别
      • 接口和接口的关系

抽象类 1. 抽象类
  • 抽象类无法自身实例化,只能通过多态的形式,向上转型,创建父类引用指向子类对象。

  • 包含抽象方法的类必须使用abstract关键字声明为抽象类。有抽象方法的类一定是抽象类。

  • 抽象类的本质是普通类的“超集”,只是在普通类的基础上扩展抽象方法。故抽象类可以包含非抽象方法、抽象方法。

  • 抽象类也有构造方法。

2. 抽象方法

使用abstract关键字声明、无方法体的方法,称为抽象方法。

e.g. Java中,没有方法体的方法就是抽象方法。(×)

native关键字声明的本地方法也没有方法体。native方法的具体实现由JVM完成,使用JNDI实现Java调用C++中的同名方法。

3. 抽象类的子类

继承了抽象类的子类,若不重写其抽象父类的所有抽象方法,则该子类也会成为抽象类。以此来强制要求子类覆写抽象类中的所有抽象方法(若想要成功实例化的话)。

1)子类是普通类:必须重写抽象父类的所有抽象方法

2)子类是抽象类:可选择性重写抽象父类的抽象方法

4. 抽象类的构造方法

抽象类是在普通类的基础上扩展抽象方法,普通类有的,抽象类也有。故抽象类也有构造方法。

抽象类虽然无法自身实例化,但通过多态形式创建子类实例时,仍然遵循继承的规则,先调用父类的构造方法,再调用子类的构造方法。

e.g. 例题, 程序输出 “num = 0”。


接口
  • 接口表示一种规范或标准,表示具备某种能力或行为。

  • 接口中只有全局常量和抽象方法,没有其他任何非抽象方法、构造方法等。(JDK8以前)

  • 接口中的方法默认被public abstract修饰,均为抽象方法。

    接口中的变量默认被public static final修饰,均为全局常量。且定义时必须初始化常量。

    接口本身可以被abstract修饰,不可被private、protected、final修饰。

  • 接口的实现类必须覆写接口的所有抽象方法。

  • 一个类同时需要继承父类、实现接口时,先声明extends再声明implements。

JDK内置的常用接口 1)Comparable接口
//jdk源码:
public interface Comparable {
    public int compareTo(T o);  
}

若要根据自定义条件比较两个引用数据类型的对象的大小,需覆写java.lang.Comparable接口的compareTo()方法。

重写的思路模板:

public class Student implements Comparable{
    private String name;
    private int score;
    
    
    @Override
    public int compareTo(Object o) {
        // 1.先判断传入的对象是否和当前对象就是同一个对象
        if(this == o) {
            return 0;  //相等
        }
        // 2.判断传入的对象是否具有可比性(是否属于同一类型)
        if(o instanceof Student) {
            // 3.具有可比性,则向下转型【规避传入的对象引用不是Student类型的风险】
            Student stu = (Student) o;
            // 4.比较传入的对象属性和当前对象属性的大小
            return this.score - stu.score;
            
        }
        // 5.不具有可比性,抛出异常,让程序止步于此
        throw new IllegalArgumentException("不是Student类型,无法比较!");
    }
}

仿照Arrays类中sort()方法的功能,自定义一个sort()方法,使得对象数组能够根据自定义的条件进行升序排序(使用冒泡排序)。

【思路】将方法形参中的对象数组定义为Comparable类型,实参传入Comparable接口的实现类的对象数组。即可根据自定条件进行大小比较。

public class Test {

    
    public static void sort(Comparable[] arr) {
        for(int i = 0; i < arr.length-1; i++) {
            for(int j = 0; j < arr.length-i-1; j++) {
                if(arr[j].compareTo(arr[j+1]) > 0) {  //arr[j]对象大于arr[j+1]对象
                    Comparable temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }

    public static void main(String[] args) {
        //Student类已经实现了Comparable接口,且根据score属性值比较对象的大小
        Student[] students = new Student[] {
                new Student("vv", 20),
                new Student("ww", 18),
                new Student("xx", 12)
        };
        //自定义的sort()方法进行升序排序
        sort(students);
        //对Student类重写toString()方法后,即可打印出具体的属性值
        System.out.println(Arrays.toString(students));
    }
}

输出结果:

2)Cloneable接口
//jdk源码:
public interface Cloneable {
}

java.lang.Cloneable 接口内部没有任何抽象方法,仅用于标记其实现类具有可克隆的能力,“拷贝”出的新对象与原对象具有相同的属性值。实现了Cloneable接口的类才可以覆写Object类中的clone()方法,否则会抛出CloneNotSupportedException异常。

JVM运行时,会检查所有实现了Cloneable接口的实现类,赋予其克隆的能力,即允许其覆写Object类的clone()方法。

标记接口:接口中没有任何抽象方法,仅用于标记某个类拥有某种能力。

java.lang.Cloneable:表明Object.clone()方法可以合法地对该类实例进行按字段复制。实现此接口的类应该使用公共方法重写Object.clone()方法(它是受保护的)。如果在没有实现 Cloneable接口的实例上调用Object的clone()方法,则会导致抛出CloneNotSupportedException异常。

java.io.Serializable:未实现此接口的类将无法使其任何状态序列化或反序列化。为保证serialVersionUID值跨不同Java编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID值。

java.util.RandomAccess:用来表明其支持快速(通常是固定时间)随机访问。此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。

java.rmi.Remote:用于标识其方法可以从非本地虚拟机上调用的接口。任何远程对象都必须直接或间接实现此接口。只有在远程接口(扩展java.rmi.Remote的接口)中指定的这些方法才可远程使用。

//jdk源码:Object类中的clone()方法
protected native Object clone() throws CloneNotSupportedException;

克隆测试:(浅拷贝)

public class CloneTest {
    public static void main(String[] args) {
        A a = new A(10);
        B b1 = new B("b1", a);
        //B类对象b1具有可克隆的能力,通过b1调用clone()方法,克隆出一个新对象
        B b2 = b1.clone();
        System.out.println(b2 == b1);  //输出false,克隆出来的对象和原对象是两个独立的对象
        System.out.println(b2.name == b1.name);  //输出true,克隆对象的name与原对象相同
        System.out.println(b2.a == b1.a);  //输出true,克隆对象b2的a引用与原对象b1的a引用指向同一个地址值,指向同一个A类对象 ==> 浅拷贝
        //修改原对象a引用,克隆对象的a引用也会同步修改
        b1.a.num = 100;
        System.out.println(b2.a.num);  //输出100,再次证实B类实现的是浅拷贝
    }
}

class A {
    int num;
    public A(int num) {
        this.num = num;
    }
}

class B implements Cloneable {

    String name;
    A a;  //B类对象包含了A类对象的引用

    public B(String name, A a) {
        this.name = name;
        this.a = a;
    }

    //重写Object类中的clone()方法,返回值为向上转型(B->Object)
    public B clone() {
        B newB = null;
        try {
            newB = (B) super.clone();
            //此处调用的是Object类的clone()方法
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
        return newB;
    }
}

深浅拷贝

深浅拷贝都会拷贝出一个新的对象,如B类对象b1经过拷贝后,产生一个新对象b2。

但若B类对象的属性中包含了对A类对象的引用A a; 则在拷贝时,此属性值(指向的A类对象)分为两种情况:

浅拷贝:拷贝后新对象内部的A类引用和原对象的A类引用指向同一个A类对象 b2.a == b1.a(地址值相同)

深拷贝:拷贝后新对象内部的A类引用和原对象的A类引用指向不同的A类对象 b2.a != b1.a(地址值不同)


Java中实现深拷贝的两种方式:

1)递归使用clone()方法

2)序列化(把对象转换为json字符串)

调用clone()方法产生新对象不会调用该类的构造方法。

Java中产生新对象的两种方式:

1)使用new关键字在堆区为该对象属性开辟内存空间,调用构造方法给属性初始化赋值。

2)实现Comparable接口,重写Object类的clone()方法,通过原对象调用clone()方法,克隆出新对象。


接口与抽象类的区别
  • 接口较抽象类是更加纯粹的“抽象”概念。

    抽象类是在普通类的基础上扩展抽象方法,普通类有的它都有; 而JDK8以前,接口中只有全局常量和抽象方法,没有普通类中的其他方法、变量等。

  • 抽象类和接口都参照多态的形式进行实例化,但抽象类的多态基于继承,子类和父类之间具有is a关系,多个子类之间存在相同的行为特性。

    而接口的多态与继承无关,接口和实现类之间不具有is a关系,多个实现类之间没有特定的关系。

  • 一个类可以实现多个接口,但只能继承一个父类。故通常情况下优先选择使用接口,避免抽象类单继承的局限性。


接口和接口的关系
  • 接口只能继承接口,且允许多继承,即一个接口可继承多个接口。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/870468.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号