Що означає помилка "Неможливо знайти символ" або "Неможливо вирішити символ"?


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 \uxxxxEscape для інших символів.


1 - Якщо, можливо, ви б побачити це в виняток у час виконання або повідомлення про помилку, то або ви налаштували IDE для коду виконання з помилками компіляції, або додаток генерує і компіляції коду .. під час виконання.

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


У мене була інша ситуація, коли ця компіляційна помилка сталася, коли затемнення не бачило проблеми: два класи із залежностями, визначеними відповідно у іншому класі. У моєму випадку я мав enum, реалізуючи інтерфейс, визначений у класі, де я дурно вже використовував enum.
Jogi

Дещо подібне до коментаря вище, коли я компілюю і запускаю свою програму з Eclipse, вона не працює. Компілюючи його з консолі, виникає маса цих помилок "Неможливо знайти символ", часто пов'язаних з останнім елементом імпорту. Я поняття не маю, що це викликає, оскільки в коді насправді немає нічого поганого.
Андрес Штадельман

Ще одна проблема полягає в тому, що IDE можуть "інтерпретувати" інші помилки до цієї категорії. Наприклад , printlnв System.out.printlnразі поміщається на рівні класу за стандартом компілятор дасть нам <identifier> expected( демо ) , але в IntelliJ ми побачимо Cannot resolve symbol 'println'( демо ).
Пшемо

Ого. Я б назвав цю помилку компілятора.
Стівен C

23

Ви також отримаєте цю помилку, якщо забудете new:

String s = String();

проти

String s = new String();

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


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);
}

"О, є дублюваний код, давайте виведемо цю загальну лінію" -> і там вона.

Найпоширенішим способом боротьби з таким видом проблеми-області буде попереднє призначення інших значень імен змінних у зовнішній області та переназначення, якщо:

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

4
"У Java немає вільних () або операторів видалення, тому вона повинна спиратися на відстеження змінної області, щоб з'ясувати, коли змінні більше не використовуються (разом із посиланнями на ці змінні причини)." - Хоча це правда, це не стосується. C і C ++ мають оператори вільного / видалення відповідно, і все ж еквівалентний код C / C ++ для ваших прикладів був би незаконним. Блоки C і C ++ обмежують область змінних так само, як у Java. Насправді це справедливо для більшості "блокованих" мов.
Стівен C

1
Кращим рішенням для коду, який присвоює різне значення для кожної гілки, є використання пустогоfinal оголошення змінної.
Даніель Приден

10

Один із способів отримати цю помилку в Eclipse:

  1. Визначте клас Aу src/test/java.
  2. Визначте інший клас, Bу src/main/javaякому використовується клас A.

Результат: Eclipse скомпонує код, але maven видасть "Неможливо знайти символ".

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

Рішення:

  1. Не визначайте свої залежності таким чином; тобто не робіть цієї помилки.
  2. Регулярно будуйте свою кодову базу за допомогою Maven, щоб ви мали помилку раніше. Один із способів зробити це - використовувати сервер CI.

Яке рішення для цього?

2
все, що ви використовуєте в src / main / java, потрібно визначати в src / main / java або в будь-яких залежностях компіляції / виконання (не тестові залежності).
Joel Costigliola

5

"Не можу знайти" означає, що компілятор, який не може знайти відповідну змінну, метод, клас тощо ... якщо у вас масаж помилок, перш за все ви хочете знайти кодову рядок, де отримуєте масаж помилок. зможете знайти, яку змінну, метод чи клас не визначили перед її використанням. Після підтвердження ініціалізуйте, що змінна, метод або клас можуть бути використані для подальшого вимагання ... Розгляньте наступний приклад.

Я створять демонстраційний клас та надрукую ім'я ...

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

А тепер подивіться на результат ..

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

Ця помилка говорить: "Ім'я змінної не можна знайти". Визначивши та ініціалізуючи значення для змінної 'name', цю помилку можна скасувати. Власне так,

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

             String name="smith";

             System.out.print(name);
      }
}

Тепер подивіться на новий вихід ...

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

Добре Успішно вирішено цю помилку. У той же час, якщо ви могли отримати щось "не можете знайти метод" або "не можете знайти клас", спочатку визначте клас чи метод, а потім скористайтеся цією ..


3

Якщо ви отримуєте цю помилку в збірці десь в іншому місці, а ваш IDE каже, що все ідеально, тоді перевірте, чи використовуєте ви однакові версії Java в обох місцях.

Наприклад, Java 7 та Java 8 мають різні API, тому виклик неіснуючого API у старій версії Java спричинить цю помилку.


2

Я теж отримував цю помилку. (для чого я гуглив, і мене направили на цю сторінку)

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

error: cannot find symbol

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


Так, це може статися, якщо ви вирішите перенести якусь функцію для багаторазового використання з вашого поточного пакета до вашого загального пакета утилітів, наприклад, але тоді ви забудете скласти ваш загальний пакет, перш ніж викликати функцію з вашого поточного пакету.
buildingKofi

2

Якщо затемнення шляху побудови Java відображається на 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 затемнення -> Встановити нове програмне забезпечення ->

Вставте наступне посилання http://download.eclipse.org/eclipse/updates/4.9-P-builds at Work With

або

Додати (відкриється вікно спливаючого вікна) ->

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>

Нарешті, клацніть правою кнопкою миші на проект Налагодження як -> Maven clean, Maven build steps


2

РЕШЕНО

Виберіть Збірка -> Проект відновлення вирішить це


1
Це дуже залежить і зазвичай це не так.
Maarten Bodewes

1

Можуть бути різні сценарії, як згадували люди вище. Кілька речей, які допомогли мені вирішити це.

  1. Якщо ви використовуєте IntelliJ

    File -> 'Invalidate Caches/Restart'

АБО

  1. Клас, на який посилався, був в іншому проекті, і ця залежність не була додана до файлу збірки Gradle мого проекту. Тому я додав залежність, використовуючи

    compile project(':anotherProject')

і це спрацювало. HTH!


1

Ви склали код за допомогою Maven compile, а потім використали тест Maven, щоб запустити його добре. Тепер, якщо ви змінили щось у своєму коді, а потім, не компілюючи, ви запускаєте його, ви отримаєте цю помилку.

Рішення: знову скомпілюйте його та запустіть тест. Для мене це працювало так.


1

У моєму випадку - мені довелося виконувати нижче операції:

  1. Перемістити context.xmlфайл з src/java/packageв resourceкаталог (IntelliJ IDE)
  2. Чистий targetкаталог.

Переміщення файлу без піклування про посилання може призвести до цієї помилки. Я вже зустрічався з цим. Просто скиньте на Git і обережно Перейдіть знову, помилка.
Huy Hóm Hỉnh

0

Для підказки ознайомтеся ближче до назви класу, що видає помилку, та номера рядка, наприклад: Помилка компіляції [ПОМИЛКА] \ програми \ xxxxx.java: [44,30] Помилка: не вдається знайти символ

Ще одна причина - це непідтримуваний метод версії для Java, скажімо, jdk7 vs 8. Перевірте свій% JAVA_HOME%


Це просто те саме, що і інші відповіді.
Стівен С
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.