Что означает ошибка «Не удается найти символ» или «Не удается разрешить символ»?

395

Пожалуйста, объясните следующее об ошибках «Не удается найти символ» и «Не удается разрешить символ»:

  • Что они имеют в виду?
  • Какие вещи могут вызвать их?
  • Как программист исправляет их?

Этот вопрос предназначен для получения исчерпывающих ответов на вопросы об этих распространенных ошибках компиляции в Java.

Стивен С
источник

Ответы:

417

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. Неясные причины

Вот пара случаев, когда «Не удается найти символ» кажется необъяснимым ... пока вы не посмотрите ближе.

  1. Неверные зависимости : если вы используете IDE или инструмент сборки, который управляет путем сборки и зависимостями проекта, возможно, вы допустили ошибку с зависимостями; например, исключил зависимость или выбрал неправильную версию. Если вы используете инструмент сборки (Ant, Maven, Gradle и т. Д.), Проверьте файл сборки проекта. Если вы используете IDE, проверьте конфигурацию пути сборки проекта.

  2. Вы не перекомпилируете : иногда случается, что новые Java-программисты не понимают, как работает цепочка инструментов Java, или не реализуют повторяемый «процесс сборки»; например, используя IDE, Ant, Maven, Gradle и так далее. В такой ситуации программист может в итоге гоняться за хвостом в поисках иллюзорной ошибки, которая на самом деле вызвана неправильной перекомпиляцией кода и тому подобным ...

  3. Проблема с более ранней сборкой . Возможно, что более ранняя сборка завершилась неудачно, что дало JAR-файл с отсутствующими классами. Такой сбой, как правило, будет замечен, если вы используете инструмент сборки. Однако, если вы получаете файлы JAR от кого-то другого, вы зависите от их правильной сборки и замечаете ошибки. Если вы подозреваете это, используйте tar -tvfдля просмотра содержимого подозрительного файла JAR.

  4. Проблемы IDE : Люди сообщали о случаях, когда их IDE запутывается, и компилятор в IDE не может найти класс, который существует ... или обратная ситуация.

    • Это может произойти, если IDE была настроена с неверной версией JDK.

    • Это может произойти, если кэши IDE не синхронизируются с файловой системой. Есть конкретные способы, чтобы исправить это.

    • Это может быть ошибка IDE. Например, @Joel Costigliola описывает сценарий, в котором Eclipse неправильно обрабатывает «тестовое» дерево Maven: см. Этот ответ .

  5. Проблемы с Android : когда вы программируете для Android и у вас есть ошибки «Не удается найти символ» R, имейте в виду, что Rсимволы определены context.xmlфайлом. Убедитесь, что ваш context.xmlфайл правильный и в правильном месте, и что соответствующий Rфайл класса был сгенерирован / скомпилирован. Обратите внимание, что символы Java чувствительны к регистру, поэтому соответствующие идентификаторы XML также чувствительны к регистру.

    Другие ошибки символов в Android, скорее всего, связаны с ранее упомянутыми причинами; например, отсутствующие или неправильные зависимости, неправильные имена пакетов, методы или поля, которых нет в конкретной версии API, ошибки правописания / набора текста и т. д.

  6. Переопределение системных классов : я видел случаи, когда компилятор жалуется, что substringэто неизвестный символ в чем-то вроде следующего

    String s = ...
    String s1 = s.substring(1);

    Оказалось, что программист создал свою собственную версию Stringи что его версия класса не определила substringметоды.

    Урок: не определяйте свои собственные классы с такими же именами, как у обычных библиотечных классов!

  7. Гомоглифы: если вы используете кодировку UTF-8 для своих исходных файлов, возможно, что идентификаторы выглядят одинаково, но на самом деле они разные, поскольку содержат гомоглифы. Смотрите эту страницу для получения дополнительной информации.

    Вы можете избежать этого, ограничив себя ASCII или Latin-1 в качестве кодировки исходного файла и используя Java- \uxxxxэкранирование для других символов.


1 - Если, возможно, вы бы увидеть это в исключение во время выполнения или сообщение об ошибке, то либо вы настроили IDE для кода выполнения с ошибками компиляции, или приложение генерирует и компиляции кода .. во время выполнения.

2 - Три основных принципа гражданского строительства: вода не течет в гору, доска сильнее на ее стороне, и вы не можете толкнуть веревку .

