写在最后
可能有人会问我为什么愿意去花时间帮助大家实现求职梦想,因为我一直坚信时间是可以复制的。我牺牲了自己的大概十个小时写了这片文章,换来的是成千上万的求职者节约几天甚至几周时间浪费在无用的资源上。
上面的这些(算法与数据结构)+(Java多线程学习手册)+(计算机网络顶级教程)等学习资源
AbstractList里面有一个方法也叫做add。
public boolean add(E e) {
add(size(), e);
return true;
}
参数是E,是泛型,目测编译器是把这种的也看成方法重写了。我们看下报错:
意思就是冲突啦,这个被认为是方法重写。
继续之前的例子,我们快速学习一下这个知识点。
class Person{
public void eat(){
System.out.println(“人在吃饭。。。”);
}
}
class Teacher extends Person {
public void eat(){
System.out.println(“老师在吃饭。。。”);
}
}
父类和子类都有一个eat方法,且都没有参数(即参数列表相同),返回值也相同,这就叫做参数重写。
测试代码:
Person p = new Teacher();
Teacher t = (Teacher) p;
t.eat();
运行结果:
老师在吃饭。。。
注意,方法重写的前提是:
1.方法名
2.参数列表
3.返回值
以上三者全部相同,唯有方法体不一样。这种的才叫方法重写。
除了方法重写,还有一种叫做方法重载,就是同一个方法名,参数列表不同的情况,比如:
class Teacher extends Person {
public void eat(){
System.out.println(“老师在吃饭。。。”);
}
public void eat(String food){
System.out.println(“老师在吃” + food );
}
}
两个方法都叫做eat,但是第二个eat方法多了一个参数,就是方法重载。注意,方法重载和返回值没有半毛钱关系,这个概念经常会在笔试题里面考。比如,下面这样的写法就是错误的:
编译器认为你这是把一个相同的方法写了两遍,所以给你报错了,再强调一遍,这可不是方法重载哦!
那么,如何修改add方法,才可以不报错呢?
很简单,让它的返回值和AbstractList里面的add方法一致就可以了。
public boolean add(Object object){
//将数据用节点类包装好,这样才能实现下一个数据的指向
Node data = new Node(object);
//先判断是否是第一个节点
if(this.firstNode == null){
this.firstNode = data;
this.currentNode = data;
}else{
//不是第一个节点,就指向当前节点的下一个节点,即currentNode.next
this.currentNode.next = data;
//因为已经指向下一个了,所以当前节点也要移动过来
this.currentNode = data;
}
return true;
}
这样就变成方法重写了。
我们来看下AbstractList中的原生add方法是怎么写的:
public boolean add(E e) {
add(size(), e);
return true;
}
我们可以发现,add方法接收一个参数e,然后立刻又调用了另一个add方法。很明显,这是方法重载,因为他的参数列表不同,接收两个参数。其中第一个参数size(),是一个方法的调用,这边得到的肯定是size方法的返回值。我们盲猜这个方法的作用是获取当前链表的长度,意思就是add方法增加的数据,直接就拼接到链表的末尾了。
到底是大佬写的代码啊,想的就是全面,而我们写的add方法,就没考虑那么多,直接添加到末尾了。
其实不一定的,万一我们要添加到链表的中间位置呢,是不是完全有可能呢?
那么,我们再来欣赏一下这个重载的add方法吧:
额,直接抛异常了?
再看看size:
public abstract int size();
直接就是抽象方法,而且,在他的子类AbstractSequentialList中也没有实现这个size方法,那么只能由继承AbstractSequentialList的LinkedList去实现啦。
至于那个add方法,直接抛了异常,意思大概就是我们必须去重写它了。虽然在AbstractSequentialList中,重写了这个add,但是我们不用它,因为它的设计太复杂了,不适合我们新手学习。
先挑软柿子捏,size方法无非就是获取链表的长度,来,搞起!
我们可以抄袭,哦不,借鉴 java.util 包里面LinkedList中size的做法,就是直接设置一个int属性,每次add的时候就+1,remove的时候就-1。
我去,这个transient关键字是啥哦?
解释:
java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
不过呢,现在我们很少用到java里面的序列化了,基本都是json传输,或者xml传输。对于一些敏感的属性,我们可以通过注解的方式过滤掉,所以这个知识点我们简单识记一下即可。
显示增加属性:
private int size = 0; //链表中的元素个数,初始化为0
然后是size方法
@Override
public int size() {
return this.size;
}
没错,就这么简单。
重载add方法,这个才是最通用的。
public void add(int index, E element) {
try {
listIterator(index).add(element);
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
原来,父类AbstractSequentialList并没有亏待咱,已经写好了这个重载的add方法。不过呢,它的实现比较复杂,而且是针对双向链表的。我们的LinkedList是单链表,就不整那么复杂了,但是,我们可以参考下大佬的写法。
@Override
public void add(int index, Object element) {
boolean isOutSize = checkIndex(index);
if(isOutSize){
throw new IndexOutOfBoundsException("Index: "+index + “已经超出范围了,目前最大容量为” + size());
}
}
/**
-
检查下标是否超限
-
@param index
-
@return
*/
private boolean checkIndex(int index) {
return index > size;
}
上面有个超纲的内容,就是异常抛出,IndexOutOfBoundsException是范围超出异常,你现在就理解成程序手动报错就行了。
测试一下,先生成size的get,set方法。
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
然后,原来的add方法加上size的自增代码:
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add(“苹果”);
list.add(“梨子”);
System.out.println(list.getSize());
}
没问题,但是当我们给第三个位置添加元素时:
list.add(3,“柚子”);
就报错了,符合我们的预期。
@Override
public void add(int index, Object element) {
boolean isOutSize = checkIndex(index);
if(isOutSize){
throw new IndexOutOfBoundsException("Index: "+index + “已经超出范围了,目前最大容量为” + size());
}
int loop = 0;
Node node = firstNode; //从第一个元素开始
while(node != null) {
loop++;
if(loop == index){
//如果已经是最后一个,则直接拼接
if(loop == size()){
Node newNode = new Node(element);
node.next = newNode;
//跳出循环
break;
}
//1.先保存当前节点的next,防止断尾
Node temp = new Node(node.next != null ? node.next.data:null);
temp.next = node.next != null ? node.next.next : null;
//2.之前断掉的部分接在新节点后面
Node newNode = new Node(element);
newNode.next = temp;
//3.新的节点接在node后面
node.next = newNode;
//跳出循环
break;
}else{
node = node.next;
}
}
}
/**
-
检查下标是否超限
-
@param index
-
@return
*/
private boolean checkIndex(int index) {
return index > size || index <= 0;
}
我们先用display方法来做测试。
LinkedList list = new LinkedList();
list.add(“苹果”);
list.add(“梨子”);
list.add(1,“柚子”);
list.display();
但是,如果我们用增强for循环,就会报错。
for (Object o : list) {
System.out.println(o);
}
咋就空指针了呢?
这边还不好调试,只能去看反编译的代码:
重点就是
Iterator var2 = list.iterator();
这行代码,点进去看:
public Iterator iterator() {
return listIterator();
}
listIterator,好眼熟啊,我们好像重写了对不?
对哦,当时我们嫌麻烦,就写了一个假的实现,直接就返回null了,所以肯定报错啊,哈哈。
正好复习一下之前的知识点,我们再来new一个匿名实现类。
LinkedList list = new LinkedList();
list.add(“苹果”);
list.add(“梨子”);
list.add(2,“柚子”);
list.forEachRemaining(new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o);
}
});
逼格瞬间就高了。
假如知道了下标,就该拿到对应的元素
面试准备+复习分享:
为了应付面试也刷了很多的面试题与资料,现在就分享给有需要的读者朋友,资料我只截取出来一部分哦
55aa9fe9f.png)
对哦,当时我们嫌麻烦,就写了一个假的实现,直接就返回null了,所以肯定报错啊,哈哈。
正好复习一下之前的知识点,我们再来new一个匿名实现类。
LinkedList list = new LinkedList();
list.add(“苹果”);
list.add(“梨子”);
list.add(2,“柚子”);
list.forEachRemaining(new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o);
}
});
逼格瞬间就高了。
假如知道了下标,就该拿到对应的元素
面试准备+复习分享:
为了应付面试也刷了很多的面试题与资料,现在就分享给有需要的读者朋友,资料我只截取出来一部分哦
[外链图片转存中…(img-p1RYhEcd-1715080236547)]