Wat betekent "Could not find or load main class"?

Een veel voorkomend probleem dat nieuwe Java ontwikkelaars ondervinden is dat hun programma's niet lopen met de foutmelding: Kan hoofdklasse niet vinden of laden ...

Wat betekent dit, wat veroorzaakt het, en hoe moet je het oplossen?

Oplossing

De java commando syntaxis

Allereerst moet je de juiste manier begrijpen om een programma te starten met het java (of javaw) commando. De normale syntaxis1 is deze:

    java [  ... ]  [ ...]

waarbij een command line optie is (beginnend met een "-" karakter), een volledig gekwalificeerde Java class naam is, en `` een willekeurig command line argument is dat wordt doorgegeven aan je applicatie.
1 - Er is een tweede syntax voor "executable" JAR files die ik onderaan zal beschrijven. De volledig gekwalificeerde naam (FQN) voor de klasse wordt conventioneel geschreven zoals je zou doen in Java broncode; bv.

    packagename.packagename2.packagename3.ClassName

Sommige versies van het java commando staan je echter toe om schuine strepen te gebruiken in plaats van punten; bijv.

    packagename/packagename2/packagename3/ClassName

wat (verwarrend genoeg) lijkt op een bestandspadnaam, maar het niet is. Merk op dat de term fully qualified name standaard Java terminologie is ... niet iets dat ik verzon om je in verwarring te brengen :-) Hier is een voorbeeld van hoe een java commando eruit zou moeten zien:

    java -Xmx100m com.acme.example.ListUsers fred joe bert

Het bovenstaande zal ervoor zorgen dat het java commando het volgende doet:

  1. Zoek naar de gecompileerde versie van de com.acme.example.ListUsers class.
  2. Laad de klasse.
  3. Controleer of de klasse een main methode heeft met signature, return type en modifiers gegeven door public static void main(String[]). (Let op, de naam van het argument van de methode is geen deel van de signatuur).
  4. Roep die methode aan en geef het de command line argumenten ("fred", "joe", "bert") als een String[]. Redenen waarom Java de class niet kan vinden

    Wanneer je de boodschap "Could not find or load main class ..." krijgt, betekent dit dat de eerste stap is mislukt. Het java commando was niet in staat om de class te vinden. En inderdaad, de "..." in de boodschap zal de volledig gekwalificeerde class naam zijn waar java naar zoekt. Dus waarom zou het niet in staat zijn om de klasse te vinden?
    Reden #1 - je hebt een fout gemaakt met het classname argument

    De eerste waarschijnlijke oorzaak is dat je de verkeerde classnaam hebt opgegeven. (Of ... de juiste klassenaam, maar in de verkeerde vorm.) In het bovenstaande voorbeeld zijn er een aantal verkeerde manieren om de klassenaam te specificeren:

  • Voorbeeld #1 - een eenvoudige klassenaam:

    java LijstGebruiker

    Als de klasse wordt gedeclareerd in een pakket zoals com.acme.example, dan moet je de volledige klasse-naam gebruiken inclusief de pakketnaam in het java commando; bijv. java com.acme.example.ListUser

  • Voorbeeld #2 - een bestandsnaam of padnaam in plaats van een class naam: java ListUser.class java com/acme/voorbeeld/ListUser.class

  • Voorbeeld #3 - een klasse-naam met een onjuiste schrijfwijze: java com.acme.example.listuser

  • Voorbeeld #4 - een tikfout java com.acme.example.mistuser

  • Voorbeeld #5 - een bron bestandsnaam java ListUser.java

  • Voorbeeld #6 - je bent de klasse naam helemaal vergeten java veel argumenten Reden #2 - het classpath van de toepassing's is onjuist gespecificeerd

    De tweede waarschijnlijke oorzaak is dat de class naam correct is, maar dat het java commando de class niet kan vinden. Om dit te begrijpen, moet je het concept van het "classpath" begrijpen. Dit wordt goed uitgelegd in de Oracle documentatie:

  • De java commando documentatie

  • Het instellen van het classpath.

  • De Java Tutorial - PATH en CLASSPATH Dus ... als je de classnaam correct hebt opgegeven, is het volgende wat je moet controleren of je het classpath correct hebt opgegeven:

  1. Lees de drie documenten hierboven gelinkt. (Ja ... LEES ze. Het is belangrijk dat een Java programmeur op zijn minst de basis van de werking van het Java classpath mechanisme begrijpt).
  2. Kijk naar de opdrachtregel en / of de CLASSPATH omgevingsvariabele die van kracht is wanneer je het java commando uitvoert. Controleer of de directorynamen en JAR-bestandsnamen correct zijn.
  3. Als er relatieve padnamen in het classpath staan, controleer dan of deze correct oplossen ... vanuit de huidige directory die van kracht is wanneer je het java commando uitvoert.
  4. Controleer of de class (genoemd in de foutmelding) op het effectieve classpath kan worden gevonden.
  5. Merk op dat de syntax van het classpath verschillend is voor Windows versus Linux en Mac OS. (Het klassepadscheidingsteken is ; onder Windows en : onder de andere. Als je het verkeerde scheidingsteken voor je platform gebruikt, krijg je geen expliciete foutmelding. In plaats daarvan krijg je een niet-bestaand bestand of map op het pad dat stilzwijgend wordt genegeerd). Reden #2a - de verkeerde directory staat op het classpath

    Wanneer je een directory op het classpath zet, komt die notioneel overeen met de root van de gekwalificeerde naamruimte. Klassen bevinden zich in de mappenstructuur onder die root, door de volledig gekwalificeerde naam in een padnaam om te zetten. Dus bijvoorbeeld, als "/usr/local/acme/classes" op het class pad staat, dan zal de JVM, wanneer hij zoekt naar een class genaamd com.acme.example.Foon, zoeken naar een ".class" bestand met deze padnaam:

  /usr/local/acme/classes/com/acme/example/Foon.class