Стивен С
источник
У меня была другая ситуация, когда произошла эта ошибка компиляции, в то время как Eclipse не увидела проблему: два класса с зависимостями, определенными соответственно в другом классе. В моем случае у меня был enum, реализующий интерфейс, определенный в классе, где я по глупости уже использовал enum.
Джоги
Несколько похоже на комментарий выше, когда я компилирую и запускаю свою программу из Eclipse, это не проблема. При компиляции из консоли возникает множество ошибок «Cannot find symbol», часто связанных с последним элементом в импорте. Я понятия не имею, что вызывает это, поскольку в коде нет ничего плохого на самом деле.
Андрес Штадельманн
Другая проблема заключается в том, что IDE могут «интерпретировать» другие ошибки в этой категории. Например, printlnв System.out.printlnслучае размещения на уровне класса в стандартном компиляторе мы получим <identifier> expected( demo ), но в IntelliJ мы увидим Cannot resolve symbol 'println'( demo ).
Пшемо
Ух ты. Я бы назвал это ошибкой компилятора.
Стивен С.
23

Вы также получите эту ошибку, если забудете new:

String s = String();

против

String s = new String();

потому что вызов без newключевого слова попытается найти (локальный) метод, вызываемый Stringбез аргументов - и эта сигнатура метода, вероятно, не определена.

thinkterry
источник
14

Еще один пример «Переменная выходит за рамки»

Как я уже видел подобные вопросы несколько раз, может быть, это еще один пример того, что незаконно, даже если это может показаться нормальным.

Рассмотрим этот код:

if(somethingIsTrue()) {
  String message = "Everything is fine";
} else {
  String message = "We have an error";
}
System.out.println(message);

Это неверный код. Потому что ни одна из названных переменных messageне видна за пределами их соответствующей области видимости - которые {}в этом случае будут окружающими скобками .

Вы можете сказать: «Но переменная с именем сообщения определяется в любом случае - так сообщение будет определено после if».

Но ты ошибаешься.

Java не имеет операторов free()или delete, поэтому она должна полагаться на отслеживание области видимости переменных, чтобы выяснить, когда переменные больше не используются (вместе со ссылками на эти переменные причины).

Особенно плохо, если ты думал, что сделал что-то хорошее. Я видел такую ​​ошибку после «оптимизации» кода, например:

if(somethingIsTrue()) {
  String message = "Everything is fine";
  System.out.println(message);
} else {
  String message = "We have an error";
  System.out.println(message);
}

«О, есть дублированный код, давайте вытянем эту общую строку» -> и вот оно.

Наиболее распространенный способ решения этой проблемы - это предварительно назначить значения else для имен переменных во внешней области, а затем переназначить, если:

String message = "We have an error";
if(somethingIsTrue()) {
  message = "Everything is fine";
} 
System.out.println(message);
январь
источник
4
«В Java нет операторов free () или delete, поэтому она должна полагаться на отслеживание области видимости переменных, чтобы выяснить, когда переменные больше не используются (вместе со ссылками на эти переменные причины)». - Хотя это правда, это не актуально. C и C ++ имеют операторы free / delete соответственно, но эквивалентный код C / C ++ вашим примерам будет недопустимым. Блоки C и C ++ ограничивают область видимости переменных, как в Java. Фактически, это верно для большинства "блочно-структурированных" языков.
Стивен С
1
Лучшее решение для кода, который присваивает разные значения каждой ветви, - это использовать пустоеfinal объявление переменной.
Даниэль Приден
10

Один из способов получить эту ошибку в Eclipse:

  1. Определите класс Aв src/test/java.
  2. Определим еще один класс Bв src/main/javaкоторый использует класс A.

Результат: Eclipse скомпилирует код, но maven выдаст «Cannot find symbol».

Основная причина: Eclipse использует комбинированный путь сборки для основного и тестового деревьев. К сожалению, он не поддерживает использование разных путей сборки для разных частей проекта Eclipse, чего требует Maven.

Решение :

  1. Не определяйте свои зависимости таким образом; т.е. не делайте эту ошибку.
  2. Регулярно создавайте свою кодовую базу, используя Maven, чтобы вы могли уловить эту ошибку на ранней стадии. Один из способов сделать это - использовать CI-сервер.
Джоэл Костиглиола
источник
Какое решение для этого?
2
все, что вы используете в src / main / java, должно быть определено в src / main / java или в любых зависимостях компиляции / времени выполнения (не в тестовых зависимостях).
Джоэл Костиглиола
5

«Не удается найти» означает, что компилятор, который не может найти подходящую переменную, метод, класс и т. Д. Если вы получили этот массаж ошибок, в первую очередь вы хотите найти строку кода, в которой получите массаж ошибок… И тогда вы будете возможность найти, какую переменную, метод или класс не определили до его использования. После подтверждения инициализации эту переменную, метод или класс можно использовать для последующего использования ... Рассмотрим следующий пример.

Я создам демонстрационный класс и напечатаю имя ...

