Java IAQ: Nieczęsto Pytania

20 Dec 11:54 am


Original: http://www.norvig.com/java-iaq.html
Copyright: Peter Norvig


Q: Co to jest Nieczęsto
Odpowiedział na pytanie:

Pytanie jest nierzadko odpowiedziało albo ponieważ niewiele osób wie
odpowiedź
albo dlatego, że jest o niejasnym, punktu subtelny (ale punkt, który może być
kluczowe znaczenie dla Ciebie). Ja
myślałem, że wymyślił ten termin, ale także pojawia się w bardzo
informacyjny
href=”http://about.com/culture/urbanlegends/library/weekly/aa082497.htm”> <a
About.com Urban Legends site. Istnieje wiele Java FAQ
wokół, ale jest to tylko Java IAQ. (Istnieje kilka Nieczęsto
zadawane pytania listy, w tym jeden na satyrycznej C.)


Q: Kod w
końcu klauzula nigdy nie uda się wykonać, prawda?

No, prawie nigdy. Ale tutaj jest przykład, gdzie końcu Kod
nie będą wykonane, niezależnie od wartości logicznej wyboru :

  try {
    if (choice) {
      while (true);
    } Else {
      System.exit (1);
    }
  } Finally {
    code.to.cleanup ();
  }

 


Q: W metodzie m w klasie C , nie jest this.getClass () zawsze C

Nie, to jest możliwe, że jakiś obiekt x , który jest instancją pewnej podklasy C1 z C albo nie ma C1.m () metoda, albo jakiś sposób na x nazywa super.m () . W każdym przypadku, this.getClass () jest C1 , nie C w ciele Cm () . Jeśli C jest końcowy , to jesteś w porządku.


Q: I określonych równa metoda, ale Hashtable ignoruje go. Dlaczego?

równa metody są zaskakująco trudne do uzyskania prawa. Oto miejsca Spójrz najpierw na problem:

  1. zdefiniowany źle równa metody. Na przykład, napisał:
    public class  C  {
      public boolean  równa  ( C , że) {return id (this) == id (to);}
    }

    Jednak aby table.get (c) do pracy trzeba wykonać Równa metody podejmowania obiektu jako argument, a nie C :

    public class  C  {
      public boolean  równa  ( Object , że) {
        powrót (to instanceof C) && id (this) == id ((C), że);
      }
    }

    Dlaczego? Kod Hashtable.get wygląda mniej więcej tak:

    public class Hashtable   {
      public Object  dostać  (klawisz Object) {
        Wpis Object;
        ...
        if (entry.equals (key)) ...
      }
    }

    Teraz metody wywoływane entry.equals (key) , zależy od rzeczywisty czas wykonywania typ obiektu entery i oświadczył kompilacji typu zmiennej key . Więc kiedy ty wywołujesz table.get (new C (...)) , to wygląda w klasie C równa metoda z argumentem typu Object . Jeśli zdarzy ci się mieć zdefiniowane równa metoda z argumentem typu C , które jest irrelevent. Ignoruje ta metoda, i szuka metody z podpisem Equals (Object) , w końcu znalezienie Object.equals (Object) . Jeśli chcesz over-ride metodę, należy dopasować typy argumentów dokładnie. W niektórych przypadkach, może chcesz mają dwie metody, aby nie płacić napowietrznej odlewania gdy wiesz, masz obiekt właściwej klasy:

    public class  C  {
      public boolean  równa  ( Object , że) {
        powrót (to ==, że)
                | | ((C, że instanceof) && this.equals ((C)));
      }
    
      public boolean  równa  ( C , że) {
        powrót id (this) == id (to) / / A wszystko, co jest właściwe dla klasy C
      }
    }

     

  2. nie wdrażały prawidłowo równa , jako równości predykat: równa musi być symetryczny, przechodnia i refleksyjnego. Symetryczna oznacza a.equals (b) musi mieć taki sam wartość jako b.equals (a) . (To jest jeden większość ludzi bałagan.) Oznacza to, że przechodnie a.equals (b) i b.equals (c) następnie a.equals (c) musi być prawdziwe. Oznacza to, że odruchowy a.equals (a) musi być prawdziwe, i jest powodem (ta == Że) test powyżej (to też często dobrą praktyką w tym ze względów efektywności: testowanie == jest szybsze niż patrząc na wszystkich gniazdach obiektu i częściowo przełamać Problem rekursji na przedmioty, które mogą mieć okrągłe pokazane).
  3. zapomniałem hashCode metoda. Anytime zdefiniować równa metody, należy również określić hashCode metoda. Musisz upewnić się, że dwa równe obiekty mają ten sam hashCode i jeśli chcesz lepszą hashtable wydajność, należy spróbować, aby większość nie same obiekty mają różne hashCode. Niektóre klasy buforować skrót w prywatnych Gniazdo przedmiotu, tak że musi być obliczane tylko raz. Jeśli to Sprawa wtedy prawdopodobnie zaoszczędzić czas w równa , jeśli to wiersz mówi if (this.hashSlot! = that.hashSlot) return false .
  4. nie obsługiwać dziedziczenie poprawnie. Po pierwsze, pod uwagę, jeśli dwa obiektów innej klasy może być równa. Zanim powiesz “NIE! Oczywiście nie! “rozważyć klasy Rectangle z szerokości i wysokość pola i Box Klasa, która ma ponad dwa Pola oraz głębokość . Czy Box o głębokości == 0 równa odpowiednik prostokąta? Być może chcesz powiedzieć tak. Jeśli masz do czynienia z non- końcowy class, to jest możliwe, że Klasa może być podklasy, a chcesz być dobrym człowiekiem i szanować do podklasy. W szczególności, aby zezwolić Przedłużacz z twojej klasy C , aby używać C.equals Metoda używając Super w następujący sposób:
    public class C {C2 extends
    
      Newfield int = 0;
    
      public boolean  równa  (Object, że) {
        if (==, że to) return true;
        else return false ((że instanceof C2)!)
        else return this.newField == ((C2), że) Newfield &&  Super  jest równa (które)..;
      }
    
    }

    Aby umożliwić to do pracy, trzeba być ostrożnym, jak traktujesz zajęcia w twojej definicji C.equals . Na przykład, sprawdź dla że instanceof C , zamiast that.getClass () == C.class . Zobacz poprzednie pytanie jakości powietrza, aby dowiedzieć się dlaczego. Używać this.getClass () == that.getClass () , jeśli jesteś pewien, że dwa Przedmioty muszą być z tego samego rodzaju są uważane równa.

  5. nie rozpatrzyła odwołania cykliczne prawidłowo. Zastanów się:
    public class LinkedList {
    
      Treści przedmiotu;
      LinkedList next = null;
    
      public boolean  równa  (Object, że) {
        powrót (to ==, że)
          | | ((Że instanceof LinkedList) && this.equals ((LinkedList), że));
      }
    
      public boolean  równa  (LinkedList że) {/ / Buggy!
       Util.equals powrotne (this.contents, that.contents) &&
              Util.equals (this.next, that.next);
      }
    
    }

    Tutaj założyliśmy jest Util class z:

      public static boolean  równa  (Object x, Object y) {
        return (x == y) | | (x = null && x.equals (y)!)
      }

    Życzę tej metody były w Object , bez niego trzeba zawsze rzucać w testach przeciwko null. W każdym razie, LinkedList.equals metoda nigdy nie powróci, jeśli poproszeni o porównanie dwa LinkedLists z odwołań cyklicznych w nich (wskaźnik z jednego elementu połączonej listy z powrotem do innego elementu). Zobacz opis funkcji Lisp Wspólnej < o wyjaśnienie, jak radzić sobie z tym problemem w czasie liniowym ze tylko dwa słowa o dodatkowym storge. (I nie dają odpowiedzi tutaj w przypadku, gdy chcesz, aby spróbować zrozumieć to dla siebie pierwsze.)

 


