Compile: примеры (JAVA)
compile(String regex): PatternОписание операции компиляции в Java
Термин «compile» в контексте Java обозначает процесс преобразования исходного кода Java в байт-код (файлы .class) или иной артефакт. Основные способы компиляции в Java: командная утилита javac и программный интерфейс компилятора javax.tools.JavaCompiler. Процесс обычно применяется на этапе сборки, при динамической генерации классов во время выполнения и при реализации инструментов разработки.
Аргументы и опции командного компилятора (частые):
-classpath / -cp <path>- поиск зависимостей (библиотек и классов).-d <dir>- каталог вывода для .class файлов.-source <release>- уровень исходного кода (например, 1.8, 11).-target <release>- целевая версия байт-кода.--release <version>- кросс-компиляция с учетом API целевой платформы.-encoding <enc>- кодировка исходников.-parameters- сохранение имен параметров в байт-коде.-proc:none|only- управление запуском процессоров аннотаций.-Xlint[:all|name]- оповещения компилятора.-g- включение отладочной информации (по умолчанию).
Поведение и возвращаемые значения:
- Командная утилита
javacзавершает выполнение с кодом возврата 0 при успешной компиляции и ненулевым кодом при ошибках. Сообщения об ошибках выводятся в stderr. - Класс
javax.tools.JavaCompilerпредоставляет методrun(InputStream, OutputStream, OutputStream, String... args), который возвращаетint(0 - успех). Более гибкий интерфейс -getTask(...), возвращающийJavaCompiler.CompilationTask, у которогоcall()возвращаетBoolean(true - успешная компиляция). - Диагностика ошибок доступна через
DiagnosticCollector<?>у программного API и через stderr дляjavac. Возвращаемые значения отражают успех или неудачу, детали ошибок находятся в сообщениях диагностического потока.
Короткие примеры использования
1) Командная строка: простой пример
// Файл Hello.java
public class Hello {
public static void main(String[] args) {
System.out.println("Hi from Java");
}
}
$ javac Hello.java $ ls Hello.class Hello.java $ java Hello Hi from Java
2) Программная компиляция строки кода с JavaCompiler (упрощённый)
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int res = compiler.run(null, null, null, "Hello.java");
System.out.println("exit=" + res);
exit=0 // при успешной компиляции // при ошибке код будет ненулевым и в stderr появятся сообщения
3) Вызов javac из кода через ProcessBuilder
ProcessBuilder pb = new ProcessBuilder("javac", "Hello.java");
Process p = pb.inheritIO().start();
int code = p.waitFor();
System.out.println("exit=" + code);
exit=0
Похожие инструменты и варианты в Java
Вместо системного javac используются другие компиляторы и подходы, в зависимости от целей.
- Eclipse Compiler for Java (ECJ)
- Javac API (javax.tools)
- Инструменты сборки (Maven, Gradle)
- Ahead-of-Time (AOT) / Native Image
Поддерживает инкрементальную компиляцию. Предпочтителен в IDE и при необходимости быстрого пересобирания модулей.
Подходит при программной компиляции и необходимости встраивания компилятора в приложение.
Автоматизируют компиляцию, управляют зависимостями и жизненным циклом сборки, предпочтительны для проектов с множеством модулей.
Инструменты типа GraalVM native-image создают нативные исполняемые файлы вместо байт-кода; используются при требовании уменьшения времени запуска и уменьшения потребления памяти.
Аналоги компиляции в других языках
Python (динамическая компиляция байт-кода)
import py_compile
py_compile.compile('script.py')
# Создаст файл __pycache__/script.cpython-38.pyc
JavaScript / TypeScript
// TypeScript: tsc hello.ts
// hello.ts
const x: number = 1;
console.log(x);
$ tsc hello.ts $ node hello.js 1
C# (Roslyn / csc)
// Файл Hello.cs
using System;
class Hello { static void Main(){ Console.WriteLine("Hi"); } }
$ csc Hello.cs $ mono Hello.exe Hi
Go
// Файл hello.go
package main
import "fmt"
func main(){ fmt.Println("Hi") }
$ go build -o hello $ ./hello Hi
Kotlin
// Файл Hello.kt
fun main(){ println("Hi") }
$ kotlinc Hello.kt -include-runtime -d Hello.jar $ java -jar Hello.jar Hi
Lua
-- simple.lua
print('Hi')
-- компиляция в байт-код luac
$ luac -o simple.luac simple.lua $ lua simple.lua Hi
Отличия от Java: в некоторых языках (Python, JS) компиляция отражает преобразование в байт-код или транслирование, а в компилируемых языках (C#, Go, Kotlin) процесс похож на javac по созданию исполняемых артефактов. Программные API (Roslyn, py_compile, tsc API) дают возможности, аналогичные JavaCompiler.
Типичные ошибки при компиляции и примеры
1) cannot find symbol / Не найден символ (отсутствует зависимость или ошибка имени)
// File A.java
public class A{ B b; }
// File B.java отсутствует
$ javac A.java
A.java:1: error: cannot find symbol
public class A{ B b; }
^
symbol: class B
1 error
2) package ... does not exist / Неправильный classpath
import com.example.Lib;
// com/example/Lib.class отсутствует в classpath
error: package com.example does not exist import com.example.Lib; ^
3) source/target mismatch и --release
// Использование API Java 11 при компиляции с target 1.8
error: 'var' is not allowed in source level 1.8
4) Отсутствие JavaCompiler в среде
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
System.out.println(compiler);
null // В JRE без JDK доступ к системному компилятору отсутствует
5) Неправильная кодировка исходников
// файл с кириллицей, но без указания -encoding
error: unmappable character for encoding Cp1252
Последние изменения и эволюция процесса компиляции
Компиляция в Java развивалась преимущественно через добавление опций и расширение API, тогда как базовый механизм остался прежним.
- Java 8: опция
-parametersдобавлена для сохранения имен параметров в байт-коде. - Java 9: введён модульный путь и изменение поведения компиляции для модулей, добавлена опция
--releaseдля корректной кросс-компиляции. - Поздние релизы: улучшения производительности
javac, дополнительные предупреждения в-Xlintи поддержка новых языковых конструкций (local-variable type inference в Java 10 и далее) без изменения API компилятора. - javax.tools.JavaCompiler остаётся стабильным API; основные изменения касались расширения диагностических возможностей и поддержки новых флагов командной строки.
Расширенные и редкие сценарии использования
1) Компиляция исходника из строки в памяти и загрузка класса
// Упрощённый пример: InMemoryJavaFileObject и класс-лоадер
// Создаётся JavaFileObject из строки, компилируется через JavaCompiler.getTask, затем загружается через кастомный ClassLoader
// Результат: получен класс в рантайме, можно вызвать метод main или другие методы // Пример выводит: Hello from dynamic class
2) Сборка с указанием модульного пути
$ javac --module-path libs -d out --module-source-path src $(find src -name "*.java")
// Компилирует модульную структуру, создаёт модульные artefacts в out
3) Использование DiagnosticCollector для детальной диагностики
import javax.tools.*;
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm = compiler.getStandardFileManager(diagnostics, null, null);
Iterable<JavaFileObject> files = fm.getJavaFileObjectsFromStrings(Arrays.asList("A.java"));
JavaCompiler.CompilationTask task = compiler.getTask(null, fm, diagnostics, null, null, files);
boolean ok = task.call();
for (Diagnostic<?> d : diagnostics.getDiagnostics()) {
System.out.println(d.getKind() + ": " + d.getMessage(null));
}
// Выводит подробные ошибки/предупреждения с номерами строк и позициями
4) Инкрементальная компиляция с ECJ
// Подключение Eclipse Compiler в сборщик для ускорения пересборок
// Конфигурация в Gradle/Maven заменяет вызов javac на ECJ
// Результат: существенно быстреее время пересборки в больших проектах, поддержка специфических фич Eclipse
5) Кросс-компиляция с --release
$ javac --release 8 -d out-8 src/com/example/**/*.java
// Сгенерирует байт-код и API, совместимые с Java 8, даже если установлен JDK 11+
6) Компиляция с пользовательским FileManager
// FileManager может перенаправлять выходные .class в базу данных, память или сетевой сервис
// Позволяет применять сложные сценарии хранения артефактов и интеграции с облачными пайплайнами
Каждый пример требует учёта прав доступа к JDK (наличие компилятора) и корректной настройки путей (classpath, module-path). Программная компиляция подходит для генерации кода, тестовых фреймворков и плагинов, а командная утилита удобна для CI и локальной сборки.