Als je "/usr/local/acme/classes/com/acme/example" op het classpath had gezet, dan zou de JVM'niet in staat zijn om de class te vinden. Reden #2b - het pad van de subdirectory komt niet overeen met de FQN

Als je klassen FQN com.acme.example.Foon is, dan zal de JVM zoeken naar "Foon.class" in de directory "com/acme/example":

  • Als je directory structuur niet overeenkomt met de package naamgeving volgens het patroon hierboven, zal de JVM je class niet vinden.
  • Als je probeert een class te hernoemen door hem te verplaatsen, zal dat ook mislukken ... maar de exception stacktrace zal anders zijn. Het is mogelijk dat er iets als dit staat: Veroorzaakt door: java.lang.NoClassDefFoundError: <path> (verkeerde naam: ) omdat de FQN in het klasse-bestand niet overeenkomt met wat de klasse-lader verwacht te vinden. Om een concreet voorbeeld te geven, stel dat:
  • je de com.acme.example.Foon class wilt draaien,
  • het volledige bestandspad is /usr/local/acme/classes/com/acme/example/Foon.class,
  • uw huidige werkdirectory is /usr/local/acme/classes/com/acme/example/, dan:
# wrong, FQN is needed
java Foon

# wrong, there is no `com/acme/example` folder in the current working directory
java com.acme.example.Foon

# wrong, similar to above
java -classpath . com.acme.example.Foon

# fine; relative classpath set
java -classpath ../../.. com.acme.example.Foon

# fine; absolute classpath set
java -classpath /usr/local/acme/classes com.acme.example.Foon

