0. Czy jest jakaś różnica między tymi dwoma błędami?
Nie całkiem. „Nie można znaleźć symbolu” i „Nie można rozwiązać symbolu” oznaczają to samo. Niektóre kompilatory Java używają jednej frazy, a niektóre drugiej.
1. Co oznacza błąd „Nie można znaleźć symbolu”?
Po pierwsze, jest to błąd kompilacji 1 . Oznacza to, że albo występuje problem w kodzie źródłowym Java, albo jest problem ze sposobem jego kompilacji.
Twój kod źródłowy Java składa się z następujących rzeczy:
- Słowa kluczowe: jak
true
, false
, class
, while
, i tak dalej.
- Literówki: jak
42
i 'X'
i "Hi mum!"
.
- Operatorów i inne tokeny niealfanumeryczne: jak
+
, =
, {
i tak dalej.
- Identyfikatory: jak
Reader
, i
, toString
, processEquibalancedElephants
, i tak dalej.
- Komentarze i białe znaki.
Błąd „Nie można znaleźć symbolu” dotyczy identyfikatorów. Po skompilowaniu kodu kompilator musi ustalić, co oznacza każdy identyfikator w kodzie.
Błąd „Nie można znaleźć symbolu” oznacza, że kompilator nie może tego zrobić. Twój kod wydaje się odnosić do czegoś, czego kompilator nie rozumie.
2. Co może powodować błąd „Nie można znaleźć symbolu”?
Jako pierwsze zamówienie istnieje tylko jedna przyczyna. Kompilator szukał we wszystkich miejscach, w których należy zdefiniować identyfikator , i nie mógł znaleźć definicji. Może to być spowodowane wieloma rzeczami. Najczęstsze z nich są następujące:
- W przypadku identyfikatorów ogólnie:
- Być może wpisałeś to imię niepoprawnie; tj.
StringBiulder
zamiast StringBuilder
. Java nie może i nie będzie próbowała kompensować błędów pisowni lub pisania.
- Być może źle zrozumiałeś sprawę; tj.
stringBuilder
zamiast StringBuilder
. Wszystkie identyfikatory Java uwzględniają wielkość liter.
- Być może niewłaściwie użyłeś podkreślenia; tj.
mystring
i my_string
są różne. (Jeśli będziesz przestrzegać zasad stylu Java, będziesz w dużej mierze chroniony przed tym błędem ...)
- Być może próbujesz użyć czegoś, co zostało zadeklarowane „gdzie indziej”; tzn. w innym kontekście niż to, w którym niejawnie powiedziałeś kompilatorowi, aby szukał. (Inna klasa? Inny zakres? Inny pakiet? Inna baza kodu?)
- W przypadku identyfikatorów, które powinny odnosić się do zmiennych:
- Być może zapomniałeś zadeklarować zmienną.
- Być może deklaracja zmiennej jest poza zakresem w punkcie, w którym próbujesz jej użyć. (Patrz przykład poniżej)
W przypadku identyfikatorów, które powinny być nazwami metod lub pól:
- Być może próbujesz odwołać się do odziedziczonej metody lub pola, które nie zostało zadeklarowane w klasach lub interfejsach nadrzędnych / przodków.
- Być może próbujesz odwołać się do metody lub pola, które nie istnieją (tj. Nie zostały zadeklarowane) w typie, którego używasz; np .
"someString".push()
2 .
- Być może próbujesz użyć metody jako pola lub odwrotnie; np .
"someString".length
lub someArray.length()
.
Być może omyłkowo operujesz na tablicy, a nie na elemencie tablicy; na przykład
String strings[] = ...
if (strings.charAt(3)) { ... }
// maybe that should be 'strings[0].charAt(3)'
W przypadku identyfikatorów, które powinny być nazwami klas:
W przypadkach, gdy typ lub instancja nie wydaje się mieć członka, którego się spodziewałeś, że ma:
- Być może zadeklarowałeś zagnieżdżoną klasę lub parametr ogólny, który zaciemnia typ, którego zamierzałeś użyć.
- Być może przesłaniasz zmienną statyczną lub instancję.
- Być może zaimportowałeś zły typ; np. z powodu zakończenia IDE lub autokorekty.
- Być może używasz (kompilujesz przeciwko) niewłaściwej wersji interfejsu API.
- Być może zapomniałeś rzucić obiekt na odpowiednią podklasę.
Problem jest często kombinacją powyższych. Na przykład może „zaimportowałeś” gwiazdkę, java.io.*
a następnie próbowałeś użyć Files
klasy ... której java.nio
nie ma java.io
. A może chciałeś napisać File
... w której jest klasa java.io
.
Oto przykład, w jaki sposób niepoprawne określanie zakresu zmiennych może prowadzić do błędu „Nie można znaleźć symbolu”:
List<String> strings = ...
for (int i = 0; i < strings.size(); i++) {
if (strings.get(i).equalsIgnoreCase("fnord")) {
break;
}
}
if (i < strings.size()) {
...
}
To spowoduje błąd „Nie można znaleźć symbolu” i
w if
instrukcji. Choć wcześniej oświadczył i
, że deklaracja jest tylko w zakresie do for
rachunku i jego ciała. Odwołanie i
w if
oświadczeniu nie widzi tej deklaracji i
. To jest poza zakresem .
(Odpowiednią poprawką może być tutaj przeniesienie if
instrukcji do pętli lub zadeklarowanie i
przed rozpoczęciem pętli).
Oto przykład, który powoduje zdziwienie, w którym literówka prowadzi do pozornie niewytłumaczalnego błędu „Nie można znaleźć symbolu”:
for (int i = 0; i < 100; i++); {
System.out.println("i is " + i);
}
Spowoduje to błąd kompilacji w println
wywołaniu, mówiąc, że i
nie można go znaleźć. Ale (słyszę, jak mówisz) ogłosiłem to!
Problemem jest sprytny średnik ( ;
) przed {
. Składnia języka Java definiuje w tym kontekście średnik jako pustą instrukcję . Pusta instrukcja staje się wówczas ciałem for
pętli. Ten kod w rzeczywistości oznacza to:
for (int i = 0; i < 100; i++);
// The previous and following are separate statements!!
{
System.out.println("i is " + i);
}
{ ... }
Blok nie jest ciało for
pętli, a więc poprzednia deklaracja i
w for
stwierdzenie jest poza zakresem w bloku.
Oto kolejny przykład błędu „Nie można znaleźć symbolu” spowodowanego literówką.
int tmp = ...
int res = tmp(a + b);
Pomimo wcześniejszego zgłoszenia, tmp
w tmp(...)
wyrażeniu jest błędna. Kompilator wyszuka metodę o nazwie tmp
i jej nie znajdzie. Poprzednio zadeklarowany tmp
jest w przestrzeni nazw dla zmiennych, a nie w przestrzeni nazw dla metod.
W przykładzie, z którym się spotkałem, programista faktycznie pominął operatora. Chciał on napisać tak:
int res = tmp * (a + b);
Jest inny powód, dla którego kompilator może nie znaleźć symbolu, jeśli kompilujesz z wiersza poleceń. Być może po prostu zapomniałeś skompilować lub przekompilować inną klasę. Na przykład, jeśli masz klasy Foo
i Bar
gdzie Foo
używa Bar
. Jeśli nigdy nie skompilowałeś Bar
i nie uruchomiłeś javac Foo.java
, możesz stwierdzić, że kompilator nie może znaleźć symbolu Bar
. Prostą odpowiedzią jest skompilowanie Foo
i Bar
połączenie; np . javac Foo.java Bar.java
lub javac *.java
. Lub lepiej nadal używać narzędzia do budowania Java; np. Ant, Maven, Gradle i tak dalej.
Są też inne niejasne przyczyny ... z którymi zajmę się poniżej.
3. Jak naprawić te błędy?
Ogólnie rzecz biorąc, zaczynasz od ustalenia, co spowodowało błąd kompilacji.
- Spójrz na wiersz w pliku wskazany w komunikacie o błędzie kompilacji.
- Określ symbol, o którym mówi komunikat o błędzie.
- Dowiedz się, dlaczego kompilator mówi, że nie może znaleźć symbolu; patrz wyżej!
Następnie pomyśl o tym, co powinien powiedzieć Twój kod. Następnie w końcu zastanawiasz się, jaką korektę musisz wprowadzić w kodzie źródłowym, aby zrobić to, co chcesz.
Pamiętaj, że nie każda „korekta” jest poprawna. Rozważ to:
for (int i = 1; i < 10; i++) {
for (j = 1; j < 10; j++) {
...
}
}
Załóżmy, że kompilator mówi „Nie można znaleźć symbolu” dla j
. Istnieje wiele sposobów na „naprawienie” tego:
- Mógłbym zmienić wewnętrzny
for
na for (int j = 1; j < 10; j++)
- prawdopodobnie poprawny.
- Mógłbym dodać deklarację
j
przed wewnętrzną for
pętlą lub zewnętrzną for
- być może poprawną.
- Mógłbym zmienić
j
się i
w wewnętrzną for
pętlę - prawdopodobnie źle!
- i tak dalej.
Chodzi o to, że musisz zrozumieć, co twój kod próbuje zrobić, aby znaleźć właściwą poprawkę.
4. Niewyraźne przyczyny
Oto kilka przypadków, w których „Nie można znaleźć symbolu” wydaje się niewytłumaczalny ... dopóki nie przyjrzysz się bliżej.
Niepoprawne zależności : jeśli używasz IDE lub narzędzia do budowania, które zarządza ścieżką kompilacji i zależnościami projektu, być może pomyliłeś się z zależnościami; np. pominął zależność lub wybrał niewłaściwą wersję. Jeśli używasz narzędzia kompilacji (Ant, Maven, Gradle itp.), Sprawdź plik kompilacji projektu. Jeśli używasz IDE, sprawdź konfigurację ścieżki kompilacji projektu.
Nie rekompilujesz : Czasami zdarza się, że nowi programiści Java nie rozumieją, jak działa łańcuch narzędzi Java lub nie wdrożyli powtarzalnego „procesu budowania”; np. używając IDE, Ant, Maven, Gradle i tak dalej. W takiej sytuacji programista może gonić za ogonem, szukając iluzorycznego błędu, który jest faktycznie spowodowany niewłaściwą rekompilacją kodu i tym podobne ...
Wcześniejszy problem z kompilacją : Możliwe, że wcześniejsza kompilacja zakończyła się niepowodzeniem w sposób, który dał plik JAR z brakującymi klasami. Taka awaria byłaby zwykle zauważalna, gdybyś używał narzędzia do kompilacji. Jednak jeśli otrzymujesz pliki JAR od kogoś innego, jesteś zależny od ich poprawnego budowania i zauważania błędów. Jeśli podejrzewasz to, użyj, tar -tvf
aby wyświetlić listę podejrzanego pliku JAR.
Problemy z IDE : ludzie zgłaszali przypadki, w których ich IDE jest mylone, a kompilator w IDE nie może znaleźć klasy, która istnieje ... lub odwrotnie.
Może się to zdarzyć, jeśli IDE zostało skonfigurowane z niewłaściwą wersją JDK.
Może się to zdarzyć, jeśli pamięci podręczne IDE nie zsynchronizują się z systemem plików. Istnieją specyficzne sposoby rozwiązania tego problemu przez IDE.
Może to być błąd IDE. Na przykład @Joel Costigliola opisuje scenariusz, w którym Eclipse nie obsługuje poprawnie drzewa „testowego” Mavena: zobacz tę odpowiedź .
Problemy z Androidem : Gdy programujesz dla Androida i występują błędy „Nie można znaleźć symbolu” R
, pamiętaj, że R
symbole są zdefiniowane w context.xml
pliku. Sprawdź, czy context.xml
plik jest poprawny i we właściwym miejscu oraz czy odpowiedni R
plik klasy został wygenerowany / skompilowany. Zauważ, że w symbolach Java rozróżniana jest wielkość liter, więc w odpowiednich identyfikatorach XML rozróżniana jest również wielkość liter.
Inne błędy symboli w Androidzie prawdopodobnie wynikają z wcześniej wymienionych przyczyn; np. brakujące lub niepoprawne zależności, niepoprawne nazwy pakietów, metody lub pola, które nie istnieją w określonej wersji interfejsu API, błędy w pisowni / pisaniu itd.
Przedefiniowanie klas systemowych : Widziałem przypadki, w których kompilator narzeka, że substring
jest to nieznany symbol w czymś takim, jak poniżej
String s = ...
String s1 = s.substring(1);
Okazało się, że programista stworzył własną wersję String
i że jego wersja klasy nie zdefiniowała substring
metod.
Lekcja: Nie definiuj własnych klas o takich samych nazwach jak wspólne klasy biblioteczne!
Homoglyphs: Jeśli używasz kodowania UTF-8 dla swoich plików źródłowych, możliwe jest, że identyfikatory wyglądają tak samo, ale w rzeczywistości są różne, ponieważ zawierają homoglyphy. Zobacz tę stronę, aby uzyskać więcej informacji.
Można tego uniknąć, ograniczając się do ASCII lub Latin-1 jako kodowania pliku źródłowego i używając \uxxxx
znaków zmiany znaczenia Java dla innych znaków.
1 - Jeśli przypadkiem, to nie patrz w ten wyjątek czasu wykonywania lub komunikat o błędzie, a następnie albo skonfigurowaniu IDE do wykonywania kodu z błędami kompilacji, czy aplikacja jest generowanie kodu i kompilacji .. przy starcie.
2 - Trzy podstawowe zasady inżynierii lądowej: woda nie płynie pod górę, deska jest silniejsza na boku i nie można naciskać na sznurek .