Q: Próbowałem do przekazania metoda super, ale od czasu do czasu nie działa. Dlaczego?

Kod jest mowa, to uproszczone np.

/ ** Wersja Hashtable, który pozwala zrobić
 * Table.put ("dog", "psów"), a następnie są
 * Table.get ("Psy") return "canine". ** /

public class  HashtableWithPlurals  rozszerza Hashtable {

  / ** Producent map zarówno stół i klawisz + "s" do wartości. ** /
  public Object  umieścić  (klawisz Object, Object value) {
    super.put (klucz + "s", wartość);
    powrót super.put (klucz, wartość);
  }
}

Trzeba być ostrożnym przy przejściu do Super że w pełni zrozumieć co super metoda robi. W tym przypadku umowa Hashtable.put jest, że będzie nagrywać mapowanie pomiędzy klucz i wartość w tabeli. Jednakże, jeśli jest zbyt Hashtable pełna, a następnie Hashtable.put przeznaczy większą tablicę dla stół, skopiować wszystkie stare obiekty nad, a następnie rekursywnie ponownie wywołać table.put (key, value) . Teraz, ponieważ Java postanawia metody na podstawie typu wykonawczego, w celu naszym przykładzie rekurencyjnego zadzwoń kodem dla Hashtable trafi do HashtableWithPlurals.put (key, value) , a wynik netto jest że od czasu do czasu (gdy wielkość przepełnienia stołowych na zaledwie zły czas), dostaniesz wpis dla “dogss” jak również dla “psów” i “pies”. Teraz, to podać w dowolnym miejscu Dokumentacja umieścić , że robi to wywołanie rekurencyjne jest możliwość? L. W takich przypadkach, to na pewno pomaga na źródło kod dostępu do JDK.


 

Q: Dlaczego mój Properties sprzeciw zignorować ustawienia domyślne, gdy zrobić dostać

Nie powinieneś tego robić dostać na obiekt właściwości; należy zrobić getProperty zamiast. Wiele ludzi uważa, że ​​tylko Różnica jest taka, że ​​ getProperty ma zgłoszoną zwracany typ String, natomiast dostać jest zadeklarowana zwraca obiekt. Ale faktycznie istnieje większa różnica: getProperty patrzy na domyślnych. Dostać jest dziedziczona z Hashtable i ignoruje domyślny, co robi dokładnie to, co jest udokumentowane w Hashtable klasy, ale chyba nie to, czego się spodziewać. Inne metody, które są odziedziczone Hashtable (jak IsEmpty oraz toString) również ignorować domyślne. Przykład kodu:
Właściwości

 defaults = new Properties ();
defaults.put ("kolor", "czarny");

Properties props = new Properties (domyślnie);

System.out.println (props.  dostać  ("kolor") + "," +
props  getProperty  (kolor)).;
/ / Drukuje " zerowy ,  czarny "

 

Czy to uzasadnione w dokumentacji? Może. Dokumentacja w rozmowach hashtable o pozycji w tabeli, a zachowaniem Właściwości jest spójne, jeśli założyć, że nie są defauls wpisy w tabeli. Jeśli z jakiegoś powodu myślałeś domyślnie były wpisy (jak może być skłonni uwierzyć przez zachowanie getProperty), wtedy będzie zdezorientowany.


 

Q: Inheritance wydaje błędów. Jak można ustrzec się przed tymi błędami

Poprzednie dwa pytania pokazują, że neeeds programista być bardzo Uważaj przy przedłużaniu klasę, a czasem po prostu w użyciu klasy która rozciąga się w jednej klasie. Problemów, takich jak tych dwóch ołowiu Jana Ousterhout’a powiedzieć “Implementacja dziedziczenia powoduje samo splot i kruchość, które zostały zachowane przy goto stwierdzenia są nadużywane. W rezultacie, system OO często cierpią złożoność i brak ponownego użycia. “( skryptów , IEEE Computer, marzec 1998) oraz Edsger Dijkstra rzekomo powiedzieć: “Object-oriented Programowanie jest wyjątkowo zły pomysł, co może mieć tylko pochodzi z Kalifornii. “(z kolekcji plików sygnatur). I nie sądzę, że jest ogólny sposób, aby zapewnić bezpieczne, ale są kilka rzeczy do być świadomi:

  • Rozszerzanie klasy, który nie masz kod źródłowy jest zawsze ryzykowne; Dokumentacja może być niepełne w sposób nie można przewidzieć.
  • telefoniczny bardzo dąży, aby te nieprzewidziane problemy wyskoczyć.
  • trzeba płacić tak dużo uwagę na metody, które nie over-ride jak metody, które robisz. Jest to jeden z big błędom projektowania obiektowego za pomocą dziedziczenia. Prawdą jest, że dziedziczenie pozwala pisać mniej kodu. Ale trzeba jeszcze myśleć o kodzie Nie zapisu.
  • Ty szukasz kłopotów szczególnie jeśli podklasa zmienia umowy z metodami, lub klasy jako całości. Jest Trudno powiedzieć, kiedy umowa została zmieniona, ponieważ umowy są nieformalne (istnieje formalny udział w podpisaniu typu, ale reszta pojawia się tylko w komentarzach). Właściwości w przykładzie, nie jest jasne, jeśli umowa jest uszkodzony, ponieważ nie jest jasne, jeśli domyślne są uznawani za “pozycje” w tabeli, czy nie.

 


 

Q: Jakie są alternatywy dla dziedzictwo?

Delegacja jest alternatywą dla dziedziczenia. Delegacja oznacza że zawierają instancję innej klasy jako zmiennej instancji, i przekazuje wiadomości do instancji. Często jest bardziej bezpieczne niż dziedziczenie, ponieważ zmusza do myślenia o każdej wiadomości cię do przodu, ponieważ przykład jest znanego klasy, niż nowe klasy, a ponieważ nie zmusza do przyjęcia wszystkie metody Super klasa: można zapewnić jedynie metody, które naprawdę sprawiają, rozsądek. Z drugiej strony, więcej czyni napisać kod i jest trudniejsze do ponownego wykorzystania (ponieważ nie jest klasą).

przykładzie HashtableWithPlurals, delegacja nie daje to (uwaga: z JDK 1.2, Słownik jest nieaktualne; mapka zamiast):

 

 / ** wersja Hashtable że pozwala robić
 * Table.put ("dog", "psów"), a następnie są
 * Table.get ("Psy") return "canine". ** /