class demo{ 
      public static void main(String a[]){
             System.out.print(name);
      }
}

Теперь посмотрим на результат ..

введите описание изображения здесь

Эта ошибка говорит, что «имя переменной не может быть найдено». Определение и инициализация значения для переменной «имя» можно отменить эту ошибку .. На самом деле, как это,

class demo{ 
      public static void main(String a[]){

             String name="smith";

             System.out.print(name);
      }
}

Теперь посмотрим на новый вывод ...

введите описание изображения здесь

Хорошо, успешно удалось решить эту ошибку. В то же время, если вы можете получить что-то «не могу найти метод» или «не можете найти класс», сначала определите класс или метод, а затем используйте это ..

GT_hash
источник
3

Если вы получаете эту ошибку в сборке где-то еще, в то время как ваша IDE говорит, что все отлично, то убедитесь, что вы используете одинаковые версии Java в обоих местах.

Например, Java 7 и Java 8 имеют разные API, поэтому вызов несуществующего API в более старой версии Java может вызвать эту ошибку.

Джонатан Лин
источник
2

Я тоже получал эту ошибку. (за что я погуглил и меня направили на эту страницу)

Проблема: я вызывал статический метод, определенный в классе проекта A, из класса, определенного в другом проекте B. Я получил следующую ошибку:

error: cannot find symbol

Решение: я решил эту проблему, сначала создав проект, в котором определяется метод, а затем проект, из которого вызывался метод.

Дивья Хосе
источник
Да, это может произойти, если вы решите переместить, например, какую-нибудь повторно используемую функцию из вашего текущего пакета в ваш пакет общих утилит, но затем вы забудете скомпилировать свой общий пакет перед вызовом функции из текущего пакета.
здание
2

Если eclipse Java build path сопоставлен с 7, 8 и в свойствах Project pom.xml Maven java.version упоминается более высокая версия Java (9,10,11 и т. Д.,), Чем 7,8, вам необходимо обновить ее в pom. XML-файл.

В Eclipse, если Java сопоставлена ​​с Java версии 11, а в pom.xml она сопоставлена ​​с Java версии 8. Обновите поддержку Eclipse до Java 11, выполнив следующие шаги в справке IDE eclipse -> Установить новое программное обеспечение ->

Вставьте следующую ссылку http://download.eclipse.org/eclipse/updates/4.9-P-builds при работе с

или

Добавить (откроется всплывающее окно) ->

Name:Поддержка Java 11 Location: http://download.eclipse.org/eclipse/updates/4.9-P-builds

затем обновите версию Java в свойствах Maven файла pom.xml, как показано ниже

<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>

Наконец, щелкните правой кнопкой мыши на проекте Debug as -> Maven clean, шаги сборки Maven.

УдайКиран Пулипати
источник
2

РЕШИТЬ

Выберите Build -> Rebuild Project, чтобы решить его

Аджай
источник
1
Это очень многое зависит, и обычно это не так.
Maarten Bodewes
1

Как уже упоминалось выше, могут быть разные сценарии. Несколько вещей, которые помогли мне решить эту проблему.

  1. Если вы используете IntelliJ

    File -> 'Invalidate Caches/Restart'

ИЛИ

  1. Указанный класс находился в другом проекте, и эта зависимость не была добавлена ​​в файл сборки Gradle моего проекта. Поэтому я добавил зависимость, используя

    compile project(':anotherProject')

и это сработало. НТН!

AVP
источник
1

Вы скомпилировали свой код с помощью maven compile, а затем использовали maven test, чтобы он работал нормально. Теперь, если вы изменили что-то в своем коде, а затем без компиляции запускаете его, вы получите эту ошибку.

Решение: Снова скомпилируйте и запустите тест. Для меня это сработало так.

АНИЛ КУМАР
источник
1

В моем случае - мне пришлось выполнить следующие операции:

  1. Переместить context.xmlфайл из src/java/packageв resourceкаталог (IntelliJ IDE)
  2. Чистый targetкаталог.
ВИПИН КУМАР
источник
Перемещение файла без заботы о ссылках может вызвать эту ошибку. Я уже встречал это. Просто перезагрузите Git и осторожно переместите снова, ошибку решите.
Хай Хом Хён
0

Для подсказок, посмотрите ближе на имя класса, которое выдает ошибку и номер строки, например: Ошибка компиляции [ОШИБКА] \ Applications \ xxxxx.java: [44,30] Ошибка: не удается найти символ

Еще одна причина - неподдерживаемый метод для java версии, скажем, jdk7 против 8. Проверьте ваш% JAVA_HOME%

ударник
источник
Это говорит о том же, что и другие ответы.
Стивен С