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;

  1. Ç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.
  2. 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.

Şekil 1: Java PATH hatası

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.

Şekil 2: JDK bin dizini

İş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.

Şekil 3: Sistem özellikleri penceresinden 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.

Şekil 4: Path satırını seçerek 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.

Şekil 5: JDK'nızın bin dizinini ekleyiniz.

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.

Şekil 6: Komutların çıktısı

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.

💡
Bazı programlar 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.
Şekil 7: Sistem ortam değişkenleri

Artık Path'e ayarladığınız JDK bin dizininizi de %JDK_HOME%/bin olarak ayarlayabilirsiniz.

Şekil 8: JDK bin dizini PATH değeri

Ö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.

💡
Komutunuzda uzun uzun -classpath yazmak yerine -cp kullanmayı da tercih edebilirsiniz. Ör: java -cp ../lib/;. myprogram.Main
💡
Her seferinde komutunuzda -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:

Şekil 9: CLASSPATH ortam değişkeni

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).