SzakaszokSzerkesztés
A Java osztályfájl szerkezetének 10 alapvető szakasza van:
- Mágikus szám: 0xCAFEBABE
- Az osztályfájl formátumának verziója: az osztályfájl kisebb és nagyobb verziója
- Állandó állomány: Az osztály konstansainak tárháza
- Access Flags: például, hogy az osztály absztrakt, statikus stb.
- This Class: Az aktuális osztály neve
- Super Class: A szuperosztály neve
- Interfaces: Az osztályban lévő bármely interfész
- Fields: Az osztályban található bármely mező
- Módszerek: Bármely metódus az osztályban
- Attribútumok: Az osztály bármely attribútuma (például a forrásfájl neve stb.)
Magic NumberEdit
Az osztályfájlokat a következő 4 bájtos fejléccel azonosítjuk (hexadecimálisan): CA FE BA BE
(az alábbi táblázat első 4 bejegyzése). Ennek a bűvös számnak a történetét James Gosling egy Palo Alto-i étteremre utalva magyarázta el:
“Egy St Michael’s Alley nevű helyre jártunk ebédelni. A helyi legenda szerint a mély, sötét múltban a Grateful Dead lépett fel ott, mielőtt nagyot alkottak. Eléggé funky hely volt, ami határozottan egy Grateful Dead-féle hely volt. Amikor Jerry meghalt, még egy kis buddhista szentélyt is felállítottak. Amikor oda jártunk, Cafe Deadként emlegettük a helyet. Valamikor aztán észrevettük, hogy ez egy HEX szám. Átalakítottam néhány fájlformátum kódot, és szükségem volt néhány mágikus számra: egy a perzisztens objektumfájlhoz, és egy az osztályokhoz. A CAFEDEAD-et használtam az objektumfájl formátumhoz, és a “CAFE” után megfelelő 4 karakteres hexa szavak keresése közben (jó témának tűnt) rábukkantam a BABE-ra, és úgy döntöttem, hogy ezt fogom használni. akkoriban nem tűnt túl fontosnak, vagy arra szántam, hogy a történelem szemétkosarába kerüljön. Így lett a CAFEBABE az osztályfájl-formátum, a CAFEDEAD pedig a tartós objektumformátum. De a perzisztens objektum lehetőség eltűnt, és vele együtt eltűnt a CAFEDEAD használata is – végül az RMI váltotta fel.
Általános layoutEdit
Mivel az osztályfájl változó méretű elemeket tartalmaz, és nem tartalmaz beágyazott fájloffszetet (vagy mutatót) is, általában szekvenciálisan, az első bájttól a vége felé elemzik. A legalacsonyabb szinten a fájlformátum néhány alapvető adattípussal írható le:
- u1: egy előjel nélküli 8 bites egész szám
- u2: egy előjel nélküli 16 bites egész szám big-endian bájtsorrendben
- u4: egy előjel nélküli 32 bites egész szám big-endian bájtsorrendben
- táblázat: valamilyen típusú, változó hosszúságú elemek tömbje. A táblázatban lévő elemek számát az azt megelőző számlálószám azonosítja (a számlálószám egy u2), de a táblázat bájtokban kifejezett mérete csak az egyes elemek vizsgálatával határozható meg.
Ezek közül az alapvető típusok közül néhányat a kontextustól függően magasabb szintű értékként (például stringként vagy lebegőpontos számként) értelmezünk újra.Nincs szókiigazítás kényszerítése, ezért soha nem használunk töltőbájtokat.Az osztályfájl teljes elrendezése a következő táblázatban látható.
byte offset | size | type or value | description |
---|---|---|---|
0 | 4 byte | u1 = 0xCA hex |
mágikus szám (CAFEBABE), amellyel a fájlt az osztályfájl formátumnak megfelelőként azonosítják |
1 | u1 = 0xFE hex |
||
2 | u1 = 0xBA hex |
||
3 | u1 = 0xBE hex |
||
4 | 2 bájt | u2 | minor verziószám a használt osztályfájlformátum |
5 | |||
6 | 2 bájt | u2 | a használt osztályfájlformátum fő verziószáma.
Java SE 17 = 61 (0x3D hex), |
7 | |||
8 | 2 byte | u2 | constant pool count, a következő constant pool táblázat bejegyzéseinek száma. Ez a szám legalább eggyel nagyobb, mint a bejegyzések tényleges száma; lásd a következő tárgyalást. |
9 | |||
10 | cpsize (változó) | table | constant pool table, változó méretű konstans pool bejegyzések tömbje, amely olyan elemeket tartalmaz, mint literális számok, karakterláncok, osztályokra vagy módszerekre való hivatkozások. Az 1-gyel kezdődően indexelt, összesen (konstanskészlet-szám – 1) számú bejegyzést tartalmaz (lásd a megjegyzést). |
… | |||
… | |||
… | |||
… | |||
10+cpsize | 2 byte | u2 | access flags, egy bitmaszk |
11+cpsize | |||
12+cpsize | 2 byte | u2 | azonosítja ezt az osztályt, index a konstanskészletben egy “Class”-típusú bejegyzéshez |
13+cpsize | |||
14+cpsize | 2 byte | u2 | azonosítja a szuperosztályt, index a konstans poolban egy “Class”-típusú bejegyzéshez |
15+cpsize | |||
16+cpsize | 2 byte | u2 | interfész szám, a következő interfész táblában |
17+cpsize | |||
18+cpsize | isize (változó) | table | interface table: Az osztály által megvalósított interfészeket leíró konstans pool indexek változó hosszúságú tömbje |
… | |||
… | |||
… | |||
18+cpsize+isize | 2 byte | u2 | mezőszám, a következő mezőtábla bejegyzéseinek száma |
19+cpsize+isize | |||
20+cpsize+isize | fsize (változó) | table | field table, változó hosszúságú mezőtömb
minden elem a https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5 |
-ben definiált field_info struktúra… | |||
… | |||
… | |||
20+cpsize+isize+fsize | 2 byte | u2 | method count, a következő módszertáblában |
21+cpsize+isize+fsize | |||
22+cpsize+isize+fsize | msize (változó) | table | method table, Módszerek változó hosszúságú tömbje
minden elem a https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.6 |
-ben definiált method_info struktúra… | |||
… | |||
… | |||
22+cpsize+isize+fsize+msize | 2 byte | u2 | attribute count, bejegyzések száma a következő attribútumtáblában |
23+cpsize+isize+fsize+msize | |||
24+cpsize+isize+fsize+msize | asize (változó) | table | attribute table, változó hosszúságú attribútumtömb
minden elem egy https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7 |
-ben definiált attribute_info struktúra… | |||
… | |||
… |
C-szerű programozási nyelven való ábrázolásSzerkesztés
Mivel a C nem támogatja a többszörös változó hosszúságú tömböket egy struktúrán belül, az alábbi kód nem fordítható le, és csak demonstrációként szolgál.
struct Class_File_Format { u4 magic_number; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces; u2 fields_count; field_info fields; u2 methods_count; method_info methods; u2 attributes_count; attribute_info attributes;}
A konstanskészletEdit
A konstanskészlet táblában tároljuk a legtöbb literális konstans értéket. Ide tartoznak az olyan értékek, mint a mindenféle számok, karakterláncok, azonosítónevek, osztályokra és metódusokra való hivatkozások, valamint típusleírók. A konstanskészlet-táblában az összes indexet, illetve a konkrét konstansokra való hivatkozást 16 bites (u2 típusú) számok adják meg, ahol az 1-es indexérték a táblázat első konstansára utal (a 0-s indexérték érvénytelen).
A fájlformátum fejlesztése során hozott történelmi döntések miatt a konstanskészlet-táblában lévő konstansok száma valójában nem azonos a táblázatot megelőző konstanskészlet számával. Először is, a táblázat indexelése 1-től kezdődik (és nem 0-tól), de a számot valójában a maximális index plusz egy indexként kell értelmezni. Továbbá, a konstansok két típusa (hosszúak és duplák) két egymást követő helyet foglal el a táblázatban, bár a második ilyen hely egy fantomindex, amelyet soha nem használunk közvetlenül.
A konstanskészletben lévő minden egyes elem (konstans) típusát egy kezdeti bájtcímke azonosítja. Az ezt követő bájtok száma és értelmezése a tag értékétől függ. Az érvényes konstans típusok és azok tag értékei a következők:
Tag bájt | Kiegészítő bájtok | A konstans leírása | Bevezetett verzió |
---|---|---|---|
1 | 2+x bájt (változó) |
UTF-8 (Unicode) string: (u2 típusú) karakterlánc, amelynek előtagja egy 16 bites szám (u2 típusú), amely a közvetlenül utána következő kódolt karakterlánc bájtjainak számát jelzi (amely eltérhet a karakterek számától). Megjegyzendő, hogy az alkalmazott kódolás valójában nem UTF-8, hanem a Unicode szabványos kódolási forma enyhe módosítását jelenti. | 1.0.2 |
3 | 4 bájt | Integer: egy előjeles 32 bites kettes komplement szám big-endian formátumban | 1.0.2 |
4 | 4 bájt | Float: 32 bites, egyszeres pontosságú IEEE 754 lebegőpontos szám | 1.0.2 |
5 | 8 bájt | Long: egy előjeles 64 bites kétkomplementes szám big-endian formátumban (két helyet foglal el a konstanskészlet táblázatban) | 1.0.2 |
6 | 8 bájt | Double: 64 bites kétszeres pontosságú IEEE 754 lebegőpontos szám (két helyet foglal el a konstansok táblájában) | 1.0.2 |
7 | 2 bájt | Osztályhivatkozás: a konstanskészleten belüli index egy UTF-8 karakterlánchoz, amely a teljesen minősített osztály nevét tartalmazza (belső formátumban) (big-endian) | 1.0.2 |
8 | 2 bájt | String hivatkozás: a konstanskészleten belüli index egy UTF-8 karakterláncra (szintén big-endian) | 1.0.2 |
9 | 4 bájt | Mezőhivatkozás: két index a konstanskészleten belül, az első egy osztályhivatkozásra, a második egy név és típus leíróra mutat. (big-endian) | 1.0.2 |
10 | 4 byte | Módszerreferencia: két index a konstanskészleten belül, az első egy osztályreferenciára, a második egy név- és típusleíróra mutat. (big-endian) | 1.0.2 |
11 | 4 byte | Interface metódus hivatkozás: két index a konstanskészleten belül, az első egy Class hivatkozásra, a második egy Name and Type leíróra mutat. (big-endian) | 1.0.2 |
12 | 4 bájt | Név és típus leíró: két index a konstanskészleten belüli UTF-8 karakterláncokra, az első egy névre (azonosítóra), a második egy speciálisan kódolt típus leíróra mutat. | 1.0.2 |
15 | 3 bájt | Módszerkezelő: Ez a struktúra egy módszerkezelő ábrázolására szolgál, és egy bájt típusleíróból, majd a konstanskészleten belüli indexből áll. | 7 |
16 | 2 bájt | Módszertípus: Ez a struktúra egy módszertípus reprezentálására szolgál, és egy indexből áll a konstanskészleten belül. | 7 |
17 | 4 bájt | Dinamikus: ez egy bootstrap módszer meghívásával előállított dinamikusan kiszámított konstans megadására szolgál. | 11 |
18 | 4 bájt | InvokeDynamic: ezt egy invokedynamic utasítás használja egy bootstrap módszer, a dinamikus hívás nevének, a hívás argumentum- és visszatérési típusainak, valamint opcionálisan a bootstrap módszer statikus argumentumainak nevezett további konstansok sorozatának megadására. | 7 |
19 | 2 bájt | Modul: egy modul azonosítására szolgál. | 9 |
20 | 2 bájt | Package: egy modul által exportált vagy megnyitott csomag azonosítására szolgál. | 9 |
Kizárólag két integrál konstans típus létezik, az integer és a long. A magas szintű nyelvben megjelenő egyéb integrál típusokat, mint például a boolean, a byte és a short, egész konstansként kell ábrázolni.
A Java-ban az osztálynevek, ha teljes minősítésűek, hagyományosan ponttal vannak elválasztva, például “java.lang.Object”. Az alacsony szintű osztályhivatkozási konstansokon belül azonban megjelenik egy olyan belső forma, amely ehelyett kötőjeleket használ, például “java/lang/Object”.
A Unicode karakterláncok az “UTF-8 string” elnevezés ellenére valójában nem a Unicode szabvány szerint vannak kódolva, bár hasonló. Két különbség van (lásd az UTF-8 teljes körű tárgyalását). Az első az, hogy az U+0000 kódpontot a szabványos egybájtos 00
kódolás helyett a kétbájtos C0 80
(hexában) kódolással kódolják. A második különbség az, hogy a kiegészítő karakterek (a BMP-n kívül eső karakterek az U+10000 és afelett) kódolása az UTF-16-hoz hasonló helyettesítőpáros konstrukcióval történik, ahelyett, hogy közvetlenül az UTF-8 kódolással történne. Ebben az esetben mindkét helyettesítő karaktert külön-külön kódoljuk az UTF-8-ban. Például az U+1D11E kódolása a 6 bájtos ED A0 B4 ED B4 9E
szekvenciaként történik, ahelyett, hogy a helyes 4 bájtos UTF-8 kódolás F0 9D 84 9E
.
lenne.