Classi glorificate nel linguaggio Java


96

Alcune classi nell'API Java standard vengono trattate in modo leggermente diverso dalle altre classi. Sto parlando di quelle classi che non potrebbero essere implementate senza un supporto speciale da parte del compilatore e / o JVM.

Quelli che mi vengono in mente subito sono:

  • Object (ovviamente) in quanto, tra l'altro, non ha una super classe.
  • String poiché la lingua ha un supporto speciale per l'operatore +.
  • Thread poiché ha questo magico metodo start () nonostante il fatto che non ci siano istruzioni bytecode che "biforcano" l'esecuzione.

Suppongo che tutte le classi come queste siano in un modo o nell'altro menzionate nel JLS. Correggimi se sbaglio.

Comunque, quali altre classi simili esistono? Esiste un elenco completo di "classi glorificate" nel linguaggio Java?


2
I generici si adattano quasi, ma non del tutto. Sono implementati utilizzando un trucco del compilatore, ma non sono isolati in una classe.
Bill the Lizard

2
Sono tutti tipi di riverenza. ;-)
starblue

1
"Thread.start" è magico? Sicuramente è solo un codice nativo chiamato per farlo?
jcoder

Quel pensiero ha colpito anche me. Forse il JNI da solo è sufficiente per implementare la classe Thread. Suppongo che se provassi a farlo, userei alcune API del thread a livello di sistema operativo, biforerei l'esecuzione nell'implementazione del metodo start (), eseguirò il metodo run () nel thread biforcuto e tornerei. Ma poi il thread del mio sistema operativo continuerebbe a funzionare. Funzionerebbe senza intoppi insieme ai thread creati dalla JVM? e onorare il modello di memoria JLS e così via?
aioobe

2
Secondo le specifiche, l'unico modo per creare un thread è dalla classe Thread ( java.sun.com/docs/books/jvms/second_edition/html/… ). Ovviamente, si basa su una comunicazione a livello ingenuo con la JVM, ma se hai fatto il tuo JNI, potresti avviare un thread, ma non faresti capire alla JVM cosa stai facendo (blocchi, modello di memoria, ecc. ). Il thread è privilegiato in quanto la specifica gli assegna un privilegio speciale: l'unico modo per avviare un thread.
Yishai

Risposte:


35

Ci sono molte risposte diverse, quindi ho pensato che sarebbe stato utile raccoglierle tutte (e aggiungerne alcune):

Classi

  • Classi di AutoBoxing : il compilatore consente solo classi specifiche
  • Class - ha i suoi letterali (int.class per esempio). Aggiungerei anche la sua tipizzazione generica senza creare nuove istanze.
  • Stringa - con il suo operatore + -credito e il supporto dei letterali
  • Enum - l'unica classe che può essere utilizzata in un'istruzione switch (presto anche un privilegio da dare a String). Fa anche altre cose (creazione automatica di metodi statici, gestione della serializzazione, ecc.), Ma queste potrebbero teoricamente essere realizzate con il codice: è solo un sacco di boilerplate e alcuni dei vincoli non possono essere applicati nelle sottoclassi (ad es. regole speciali di sottoclasse) ma ciò che non potresti mai ottenere senza lo stato privilegiato di un'enumerazione è includerlo in un'istruzione switch.
  • Object : la radice di tutti gli oggetti (e aggiungerei i suoi metodi clone e finalize non sono qualcosa che potresti implementare)
  • Riferimenti : WeakReference, SoftReference, PhantomReference
  • Thread : il linguaggio non ti dà un'istruzione specifica per avviare un thread, piuttosto lo applica magicamente al metodo start ().
  • Throwable : la radice di tutte le classi che possono lavorare con throw, throws e catch, nonché la comprensione del compilatore di Exception vs RuntimeException ed Error.
  • NullPointerException e altre eccezioni come ArrayIndexOutOfBounds che possono essere lanciate da altre istruzioni bytecode diverse da athrow.

Interfacce

  • Iterabile : l'unica interfaccia che può essere utilizzata in un ciclo for migliorato