public class  HashtableWithPlurals  extends Słownik {

  Table = new Hashtable Hashtable ();

  / ** Producent map zarówno stół i klawisz + "s" do wartości. ** /
  public Object  umieścić  (klawisz Object, Object value) {
    table.put (klucz + "s", wartość);
    powrót table.put (klucz, wartość);
  }

  ... / / Potrzebne do wdrożenia innych metod, jak również
}

 

przykładzie Właściwości, jeśli chcesz wymusić interpretację że wartości domyślne wpisy, lepiej byłoby zrobić z delegacji. Dlaczego to zrobił z dziedziczenia, a potem? Ponieważ Java Zespół wdrożeniowy był w pośpiechu, i wziął kurs, który wymaga pisząc mniej kodu.


 

Q: Dlaczego nie ma globalny zmienne w Javie

Zmienne globalne są uznawane za złe formularz z wielu powodów:

  • Dodawanie zmiennych stanu zrywa więzy przezroczystość (już nie rozumiem oświadczenie lub wyrażenie na własną rękę musisz rozumiem w kontekście ustawień zmiennych globalnych). Zmienne stanu
  • zmniejszenia spójności programu: co musisz wiedzieć więcej zrozumieć, jak coś działa. Głównym punktem Programowanie obiektowe jest zerwać globalny stan na łatwiejsze rozumieć zbiory miejscowego stanu.
  • Po dodaniu jeden zmienna, można ograniczyć korzystanie z programu do jednej instancji. Co wydawało ci się globalne, ktoś mógłby pomyśleć jako lokalne: może chcą uruchomić dwie kopie programu na raz.

Z tych powodów, Jawa postanowił zakaz zmiennych globalnych.


 

Q: I nadal brakuje globalny zmiennych. Co mogę zrobić w zamian?

To zależy od tego, co chcesz zrobić. W każdym przypadku trzeba zdecydować, dwa rzeczy: ile egzemplarzy tej tak zwanej zmiennej globalnej jest mi potrzebne? I gdzie będzie to wygodne miejsce, aby umieścić go? Oto kilka typowych rozwiązania:

 

Jeśli naprawdę chcesz tylko jedną kopię za każdym razem, gdy użytkownik wywołuje Java przez uruchomienie wirtualnej maszyny Java, to prawdopodobnie chcesz statyczna zmienna instancji. Na przykład, masz klasę MainWindow w aplikacji, a chcesz, aby policzyć liczbę okien, które użytkownik jest otwarty, i zainicjować “Naprawdę rzucić?” Okno, gdy użytkownik został zamknięty ostatni. Do tego, jak chcesz:

 / /  jednej zmiennej w klasie (za JVM) 
{public Class MainWindow
   Static int numWindows  = 0;
  ...
  / / Kiedy otwarcie:  MainWindow.numWindows + + ;
  / / Kiedy zamknięcie:  MainWindow.numWindows-- ;
}
W wielu przypadkach naprawdę chcesz zmienną instancji klasy. Na przykład, załóżmy, że napisał przeglądarkę internetową i chciałem mieć Lista historii jako zmiennej globalnej. W Javie, to więcej sensu mieć Lista historii jest zmienną instancji w klasie Browser. Następnie użytkownik może uruchomić dwie kopie przeglądarki na raz, w tym samym JVM, bez konieczności ich krok od siebie.

 / /  jednej zmiennej na przykład 
public class {Browser
   HistoryList historii  = new HistoryList ();
  ...
  / / Upewnij wpisy  this.history 
}

 

Teraz załóżmy, że masz ukończone projekt i większość z wdrożenie przeglądarki, a okazuje się, że w głębi duszy w szczegóły, powiedzmy, klasę Cookie, wewnątrz klasy HTTP, możesz chcesz wyświetlić komunikat o błędzie. Ale nie wiem, gdzie, aby wyświetlić wiadomość. Można łatwo dodać zmienną instancji do przeglądarki Klasa do przechowywania strumienia wyświetlacza lub ramkę, ale nie minęło prąd wystąpienie przeglądarce dół do metod w klasie Cookie. Państwo nie Aby zmienić podpisy wielu metod, aby przejść przeglądarkę wzdłuż. Nie można użyć zmiennej statycznej, bo tam może być wiele Przeglądarki eksploatacji. Jednakże, jeśli można zagwarantować, że nie będzie tylko jedna przeglądarka działa na wątku (nawet jeśli każda przeglądarka może mieć wiele wątków), to nie jest to dobre rozwiązanie: przechowywać tabelę gwint do przeglądarek mapowania zmiennej statycznej w klasie przeglądarce, i poszukać odpowiedniego przeglądarkę (a więc wyświetlacz) korzystania przez bieżący wątek:

 / /  One "zmienna" per wątku 
public class {Browser
   Statyczne przeglądarek Hashtable  = new Hashtable ();
  public Browser () {/ / Konstruktor
    browsers.put (Thread.currentThread (), this);
  }
  ...
  public reportError void (String message) {
    Thread t = Thread.currentThread ();
    ((Browser) Przeglądarka.  Browsers.get (t) )
      . Show (komunikat)
  }
}
wreszcie, że chcemy wartość zmiennej globalnej do przetrwania między wywołaniami JVM, lub które mają być współużytkowane przez wiele JVMs w sieć maszyn. Wtedy prawdopodobnie powinien korzystać z bazy danych, które można dostęp przez JDBC, czy należy serializować danych i zapisać go do pliku.

 


 

Q: Czy mogę napisać grzech (x) zamiast Math.sin (x)

Krótka odpowiedź: Przed Java 1.5, no. W Java 1.5, tak, za pomocą href=”http://java.sun.com/j2se/1.5.0/docs/guide/language/static-import.html”> <a statyczne import ,. można teraz napisać import statyczny java.lang.Math * , a następnie używać sin (x) bezkarnie. Ale zwróć uwagę na ostrzeżenie od Słońca: “Więc kiedy należy użyć statycznej import? Bardzo oszczędnie

Oto niektóre z opcji, które mogą być stosowane przed Java 1.5:

 

Jeśli chcesz tylko kilka metod, które można umieścić w wywołaniach je we własnej klasie:
public static double

  sin  (double x) {return Math.sin (x);}
public static double  cos  (double x) {return Math.cos (x);}
...
sin (x)

 

Metody statyczne się cel (co do lewej kropki), który jest albo Nazwa klasy, czy obiekt, którego wartość jest ignorowana, ale musi być uznano, że z prawej klasy. Więc można zapisać trzy znaki na wezwanie robi:

 / / Nie można utworzyć wystąpienia Math, więc to musi być null.
Matematyka m = null;
...
m.sin (x)

 

java.lang.Math jest final class, więc nie mogą dziedziczyć z nim, ale jeśli mieć swój własny zestaw metod statycznych, które chcesz udostępniać między wiele swoich własnych klas, a następnie można je spakować i dziedziczą je:

