Wyróżnione klasy w języku Java


96

Niektóre klasy w standardowym interfejsie API języka Java są traktowane nieco inaczej niż inne klasy. Mówię o tych klasach, których nie dałoby się zaimplementować bez specjalnego wsparcia ze strony kompilatora i / lub JVM.

Te, które wymyśliłem od razu, to:

  • Object (oczywiście) ponieważ między innymi nie ma super klasy.
  • String ponieważ język ma specjalne wsparcie dla operatora +.
  • Thread ponieważ ma tę magiczną metodę start (), pomimo faktu, że nie ma instrukcji kodu bajtowego, która „rozwidla” wykonanie.

Przypuszczam, że wszystkie takie klasy są w taki czy inny sposób wspomniane w JLS. Popraw mnie, jeśli się mylę.

Zresztą, jakie inne takie klasy istnieją? Czy istnieje pełna lista „gloryfikowanych klas” w języku Java?


2
Generics prawie pasują, ale nie do końca. Są implementowane przy użyciu sztuczki kompilatora, ale nie są izolowane do jednej klasy.
Bill the Lizard

2
Wszyscy są typami czci. ;-)
starblue

1
Czy „thread.start” jest magiczna? Z pewnością to tylko jakiś natywny kod, który to robi?
jcoder

Ta myśl też mnie uderzyła. Być może sam JNI wystarczy do zaimplementowania klasy Thread. Przypuszczam, że gdybym spróbował to zrobić, użyłbym jakiegoś api wątku na poziomie systemu operacyjnego, rozwidliłbym wykonanie w implementacji metody start (), wykonałbym metodę run () w rozwidlonym wątku i wrócił. Ale wtedy mój wątek systemu operacyjnego będzie nadal działał. Czy przebiegałoby to płynnie wraz z wątkami utworzonymi przez JVM? i honorować model pamięci JLS i tak dalej?
aioobe

2
Zgodnie ze specyfikacją, jedynym sposobem na utworzenie wątku jest użycie klasy Thread ( java.sun.com/docs/books/jvms/second_edition/html/… ). Oczywiście opiera się na jakiejś naiwnej komunikacji z JVM, ale gdybyś zrobił własne JNI, mógłbyś rozpocząć wątek, ale nie dałbyś JVM zrozumieć, co robisz (blokady, model pamięci itp. ). Wątek jest uprzywilejowany, ponieważ specyfikacja nadaje mu specjalny przywilej - jedyny sposób na rozpoczęcie wątku.
Yishai

Odpowiedzi:


35

Istnieje wiele różnych odpowiedzi, więc pomyślałem, że warto je wszystkie zebrać (i dodać kilka):

Zajęcia

  • Klasy AutoBoxing - kompilator dopuszcza tylko określone klasy
  • Class - ma swoje własne literały (na przykład int.class). Dodałbym również jego ogólne pisanie bez tworzenia nowych instancji.
  • String - z jego przeciążeniem + -operator i wsparciem literałów
  • Enum - jedyna klasa, której można użyć w instrukcji switch (wkrótce nadany zostanie również przywilej String). Robi też inne rzeczy (automatyczne tworzenie metod statycznych, obsługa serializacji itp.), Ale teoretycznie można by to osiągnąć za pomocą kodu - to po prostu dużo schematu, a niektórych ograniczeń nie można narzucić w podklasach (np. specjalne reguły dotyczące podklas), ale to, czego nigdy nie można by osiągnąć bez uprzywilejowanego statusu wyliczenia, to uwzględnienie go w instrukcji switch.
  • Obiekt - korzeń wszystkich obiektów (a dodałbym, że jego metody klonowania i finalizowania nie są czymś, co można zaimplementować)
  • Odniesienia : WeakReference, SoftReference, PhantomReference
  • Wątek - język nie podaje konkretnej instrukcji, aby rozpocząć wątek, a raczej w magiczny sposób stosuje ją do metody start ().
  • Throwable - katalog główny wszystkich klas, które mogą współpracować z rzutami, rzutami i przechwytywaniem, a także interpretacja kompilatora w zakresie wyjątków, wyjątków i błędów.
  • NullPointerException i inne wyjątki, takie jak ArrayIndexOutOfBounds, które mogą być generowane przez inne instrukcje kodu bajtowego niż athrow.

Interfejsy

  • Iterable - jedyny interfejs, którego można używać w rozszerzonej pętli for

