Java Path ve Classpath Nedir?
Java PATH ve CLASSPATH, Java'yı Windows ve Linux'ta derlemek ve çalıştırmak için kullanılan JDK binary dosyalarını ve derlenmiş Java byte kodları olan class dosyalarını bulmak için kullanılan Java ortamının en önemli iki ortam değişkenidir.
Java programlama dilini öğrenmeye başlayan insanların neredeyse tamamı, Path ve Classpath kavramlarına hakim olmadan veya tam anlamadan kod yazmaya başlıyor. Bunun iki temel sebebi var;
- Çoğu Java kursunda kimse PATH ve CLASSPATH kavramlarının ne olduğuna değinilmiyor. PATH ve CLASSPATH ne yapar, PATH ve CLASSPATH ayarlamanın anlamı nedir, bunları ayarlamazsak ne olur, Java'da PATH ve CLASSPATH arasındaki fark nedir veya basitçe Classpath Java'da nasıl çalışır, vb. gibi hiçbir soruya cevap bulamıyorsunuz. Eğitmenler bu iki temel konuyu yok sayarak temel derslere geçiyorlar.
- IDE'leri kullanmak o kadar rahat ve pratik ki, bu tür temel ayarlarla neredeyse hiç uğraşmadan direkt kod yazmaya başlıyorsunuz. IDE'ler rahatlığımız için bu ayarları bizden saklıyor.
Java.lang.NoClassDefFoundError
ve Java.lang.ClassNotFoundException
gibi korkulan hatanın en yaygın nedeni, Java'da yanlış yapılandırılmış veya hiç yapılandırılmamış CLASSPATH'dir.Bu yazıda sizlere PATH ve CLASSPATH ortam değişkenleri arasındaki pratik farkı, nerede bulunduklarını ve Java derleyicisi ve JVM tarafından tam olarak nasıl kullanıldığını anlatacağım. Bu temel ayrıntıyı öğrendikten sonra, CLASSPATH ile ilgili sorunların çoğunu kendi başınıza çözebileceksiniz.
1. Java PATH Nedir ve Nasıl Eklenir?
PATH, işletim sisteminin JDK'ya ait exe veya binary dosyalarını (java
veya javac
gibi) bulabilmesi için kullanılan bir ortam değişkenidir. Java komutlarını çalıştırdığınızda işletim sistemi bu tanımladığınız PATH'e giderek ilgili komut dosyalarını arar.
Konsol tabanlı Java programlarını Windows veya Linux ortamlarında çalıştırmak için java
ve javac
komutlarını kullanmalıyız. Bu yürütülebilir dosyaların nerede olduğunu belirtmediğimiz için java
, javac
komutları işletim sistemi tarafından anlaşılmaz. Bu nedenle, yürütülebilir dosyaların bulunduğu yolu belirtmemiz gerekiyor.
Java exe ve binary dosyaları kullandığınız JDK'nın bin
dizini içinde yer alır. Bu dizin yolunu ayarladıktan sonra, derleyici veya yorumlayıcının kendisi de dahil olmak üzere programdaki tüm gerekli öğeleri OS kullanmaya başlar.
Örneğin, eğer daha önce PATH ayarlamadıysanız, bilgisayarınızda kurulu olan java versiyonunu öğrenmek için java -version
komutu çalıştırdığınızda "'java' is not recognized as an internal or external command, operable program or batch file." hatasıyla karşılaşırsınız.
Hatayı almanızın sebebi, işletim sisteminizin yürütülebilir (executable) java
dosyasının nerede olduğunu bilmemesidir. İşletim sisteminize bu dosyanın nerede olduğunu söylemek için bir PATH eklemelisiniz.
Örnekte kullanacağım ve sahip olduğum JDK'nın bin
dizini C:\Program Files\Eclipse Adoptium\jdk-17.0.2.8-hotspot\bin
olduğu için eklemem gereken PATH değeri budur. Siz de kendi kullandığınız JDK'nın bin
dizinini elde etmelisiniz.
İşletim sistemine JDK yürütülebilir dosyalarının olduğu bin dizinini eklemek için sırasıyla Bilgisayarım
->Gelişmiş Sistem Ayarları
penceresini açınız ve Gelişmiş
sekmesinden Ortam Değişkenleri
butonuna tıklayınız.
Ardından Sistem Değişkenleri alanındaki Path
satırını seçiniz ve Düzenle
butonuna tıklayınız.
Açılan pencerede Yeni butonunu kullanarak JDK'nızın bin
dizinini ekleyiniz ve kaydederek tüm pencereleri kapatınız.
Eski açtığınız terminali kapatarak yeni bir terminal açınız ve tekrardan java -version
ve echo %PATH%
komutlarını çalıştırınız.
Artık işletim sisteminiz java
JDK yürütülebilir dosyasını nerede bulacağını bildiği için ilgili dosyayı bulup yürütebiliyor.
JAVA_HOME
, JDK_HOME
ve JRE_HOME
ortam değişkenlerine ihtiyaç duyabilir.PATH değişkenini ayarladık fakat bazı programlar JAVA_HOME
, JDK_HOME
ve JRE_HOME
ortam değişkenlerine ihtiyaç duyabilir. Bu nedenle bu değişkenleri de ayarlamak size fayda sağlayacaktır.
- JAVA_HOME: JDK dizininizi ifade ediyor.
JDK_HOME
ile aynıdır. - JDK_HOME: JDK dizininizi ifade ediyor.
- JRE_HOME: Eğer JDK yerine JRE kullanıyorsanız, JRE dizininizi ifade eder.
Artık Path'e ayarladığınız JDK bin
dizininizi de %JDK_HOME%/bin
olarak ayarlayabilirsiniz.
Özetle; Java PATH, Java'yı Windows ve Linux'ta derlemek ve çalıştırmak için kullanılan JDK / JRE exe veya binary yürütülebilir dosyalarının bulunduğu dizindir.
2. Java CLASSPATH Nedir ve Nasıl Eklenir?
CLASSPATH, sınıf dosyalarını aramak için basitçe dizinlerin, JAR dosyalarının ve ZIP arşivlerinin bir listesini tutan bir ortam değişkenidir. Java sanal makinesinin çalışma zamanında derlenmiş sınıflarınızı nerede bulacağını bilmesi gerekiyor. JVM'in makinenizdeki her klasöre bakmasını sağlamak pratik olmayacaktır, bu nedenle JVM'ye bakılacak yerlerin bir listesini sağlamanız gerekir. Bu, klasör ve jar dosyalarını sınıf yolunuza koyarak yapılır.
CLASSPATH'in nasıl ayarlandığından bahsetmeden önce, .class
dosyaları, paketler ve .jar
dosyaları hakkında konuşalım ve basit bir örnekle başlayalım:
Şu paket yapısına sahip bir projeniz olduğunu düşünün:
ClasspathProject/src/java/myprogram/
|
---> Main.java
|
---> utils/
|
---> Util.java
Main.java
ve Util.java
içeriğinizde şu şekilde olsun:
package myprogram;
import myprogram.utils.*;
public final class Main {
public static void main(String[] args) {
System.out.println(Util.get(1337));
}
}
package myprogram.utils;
public final class Util {
public static String get(int num) {
return "Here is " + num;
}
}
Java'da yeniyseniz, paketlerin genellikle dosya yapısını yansıttığını varsayıldığını bilmek yardımcı olabilir. Örneğin Utils.java
dosyasını düşünün. Bu myprogram.utils
paketine sahiptir çünkü myprogram/utils
klasöründe yer alır.
Şimdi bu küçük projeyi derleyelim. Önce, derlenen öğeleri ana proje klasöründe tutmak için ClasspathProject
klasöründe bin
isimli bir klasör oluşturalım. Ardından iki dosyayı javac
komutu ile derleyelim.
javac src/java/myprogram/Main.java src/java/myprogram/utils/Util.java -d bin/
Şimdi aşağıdaki dosya yapısına sahip olmalısınız.
ClasspathProject/
|
---> src/java/myprogram/
|
---> Main.java
|
---> utils/
|
---> Util.java
|
---> bin/myprogram/
|
---> Main.class
|
---> utils/
|
---> Util.class
Şimdi bin
klasörüne geçersek Main sınıfını çalıştırabiliriz.
> java myprogram.Main
Here is 1337
Harika, programımız çalışıyor fakat herhangi bir CLASSPATH ayarlamadan bu nasıl çalışıyor? Cevap basittir, çünkü CLASSPATH için varsayılan değer geçerli dizindir. Yani programımızın çalışabilmesi için Main.class
ve Utils.class
derlenmiş sınıflarına ihtiyacı var ve biz bin
dizininde myprogram.Main
ana sınıfıyla komutu çalıştırdığımızda java
şu iki derlenmiş sınıfı arayacaktır:
- myprogram/Main.class
- myprogram/utils/Util.class
Bu dosyaların ilgili dizinlerde var olduğunu biliyoruz, bu yüzden de program başarılı bir şekilde çalışıyor.
Şimdi dosya yapımızı biraz değiştirelim. bin
ile aynı seviyede yeni bir lib
isimli dizin ekleyelim ve Utils.class
'ı bu dizinin altına taşıyalım. Son durumda dosya yapımız şu şekilde olacaktır:
ClasspathProject/
|
---> src/java/myprogram/
|
---> Main.java
|
---> utils/
|
---> Util.java
|
---> bin/myprogram/
|
---> Main.class
|
---> lib/myprogram/
|
---> utils/
|
---> Util.class
Eğer yine bin
klasörüne gidersek ve aynı komutu çalıştırmayı denersek, Util.class
dosyası bulunamadığı için java.lang.NoClassDefFoundError
hatası alırız.
> java myprogram.Main
Exception in thread "main" java.lang.NoClassDefFoundError: myprogram/utils/Util
at myprogram.Main.main(Main.java:8)
Caused by: java.lang.ClassNotFoundException: myprogram.utils.Util
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 1 more
Bu sorunu sınıf dosyasını CLASSPATH'e ekleyerek çözmeye çalışacağız (tüm bu komutları bin
klasöründen çalıştırıyoruz).
> java -classpath ../lib/myprogram/utils/Util.class myprogram.Main
Error: Could not find or load main class myprogram.Main
Caused by: java.lang.ClassNotFoundException: myprogram.Main
Dikkat, şimdi de Main.class
dosyasını bulamıyor ve java.lang.ClassNotFoundException
hatası veriyor! Çünkü daha önce açıklandığı gibi, JVM, CLASSPATH'de sağlanan belirtilen dizinlerdeki dosyaları bulmaya çalışacaktır. Ve biz bir dizin sağlamadık. Öyleyse komutumuzu şu şekilde düzeltelim:
> java -classpath ../lib/myprogram/utils/ myprogram.Main
Error: Could not find or load main class myprogram.Main
Caused by: java.lang.ClassNotFoundException: myprogram.Main
Yine aynı hata! Peki burada ne oldu? Varsayılanı (geçerli dizin olan) geçersiz kılan bir CLASSPATH argümanı sağladığımızdan, Main.class
dosyamızı hiçbir yerde bulamıyor. Öyleyse varsayalınanı ezmemek için Unix sistemlerinde iki nokta üst üste (:) ile, Windows'ta noktalı virgül (;) ile varsayılan dizini de CLASSPATH'e ekleyelim.
> java -classpath ../lib/myprogram/utils/;. myprogram.Main
Exception in thread "main" java.lang.NoClassDefFoundError: myprogram/utils/Util
at myprogram.Main.main(Main.java:8)
Caused by: java.lang.ClassNotFoundException: myprogram.utils.Util
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 1 more
Şimdi Main.class dosyasını bulabilecek gibi görünüyor. Ama yine de Util.class dosyasını bulamıyor. Daha önce açıklandığı gibi, CLASSPATH'lerden herhangi birini kullanarak aşağıdaki dosyaları bulmaya çalışacaktır:
- myprogram/Main.class
- myprogram/utils/Util.class
Bizim ise CLASSPATH'imizde iki adet dizin var:
- ../lib/myprogram/utils/
- .
Basitçe söylemek gerekirse, var olup olmadıklarını görmek için sınıf yollarını gerekli dosyalara eklemeye çalışacak ve süreç şöyle görünecek.
Cp1 = ../lib/myprogram/utils/
Cp2 = ./
File1 = myprogram/Main.class
File2 = myprogram/utils/Util.class
Trying to find File1
Cp1 + File1 = ../lib/myprogram/utils/myprogram/Main.class
NOT FOUND
Cp2 + File1 = ./myprogram/Main.class
FOUND
Trying to find File2
Cp1 + File2 = ../lib/myprogram/utils/myprogram/utils/Util.class
NOT FOUND
Cp2 + File2 = ./myprogram/utils/Util.class
NOT FOUND
---> ERROR
Bu alt klasörleri lib
altına ekleyerek bunu çözebilir miyiz? Elbette yapabiliriz ancak, bu klasörleri eklemek yerine CLASSPATH argümanını şöyle görünecek şekilde değiştirelim.
> java -classpath ../lib/;. myprogram.Main
Here is 1337
Bu işe yarar çünkü artık ilk yolu kullanarak Utils.class
dosyasını bulabilecektir.
../lib/ + myprogram/utils/Util.class
FOUND
Umarım buradaki bazı hatalar CLASSPATH kavramını anlamanıza yardımcı olmuştur.
-classpath
yazmak yerine -cp
kullanmayı da tercih edebilirsiniz. Ör: java -cp ../lib/;. myprogram.Main
-cp
argümanı geçmek yerine işletim sisteminizde CLASSPATH isimli bir ortam değişkeni ayarlayarak JVM'in derlenmiş sınıfları aramasını istediğiniz yerleri bu değişkene geçebilirsiniz.Üstteki bilgi notunu Windows'ta ayarlamak için ilk başlıkta anlattığımız Ortam Değişkenleri özelliğini kullanabilirsiniz:
3. Java PATH ve CLASSPATH Farkı Nedir?
Konuyu toparlamak adına hızlı bir özet geçersek;
PATH:
- JDK exe ve binary dosyalarını (sistem yürütülebilir dosyaları) bulmak için kullanılan ortam değişkeni. Örneğin: java, javac
- Herhangi bir yürütülebilir dosyayı bulmak için OS tarafından kullanılır.
- JDK_HOME/bin dizinini PATH ortam değişkenine dahil etmelisiniz.
- JDK_HOME/bin dizinini PATH ortam değişkenine dahil etmelisiniz.
CLASSPATH:
- Yalnızca Java Class Loader tarafından sınıf dosyalarını yüklemek için kullanılır.
- Sınıf dosyasında depolanan derlenmiş Java byte kodlarını bulmak ve yüklemek için Sistem/Uygulama Class Loader tarafından kullanılan ortam değişkenidir.
- Java uygulamasının ihtiyaç duyduğu .class veya jar dosyalarını koyduğunuz tüm dizinleri dahil etmelisiniz.
- Komut satırı seçenekleriyle (-cp) geçersiz kılınabilir (override edilebilir).