![]() |
Spec-Zone .ru
спецификации, руководства, описания, API
|
Содержание | Предыдущий | Следующий | Индекс | Спецификация языка Java Третий Выпуск |
ГЛАВА 16
Каждая локальная переменная (§14.4) и каждый пробел final
у поля (§4.12.4) (§8.3.1.2) должно быть определенно присвоенное значение, когда любой доступ его значения происходит. Доступ к его значению состоит из простого имени переменной, происходящей где угодно в выражении за исключением левого операнда простого оператора присваивания =
. Компилятор Java должен выполнить определенный консервативный анализ потоков, чтобы удостовериться что для каждого доступа локальной переменной или пробела final
поле f, f определенно присваивается перед доступом; иначе ошибка времени компиляции должна произойти.
Точно так же каждый пробел final
переменная должна быть присвоена самое большее однажды; это должно быть определенно неприсвоенным, когда присвоение на это происходит. Такое присвоение определяется, чтобы произойти если и только если или простое имя переменной, или ее простое имя, квалифицированное this
, происходит на левой стороне оператора присваивания. Компилятор Java должен выполнить определенный консервативный анализ потоков, чтобы удостовериться что для каждого присвоения на пробел final
переменная, переменная является определенно неприсвоенной перед присвоением; иначе ошибка времени компиляции должна произойти.
Остаток от этой главы посвящается точному объяснению слов, "определенно присвоенных прежде, чем" и "определенно отменил присвоение прежде".
Идея позади определенного присвоения состоит в том что присвоение на локальную переменную или пробел final
поле должно произойти на каждом возможном пути выполнения к доступу. Точно так же идея позади определенного неприсвоения состоит в том что никакое другое присвоение на пробел final
переменной разрешают произойти на любом возможном пути выполнения к присвоению. Анализ принимает во внимание структуру операторов и выражений; это также обеспечивает специальный режим операторов выражения !
, &&
, ||
, и ? :
, и оцененных булевской переменной константных выражений.
Например, компилятор Java распознает это k
определенно присваивается перед его доступом (как параметр вызова метода) в коде:
потому что доступ происходит только если значение выражения:{ int k; if (v > 0 && (k = System.in.read()) >= 0) System.out.println(k); }
истина, и значение может бытьv > 0 && (k = System.in.read()) >= 0
true
только если присвоение на k
выполняется (более должным образом, оценивается).Точно так же компилятор Java распознает что в коде:
переменная{ int k; while (true) { k = n; if (k >= 5) break; n = 6; } System.out.println(k); }
k
определенно присваивается while
оператор, потому что выражение условия true
никогда не имеет значение false
, так только break
оператор может вызвать while
оператор, чтобы обычно завершаться, и k
определенно присваивается перед break
оператор.С другой стороны, код
должен быть отклонен компилятором Java, потому что в этом случае{ int k; while (n < 4) { k = n; if (k >= 5) break; n = 6; } System.out.println(k); // k is not "definitely assigned" before this }
while
оператор, как гарантируют, не выполнит свое тело, насколько правила определенного присвоения затрагиваются.
За исключением специального режима условных булевых операторов &&
, ||
, и ? :
и оцененных булевской переменной константных выражений, значения выражений не принимаются во внимание в анализе потоков.
Например, компилятор Java должен произвести ошибку времени компиляции для кода:
даже при том, что значение{ int k; int n = 5; if (n > 2) k = 3; System.out.println(k); // k is not "definitely assigned" before this }
n
известен во время компиляции, и в принципе можно быть известно во время компиляции что присвоение на k
будет всегда выполняться (более должным образом, оцениваться). Компилятор Java должен работать согласно правилам, размеченным в этом разделе. Правила распознают только константные выражения; в этом примере, выражении n
>
2
не константное выражение как определено в §15.28. Как другой пример, компилятор Java примет код:
до определенного присвоенияvoid flow(boolean flag) { int k; if (flag) k = 3; else k = 4; System.out.println(k); }
k
затрагивается, потому что правила, обрисованные в общих чертах в этом разделе, позволяют этому говорить это k
присваивается независимо от того, является ли флаг true
или false
. Но правила не принимают изменение:
и так компиляция этой программы должна заставить ошибку времени компиляции происходить.void flow(boolean flag) { int k; if (flag) k = 3; if (!flag) k = 4; System.out.println(k); // k is not "definitely assigned" before here }
Связанный пример иллюстрирует правила определенного неприсвоения. Компилятор Java примет код:
до определенного неприсвоенияvoid unflow(boolean flag) { final int k; if (flag) { k = 3; System.out.println(k); } else { k = 4; System.out.println(k); } }
k
затрагивается, потому что правила, обрисованные в общих чертах в этом разделе, позволяют этому говорить это k
присваивается самое большее однажды (действительно, точно однажды) независимо от того, является ли флаг true
или false
. Но правила не принимают изменение:
и так компиляция этой программы должна заставить ошибку времени компиляции происходить.void unflow(boolean flag) { final int k; if (flag) { k = 3; System.out.println(k); } if (!flag) { k = 4; // k is not "definitely unassigned" before here System.out.println(k); } }
Чтобы точно определить все случаи определенного присвоения, правила в этом разделе определяют несколько технических терминов:
Например, локальная переменная k определенно присваивается значение после оценки выражения
когда выражениеa && ((k=m) > 5)
true
но не, когда выражение false
(потому что, если a
false
, тогда присвоение на k
не обязательно выполняется (более должным образом, оценивается)).
Фраза "V определенно присваивается, после X" (то, где V локальная переменная и X, является оператором или выражением) означает "V, определенно присваивается после X, если X обычно завершается". Если X завершается резко, присвоение не должно произойти, и правила, утвержденные здесь, принимают это во внимание. Специфическое последствие этого определения - то, что "V определенно присваивается после break;
"всегда истина! Поскольку a break
оператор никогда обычно не завершается, это вырождено истинно, что V был присвоен значение если break
оператор обычно завершается.
Точно так же оператор "V является определенно неприсвоенным, после X" (то, где V переменная и X, является оператором или выражением) означает "V, является определенно неприсвоенным после X, если X обычно завершается". Еще более специфическое последствие этого определения - то, что "V является определенно неприсвоенным после break;
"всегда истина! Поскольку a break
оператор никогда обычно не завершается, это вырождено истинно, что V не был присвоен значение если break
оператор обычно завершается. (В этом отношении это также вырождено истинно, что луна делается из зеленого сыра если break
оператор обычно завершается.)
В целом, есть четыре возможности для переменной V после оператора, или выражение было выполнено:
Например:
V [un], присвоенный после пустой эквивалентности оператора, которая это [un], присвоенный перед пустым оператором.как должны понимать, обозначает два правила:
Определенный анализ неприсвоения операторов цикла повышает специальную проблему. Рассмотрите оператор while
(e) S. Чтобы определить, является ли V определенно неприсвоенным в пределах некоторого подвыражения e, мы должны определить, является ли V определенно неприсвоенным прежде e. Можно было бы спорить по аналогии с правилом для определенного присвоения (§16.2.10), который V является определенно неприсвоенным, прежде e эквивалентность это является определенно неприсвоенным перед while
оператор. Однако, такое правило является несоответствующим в наших целях. Если e оценит к истине, то оператор S будет выполняться. Позже, если V присваивается S, то в следующей итерации (ях) V будет уже присвоен, когда e оценивается. По правилу, предложенному выше, было бы возможно присвоиться V многократно, который является точно, чего мы стремились избежать, представляя эти правила.
Пересмотренное правило было бы: "V является определенно неприсвоенным, прежде e эквивалентность это является определенно неприсвоенным перед, в то время как оператор и определенно отменил присвоение после S". Однако, когда мы формулируем правило для S, мы находим: "V является определенно неприсвоенным, прежде S эквивалентность это является определенно неприсвоенным после e когда истина". Это приводит к зацикливанию. В действительности, V является определенно неприсвоенным перед условием цикла e, только если оно является неприсвоенным после цикла в целом!
Мы повреждаем этот порочный круг, используя гипотетический анализ условия цикла и тела. Например, если мы предполагаем, что V является определенно неприсвоенным, прежде e (независимо от того, является ли V действительно определенно неприсвоенным прежде e), и может тогда доказать, что V был определенно неприсвоенным, после e тогда мы знаем, что e не присваивается V. Это утверждается более формально как:
Принятие V является определенно неприсвоенным, прежде e, V является определенно неприсвоенным после e.
Изменения на вышеупомянутом анализе используются, чтобы определить хорошо основанные определенные правила неприсвоения для всех операторов цикла на языке.
Всюду по остальной части этой главы мы, если явно не утверждено иначе, запишем V, чтобы представить локальную переменную или пробелfinal
поле (для правил определенного присвоения) или пробел final
переменная (для правил определенного неприсвоения). Аналогично, мы будем использовать a, b, c, и e, чтобы представить выражения, и S и T, чтобы представить операторы. Мы будем использовать фразу V, чтобы означать что или простое имя переменной V, или V `s простое имя, квалифицированное this
(игнорирование круглых скобок). Мы будем использовать фразу не V, чтобы означать отрицание V.true
когда ложь.
false
когда истина. true
никогда не имеет значение false
, и константное выражение, значение которого false
никогда не имеет значение true
, два предыдущих правила вырождено удовлетворяются. Они полезны в анализе выражений, включающих операторы &&
(§16.1.2), ||
(§16.1.3), !
(§16.1.4), и ?
:
(§16.1.5).
true
когда истинная эквивалентность V [un], присвоенный перед константным выражением.
false
когда ложная эквивалентность V [un], присвоенный перед константным выражением.
&&
b, когда истинная эквивалентность V [un], присвоенный после b когда истина.
&&
b, когда ложная эквивалентность V [un], присвоенный после, когда ложь и V [un], присвоенный после b когда ложь.
&&
b.
&&
b эквивалентность V [un], присвоенный после a &&
b, когда истина и V [un], присвоенный после a &&
b, когда ложь. ||
b, когда истинная эквивалентность V [un], присвоенный после, когда истина и V [un], присвоенный после b когда истина.
||
b, когда ложная эквивалентность V [un], присвоенный после b когда ложь.
||
b.
||
b эквивалентность V [un], присвоенный после a ||
b, когда истина и V [un], присвоенный после a ||
b, когда ложь. !
когда истинная эквивалентность V [un], присвоенный после когда ложь.
!
когда ложная эквивалентность V [un], присвоенный после когда истина.
!
a.
!
эквивалентность V [un], присвоенный после !
когда истина и V [un], присвоенный после !
когда ложь. (Это эквивалентно высказыванию, которое V [un], присвоенный после !
эквивалентность V [un], присвоенный после a.)
?
b :
c, когда истинная эквивалентность V [un], присвоенный после b, когда истина и V [un], присвоенный после c когда истина.
?
b :
c, когда ложная эквивалентность V [un], присвоенный после b, когда ложь и V [un], присвоенный после c когда ложь.
?
b :
c.
?
b :
c эквивалентность V [un], присвоенный после a ?
b :
c, когда истина и V [un], присвоенный после a ?
b :
c, когда ложь.
?
b :
c эквивалентность V [un], присвоенный после того, как b и V [un], присвоенный после c.
?
b :
c.
!
a, условное выражение - и выражение a && b, условное выражение - или выражение a ||
b, или условное выражение a ?
b :
c.
=
b, a +=
b, a -=
b, a *=
b, a /=
b, a %=
b, a <<=
b, a >>=
b, a >>>=
b, a &=
b, a |=
b, или a ^=
b.
Отметьте это, если V и V определенно не присваивается перед составным присвоением, таким как a &=
b, затем ошибка времени компиляции обязательно произойдет. Первое правило для определенного вышеизложенного присвоения включает разобщенное "V" даже для составных выражений присвоения, не только простых присвоений, так, чтобы V, как полагали, был определенно присвоен в более поздних точках в коде. Включая разобщенное "V" не влияет на выбор из двух альтернатив относительно того, является ли программа приемлемой или приведет к ошибке времени компиляции, но это влияет, сколько различных точек в коде может быть расценено как ошибочные, и так практически это может улучшить качество сообщения об ошибке. Подобный комментарий применяется к включению соединенного "не V" в первом правиле для определенного вышеизложенного неприсвоения.
++
a, --
a, ++, или - эквивалентность или V или V определенно присваивается после выражения операнда.
++
a, --
a, ++, или - эквивалентность не V и V является определенно неприсвоенным после выражения операнда.
++
a, --
a, ++, или-. ++
a, преддекрементное выражение --
a, постинкрементное выражение a ++, постдекрементное выражение a - логическое дополнительное выражение !
a, условное выражение - и выражение a && b, условное выражение - или выражение a ||
b, условное выражение a ?
b :
c, или выражение присвоения, тогда следующие правила применяются:
this
(и квалифицированный и неполный), неполные выражения создания экземпляра класса без параметров, инициализированные выражения создания массива, инициализаторы которых не содержат выражений, неполных выражений доступа к полю суперкласса, названных вызовами метода без параметров, и неполными вызовами метода суперкласса без параметров.
Есть часть тонкого рассуждения позади утверждения, что переменная V, как может быть известно, является определенно неприсвоенной после вызова метода. Взятый отдельно, по номиналу и без квалификации, такое утверждение является не всегда истиной, потому что вызванный метод может выполнить присвоения. Но нужно помнить, что в целях языка программирования Java понятие определенного неприсвоения применяется только, чтобы очистить final
переменные. Если V пробел final
локальная переменная, тогда только метод, которому принадлежит его объявление, может выполнить присвоения на V. Если V пробел final
поле, тогда только конструктор или инициализатор для класса, содержащего объявление для V, может выполнить присвоения на V; никакой метод не может выполнить присвоения на V. Наконец, явные вызовы конструктора (§8.8.7.1) обрабатываются особенно (§16.9); хотя они синтаксически подобны операторам выражения, содержащим вызовы метода, они не операторы выражения, и поэтому правила этого раздела не применяются к явным вызовам конструктора.
Отметьте, что нет никаких правил, которые позволили бы нам приходить к заключению, что V является определенно неприсвоенным перед блоком, который является телом любого конструктора, метода, инициализатора экземпляра или статического инициализатора, объявленного в C. Мы можем неофициально прийти к заключению, что V не является определенно неприсвоенным перед блоком, который является телом любого конструктора, метод, инициализатор экземпляра или статический инициализатор, объявленный в C, но нет никакой потребности в таком правиле, которое будет утверждено явно.
=
e, V +=
e, V -=
e, V *=
e, V /=
e, V %=
e, V <<=
e, V >>=
e, V >>>=
e, V &=
e, V |=
e, или V ^=
e, который происходит в B.
++
V, --
V, V ++, или V-. это происходит в B. V = e
. Если V
определенно присваивается после e
, тогда также:
V
vacouusly, определенно присвоенный. В этом случае присвоение не будет фактически иметь место, и мы можем принять это V
не присваивается выражением присвоения.
V
был уже присвоен более ранним выражением до e
. В этом случае текущее присвоение вызовет ошибку времени компиляции. Так, мы можем прийти к заключению что, если условия соблюдает программа, которая не вызывает ошибки времени компиляции, тогда любые присвоения на V
в B
не будет фактически иметь место во время выполнения.
break
оператор, который может выйти из помеченного оператора L:S.
if
(e) S:
if
(e) S эквивалентность V [un], присвоенный после того, как S и V [un], присвоенный после e когда ложь.
if
(e) S.
if
(e) S else
T:
if
(e) S else
T эквивалентность V [un], присвоенный после того, как S и V [un], присвоенный после T.
if
(e) S else
T.
assert
e1 и к оператору assert
e1:e2:
assert
оператор.
assert
эквивалентность оператора V определенно присваивается перед assert
оператор.
assert
эквивалентность оператора V является определенно неприсвоенной перед assert
оператор и V является определенно неприсвоенным после e1 когда истина. assert
e1: e2:
switch
эквивалентность оператора все следующее является истиной: default
метка в switch
блок или V [un], присвоенный после выражения переключателя.
switch
блок, которые не начинают группу оператора блока (то есть, нет никаких меток переключателя сразу перед"}
"это заканчивает блок переключателя), или V [un], присвоенный после выражения переключателя.
switch
блок не содержит групп оператора блока, или V [un], присвоенный после последнего оператора блока последней группы оператора блока.
break
оператор, который может выйти switch
оператор. switch
оператор.
while
(e) S эквивалентность V [un], присвоенный после e, когда ложь и V [un], присвоенный перед каждым break
оператор тот, для который while
оператор является целью повреждения.
while
оператор.
while
оператор.
continue
оператор тот, для который while
оператор является продолжать целью. do
S while
(e); эквивалентность V [un], присвоенный после e, когда ложь и V [un], присвоенный перед каждым break
оператор тот, для который do
оператор является целью повреждения.
do
оператор.
do
оператор.
continue
оператор тот, для который do
оператор является продолжать целью. for
оператор (§14.14.1). Начиная с улучшенного for
(§14.14.2) оператор определяется traslation к основному for
оператор, никакие специальные правила не должны быть обеспечены для этого.
for
эквивалентность оператора оба из следующего является истиной: break
оператор тот, для который for
оператор является целью повреждения. for
эквивалентность оператора V [un], присвоенный перед for
оператор.
for
эквивалентность оператора V определенно присваивается после части инициализации for
оператор.
for
эквивалентность оператора все следующие условия содержит: for
оператор.
for
оператор, V является определенно неприсвоенным после содержавшего оператора.
continue
оператор тот, для который for
оператор является продолжать целью. for
оператор. for
эквивалентность оператора V [un], присвоенный после того, как содержавший оператор и V [un], присвоенный перед каждым continue
оператор тот, для который for
оператор является продолжать целью. for
оператор является оператором объявления локальной переменной, правила §16.2.4 применяются.
for
оператор пуст, тогда V [un], присвоенный после того, как эквивалентность части приращения V [un], присвоенный перед частью приращения.
break
, continue
, return
, или throw
оператор. Понятие, что переменная" [un] присвоена после" оператора или выражения действительно, означает, "[un], присвоенный после оператора, или выражение обычно завершается". Поскольку a break
, continue
, return
, или throw
оператор никогда обычно не завершается, он вырождено удовлетворяет это понятие.
return
оператор с выражением e или a throw
оператор с выражением e, V [un], присвоенный прежде, чем e эквивалентность V будет [un], присвоенный перед return
или throw
оператор. synchronized
(e) S эквивалентность V [un], присвоенный после S.
synchronized
(
e) S.
try
оператор, есть ли у этого a finally
блок:
try
блочная эквивалентность V [un], присвоенный перед try
оператор.
catch
блочная эквивалентность V определенно присваивается перед try
блок.
catch
блочная эквивалентность все следующие условия содержит: try
блок.
return
оператор, который принадлежит try
блок.
throw
e, который принадлежит try
блок.
assert
e1, который происходит в блоке попытки.
assert
e1: e2, который происходит в блоке попытки.
break
оператор, который принадлежит try
блок и чья цель повреждения содержит (или), try
оператор.
continue
оператор, который принадлежит try
блок и чей продолжают цель, содержит try
оператор. try
у оператора нет a finally
блок, тогда это правило также применяется:
try
эквивалентность оператора V [un], присвоенный после try
блок и V [un], присвоенный после каждого catch
блок в операторе попытки. try
у оператора действительно есть a finally
блок, тогда эти правила также применяются:
try
эквивалентность оператора по крайней мере одно из следующего является истиной: catch
блок в операторе попытки.
finally
блок.
try
эквивалентность оператора V является определенно неприсвоенной после finally
блок. finally
блочная эквивалентность V определенно присваивается перед try
оператор.
finally
блочная эквивалентность все следующие условия содержит: try
блок.
return
оператор, который принадлежит try
блок.
throw
e, который принадлежит try
блок.
assert
e1, который происходит в блоке попытки.
assert
e1: e2, который происходит в блоке попытки.
break
оператор, который принадлежит try
блок и чья цель повреждения содержит (или), try
оператор.
continue
оператор, который принадлежит try
блок и чей продолжают цель, содержит try
оператор.
catch
блок try
оператор. catch
пункт определенно присваивается (и кроме того не является определенно неприсвоенным) перед телом catch
пункт. Обсуждение
Это - то, потому что перечислимая константа является по существу статическим заключительным полем (§8.3.1.1, §8.3.1.2), который инициализируется с выражением создания экземпляра класса (§15.9).
Позвольте y быть параметром перечислимой константы, но не первым. Затем:
Обсуждение
Это должно быть четким, что, если анонимный класс неявно определяется перечислимой константой, правила раздела §16.5 применяются.
Отметьте, что нет никаких правил, которые позволили бы нам приходить к заключению, что V является определенно неприсвоенным перед инициализатором статической переменной или перечислимой константой. Мы можем неофициально прийти к заключению, что V не является определенно неприсвоенным ни перед каким инициализатором статической переменной C, но нет никакой потребности в таком правиле, которое будет утверждено явно.
Позвольте C быть классом, и позволять V быть пробеломfinal
static
задействованное поле C, объявленного в C. Затем:
static
инициализатор или static
переменный инициализатор C.
static
инициализатор или static
переменный инициализатор C кроме крайней левой эквивалентности V [un], присвоенный после предыдущей перечислимой константы, static
инициализатор или static
переменный инициализатор C. final
static
задействованное поле C, объявленного в суперклассе C. Затем:
Отметьте, что нет никаких правил, которые позволили бы нам приходить к заключению, что V является определенно неприсвоенным перед инициализатором переменной экземпляра. Мы можем неофициально прийти к заключению, что V не является определенно неприсвоенным ни перед каким инициализатором переменной экземпляра C, но нет никакой потребности в таком правиле, которое будет утверждено явно.
Позвольте C быть классом, и позволять V быть пробеломfinal
не -static
задействованное поле C, объявленного в C. Затем:
final
задействованное поле C, объявленного в суперклассе C. Затем:
Содержание | Предыдущий | Следующий | Индекс | Спецификация языка Java Третий Выпуск |
Авторское право © 1996-2005 Sun Microsystems, Inc. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления через нашу