Co oznacza błąd „Nie można znaleźć symbolu” lub „Nie można rozwiązać symbolu”?
Wyjaśnij następujące kwestie dotyczące błędów „Nie można znaleźć symbolu”, „Nie można rozwiązać symbolu” lub „Nie znaleziono symbolu”:
- Co mieli na myśli?
- Jakie rzeczy mogą je powodować?
- Jak programista je naprawia?
To pytanie ma na celu zapoczątkowanie obszernego pytania i odpowiedzi na temat tych typowych błędów kompilacji w Javie.
Odpowiedzi
0. Czy jest jakaś różnica między tymi dwoma błędami?
Nie całkiem. „Nie można znaleźć symbolu”, „Nie można znaleźć symbolu” i „Nie znaleziono symbolu” - wszystkie oznaczają to samo. Różne kompilatory języka Java używają różnych frazeologii.
1. Co oznacza błąd „Nie można znaleźć symbolu”?
Po pierwsze, jest to błąd kompilacji 1 . Oznacza to, że albo jest problem w kodzie źródłowym Java, albo jest problem w sposobie 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. - Literały: 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 spacje.
Błąd „Nie można znaleźć symbolu” dotyczy identyfikatorów. Kiedy Twój kod jest kompilowany, kompilator musi dowiedzieć się, co oznacza każdy identyfikator w Twoim 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 spowodować błąd „Nie można znaleźć symbolu”?
Po pierwsze, jest tylko jedna przyczyna. Kompilator przeszukał wszystkie miejsca, w których powinien zostać zdefiniowany identyfikator , i nie mógł znaleźć definicji. Może to być spowodowane wieloma czynnikami. Typowe są następujące:
Ogólnie dla identyfikatorów:
- Być może niepoprawnie przeliterowałeś nazwę; tj.
StringBiulder
zamiastStringBuilder
. Java nie może i nie będzie próbować rekompensować błędów ortograficznych lub literówek. - Być może źle zrozumiałeś sprawę; tj.
stringBuilder
zamiastStringBuilder
. We wszystkich identyfikatorach Java jest rozróżniana wielkość liter. - Być może niewłaściwie użyłeś podkreśleń; ie
mystring
imy_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”; tj. w innym kontekście niż ten, w którym niejawnie nakazałeś kompilatorowi szukać. (Inna klasa? Inny zakres? Inny pakiet? Inna baza kodu?)
- Być może niepoprawnie przeliterowałeś nazwę; tj.
Identyfikatory, które powinny odnosić się do zmiennych:
- Być może zapomniałeś zadeklarować zmienną.
- Być może deklaracja zmiennej jest poza zakresem w momencie, gdy próbowałeś jej użyć. (Zobacz 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 dziedziczonej metody lub pola, które nie zostało zadeklarowane w klasach lub interfejsach rodzica / przodka.
Być może próbujesz odwołać się do metody lub pola, które nie istnieje (tj. Nie zostało 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
lubsomeArray.length()
.Być może omyłkowo operujesz na tablicy zamiast 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:
Być może zapomniałeś zaimportować klasy.
Być może użyłeś importu „star”, ale klasa nie jest zdefiniowana w żadnym z importowanych pakietów.
Być może zapomniałeś
new
jak w:String s = String(); // should be 'new String()'
W przypadkach, gdy typ lub instancja nie wydaje się mieć członka, którego się spodziewałeś:
- Być może ogłoszony zagnieżdżone klasy lub ogólny parametr cienie typ nosisz się z zamiarem użycia.
- Być może śledzisz zmienną statyczną lub instancji.
- Być może zaimportowałeś niewłaściwy typ; np. ze względu na zakończenie IDE lub autokorektę.
- Być może używasz (kompilujesz przeciwko) niewłaściwej wersji API.
- Być może zapomniałeś rzucić swój obiekt na odpowiednią podklasę.
Problem jest często połączeniem powyższych. Na przykład, być 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 chodziło o pisanie File
... który jest klasą w java.io
.
Oto przykład tego, jak 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()) {
...
}
Spowoduje to wyświetlenie błędu „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. Odniesienie do i
w if
oświadczeniu nie może zobaczyć tego oświadczenia i
. To jest poza zakresem .
(Odpowiednią poprawką może być tutaj przeniesienie if
instrukcji wewnątrz pętli lub zadeklarowanie i
przed rozpoczęciem pętli).
Oto przykład, który powoduje zdziwienie, gdy 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 wyświetlenie błędu kompilacji w println
wywołaniu, informującego, że i
nie można znaleźć. Ale (słyszę, jak mówisz) ogłosiłem to!
Problem polega na tym, że podstępny średnik ( ;
) przed {
. Składnia języka Java definiuje średnik w tym kontekście jako instrukcję pustą . Pusta instrukcja staje się wówczas treścią for
pętli. Więc ten kod faktycznie 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”, który jest spowodowany literówką.
int tmp = ...
int res = tmp(a + b);
Pomimo poprzedniej deklaracji wyrażenie tmp
w tmp(...)
wyrażeniu jest błędne. Kompilator będzie szukał wywoływanej metody tmp
, ale jej nie znajdzie. Poprzednio zadeklarowana tmp
znajduje się w przestrzeni nazw dla zmiennych, a nie w przestrzeni nazw dla metod.
W przykładzie, z którym się spotkałem, programista właściwie pominął operatora. Miał zamiar napisać tak:
int res = tmp * (a + b);
Jest jeszcze jeden powód, dla którego kompilator może nie znaleźć symbolu, jeśli kompilujesz z wiersza poleceń. Mogłeś po prostu zapomnieć o skompilowaniu lub ponownej kompilacji innej klasy. Na przykład, jeśli masz klasy Foo
i Bar
gdzie Foo
używa Bar
. Jeśli nigdy nie kompilowałeś Bar
i uruchamiasz javac Foo.java
, możesz stwierdzić, że kompilator nie może znaleźć symbolu Bar
. Prosta odpowiedź to kompilacja Foo
i Bar
razem; np . javac Foo.java Bar.java
lub javac *.java
. Lub lepiej nadal używać narzędzia do budowania języka Java; np. Ant, Maven, Gradle i tak dalej.
Są też inne, bardziej niejasne przyczyny ... 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.
- Zidentyfikuj symbol, o którym mowa w komunikacie o błędzie.
- Dowiedz się, dlaczego kompilator mówi, że nie może znaleźć symbolu; patrz wyżej!
Następnie myślisz o tym, co ma mówić Twój kod. Następnie w końcu ustalasz, jaką korektę musisz wprowadzić w swoim kodzie źródłowym, aby zrobić to, co chcesz.
Zauważ, ż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
. Jest wiele sposobów, w jakie mogę to „naprawić”:
- Mógłbym zmienić wewnętrzne
for
nafor (int j = 1; j < 10; j++)
- prawdopodobnie poprawne. - Mógłbym dodać deklarację
j
przedfor
pętlą wewnętrzną lubfor
pętlą zewnętrzną - prawdopodobnie poprawna. - Mogę zmienić
j
sięi
w wewnętrznejfor
pętli - prawdopodobnie źle! - i tak dalej.
Chodzi o to, że musisz zrozumieć, co próbuje zrobić twój kod, aby znaleźć właściwą poprawkę.
4. Niejasne przyczyny
Oto kilka przypadków, w których „Nie można znaleźć symbolu” wydaje się niewytłumaczalne ... dopóki nie przyjrzysz się bliżej.
Nieprawidłowe zależności : jeśli używasz IDE lub narzędzia do budowania, które zarządza ścieżką budowania i zależnościami projektu, być może popełniono błąd z zależnościami; np. pominięto zależność lub wybrano złą wersję. Jeśli używasz narzędzia do 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 zaimplementowali powtarzalnego „procesu budowania”; np. używając IDE, Ant, Maven, Gradle i tak dalej. W takiej sytuacji programista może skończyć goniąc za swoim ogonem, szukając iluzorycznego błędu, który w rzeczywistości jest spowodowany niepoprawną rekompilacją kodu i tym podobne ...
Problem z wcześniejszą kompilacją : Możliwe, że wcześniejsza kompilacja nie powiodła się w sposób, który spowodował utworzenie pliku JAR z brakującymi klasami. Taka awaria byłaby zwykle zauważana, gdybyś używał narzędzia do kompilacji. Jeśli jednak otrzymujesz pliki JAR od kogoś innego, jesteś zależny od tego , czy poprawnie budują i zauważają błędy. Jeśli podejrzewasz to, użyj,
tar -tvf
aby wyświetlić zawartość podejrzanego pliku JAR.Problemy z IDE : ludzie zgłaszali przypadki, w których ich IDE jest zdezorientowane, a kompilator w IDE nie może znaleźć klasy, która istnieje ... lub sytuacja odwrotna.
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 stracą synchronizację z systemem plików. Istnieją specyficzne dla IDE sposoby, aby to naprawić.
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 : Kiedy programujesz dla Androida i masz błąd „Nie można znaleźć symbolu”
R
, pamiętaj, żeR
symbole są zdefiniowane wcontext.xml
pliku. Sprawdź, czycontext.xml
plik jest poprawny i znajduje się we właściwym miejscu oraz czy odpowiedniR
plik klasy został wygenerowany / skompilowany. Zauważ, że w symbolach Java jest rozróżniana wielkość liter, więc odpowiednie identyfikatory XML również uwzględniają wielkość liter.Inne błędy symboli w systemie Android prawdopodobnie wynikają z wcześniej wymienionych powodów; np. brakujące lub niepoprawne zależności, nieprawidłowe nazwy pakietów, metody lub pola, których nie ma w określonej wersji API, błędy ortograficzne / pisarskie i tak dalej.
Przedefiniowanie klas systemowych : widziałem przypadki, w których kompilator narzeka, że
substring
jest to nieznany symbol w czymś podobnym do następującegoString s = ... String s1 = s.substring(1);
Okazało się, że programista stworzył własną wersję
String
i że jego wersja klasy nie definiujesubstring
metod.Lekcja: Nie definiuj własnych klas o takich samych nazwach, jak zwykłe klasy biblioteczne!
Homoglify: jeśli używasz kodowania UTF-8 dla plików źródłowych, możesz mieć identyfikatory, które wyglądają tak samo, ale w rzeczywistości są różne, ponieważ zawierają homoglify. Więcej informacji znajdziesz na tej stronie .
Możesz tego uniknąć, ograniczając się do ASCII lub Latin-1 jako kodowania pliku źródłowego i używając znaków specjalnych Java
\uxxxx
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 mocniejsza na boku i nie można pchać sznurka .
Otrzymasz również ten błąd, jeśli zapomnisz new
:
String s = String();
przeciw
String s = new String();
ponieważ wywołanie bez new
słowa kluczowego będzie próbowało szukać (lokalnej) metody wywoływanej String
bez argumentów - a ta sygnatura metody prawdopodobnie nie jest zdefiniowana.
Jeszcze jeden przykład „Zmienna poza zakresem”
Jak już kilka razy widziałem tego rodzaju pytania, może jeszcze jeden przykład tego, co jest nielegalne, nawet jeśli może wydawać się w porządku.
Rozważ ten kod:
if(somethingIsTrue()) {
String message = "Everything is fine";
} else {
String message = "We have an error";
}
System.out.println(message);
To nieprawidłowy kod. Ponieważ żadna z wymienionych zmiennych nie message
jest widoczna poza swoim zakresem - {}
w tym przypadku byłyby to otaczające nawiasy .
Możesz powiedzieć: „Ale zmienna o nazwie message jest zdefiniowana tak czy inaczej - więc wiadomość jest definiowana po znaku if
”.
Ale mylisz się.
Java nie ma operatorów free()
lub delete
, więc musi polegać na zakresie zmiennych śledzenia, aby dowiedzieć się, kiedy zmienne nie są już używane (wraz z odwołaniami do tych zmiennych przyczyny).
Jest to szczególnie złe, jeśli myślisz, że zrobiłeś coś dobrego. Widziałem tego rodzaju błąd po „optymalizacji” kodu w następujący sposób:
if(somethingIsTrue()) {
String message = "Everything is fine";
System.out.println(message);
} else {
String message = "We have an error";
System.out.println(message);
}
„Och, jest zduplikowany kod, wyciągnijmy tę wspólną linię” -> i gotowe.
Najczęstszym sposobem radzenia sobie z tego rodzaju problemami z zakresem byłoby wstępne przypisanie wartości-else do nazw zmiennych w zakresie zewnętrznym, a następnie ponowne przypisanie, jeśli:
String message = "We have an error";
if(somethingIsTrue()) {
message = "Everything is fine";
}
System.out.println(message);
Jeden ze sposobów uzyskania tego błędu w Eclipse:
- Zdefiniuj klasę
A
wsrc/test/java
. - Zdefiniowanie innej klasy
B
wsrc/main/java
tej klasie zastosowańA
.
Wynik: Eclipse skompiluje kod, ale maven poda „Nie można znaleźć symbolu”.
Podstawowa przyczyna: Eclipse używa połączonej ścieżki budowania dla drzewa głównego i drzewa testowego. Niestety, nie obsługuje używania różnych ścieżek kompilacji dla różnych części projektu Eclipse, czego wymaga Maven.
Rozwiązanie :
- Nie definiuj w ten sposób swoich zależności; tj. nie popełniaj tego błędu.
- Regularnie buduj bazę kodu za pomocą Mavena, aby wcześnie wykryć ten błąd. Jednym ze sposobów jest użycie serwera CI.
„Nie można znaleźć” oznacza, że kompilator, który nie może znaleźć odpowiedniej zmiennej, metody, klasy itp.… Jeśli otrzymałeś komunikat o błędzie, najpierw chcesz znaleźć wiersz kodu, w którym zostanie wyświetlony komunikat o błędzie. w stanie znaleźć, która zmienna, metoda lub klasa nie zostały zdefiniowane przed jej użyciem. Po potwierdzeniu zainicjuj tę zmienną, metodę lub klasę, aby później wymagać ... Rozważ następujący przykład.
Utworzę klasę demonstracyjną i wydrukuję nazwę ...
class demo{
public static void main(String a[]){
System.out.print(name);
}
}
Teraz spójrz na wynik ...
Ten błąd mówi: „Nie można znaleźć nazwy zmiennej” .. Definiowanie i inicjowanie wartości dla zmiennej „nazwa” może zostać zniesione. Tak naprawdę,
class demo{
public static void main(String a[]){
String name="smith";
System.out.print(name);
}
}
Teraz spójrz na nowe wyjście ...
Ok Pomyślnie rozwiązałem ten błąd ... Jednocześnie, jeśli udało Ci się uzyskać coś „nie można znaleźć metody” lub „nie można znaleźć klasy”, najpierw zdefiniuj klasę lub metodę, a następnie użyj jej.
Jeśli otrzymujesz ten błąd podczas kompilacji w innym miejscu, a twoje IDE mówi, że wszystko jest w porządku, sprawdź, czy używasz tych samych wersji Java w obu miejscach.
Na przykład Java 7 i Java 8 mają różne interfejsy API, więc wywołanie nieistniejącego interfejsu API w starszej wersji Java spowodowałoby ten błąd.
ROZWIĄZANY
Korzystanie z IntelliJ
Wybierz Build -> Rebuild Project rozwiąże to
Ja też otrzymywałem ten błąd. (dla którego wyszukałem w Google i zostałem przekierowany na tę stronę)
Problem: Wywołałem metodę statyczną zdefiniowaną w klasie projektu A z klasy zdefiniowanej w innym projekcie B. Otrzymałem następujący błąd:
error: cannot find symbol
Rozwiązanie: rozwiązałem ten problem, najpierw budując projekt, w którym zdefiniowano metodę, a następnie projekt, z którego metoda została wywołana.
Jeśli ścieżka budowania środowiska Java zaćmienia jest odwzorowana na 7, 8, a we właściwościach Mavena projektu pom.xml o nazwie java.version wspomniana jest wyższa wersja Java (9,10,11 itd.) Niż 7,8, należy zaktualizować w pom. xml plik.
W Eclipse, jeśli Java jest odwzorowana na Javę w wersji 11, aw pom.xml, na wersję Java 8. Zaktualizuj obsługę Eclipse do Java 11, wykonując poniższe kroki w pomocy eclipse IDE -> Zainstaluj nowe oprogramowanie ->
Wklej następujący link http://download.eclipse.org/eclipse/updates/4.9-P-builds at Work With
lub
Dodaj (otworzy się wyskakujące okienko) ->
Name:
Obsługa języka Java 11
Location:
http://download.eclipse.org/eclipse/updates/4.9-P-builds
następnie zaktualizuj wersję Java we właściwościach Maven pliku pom.xml , jak poniżej
<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
Na koniec kliknij prawym przyciskiem myszy projekt Debuguj jako -> Maven wyczyść, kroki kompilacji Maven
Mogą istnieć różne scenariusze, jak wspomniano powyżej. Kilka rzeczy, które pomogły mi rozwiązać ten problem.
Jeśli używasz IntelliJ
File -> 'Invalidate Caches/Restart'
LUB
Klasa, do której się odwołujemy, znajdowała się w innym projekcie i ta zależność nie została dodana do pliku kompilacji Gradle mojego projektu. Więc dodałem zależność za pomocą
compile project(':anotherProject')
i zadziałało. HTH!
skompilowałeś swój kod za pomocą kompilacji maven, a następnie użyłeś maven test do uruchomienia, działał dobrze. Teraz, jeśli zmieniłeś coś w swoim kodzie, a następnie bez kompilacji go uruchamiasz, otrzymasz ten błąd.
Rozwiązanie: ponownie skompiluj go, a następnie uruchom test. U mnie to zadziałało w ten sposób.
W moim przypadku - musiałem wykonać poniższe operacje:
- Przenieś
context.xml
plik zsrc/java/package
doresource
katalogu (IntelliJ IDE) - Czysty
target
katalog.
Aby uzyskać wskazówki, przyjrzyj się bliżej nazwie klasy, która zgłasza błąd, i numerowi linii, na przykład: Błąd kompilacji [BŁĄD] \ applications \ xxxxx.java: [44,30] błąd: nie można znaleźć symbolu
Inną przyczyną jest nieobsługiwana metoda dla wersji Java, na przykład jdk7 vs 8. Sprawdź% JAVA_HOME%
Wystąpił błąd w projekcie Java, który jest skonfigurowany jako kompilacja wielu projektów Gradle. Okazało się, że w jednym z podprojektów brakowało wtyczki Gradle Java Library . Dzięki temu pliki klas podprojektu nie były widoczne dla innych projektów w kompilacji.
Po dodaniu wtyczki biblioteki Java do podprojektu build.gradle
w następujący sposób błąd zniknął:
plugins {
...
id 'java-library'
}
Rozwiązałem ten błąd w ten sposób ... Szaleństwo Androida. Miałem nazwę pakietu jako Adapter, a ja przerobiłem nazwę na adapter z „a” zamiast „A” i rozwiązałem błąd.