Le menzioni d'onore vanno a:

  • java.lang.reflect. Array : la creazione di un nuovo array come definito da un oggetto Class non sarebbe possibile.
  • Annotazioni Sono una caratteristica del linguaggio speciale che si comporta come un'interfaccia in fase di runtime. Certamente non potresti definire un'altra interfaccia di annotazione, così come non puoi definire una sostituzione per Object. Tuttavia, potresti implementare tutte le loro funzionalità e avere solo un altro modo per recuperarle (e un intero gruppo di boilerplate) piuttosto che riflettere. In effetti, c'erano molte implementazioni basate su XML e javadoc tag prima dell'introduzione delle annotazioni.
  • ClassLoader - ha certamente una relazione privilegiata con la JVM in quanto non esiste un modo di linguaggio per caricare una classe, sebbene esista un modo bytecode, quindi è come Array in quel modo. Ha anche il privilegio speciale di essere richiamato dalla JVM, sebbene si tratti di un dettaglio di implementazione.
  • Serializzabile : potresti implementare la funzionalità tramite reflection, ma ha una sua parola chiave privilegiata e in alcuni scenari passeresti molto tempo a entrare in intimità con SecurityManager.

Nota: ho lasciato fuori dalla lista le cose che forniscono JNI (come IO) perché potresti sempre implementare la tua chiamata JNI se fossi così incline. Tuttavia, le chiamate native che interagiscono con la JVM in modi privilegiati sono diverse.

Gli array sono discutibili: ereditano Object, hanno una gerarchia comprensibile (Object [] è un supertipo di String []), ma sono una caratteristica del linguaggio, non una classe definita di per sé.


@Donal, molto vero, stavo pensando all'annotazione di runtime, ma le annotazioni a livello di sorgente sono state effettivamente fatte in questo modo (xdoclet in modo più evidente, o anche nel core java con @deprecated)
Yishai

Ho appena combattuto attraverso un folto di codice che era stato originariamente scritto con l'elaborazione xdoclet e poi convertito in annotazioni. Oh, come preferisco le annotazioni a quella (anche se, con opportuni incantesimi esperti, l'effetto netto è lo stesso).
Donal Fellows

@ KK_07k11A0585, le collezioni sono un'api standard che potrebbe essere costruita da chiunque in modo diverso (infatti ci sono implementazioni alternative che sono orientate intorno alle primitive, così come un famosissimo progetto Google che le valorizza). L'unica cosa che ottengono di speciale è Iterable, menzionata nella risposta. Sono il pane quotidiano della programmazione java, certo, ma sono solo classi regolari, senza privilegi speciali.
Yishai

@Yishai Durante lo sviluppo di qualsiasi applicazione, l'aspetto principale che assume importanza è la GESTIONE DEI DATI. Tutti possiamo svolgere un'attività in diversi modi, ma il modo ottimizzato è quello che occupa meno memoria e che utilizza meno riferimenti non necessari. Utilizzando le raccolte possiamo ordinare, cercare e confrontare una grande quantità di dati, che fornisce algoritmi predefiniti come la ricerca binaria, l'ordinamento di unione, ecc. Ci fornisce anche il comparatore e le interfacce comparabili per personalizzare le nostre tecniche. La classe delle collezioni potrebbe non essere la migliore classe in Java, ma è senza dubbio una classe di qualità glorificate.
KK_07k11A0585

1
Di cosa SecurityManager? O qualcosa relativo alla riflessione o alla serializzazione?
Antimonio

19

Class, ovviamente. Ha i suoi letterali (una distinzione con cui condivide String, BTW) ed è il punto di partenza di tutta quella magia di riflessione.


Ah, buon punto. Allora qual è un esempio di letterale di classe? Ti riferisci a MyClass.class?
aioobe

4
@ aioobe: esattamente. Nota che hai anche int.class, char.class, ecc.
Michael Borgwardt


12
  1. Enum. Non sei autorizzato a sottoclassarlo, ma il compilatore può.
  2. Molte cose sotto java.util.concurrent possono essere implementate senza il supporto JVM, ma sarebbero molto meno efficienti.

1
È possibile creare una sottoclasse anonima di enumerazioni (che è la base del modello di visitatore impl. Per i valori di enumerazione). Ma sì, non è possibile creare una sottoclasse completa di un enum ;-)
Thierry


10

Poiché sono state menzionate le classi importanti , menzionerò alcune interfacce:

L' Iterableinterfaccia (dalla 1.5) - consente a un oggetto di partecipare a un ciclo foreach:

Iterable<Foo> iterable = ...;
for (Foo foo : iterable) {

}