public abstract class  MyStaticMethods  {
  public static double mysin (double x) {... }
}

public class  MyClass1  rozszerza MyStaticMethods {
  ...
  mysin (x)
}

 

Peter van der Linden , autor Wystarczy Java , nie zaleca obu ostatnich dwóch praktyki w jego FAQ . Zgadzam z nim, że Matematyka m = null jest to zły pomysł, w większości przypadków, ale Nie jestem przekonany, że MyStaticMethods demonstruje “bardzo kiepskim stylu OOP użyć dziedziczenia do uzyskania trywialny nazwę Skrót (zamiast typu wyrazić hierarchii). “ pierwsze wszystko, trywialne jest w oku patrzącego, skrót może być znaczna. (Patrz HREF=”http://www.norvig.com/jscheme-design.html”> <A example , jak użyłem tego podejścia do tego, co uważam, że było dobre efekt.) Po drugie, jest raczej zuchwalstwem że jest to bardzo bad OOP styl. Można zrobić sprawę, że to jest złe Java styl, ale w językach z wielokrotnego dziedziczenia, to idiom byłoby bardziej akceptowalne.

Innym sposobem patrzenia na to jest to, że cechy Javy (i wszelkie language) muszą obejmować kompromisów i mieszają wiele problemów. Ja Zgadzam się, że jest źle używać dziedziczenia w taki sposób, że w błąd użytkownika do myślenia, że ​​ MyClass1 dziedziczy zachowanie z MyStaticMethods , a to jest złe zakazać MyClass1 rozszerzanie cokolwiek inna klasa to naprawdę chce rozszerzyć. Jednak w Java klasy jest również jednostką enkapsulacji, kompilacja (W większości), a zakres nazwy. MyStaticMethod podejście ocenę punkty ujemne na przedzie hierarchii typu, ale pozytywne punkty na przedni zakres nazwy. Jeśli powiesz, że widok hierarchii typ jest bardziej ważne, nie będę się z tobą kłócić. Ale będę się upierał jeśli myślisz o Klasa jak robi tylko jedno, a nie wielu rzeczy na raz, a jeśli myślisz o stylu przewodnikach jako absolutne, a nie kompromisów.


 

Q: jest Null Object

Absolutnie nie. Przez to, mam na myśli (null instanceof Object) jest false. Kilka innych rzeczy, które powinieneś wiedzieć o nieważną :

  1. Nie można wywołać metodę na null: xm () jest błąd, gdy x jest nieważna i m jest nie-statyczna metoda. (Gdy m jest statyczna metoda jest w porządku, ponieważ jest to klasa x, że liczy, wartość jest ignorowana).
  2. zerowy tylko jeden, nie jeden dla każdej grupy. Tak więc, ((String) null == (Hashtable) null) , na przykład.
  3. Jest ok przekazać null jako argument do metody, tak długo, jak Metoda jest spodziewał. Niektóre metody zrobić, niektóre nie. Tak więc, na przykład System.out.println (null) jest ok, ale string.compareTo (null) nie jest. Dla metod piszesz, Twój javadoc Komentarze powinny powiedzieć, czy null jest ok, chyba że jest to oczywiste.
  4. W JDK 1.1 do 1.1.5, przechodząc nieważne w dosłownym argumentu do konstruktora klasy anonimowej wewnętrznej (np. nowy SomeClass (null) {...} spowodowała błąd kompilatora. Jest ok, aby przejść Wyrażenie, którego wartość jest równa null lub przekazać przymuszany nieważne jak new SomeClass ((String) null) {...}
  5. Istnieją co najmniej trzy różne znaczenia, że ​​null jest powszechnie stosowane do Express:
    • zwalniane. Zmienna lub gniazdo, które nie zostały jeszcze przypisane zostały realna wartość.
    • Non-existant/not dotyczy. Na przykład końcowe węzły Drzewo binarne może być reprezentowany przez zwykłego węzła z pustym dzieckiem wskaźniki.
    • Empty. Na przykład, można użyć wartości null do reprezentowania pusty drzewo. Należy zauważyć, że jest to nieco inne od poprzedniego przypadku, chociaż niektórzy ludzie popełniają błąd, myląc te dwa przypadki. różnica jest zerowy, czy węzeł drzewa akceptowalne, czy też Jest to sygnał, by nie traktować wartość jako węzła drzewa. Porównaj następujące trzy implementacje węzłów drzewa binarnego z założoną na zamówienie metodą druku:

     

 

/ / Null to nie dotyczy
/ / Nie ma pustej drzewa.

Klasa  Node  {
  Dane obiektu;
  Węzeł w lewo, w prawo;

  void  print  () {
    if (left! = null)
      left.print ();
    System.out.println (data);
    if (right! = null)
      right.print ();
  }
}

 

/ / Null oznacza puste drzewo
/ / Uwaga, nie-statyczne metody statyczne

Klasa  Node  {
  Dane obiektu;
  Węzeł w lewo, w prawo;

   void print  (węzeł Node) {
    if (węzeł = null!) node.print ();
  }

  void  print  () {
    print (po lewej);
    System.out.println (data);
    wydrukować (z prawej);
  }
}

 

/ / Oddzielne klasy dla Empty
/ / Null nigdy nie jest używany

Interfejs  Node  {print void ();}

Klasa  DataNode  implementuje Node {
  Dane obiektu;
  Węzeł w lewo, w prawo;

  void  print  () {
    left.print ();
    System.out.println (data);
    right.print ();
  }
}

Klasa  EmptyNode  implementuje Node {
  void  print  () {}
}

 

 


 

Q: Jak duży jest obiekt? Dlaczego jest nie ma sizeof

C ma sizeof operatora, i to musi mieć, bo Użytkownik ma zarządzać połączeniami malloc , a ponieważ wielkość prymitywnych typów (jak długo ) nie jest znormalizowana. Jawa nie trzeba sizeof, ale to wciąż było wygodna pomoc. Ponieważ nie jest tam, można to zrobić:

static Runtime uruchomieniowa = Runtime.getRuntime ();
...
długi start, end;
Object obj;
runtime.gc ();
start = runtime.freememory ();
 obj = new Object  () / / A co chcesz patrzeć na
= koniec runtime.freememory ();
System.out.println ("To zajęło" + (start-end) + "
. bajtów ")

Ta metoda nie jest niezawodny, bo zbieranie śmieci mogłyby wystąpić w

środku kodu Jesteś instrumentacji, wyrzucając liczbę bajtów.
Ponadto
jeśli używasz just-in-time kompilator niektóre bajty mogą pochodzić z
generowanie kodu.

Możecie być zaskoczeni tym, że zajmuje 16 bajtów Object lub 4
słowa, w Sun JDK VM. Ten dzieli się następująco: jest
dwa słowa nagłówek, gdzie jedno słowo jest wskaźnik do obiektu klasy,
i inne punkty do zmiennych instancji. Nawet jeśli obiekt
nie ma zmiennych instancji, Java nadal przydziela jedno słowo
zmiennych. Wreszcie jest “uchwyt”, który jest inny wskaźnik
dwa słowa nagłówka. Niedz mówi, że ten dodatkowy poziom pośredni
sprawia, że ​​zbieranie śmieci prostsze. (Nie były wysokie
wydajność Lisp i kolektory Smalltalk śmieci, które nie korzystają
dodatkowy poziom, przez co najmniej 15 lat. Słyszałem, ale nie potwierdziła, że
Microsoft JVM nie ma dodatkowy poziom pośredni.)

pusty new String () zajmuje 40 bajty lub 10 słowa: 3
Słowa napowietrznych wskaźnik, 3 słów do zmiennych instancji (
rozpocząć indeksu, indeks i koniec tablicy znaków) i 4 słowa na
pusta tablica char. Tworzenie podciągu istniejącego łańcucha trwa
“Tylko” 6 słów, ponieważ tablica char jest wspólna. Umieszczenie Integer
klucz i wartość Integer do Hashtable ma 64 bajtów (oprócz
cztery bajty, które zostały wstępnie przydzielone w tablicy Hashtable): dam
Państwo
pracować, dlaczego.


Q: W jakiej kolejności jest
kod inicjalizacji wykonany? Co należy uwzględnić, gdzie?

Instancja zmiennej kod inicjalizacji może iść w trzech miejscach w ciągu
klasa:

W inicjatorze zmiennej instancji o klasie (lub nadklasy).

Klasa  C  {
    String  var = "val": 
W konstruktorze klasy (lub nadklasy).

    public  C  () { var = "val": }
w obiekcie bloku inicjator. To jest nowy w Javie 1.1, a jego po prostu
jak statycznego bloku inicjator ale bez słowa kluczowego static .
     {var = "val";} 
}

Celu oceny (pomijając z problemami z pamięcią), gdy mówisz nowy C () jest:

  1. Wywołanie konstruktora nadklasy z C (chyba, że ​​C jest Object , w którym to przypadku nie ma nadrzędnej). To zawsze będzie być no-argument konstruktora, chyba że programista jawnie zakodowany super (...) , jak bardzo pierwszej deklaracji konstruktora.
  2. Po super konstruktor powrócił, wykonać dowolną instancję Zmienna inicjalizatory i bloki inicjatora obiektu w tekstowych (Od lewej do prawej) porządek. Nie mylić się tym, że javadoc i javap wykorzystanie kolejność alfabetyczna, to nie ważne.
  3. Teraz wykonaj resztę ciała dla konstruktora. Może to ustawić zmienne instancji lub cokolwiek innego.

W ogóle, masz dużo swobody wyboru jednego z tych trzech formy. Moja rekomendacja jest użycie instancji zmiennych initailizers w przypadki, w których istnieje zmienna ma taką samą wartość, bez względu na których stosuje konstruktora. Użyj bloków inicjatora obiektu tylko wtedy, gdy Inicjalizacja jest złożone (np. wymaga pętli) i nie chcesz powtarzanie tego w wielu konstruktorów. Użyj konstruktora dla reszta.

Oto kolejny przykład:

Program:

Klasa   {
    String a1 = ABC.echo ("1: a1");
    A2 = ABC.echo ciąg ("2: A2");
    publicznych () {ABC.echo ("3: ()");}
}

Klasa  B  rozszerza {
    String b1 = ABC.echo ("4: b1");
    B2 String;
    public B () {
        ABC.echo ("5: B ()");
        b1 = ABC.echo ("6: reset b1");
        a2 = ABC.echo ("7: reset a2");
    }
}

Klasa  C  rozszerza B {
    String c1;
    {C1 = ABC.echo ("8: c1");}
    String c2;
    C3 = ABC.echo String ("9: c3");

    public C () {
        ABC.echo ("10: C ()");
        C2 = ABC.echo ("11: C2");
        b2 = ABC.echo ("12: B2");
    }
}

public class  ABC  {
    static String  echo  (String arg) {
        System.out.println (arg);
        powrót Arg;
    }

    public static void  Main  (String [] args) {
        Nowy C ();
    }
}

 

wyjściowa:

 1: a1
 2: a2
 3: ()
 4: B1
 5: B ()
 6: reset b1
 7: reset a2
 8: c1
 9: c3
10: C ()
11: C2
12: b2

Q: Co z klasy
inicjalizacji

Ważne jest, aby odróżnić od inicjalizacji przykład klasy
stworzenie. Instancja jest tworzony po wywołaniu konstruktora z
nowe . Klasa C jest inicjowany po raz pierwszy jest ona
aktywnie wykorzystywane. W tym czasie, do inicjalizacji Kod klasy jest
bieg w tekstowej kolejności. Istnieją dwa rodzaje inicjalizacji klasy
Kod: statyczne bloki inicjator ( static {...} ) oraz klasy
Zmienna inicjalizatory ( static String var = ... ).

Aktywne korzystanie jest zdefiniowany jako pierwszy raz to zrobić jeden z
następujące brzmienie:

  1. Utwórz instancję C przez wywołanie konstruktora;
  2. Zadzwoń statyczną metodę zdefiniowaną w C (nie dziedziczone);
  3. Przypisywanie lub uzyskać dostęp do zmiennej statycznej deklarowaną (nie
    dziedziczone) w C . To się nie liczy, jeśli zmienna jest statyczna
    zainicjowana stałej ekspresji (jeden z udziałem tylko prymitywne
    operatorów (jak + lub | |), literałów i statyczne zmienne końcowe),
    bo te są inicjowane w czasie kompilacji.

Oto przykład:

Program:
Klasa   {
    static String a1 = ABC.echo ("1: a1");
    A2 = statyczna String ABC.echo ("2: A2");
}

Klasa  B  rozszerza {
    static String b1 = ABC.echo ("3: b1");
    static b2 String;
    static {
        ABC.echo ("4: B ()");
        b1 = ABC.echo ("5: reset b1");
        a2 = ABC.echo ("6: reset a2");
    }
}

Klasa  C  rozszerza B {
    static String c1;
    static {c1 = ABC.echo ("7: c1");}
    static String c2;
    static String c3 = ABC.echo ("8: c3");

    static {
        ABC.echo ("9: C ()");
        C2 = ABC.echo ("10: C2");
        b2 = ABC.echo ("11: b2");
    }
}

public class  ABC  {
    static String  echo  (String arg) {
        System.out.println (arg);
        powrót Arg;
    }

    public static void  Main  (String [] args) {
        Nowy C ();
    }
}

wyjściowa:

 1: a1
 2: a2
 3: B1
 4: B ()
 5: reset b1
 6: reset a2
 7: c1
 8: c3
 9: C ()
10: C2
11: b2

Q: Mam klasy z
sześć instancji zmienne, z których każdy może być zainicjowany, czy nie. Powinien
Piszę 64
Konstruktorzy

Oczywiście nie trzeba (2 6 ) konstruktorów. Powiedzmy, że masz
Klasa C określone następująco:

 

public class  C  {int a, b, c, d, e, f;}

Oto kilka rzeczy, które możesz zrobić dla konstruktorów:

  1. Zgadnij, co kombinacje zmiennych prawdopodobnie będzie chciał,
    i zapewnienia konstruktorów tych kombinacji.
    Pro: To jest, jak to się zwykle robi.
    Con: , trudno zgadnąć prawidłowo; dużo nadmiarowego kodu do
    pisać.
  2. Zdefiniuj ustawiaczy, że mogą być łączone kaskadowo, ponieważ powrót ten .
    Oznacza to, że określenie setter dla każdej zmiennej instancji, a następnie użyć ich po
    wywołanie domyślnego konstruktora:
public  C  seta (val int) {a = val; wrócić w tym;}
...
. nowy C () seta (1) SIWZ (3) Sete (5);..

Pro: To jest dość proste i skuteczne podejście. Podobny
pomysł
jest omawiany przez Bjarne Stroustrop na stronie 156 z Projektowanie i
Evolution of C + +
.
Con: Musisz napisać wszystkie małe ustawiaczy, nie są one
JavaBeans zgodny (ponieważ powrót ten , nie void ),
nie działają, czy są interakcje między dwiema wartościami.

  • Użyj konstruktora domyślnego dla anonimowego sub-klasie
    niestatyczny inicjator:

 

Nowy C () {{a = 1, c = 3, e = 5;}}

Pro: Bardzo zwięzły; nie bałagan z ustawiaczy.
Con: Zmienne instancji nie mogą być prywatne, masz nad głową
z podklasy, twój obiekt nie będzie
rzeczywiście C jako swojej klasie (choć to nadal będzie instanceof
C), działa tylko jeśli masz dostępne zmienne instancji, a wiele
osób, w tym doświadczonych programistów Java, nie będzie
zrozumieć. Właściwie, jest dość prosty: definiowania nowych,
unnamed (anonim) podklasa C, bez nowych metod lub zmiennych, ale
z bloku inicjalizacji, który inicjuje, C i E. Wzdłuż
przy definiowaniu tej klasy, to są również dokonywania instancję. Kiedy pokazałem
to Guy Steele powiedział “heh, heh! To bardzo ładny, wszystko w porządku,
ale nie jestem pewien,
Chciałbym opowiadają szerokie zastosowanie … “
Jak zwykle, Guy jest prawo.
(Nawiasem mówiąc, można również wykorzystać do tworzenia i zainicjować wektor.
Wiesz, jak wspaniale jest utworzyć i zainicjować, powiedzmy, tablicy ciągów
z new String [] {"jeden", "dwa", "trzy"} . Teraz z
Klasy wewnętrzne można zrobić to samo dla wektora, gdzie wcześniej
myślałeś, że musisz użyć stwierdzenia assignement:
nowy wektor (3) {{dodaj ("jeden"); add ("dwa"); add ("trzy")}} ).

 

  • Możesz przełączyć się do języka, który bezpośrednio wspiera ten idiom .. Dla
    Na przykład,
    C + + ma opcjonalne argumenty . Więc można to zrobić:

 

Klasa  C  {
publicznej: C (int a = 1, b = 2 int, int c = 3, d = 4 int, int e = 5);
}
...
Nowy C (10) / /  Construct wystąpienie z domyślnych dla b, c, d, e 

Common Lisp oraz Python mają argumentów kluczowych , jak i opcjonalne argumenty,
więc można to zrobić:

C (= 10, c = 30, e = 50) #  Construct instancji;. Defaults użytku do B i D 

Q: Kiedy należy używać konstruktorów,
i
kiedy należy stosować inne metody?

Glib odpowiedź jest użycie konstruktorów, gdy chcesz nowy obiekt;
że to, co kluczowe nowe jest dla. Odpowiedź jest nieczęste
, które są często zbyt konstruktorzy wykorzystane, gdy zarówno nazywa
i jak wiele mają do zrobienia. Oto kilka punktów do rozważenia

  • Modyfikatory: Jak widzieliśmy w poprzednim pytaniu, można przejść
    burtę dostarczyły zbyt wielu konstruktorów. Zwykle jest lepiej
    zminimalizować liczbę konstruktorów, a następnie podać modyfikator
    metody, które robią resztę inicjalizacji. Jeżeli modyfikatory
    powrót ten , a następnie można utworzyć przydatne obiektu w jednym
    wyrażenie, jeśli nie, trzeba będzie zastosować wiele
    oświadczenia. Modyfikatory są dobre, ponieważ często zmiany chcesz
    dokonać w trakcie budowy są również zmiany, które chcesz, aby później
    dlaczego więc powielać kod pomiędzy konstruktorami i metod.

 

  • Fabryki: Często chcesz stworzyć coś, co jest
    instancja pewnej klasy lub interfejsu, ale albo nie obchodzi dokładnie
    która podklasa tworzyć, lub chcesz odłożyć tę decyzję do
    runtime. Na przykład, jeśli piszesz aplet kalkulatora, można
    może chcieć, że można zadzwonić nowy Numer (string) , i
    Pokój ten powrót, jeśli ciąg jest w formacie zmiennoprzecinkowym,
    lub długo, jeśli ciąg jest w liczbę całkowitą. Ale nie można robić
    że z dwóch powodów: Liczba to klasa abstrakcyjna, więc nie może powoływać
    jego konstruktor bezpośrednio, a każde wezwanie do konstruktora musi wrócić
    Nowa instancja tej klasy bezpośrednio nie, podklasy. Metoda
    która zwraca obiekty podobnie do konstruktora, ale to ma więcej swobody w sposobie
    Obiekt jest wykonany (i jakie to jest) jest
    nazywany fabryki . Java nie ma wbudowanego wsparcia lub konwencji
    dla fabryk, ale będziemy chcieli wynaleźć konwencje ich wykorzystania
    w kodzie.

 

 

  • Caching i recykling: konstruktor musi stworzyć nowy
    obiekt. Ale tworzenie nowego obiektu jest dość drogie
    operacji. Podobnie jak w rzeczywistym świecie, można uniknąć kosztownych śmieci
    kolekcja recyklingu. Na przykład, new Boolean (x) tworzy
    new Boolean, ale prawie zawsze powinien
    używać zamiast (x Boolean.TRUE: Boolean.FALSE) ,
    który przetwarza istniejącą wartość zamiast rozrzutnie tworzenia nowego
    jeden. Java byłoby lepiej, gdyby to metoda, która reklamowana
    A właśnie to, a nie reklamy konstruktora.
    Boolean to tylko jeden przykład, powinieneś
    również rozważyć recyklingu innych klas niezmiennych, w tym
    Character, Integer, a być może wielu z własnych klas. Poniżej
    Przykładem fabryki recyklingu Numbers. Gdybym miał wybór,
    nazwałbym to Number.make , ale oczywiście nie mogę dodać
    metody do klasy Number, więc będzie trzeba iść gdzie indziej.

 

  publiczny numer  numberFactory  (String str) {rzuca NumberFormatException
    try {
      długości l = Long.parseLong (str);
      if (l> = 0 && cachedLongs.length l <) {
        int i = (int) l;
        if (cachedLongs [i] = null!) cachedLongs powrotne [i];
        else return cachedLongs [i] = new Long (str);
      } Else {
        return new Long (L);
      }
    } Catch (NumberFormatException e) {
      double d = Double.parseDouble (str);
      powrót d == 0,0? ZERO: d == 1,0? ONE: new Double (d);
    }
  }

  private Long [] = new cachedLongs Long [100];
  prywatne podwójne zero = new double (0,0);
  prywatne Dwuosobowy ONE = new double (1,0),

Widzimy, że nowe jest przydatna konwencja, ale
Fabryki i recykling są również przydatne. Java wybrał
obsługuje tylko nowe , ponieważ jest to najprostsza możliwość, a
filozofia Java jest utrzymanie sam język tak proste jak
możliwe. Ale to nie oznacza, że ​​biblioteki klas trzeba się trzymać
Najniższa mianownik. (I to nie oznaczało, że wbudowany w
Biblioteki przyklejone do niego, ale niestety, oni.)



P: Czy otrzymam zabity przez napowietrznej tworzenia obiektów i GC

Załóżmy, że aplikacja ma do czynienia z dużo manipulacji 3D geometryczne punktów.
Oczywistym sposobem Java to zrobić jest mieć klasy Point z sobowtórów dla x, y, z współrzędne. Ale przeznaczenie i odzyskują pamięć dużo punktów można
rzeczywiście powodować problemy z wydajnością. Możesz pomóc zarządzania własnym
przechowywania w puli zasobów .

Zamiast podziału każdy punkt, gdy jej potrzebujesz, możesz przeznaczyć na
dużej tablicy punktów na początku programu. Array (zawinięte
w klasie) działa jako fabrycznie punktów, ale
społecznie świadomy fabryki recyklingu. Wywołanie metody
pool.point (x, y, z) zajmuje pierwsze niewykorzystane punkt w tablicy,
określa swoje 3 pola do określonych wartości i oznacza go jako używane. Teraz
ty jako programista jest odpowiedzialny za powrót do puli punktów
raz, że nie są już potrzebne. Istnieje kilka sposobów, aby to zrobić.
Najprostszym jest, gdy wiesz, będzie przydzielanie punktów w blokach
, które są używane przez dłuższy czas, a następnie odrzucono. Następnie zrobić int pos
= Pool.mark ()
, aby zaznaczyć aktualną pozycję w basenie. Kiedy
wykonywane są z sekcji kodu, zadzwoń pool.restore (pos)
, aby ustawić znak z powrotem do pozycji. Jeśli istnieje kilka punktów, które
chcesz zachować, po prostu umieszczając je z innej puli.

Pula zasobów pozwala uniknąć kosztów poboru śmieci (jeśli masz
dobry model, gdy obiekty zostanie zwolniona), ale nadal masz
początkowe koszty tworzenia obiektów. Można obejść, że wchodząc
“Powrót do Fortran”: przy użyciu macierzy X, Y i Z, a nie
poszczególne obiekty punktowe. Masz klasę punktów, ale nie klasa dla
pojedynczy punkt. Rozważ tę klasę puli zasobów:

public class  PointPool  {
  / ** Przeznaczyć pulę n punktów. ** /
  public  PointPool  (int n) {
    x = new double [n];
    y = new double [n];
    z = new double [n];
    next = 0;
  }
  public double  x  [],  y  [],  z  [];

  / ** Zainicjować następny punkt, przedstawiony jako liczba całkowita w indeksie. ** /
  int  punkt  (double x1, double y1, double z1) {
    x [dalej] = x1; y [dalej] = y1, z [dalej] = z1;
    wrócimy w przyszłym + +;
  }

  / ** Zainicjować następny punkt, initilized do zera. ** /
  int  punkt  () {return punkt (0,0, 0,0, 0,0);}

  / ** Zainicjować kolejny punkt jako kopię punktu w jakimś basenie. ** /
  int  punkt  (basen PointPool, int p) {
    powrót punkt (pool.x [p], pool.y [p], pool.z [p]);
  }

  public int  Dalej ;
}

Można by użyć tej klasy w następujący sposób:

PointPool pool = new PointPool (1000000);
Wyniki PointPool = new PointPool (100);
...
int pos = pool.next;
doComplexCalculation (...);
pool.next = pos;

...

doComplexCalculation void (...) {
  ...
  int p1 = pool.point (x, y, z);
  int p2 = pool.point (p, q, r);
  double diff = pool.x [p1] - pool.x [p2];
  ...
  int p_final = results.point (basen, p1);
  ...
}

Przydzielanie milion punktów trwało pół sekundy za PointPool podejście i 6 sekund proste podejście, które przeznacza miliony wystąpień Punkt class, więc to jest 12-krotne przyspieszenie.

Czy nie byłoby miło, gdyby mógł zadeklarować P1, P2 i p_final jako Punkt , zamiast int ? W C lub C + +, to może po prostu nie typedef int Punkt , ale Java nie pozwala. Jeśli jesteś siłach, możesz ustawić, aby pliki do uruchomienia plików przez C preprocesor przed kompilatora Java, a następnie można zrobić # define Punkt int .


 

Q: Mam złożone wyrażenie wewnątrz pętli. Dla efektywności, chciałbym Obliczenie jest tylko raz. Ale dla czytelności, chcę go zatrzymać w pętli w którym jest stosowany. Co mogę zrobić?

Załóżmy przykład gdzie mecz jest wyrażenia regularnego rutynowe spotkania, a compile kompiluje ciąg na maszynie skończonej państwa, które mogą być wykorzystane przez mecz :

for (; ;) {
  ...
  String str = ...
  match (str, kompilacji ("a * b * c *"));
  ...
}

Ponieważ Java nie ma makra i niewielką kontrolę nad czasem realizacji, Twoje wybory są ograniczone tutaj. Jedną z możliwości, choć nie bardzo ładna, jest wykorzystanie interfejsu wewnętrzną o zmiennej inicjatora:

for (; ;) {
  ...
  String str = ...
  Interfejs P1 {FSA f = compile ("a * b * c *);}
  match (str, P1.f);
  ...
}

Stosunek P1.f zostanie zainicjowany na pierwszym użyciu P1 , i nie zmienia się, ponieważ zmienne interfejsów są niejawnie static final . Jeśli ci się nie podoba, że ​​można przełączyć na język, który daje lepiej kontrolować. W Common Lisp, sekwencji znaków #. oznacza ocenić następujące wyrażenie na czytaj (Kompilacji) czas nie, czas. Więc można napisać:

(Pętla
  ...
  (Mecz str #. (Skompilować "a * b * c *"))
  ...)

 


 

Q: Jakie inne operacje są zaskakująco niska?

Od czego mam zacząć? Oto kilka z nich, które są najbardziej przydatne wiedzieć temat. Napisałem narzędzia rozrządu, który działa fragmentów kodu w pętli, raportowania wyników w kategoriach tysięcy iteracji na sekundę (K / s) i na iteracji mikrosekund (uSecs). Timing dokonano na Sparc 20 z JDK 1.1.4 kompilator JIT. I pamiętać, że:

  • Były wszystkie wykonane w 1998 roku. Kompilatory zmieniło od tamtego czasu.
  • Odliczanie (tj. for (int i = n; i> 0, i -) ) jest dwukrotnie szybko, jak liczenie: moja maszyna może odliczać do 144 mln EUR w sekund, ale do tylko 72 mln.
  • Dzwonienie Math.max (a, b) jest 7 razy wolniej niż (a> b) ? : b . To koszt metody. Tablice
  • są 15 do 30 razy szybciej niż wektory. Hashtables są 2/3 tak szybko, jak wektory.
  • Korzystanie bitset.get (i) jest 60 razy wolniejszy niż bitów & 1 << i . To jest koszt metody synchronicznej dzwonić, przeważnie. Oczywiście, jeśli chcesz więcej niż 64 bitów, nie można używać mój bit-twiddling przykład. Oto wykres czasów uzyskania i określające elementy różnych struktur danych:
      K / sec Operation Code uSecs
    ===============================================
      147.058 0.007 = & 0x100; dostać element int bitów
          314 3.180 bitset.get (3); dostać element bitset
       20.000 0.050 obj = OBJS [1]; dostać element tablicy
        5.263 0.190 str.charAt (5); dostać element String
          361 2.770 buf.charAt (5); dostać element StringBuffer
          337 2.960 objs2.elementAt (1); dostać element Vector
          241 4.140 hash.get ("a"); dostać element Hashtable
    
          336 2.970 bitset.set (3), ustawić element bitset
        5.555 0.180 OBJS [1] obj =; elementem zestaw Array
          355 2.810 buf.setCharAt (5, '') element zestaw StringBuffer
          308 3.240 objs2.setElementAt (1 zestaw elementów wektora
          237 4.210 hash.put ("a", obj); elementem zestaw Hashtable

     

  • kompilatory Java są bardzo ubogie w podnoszenie stałej wyrażeń z
    z pętli. C / Java dla pętli jest zły abstrakcją, ponieważ
    zachęca ponowne obliczenie wartości końcowej w najbardziej typowy
    przypadku. Więc for (int i = 0; i <str.length (); i + +) jest trzy razy
    wolniej niż int len ​​= str.length (); for (int i = 0; i <len;
    i + +)

Q: Czy można uzyskać dobrą radę od
książek o Javie

Istniejedużoksiążek Java tam, wchodzących na trzy klasy:

Bad. Większość książek Java są pisane przez ludzi, którzy nie mogli
praca jako programista Java (od programowania prawie zawsze płaci więcej
niż pisanie książki, wiem bo robiłem oba). Te książki są
pełna błędów, porad i programów złe złe. Te książki są
niebezpieczne dla początkujących, ale są łatwo rozpoznawane i odrzucane przez
Programista z nawet niewielkim doświadczeniem w innym języku.

Excellent.
Istnieje niewielka liczba znakomitych książek Java. Podoba mi się oficjalna
Specyfikacja i książki Arnold i
Gosling , Marty Hall ,
i Peter van
der Linden
.
Dla odniesienia lubię Java w pigułce i
online, referencje na niedz (kopia I
javadoc href=”http://java.sun.com/products/index.html”> <a API i
język href=”http://java.sun.com/docs/books/jls/index.html”> <a
Specyfikacja i jej poprawki do mojego lokalnego dysku i zakładkę je w mojej przeglądarce, więc ‘ll zawsze mieć szybki dostęp.)

Iffy. W między tymi dwoma skrajnościami jest zbiorem niechlujstwa
pisania przez ludzi, którzy powinni wiedzieć lepiej, ale albo nie podjęły
czas, aby naprawdę zrozumieć, jak Java działa, lub są po prostu pędzi, aby uzyskać
coś publikowane szybko. Jednym z takich przykładów półprawd jest Edward Yourdona’S Java
i nowy paradygmat programowania Internet
z Rise
Resurrection of American Programmer
[przypis na Yourdona] . Oto co mówi Yourdona
o tym, jak różni Java jest:

  • “Funkcje zostały wyeliminowane” To prawda, że ​​nie ma
    “Funkcja” słowo kluczowe w Javie. Java nazywa ich metod (i Perl zwraca je
    podprogramy, i schemat nazywa je procedury, ale nie powiedziałbym, te
    języki wyeliminowały funkcji). Można zasadnie powiedzieć, że nie
    istnieją globalne funkcje w Javie. Ale myślę, że byłoby bardziej precyzyjne, aby
    powiedzieć, że nie są funkcje w zasięgu światowym, a jej po prostu, że
    muszą być zdefiniowane w klasie, i nazywane są “statyczne metoda Por.
    zamiast “funkcji f “.

 

  • “Automatyczne coercions typów danych zostały wyeliminowane” To prawda
    ograniczenia, które są w coercions, które są wykonane, ale
    wyeliminowane są daleko. Nadal można powiedzieć (1,0 + 2) i 2 będzie
    zostanie automatycznie przekonwertowana na podwójne. Albo można powiedzieć ("jeden" +
    2)
    i 2 będzie zmuszona do łańcucha.

 

 

  • “Wskaźniki i arytmetyczne wskaźnika zostały wyeliminowane” To prawda, że
    wyraźne arytmetyczna wskaźnika została wyeliminowana (i niech spoczywa w spokoju). Ale
    wskaźniki pozostają w rzeczywistości, każdy odwołanie do obiektu jest wskaźnik.
    (Dlatego mamy NullPointerException .)
    To jest niemożliwe, aby mieć właściwy programista Java bez zrozumienia
    ta. Każdy programista Java musi wiedzieć, że gdy zrobisz:

 

    int [] = {0, 1, 2};
    int [] b = a;
    b [0] = 99;

następnie [0] jest 99 , ponieważ i b jest
wskazówki (lub linków) do
sam obiekt.

 

  • “Ponieważ struktury zostały usunięte, a tablice i ciągi są reprezentowane jako
    obiekty, potrzeba wskaźników w dużej mierze zanikło. “
    to także
    mylące. Po pierwsze, struktury nie zostaną usunięte, są one po prostu przemianowana
    “Klas”. Co już nie jest kontrola nad tym, czy programista
    struktura / klasa przypadki są alokowane na stercie lub na stos. W Javie wszystkie
    obiekty są alokowane na stercie. Dlatego nie ma potrzeby
    składniowe znaczniki (np. * ) dla wskaźników – jeśli odwołuje się
    obiekt w Javie, to wskaźnik. Yourdan ma rację mówiąc, że posiadanie
    wskaźniki do środka łańcucha lub tablicy jest uważany za dobry idiomatyczne
    Wykorzystanie w C i asemblera (i przez niektórych ludzi w C + +), ale
    nie jest ani obsługiwane ani
    brakowało w innych językach.

 

 

  • Yourdona zawiera również szereg drobnych literówek, jakby powiedzieć, że
    Tablice mają długość () metoda (zamiast długości
    pole) i że ciągi modyfikowalne są reprezentowane przez
    StringClass (zamiast StringBuffer ). To
    irytujące, ale nie tak szkodliwe, jak bardziej podstawowych półprawd.

 

Tłumaczenia name=”translations”> <a:


Peter Norvig

Leave your message