İçeriğe geç
academia.sh

Ders 12 / 12

JVM Üzerinde Diğer Diller

Kaynak metni olmadan, doğrudan sınıf dosyası biçiminde üretilen bir sınıfın, bir arayüzü gerçekleştirdiği sürece javac ile derlenmiş bir sınıf gibi yüklenip çağrıldığı koşturularak gösterilir; çağıranın sınıf dosyası ne gerçek ne üretilen gerçekleştirimin adını anmaz. Sınırlayıcı ölçüm, üretilen sınıf dosyasının Java söz diziminde karşılığı olmayan bir üye adı taşıyabildiğini ve bunun yalnız yansıma (reflection) ile çağrılabildiğini gösterir.

İçindekiler

Bu iki ders boyunca ölçülen her şey aynı çalışma zamanının içindeydi: cephe de, gerçekleştirim de, günlükçü de, hepsi kaynağı Java olan sınıflardı. Bu ders bu son varsayımı kaldırıyor. Koşum sınıf yoluna giren bir sınıf dosyası, kaynağı hiç Java diliyle yazılmamış olsa bile aynı biçimde mi karşılanıyor, ve çağıran bunu fark ediyor mu? Kursun tamamını taşıyan soru burada son bir kez soruluyor: koşum sınıf yolu, üzerine konan şeyin hangi dilden geldiğini hiç bilmiyor, yalnız sınıf dosyası biçimine uyup uymadığını biliyor. Bu dersin ölçümü bunu bir iddia olarak değil, bir koşum olarak kuruyor: sınıf dosyasını javac’a hiç uğramadan, doğrudan sınıf dosyası biçiminde üretiyor, koşum sınıf yoluna koyuyor, ve Java’dan yazılmış bir çağıranın onu çağırdığını gösteriyor. Hiçbir dil adı, satıcı adı ya da ürün adı geçmiyor — konu “aynı sınıf dosyası biçimini üreten başka bir üretici” olarak kuruluyor.

Sınıf Dosyasını Kaynak Metin Olmadan Üretmek

GL16. Bir sınıf dosyası üretmenin zorunlu yolu kaynak metin yazıp derlemek değildir; sınıf dosyası biçiminin kendisini doğrudan üreten bir yordam da sanal makinenin kabul edeceği bir sınıf dosyası üretebilir. GL17. Böyle üretilen bir sınıf dosyası, bir arayüzü gerçekleştirdiğini bildirdiği ve arayüzün istediği imzayı taşıdığı sürece, kaynaktan derlenmiş bir sınıf gibi yüklenir ve çağrılır.

// Arac.java — kaynak yazma, derleme, sabit havuz okuma ve kaynaksiz sinif dosyasi uretme icin ortak yordamlar (yardimci sinif, main yok)
import java.io.*;
import java.lang.classfile.*;
import java.lang.classfile.constantpool.*;
import java.lang.constant.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import java.util.spi.ToolProvider;

class Arac {
    static Path yaz(Path dizin, String ad, String kaynak) throws IOException {
        Files.createDirectories(dizin);
        Path p = dizin.resolve(ad + ".java");
        Files.writeString(p, kaynak);
        return p;
    }

    static boolean derle(Path cikti, List<Path> sinifYolu, List<Path> kaynaklar) throws IOException {
        Files.createDirectories(cikti);
        List<String> arg = new ArrayList<>(List.of("-d", cikti.toString()));
        if (!sinifYolu.isEmpty()) {
            StringJoiner sj = new StringJoiner(File.pathSeparator);
            for (Path p : sinifYolu) sj.add(p.toString());
            arg.add("-classpath");
            arg.add(sj.toString());
        }
        for (Path k : kaynaklar) arg.add(k.toString());
        ToolProvider derleyici = ToolProvider.findFirst("javac").orElseThrow();
        StringWriter sessizCikti = new StringWriter();
        int sonuc = derleyici.run(new PrintWriter(sessizCikti), new PrintWriter(sessizCikti),
                arg.toArray(new String[0]));
        return sonuc == 0;
    }

