[英]super() causes NullPointerException
當我學習梁的書時,我陷入了困境,我不知道發生了什么。 錯誤的原因是MyArrayList類的構造函數。 作者警告我們不要調用super(object),但是他們沒有解釋原因。 現在,當我嘗試運行代碼時,作者是正確的-當我們調用super(object)時,我得到一個錯誤。 此錯誤的原因是什么?
MyArrayList.java
public class MyArrayList<E> extends MyAbstractList<E> {
public static final int INITIAL_CAPACITY = 16;
private E[] data = (E[])new Object[INITIAL_CAPACITY];
/** Create a default list */
public MyArrayList() {
}
/** Create a list from an array of objects */
public MyArrayList(E[] objects) {
/*for (int i = 0; i < objects.length; i++)
add(objects[i]); // Warning: don't use super(objects)! */
super(objects); //!!! AUTHOR WARNS US ABOUT NOT INVOKING THIS LINE !!!
}
/** Add a new element at the specified index in this list */
public void add(int index, E e) {
ensureCapacity();
// Move the elements to the right after the specified index
for (int i = size - 1; i >= index; i--)
data[i + 1] = data[i];
// Insert new element to data[index]
data[index] = e;
// Increase size by 1
size++;
}
/** Create a new larger array, double the current size */
private void ensureCapacity() {
if (size >= data.length) {
E[] newData = (E[])(new Object[size * 2 + 1]);
System.arraycopy(data, 0, newData, 0, size);
data = newData;
}
}
/** Clear the list */
public void clear() {
data = (E[])new Object[INITIAL_CAPACITY];
size = 0;
}
/** Return true if this list contains the element */
public boolean contains(E e) {
for (int i = 0; i < size; i++)
if (e.equals(data[i])) return true;
return false;
}
/** Return the element from this list at the specified index */
public E get(int index) {
return data[index];
}
/** Return the index of the first matching element in this list.
* Return -1 if no match. */
public int indexOf(E e) {
for (int i = 0; i < size; i++)
if (e.equals(data[i])) return i;
return -1;
}
/** Return the index of the last matching element in this list
* Return -1 if no match. */
public int lastIndexOf(E e) {
for (int i = size - 1; i >= 0; i--)
if (e.equals(data[i])) return i;
return -1;
}
/** Remove the element at the specified position in this list
* Shift any subsequent elements to the left.
* Return the element that was removed from the list. */
public E remove(int index) {
E e = data[index];
// Shift data to the left
for (int j = index; j < size - 1; j++)
data[j] = data[j + 1];
data[size - 1] = null; // This element is now null
// Decrement size
size--;
return e;
}
/** Replace the element at the specified position in this list
* with the specified element. */
public E set(int index, E e) {
E old = data[index];
data[index] = e;
return old;
}
/** Override toString() to return elements in the list */
public String toString() {
StringBuilder result = new StringBuilder("[");
for (int i = 0; i < size; i++) {
result.append(data[i]);
if (i < size - 1) result.append(", ");
}
return result.toString() + "]";
}
/** Trims the capacity to current size */
public void trimToSize() {
if (size != data.length) { // If size == capacity, no need to trim
E[] newData = (E[])(new Object[size]);
System.arraycopy(data, 0, newData, 0, size);
data = newData;
}
}
}
MyList.java
public interface MyList<E> {
/** Add a new element at the end of this list */
public void add(E e);
/** Add a new element at the specified index in this list */
public void add(int index, E e);
/** Clear the list */
public void clear();
/** Return true if this list contains the element */
public boolean contains(E e);
/** Return the element from this list at the specified index */
public E get(int index);
/** Return the index of the first matching element in this list.
* Return -1 if no match. */
public int indexOf(E e);
/** Return true if this list contains no elements */
public boolean isEmpty();
/** Return the index of the last matching element in this list
* Return -1 if no match. */
public int lastIndexOf(E e);
/** Remove the first occurrence of the element o from this list.
* Shift any subsequent elements to the left.
* Return true if the element is removed. */
public boolean remove(E e);
/** Remove the element at the specified position in this list
* Shift any subsequent elements to the left.
* Return the element that was removed from the list. */
public E remove(int index);
/** Replace the element at the specified position in this list
* with the specified element and returns the new set. */
public Object set(int index, E e);
/** Return the number of elements in this list */
public int size();
}
MyAbstractList.java
public abstract class MyAbstractList<E> implements MyList<E> {
protected int size = 0; // The size of the list
/** Create a default list */
protected MyAbstractList() {
}
/** Create a list from an array of objects */
protected MyAbstractList(E[] objects) {
for (int i = 0; i < objects.length; i++)
add(objects[i]);
}
/** Add a new element at the end of this list */
public void add(E e) {
add(size, e);
}
/** Return true if this list contains no elements */
public boolean isEmpty() {
return size == 0;
}
/** Return the number of elements in this list */
public int size() {
return size;
}
/** Remove the first occurrence of the element o from this list.
* Shift any subsequent elements to the left.
* Return true if the element is removed. */
public boolean remove(E e) {
if (indexOf(e) >= 0) {
remove(indexOf(e));
return true;
}
else
return false;
}
}
TestMyArrayList.java
public class TestMyArrayList {
public static void main(String[] args)
{
String[] str = {"manisa","turkey","germany"};
MyList<String> list = new MyArrayList<String>(str);
list.add("America");
list.add(0,"Canada");
list.add(1,"England");
System.out.println(list);
}
}
這是錯誤代碼:
Exception in thread "main" java.lang.NullPointerException
at MyArrayList.ensureCapacity(MyArrayList.java:36)
at MyArrayList.add(MyArrayList.java:21)
at MyAbstractList.add(MyAbstractList.java:16)
at MyAbstractList.<init>(MyAbstractList.java:11)
at MyArrayList.<init>(MyArrayList.java:16)
at TestMyArrayList.main(TestMyArrayList.java:8)
這里的問題是MyAbstractList
中的構造函數在初始化data
之前調用add
。
data
字段在MyArrayList
類中聲明和初始化。 但是直到超類初始化完成后才進行初始化,並且在超類初始化期間進行add
調用...當data
仍為null
。
這里的一般問題是,構造函數調用可能被子類覆蓋的方法是危險的。 可能在子類初始化發生之前調用重寫方法。
在這種情況下, add(E)
可能會被覆蓋。 更糟糕的是, add(E)
調用add(E, int)
,因為它在超類中是抽象的,所以肯定會被覆蓋。
讓我們將代碼簡化為基本要點:
public abstract class MyAbstractList<E> {
protected int size = 0; // The size of the list
protected MyAbstractList() {}
protected MyAbstractList(E[] objects) {
for (int i = 0; i < objects.length; i++)
add(objects[i]);
}
public class MyArrayList<E> extends MyAbstractList<E> {
public static final int INITIAL_CAPACITY = 16;
private E[] data = (E[])new Object[INITIAL_CAPACITY];
public MyArrayList(E[] objects) {
super(objects); // this call to super() executes before data is initialized
}
}
public static void main(String[] args) {
String[] str = {"manisa","turkey","germany"};
MyList<String> list = new MyArrayList<String>(str);
}
要了解的重要一點是,父類的構造函數在子類甚至尚未初始化之前就被調用(這就是為什么super()
始終必須是構造函數中的第一個調用),這意味着在MyAbstractList
的構造函數運行時, data
仍然為null
。
用內容替換super()
調用意味着在正確設置data
之后 , 在 MyArrayList
初始化時執行for
循環。
本質上,問題在於MyAbstractList
提供了一個構造函數,該構造函數調用將MyAbstractList
類覆蓋的方法,這是一種嚴重的反模式。 MyAbstractList
不應該提供所有樣式的構造函數。
有關更多信息,請參見有效的Java項目17,其中指出:
構造函數不得直接或間接調用可重寫的方法 。 如果違反此規則,將導致程序失敗。 超類構造函數在子類構造函數之前運行,因此子類中的重寫方法將在子類構造函數運行之前被調用。 如果重寫方法依賴於子類構造函數執行的任何初始化,則該方法將無法正常工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.