Java - Myśleć jak programista
Autor: Allen B. Downey
Tutuł oryginału - How to Think Like a Computer Scientist, Java Version, 4th Edition
Treść oryginału dostępna jest pod adresem http://www.greenteapress.com/thinkapjava/ .
Niniejszy tekst jest tłumaczeniem powyższej pozycji. Jeżeli chciałabyś (chciałbyś) wspomóc proces tłumaczenia, twój czas i zaangażowanie są bardzo mile widziane. Zapoznaj się z oryginalnym tekstem, skoncentruj się na którymś z rozdziałów i zacznij tłumaczyć. Wszelkie przetłumaczone akapity umieść tutaj w odpowiednim rozdziale, tak aby ktoś inny nie tłumaczył po raz drugi tego, co raz już zostało przez Ciebie przetłumaczone.
Przedmowa
Dlaczego napisałem tę książkę
To jest czwarta edycja książki, którą rozpocząłem pisać w 1999 roku, kiedy uczyłem w Colby College. Uczyłem początkującą klasę informatyczną języka programowania Java. Nie znalazłem jednak podręcznika, który by mnie uszczęśliwił. Z jednej strony, istniejące podręczniki były zbyt obszerne! Nie było możliwości, by moi studenci przeczytali 800 stron gęstego, technicznego materiału, nawet gdybym tego od nich oczekiwał. I nie oczekiwałem. Większość materiałów było zbyt szczegółowych - o języku Java i jego bibliotekach. Wszystko to mogłoby się okazać nieaktualne pod koniec semestru. Wszystko to przesłaniało także materiał, który faktycznie chciałem moim studentom przekazać.
Innym problemem, z którym się spotkałem, było to, że wstęp do programowania zorientowanego obiektowo był zbyt gwałtowny. Wielu studentów, którzy do tej pory radzili sobie bardzo dobrze, rozkładali ręce, gdy dochodzili do obiektów.
[...]
Droga do programowania
[edytuj]Podstawowym celem tej książki jest nauczenie ciebie myślenia jak programista. Osobiście lubię osoby, ktore myślą w taki sposob jak programiści, ponieważ łączą w sobie to co najlepsze w matematykach, inżynierach i naukowcach. Podobnie do matematyków programiści używają specyficznych języków w celu zapisania pomysłów (w szczególności dotyczących obliczeń). Podobnie do inżynierów, projektują układy łączące się w systemy, ewoluujące w kierunku doskonalszych rozwiązań. Podobnie do naukowców, obserwują zachowanie skomplikowanych systemów, formułują hipotezy, oraz testują je doświadczalnie.
Najważniejszą umiejętnością programisty jest rozwiązywanie problemów. Mówiąc to mam na myśli zdolność do formułowania problemów, kreatywnego myślenia na temat rozwiązania oraz zrozumiałego i właściwego wyrażania rozwiązania problemu. Dzięki temu nauka programowania jest wspaniałą okazją do ćwiczenia umiejętności rozwiązywania problemów. Dlatego właśnie nazwałem ten rozdział "Droga do programowania".
[...]
Czym jest język programowania?
[edytuj]Język programowania, którego będziesz się właśnie uczyć to Java. Jest to relatywnie nowy język (Sun wydał jego pierszą wersję w maju 1995 roku). Java jest przykładem języka programowania wysokiego poziomu. Inne języki wysokiego poziomu, o których mogłeś słyszeć to Pascal, C, C++ jak również FORTRAN.
Jak zapewne domyślasz się po nazwie "język wysokiego poziomu", istnieją również języki niskiego poziomu, czasami zwane językami maszynowymi lub asemblerami. Mówiąc najprościej, komputery potrafią bezpośrednio wykonywać wyłącznie programy napisane w języku niskiego poziomu. Dlatego programy napisane w języku wysokiego poziomu muszą zostać przetłumaczone na język niskiego poziomu zanim zostaną wykonane przez komputer. To tłumaczenie zajmuje nieco czasu, co jest pewną niedogodnością języków wysokiego poziomu.
Jednak ich zalet jest znacznie więcej. Po pierwsze, o wiele prościej jest pisać programy w języku wysokiego poziomu. Mówiąc "prościej" mam na myśli to, iż znacznie mniej czasu potrzeba, by program napisać. Jest on krótszy i bardziej czytelny, ponadto znacznie łatwiej wyszukiwać w nim błedy i wprowadzać poprawki. Po drugie, programy napisane w językach wysokiego poziomu są znacznie bardziej przenośne, tzn. można je uruchamiać na wielu rodzajach komputerów praktycznie bez potrzeby wprowadzania dodatkowych modyfikacji. Programy napisane w języku niskiego poziomu mogą być uruchamiane wyłącznie na platformie, na którą zostały napisane. W celu uruchomienia programu na innej platformie należałoby taki program przepisać od podstaw.
Ze względu na powyższe zalety, praktycznie wszystkie programy pisane są w językach wysokiego poziomu. Języki niskiego poziomu używane są w nielicznych specjalizowanych aplikacjach.
Istnieją dwie drogi przetłumaczenia programu napisanego w języku wysokiego poziomu do języka niskiego poziomu tak, by mógł być uruchomiony na komputerze: interpretacja oraz kompilacja. Interpreter to program odczytujący i jednocześnie wykonujący zawartość programu w języku wysokiego poziomu wiersz po wierszu.
Kompilator to program, który odczytuje i tłumaczy zawartość programu wysokiego poziomu w całości przed wykonaniem jakiejkolwiek komendy tego programu. Często kompilacja programu przebiega jako osobny krok. Skompilowany kod uruchamiany jest później. W tej sytuacji program napisany w języku wysokiego poziomu nazywamy kodem źródłowym, natomiast całkowicie przetłumaczony program - kodem obiektowym lub plikiem wykonywalnym.
Jako przykład proponuję ci napisać program w języku C. Być może użyjesz w tym celu edytora tekstu (edytor tekstu to uproszczony procesor tekstu). Kiedy program będzie skończony, zapiszesz go w pliku program.c, gdzie słowo program jest nazwą nadaną przez ciebie, natomiast końcówka .c jest, zgodnie z umową, rozszerzeniem nazwy pliku informującą, że plik zawiera kod źródłowy napisany w języku C.
Potem, w zależności od ulubionego środowiska programistycznego, opuścisz edytor tekstu i uruchomisz kompilator. Kompilator odczyta kod źródłowy twojego programu, przetłumaczy go i utworzy nowy plik o nazwie program.o (dla kodu obiektowego) lub program.exe (dla pliku wykonywalnego).
Język Java jest pod tym względem wyjątkowy, gdyż jest językiem zarówno kompilowanym jak i interpretowanym. Zamiast tłumaczyć programy w Javie do języka maszynowego, kompilator Javy generuje kod bajtowy Javy. Kod bajtowy jest prosty (i szybki) w interpretacji, zupełnie jak kod maszynowy. Jednocześnie jest równie przenośny jak języki wysokiego poziomu. Dzięki temu możliwa jest kompilacja programu w Javie na jednym komputerze, przesłanie kodu bajtowego do innego komputera przez sieć komputerową, oraz interpretacja kodu bajtowego przez inny komputer. Jest to zaletą języka Java, której nie posiadają inne języki wysokiego poziomu.
Choć powyższy proces może wydawać się skomplikowany, w większości środowisk programistycznych (czasem zwanych środowiskami rozwojowymi), te kroki zostały zautomatyzowane dla użytkownika. Zazwyczaj będziesz musiał tylko napisać program i nacisnąć przycisk (lub wydać pojedynczą komendę) by skompilować i uruchomić swój program. Z drugiej strony, dobrze jest znać wszystkie etapy zautomatyzowanego procesu, tak aby w sytuacji, gdy coś pójdzie nie po naszej myśli, móc zorientować się, gdzie leży przyczyna.
Czym jest program?
[edytuj]Program jest ciągiem instrukcji, które określają, jak wykonać obliczenia. Obliczenia matematyczne mogą być czymś matematycznym, na przykład algorytm rozwiązania układu równań lub znalezienie pierwiastka wielomianu, ale mogą to być również obliczenia symboliczne, jak wyszukiwanie i zamiana tekstu w dokumencie lub (o dziwo) kompilacji programu .
Instrukcje, które nazywamy wyrażeniami, wyglądają inaczej w różnych językach programowania, ale istnieje kilka podstawowych operacji, które wykonuje większość języków:
wejście (input):
Pobierz dane z klawiatury lub pliku, lub z innego urządzenia.
wyjście (output) :
Wyświetlanie danych na ekranie lub wysyłanie danych do pliku lub innego urządzenia.
operacje matematyczne:
Wykonywanie podstawowych operacji matematycznych, takich jak dodawanie czy mnożenie.
testowanie:
Testowanie na pewnych określonych warunkach i uruchamianie odpowiedniej sekwencji instrukcji.
pętle:
Wykonywanie jakiejś akcji wielokrotnie, zwykle z pewnymi zmianami.
To prawie wszystko, co jest niezbędne. Każdy program, jaki kiedykolwiek był używany, bez względu na to, jak skomplikowany, składa się z instrukcji, które wykonują te przedstawione wyżej czynności. Zatem jednym ze sposobów, aby opisać programowanie to proces dzielenia dużego, skomplikowanego zadania na mniejsze podzadania (podproblemy), aż staną się one proste do wykonania z wykorzystaniem tych podstawowych operacji.
Co to jest debugowanie?
[edytuj]Z dziwnego powodu błędy w programowaniu nazywane są bug'ami a proces ich odszukiwania i poprawiania - debugowaniem. Istnieją trzy rodzaje błędów, które mogą wystąpić w aplikacji, a ich rozpoznawanie pomaga w szybszym ich odnajdywaniu.
Błędy składniowe
[edytuj]Kompilator potrafi przetłumaczyć program tylko jeżeli został on napisany poprawnie składniowo. W przeciwnym wypadku kompilacja zakończy się niepowodzeniem i nie będziesz mógł uruchomić swojego programu. Składnia to również struktura twojego kodu i zasady związane z tą strukturą. Dla przykładu w języku polskim zdanie musi się zaczynać wielką literą i kończyć kropką. to zdanie zawiera błąd składniowy. To również
Dla większości czytelników kilka błędów składniowych nie jest znaczącym problemem, dzięki czemu potrafimy czytać poezję bez wyrzucania błędów.
Kompilatory niestety nie są tak wyrozumiałe. Jeżeli gdziekolwiek w Twoim kodzie jest błąd składniowy kompilator wyświetli komunikat błędu i zakończy się - nie będziesz mógł uruchomić swojego programu.
Aby było jeszcze trudniej, więcej jest zasad składniowych w Java niż jest ich w języku angielskim, a komunikaty błędów kompilatora bywają mało pomocne. Podczas pierwszych tygodni swojej kariery jako programista spędzisz zapewne dużo czasu wyszukując błędy składniowe. Jednak wraz ze zdobywanym doświadczeniem będziesz popełniać ich coraz mniej i odszukiwać szybciej.
Błędy podczas działania
[edytuj]Drugim rodzajem błędów są błędy występujące podczas działania programu (ang. run-time error), nie pojawią się dopóki nie uruchomisz aplikacji. W Java błędy te występują gdy interpreter uruchamia kod bajtowy i coś idzie nie tak.
Java zmierza do bycia pewnym i bezpiecznym językiem, co oznacza że kompilator wyłapuje wiele błędów. Tak więc błędy podczas działania programu są rzadkie, zwłaszcza w skali drobnych aplikacji.
W Java błędy te nazywane są wyjątkami i w większości środowisk pojawiają się jako okienka zawierające informacje o tym co się wydarzyło i czym program się zajmował w momencie wystąpienia błędu - te informacje są przydatne przy debugowaniu.
Błędy logiczne i semantyczne
[edytuj]Trzecim rodzajem błędów są błędy logiczne i semantyczne. Jeżeli w twoim kodzie występuje błąd logiczny skompiluje i uruchomi się on bez generowania błędów, ale nie zrobi tego co powinien. Zrobi coś innego. Uściślając, zrobi to co ty mu powiesz by zrobił.
Problem polega na tym, że program który napisałeś nie jest tym programem który chciałeś napisać. Semantyka lub znaczenie programu, są błędne. Zidentyfikowanie błędów logicznych może być trudne, ponieważ musisz pracować na odwrót - obserwując efekty działania programu próbować zrozumieć co zostało wykonane.
Debugowanie eksperymentalne
[edytuj]Jedną z najważniejszych umiejętności, których nauczysz się na tych zajęciach jest debugowanie. Pomimo tego, że może ono być frustrujące, jest jednym z najbardziej interesujących, wymagających i wartościowych elementów programowania.
Debugowanie jest jak praca detektywa. Konfrontujesz się z podpowiedziami i musisz wydedukować proces i zdarzenia, które doprowadziły do rezultatu, który widzisz.
Debugowanie jest również jak nauka eksperymentalna. W momencie gdy masz pomysł co się dzieje źle zmodyfikuj swój program i spróbuj ponownie. Jeżeli twoja hipoteza była trafna możesz przewidzieć rezultat modyfikacji i robisz krok w kierunku działającego kodu. Jeżeli jednak hipoteza była błędna musisz wymyślić inną. Jak Sherlock Holmes zauważył, "Jeżeli wyeliminowałeś niemożliwe, cokolwiek pozostało, jakkolwiek nieprawdopodobne, musi być prawdą" (The Sign of Four, A. Conan Doyle).
Dla niektórych ludzi programowanie i debugowanie to ta sama rzecz. To jest, programowanie jest procesem stopniowego debugowania programu dopóki nie działa w taki sposób jaki chcesz. Pomysł polega na tym, że zawsze powinieneś zaczynać z działającym programem który robi coś i wykonywać drobne modyfikacje, debugując krok po kroku tak by cały czas mieć działającą aplikację.
Dla przykładu, Linuks jest jądrem systemu operacyjnego, ale zaczął się od bardzo prostego programu użytego przez Linusa Torvalds'a by odkryć chip Intel 80386. Zgodnie z Larrym Greenfield'em "Jednym z wcześniejszych projektów Linusa był program który przełączał się między wyświetlaniem 'AAAA' i 'BBBB'. To potem wyewoluowało do Linuksa" (The Linux Users’ Guide Beta Version 1).
W dalszych rozdziałach opowiem więcej o debugowaniu i innych praktykach programistycznych
Języki formalne i naturalne
[edytuj]Pierwszy program
[edytuj]Tradycyjnie, pierwszym programem jaki piszą ludzie w nowym języku jest "Hello, World.". Ponieważ wszystko co ma zrobić taki program, to wyświetlić napis "Hello, World.". W języku Java program ten wygląda tak:
class Hello {
// main: generate some simple output
public static void main (String[] args) {
System.out.println ("Witaj świecie.");
}
}
[...]
Wszystkie programy tworzone są za pomocą klas, które mają postać:
class NAZWAKLASY {
public static void main (String[] argumenty) {
KOD
}
}
Pojęcia
[edytuj]rozwiązywanie problemów :
Sposób formułowania problemu, znajdowania rozwiązania i wyrażanie jego.
język wysokiego poziomu :
Język programowania (taki jak Java, czy C++), zaprojektowany w sposób, aby był łatwy do czytania i pisania.
język niskiego poziomu:
Język programowania zaprojektowany tak, by był łatwy do uruchomienia dla komputera. Zwany także językiem „maszynowym” lub „asemblerowym”
Język formalny:
Język ludzi przeznaczony do określonych celów, takich jak reprezentowanie idei matematycznych i programów komputerowych. Wszystkie języki programowania to języki formalne.
Język naturalny:
Wszystkie ludzkie języki rozwinęły się w sposób naturalny.
Przenośność:
Program, może być uruchomiony na więcej niż jednym rodzaju systemu operacyjnego.
Interpretować:
Uruchomić program w języku wysokiego poziomu, tłumacząc po jednym wierszu na raz.
Skompilować:
Tłumaczyć program w języku wysokiego poziomu na język niskiego poziomu, w przygotowaniu do późniejszego wykonania.
Kod źródłowy:
Program w języku wysokiego poziomu, zanim zostanie skompilowany.
Kod obiektu:
Wyjście (wynik) kompilatora, po translacji programu.
Plik wykonywalny:
Inna nazwa kodu obiektowego, który jest gotowy do uruchomienia.
Kod bajtowy:
Specjalny rodzaj kodu wynikowego użyty do programów Java. Kod bajtowy jest podobny do języka niskiego poziomu, ale jest przenośny, tak jak w języku wysokiego poziomu.
Wyrażenie:
Część programu, która określa obliczenia.
Instrukcja print:
Wyrażenie to powoduje wynik, który będzie wyświetlany na ekranie.
Komentarz:
Część programu, który zawiera informacje o programie, niemający znaczenia, gdy program działa.
Metoda:
Nazwany zbiór wyrażeń.
Biblioteka:
Zbiór definicji klas i metod.
Bug:
Błąd w programie.
Składnia:
Struktura programu.
Semantyka:
Znaczenie programu.
Parsowanie:
Zbadanie programu i analizowanie jego struktury składniowej.
Błąd składni:
Błąd w programie, który sprawia, że niemożliwe jest parsowanie (i dlatego niemożliwa jest kompilacja).
Wyjątek:
Błąd w programie, który pojawia się podczas działania programu. Zwany także jako błąd w czasie wykonywania (run-time error).
Błąd logiczny:
Błąd w programie, który powoduje działanie inne niż to, które programista miał na myśli.
Debugowanie:
Proces wyszukiwania i usuwania któregokolwiek z tych trzech rodzajów błędów.
Ćwiczenia
[edytuj]Ćwiczenie 1
Informatycy mają denerwujący zwyczaj używania typowych angielskich słów do wyrażania czegoś innego niż to co faktycznie znaczą te słowa. Na przykład, w języku angielskim, wyrażenia i komentarze to to samo, ale w programowaniu to zupełnie co innego.
Słowniczek na końcu każdego rozdziału pomaga zwrócić uwagę na słowa i zwroty, które mają specjalne znaczenie w informatyce. Kiedy widzisz znajome słowa, nie zakładaj, że wiesz co to znaczy!
1. Jaka jest różnica między wyrażeniem a komentarzem?
2. Co to znaczy , że program jest przenośny?
3. Co to jest plik wykonywalny?
Ćwiczenie 2
Zanim zrobisz cokolwiek innego, musisz dowiedzieć się jak skompilować i uruchomić program w środowisku Java. Niektóre środowiska dostarczają gotowe programy przykładowe podobnych do tych z sekcji 1.5.
1. Wypisz na wyjście " Hello, World ", a następnie skompiluj i uruchom.
2. Dodaj wyrażenie print, drukujące inną wiadomość niż typowe Hello, World !. Na przykład: Jak się masz?. Skompiluj i ponownie uruchom program.
3. Dodaj komentarz do programu (w dowolnym miejscu), skompiluj i uruchom program ponownie. Nowy komentarz nie powinien mieć wpływu na wynik.
Kolejne ćwiczenie może wydawać się trywialne , ale jest to punkt wyjścia dla wielu programów które będziemy tu omawiać. By debugować, trzeba mieć zaufanie do środowiska programistycznego. W niektórych środowiskach łatwo się pogubić, który program jest aktualnie wykonywany i może okazać się, że próbujesz debugować program podczas omyłkowego uruchomienia kolejnego. Dodawanie (i zmiana) wyrażeń print jest prosty sposobem, aby upewnić się, że program, który widzisz to program, którego używasz.
Ćwiczenie 3
Dobrym pomysłem jest popełnienie tak dużej ilości błędów, jak to tylko możliwe. Zauważysz pewnie wiadomości o błędach, które produkuje kompilator. Czasem kompilator mówi dokładnie to, co jest złe, a wszystko co musisz zrobić to naprawić to. Czasem jednak komunikaty o błędach są mylące. Będziesz rozwijał wyczucie, kiedy można zaufać kompilatorowi i kiedy trzeba będzie rozwiązać problem bez pomocy kompilatora.
1. Usuń jeden z otwierających "wąsatych" nawiasów.
2. Usuń jeden z zamykających "wąsatych" nawiasów.
3. Zamiast main, napisz mian.
4. Usuń słowo kluczowe static.
5. Usuń słowo kluczowe public.
6. Usuń słowo System.
7. Zamień println na Println.
8. Zamień println na print. Jest to zawiłe, ponieważ jest to błąd logiczny, a nie składniowy . Wyrażenie System.out.println jest dopuszczalne, lecz zapewne nie zrobi tego, czego się spodziewamy.
9. Usuń jeden z nawiasów. Dodaj jeden nawias dodatkowy.
Zmienne i typy
[edytuj]Więcej napisów
[edytuj]Jak wspomniałem w poprzednim rozdziale, w main możesz umieścić tyle instrukcji ile chcesz. Na przykład aby wypisać więcej niż jedną linię:
class Hello {
// main: generuje proste napisy
public static void main (String[] arg) {
System.out.println ("Witaj świecie."); // wyświetla pierwszą linię
System.out.println ("Jak się czujesz?"); // wyświetla drugą linię
}
}
Jak widać, można umieszczać komentarze na końcu linii jak i samodzielnie.
Frazy, które pojawiły się w cudzysłowach są nazywane stringami ponieważ są sekwencją (ang. łańcuchem) liter. W zasadzie stringi mogą zawierać dowolną kombinację liter, liczb, znaków interpunkcyjnych i innych znaków specjalnych.
println jest skrótem od "print line" (wypisz linię), ponieważ po każdej linii dodaje specjalny znak zwany newline, który powoduje, że kursor przesuwa się do następnej linii. Następnym razem, kiedy zostanie wywołany println nowy tekst pojawi się w następnej linii.
Często wygodniej jest, aby wynik wielu instrukcji 'wypisz' znalazł się w jednej linii. Można to osiągnąć używając komendy print.
class Hello {
// main: generuje proste napisy
public static void main (String[] args) {
System.out.print ("Żegnaj, ");
System.out.println ("okrutny świecie!");
}
}
W tym wypadku wynik pojawi się w pojedynczej linii: Żegnaj, okrutny świecie!. Zauważ, że pomiędzy słowem "Żegnaj" i drugim cudzysłowem znalazła się spacja. Ta spacja pojawi się na wyjściu (w wyniku), a więc wpływa na zachowanie programu.
Spacje, które znalazły się poza cudzysłowami nie wpływają na zachowanie programu. Na przykład, mogłem napisać:
class Hello {
// main: generuje proste napisy
public static void main (String[] args) {
System.out.print ("Żegnaj, ");
System.out.println ("okrutny świecie!");
}
}
class Hello {
public static void main (String[] args){
System.out.print ("Żegnaj, ");
System.out.println("okrutny świecie!");
}
}
To również by zadziałało, jednak pewnie zauważyłeś, że program staje się trudniejszy do przeczytania. Nowe linie i spacje są przydatne do organizacji twojego programu wizualnie, sprawiając, że będzie łatwiejszy do przeczytania i łatwiej będzie w nim można znaleźć błędy składniowe.
Zmienne
[edytuj]Jedna z najbardziej potężnych możliwości języka programowania to możliwość manipulowania zmiennymi. Zmienna jest nazwanym miejscem które przechowuje wartość. Wartości są rzeczami, które mogą być wypisane lub przechowane i (jak zobaczymy później) możemy na nich operować. Napisy, które wypisywaliśmy ("Witaj, świecie.", "Żegnaj, ", etc.) są wartościami.
Aby przechować wartość musisz stworzyć zmienną. Jako, że wartości, które chcemy przechowywać to napisy, zadeklarujemy, że nowa zmienna będzie napisem:
String fred;
Ta instrukcja jest deklaracją, ponieważ deklaruje, że zmienna o nazwie fred jest typu String. Każda zmienna ma typ, który określa jakiego rodzaju wartości może przechowywać. Na przykład, int może przechowywać liczby całkowite i pewnie nie jest zaskoczeniem, że String może przechowywać napisy.
Pewnie zauważyłeś, że niektóre typy zaczynają się wielką literą a niektóre małą. Później nauczymy się znaczenia tej różnicy ale narazie warto to dobrze zapamiętać. Nie istnieją takie typy jak Int czy string i kompilator zaprotestuje jeśli spróbujesz ich użyć.
Aby stworzyć zmienną będącą liczbą całkowitą należy napisać int bob; gdzie bob to nadana przez ciebie nazwa tej zmiennej. Generalnie będziesz chciał nadawać zmiennym takie nazwy, które odzwierciedlają to co chcesz z nimi robić. Na przykład, jeśli zobaczysz takie deklaracje zmiennych:
String imie;
String nazwisko;
int godzina, minuta;
pewnie będziesz mógł zgadnąć jakie wartości są w nich przechowywane. To pokazuje także składnię deklaracji wielu zmiennych o takim samym typie: godzina i minuta są liczbami całkowitymi (typ int).
Przypisywanie wartości zmiennym
[edytuj]przypisywać można na 2 sposoby:
1. Przy tworzeniu zmiennej
int mysz = 25;
2. Po utworzeniu zmiennej
int mysz; mysz = 25;
Wyświetlanie wartości zmiennych
[edytuj]Możesz wyświetlić wartość zmiennej posługując się poleceniami użytymi już przez nas do wyświetlania stringów.
class Hello{
public static void main(String[] args){
String piewszaLinia;
pierwszaLinia=”Witaj ponownie!”;
System.out.println(pierwszaLinia);
}
}
Powyższy program tworzy zmienną o nazwie pierwszaLinia, przypisuje jej wartość „Witaj ponownie!” a następnie wyświetla tą wartość. Gdy mówimy o „wyświetlaniu zmiennej”, mamy na myśli wyświetlanie wartości zmiennej. Aby wyświetlić nazwę zmiennej, musisz umieścić ją w cudzysłowie. Dla przykładu: System.out.println(„pierwszaLinia”);
Gdybyśmy zechcieli dalej pójść tym torem, moglibyśmy napisać
pierwszaLinia = "Witaj ponownie!";
System.out.print ("Wartością pierwszaLinia jest ");
System.out.println (pierwszaLinia);
Wynikiem tego programu jest:
Wartością pierwszaLinia jest Witaj ponownie!
Należy nadmienić, że składnia polecenia wyświetlającego wartość zmiennej jest taka sama bez względu na jej typ.
int godzina, minuta;
godzina = 11;
minuta = 59;
System.out.print ("Obecny czas to ");
System.out.print (godzina);
System.out.print (":");
System.out.print (minuta);
System.out.println (".");
Wynikiem tego programu jest:
Obecny czas to 11:59.
UWAGA: To ćwiczenie pokazuje jak wyświetlić wiele wartości w tej samej linii za pomocą kilku poleceń, poprzedzających println. Powinieneś pamiętać o dodaniu println na końcu. W wielu środowiskach wynik wyświetlania jest przechowywany bez wyświetlania dopóki nie zostanie wywołane polecenie println kiedy to wyświetlana jest od razu cała linia. Gdybyś pominąć println, program może się zakończyć bez wyświetlenia wyniku!
Słowa kluczowe
[edytuj]Kilka sekcji temu, powiedziałem, że możesz nazwać swą zmienną jak chcesz, lecz nie jest to do końca prawdą. Istnieją pewne słowa zarezerwowane przez Javę, ponieważ są one używane przez kompilator podczas analizy struktury twojego programu i jeśli ich użyjesz, jako nazw zmiennych spowoduje to zamieszanie. Słowa te, zwane słowami kluczowymi to: include, public, class, void, int i wiele innych. Pełna lista dostępna jest pod [1]. Strona ta, prowadzona przez firmę Sun, zawiera dokumentację, do której będę odwoływał się w tej książce.
Zamiast zapamiętywania listy słów kluczowych, sugerowałbym ci skorzystanie jednego z wielu środowisk programistycznych dla języka Java, oferujących możliwość podświetlania kodu. Podczas pisania różne części kodu programu podświetlane są różnymi kolorami. Dla przykładu, słowa kluczowe mogą być wyświetlane na niebiesko, ciągi znakowe na czerwono a reszta kodu czarna. Uważaj, jeśli wpiszesz nazwę zmiennej a ona podświetli się na niebiesko, bo możesz spodziewać się dziwnego zachowania kompilatora.
Operatory
[edytuj]Operatory są to specjalne symbole, które używane są do wyrażenia prostych operacji liczbowych np. dodawanie i mnożenie. Większość operatorów w języku Java wykonuje dokładnie to, czego byś się po nich spodziewał, gdyż są to powszechnie znane symbole matematyczne. Przykładowo operatorem dodawania dwóch liczb całkowitych jest +. Wszystkie poniższe wyrażenia są dozwolone w języku Java. Ich znaczenie jest mniej lub bardziej oczywiste:
1+1
hour-1
hour*60 + minute
minute/60
Wyrażenia mogą zawierać zarówno nazwy zmiennych jak i liczby. W każdym przypadku nazwa zmiennej jest zastąpiona jej wartością liczbową zanim dane działanie jest wykonane. Dodawanie, odejmowanie oraz mnożenie działa dokładnie tak, jak byś tego oczekiwał, możesz jednak być zaskoczony dzieleniem. Przykładowo, poniższy program:
int hour, minute;
hour = 11;
minute = 59;
System.out.print ("Number of minutes since midnight: ");
System.out.println (hour*60 + minute);
System.out.print ("Fraction of the hour that has passed: ");
System.out.println (minute/60);
wygenerowałby następujący wynik:
Number of minutes since midnight: 719 Fraction of the hour that has passed: 0
Pierwsza linia jest dokładnie taka, jak jej oczekiwaliśmy, jednak druga linia już nie. Wartość zmiennej minute jest równa 59, natomiast 59 podzielone przez 60 równa się 0.98333, a nie 0. Przyczyną tej rozbieżności jest wykonanie dzielenia całkowitego przez maszynę wirtualną Java. Kiedy obydwa operandy są zmiennymi całkowitymi (operandy są tym, na czym operatory wykonują operacje matematyczne), wynik musi również być zmienną całkowitą, która zgodnie z konwencją dzielenia całkowitego, zawsze jest zaokrąglana w dół, także i w tym przypadku, gdy następna wartość całkowita jest tak blisko. Innym rozwiązaniem w tym przypadku jest obliczenie wartości procentowej zamiast dzielenia:
System.out.print ("Percentage of the hour that has passed: ");
System.out.println (minute*100/60);
Rezultatem którego jest:
Percentage of the hour that has passed: 98
Znowu wynik jest zaokrąglony w dół, ale przynajmniej teraz odpowiedź jest w przybliżeniu prawidłowa. Aby otrzymać jeszcze dokładniejszą odpowiedź, możemy użyć innego typu zmiennej, zwanego zmiennoprzecinkowym (ang. floating-point), który pozwala przechowywać wartości ułamkowe. Wrócimy do tego w następnym rozdziale.
Kolejność działań
[edytuj]Kiedy w wyrażeniu pojawia się więcej niż jeden operator, kolejność wykonywania działań zależy od reguł pierwszeństwa. Dokładne wytłumaczenie reguł pierwszeństwa może być skomplikowane, jednak aby od czegoś zacząć:
- mnożenie i dzielenie ma pierwszeństwo (jest wykonywane) przed dodawaniem i odejmowaniem. Zatem 2*3-1 równa się 5, a nie 4, oraz 2/3-1 równa się -1, a nie 1 (pamiętaj, że w dzieleniu całkowitym 2/3 jest równe 0).
- jeżeli operatory mają takie samo pierwszeństwo, wykonywane są kolejno od lewej do prawej. Zatem w wyrażeniu minute*100/60 mnożenie wykonywane jest najpierw dając 5900/60, co następnie daje 98. Jeżeli operacje matematyczne byłyby wykonane od prawej do lewej, dałoby to nieprawidłowy wynik 59*1 co jest równe 59.
- zawsze, kiedy chcesz obejść zasady pierwszeństwa (lub nie jesteś pewien, na czym one polegają) możesz użyć nawiasów. Wyrażenia w nawiasach obliczane są najpierw, zatem 2 * (3-1) da wynik 4. Możesz także użyć nawiasów, aby uczynić wyrażenie bardziej czytelnym, przykładowo (minute * 100) / 60, nawet jeśli ich zastosowanie nie ma żadnego wpływu na wynik końcowy.
Operatory dla zmiennych typu String
[edytuj]Na ogół nie można wykonywać operacji matematycznych na Stringach, nawet jeśli wyglądają jak liczby. Poniżej znajdują się nielegalne (jeśli wiemy, że zmienna Bob jest typu String)
bob - 1
"Cześć" / 123
Bob * "Dzień dobry"
Możesz się zastanowić, patrząc na te wyrażenia: Bob jest liczbą całkowitą, czy Stringiem? NIE! Jedynym sposobem, aby stwierdzić typ zmiennej jest odnalezienie miejsca, w którym jest ona zadeklarowana.
Co ciekawe, operator + działa ze Stringami, ale nie robi tego, czego można się spodziewać. Operator + działając na Stringach oznacza konkatenację, czyli łączenie jednego z argumentów do końca drugiego. Zatem po operacji "Witaj, " + "świecie" otrzymujemy ciąg "Witaj, świecie".
Kompozycja
[edytuj]Do tej pory patrzyłem na zmienne, wyrażenia i deklaracje w izolacji , nie łącząc ich.
Jedną z najbardziej przydatnych funkcji języków programowania jest ich zdolność do zorganizowania na małe bloki. Przykładowo, wiemy, jak pomnożyć liczbę i wiemy jak ją wypisać na ekran; okazuje się, możemy połączyć je w jednym stwierdzeniu:
System.out.println ( 17 * 3 ) ;
Każde wyrażenie z udziałem liczby, ciągu znaków czy zmiennych, mogą być stosowane wewnątrz instrukcji print. Widzieliśmy już wcześniej przykład:
System.out.println ( godziny * 60 + minut );
Ale można też umieścić dowolne wyrażenie po prawej stronie instrukcji przypisania:
int procent ;
odsetek = ( minuty * 100 ) / 60 ;
Może teraz jest to mało imponujące, ale zobaczymy przykłady, w których kompozycja wyraża złożone obliczenia, starannie i zwięźle.
UWAGA: po lewej stronie przypisania musi być nazwa zmiennej, nie wyrażenie, ponieważ lewa strona wskazuje miejsce przechowywania, gdzie wynik zostanie zapisany. Wyrażenia stanowią tylko wartości, nie stanowią miejsca przechowywania. Zatem instrukcja taka jak ta poniżej jest niedopuszczalna!
minuta + 1 = godzina ;
Pojęcia
[edytuj]Ćwiczenia
[edytuj]Metody
[edytuj]Zmiennoprzecinkowe
[edytuj]Konwersja z double do int
[edytuj]Metody matematyczne
[edytuj]Wykorzystanie
[edytuj]Tworzenie nowych metod
[edytuj]Klasy i metody
[edytuj]Programy z wieloma metodami
[edytuj]Parametry i argumenty
[edytuj]Diagramy blokowe
[edytuj]Metody z wieloma parametrami
[edytuj]Rezultaty działania metod
[edytuj]Pojęcia
[edytuj]Ćwiczenia
[edytuj]Operacje warunkowe i rekurencja
[edytuj]Operator modulo
[edytuj]Wykonanie warunkowe
[edytuj]Wykonanie wykluczające
[edytuj]Łańcuch operacji warunkowych
[edytuj]Operacje warunkowe zagnieżdżone
[edytuj]Polecenie return
[edytuj]Konwersja pomiędzy typami
[edytuj]Rekurencja
[edytuj]Diagramy blokowe dla metod rekurencyjnych
[edytuj]Konwencja i prawo boskie
[edytuj]Pojęcia
[edytuj]Ćwiczenia
[edytuj]Bogactwo metod
[edytuj]Zwracanie wartości
[edytuj]Rozwój programu
[edytuj]Wykorzystanie
[edytuj]Przeciążenie
[edytuj]Wyrażenia logiczne
[edytuj]Operatory logczne
[edytuj]Metody logiczne
[edytuj]Więcej rekurencji
[edytuj]Wiara w indukcję
[edytuj]Jeszcze jeden przykład
[edytuj]Pojęcia
[edytuj]Ćwiczenia
[edytuj]Iteracja
[edytuj]Wielokrotne przypisywanie wartości
[edytuj]Iteracja
[edytuj]Wyrażenie while
[edytuj]Tablice
[edytuj]Tablice dwuwymiarowe
[edytuj]Enkapsulacja i uogólnianie
[edytuj]Metody
[edytuj]Więcej enkapsulacji
[edytuj]Zmienne lokalne
[edytuj]Więcej uogólniania
[edytuj]Pojęcia
[edytuj]Ćwiczenia
[edytuj]Ciągi znaków itp.
[edytuj]Wywoływanie metod na obiektach
[edytuj]Długość
[edytuj]Przeliterowanie
[edytuj]Błędy wykonania
[edytuj]Czytanie dokumentacji
[edytuj]Metoda indexOf
[edytuj]Zapętlanie i zliczanie
[edytuj]Operatory zwiększania i zmniejszania wartości
[edytuj]Niezmienność danych typu String
[edytuj]Nieporównywalność danych typu String
[edytuj]Pojęcia
[edytuj]Ćwiczenia
[edytuj]Interesujące obiekty
[edytuj]Łańcuchy znaków są obiektami, ale nie są typowymi obiektami, ponieważ
Są one niezmienne.
Nie mają atrybutów.
Nie musisz korzystać z operatora new aby je utworzyć.
W tym rozdziale , użyjemy dwóch typów obiektów z bibliotek Java, Punkt oraz Prostokąt. Na początek jednak chcę zaznaczyć, że te punkty i prostokąty nie będą obiektami graficznymi, które pojawiają się na ekranie. Są to wartości, które zawierają dane, podobnie jak typy int czy double. Podobnie jak inne wartości są wykorzystywane wewnętrznie w celu przeprowadzenia obliczeń.
Paczki
[edytuj]Biblioteki Java są podzielone na pakiety, w tym java.lang, która zawiera większość klas, które używaliśmy do tej pory oraz java.awt, "Abstrakcyjne Okno Narzędziowe"(AWT - Abstract Window Toolkit), który zawiera klasy okien, przyciski, grafiki, itp.
Aby użyć klasy zdefiniowanej w innym pakiecie, trzeba go importować. Klasy Punkt i Prostokąt są w pakiecie java.awt, więc importujemy je w taki sposób:
import java.awt.Point ;
import java.awt.Rectangle ;
Wszystkie deklaracje importu pojawiają się na początku programu, poza definicją klasy.
Klasy zawarte w java.lang, takie jak Math i String, są importowane automatycznie, dlatego nie jest potrzebna kolejna instrukcja import.
Co jest interesujące?
[edytuj]Obiekty typu Point
[edytuj]Punkt to dwie liczby(współrzędne), które traktujemy łącznie jako jeden obiekt. W notacji matematycznej, punkty są często pisane w nawiasach, z przecinkiem oddzielającym współrzędne. Na przykład (0, 0) oznacza początek; (x, y) oznacza jednostki punktu X na prawo i y jednostek w górę od początku układu współrzędnych.
W Javie, punkt jest reprezentowany przez obiekt Punkt. Aby utworzyć nowy punkt, trzeba użyć operatora new:
Punkt pusty;
pusty = new Punkt(3, 4);
Pierwsza linia jest konwencjonalną deklaracją zmiennej: blank jest typem Punkt. Druga linia wywołuje za pomocą new, określa rodzaj nowego obiektu i zapewnia argumenty. Argumentami są współrzędne nowego punktu: (3, 4).
Rezultatem new jest referencja do nowego punktu, więc pusty zawiera odwołanie do nowo utworzonego obiektu. Jest to standardowy sposób przypisania, zobrazowany na diagramie pod tym linkiem.
Zmienne instancji
[edytuj]Fragmenty danych, które składają się na obiekt, nazywane są zmiennymi instancji, ponieważ każdy obiekt, który jest instancją tego typu , posiada własną kopię zmiennych instancji.
To coś jak schowek samochodu. Każdy samochód jest instancją typu "samochód ", a każdy samochód ma swój własny schowek. Jeśli pytasz mnie, co jest w tym schowku samochodu, musisz mi powiedzieć, który samochód jest twój.
Podobnie jest gdy chcesz odczytać wartość ze zmiennej instancji, należy określić przedmiot, do którego chcesz się dostać. W Javie jest to zrobić za pomocą notacji z użyciem kropki.
int x = pusty.x;
Wyrażenie pusty.x oznacza "idź do obiektu do którego odnosi się pusty i uzyskaj wartość x." W tym przypadku możemy przypisać wartość do zmiennej lokalnej o nazwie x. Nie ma konfliktu między lokalnymi zmiennej o nazwie x i zmiennej instancji o nazwie x. Celem stosowania notacji z użyciem kropki jest jednoznaczna identyfikacja zmiennej, do której się odnosimy.
Możesz użyć notacji z użyciem kropki jako część jakiegokolwiek wyrażenia Java, więc następujące są w pełni poprawne:
System.out.println ( pusty.x + "," + pusty.y ) ;
int dystans = pusty.x * pusty.x + pusty.y * pusty.y ;
Pierwsza linia wypisuje 3, 4
Drugi wiersz oblicza wartość 25.
Obiekty jako parametry
[edytuj]Możesz przekazać obiekty jako parametry w zwykły sposób, na przykład:
public static void drukujPunkt ( punkt P )
{
- System.out.println ("( " + p.x + "," + p.y + " )");
- System.out.println ("( " + p.x + "," + p.y + " )");
}
Metoda ta przyjmuje jako argument punkt i wypisuje na ekran w standardowym formacie. Jeśli wywołamy drukujPunkt(pusty), wypisuje na ekran
( 3 , 4 ). Właściwie , Java ma już sposoby na drukowanie punktów. Jeśli wywołamy System.out.println(pusty), dostajemy
java.awt.Punkt [ x = 3 , y = 4 ]
Jest to standardowy format, który Java używa do drukowania(wyświetlania na ekran)obiektów. Wypisuje nazwę typu, a następnie nazwy i wartości zmiennych instancji.
Jako drugi przykład, możemy przepisać metodę dystans omawianą wcześniej, tak że ma dwa Punkty jako parametry zamiast czterech zmiennych typu double.
public static double dystans ( Punkt p1 , Punkt p2 ) {
- double dx = ( double ) ( p2.x - p1.x ) ;
- double dy = ( double ) ( p2.y - p1.y ) ;
- return Math.sqrt( dx * dx + dy * dy ) ;
- double dx = ( double ) ( p2.x - p1.x ) ;
}
Rzutowanie nie jest konieczne; dodałem to jako przypomnienie, że zmienne instancji w punkcie są liczbami całkowitymi.
Prostokąty
[edytuj]Prostokąty są podobne do punktów, oprócz tego, że mają cztery zmienne w swej klasie: x, y, width, height (szerokość i wysokość). Poza tym, wszystko jest prawie takie same.
Ten przykład tworzy obiekt klasy Prostokat i sprawia, że box jest jego referencją
- Prostokat box = new Prostokat(0, 0, 100, 200);
- Prostokat box = new Prostokat(0, 0, 100, 200);
Ten rysunek pokazuje wpływ tego zadania.
Jeśli wpiszesz na ekran zmienną box otrzymasz
- java.awt.Prostokat [x = 0, y = 0, width = 100, height = 200]
- java.awt.Prostokat [x = 0, y = 0, width = 100, height = 200]
Jest to wynik metody Java, która wie, jak wydrukować obiekty Prostokat.
Obiekty jako typ zwracany
[edytuj]Możesz pisać metody, które zwracają obiekty. Na przykład, findCenter przyjmuje prostokąt jako argument i zwraca punkt zawierający współrzędne środka prostokąta:
public static Punkt findCenter (Prostokat box) {
- int x = box.x + box.width / 2;
- int y = box.y + box.height / 2;
- return new Point (x, y);
- int x = box.x + box.width / 2;
}
Zauważ, że używamy tu operatora new, aby utworzyć nowy obiekt, a następnie natychmiast użyć wynik jako wartość zwracaną.
Zmienność obiektów
[edytuj]Możesz zmienić zawartość obiektu poprzez przypisanie do jednej z jego zmiennych instancji. Na przykład, aby " przesunąć " prostokąt bez zmiany jego rozmiaru , można zmodyfikować wartości X i Y :
- box.x = box.x + 50 ;
- box.y = box.y + 100 ;
- box.x = box.x + 50 ;
Wynik jest pokazany na rysunku
Możemy upakować ten kod w metodzie i uogólnić to bardziej, aby przesunąć prostokąt o dowolną odległość:
public static void moveRect (Prostokat box, int dx , int dy ) {
- box.x = box.x + dx ;
- box.y = box.y + dy ;
- box.x = box.x + dx ;
}
Zmienne dx oraz dy wskazują zmienne i określają jak daleko chcemy przesunąć prostokąt w każdym kierunku. Wywołanie tej metody nie powoduje zmiany prostokąta, który jest przekazywany jako argument.
- Prostokat box = new Prostokat( 0 , 0 , 100 , 200 ) ;
- moveRect (box, 50, 100) ;
- System.out.println (box) ;
- Prostokat box = new Prostokat( 0 , 0 , 100 , 200 ) ;
wydruk spowoduje java.awt.Prostokat [ x = 50 , y = 100, width = 100, height = 200 ].
Modyfikowanie obiektów , przekazując je jako argumenty metod mogą być przydatne, ale może również sprawić, że debugowanie staje się trudniejsze, ponieważ nie zawsze wiadomo, która metoda wywołania zmodyfikuje(bądź nie) swoje argumenty. W dalszej części omówimy wady i zalety tego stylu programowania.
Java udostępnia metody , które działają na punkty i prostokąty . Możesz zapoznać się z dokumentacją na stronie
TUTAJ oraz TUTAJ
Na przykład metoda translate ma taki sam skutek, jak moveRect, ale zamiast przekazywać Prostokąt jako argument, należy użyć notacji z użyciem kropki:
- box.translate (50, 100);
Aliasy
[edytuj]Warto wiedzieć, że po przypisaniu obiektu do zmiennej, przypisujemy tak naprawdę referencję do obiektu. Możliwe jest, że wiele zmiennych odnosi się do tego samego obiektu. Na przykład w tym kodzie:
- Prostokat box1 = new Prostokat(0, 0, 100, 200);
- Prostokat box2 = box1;
- Prostokat box1 = new Prostokat(0, 0, 100, 200);
sytuacja wygląda w taki sposób
box1 i box2 odnoszą się do tego samego obiektu. Innymi słowy, obiekt ten ma dwie nazwy, box1 i box2. Kiedy osoba używa dwóch nazw, nazywamy to aliasem. To samo z obiektami.
Kiedy dwie zmienne są aliasami, wszelkie zmiany, które wpływają na jedną zmienną również wpływ inne. Na przykład:
- System.out.println (box2.width);
- box1.grow (50, 50);
- System.out.println (box2.width);
- System.out.println (box2.width);
Pierwsza linia wypisuje 100, która jest szerokością prostokąta, która jest referencją box2.Druga linia wywołuje metodę grow na obiekcie box1, który rozszerza prostokąt o 50 pikseli w każdym kierunku (więcej informacji w dokumentacji). Efekt ten jest przedstawiony na rysunku
Jakiekolwiek zmiany są wprowadzone w zmiennej box1 powodują zmiany w box2. Zatem wartość wypisana w trzeciej linii to 200, szerokość rozszerzonego prostokąta(Tak na marginesie, współrzędne prostokąta MOGĄ być ujemne.)
Jak widać, na powyższym przykładzie, kod, który zawiera aliasy może wydać ci się niejasny i może sprawić problemy w debugowaniu. Na ogół należy unikać aliasowania, albo stosować bardzo ostrożnie.
Odnośnik null
[edytuj]Po utworzeniu zmiennej obiektowej, pamiętaj, że tworzysz odwołanie do obiektu. Dopóki nie nadasz zmiennej wartości, zmienna ma wartość null. null jest specjalną wartością (i słowem kluczowym w języku Java), które oznacza "brak obiektu"
Deklaracja Punkt pusty; Jest to równoważne inicjalizacji:
- Punkt pusty = null;
- Punkt pusty = null;
Jeśli spróbujesz użyć obiektu null, albo przez dostęp do zmiennej instancji i wywołanie metody, Java rzuca wyjątek NullPointerException, po czym wypisuje komunikat o błędzie i kończy program.(Więcej o wyjątkach w kolejnych rozdziałach)
- Point pusty = null;
- int x = pusty.x; / / NullPointerException
- pusty.translate (50, 50); / / NullPointerException
- Point pusty = null;
Z drugiej strony, można przekazywać obiekt null jako argument lub otrzymać go jako wartość zwracaną. W rzeczywistości, jest oczywiste, po co to robić, na przykład: reprezentować zbiór pusty lub zidentyfikować błąd.
Odśmiecacz pamięci - Garbage collector
[edytuj]Omawiając aliasy rozmawialiśmy o tym, co dzieje się, gdy więcej niż jedna zmienna odnosi się do tego samego obiektu. Co się stanie, gdy nie ma zmiennej odnoszącej się do obiektu? Na przykład:
- Punkt pusty = new Punkt (3, 4);
- pusty = null;
- Punkt pusty = new Punkt (3, 4);
Pierwsza linia tworzy nowy obiekt Punkt i sprawia, puste na niego powoływać.Druga linia zmienia obiekt pusty, tak że zamiast odnoszenia się do obiektu, odnosi się do niczego (obiekt null).
Jeśli nic nie odnosi się do obiektu, niemożliwe jest odczytanie lub zapisanie jego wartości(w tym wypadku x,y). Nie można też wywołać na nim metody. W efekcie, obiekt przestaje istnieć. Możemy utrzymać obiekt w pamięci, ale jest to tylko strata przestrzeni pamięci, więc okresowo podczas działania programu, system szuka "osieroconych" obiektów i odzyskuje je w procesie zwanym 'garbage collection' - odśmiecanie pamięci. Przestrzeń pamięci zajmowana przez obiekt będzie dostępna do użycia dla nowo tworzonych obiektów.
Nie musisz nic robić, aby korzystać z odśmiecacza pamięci, nie musisz się o to martwić. Wystarczy wiedzieć, że mechanizm ten okresowo pracuje w tle.
Obiekty i typy bazowe
[edytuj]Istnieją dwa rodzaje typów w Javie, typy bazowe(podstawowe) i typy obiektowe. Typy bazowe, takie jak int czy bool zaczynają się małymi literami. Typy obiektów zaczynają się od wielkich liter. Rozróżnienie to jest użyteczne, ponieważ przypomina nam różnice między nimi :
Kiedy deklarujesz zmienną typu bazowego, masz miejsce w pamięci do przechowywania wartości typu bazowego. Kiedy deklarujesz zmienną obiektu, można uzyskać przestrzeń dla referencji do obiektu. Aby uzyskać przestrzeń dla samego obiektu, należy użyć słowa kluczowego new.
Jeśli nie zainicjalizujesz zmiennej typu podstawowego, to przyjmie ona wartość domyślną, która zależy od typu. Na przykład , 0 za typu całkowitoliczbowego(int) czy (false) dla typu logicznego(boolean). Domyślną wartością dla typów obiektowych jest null, co wskazuje na brak obiektu.
Zmienne podstawowe są izolowane w tym sensie, że zmienne w jednej metodzie, nie mogą mieć wpływu na zmienne w innej metodzie. Zmienne obiektu mogą być nieraz zagadkowe z tego powodu, że nie są dobrze izolowane. Jeśli przekażemy referencję do obiektu jako argument, metoda która zostanie wywołana na tym obiekcie może zmodyfikować obiekt, i w tym przypadku możesz zaobserwować efekt. Oczywiście, może być to zaleta, lecz trzeba zdawać sobie z tego sprawę.
Kolejna istotna różnica między typami podstawowymi a obiektami w następnym rozdziale!
Pojęcia
[edytuj]pakiet:
Biblioteka klas. Klasy w języku Java organizowane są w pakiety.
AWT:
Abstract Window Toolkit - jeden z największych i najczęściej używanych pakietów Java.
instancja:
Przykład: Mój kot(obiekt) jest instancją klasy "Urocze zwierzaki" Każdy obiekt jest instancją pewnej klasy.
instancja zmiennej:
Jeden z wymienionych elementów danych, które tworzą przedmiot. Każdy obiekt (instancja) ma własną kopię zmiennych instancji w swojej klasie.
referencyjny:
Wartość wskazująca obiekt. Diagram stanu, pojawia się odniesienie jak strzała.
aliasing:
Stan, gdy dwa lub więcej zmiennych odnoszą się do tego samego obiektu.
garbage collection -odśmiecacz pamięci:
Proces znajdywania obiektów, które nie mają referencji i zwalnianie ich miejsca w pamięci.
stan:
Pełny opis wszystkich zmiennych i obiektów oraz ich wartości, w danym punkcie w czasie realizacji programu.
Diagram stanów:
Snapshot stanu programu, przedstawiono graficznie.
(Przyp.tłum. - stany i diagramy stanów - chodzi o te obrazki które niby ukazują co się dzieje w programie. Moim zdaniem autor zamieścił je bo chciał sobie porysować i zwiększyć objętość książki)
Ćwiczenia
[edytuj]Dla ustalenia uwagi: Rectangle to klasa Prostokąt
ćwiczenie 1
1. W poniższym programie, narysować diagram pokazujący zmienne lokalne i parametry main i metody riddle, i pokazać wszystkie obiekty do których te zmienne się odnoszą.
2.Co jest wyjściem tego programu?
public static void main(String[] args)
{
- int x = 5;
- Point blank = new Point(1, 2);
- int x = 5;
- System.out.println(riddle(x, blank));
- System.out.println(x);
- System.out.println(blank.x);
- System.out.println(blank.y);
- System.out.println(riddle(x, blank));
}
public static int riddle(int x, Point p)
{
- x = x + 7;
- return x + p.x + p.y;
- x = x + 7;
}
Celem tego ćwiczenia jest zrozumienie mechanizmu przekazywania obiektu jako parametru metody.
ćwiczenie 2
W poniższym programie narysuj diagram ukazujący stan programu tuż przed zwróceniem distance. Powinieneś zawrzeć w diagramie wszystkie zmienne i parametry obiektów do których te zmienne się odnoszą.
Jakie jest wyjście tego programu?
public static double distance ( Punkt p1 , Point p2 ) {
int dx = p1.x - p2.x ;
int dy = p1.y - p2.y ;
return Math.sqrt ( dx * dx + dy * dy ) ;
}
public static Point findCenter (Rectangle box) {
- int x = box.x + box.width / 2;
- int y = box.y + box.height / 2;
- return new Point ( x, y) ;
- int x = box.x + box.width / 2;
}
public static void main ( String [ ] args ) {
- Point blank = new Point ( 5, 8 );
- Point blank = new Point ( 5, 8 );
- Rectangle rect = new Rectangle ( 0, 2, 4, 4);
- Point center = findCenter ( rect ) ;
- Rectangle rect = new Rectangle ( 0, 2, 4, 4);
- double dist = distance(center, blank);
- double dist = distance(center, blank);
- System.out.println ( dist ) ;
- System.out.println ( dist ) ;
}
ćwiczenie 3
Metoda grow jest częścią klasy Rectangle. Przeczytaj dokumentację na TEJ stronie
Jakie jest wyjście tego programu?
Narysować diagram stanu , który pokazuje stan programu tuż przed końcem main .Powinieneś zawrzeć w diagramie wszystkie zmienne i parametry obiektów do których te zmienne się odnoszą.
Na końcu main: czy p1 i p2 są aliasami? Dlaczego lub dlaczego nie?
public static void printPoint ( punkt P ) {
- System.out.println ("( " + p.x + "," + p.y + " )");
- System.out.println ("( " + p.x + "," + p.y + " )");
}
public static Point findCenter (Rectangle box ) {
- int x = box.x + box.width / 2 ;
- int y = box.y + box.height / 2 ;
- return new Point ( x, y) ;
- int x = box.x + box.width / 2 ;
}
public static void main ( String [ ] args ) {
- Rectangle box1 = new Rectangle ( 2 , 4 , 7 , 9 ) ;
- Point p1 = findCenter ( box1 ) ;
- printPoint ( P1) ;
- Rectangle box1 = new Rectangle ( 2 , 4 , 7 , 9 ) ;
- box1.grow ( 1 , 1) ;
- Point p2 = findCenter ( box1 ) ;
- printPoint (P2 ) ;
- box1.grow ( 1 , 1) ;
}
ćwiczenie 4
Możesz już rzygać metodą rekurencyjną silnia, ale mamy zamiar zrobić jeszcze jedną jej wersję.
Utwórz nowy program o nazwie Big.java i napisz iteracyjną wersję funkcji silnia.
Wypisz na ekran tabelę z liczbami całkowitymi od 0 do 30 wraz z ich silniami. W pewnym momencie około 15, prawdopodobnie zobaczysz , że odpowiedzi nie są poprawne. Dlaczego nie?
BigIntegers są obiektami Java , które mogą reprezentować dowolnie duże liczby całkowite. Nie istnieje górna granica. Wyjątkiem ograniczenia jest wielkość pamięci na twoim komputerze i prędkości przetwarzania. Przeczytaj dokumentację BigIntegers na tej stronie.
Aby korzystać z klasy BigIntegers , trzeba zaimportować java.math.BigInteger na początku programu.
Istnieje kilka sposobów tworzenia BigInteger , ale jeden ze sposobów, który osobiście polecam używa valueOf. Następujący kod konwertuje liczbę całkowitą do BigInteger :
- int x = 17 ;
- BigInteger big = BigInteger.valueOf ( x ) ;
- int x = 17 ;
Wpisz ten kod i wypróbuj go. Spróbuj wypisać BigInteger.
Ponieważ BigIntegers nie są typami bazowymi, zwykłe operatory matematyczne na nich nie działają. Zamiast tego musimy użyć metody, takie jak add. Aby dodać dwa obiekty BigIntegers, musisz wywołać add na jednym obiekcie z drugim obiektem jako jego argument:
- BigInteger small = BigInteger.valueOf ( 17);
- BigInteger big = BigInteger.valueOf (1700000000) ;
- BigInteger total = small.add (big) ;
- BigInteger small = BigInteger.valueOf ( 17);
Wypróbuj kilka innych metod, takich jak multiply czy pow.
Napisz metodę silnia która obsługuje obiekty typu BigInteger.
Spróbuj ponownie wypisać tabelę używając zmodyfikowanej metody silni. Czy udało się wypisać silnie do 30? Jak dużą silnię można uzyskać? Ja obliczyłem silnię wszystkich liczb od 0 do 999, ale mój komputer jest dość powolny, więc zajęło to trochę czasu. Ostatnia liczba, 999!, miała 2565 cyfr.
ćwiczenie 5
Wiele technik szyfrowania zależy od zdolności do podniesienia dużych liczb całkowitych do potęgi całkowitej. Oto metoda, która implementuje
(całkiem rozsądnie) szybką technikę całkowitego potęgowania:
public static int pow ( int x , int n ) {
- if ( n == 0 ) return 1 ;
- if ( n == 0 ) return 1 ;
- //znajdź x do n / 2 rekurencyjnie
- int t = pow ( x , n / 2 ) ;
- //znajdź x do n / 2 rekurencyjnie
- //Jeśli n jest parzyste ,wynikiem jest t do kwadratu
- //Jeśli n jest nieparzyste , to wynikiem jest t do kwadratu razy x
- //Jeśli n jest parzyste ,wynikiem jest t do kwadratu
- if ( n% 2 == 0 ) {
- return t * t ;
- } else {
- return t * t * x;
- }
- if ( n% 2 == 0 ) {
}
Problem z tej metody jest to, że to działa tylko dla liczb mniejszych niż 2 miliardy. Przerób kod tak, aby można było użyć obiektów BigInteger. Parametry powinny nadal być liczbami całkowitymi, choć możesz użyć metod BigInteger: add czymultiply, lecz nie używaj pow, metoda ta psuje całą zabawę.
ćwiczenie 6
- Jeśli jesteś zainteresowany grafiką, teraz jest dobry moment, aby przeczytać Dodatek A i zrobić zamieszczone tam ćwiczenia.
Stwórz swoje własne obiekty
[edytuj]Definicje klasy i typy obiektów
[edytuj]Powracamy do początkowych rozdziałów, gdzie określono klasę Hello, która jest również typem obiektu o nazwie Hello. Nie stworzyliśmy żadnych zmiennych o typie Hello, i nie używaliśmy słowa kluczowego new do utworzenia obiektów Hello. Ale przecież mogliśmy!
Ten przykład nie ma sensu, ponieważ nie ma żadnego powodu, aby utworzyć obiekt Hello. Niewiele by to dało, jeśli byśmy spróbowali. W tym rozdziale przyjrzymy się definicji klas, które tworzą użyteczne typy obiektów.
Oto najważniejsze pomysły w tym rozdziale :
Definiowanie nowej klasy tworzy także nowy typ obiektu o tej samej nazwie.
Definicja klasy jest jakby szablonem dla obiektów: określa, jakie zmienne instancji posiadają obiekty i jakie metody mogą być na nich wywoływane.
Każdy obiekt należy do pewnego typu obiektowego, to znaczy, że jest instancją pewnej klasy.
Po użyciu new, aby utworzyć obiekt, Java powołuje specjalną metodę zwaną konstruktorem, aby zainicjalizować zmienne instancji. Można tworzyć jeden lub więcej konstruktorów jako część definicji klasy.
Metody, które działają na typie zdefiniowanym są określone w definicji klasy dla danego typu.
Oto niektóre zagadnienia składniowe związane z definicją klas:
Nazwy klasy (a więc typ obiektów) powinna zaczynać się wielką literą, która pomaga odróżnić je od typów bazowych i nazw zmiennych.
Zazwyczaj umieszcza się jedną definicję klasy w każdym pliku; nazwa pliku musi być taka sama jak nazwa klasy, z przyrostkiem .Java. Na przykład klasa Time jest określona w pliku o nazwie Time.java.
W każdym programie, jedna klasa jest oznaczona jako klasa startowa. Klasa startowa musi zawierać metodę o nazwie main, czyli tam, gdzie zaczyna się realizacja programu. Inne klasy też mogą mieć metodę o nazwie main, lecz te metody nie będą wykonywane.
Po tym przydługawym wstępie, spójrzmy na przykład klasy zdefiniowanej przez użytkownika: Time.
Time
[edytuj]Wspólnym celem tworzenia typu obiektowego jest hermetyzacja danych związanych z obiektem, który może być traktowany jako pojedyncza jednostka. Widzieliśmy już dwa takie typy, Point i Rectangle.
Kolejnym przykładem, który będziemy omawiać, jest klasa Time, który reprezentuje czas. Dane zamknięte w obiekcie Time to godziny, minuty, i liczba sekund. Ponieważ każdy obiekt Time zawiera te dane, tworzymy zmienne instancji, aby je przechowywać.
Pierwszym krokiem jest podjęcie decyzji, jaki typ powinna mieć każda. Wydaje się jasne, że godzina i minuta powinny być liczbami całkowitymi. Sekundy będziemy reprezentować typem double, aby zachować dokładność.
Zmienne instancji są zadeklarowane na początku definicji klasy, poza jakąkolwiek definicją metody, w taki sposób:
class Time {
- int hour, minute;
- double second;
- int hour, minute;
}
Tak naprawdę, powyższy kod jest poprawną definicją klasy.
Po zadeklarowaniu zmiennych instancji, następnym krokiem jest zdefiniowanie konstruktora nowej klasy.
Konstruktory
[edytuj]Więcej konstruktorów
[edytuj]Tworzenie nowego obiektu
[edytuj]Wyświetlanie zawartości obiektu
[edytuj]Operacje na obiektach
[edytuj]Proste funkcje
[edytuj]Modyfikatory
[edytuj]Metody Fill-in
[edytuj]Co jest najlepsze?
[edytuj]Planowanie kontra rozwój ewolucyjny
[edytuj]Uogólnianie
[edytuj]Algorytmy
[edytuj]Pojęcia
[edytuj]Ćwiczenia
[edytuj]Tablice
[edytuj]Tablica jest zbiorem wartości, gdzie każda z nich jest identyfikowana za pomocą indeksu. Możesz stworzyć tablicę wartości całkowitych, zmiennoprzecinkowych, lub każdego innego typu, byle tylko wszystkie wartości w tablicy były tego samego typu.
Dostęp do elementów
[edytuj]Powielanie tablic
[edytuj]Pętle for
[edytuj]Tablice i obiekty
[edytuj]Długość tablicy
[edytuj]Liczby losowe
[edytuj]Tablica liczb losowych
[edytuj]Zliczanie
[edytuj]Histogram
[edytuj]Rozwiązania 1-przebiegowe
[edytuj]Pojęcia
[edytuj]Ćwiczenia
[edytuj]Tablice obiektów
[edytuj]Kompozycja
[edytuj]Obiekty typu Card
[edytuj]Metoda printCard
[edytuj]Metoda sameCard
[edytuj]Metoda compareCard
[edytuj]Tablice obiektów typu Card
[edytuj]Metoda printDeck
[edytuj]Przeszukiwanie
[edytuj]?? (Decks and subdecks)
[edytuj]Pojęcia
[edytuj]Ćwiczenia
[edytuj]Obiekty tablic
[edytuj]Klasa Deck
[edytuj]Tasowanie kart
[edytuj]Sortowanie
[edytuj]?? (Subdecks)
[edytuj]Tasowanie i rozdanie
[edytuj]Mergesort
[edytuj]Pojęcia
[edytuj]Ćwiczenia
[edytuj]Programowanie zorientowane obiektowo
[edytuj]Języki i style programowania
[edytuj]Metody obiektowe i klasowe
[edytuj]Bieżący obiekt
[edytuj]Liczby zespolone
[edytuj]Funkcje na liczbach zespolonych
[edytuj]Inna funkcja na liczbach zespolonych
[edytuj]Modyfikator
[edytuj]Metoda toString
[edytuj]Metoda equals
[edytuj]Wywoływanie jednej metody obiektu z innej
[edytuj]Dziwne zachowania i błędy
[edytuj]Dziedziczenie
[edytuj]Prostokąty
[edytuj]Hierarchia klas
[edytuj]Projektowanie zorientowane obiektowo
[edytuj]Pojęcia
[edytuj]Ćwiczenia
[edytuj]Listy łączone
[edytuj]Referencje w obiektach
[edytuj]Klasa Node
[edytuj]Listy jako kolekcje
[edytuj]Listy i rekurencja
[edytuj]Nieskończone listy
[edytuj]?? (The fundamental ambiguity theorem)
[edytuj]Metody obiektowe dla węzłów
[edytuj]Modyfikowanie list
[edytuj]Wrappery i funkcje wrapowane
[edytuj]Klasa IntList
[edytuj]Niezmienniki
[edytuj]Pojęcia
[edytuj]Ćwiczenia
[edytuj]Stosy
[edytuj]Kolejki priorytetowe
[edytuj]Drzewa
[edytuj]Sterta
[edytuj]Mapy
[edytuj]Kodowanie Huffmana w przeciwieństwie do algorytmu Imploding jest algorytmem
kompresji statycznej. Oznacza to, że program kompresujący musi przynajmniej raz przelecieć cały kompresowany plik w celu stworzenia histogramu.
Kodowanie Huffmana jest sposobem dopasowania zapisu znaków do rodzaju kompresowanych danych. Wykorzystywane jest m. in. w kompresji JPEG jako końcowy etap przetwarzania.
Teraz już o samym algorytmie:
Algorytm wykorzystuje drzewa binarne i kodowanie znaków zmienną długością kodu.
1. Utwórz histogram (częstotliwość występowania znaków w pliku).
2. Utwórz wierzchołki drzewa o odpowiednich wagach (waga wierzchołka jest równa liczbie znaków).
3. Połącz dwa najlżejsze wierzchołki w jeden dodając do kodu pierwszego cyfrę 0 a drugiemu 1.
4. Powtarzać etap 3, aż osiągniemy koniec drzewa.