L' Serializableinterfaccia ha un significato molto speciale, diverso da un'interfaccia standard. È possibile definire metodi che verranno presi in considerazione anche se non sono definiti nell'interfaccia (come readResolve()). La transientparola chiave è l'elemento del linguaggio che influenza il comportamento degli Serializableimplementatori.


È vero, sono interfacce però, quindi non richiedono implementazioni "speciali" per vedere. (Simile a Throwable.)
aioobe

Serializable in realtà richiede un'implementazione speciale. È un'interfaccia che ci si aspetta abbia due metodi (dimentico i nomi ... potrei cercarla su google ...) ma non sono richiesti o definiti nello schema dell'interfaccia standard.
corsiKa

@glowcoder: tuttavia, una volta si potrebbe sostenere che tutta la particolarità di Serializable non è rilevante per il linguaggio Java, solo per l'implementazione di ObjectOutputStream e ObjectInputStream.
Michael Borgwardt

5
@Michael Borgwardt ha - la transientparola chiave
Bozho

1
Non la penso così - Comparablenon prende parte a niente di interno. Potresti facilmente scrivere NewComparablee nuovo NewArrays.sort(..)con la stessa funzionalità
Bozho

6
  1. Throwable , RuntimeException, Error AssertionError
  2. Riferimenti WeakReference, SoftReference, PhantomReference
  3. Enum
  4. Annotazione

Bella lista. +1, l'annotazione è tuttavia un'interfaccia e non ha implementazione.
aioobe l'

1
Le annotazioni vengono trattate in modo diverso dal compilatore rispetto alle normali interfacce. Simili a enum, estendono tutti Annotation e il compilatore lo fa automaticamente. Dal JavaDoc di Annotation: l'interfaccia comune estesa da tutti i tipi di annotazione. Notare che un'interfaccia che estende manualmente questa non definisce un tipo di annotazione. Notare inoltre che questa interfaccia non definisce di per sé un tipo di annotazione. Quindi non puoi creare un'annotazione usando la sintassi normale, avevano bisogno di cambiare il compilatore per aggiungerli.
Andrei Fierbinteanu



2

Non sono sicuro di questo. Ma non riesco a pensare a un modo per implementare manualmente gli oggetti IO.


Hai ragione, non possono essere implementati in Java puro. Devono essere implementati tramite JNI (proprio come qualsiasi altra classe che deve eseguire chiamate di sistema). A parte questo, tuttavia, non hanno bisogno di alcun supporto speciale da parte del compilatore o di jvm.
aioobe

2

C'è un po 'di magia nella Systemclasse.

System.arraycopy è un aggancio al codice nativo

public static native void arraycopy(Object array1, int start1, 
  Object array2, int start2, int length);

ma...

/**
 * Private version of the arraycopy method used by the jit
 * for reference arraycopies
 */
private static void arraycopy(Object[] A1, int offset1,
  Object[] A2, int offset2, int length) {
   ...
}

1
Ehi, cosa intendi con la seconda parte della risposta?
Pacerier

1

Bene, da quando è stata menzionata la gestione speciale di assert. Ecco alcuni altri tipi di eccezioni che hanno un trattamento speciale da parte della jvm:

  • NullPointerException
  • ArithmeticException.
  • StackOverflowException
  • Tutti i tipi di OutOfMemoryErrors
  • ...

Le eccezioni non sono speciali, ma la jvm le usa in casi speciali, quindi non puoi implementarle da solo senza scrivere la tua jvm. Sono sicuro che ci siano altre eccezioni speciali in giro.


0

La maggior parte di queste classi non è realmente implementata con l'aiuto "speciale" del compilatore o della JVM. Object registra alcuni nativi che esplorano le strutture JVM interne, ma puoi farlo anche per le tue classi. (Ammetto che questo sia soggetto a semantica, "chiama un nativo definito nella JVM" può essere considerato come supporto JVM speciale.)

Ciò che / è / speciale è il comportamento delle istruzioni "nuove" e "lancia" nel modo in cui inizializzano queste strutture interne.

Le annotazioni e i numeri sono praticamente stravaganti.


4
aioobe si riferisce a classi che non puoi implementare da solo, poiché hanno un supporto speciale per build. Non è possibile creare la propria classe Object come root del sistema di tipi senza ereditare il modulo java.lang.Object. Object ha un supporto speciale sia dal compilatore che dalla jvm per assicurarsi che sia una classe root. Le chiamate native non sono sufficienti per aggirare questo problema.
josefx
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.