    static void goster(String etiket, Object deger) {
        System.out.printf("%-58s: %s%n", etiket, deger);
    }

    static Set<String> atiflar(Path sinifDosyasi) throws IOException {
        Set<String> adlar = new TreeSet<>();
        ClassModel model = ClassFile.of().parse(sinifDosyasi);
        for (PoolEntry e : model.constantPool()) {
            if (e instanceof ClassEntry ce) adlar.add(ce.asInternalName().replace('/', '.'));
        }
        return adlar;
    }

    /** Kaynagi hic olmayan, verilen arayuzu gerceklestiren tek yontemli bir sinif dosyasi uretir. */
    static byte[] uret(String sinifAdi, String arayuzAdi, String yontemAdi, int sabit) {
        ClassDesc kendi = ClassDesc.of(sinifAdi);
        ClassDesc arayuz = ClassDesc.of(arayuzAdi);
        return ClassFile.of().build(kendi, cb -> {
            cb.withFlags(ClassFile.ACC_PUBLIC);
            cb.withInterfaceSymbols(arayuz);
            cb.withMethodBody(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, ClassFile.ACC_PUBLIC, code -> {
                code.aload(0);
                code.invokespecial(ConstantDescs.CD_Object, ConstantDescs.INIT_NAME, ConstantDescs.MTD_void);
                code.return_();
            });
            cb.withMethodBody(yontemAdi,
                    MethodTypeDesc.of(ConstantDescs.CD_int, ConstantDescs.CD_int),
                    ClassFile.ACC_PUBLIC,
                    code -> {
                        code.iload(1);
                        code.loadConstant(sabit);
                        code.iadd();
                        code.ireturn();
                    });
        });
    }
}

Arac.uret hiçbir kaynak dosyası yazmıyor; doğrudan sınıf dosyasının bayt dizisini kuruyor — sabit havuzu, yöntem tablosunu, kod dizisini elle örüyor. Bu, önceki iki derste javac’ı ToolProvider ile çağıran Arac.derle’nin tam tersi: burada derleyicinin kendisi hiç devrede değil.

// KaynaksizUretim.java — sinif dosyasi kaynak metin hic yazilmadan uretiliyor, ama derlenmis bir sinif gibi calisiyor
import java.net.*;
import java.nio.file.*;
import java.util.*;

public class KaynaksizUretim {
    static String hesaplayiciKaynagi() {
        return "public interface Hesaplayici { int hesapla(int girdi); }";
    }

    public static void main(String[] args) throws Exception {
        Path kok = Files.createTempDirectory("gl17");
        Path kaynak = kok.resolve("kaynak");
        Path arayuzD = kok.resolve("arayuz");
        Path uretilenD = kok.resolve("uretilen");

        Path sHesaplayici = Arac.yaz(kaynak, "Hesaplayici", hesaplayiciKaynagi());
        Arac.derle(arayuzD, List.of(), List.of(sHesaplayici));

        Files.createDirectories(uretilenD);
        byte[] bayt = Arac.uret("UretilenSinif", "Hesaplayici", "hesapla", 1);
        Path sinifDosyasi = uretilenD.resolve("UretilenSinif.class");
        Files.write(sinifDosyasi, bayt);

        boolean kaynakVarMi = Files.exists(uretilenD.resolve("UretilenSinif.java"));
        boolean sinifVarMi = Files.exists(sinifDosyasi);

        URL[] url = { arayuzD.toUri().toURL(), uretilenD.toUri().toURL() };
        Object sonuc;
        try (URLClassLoader y = new URLClassLoader(url, KaynaksizUretim.class.getClassLoader().getParent())) {
            Class<?> arayuz = Class.forName("Hesaplayici", true, y);
            Class<?> sinif = Class.forName("UretilenSinif", true, y);
            Object ornek = sinif.getDeclaredConstructor().newInstance();
            sonuc = arayuz.getMethod("hesapla", int.class).invoke(ornek, 10);
        }

        Arac.goster("UretilenSinif.java kaynak dosyasi diskte var mi", kaynakVarMi);
        Arac.goster("UretilenSinif.class sinif dosyasi diskte var mi", sinifVarMi);
        Arac.goster("uretilen sinifin hesapla(10) sonucu", sonuc);
    }
}
UretilenSinif.java kaynak dosyasi diskte var mi           : false
UretilenSinif.class sinif dosyasi diskte var mi           : true
uretilen sinifin hesapla(10) sonucu                       : 11

