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.