JavaRush /Java блог /Java Developer /"Javac не видит классы в пакете". Несколько нюансов компи...
ChupaFx
32 уровень
Москва

"Javac не видит классы в пакете". Несколько нюансов компиляции через cmd.

Статья из группы Java Developer
Всем привет. В данной статье я, что называется "на пальцах" попытаюсь объяснить пару нюансов при компиляции .java файлов через cmd (или терминал). Свои первые шаги в написании кода на java я делал при прочтении книги Шилдта "Java для начинающих". Следовательно моим первым инструментом был блокнот и командная строка Windows (далее CMD/cmd). Впоследствии я перешел на Idea, но привычка запускать код через терминал и javac осталась. (Дальнейший текст подразумевает, что читатель как минимум знает для чего нужен javac и как с его помощью можно скомпилировать простой .java файл. Для тех, кто не в курсе - вот ссылка на статью , нужно прочитать до раздела "Компиляция и выполнение нескольких классов"). Итак, собирая какой-то свой мини-проект состоящий из двух классов Animal и Dog (extends Animal), лежащих в одном пакете animal, при привычном вызове javac: C:\Java\animal>javac Dog.java столкнулся с ошибкой компиляции вида

symbol: class Animal
Dog.java:5: error: cannot find symbol
        Animal animal = new Animal();
        ^
дерево папок вместе с пакетом animal на диске имеет вид: C:\Java\animal исходный код классов ниже.

package animal;

public class Animal {
}


package animal;

public class Dog extends Animal{
}

Смутило и сбило с толку то, что javac Animal.java отрабатывал корректно, ошибка появлялась при попытке компилировать Dog.java. Как беглый так и, впоследствии, плотный гуглеж ответа на вопрос "WTF" так и не дал. И на первый взгляд все сделано по канону: есть package с названием animal, в нем два связанных .java файла, но тем не менее компиляция не проходит. Более того - эти же классы в idea и билдятся и работают без ошибок. Спустя примерно 6-8 часов гугления, и попыток использования возможных флагов -sourcepath -d и т.д видимо какой-то Бог всех разработчиков сжалился над моими потугами и послал мне озарение, которым я и решил поделиться с такими же бедолагами как и я. *Просьба убрать от мониторов опытных, гуру и всех тех, у кого от разжевывания очевидных вещей начинается изжога и припекание ниже спины. Дальнейший текст предназначен искючительно для личинок java-разработчиков либо тех, кто столкнулся с похожими проблемами и не находит ответа традиционными методами. Итак, наш javac может работать упрощенно говоря в двух режимах: либо "по умолчанию" (как я его для себя назвал) - это если (во первых) у вас все зависимые классы лежат в одной директории, и внутри них не прописано строчки package; во вторых javac запускается непосредственно в той директории, где лежат компилируемые файлы: для примера выше это означает, что в cmd я сначала перехожу в директорию с файлами командой cd например : cd c:\Java\animal и уже в ней вызываю javac Dog.java (в этом случае все компилируется без ошибок) либо с использованием различных опций (опять же, это я его так для себя назвал) - в этом случае либо сам javac запускается из другой директории с использованием различных флагов, либо классы имеют в себе запись принадлежности к package и т.д. И вот здесь есть, не самые очевидные для новичков, подводные камни. 1) если компилируемый файл принадлежит какому-то пакету и в нем это указано (т.е. если в нем есть строка вида package animal;) а так же если в нем есть упоминание других классов (в моем примере это Dog extends Animal), то при запуске javac нужно либо прописать путь до папки package либо запустить javac из папки, где есть папка package. Обратите внимание на выделенное. Рассмотрим на примере: Допустим мы хотим скомпилировать Dog.java не переходя по директориям. В этом случае в cmd нежно будет написать: C:\>javac -sourcepath Java Java\animal\Dog.java запись "-sourcepath" говорит компилятору в какой папке лежит наш package. (это папка c:\java но поскольку мы и так находимся в корне диска с: , то фактически говорим компилятору "эй, машина, нужный пакет лежит от этого места в папке java".) Если бы у нас было другое дерево, например файлы классов лежали бы по пути c:\java\myFiles\animal - то команда имела бы вид C:\>javac -sourcepath Java\myFiles Java\animal\Dog.java. Если же мы хотим вызвать javac изнутри файловой системы, то опять же - нужно запустить javac из папки, где лежит наш исходный пакет, например: C:\>cd Java //перешли в директорию с пакетом C:\Java>javac animal\Dog.java запустили компиляцию файла в пакете. 2) для успешной компиляции файла сам он не обязательно должен находится в пакете. Главное чтоб в этом пакете находился зависимый файл (в моем примере это Animal) и в самом зависимом файле нужна корректная запись названия пакета. (не знаю, кому может такое понадобится, но помогает лучше понять механизм работы javac) Пример: Допустим я решаю, что пакет с файлами будет иметь вид package object.animal; Допустим что общий путь до исходника Animal.java - c:\Java\obj\objects\animal\Animal.java: соответственно путь c:\Java\obj - это путь до папки, где лежит сам пакет objects\animal в которой лежит исходник Animal.java; Соответственно в файлах Animal и Dog которые нуждаются в компиляции, нужно "проапгрейдить" запись package до корректного пакета т.е. package object.animal; Ну и напоследок компилируемый файл Dog.java я захотел поместить в папку, например c:\java\dog - т.е. не связанную с пакетом (принадлежность к которому, напоминаю, определяет запись package object.animal; которую я не изменяю даже при переносе файла в другую папку). Для успешной компиляции, поскольку файл Dog.java помещен в папку dog, и в ней нет нашего package, компилятору необходимо явно указать путь, где лежит связанный файл ( т.к. Dog extends Animal): При компиляции из корня C:\ командой: C:\>javac -sourcepath Java\obj \Java\dog\Dog.java и при компиляции находясь в папке с Dog.java: C:\Java\dog>javac -sourcepath C:\Java\obj Dog.java На этом все, успехов в изучении Java!
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