Hesaplayici arayüzünün bir kaynak dosyası var ve javac ile derlendi; UretilenSinif’in hiç yok (GL16) — uretilenD dizininde yalnız .class dosyası duruyor. Buna rağmen bu sınıf Hesaplayici arayüzüne atanabiliyor ve hesapla yöntemi arayüz üzerinden çağrılabiliyor, sonuç doğru: 10 + 1 = 11 (GL17). Sanal makine için önemli olan, sınıf dosyasının formatına uyması ve bildirdiği arayüzün istediği imzayı taşıması; o dosyayı kimin, hangi kaynak dilden, hangi araçla ürettiği sanal makinenin sınıf yükleme kararına hiç girmiyor.

Çağıran Fark Görmeden Çağırıyor

GL18. Çağıranın derleme sınıf yolunda yalnız arayüz bulunur; hangi sınıfın gerçekleştirimi sağlayacağı çağıranın derleme anında bilmediği bir karardır — kaynak dilinin adı dahil. GL19. Aynı arayüze karşı derlenmiş çağıran, kaynağı Java ile yazılmış bir gerçekleştirim ile kaynağı hiç olmayan bir gerçekleştirim arasında hiçbir davranış farkı görmez.

// FarkGormeden.java — kaynagi Java olan gerceklestirim ile kaynagi hic olmayan uretilen sinif, cagirandan ayirt edilemiyor
import java.net.*;
import java.nio.file.*;
import java.util.*;

public class FarkGormeden {
    static String hesaplayiciKaynagi() {
        return "public interface Hesaplayici { int hesapla(int girdi); }";
    }

    static String gercekSinifKaynagi() {
        return "public class GercekSinif implements Hesaplayici {\n"
             + "  public int hesapla(int girdi) { return girdi + 1; }\n"
             + "}";
    }

    static String cagiranKaynagi() {
        return "public class Cagiran {\n"
             + "  public static java.util.List<Integer> sonuclar = new java.util.ArrayList<>();\n"
             + "  public static void main (String[] a) throws Exception {\n"
             + "    String sinifAdi = a[0];\n"
             + "    Hesaplayici h = (Hesaplayici) Class.forName(sinifAdi).getDeclaredConstructor().newInstance();\n"
             + "    for (int girdi : new int[]{ 1, 5, 100 }) sonuclar.add(h.hesapla(girdi));\n"
             + "  }\n"
             + "}";
    }