Wyróżnienia dla:

  • java.lang.reflect. Array - utworzenie nowej tablicy zdefiniowanej przez obiekt Class nie byłoby możliwe.
  • Adnotacje To specjalna funkcja języka, która zachowuje się jak interfejs w czasie wykonywania. Z pewnością nie możesz zdefiniować innego interfejsu Adnotacji, tak jak nie możesz zdefiniować zamiennika dla Object. Możesz jednak zaimplementować całą ich funkcjonalność i po prostu mieć inny sposób na ich odzyskanie (i całą masę schematów) zamiast odbicia. W rzeczywistości istniało wiele implementacji opartych na XML i tagach javadoc przed wprowadzeniem adnotacji.
  • ClassLoader - z pewnością ma uprzywilejowaną relację z JVM, ponieważ nie ma sposobu na załadowanie klasy w języku, chociaż istnieje sposób kodowania bajtowego, więc jest podobny do Array. Ma również specjalny przywilej wywołania zwrotnego przez JVM, chociaż jest to szczegół implementacji.
  • Serializowalny - możesz zaimplementować tę funkcjonalność przez odbicie, ale ma ona swoje własne uprzywilejowane słowo kluczowe i w niektórych scenariuszach spędzisz dużo czasu na intymności z SecurityManager.

Uwaga: Pominąłem z listy rzeczy, które zapewniają JNI (takie jak IO), ponieważ zawsze możesz zaimplementować własne wywołanie JNI, jeśli masz takie skłonności. Jednak wywołania natywne, które współdziałają z maszyną JVM w uprzywilejowany sposób, są różne.

Tablice są dyskusyjne - dziedziczą Object, mają zrozumiałą hierarchię (Object [] jest nadtypem String []), ale są cechą języka, a nie samą zdefiniowaną klasą.


@Donal, bardzo prawda, myślałem o adnotacji w czasie wykonywania, ale adnotacje na poziomie źródła rzeczywiście zostały zrobione w ten sposób (szczególnie xdoclet, a nawet w core java z @deprecated)
Yishai

Właśnie przedzierałem się przez gąszcz kodu, który został pierwotnie napisany przy użyciu przetwarzania xdoclet, a następnie przekształcony w adnotacje. Och, jak ja wolę adnotacje od tego (mimo że przy odpowiednich zaklęciach maven efekt netto jest taki sam).
Donal Fellows

@ KK_07k11A0585, Kolekcje to standardowy interfejs API, który każdy może zbudować w inny sposób (w rzeczywistości istnieją alternatywne implementacje, które są nastawione na prymitywy, a także bardzo znany projekt Google, który je ulepsza). Jedyną wyjątkową rzeczą, jaką otrzymują, jest iterowalność, o której wspomniano w odpowiedzi. Z pewnością są one chlebem powszednim programowania w Javie, ale to tylko zwykłe zajęcia, bez specjalnych przywilejów.
Yishai

@Yishai Podczas tworzenia dowolnej aplikacji najważniejszą rzeczą jest ZARZĄDZANIE DANYMI. Wszyscy możemy wykonać zadanie na wiele różnych sposobów, ale zoptymalizowany sposób to taki, który zajmuje mniej pamięci i ma mniejsze wykorzystanie niepotrzebnych odniesień. Korzystając z kolekcji, możemy sortować, wyszukiwać i porównywać duże ilości danych, co zapewnia predefiniowane algorytmy, takie jak wyszukiwanie binarne, sortowanie przez scalanie itp. Zapewnia również komparator i porównywalne interfejsy w celu dostosowania naszych technik. Klasa kolekcji może nie jest najlepszą klasą w Javie, ale niewątpliwie jest klasą gloryfikowanych cech.
KK_07k11A0585

1
O co chodzi SecurityManager? Albo coś związanego z odbiciem lub serializacją?
Antymon

19

Class, oczywiście. Ma swoje własne literały (przy Stringokazji dzieli je rozróżnienie ) i jest punktem wyjścia całej tej magii refleksji.


Ach, słuszna uwaga. Więc jaki jest przykład literału klasowego? Czy odnosisz się do MyClass.class?
aioobe

4
@aioobe: dokładnie. Zauważ, że masz również int.class, char.class itd
Michael Borgwardt


12
  1. Enum. Nie możesz tworzyć podklasy, ale kompilator może.
  2. Wiele rzeczy w java.util.concurrent można zaimplementować bez obsługi JVM, ale byłyby one dużo mniej wydajne.

1
Możesz anonimowo podklasować wyliczenia (co jest podstawą implantu wzorca gości dla wartości wyliczeń). Ale tak, nie możesz w pełni podklasować wyliczenia ;-)
Thierry,


10

Ponieważ wspomniałem o ważnych klasach , wspomnę o kilku interfejsach:

