PODSTAWY PROGRAMOWANIA OBIEKTOWEGO W JAVIE ZAGADNIENIA SZCZEGÓŁOWE Konwencje programistyczne Pakiety z małych liter Pakiety w konwencji pl.edu.pwr.programowanie.lab4 com.net.super.program.pakiet1.podpakiet2 Nazwy klas rozpoczynamy Wielką literą. class Klasa{... Nazwy metod rozpoczynamy z małej litery. void metoda(){... Nazwy zmiennych rozpoczynamy z małej litery. int zmienna; Jeśli nazwa składa się z kilku wyrazów, to kolejne człony rozpoczynamy także z Wielkiej litery (notacja wielbłądzia ). Stałe (public static final) piszemy z samych dużych liter (można używać podłogi) Klasy a typy podstawowe Obiekt Podstawowy Symbol Size/Format Byte byte 8-bit kod U2 Short short 16-bit kod U2 Integer int 32-bit kod U2 Long long l 64-bit kod U2 Float float f 32-bit IEEE 754 Double double d (opcja) 64-bit IEEE 754 Character '' Pojedynczy znak - 16- bit Unicode String -- "" Łańcuch tekstowy Boolean bool true lub false Klasy podstawowe (pisane z małych liter są jedynym wyjątkiem zasady, że wszystkie typy w jawie to obiekty). Każdy typ podstawowy (pisany z małej) ma jednak odpowiednik obiektowy (pisany z dużej) int i=new int(10); // błąd int i=10; //OK Integer i=new Integer(10); //OK Polimorfizm Jednym z najważniejszych mechanizmów programowania obiektowego jest polimorfizm dynamiczny, czyli możliwość różnego implementowania metod w klasie bazowej i w klasach pochodnych, przy zachowaniu tego samego celu (Klasyczny przykład z polami figur). W języku C++ podobny efekt otrzymuje się stosując metody wirtualne.
public class DziedziczenieA1 { protected void mymethod1(){ System.out.println("Wykonuj metodę 1 w klasie A1"); protected void mymethod2(){ System.out.println("Wykonuj metodę 2 w klasie A1"); protected void mymethod3(){ System.out.println("Wykonuj metodę 3 w klasie A1"); public class DziedziczenieA2 extends DziedziczenieA1 { protected void mymethod2(){ System.out.println("Wykonuj metodę 2 w klasie A2"); protected void mymethod3(){ super.mymethod3(); System.out.println("Wykonuj metodę 3 w klasie A2"); protected static void execute(){ DziedziczenieA2 da2=new DziedziczenieA2(); da2.mymethod1(); da2.mymethod2(); da2.mymethod3(); Programowanie obiektowe to sztuka oddzielenia tego co stałe od tego co zmienne. Podstawowym problemem przy tworzeniu hierarchii klas jest oddzielenie tych pól i metod, które na pewno będą używane (choć być może zostaną one później uszczegółowione) we wszystkich klasach i zdefiniowanie ich w klasie bazowej od metod i zmiennych związanych tylko ze specyfiką klas pochodnych (i problemów programistycznych z nimi związanymi) Klasa bazowa : Odtwarzacz multimedialny public class BaseMultimediaPlayer { Label title; Browser browser; String resourcetodisplay; void initgui(){ Display display = new Display (); final Shell shell = new Shell(display); GridLayout gl=new GridLayout(1, true); shell.setlayout(gl); title=new Label(shell, SWT.CENTER); title.settext( this.gettext()); GridData gdtitle = new GridData(SWT.FILL, SWT.FILL, true, false); gdtitle.heighthint=50; title.setlayoutdata( gdtitle); Browser browser=new Browser(shell, SWT.BORDER); GridData gdbrowser=new GridData(SWT.FILL,SWT.FILL,true,true); browser.setlayoutdata(gdbrowser); browser.seturl(this.geturl()+this.getresource()); shell.open (); shell.setsize(600,800);
while (!shell.isdisposed ()) { if (!display.readanddispatch ()) display.sleep (); display.dispose (); String gettext(){ return "Przeglądarka plików multimedialnych różnych typów"; String geturl(){ return "http://www.google.pl/"; String getresource() { return ""; Klasa pochodne: odtwarzacz filmów, zdjęć i radiowy public class MoviePlayer extends BaseMultimediaPlayer { String moviepath; public MoviePlayer(String moviepath) { super(); this.moviepath = moviepath; String gettext() { return super.gettext() + "- Przeglądanie filmów"; String geturl() { return "http://www.youtube.com/"; String getresource() { return moviepath; public class PhotoViewer extends BaseMultimediaPlayer { String photo; public PhotoViewer(String photo) { super(); this.photo = photo; String gettext() { return super.gettext()+" - Przeglądanie fotografii"; String geturl() { return "https://www.instagram.com/"; String getresource() { return photo;
public class RadioTuner extends BaseMultimediaPlayer { String radiostation; public RadioTuner(String radiostation) { super(); this.radiostation = radiostation; String gettext() { return super.gettext()+" - odtwarzacz stacji radiowych"; String geturl() { return "http://tunein.com/"; String getresource() { return radiostation; Polimorfizm możemy też wykorzystywać do wyłączania niepotrzebnych funkcjonalności public class PainInTheAxx { private Label label; private Text text; private Button button; String[] titles={"imię","nazwisko","firma","ulica","numer domu","numer lokalu","nip","pesel","data wystawienia","data płatności"; Shell shell; public void initgui() { for (String s:titles){ label=new Label(shell,SWT.NONE); labe label.settext(s); text=new Text(shell, SWT.BORDER); text.setlayoutdata(gd); text.addverifylistener(new VerifyListener() { ); public void verifytext(verifyevent arg0) { arg0.doit=verifyfield(arg0.getsource()); protected boolean verifyfield(object source) { MessageBox mb= new MessageBox(shell,SWT.OK+SWT.CANCEL); mb.setmessage("czy na pewno chcesz zmienić to pole"); return mb.open()==swt.ok; // Klasa otaczająca PainInTheAxx cw=new PainInTheAxx(){ protected boolean verifyfield(object source) {return true;
; Metody i klasy abstrakcyjne Niekiedy na etapie projektowania klasy bazowej jest jeszcze za wcześnie aby określać co dokładnie ma wykonywać dana metoda, mimo, ze wiemy że na pewno będzie potrzebna. Możemy zdefiniować taką metodę jako abstrakcyjną tworząc tylko jej prototyp poprzedzony słowem kluczowy abstract Klasa jest abstrakcyjna jeżeli zawiera przynajmniej jedną metodę abstrakcyjną lub jeśli którakolwiek z jej przodków zawiera metodę abstrakcyjną nie uszczegółowioną w tej klasie. Klasy abstrakcyjne nie mogą mieć bezpośrednich instancji (chyba, że z wykorzystaniem anonimowych klas wewnętrznych) Klasa bazowa formularz z możliwością (prowizorycznego) zapisywania danych: public abstract class AbstractPersistentComposite extends Composite { Shell shell; Display display; String databasename; public AbstractPersistentComposite(Shell parentshell, int swtarguments,string databasename) { super(parentshell, swtarguments); this.shell=parentshell; this.display=display.getdefault(); this.databasename=databasename; void initgui(){ GridLayout glshell=new GridLayout(1,true); shell.setlayout(glshell); GridLayout gdcomposite=new GridLayout(2,true); this.setlayout(gdcomposite); setfields(this); GridData gdbutton=new GridData(SWT.FILL,SWT.FILL,true,true); gdbutton.horizontalspan=2; Button b=new Button(shell, SWT.FLAT); b.settext("zapisz dane i zamknij"); b.setlayoutdata(gdbutton); b.addselectionlistener(new SelectionAdapter() { public void widgetselected(selectionevent arg0) { savefieldstofilestream(); shell.close(); ); loadfieldsfromfilestream(); void savefieldstofilestream() { BufferedWriter br=null; try { br=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(databaseName))); for(control c:this.getchildren()){ if(c instanceof Text){ br.write(((text)c).gettext()); br.newline(); if(c instanceof Combo){ br.write(((combo)c).gettext()); br.newline(); br.close(); catch (Exception e) {
e.printstacktrace(); void loadfieldsfromfilestream() { BufferedReader br=null; try { br=new BufferedReader(new InputStreamReader(new FileInputStream(databaseName))); for(control c:this.getchildren()){ if(c instanceof Text){ ((Text)c).setText(br.readLine()); if(c instanceof Combo){ ((Combo)c).setText(br.readLine()); br.close(); catch (Exception e) { e.printstacktrace(); abstract void setfields(abstractpersistentcomposite apc); void open(){ shell.open (); shell.setsize(400,600); while (!shell.isdisposed ()) { if (!display.readanddispatch ()) display.sleep (); display.dispose (); public class BasicDataPersistentComposite extends AbstractPersistentComposite { public BasicDataPersistentComposite(Shell parentshell, int swtarguments, String databasename) { super(parentshell, swtarguments, databasename); // TODO Auto-generated constructor stub Klasa pochodna: formularz danych osobowych z możliwością zapisywania danych void setfields(abstractpersistentcomposite apc) { GridData gd=new GridData(); gd.widthhint=100; gd.heighthint=40; gd.grabexcesshorizontalspace=true; gd.horizontalalignment=swt.fill; Label l=new Label(this, SWT.NONE); l.settext("imię"); Text t=new Text(this, SWT.BORDER); t.setlayoutdata(gd); l=new Label(this, SWT.NONE); l.settext("nazwisko"); t=new Text(this, SWT.BORDER); t.setlayoutdata(gd); l=new Label(this, SWT.NONE); l.settext("zawód"); t=new Text(this, SWT.BORDER); t.setlayoutdata(gd);
Wykorzystanie klasy abstrakcyjnej z anonimowymi klasami wewnętrznymi AbstractPersistentComposite apcregions=new AbstractPersistentComposite(new Shell(Display.getDefault()), SWT.NONE, "baza_regionow") { void setfields(abstractpersistentcomposite apc) { GridData gd=new GridData(); gd.widthhint=100; gd.heighthint=40; gd.grabexcesshorizontalspace=true; gd.horizontalalignment=swt.fill; Label l=new Label(this, SWT.NONE); l.settext("kontynent"); Combo t=new Combo(this, SWT.BORDER); t.setlayoutdata(gd); t.setitems(new String[]{"Afryka","Ameryka Płd","Ameryka Płn","Australia","Azja","Europa"); l=new Label(this, SWT.NONE); l.settext("kraj"); t=new Combo(this, SWT.BORDER); t.setitems(new String[]{"Egipt","Grecja","Hiszpania","Tunezja","USA","Wenezuela",); t.setlayoutdata(gd); l=new Label(this, SWT.NONE); l.settext("region"); t=new Combo(this, SWT.BORDER); t.setitems(new String[]{"Rodos","Kreta","Teneryfa","Grand Canaria","Synaj","Wielki Kanion",); t.setlayoutdata(gd); Interfejs ; apcregions.initgui(); apcregions.open(); Liniowa struktura klas nie zawsze w pełni odzwierciedlać rzeczywistość (Samochód Prius w hierarchii samochodów można umieścić jako potomka samochodu osobowego, ale równocześnie w innej hierarchii jest to samochód z napędem hybrydowym). W niektórych językach obiektowych możemy używać wielodziedziczenia, w Javie do odzwierciedlania tych dodatkowych hierarchii (funkcjonalności) mogą służyć interfejsu, które należy rozumieć jako rodzaj kontraktu: Klasa implementująca mechanizm na pewno będzie definiować metody w nim wyszczególnione, choć na etapie definiowania nie wiadomo jeszcze nic o konkretnej implementacji, metody te są więc zawsze abstrakcyjne (choć w definicji interfejsu nie musimy używać słowa kluczowego abstract) Kontrakt Klasa implementująca ten interfejs będzie umiała wysłać dane mailem. public interface Mailable { void mailme(); Klasa bazowa news nie potrafiąca wysyłać maili public class News { public String title; public String messagebody; public Date date; public News(String title, String message, Date date) {
super(); this.title = title; this.messagebody = message; this.date = date; public String gettitle() { return title; public void settitle(string title) { this.title = title; public String getmessagebody() { return messagebody; public void setmessagebody(string message) { this.messagebody = message; public Date getdate() { return date; public void setdate(date date) { this.date = date; public String tostring() { return "News [title=" + title + ", messagebody=" + messagebody + ", date=" + date + "]"; Klasa potomna news polityczny - mająca dodatkowo funkcjonalność wysyłania treści newsa mailem. public class PoliticalNews extends News implements Mailable { private Boolean aboutconstitutionalcourt; public PoliticalNews(String title, String message, Date date,boolean aboutconstitutionalcourt) { super(title, message, date); this.aboutconstitutionalcourt=aboutconstitutionalcourt; public void mailme() { final String username = "imie_nazwisko@gmail.com"; final String password = "XXXXXXXXXX"; Properties props = new Properties(); props.put("mail.smtp.auth", "true"); props.put("mail.smtp.starttls.enable", "true"); props.put("mail.smtp.host", "smtp.gmail.com"); props.put("mail.smtp.port", "587"); Session session = Session.getInstance(props, new javax.mail.authenticator() { protected PasswordAuthentication getpasswordauthentication() { return new PasswordAuthentication(username, password); ); try { Message message = new MimeMessage(session); message.setfrom(new InternetAddress("alphablondy2007@gmail.com")); message.setrecipients(message.recipienttype.to, InternetAddress.parse("andrzej.dudek@ue.wroc.pl ")); message.setsubject(this.title + (aboutconstitutionalcourt?" UWAGA DOTYCZY TRYBUNAŁU KONSTYTUCYJNEGO":""));
"+this.messagebody); message.settext(new SimpleDateFormat("YY-MM-dd").format(date)+" : Transport.send(message); System.out.println("Wysłałem maila"); catch (MessagingException e) { throw new RuntimeException(e); Kontener newsów różnych typów wie, że klasy implementujące interfejs mogą wysyłać maile public class NewsContainer { ArrayList<News> mynews=new ArrayList<News>(5); void addnews(news n){ mynews.add(n); void displayallnews(){ for(news n:mynews){ System.out.println(n.toString()); if(n instanceof Mailable){ ((Mailable)n).mailMe(); Klasy finalne / Słowo kluczowe final Klasy finalne, to klasy, które nie powinny być już bardziej uszczegóławiane i mieć klas pochodnych. Słowo kluczowe final może także dotyczyć pól, zmiennych, metod lub parametrów funkcji. W przypadku pól pole finalne nie musi być zdefiniowane od razu ale może być zmienione maksymalnie raz. W przypadku zmiennych final używa się bardzo często, aby mogły być wykorzystywane w anonimowych klasach wewnętrznych. Uwaga : widgety SWT nie są finalne a jedynie je udają Anonimowe klasy wewnętrzne PainInTheAxx cw=new PainInTheAxx(){ protected boolean verifyfield(object source) {return true; ; Anonimowe (Bo nie używające osobnych klas pochodnych) klasy wewnętrzne są wygodnym narzędziem, bo po pierwsze redukują liczbą plików (klas) w systemie, po drugie umożliwiają bezpośredni dostęp do pól klas, z której są wywoływane. Enkapsulacja
Nie wszystkie zmienne i metody obiektu są widoczne na zewnątrz. Część z nich jest dostępne tylko wewnątrz klasy, w której jest zdefiniowana lub tylko wewnątrz podklas. Ten proces nosi nazwę enkapsulacji i do jego implementacji używa się następujących modyfikatorów zmiennych: Modyfikator Klasa Klasa pochodna Pakiet private X protected X X X public X X X X package* X X Inne klasy * Ten typ dostępu jest domyślny, nie wymaga żadnego słowa kluczowego class Alpha { private int iamprivate; private void privatemethod() { System.out.println("privateMethod"); class Beta { void accessmethod() { Alpha a = new Alpha(); a.iamprivate = 10; a.privatemethod(); // błąd // błąd package Greek; public class Alpha { protected int iamprotected; protected void protectedmethod() { System.out.println("protectedMethod"); package Greek; class Gamma { void accessmethod() { Alpha a = new Alpha(); a.iamprotected = 10; a.protectedmethod(); // ok // ok package Latin; import Greek.*; class Delta extends Alpha { void accessmethod(alpha a, Delta d) { a.iamprotected = 10; // błąd d.iamprotected = 10; // ok a.protectedmethod(); // błąd d.protectedmethod(); // ok Pułapki enkapsulacji prywatne pola nadpisywane w klasach pochodnych, prywatne konstruktory.
Zmienne instancji i zmienne klas Domyślnie wszystkie zmienne są zmiennymi instancji, czyli dotyczą obiektów, w których są wywoływane. Może się jednak zdarzyć sytuacja, gdy chcemy, aby wartość zmiennej była dzielona pomiędzy wszystkie instancje danej klasy. Możemy wtedy zadeklarować zmienną klasy używając słowa kluczowego static. Każdorazowo wykorzystanie zmiennej lub metody statycznej musi być przemyślane i stosowane z umiarem. W klasach możemy stosować też bloki statyczne kodu static {..kod wykonywany jednorazowo przy tworzeniu klasy Żonglowanie Typami (NazwaTypuLubInterfejsu) obiekt obiekt instanceof KlasLubInterfejs Uwaga sprawdzania i rzutowanie ( castowanie ) klas nie może dotyczyć klas pochodnych dla danej klasy. Projektując klasę bazową zakładamy, że nie mamy żadnej wiedzy o tym jakie będą jej klasy pochodne. Często spotykane, zwłaszcza u początkujących programistów konstrukcje, które w klasie bazowej rozróżniają zachowania metod w zależności od tego z klasy pochodnej zostały wywołane są w SPRZECZNOŚCI z zasadami programowania obiektowego i mogą prowadzić do trudnych do zdiagnozowania błędów. Try catch wprowadzenie Konwencja try{ Kod, który może się wykonać z błędami (np. z powodu braku pliku) catch{exception e){ Obsługa błędu Oznacza, że spodziewamy się w bloku bazowym wystąpienia błędu ale ten błąd nie powinien kończyć pracy programu, bo wiemy co w sytuacji błędnej możemy zrobić (brak zasobów systemowych, brak pliku, przerwane połączenie z internetem itp.) i program powinien być wykonywany dalej. Konstrukcje, try-catch-finally, throws, throw będą omówione na osobnym wykładzie