#java #arrays #dynamic-arrays
Вопрос:
MRP
class B {
static int v;
public B(int i) {
System.out.format("Constructor called with value %dn", i);
v=i;
}
}
public class A {
static B[] c;
A(){
c=new B[5];
c[1]=new B(1);
for (int i=0; i<3; i ) {
System.out.format("c[%d] is %dn", i, c[i].v);
}
c[2]=new B(2);
for (int i=0; i<3; i ) {
System.out.format("c[%d] is %dn", i, c[i].v);
}
}
public static void main(String[] args) {
new A();
}
}
Output is:
Constructor called with value 1
c[0] is 1
c[1] is 1
c[2] is 1
Constructor called with value 2
c[0] is 2
c[1] is 2
c[2] is 2
Ожидалось бы, что исключение будет вызвано ссылкой на неназначенные элементы массива, например, c[0]. Значения массива также неправильно изменены предыдущим назначением. c[0] никогда не присваивается значение, но принимает значения 1 и 2 в выводе выше.
public class A {
static String[] c;
A(){
c=new String[5];
c[0]=new String("alpha");
for (int i=0; i<3; i ) {
System.out.format("c[%d] is %sn", i, c[i]);
}
c[1]=new String("beta");
for (int i=0; i<3; i ) {
System.out.format("c[%d] is %sn", i, c[i]);
}
}
public static void main(String[] args) {
new A();
}
}
Output for the above is:
c[0] is alpha
c[1] is null
c[2] is null
c[0] is alpha
c[1] is beta
c[2] is null
Другое поведение наблюдается для строкового объекта в приведенном выше примере.
Ответ №1:
Таким образом, вопрос в том, почему это не c[i].v
приводит к NullPointerException
тому, когда c[i]
это null
происходит .
Давайте начнем с чего-нибудь немного более простого:
B b = null;
System.out.println(b.v);
Это не будет бросать NullPointerException
.
Почему?
Поскольку v
поле B
есть static
, нам не нужно разыменовывать значение b
, чтобы получить значение v
. Значение v
не связано с каким-либо конкретным экземпляром B
.
Так, по сути, b.v
и B.v
есть эквиваленты.
В более общем случае рассмотрим, что <expr>
это какое-то выражение, статическим типом которого является B
. Затем:
V v = <expr>.v
имеет тот же эффект, что и:
B temp = <expr>;
V v = B.v;
Другими словами, выражение вычисляется, и его значение отбрасывается. Затем берется значение статического поля. Но поскольку temp
разыменование не разыменовано (потому что в этом нет необходимости), в случае, если выражение равно нулю, NPE не будет … как это происходит в вашем примере.
Разница в вашем примере со строкой заключается в том, что вы печатаете состояние String
экземпляра, а не состояние static
поля. И в конкатенации строк не возникает NPE, потому
что оператор сопоставляет null
to "null"
, а не вызывает null.toString()
.
Суть в том, что использование ссылки на экземпляр для доступа к статическому полю-плохая идея. Потому что синтаксис делает не то, что вы могли бы от него ожидать.
Действительно, некоторые средства проверки стиля Java / статические анализаторы отметят это как плохой стиль или возможную ошибку.
Комментарии:
1. Хорошо, я все понял. Таким образом, тот факт, что статическое поле не связано с экземпляром, означает, что все экземпляры B, на которые ссылаются значения массива c[0], c[1] и c[2], разрешаются в статическое поле.
2. Это верно. (Это один из тех случаев, когда я думаю, что они немного неправильно поняли Java. IMO
b.v
должна быть ошибкой компиляции, еслиv
static
это поле, аb
не класс, который объявляетv
. Это позволило бы избежать проблем, подобных вашим …)