IterableInterfejs (od 1.5), - pozwala na przedmiot do udziału w pętli foreach:

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

}

SerializableInterfejs ma szczególne znaczenie, inny od standardowego interfejsu. Możesz zdefiniować metody, które będą brane pod uwagę, nawet jeśli nie są zdefiniowane w interfejsie (np readResolve().). Słowo transientkluczowe to element języka, który wpływa na zachowanie Serializableimplementatorów.


To prawda, są to jednak interfejsy, więc nie wymagają żadnej „specjalnej” implementacji. (Podobnie jak Throwable.)
aioobe

Możliwość serializacji faktycznie wymaga specjalnej implementacji. Jest to interfejs, który powinien mieć dwie metody (zapomniałem nazw ... mógłbym go wygooglować ...), ale nie są one wymagane ani zdefiniowane w standardowym schemacie interfejsu.
corsiKa

@glowcoder: jednak kiedyś można by argumentować, że cała specjalność Serializable nie ma znaczenia dla języka Java, tylko dla implementacji ObjectOutputStream i ObjectInputStream.
Michael Borgwardt,

5
@Michael Borgwardt ma - transientsłowo kluczowe
Bozho

1
Nie sądzę - Comparablenie bierze udziału w niczym wewnętrznym. Możesz łatwo pisać NewComparablei nowe NewArrays.sort(..)z tą samą funkcjonalnością
Bozho

6
  1. Throwable , RuntimeException, Error AssertionError
  2. Odnośniki WeakReference, SoftReference, PhantomReference
  3. Enum
  4. Adnotacja

Niezła lista. +1, Adnotacja jest jednak interfejsem i nie ma implementacji.
aioobe

1
Kompilator traktuje adnotacje inaczej niż zwykłe interfejsy. Podobnie jak enum, wszystkie one rozszerzają Annotation, a kompilator robi to automagicznie. Z JavaDoc of Annotation: wspólny interfejs rozszerzony o wszystkie typy adnotacji. Należy zauważyć, że interfejs, który ręcznie rozszerza ten interfejs, nie definiuje typu adnotacji. Należy również zauważyć, że ten interfejs sam w sobie nie definiuje typu adnotacji. Więc nie możesz utworzyć adnotacji przy użyciu normalnej składni, musieli zmienić kompilator, aby je dodać.
Andrei Fierbinteanu



2

Nie jestem tego pewien. Ale nie mogę wymyślić sposobu na ręczne implementowanie obiektów IO.


Masz rację, nie można ich zaimplementować w czystej Javie. Muszą być implementowane przez JNI (tak jak każda inna klasa, która musi wykonywać wywołania systemowe). Poza tym jednak nie potrzebują specjalnego wsparcia ze strony kompilatora lub jvm.
aioobe

2

W tej Systemklasie jest trochę magii .

System.arraycopy jest zaczepem do kodu natywnego

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

ale...

/**
 * 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
Hej, co masz na myśli mówiąc o drugiej części odpowiedzi?
Pacerier

1

Cóż, odkąd wspomniano o specjalnej obsłudze asercji. Oto kilka innych typów wyjątków, które są specjalnie traktowane przez jvm:

  • NullPointerException
  • ArithmeticException.
  • StackOverflowException
  • Wszystkie rodzaje błędów OutOfMemoryErrors
  • ...

Wyjątki nie są specjalne, ale jvm używa ich w specjalnych przypadkach, więc nie możesz ich zaimplementować samodzielnie bez napisania własnego jvm. Jestem pewien, że w okolicy jest więcej wyjątków.


0

Większość z tych klas nie jest tak naprawdę zaimplementowana z „specjalną” pomocą kompilatora lub maszyny JVM. Object rejestruje niektórych tubylców, którzy przeglądają wewnętrzne struktury JVM, ale możesz to zrobić również dla swoich własnych klas. (Przyznaję, że jest to przedmiotem semantyki, „wywołania natywne zdefiniowane w JVM” można uznać za specjalne wsparcie JVM.)

To, co / jest / specjalne, to zachowanie „nowych” i „rzucających” instrukcji w jaki sposób inicjalizują te wewnętrzne struktury.

Adnotacje i liczby są jednak dość dziwaczne.


4
aioobe odnosi się do klas, których nie możesz zaimplementować samodzielnie, ponieważ mają one specjalne wsparcie. Nie można utworzyć własnej klasy Object jako root systemu typów bez dziedziczenia po postaci java.lang.Object. Object ma specjalne wsparcie zarówno ze strony kompilatora, jak i jvm, aby upewnić się, że jest to klasa główna. Połączenia rodzime nie wystarczą, aby to obejść.
josefx
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.