    public static void main(String[] args) throws Exception {
        Path kok = Files.createTempDirectory("gl18");
        Path kaynak = kok.resolve("kaynak");

        Path sHesaplayici = Arac.yaz(kaynak, "Hesaplayici", hesaplayiciKaynagi());
        Path sGercek = Arac.yaz(kaynak, "GercekSinif", gercekSinifKaynagi());
        Path sCagiran = Arac.yaz(kaynak, "Cagiran", cagiranKaynagi());

        Path arayuzD = kok.resolve("arayuz");
        Path gercekD = kok.resolve("gercek");
        Path cagiranD = kok.resolve("cagiran");
        Path uretilenD = kok.resolve("uretilen");

        Arac.derle(arayuzD, List.of(), List.of(sHesaplayici));
        Arac.derle(gercekD, List.of(arayuzD), List.of(sGercek));
        Arac.derle(cagiranD, List.of(arayuzD), List.of(sCagiran));

        Files.createDirectories(uretilenD);
        byte[] bayt = Arac.uret("UretilenSinif", "Hesaplayici", "hesapla", 1);
        Files.write(uretilenD.resolve("UretilenSinif.class"), bayt);

        Object gercekSonuc = calistirVeOku(List.of(cagiranD, arayuzD, gercekD), "GercekSinif");
        Object uretilenSonuc = calistirVeOku(List.of(cagiranD, arayuzD, uretilenD), "UretilenSinif");

        Arac.goster("kaynagi Java ile yazilmis GercekSinif'in sonuclari", gercekSonuc);
        Arac.goster("kaynagi hic olmayan UretilenSinif'in sonuclari", uretilenSonuc);
        Arac.goster("ikisi birebir ayni mi", gercekSonuc.equals(uretilenSonuc));

        Set<String> atif = Arac.atiflar(cagiranD.resolve("Cagiran.class"));
        Arac.goster("cagiranin sinif dosyasi Hesaplayici'yi aniyor mu", atif.contains("Hesaplayici"));
        Arac.goster("cagiranin sinif dosyasi GercekSinif'i aniyor mu", atif.contains("GercekSinif"));
        Arac.goster("cagiranin sinif dosyasi UretilenSinif'i aniyor mu", atif.contains("UretilenSinif"));
    }

    static Object calistirVeOku(List<Path> sinifYolu, String argSinifAdi) throws Exception {
        URL[] url = new URL[sinifYolu.size()];
        for (int i = 0; i < url.length; i++) url[i] = sinifYolu.get(i).toUri().toURL();
        try (URLClassLoader y = new URLClassLoader(url, FarkGormeden.class.getClassLoader().getParent())) {
            Class<?> cagiran = Class.forName("Cagiran", true, y);
            cagiran.getMethod("main", String[].class).invoke(null, (Object) new String[]{ argSinifAdi });
            return cagiran.getField("sonuclar").get(null);
        }
    }
}
kaynagi Java ile yazilmis GercekSinif'in sonuclari        : [2, 6, 101]
kaynagi hic olmayan UretilenSinif'in sonuclari            : [2, 6, 101]
ikisi birebir ayni mi                                     : true
cagiranin sinif dosyasi Hesaplayici'yi aniyor mu          : true
cagiranin sinif dosyasi GercekSinif'i aniyor mu           : false
cagiranin sinif dosyasi UretilenSinif'i aniyor mu         : false

Cagiran kaynağında ne GercekSinif ne UretilenSinif adı hiç geçiyor; hangi sınıfın kullanılacağına komut satırı argümanı sinifAdi karar veriyor, ve bu karar koşum sınıf yoluyla birlikte dışarıdan geliyor (GL18, ilk dersteki cephe/gerçekleştirim ayrımının aynı kalıbı). Üç girdi üzerinden alınan sonuç listesi — [2, 6, 101] — iki koşumda da birebir aynı (GL19). Sabit havuz okuması bunu doğruluyor: Cagiran.class yalnız Hesaplayici’yi anıyor, ne GercekSinif’i ne UretilenSinif’i. Çağıranın gözünden iki gerçekleştirim arasında hiçbir fark yok, çünkü çağıranın gördüğü tek şey arayüz ve arayüzün verdiği söz; sınıfın kaynağının bir derleyiciden mi yoksa doğrudan bir bayt dizisi kurucusundan mı geldiği bu sözün parçası değil.

Sınırlayıcı Ölçüm: Söz Diziminde Karşılığı Olmayan Bir Üye

