Sobes.tech
Middle+
297

Rääkige Java klasside laadijatest ja klasside dünaamilisest laadimisest.

Vastus AI-lt

sobes.tech AI

ClassLoader ehk klassi laadija on objekt, mis vastutab Java klasside laadimise eest täitmise ajal. JVM ei tea klassifailide kohta enne, kui need on otseselt või kaudselt nõutud. ClassLoader vastutab klassi baitkoodi otsimise, lugemise ja määramise eest, muutes selle Class objektiks:

Sisemiste ClassLoader-ide peamised tüübid:

  1. Bootstrap ClassLoader: laadib põhiklassid rt.jar-st (jooksukirjeldusraamatukogu) ja teistest tuumraamatukogudest. On kõigi teiste laadijate esivanem. On kirjutatud C/C++ keeles.
  2. Extension ClassLoader: laadib klassid kaustast jre/lib/ext. Kasutatakse platvormi laiendamiseks. On Bootstrap ClassLoaderi järglane.
  3. System (Application) ClassLoader: laadib klassid rakenduse classpath-ist. See on vaikimisi kasutatav klasside laadija kasutajale. On Extension ClassLoaderi järglane.

ClassLoader-id töötavad delegatsiooni põhimõttel: kui ClassLoader saab päringu klassi laadimiseks, siis ta esmalt delegeerib selle päringu oma vanemale. Kui vanem ei suuda klassi laadida, siis proovib seda teha praegune ClassLoader.

Dünaamiline klasside laadimine Java-s võimaldab laadida klasse programmi täitmise ajal, mitte käivitamise ajal. Seda tehakse Class.forName() või kasutaja määratud ClassLoader-i abil.

Dünaamilise laadimise kasutus:

  • Pluginate arhitektuur: rakendused saavad laadida ja käivitada "plugina" koodi, mis pole ette teada.
  • Hilinenud laadimine: klassid laaditakse ainult siis, kui need on tõeliselt vajalikud, säästes ressursse.
  • Käitumise muutmine täitmise ajal: võimalus laadida erinevaid teostusi või klasse sõltuvalt tingimustest.

Näide Class.forName() kasutamisega dünaamilisest laadimisest:

// Dünaamiline laadimine ja klassi näide
try {
    // Klassinimi, mida laadida
    String className = "com.example.MyDynamicClass";

    // Klassist Class objekti saamine nime järgi
    Class<?> dynamicClass = Class.forName(className);

    // Näide klassi eksemplari loomisest (vajab ilmaargumentidega konstruktorit)
    Object instance = dynamicClass.getDeclaredConstructor().newInstance();

    // Kui klass implementeerib mõnda tuntud liidest
    if (instance instanceof MyInterface) {
        MyInterface myObject = (MyInterface) instance;
        myObject.doSomething();
    }

} catch (ClassNotFoundException e) {
    System.err.println("Class not found: " + e.getMessage());
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | java.lang.reflect.InvocationTargetException e) {
    e.printStackTrace();
}

// Oodatav klass, mida saab dünaamiliselt laadida
// package com.example;
// public class MyDynamicClass implements MyInterface {
//     public MyDynamicClass() { ... }
//     @Override
//     public void doSomething() {
//         System.out.println("Doing something dynamically!");
//     }
// }

// Oodatud liides
// public interface MyInterface {
//     void doSomething();
// }

Kasutaja loodud ClassLoader-i näide, et pakkuda paindlikumat laadimist:

// Lihtne kasutaja loodud ClassLoader näide
import java.io.*;

class CustomClassLoader extends ClassLoader {

    private String classDir;

    public CustomClassLoader(String classDir) {
        this.classDir = classDir;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classData = loadClassFromFile(name); // baitkoodi laadimine failist

        if (classData == null) {
            throw new ClassNotFoundException("Class not found in directory: " + name);
        }

        // klassi määratlemine baitkoodist
        return defineClass(name, classData, 0, classData.length);
    }

    private byte[] loadClassFromFile(String name) {
        String fileName = classDir + File.separator + name.replace('.', File.separatorChar) + ".class";
        File file = new File(fileName);

        try (InputStream is = new FileInputStream(file);
             ByteArrayOutputStream os = new ByteArrayOutputStream()) {

            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            return os.toByteArray();

        } catch (IOException e) {
            return null; // Fail või lugemisviga
        }
    }
}

// Kuidas kasutada kasutaja loodud ClassLoader-i
// public static void main(String[] args) {
//    String classesPath = "/path/to/your/custom/classes"; // tee .class failidele
//    CustomClassLoader customLoader = new CustomClassLoader(classesPath);
//
//    try {
//        String classNameToLoad = "com.yourcompany.YourCustomClass";
//        Class<?> customClass = customLoader.loadClass(classNameToLoad);
//        Object instance = customClass.getDeclaredConstructor().newInstance();
//        // edasine töö instance-iga
//
//    } catch (ClassNotFoundException e) {
//        System.err.println("Custom class not found: " + e.getMessage());
//    } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | java.lang.reflect.InvocationTargetException e) {
//        e.printStackTrace();
//    }
// }

Oluline mõista, et iga ClassLoader määratleb oma nimepinna klassidele. Klass, mille täisnimi on sama, kuid laaditud erinevate ClassLoader-idega, loetakse erinevaks klassiks. Seda omadust kasutatakse rakenduste või nende komponentide isoleerimiseks.