0
点赞
收藏
分享

微信扫一扫

01_链表

GhostInMatrix 2022-03-11 阅读 59

抽象类与接口

抽象类

  • 抽象类不能被实例化,只能被继承。
  • 抽象类可以实现接口中的所有方法或其中几个方法
  • 被关键字 abstract 修饰的类称为抽象类;
  • 包含抽象方法的类一定是抽象类,但抽象类不一定包含抽象方法(抽象类可以包含普通方法)
  • 被 abstract 修饰的方法称为抽象方法,抽象方法只有方法声明没有方法体,但普通方法可以包含方法体
  • 一个类继承于一个抽象类,则子类必须实现抽象类的抽象方法,如果子类没有实现父类的抽象方法,那子类必须定义为抽象类。
  • 抽象方法的权限修饰符只能为 public、protected 或 default,默认情况下为 public。···

接口

  • 接口支持多继承,即一个接口可以继承(extends)多个接口,间接解决了 Java 中类不能多继承的问题。
  • 一个类可以同时实现多个接口,一个类实现某个接口则必须实现该接口中的抽象方法,否则该类必须被定义为抽象类。

抽象类和接口的应用场景

抽象类应用场景

(1)父类只知道其子类应该包含怎样的方法,不能准确知道这些子类如何实现这些方法的情况下,使用抽象类。
(2)从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为子类的模板,从而避免了子类设计的随意性。

接口的应用场景:

(1)一般情况下,实现类和它的抽象类之前具有 “is-a” 的关系,但是如果我们想达到同样的目的,但是又不存在这种关系时,使用接口。
(2)由于 Java 中单继承的特性,导致一个类只能继承一个类,但是可以实现一个或多个接口,此时可以使用接口。

什么时候使用抽象类和接口:

(1)如果拥有一些方法并且想让它们有默认实现,则使用抽象类。
(2)如果想实现多重继承,那么必须使用接口。因为 Java 不支持多继承,子类不能继承多个类,但可以实现多个接口,因此可以使用接口。
(3)如果基本功能在不断改变,那么就需要使用抽象类。如果使用接口并不断需要改变基本功能,那么就需要改变所有实现了该接口的类。

链表

1.链表数据结构

public class LinkedList<E> extends AbstractList<E> {
    // 1.linkedlist有size和first(即第一个结点,头节点node)
    // 2.每个节点内部有真正的元素element和next下一个结点
    private int size;
    private Node<E> first;

    private static class Node<E>{
        E elementE;
        Node<E> next;
        public Node(E elementE, Node<E> next) {
            this.elementE = elementE;
            this.next = next;
        }
    }
}

2.获取index处对应的结点对象

	private Node<E> node(int index) {
        rangeCheck(index);//检查索引范围
        Node<E> node = first;
        for (int i = 0; i < index; i++) {
            node = node.next;
        }
        return node;
    }

3.查看元素索引

	public int indexOf(E element) {
		// 1.判断所查对象是否为null
        if(element == null){
            Node<E> node = first;
            for (int i = 0; i < size; i++){
                if (node.elementE == null)
                    return i;
                node = node.next;
            }
        }else {
            Node<E> node = first;
            for(int i = 0; i < size; i++){
                if (element.equals(node.elementE))
                    return i;
                node = node.next;
            }
        }
        return 0;
    }

4.在任意位置添加结点

	public void add(int index, E element) {
		rangeCheckForAdd(index);//检查索引范围
        if(index == 0){// 在链表的头部加
            first = new Node<E>(element, first);
        } else {
            Node<E> prev = node(index - 1);
            prev.next = new Node<E>(element, prev.next);
            size++;
        }

    }

	// 在链表尾部加
	public void add(E element) {
		add(size, element);
	}

5.删除任意位置的结点

	public E remove(int index) {
		rangeCheck(index);//检查索引范围
        Node<E> node = first;
        if(index == 0) {
            first = first.next;
        }
        Node<E> prev = node(index - 1);//node为一个方法
        node = prev.next;
        prev.next = prev.next.next;
        return node.elementE;
    }

检查索引范围封装代码

	protected void outOfBounds(int index) {
		throw new IndexOutOfBoundsException("Index:" + index + ", Size:" + size);
	}
	
	protected void rangeCheck(int index) {
		if (index < 0 || index >= size) {
			outOfBounds(index);
		}
	}
	
	protected void rangeCheckForAdd(int index) {
		if (index < 0 || index > size) {
			outOfBounds(index);
		}
	}
举报

相关推荐

0 条评论