GL20. Sanal makinenin bir üye adına koyduğu kısıt Java dilinin bir tanıtıcıya koyduğu kısıttan çok daha gevşektir; birkaç ayrılmış karakterin (., ;, [, /) ve iki özel adın (<init>, <clinit>) dışında kalan hemen her dizge geçerli bir üye adıdır. GL21. Böyle bir ad taşıyan bir yöntem yalnız üretici tarafından ve yansıma (reflection) yoluyla çağrılabilir; kaynak metinde hiçbir çağrı satırı bu adı yazamaz, çünkü derleyicinin sözdizimi onu bir tanıtıcı olarak kabul etmez.

// SozDizimiDisi.java — sinif dosyasinin uyesi, hicbir Java kaynagi tarafindan yazilamayacak bir ad tasiyabiliyor
import java.lang.classfile.*;
import java.lang.constant.*;
import java.lang.reflect.*;
import java.net.*;
import java.nio.file.*;

public class SozDizimiDisi {
    public static void main(String[] args) throws Exception {
        Path kok = Files.createTempDirectory("gl20");
        Path uretilenD = kok.resolve("uretilen");
        Files.createDirectories(uretilenD);

        String gecerliAd = "hesapla";
        String gecersizAd = "2#kat";

        ClassDesc kendi = ClassDesc.of("IkiYontemliSinif");
        byte[] bayt = ClassFile.of().build(kendi, cb -> {
            cb.withFlags(ClassFile.ACC_PUBLIC);
            cb.withMethodBody(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, ClassFile.ACC_PUBLIC, code -> {
                code.aload(0);
                code.invokespecial(ConstantDescs.CD_Object, ConstantDescs.INIT_NAME, ConstantDescs.MTD_void);
                code.return_();
            });
            cb.withMethodBody(gecerliAd, MethodTypeDesc.of(ConstantDescs.CD_int, ConstantDescs.CD_int),
                    ClassFile.ACC_PUBLIC, code -> {
                        code.iload(1); code.iconst_1(); code.iadd(); code.ireturn();
                    });
            cb.withMethodBody(gecersizAd, MethodTypeDesc.of(ConstantDescs.CD_int, ConstantDescs.CD_int),
                    ClassFile.ACC_PUBLIC, code -> {
                        code.iload(1); code.loadConstant(2); code.imul(); code.ireturn();
                    });
        });
        Path sinifDosyasi = uretilenD.resolve("IkiYontemliSinif.class");
        Files.write(sinifDosyasi, bayt);

        URL[] url = { uretilenD.toUri().toURL() };
        try (URLClassLoader y = new URLClassLoader(url, SozDizimiDisi.class.getClassLoader().getParent())) {
            Class<?> sinif = Class.forName("IkiYontemliSinif", true, y);
            Object ornek = sinif.getDeclaredConstructor().newInstance();

            Method gecerli = sinif.getMethod(gecerliAd, int.class);
            Method gecersiz = sinif.getMethod(gecersizAd, int.class);

            boolean gecerliTanimlayiciMi = gecersizAd.length() > 0
                    && Character.isJavaIdentifierStart(gecersizAd.charAt(0))
                    && gecersizAd.chars().allMatch(Character::isJavaIdentifierPart);

            Arac.goster("gecerli addaki yontemin (" + gecerliAd + ") cagri sonucu", gecerli.invoke(ornek, 10));
            Arac.goster("gecersiz addaki yontemin (" + gecersizAd + ") cagri sonucu", gecersiz.invoke(ornek, 10));
            Arac.goster("\"" + gecersizAd + "\" gecerli bir Java tanimlayicisi mi", gecerliTanimlayiciMi);
        }
    }
}
gecerli addaki yontemin (hesapla) cagri sonucu            : 11
gecersiz addaki yontemin (2#kat) cagri sonucu             : 20
"2#kat" gecerli bir Java tanimlayicisi mi                 : false

IkiYontemliSinif iki yöntem taşıyor: hesapla, geçerli bir Java tanıtıcısı, ve 2#kat, değil — rakamla başlıyor ve bir tanıtıcının taşıyamayacağı # karakterini içeriyor. Character.isJavaIdentifierStart/isJavaIdentifierPart denetimi bunu doğruluyor: false (GL20). Buna rağmen sanal makine bu adı reddetmiyor; sınıf yükleniyor, getMethod("2#kat", ...) onu buluyor, invoke onu çağırıyor ve sonuç doğru: 10 * 2 = 20. Bu, dersin sınırlayıcı ölçümü: koşum sınıf yoluna giren bir üye, kaynak dilin sözdizim kurallarına uymak zorunda değil, yalnız sınıf dosyası biçiminin kurallarına uymak zorunda. Bu ayrım tek bir cümleyle sınırlanır — paylaşılan şey burada anlambilim değil, yalnız biçimdir: 2#kat adlı yöntemin ne anlama geldiğini, hangi sözleşmeyi karşıladığını sanal makine hiç bilmiyor, yalnız çağrılabilir bir üye olarak kabul ediyor. Bu satırı hiçbir Java kaynak dosyası yazamaz (GL21); üreten taraf onu doğrudan bayt dizisine yazdı, çağıran taraf da onu yalnız bir dizge olarak sağlayan yansıma ile buldu.

Özet

  • Bir sınıf dosyası üretmenin zorunlu yolu kaynak metin yazıp derlemek değildir; sınıf dosyası biçimini doğrudan üreten bir yordam da sanal makinenin yükleyeceği bir sınıf dosyası üretebilir.
  • Böyle üretilen bir sınıf, bir arayüzü gerçekleştirdiğini bildirdiği ve arayüzün istediği imzayı taşıdığı sürece, kaynaktan derlenmiş bir sınıf gibi yüklenir ve çağrılır.
  • Çağıranın derleme sınıf yolunda yalnız arayüz bulunur; kaynağı Java olan bir gerçekleştirim ile kaynağı hiç olmayan bir gerçekleştirim arasında çağıranın gördüğü ya da sınıf dosyasının andığı hiçbir fark yoktur.
  • Sanal makinenin bir üye adına koyduğu kısıt, Java dilinin bir tanıtıcıya koyduğu kısıttan çok daha gevşektir; birkaç ayrılmış karakterin dışında kalan hemen her dizge geçerli bir üye adıdır.
  • Sınırlayıcı ölçüm: üretilen bir sınıf dosyası, hiçbir Java kaynağının yazamayacağı bir üye adı taşıyabilir; bu üye yalnız yansıma ile, adı bir dizge olarak vererek çağrılabilir.

Kurs Kapanışı

Kurs tek bir soruyla açıldı: bir Java programı iki ayrı sınıf yoluna karşı yaşıyor — derlendiği yola ve koştuğu yola — ve kaynak metin ikisini de göremiyor. On iki ders bu soruyu dört ayrı düzenekte sordu: derleme aracının bildirimden iki yol çıkarması, testin üçüncü bir yol eklemesi, günlükleme cephesinin bu ayrımı isteyerek kurması, ve bu son derste koşum sınıf yolunun kaynak dilini hiç görmediğinin gösterilmesi.

Birinci iddia doğrulandı: derlenen metin, koşacak programı belirlemiyor — bu derste çağıranın gördüğü tek şey bir arayüzdü ve arkasındaki sınıfın bir derleyiciden geçip geçmediği fark etmedi. İkinci iddia, kusurun çoğunlukla gürültülü olmadığı, günlükleme cephesinde bir kez daha görüldü: gerçekleştirim bulunmadığında çağıran çökmüyor, yalnız konuşmuyor. Üçüncü iddia — bağımlılığın yönünün derlenmiş üründe okunabilen bir olgu olduğu — kursun üç konusunda da sabit havuzdan okunarak sınandı. Sınır da baştan beri aynıydı: “derlendi” bir kanıt değil.

Ders Ölçülen davranış Gereken yol (derleme · koşum · ikisi) Sınırlayıcı ölçüm
Derleme Aracı Modeli 3 bildirilen ögeden 4 evre türüyor; sıra bildirimin yazılış sırasından bağımsız ikisi (aynı bildirimden iki yol birden çıkıyor) Kaynakta kalan açıklama tipi sabit havuzda hiç görünmüyor
Bağımlılık Yönetimi Altı tipte dağılım 1 derleme, 3 koşum, 2 ikisi; dört kusurun ikisi sessiz derleme (1) · koşum (3) · ikisi (2) Koşum yolunda gerekmemek bağımlılığın yokluğu değil, değerin gömülmüş olması olabilir
Çoklu Modül Projeler Üç sınır kurulumundan biri derlemede, biri koşumda yakalanıyor, biri hiç yakalanmıyor derleme · koşum (aynı tipin iki kullanımı) Sınırı kuran dizin değil yükleyicinin kararı; iki sınıf aynı dizinde kalıyor
Paketleme ve Çalıştırılabilir Çıktı Kendine yeten arşiv tek başına koşuyor, dışa muhtaç arşiv koşum hatasıyla düşüyor koşum (arşivin taşıdığı yol) Giriş noktası kaynakta değil bildirimde; derlemenin geçmesi arşivin yeterliğini göstermiyor
Birim Test Çerçevesi Üretim yolu bir tip, test yolu üç tip; çerçeve tipi eksikken aynı sınıf koşumda düşüyor ikisi (test yolu üçüncü yol) Çerçevenin çağırdığı sıra doğruluğu vermiyor: aynı iddia sökmeden önce geçiyor, sonra düşüyor
Sahteleme Aynı derlenmiş test iki koşum yoluyla iki ayrı sonuç veriyor, ikisi de hatasız koşum (sahte tipin yeri) İmza ayrıldığında kusur derlemede değil koşumda düşüyor, nedeni test metninde görünmüyor
Parametreli Testler Tek gövde üç veri satırına karşı çağrılıyor; bağımsız kurulumda üçü de geçiyor koşum (veri bildirimi) Kurulum paylaşıldığında hangi satırın geçtiği veri sırasına bağlı hâle geliyor
Tümleşim Testleri Yolun içeriği bir tipten iki tipe çıkıyor; aynı istek birimde sessiz, tümleşimde istisna koşum (gerçek gerçekleştirim ve bağımlısı) Gerçek tipi geri koymak testi doğrulamıyor, yalnız başka bir kusur sınıfını görünür kılıyor
Davranış Odaklı Test Dört adımın üçü eşleşiyor; eksik adım bir kurulumda sessiz, ötekinde koşumu durduruyor koşum (adım haritası) Tam eşleşme doğruluğu göstermiyor: iddiası kaldırılmış harita yanlış değerle geçti diyor
Günlükleme Cephesi ve Gerçekleştirim Çağıranın sınıf dosyası cepheyi anıp gerçekleştirimi anmaması; gerçekleştirimin servis bildirimiyle koşum sınıf yoluna girip sırayla seçilmesi ikisi (cephe) · koşum (gerçekleştirim) Gerçekleştirim hiç bulunmadığında çağıran derlenip hatasız koşar ama hiçbir kayıt üretmez
Günlük Düzeyleri ve Yapılandırılmış Günlük Aynı çağrı kümesinin iki ayrı koşum eşiğinde ürettiği kayıt sayısı; alan tutarlılığının okunabilirliğe etkisi koşum (eşik ve işleyici ayarı, alan bildirimi) Eşiğin altında kalan çağrının argümanı dizgi birleştirmeyle yine değerlendirilir, Supplier ile değerlendirilmez
JVM Üzerinde Diğer Diller Kaynak metni olmadan üretilen bir sınıfın, arayüzü gerçekleştirdiği sürece derlenmiş bir sınıf gibi çağrılması koşum (üretilen sınıf dosyası) Üretilen sınıf dosyası Java söz diziminde karşılığı olmayan bir üye adı taşıyabilir, çağıran onu yalnız yansıma ile çağırabilir

Derleme, Test ve Ekosistem burada kapanıyor. Kursun sorduğu soru — bu tip hangi sınıf yolunda bulunmak zorunda — bir Java programının derlenmesiyle koşması arasındaki her adımda hep durur; kaynak metin bu sorunun yanıtını hiçbir zaman kendiliğinden vermiyor.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat