inhoud
introductie
dynamische Proxy-API
serialisatie
voorbeelden
introductie
een dynamische proxy-klasse is een klasse die een lijst van interfaces implementeert die tijdens runtime zijn gespecificeerd, zodat een methode-aanroep via een van de interfaces op een instantie van de klasse gecodeerd en verzonden wordt naar een ander object via een uniforminterface. Zo kan een dynamische proxy klasse worden gebruikt om een type-veilig proxy object te maken voor een lijst van interfaces zonder dat vooraf generatie van de proxy klasse vereist is, zoals met compile-time tools.Methode aanroepingen op een instantie van een dynamische proxy klasse worden verzonden naar een enkele methode in de aanroephandler van de instantie, en ze zijn gecodeerd met eenjava.lang.reflect.Method
object dat de methode identificeert die werd aangeroepen en een array van type Object
die de argumenten bevat.
dynamische proxyklassen zijn nuttig voor een toepassing of bibliotheekdie typeveilige reflecterende verzending van aanroepingen op objecten met interface-API ‘ s moet bieden. Een toepassing kan bijvoorbeeld een dynamische proxy-Klasse gebruiken om een object te maken dat meerdere willekeurige gebeurtenislistener-interfaces implementeert– interfaces die java.util.EventListener
uitbreiden– om een verscheidenheid aan gebeurtenissen van verschillende typen op een uniforme manier te verwerken, zoals door al dergelijke gebeurtenissen naar een bestand te loggen.
dynamische Proxy-klasse API
een dynamische proxy-klasse (hieronder simpelweg een proxy-klasse genoemd) is een klasse die een lijst implementeert van interfaces die gespecificeerd zijn tijdens runtime wanneer de klasse wordt aangemaakt.
een proxy-interface is zo ‘ n interface die wordt uitgevoerd door een proxy-klasse.
een proxy-instantie is een instantie van een proxyklasse.
het maken van een Proxy Klasse
Proxy klassen, evenals instanties van hen, worden gemaakt met behulp van de statische methoden van de klasse java.lang.weerspiegelen.Proxy.
de Proxy.getProxyClass
methode geeft hetjava.lang.Class
object terug voor een proxy klasse gegeven een classloader en een array van interfaces. De proxy klasse zal worden gedefinieerd in de opgegeven klasse loader en zal alle van de geleverde interfaces implementeren. Als een proxy-klasse voor dezelfde permutatie van interfaces al is gedefinieerd in de class loader, dan zal de bestaande proxy-klasse worden geretourneerd; anders zal een proxy-klasse voor deze interfaces dynamisch worden gegenereerd en gedefinieerd in de class loader.
er zijn verschillende beperkingen voor de parameters die kunnen worden toegepast op Proxy.getProxyClass
:
- alle
Class
objecten in deinterfaces
array moeten interfaces, notclasses of primitieve types vertegenwoordigen. - geen twee elementen in de
interfaces
array mogen verwijzen naar identiekeClass
objecten. - alle interfacetypen moeten zichtbaar zijn op naam via de gespecificeerde klassenlader. Met andere woorden, voor klasse Lader
cl
en elke interfacei
moet de volgende expressie waar zijn:Class.forName(i.getName(), false, cl) == i
- alle niet-publieke interfaces moeten in hetzelfde pakket zitten;anders zou het niet mogelijk zijn voor de proxy klasse om alle interfaces uit te voeren, ongeacht in welk pakket het is gedefinieerd.
- voor elke reeks ledenmethoden van de gespecificeerde interfaces die dezelfde handtekening hebben:
- als het retourtype van een van de methoden een primitief type orvoid is, moeten alle methoden hetzelfde retourtype hebben.
- anders moet een van de methoden een retourtype hebben dat kan worden toegeschreven aan alle retourtypes van de rest van de methoden.
- de resulterende proxy-klasse mag geen limieten overschrijden die door de virtuele machine aan klassen zijn opgelegd. De VM kan bijvoorbeeld het aantal interfaces dat een klasse kan implementeren beperken tot 65535; in dat geval mag de grootte van de
interfaces
array niet groter zijn dan 65535.
als een van deze beperkingen wordt geschonden, zalProxy.getProxyClass
eenIllegalArgumentException
gooien. Als het interfaces
array argument of een van de elementennull
is, zal een NullPointerException
worden omvergeworpen.
merk op dat de volgorde van de opgegeven proxy-interfaces significant is: twee verzoeken voor een proxy-klasse met dezelfde combinatie van interfaces maar in een andere volgorde zullen resulteren in twee verschillende proxy-klassen. Proxy klassen worden onderscheiden door de volgorde van hun proxy interfaces om deterministicmethod aanroep codering in gevallen waarin twee of meer van de proxy interfaces delen een methode met dezelfde naam en parametersignatuur; deze redenering wordt in meer detail beschreven in de sectie hieronder getiteld methoden gedupliceerd inMultiple Proxy Interfaces.
zodat een nieuwe proxy-klasse niet elke keer hoeft te worden gegenereerd Proxy.getProxyClass
wordt aangeroepen met dezelfde klasse-lader en lijst van interfaces, moet de implementatie van de dynamische proxy-klasse API een cache van gegenereerde proxy-klassen bijhouden, gekoppeld aan de bijbehorende laders en interface-lijst.De implementatie moet voorzichtig zijn om niet te verwijzen naar de classloaders, interfaces, en proxy klassen op een zodanige manier dat class laders, en al hun klassen, te voorkomen dat garbage collected wanneer van toepassing.
Proxy Klasse eigenschappen
een proxy klasse heeft de volgende eigenschappen:
- Proxy klassen zijn openbaar, definitief, en niet abstract.
- de niet-gekwalificeerde naam van een proxy-klasse is niet gespecificeerd. De ruimte van klassenamen die begint met de tekenreeks
"$Proxy"
moet echter gereserveerd worden voor proxy-klassen. - een proxy-klasse breidt
java.lang.reflect.Proxy
uit. - een proxy-klasse implementeert precies de interfaces die zijn opgegeven voor het aanmaken van atits, in dezelfde volgorde.
- als een proxy klasse een niet-publieke interface implementeert, dan zal deze in hetzelfde pakket als die interface worden gedefinieerd. Anders is het pakket van een proxy klasse ook niet gespecificeerd. Merk op dat packagesealing niet zal voorkomen dat een proxy class succesvol wordt gedefinieerd in een bepaald pakket tijdens runtime, en evenmin zalclasses al gedefinieerd in dezelfde klasse loader en hetzelfde pakket met bepaalde ondertekenaars.
- aangezien een proxy Klasse alle interfaces implementeert die bij het aanmaken zijn opgegeven, zal
getInterfaces
op zijnClass
object een array retourneren die dezelfde lijst van interfaces bevat (in de volgorde die is opgegeven bij het aanmaken),getMethods
op zijnClass
object zal een array vanMethod
objecten retourneren die alle methoden in die interfaces bevatten, engetMethod
zal methoden in de proxy interfaces vinden zoals verwacht zou worden. - de methode
Proxy.isProxyClass
geeft true terug als het wordt doorgegeven aan een proxy-Klasse– Een klasse geretourneerd doorProxy.getProxyClass
of de klasse van een object geretourneerd doorProxy.newProxyInstance
— en anders false. De betrouwbaarheid van deze methode is belangrijk voor de mogelijkheid om het te gebruiken om beveiligingsbeslissingen te nemen, dus de implementatie ervan moet niet alleen testen of de klasse in kwestie zich uitbreidt totjava.lang.reflect.Proxy
. - de
java.security.ProtectionDomain
van een proxy-klasse is dezelfde als die van systeemklassen die door de bootstrapclass-lader worden geladen, zoalsjava.lang.Object
, omdat de code voor een proxy-klasse wordt gegenereerd door vertrouwde systeemcode. Dit beschermdomein wordt doorgaans verleendjava.security.AllPermission
.
een proxy-Instance aanmaken
elke proxy-klasse heeft één publieke constructor die één jaar geleden is gestart, een implementatie van de interface InvocationHandler
.
elke proxy-instantie heeft een geassocieerd aanroep-handler-object,het object dat aan de constructor is doorgegeven. In plaats van gebruik te maken van de reflectie-API om toegang te krijgen tot de publieke constructor, kan een proxy-instance ook worden gemaakt door deProxy.newProxyInstance
methode aan te roepen, die de handelingen van Proxy.getProxyClass
combineert met het aanroepen van de constructor met een aanroephandler.Proxy.newProxyInstance
gooitIllegalArgumentException
om dezelfde redenen alsProxy.getProxyClass
.
Proxy-Instance Eigenschappen
Een proxy instantie heeft de volgende eigenschappen:
- Gegeven een proxy-instance
proxy
en een van theinterfaces uitgevoerd door de proxy klasseFoo
, de volgende expressie return true:proxy instanceof Foo
en de volgende cast operatie zal slagen (in plaats van throwinga
ClassCastException
):(Foo) proxy
- De statische
Proxy.getInvocationHandler
methode willreturn het inroepen van de handler gekoppeld aan de proxy instancepassed als argument. Als het object dat aanProxy.getInvocationHandler
is doorgegeven Geen proxy-instantie is, dan zal eenIllegalArgumentException
worden gegooid. - een interfacemethode aanroep op een proxy-instantie wordt gecodeerd en verzonden naar de
invoke
methode van de aanroepafhandeling, zoals hieronder beschreven.de proxy-instantie zelf zal worden doorgegeven als het eerste argument van
invoke
, dat van het typeObject
is.het tweede argument dat wordt doorgegeven aan
invoke
zal dejava.lang.reflect.Method
instantie zijn die overeenkomt met de interface methode die wordt aangeroepen op de proxy instantie. De declarerende klasse van hetMethod
object zal de interface zijn waarin de methode is gedeclareerd, wat een superinterface kan zijn van het proxyinterface waar de proxy-klasse de methode mee erft.het derde argument dat wordt doorgegeven aan
invoke
zal een array zijn van objecten die de waarden bevatten van de argumenten die worden doorgegeven in de methode aanroep op de proxy instantie. Argumenten van primitivetypes worden verpakt in een instantie van de juiste primitivewrapper klasse, zoalsjava.lang.Integer
ofjava.lang.Boolean
. De implementatie van deinvoke
methode is vrij om de inhoud van deze serie te wijzigen.de waarde geretourneerd door de
invoke
methode wordt de retourwaarde van de methode aanroep op de proxy instantie. Als de gedeclareerde return-waarde van de interface-methode een primitivetype is, dan moet de waarde dieinvoke
wordt geretourneerd een instantie zijn van de corresponderende primitieve wrapper-klasse; anders moet het een type zijn dat kan worden toegewezen aan het gedeclareerde return-type. Als de waarde geretourneerd doorinvoke
null
is en het returntype van de interface methode primitief is, dan zal eenNullPointerException
worden gegooid door de methodinvocation op de proxy instantie. Als de metinvoke
geretourneerde waarde anders niet compatibel is met het gedeclareerde retourtype van de methode zoals hierboven beschreven, zal eenClassCastException
worden gegooid door de proxyinstance.als een uitzondering wordt gegooid door de
invoke
methode, zal deze ook worden gegooid door de methode aanroep op de proxy instantie.Het type van de uitzondering moet kunnen worden toegewezen aan een van de in de handtekening van de interface-methode gedeclareerde exceptiontypes of aan de niet-aangevinkte exceptiontypesjava.lang.RuntimeException
ofjava.lang.Error
. Als een aangevinkte uitzondering wordt gegooid metinvoke
die niet kan worden toegewezen aan een van de uitzonderingstypes die zijn aangegeven in dethrows
clausule van de interfacemethod, dan zal eenUndeclaredThrowableException
worden gegooid door de methode aanroep op de proxy instantie. DeUndeclaredThrowableException
wordt geconstrueerd met uitzondering van deinvoke
methode. - een aanroep van de
hashCode
,equals
oftoString
injava.lang.Object
gedeclareerde methoden op een proxy-instantie zal worden gecodeerd en verzonden naar deinvoke
methode van de aanroepbewerker op dezelfde manier als de aanroepmethoden gecodeerd en verzonden worden, zoals hierboven beschreven. De declarerende klasse van hetMethod
object dat is doorgegeven aaninvoke
isjava.lang.Object
. Andere openbare methoden van een proxy die vanjava.lang.Object
zijn geërfd, worden niet overschreven door een proxy-klasse, zodat aanroepingen van deze methoden zich gedragen zoals ze doen bij gevallen vanjava.lang.Object
.
methoden gedupliceerd in MultipleProxy-Interfaces
wanneer twee of meer interfaces van een proxy-klasse een methode met dezelfde naam en parameterhandtekening bevatten, wordt de volgorde van de interfaces van de proxy-klasse significant. Wanneer een dergelijke duplicatemethod wordt aangeroepen op een proxy-instantie, zal het Method
– object dat wordt doorgegeven aan de aanroepafhandeling niet noodzakelijk het object zijn waarvan de declarerende klasse kan worden toegewezen aan het referentietype van de interface waarmee de proxy-methode werd aangeroepen. Deze beperking bestaat omdat de corresponderende methodeimplementatiein de gegenereerde proxy klasse niet kan bepalen via welke interface het werd aangeroepen. Daarom, wanneer een dubbele methode wordt aangeroepen op een proxy instantie, wordt het Method
object voor de methode in de belangrijkste interface die de methode bevat (direct of geërfd via een superinterface) in de proxy klasse lijst van interfaces doorgegeven aan de aanroep handlerinvoke
methode, ongeacht het referentietype waarmee de methode aanroep plaatsvond.
als een proxy-interface een methode bevat met dezelfde naam enparameterhandtekening als dehashCode
, equals
, oftoString
methoden van java.lang.Object
, wanneer een dergelijke methode wordt aangeroepen op een proxy-instantie, zal het Method
– object dat wordt doorgegeven aan de uitnodiging-handler java.lang.Object
als declaring-klasse hebben. Met andere woorden, de openbare, niet-definitieve methoden vanjava.lang.Object
gaan logischerwijs vooraf aan alle proxyinterfaces voor de bepaling waarvan Method
bezwaar maakt om door te geven aan de aanroepafhandeling.
merk ook op dat wanneer een dubbele methode wordt verzonden naar eeninvocatie-handler, de invoke
methode alleen gechecked uitzonderingstypes mag bevatten die kunnen worden toegewezen aan een van de uitzonderingstypes in de throws
clausule van de methode in alle proxy-interfaces waarmee het kan worden aangeroepen. Als deinvoke
methode een aangevinkte uitzondering gooit die niet kan worden toegewezen aan een van de typen uitzonderingen die zijn gedeclareerd door de methode in een van de proxy interfaces waarmee het kan worden aangeroepen, dan zal een niet-aangevinkte UndeclaredThrowableException
worden geworpen door de aanroep op de proxy instantie. Deze beperking houdt in dat niet alle soorten uitzonderingen die worden geretourneerd doorgetExceptionTypes
aan te roepen op het Method
object dat aan de invoke
methode wordt toegekend, noodzakelijkerwijs met succes kunnen worden uitgevoerd door de invoke
methode.
serialisatie
aangezien java.lang.reflect.Proxy
java.io.Serializable
implementeert, kunnen proxy-instanties worden gehererialiseerd, zoals beschreven in deze sectie. Als een proxy instance echter een aanroephandler bevat die niet aanjava.io.Serializable
kan worden toegewezen, dan zal eenjava.io.NotSerializableException
worden gegooid als een dergelijke instantie naar eenjava.io.ObjectOutputStream
wordt geschreven. Merk op dat Voor proxyklassen het implementeren van java.io.Externalizable
hetzelfde effect heeft met betrekking tot serialisatie als het implementeren vanjava.io.Serializable
: de writeExternal
en readExternal
methoden van deExternalizable
interface zullen nooit worden aangeroepen op een proxy instantie (of een aanroep handler) als onderdeel van het serialisatieproces. Zoals met alle Class
objecten is het Class
object voor een proxy-Klasse altijd serialiseerbaar.
een proxy-klasse heeft geen serialiseerbare velden en eenserialVersionUID
van 0L
. Met andere woorden, wanneer het Class
object voor een proxy klasse wordt doorgegeven aan de statische lookup
methode vanjava.io.ObjectStreamClass
, zal de geretourneerdeObjectStreamClass
instantie de volgende eigenschappen hebben:
- het aanroepen van de
getSerialVersionUID
methode zal0L
draaien. - door gebruik te maken van de
getFields
methode zal een array van lengte nul worden geretourneerd. - het aanroepen van de
getField
methode met eenString
argument geeftnull
terug.
het stream-protocol voor Objectserialisatie ondersteunt een typecode met de naam TC_PROXYCLASSDESC
, wat een terminalsymbool is in de grammatica voor het stream-formaat; het type en de waarde aredefined door de volgende constante veld in dejava.io.ObjectStreamConstants
interface:
final static byte TC_PROXYCLASSDESC = (byte)0x7D;
De grammatica bevat ook de volgende twee regels, de firstbeing een alternatieve uitbreiding van de oorspronkelijke newClassDescrule:
newClassDesc:TC_PROXYCLASSDESC
newHandle proxyClassDescInfo
proxyClassDescInfo:(int)<count>
proxyInterfaceName classAnnotationsuperClassDesc
proxyInterfaceName:(utf)
wanneer een ObjectOutputStream
de classdescriptor serialiseert voor een klasse die een proxy-klasse is, zoals bepaald door het Class
object te omzeilen van deProxy.isProxyClass
methode, gebruikt het deTC_PROXYCLASSDESC
type code in plaats vanTC_CLASSDESC
, volgens de bovenstaande regels. In de expansion of proxyClassDescInfo is de volgorde van proxy-interfacename-items de namen van alle interfaces die door de proxy-klasse zijn geïmplementeerd, in de volgorde dat ze worden geretourneerd door de getInterfaces
– methode op het Class
– object aan te roepen. De classannotatie-en superclassdesc-items hebben dezelfde betekenis als in de classdescinfo-regel. Voor een proxy-klasse is superclassdescriptor de class-descriptor voor zijn superklasse,java.lang.reflect.Proxy
; inclusief deze descriptor maakt de evolutie van de seriële representatie van de klasse Proxy
voor proxy-instanties mogelijk.
voor non-proxy klassen, ObjectOutputStream
roept de beveiligde annotateClass
methode aan om subklassen toe te staan aangepaste gegevens op te slaan in de stream voor een bepaalde klasse. Voor proxyklassen wordt in plaats van annotateClass
de volgende methode in java.io.ObjectOutputStream
aangeroepen met het Class
object voor de proxy-klasse:
protected void annotateProxyClass(Class cl) throws IOException;
de standaard implementatie van annotateProxyClass
inObjectOutputStream
doet niets.
wanneer een ObjectInputStream
de typecodeTC_PROXYCLASSDESC
tegenkomt, deserialiseert het de classdescriptor voor een proxy-klasse uit de stream, opgemaakt zoals hierboven beschreven. In plaats van de resolveClass
methode aan te roepen om het Class
object voor de classdescriptor op te lossen, wordt de volgende methode injava.io.ObjectInputStream
aangeroepen:
protected Class resolveProxyClass(String interfaces) throws IOException, ClassNotFoundException;
de lijst van interfacenamen die gedeserialiseerd werden in de proxyclass descriptor wordt doorgegeven als interfaces
argumentto resolveProxyClass
.
de standaard implementatie van resolveProxyClass
inObjectInputStream
geeft de resultaten van het aanroepen vanProxy.getProxyClass
met de lijst van Class
objecten voor de interfaces genoemd in deinterfaces
parameter. Het Class
object dat voor elke interfacenaam i
wordt gebruikt, is de waarde die wordt retuned bij het ophalen
Class.forName(i, false, loader)
waarbijloader
de eerste Niet-null-klasse-lader op de uitvoerstack is, ofnull
als er geen Niet-null-klasse-laders op de stack staan. Dit is dezelfde klasse Lader keuze gemaakt door de default gedrag van deresolveClass
methode. Deze samevalue vanloader
is ook de klasse Lader doorgegeven aanProxy.getProxyClass
. AlsProxy.getProxyClass
eenIllegalArgumentException
gooit, zalresolveClass
eenClassNotFoundException
gooien die deIllegalArgumentException
bevat.
omdat een proxy-klasse nooit zijn eigen serialiseerbare velden heeft, bestaat de class-gegevens in de stream-representatie van een proxy-instance volledig uit de instance-gegevens voor zijn superklasse,java.lang.reflect.Proxy
. Proxy
heeft een eenerialiseerbaar veld, h
, dat de aanroephandler voor de proxy-instantie bevat.
voorbeelden
hier is een eenvoudig voorbeeld dat een bericht voor en na een methodeaanroep afdrukt op een object dat een arbitraire lijst van interfaces implementeert:
public interface Foo { Object bar(Object obj) throws BazException;}public class FooImpl implements Foo { Object bar(Object obj) throws BazException { // ... }}public class DebugProxy implements java.lang.reflect.InvocationHandler { private Object obj; public static Object newInstance(Object obj) { return java.lang.reflect.Proxy.newProxyInstance( obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new DebugProxy(obj)); } private DebugProxy(Object obj) { this.obj = obj; } public Object invoke(Object proxy, Method m, Object args) throws Throwable { Object result; try { System.out.println("before method " + m.getName()); result = m.invoke(obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } catch (Exception e) { throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); } finally { System.out.println("after method " + m.getName()); } return result; }}
een DebugProxy
construeren voor een implementatie van de Foo
interface en een van zijn methoden aanroepen:
Foo foo = (Foo) DebugProxy.newInstance(new FooImpl()); foo.bar(null);
hier is een voorbeeld van een utility aanroep handler klasse die standaard proxy gedrag voor methoden overgenomen vanjava.lang.Object
en implementeert delegatie van bepaalde proxy methode aanroepingen aan verschillende objecten afhankelijk van deinterface van de aangeroepen methode:
import java.lang.reflect.*;public class Delegator implements InvocationHandler { // preloaded Method objects for the methods in java.lang.Object private static Method hashCodeMethod; private static Method equalsMethod; private static Method toStringMethod; static { try { hashCodeMethod = Object.class.getMethod("hashCode", null); equalsMethod = Object.class.getMethod("equals", new Class { Object.class }); toStringMethod = Object.class.getMethod("toString", null); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.getMessage()); } } private Class interfaces; private Object delegates; public Delegator(Class interfaces, Object delegates) { this.interfaces = (Class) interfaces.clone(); this.delegates = (Object) delegates.clone(); } public Object invoke(Object proxy, Method m, Object args) throws Throwable { Class declaringClass = m.getDeclaringClass(); if (declaringClass == Object.class) { if (m.equals(hashCodeMethod)) { return proxyHashCode(proxy); } else if (m.equals(equalsMethod)) { return proxyEquals(proxy, args); } else if (m.equals(toStringMethod)) { return proxyToString(proxy); } else { throw new InternalError( "unexpected Object method dispatched: " + m); } } else { for (int i = 0; i < interfaces.length; i++) { if (declaringClass.isAssignableFrom(interfaces)) { try { return m.invoke(delegates, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } } } return invokeNotDelegated(proxy, m, args); } } protected Object invokeNotDelegated(Object proxy, Method m, Object args) throws Throwable { throw new InternalError("unexpected method dispatched: " + m); } protected Integer proxyHashCode(Object proxy) { return new Integer(System.identityHashCode(proxy)); } protected Boolean proxyEquals(Object proxy, Object other) { return (proxy == other ? Boolean.TRUE : Boolean.FALSE); } protected String proxyToString(Object proxy) { return proxy.getClass().getName() + '@' + Integer.toHexString(proxy.hashCode()); }}
subklassen van Delegator
kunneninvokeNotDelegated
overschrijven om het gedrag van proxymethod-aanroepingen te implementeren die niet direct aan andere objecten mogen worden gedelegeerd,en ze kunnen proxyHashCode
,proxyEquals
en proxyToString
overschrijven om het standaardgedrag van de methoden die de proxy erft van java.lang.Object
te omzeilen.
een Delegator
construeren voor een implementatie van de interface Foo
:
Class proxyInterfaces = new Class { Foo.class }; Foo foo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), proxyInterfaces, new Delegator(proxyInterfaces, new Object { new FooImpl() }));
merk op dat de toepassing van de bovengenoemde klasse Delegator
meer illustratief is dan geoptimaliseerd; bijvoorbeeld, in plaats van de Method
objecten voor de hashCode
, equals
entoString
te cachen en te vergelijken, zou het ze gewoon kunnen matchen met hun string namen, omdat geen van deze methodenamen overbelast zijn injava.lang.Object
.