0. Есть ли разница между двумя ошибками?
На самом деле, нет. «Не удается найти символ» и «Не удается найти символ» означают одно и то же. Некоторые компиляторы Java используют одну фразу, а некоторые - другую.
1. Что означает ошибка «Не удается найти символ»?
Во-первых, это ошибка компиляции 1 . Это означает, что либо проблема в исходном коде Java, либо в способе его компиляции.
Ваш исходный код Java состоит из следующих вещей:
- Ключевые слова: как
true
, false
, class
, while
и так далее.
- Литералы: как
42
и 'X'
и "Hi mum!"
.
- Операторы и другие не буквенно-цифровые маркеры: как
+
, =
, {
и так далее.
- Идентификаторы: как
Reader
, i
, toString
, processEquibalancedElephants
и так далее.
- Комментарии и пробелы.
Ошибка «Не удается найти символ» связана с идентификаторами. Когда ваш код скомпилирован, компилятору необходимо выяснить, что означает каждый идентификатор в вашем коде.
Ошибка «Не удается найти символ» означает, что компилятор не может этого сделать. Похоже, ваш код ссылается на то, что компилятор не понимает.
2. Что может вызвать ошибку «Не удается найти символ»?
Как первый заказ, есть только одна причина. Компилятор просмотрел все места, где должен быть определен идентификатор , и не смог найти определение. Это может быть вызвано рядом вещей. Общие из них следующие:
- Для идентификаторов в целом:
- Возможно, вы написали имя неправильно; т.е.
StringBiulder
вместо StringBuilder
. Java не может и не будет пытаться компенсировать плохие орфографические или опечатки.
- Возможно, вы ошиблись; т.е.
stringBuilder
вместо StringBuilder
. Все идентификаторы Java чувствительны к регистру.
- Возможно, вы неправильно использовали подчеркивание; то есть
mystring
и my_string
разные. (Если вы будете придерживаться правил стиля Java, вы будете в значительной степени защищены от этой ошибки ...)
- Возможно, вы пытаетесь использовать то, что было объявлено «где-то еще»; то есть в другом контексте, где вы явно указали компилятору искать. (Другой класс? Другая область применения? Другой пакет? Другая база кода?)
- Для идентификаторов, которые должны ссылаться на переменные:
- Возможно, вы забыли объявить переменную.
- Возможно, объявление переменной выходит за рамки того, что вы пытались использовать. (См. Пример ниже)
Для идентификаторов, которые должны быть именами методов или полей:
- Возможно, вы пытаетесь сослаться на унаследованный метод или поле, которое не было объявлено в классах или интерфейсах родителя / предка.
- Возможно, вы пытаетесь сослаться на метод или поле, которые не существуют (т.е. не были объявлены) в используемом вами типе; например
"someString".push()
2 .
- Возможно, вы пытаетесь использовать метод в качестве поля или наоборот; например
"someString".length
или someArray.length()
.
Возможно, вы ошибочно работаете с массивом, а не с элементом массива; например
String strings[] = ...
if (strings.charAt(3)) { ... }
// maybe that should be 'strings[0].charAt(3)'
Для идентификаторов, которые должны быть именами классов:
- Возможно, вы забыли импортировать класс.
- Возможно, вы использовали импорт «звезда», но класс не определен ни в одном из импортированных вами пакетов.
Возможно, вы забыли, new
как в:
String s = String(); // should be 'new String()'
Для случаев, когда тип или экземпляр не имеют члена, которого вы ожидали иметь:
- Возможно, вы объявили вложенный класс или универсальный параметр, который скрывает тип, который вы хотели использовать.
- Возможно, вы отслеживаете статическую переменную или переменную экземпляра.
- Возможно, вы импортировали неправильный тип; например, из-за завершения IDE или автокоррекции.
- Возможно, вы используете (компилируете) не ту версию API.
- Возможно, вы забыли привести свой объект к соответствующему подклассу.
Проблема часто заключается в сочетании вышеперечисленного. Например, может быть, вы импортировали "звезду", java.io.*
а затем пытались использовать Files
класс ... которого java.nio
нет java.io
. Или , может быть , вы имели в виду , чтобы написать File
... который является классом в java.io
.
Вот пример того, как неправильная область видимости переменной может привести к ошибке «Не удается найти символ»:
List<String> strings = ...
for (int i = 0; i < strings.size(); i++) {
if (strings.get(i).equalsIgnoreCase("fnord")) {
break;
}
}
if (i < strings.size()) {
...
}
Это даст «Не удается найти символ» ошибка для i
в if
заявлении. Хотя мы ранее заявили i
, что заявление только в области видимости для for
заявления и его тела. Ссылка i
в if
заявлении не может видеть эту декларацию i
. Это выходит за рамки .
(Соответствующее исправление здесь может заключаться в том, чтобы переместить if
инструкцию внутри цикла или объявить i
до начала цикла.)
Вот пример, который вызывает недоумение, когда опечатка приводит к, казалось бы, необъяснимой ошибке «Не удается найти символ»:
for (int i = 0; i < 100; i++); {
System.out.println("i is " + i);
}
Это даст вам ошибку компиляции в println
вызове, сказав, что i
не может быть найден. Но (я слышу, вы говорите), я это объявил!
Проблема в хитрой точке с запятой ( ;
) перед {
. Синтаксис языка Java определяет точку с запятой в этом контексте как пустой оператор . Пустой оператор становится телом for
цикла. Так что код на самом деле означает это:
for (int i = 0; i < 100; i++);
// The previous and following are separate statements!!
{
System.out.println("i is " + i);
}
{ ... }
Блок НЕ тело for
цикла, и , следовательно, предыдущая декларация i
в for
утверждение из области видимости в блоке.
Вот еще один пример ошибки «Не удается найти символ», вызванной опечаткой.
int tmp = ...
int res = tmp(a + b);
Несмотря на предыдущей декларации, то tmp
в tmp(...)
выражении является ошибочным. Компилятор будет искать вызываемый метод tmp
и не найдет его. Ранее объявленное tmp
находится в пространстве имен для переменных, а не в пространстве имен для методов.
В приведенном мной примере программист фактически исключил оператор. Он хотел написать следующее:
int res = tmp * (a + b);
Есть еще одна причина, по которой компилятор может не найти символ, если вы компилируете из командной строки. Возможно, вы просто забыли скомпилировать или перекомпилировать какой-то другой класс. Например, если у вас есть классы Foo
и Bar
где Foo
используется Bar
. Если вы никогда не компилировали Bar
и запускали javac Foo.java
, вы наверняка обнаружите, что компилятор не может найти символ Bar
. Простой ответ - собрать Foo
и собрать Bar
вместе; например javac Foo.java Bar.java
или javac *.java
. Или лучше использовать инструмент сборки Java; например муравей, мавен, градль и так далее.
Есть и другие, более неясные причины ... о которых я расскажу ниже.
3. Как я могу исправить эти ошибки?
Вообще говоря, вы начинаете путем выяснить , что вызвало ошибку компиляции.
- Посмотрите на строку в файле, обозначенную сообщением об ошибке компиляции.
- Определите символ, о котором говорится в сообщении об ошибке.
- Выясните, почему компилятор говорит, что он не может найти символ; смотри выше!
Затем вы думаете о том, что ваш код должен говорить. Затем, наконец, вы решаете, какое исправление необходимо внести в исходный код, чтобы сделать то, что вы хотите.
Обратите внимание, что не каждая «коррекция» является правильной. Учти это:
for (int i = 1; i < 10; i++) {
for (j = 1; j < 10; j++) {
...
}
}
Предположим, что компилятор говорит «Не удается найти символ» для j
. Есть много способов, которыми я мог бы «исправить» это:
- Я мог бы изменить внутреннее
for
на for (int j = 1; j < 10; j++)
- возможно, правильно.
- Я мог бы добавить объявление для
j
перед внутренним for
циклом, или внешний for
цикл - возможно, правильный.
- Я мог бы изменить ,
j
чтобы i
во внутреннем for
цикле - вероятно , неправильно!
- и так далее.
Дело в том, что вам нужно понять, что ваш код пытается сделать, чтобы найти правильное решение.
4. Неясные причины
Вот пара случаев, когда «Не удается найти символ» кажется необъяснимым ... пока вы не посмотрите ближе.
Неверные зависимости : если вы используете IDE или инструмент сборки, который управляет путем сборки и зависимостями проекта, возможно, вы допустили ошибку с зависимостями; например, исключил зависимость или выбрал неправильную версию. Если вы используете инструмент сборки (Ant, Maven, Gradle и т. Д.), Проверьте файл сборки проекта. Если вы используете IDE, проверьте конфигурацию пути сборки проекта.
Вы не перекомпилируете : иногда случается, что новые Java-программисты не понимают, как работает цепочка инструментов Java, или не реализуют повторяемый «процесс сборки»; например, используя IDE, Ant, Maven, Gradle и так далее. В такой ситуации программист может в итоге гоняться за хвостом в поисках иллюзорной ошибки, которая на самом деле вызвана неправильной перекомпиляцией кода и тому подобным ...
Проблема с более ранней сборкой . Возможно, что более ранняя сборка завершилась неудачно, что дало JAR-файл с отсутствующими классами. Такой сбой, как правило, будет замечен, если вы используете инструмент сборки. Однако, если вы получаете файлы JAR от кого-то другого, вы зависите от их правильной сборки и замечаете ошибки. Если вы подозреваете это, используйте tar -tvf
для просмотра содержимого подозрительного файла JAR.
Проблемы IDE : Люди сообщали о случаях, когда их IDE запутывается, и компилятор в IDE не может найти класс, который существует ... или обратная ситуация.
Это может произойти, если IDE была настроена с неверной версией JDK.
Это может произойти, если кэши IDE не синхронизируются с файловой системой. Есть конкретные способы, чтобы исправить это.
Это может быть ошибка IDE. Например, @Joel Costigliola описывает сценарий, в котором Eclipse неправильно обрабатывает «тестовое» дерево Maven: см. Этот ответ .
Проблемы с Android : когда вы программируете для Android и у вас есть ошибки «Не удается найти символ» R
, имейте в виду, что R
символы определены context.xml
файлом. Убедитесь, что ваш context.xml
файл правильный и в правильном месте, и что соответствующий R
файл класса был сгенерирован / скомпилирован. Обратите внимание, что символы Java чувствительны к регистру, поэтому соответствующие идентификаторы XML также чувствительны к регистру.
Другие ошибки символов в Android, скорее всего, связаны с ранее упомянутыми причинами; например, отсутствующие или неправильные зависимости, неправильные имена пакетов, методы или поля, которых нет в конкретной версии API, ошибки правописания / набора текста и т. д.
Переопределение системных классов : я видел случаи, когда компилятор жалуется, что substring
это неизвестный символ в чем-то вроде следующего
String s = ...
String s1 = s.substring(1);
Оказалось, что программист создал свою собственную версию String
и что его версия класса не определила substring
методы.
Урок: не определяйте свои собственные классы с такими же именами, как у обычных библиотечных классов!
Гомоглифы: если вы используете кодировку UTF-8 для своих исходных файлов, возможно, что идентификаторы выглядят одинаково, но на самом деле они разные, поскольку содержат гомоглифы. Смотрите эту страницу для получения дополнительной информации.
Вы можете избежать этого, ограничив себя ASCII или Latin-1 в качестве кодировки исходного файла и используя Java- \uxxxx
экранирование для других символов.
1 - Если, возможно, вы бы увидеть это в исключение во время выполнения или сообщение об ошибке, то либо вы настроили IDE для кода выполнения с ошибками компиляции, или приложение генерирует и компиляции кода .. во время выполнения.
2 - Три основных принципа гражданского строительства: вода не течет в гору, доска сильнее на ее стороне, и вы не можете толкнуть веревку .