Opmerkingen:

  • De -classpath optie kan in de meeste Java releases worden afgekort tot -cp. Zie de respectievelijke handleidingen voor java, javac enzovoort.

  • Denk goed na bij het kiezen tussen absolute en relatieve padnamen in classpaths. Bedenk dat een relatieve padnaam kan breken als de huidige directory verandert.
    Reden #2c - ontbrekende dependencies in het classpath

    Het classpath moet alle andere (niet-systeem) classes bevatten waar je applicatie van afhankelijk is. (De systeemklassen worden automatisch gelokaliseerd, en je hoeft je hier zelden mee bezig te houden). Om de hoofdklasse correct te laden, moet de JVM vinden:

  • de klasse zelf.

  • alle klassen en interfaces in de superclass hiërarchie (zie bijvoorbeeld https://stackoverflow.com/questions/42880748)

  • alle klassen en interfaces waarnaar verwezen wordt door middel van variabelen of variabelen declaraties, of methode aanroep of veld toegang expressies. (Opmerking: de JLS en JVM specificaties staan enige ruimte toe voor een JVM om klassen "lazily" te laden, en dit kan van invloed zijn op wanneer een classloader exception wordt gegooid). Reden #3 - de klasse is gedeclareerd in het verkeerde package

    Het gebeurt wel eens dat iemand een broncode bestand in de verkeerde map in hun source code tree zet, of ze laten de package declaratie weg. Als je dit in een IDE doet, zal de IDE's compiler je dit onmiddellijk vertellen. Evenzo als je een fatsoenlijke Java build tool gebruikt, zal de tool javac uitvoeren op een manier die het probleem zal detecteren. Echter, als je je Java code met de hand bouwt, kun je het op zo'n manier doen dat de compiler het probleem niet'opmerkt, en het resulterende ".class" bestand staat niet op de plaats waar je het verwacht te hebben. Kunt u het probleem nog steeds niet vinden?

    Er zijn veel dingen om te controleren, en het is makkelijk om iets te missen. Probeer de -Xdiag optie toe te voegen aan de java commandolijn (als eerste na java). Deze optie zal verschillende dingen over het laden van de class weergeven, en dit kan je aanwijzingen geven over wat het echte probleem is. Denk ook aan mogelijke problemen veroorzaakt door het kopiëren en plakken van onzichtbare of niet-ASCII tekens van websites, documenten, enzovoort. En denk aan "homoglyphs", waarbij twee letters of symbolen er hetzelfde uitzien ... maar dat niet zijn.

    De java -jar syntaxis

    De alternatieve syntaxis die gebruikt wordt voor "executable" JAR bestanden is als volgt:

  java [  ... ] -jar  [ ...]

b.v.

  java -Xmx100m -jar /usr/local/acme-example/listuser.jar fred

In dit geval worden de naam van de entry-point class (d.w.z. com.acme.example.ListUser) en het classpath gespecificeerd in de MANIFEST van het JAR bestand.

IDE's

Een typische Java IDE heeft ondersteuning voor het draaien van Java applicaties in de IDE JVM zelf of in een child JVM. Deze zijn algemeen immuun voor deze specifieke uitzondering, omdat de IDE zijn eigen mechanismen gebruikt om het runtime classpath samen te stellen, de hoofdklasse te identificeren en de java commandoregel aan te maken. Het is echter nog steeds mogelijk dat deze uitzondering zich voordoet, als je dingen achter de rug van de IDE doet. Bijvoorbeeld, als je eerder een Application Launcher hebt ingesteld voor je Java app in Eclipse, en je hebt toen het JAR bestand met de "main" class verplaatst naar een andere plaats in het bestandssysteem zonder Eclipse dit te vertellen, dan zou Eclipse onbewust de JVM starten met een incorrect classpath. In het kort, als je dit probleem krijgt in een IDE, controleer dan op zaken als stale IDE state, broken project references of broken launcher configuraties. Het is ook mogelijk dat een IDE gewoon in de war raakt. IDE's zijn enorm ingewikkelde stukken software die bestaan uit veel op elkaar inwerkende onderdelen. Veel van deze onderdelen gebruiken verschillende caching-strategieën om de IDE als geheel responsief te maken. Deze kunnen soms fout gaan, en een mogelijk symptoom is problemen bij het starten van toepassingen. Als u vermoedt dat dit zou kunnen gebeuren, is het de moeite waard om uw IDE opnieuw op te starten en het project opnieuw te bouwen.

Andere referenties

Commentaren (11)

Soms heeft wat het probleem veroorzaakt niets te maken met de hoofdklasse, en ik moest dit op de harde manier ontdekken. Het was een gerefereerde bibliotheek die ik verplaatste, en het gaf me de:

Kon de hoofdklasse xxx Linux niet vinden of laden

Ik verwijderde gewoon die referentie, voegde hem weer toe, en het werkte weer prima.

Commentaren (5)

Stel eerst het pad in met dit commando;

set path="paste the set path address"

Dan moet je het programma laden. Type "cd (mapnaam)" in de opgeslagen drive en compileer het. Bijvoorbeeld, als mijn programma is opgeslagen op de D-schijf, typ "D:" druk op enter en typ " cd (mapnaam)".

Commentaren (3)