TDT4100: Objektorientert Programmering
# Primitive datatyper
|| __Type__ || __Beskrivelse__ || __Størrelse__ ||
|| __int__ || Heltall || 4 byte ||
|| __byte__ || En enkelt byte. || 1 byte ||
|| __short__ || Heltall med mindre rekkevidde. || 2 byte ||
|| __long__ || Heltall med større rekkevidde. || 8 byte ||
|| __double__ || Dobbel-presisjons-flyttall. || 8 byte ||
|| __float__ || Enkel-presisjons-flyttall. || 4 byte ||
|| __char__ || Tegn || 2 byte ||
|| __boolean__ || True eller false. || 1 bit ||
# Wrapper-klasser
## String
`String`-klassen er en wrapperklasse for serier av `char`-primitiver til strenger som en kan manipulere. Noen av de mange metodene en kan bruke er:
- `charAt(int index)`
- `contains(String str)`
- `equalsIgnoreCase(String str)`
- `isEmpty()`
- `lastIndexOf(char ch)`
- `length()`
- `replace(char oldChar, char newChar)`
- `toLowerCase()`
- `toUpperCase()`
Når en skal sammenlikne to `String`-objekter så burde en alltid gjøre dette med `string1.equals(string2)` eller bruke `equalsIgnoreCase(String str)`-metoden fremfor å bruke to likhetestegn.
## Integer
`Integer`-klassen er en wrapperklasse for den primitive datatypen `int`. Vi gjør dette blant annet for å kunne ha `int`-primitiver inne i `ArrayList`-lister. Vi kan sammenlikne to `Integer`-objekter med `compare(int x, int y)`-metoden.
# Modifikatorer
## Synlighetsmodifikatorer
|| __Modifikator__ || __Klasse__ || __Pakke__ || __Subklasse__ || __Verden__ ||
|| __Public__ || Ja || Ja || Ja || Ja ||
|| __Protected__ || Ja || Ja || Ja || Nei ||
|| __Default/no modifier__|| Ja || Ja || Nei || Nei ||
|| __Private__ || Ja || Nei || Nei || Nei ||
## Abstract
En abstrakt klasse er en klasse som ikke kan instansieres, men som kan brukes av subklasser. Et eksempel på en abstrakt klasse er gitt under.
public abstract class klasseNavn {}
En abstrakt metode er en metode uten spesifisert implementasjon. Alle klasser som arver fra en abstrakt klasse med abstrakte metoder må implementere de abstrakte metodene for at subklassen skal kunne instansieres. For eksempel:
public abstract String makeString();
For at en klasse skal kunne inneholde abstrakte metoder så må klassen deklareres som en abstrakt klasse. Derimot må ikke en abstrakt klasse inneholde abstrakte metoder; det kan også brukes som et skjold mot instansiering. En abstrakt klasse skiller seg fra et grensesnitt på den måten at den kan implementere noen metoder om den ønsker.
## Final
Modifikatoren `final` uttrykker immuterbarhet; dersom en instansvariabel markeres som `final` vil den ikke kunne forandres etter at den har blitt initialisert. Eksempel på dette er gitt nedenfor.
public final int someNumber = 10;
`someNumber` kan ikke forandres senere i koden. Eventuelt kan en ha følgende tilfelle:
public final int someNumber;
Denne variabelen kan kun initialiseres i konstruktøren, og aldri igjen etter det. Immuterbare variabler brukes ofte sammen med `static`, et nøkkelord som forklares i avsnittet under.
## Static
Et felt merket med `static`-modifikatoren regnes som felles for alle objekter av samme klasse. Det vil si at alle instansene av en klasse med en `static` variabel deler den samme variabelen. For eksempel:
public static final double PI = 3.14;
Alle objektene av denne klassen vil dele variabelen PI, og den vil være uforandelig og satt til 3,14.
# Standardteknikken
## Observatør-observert
Dette er en viktig teknikk som handler om å kunne følge med på endringer som skjer i essensielle data som for eksempel kan forandre rekkefølgen på en sortering eller lignende. Man har en eller flere observatører som ønsker å følge med på den observerte. Den observerte må ha metoder som for å lage og sende endringshendelser, samt for å legge til og fjerne observatører til/fra en liste med alle som ønske rendringsmeldingene. Observatøren må implementere et grensesnitt av typen `EventListener`, for eksempel `PropertyChangeListener`, med metoden `propertyChange()`.
## Delegering
Har et interface man kan implementere i f.eks. en standardklasse for grensesnittet.
Deretter kan en annen klasse, som ikke implements interface, lage et felt som instansierer et standardklasse-objekt,
det objektet kan igjen brukes i metoder i den vanlige klassens metoder som ligner på metodene i interfacet / implementarsjonsklassen/standardklassen.
På den måten kan standardklasse-objektet enkelt byttes ut, som gjør delegeringsteknikken mer fleksibel enn arv.
# Casting
Når man itererer gjennom en mengde, kan det være hensiktsmessig å bruke casting. For eksempel kan det være en mengde objekter som arver fra samme objekt, men der ikke alle metodene er definert for alle typene klasser.
La oss si at vi har en klasse som heter togvogn, og to klasser passasjervogn og godsvogn som begge arver fra togvogn. Passasjervogn har en metode for å telle passasjerer tellePassasjerer(). Et tog kan beskrives som en liste av vogner. Hvis vi nå ønsker å summere opp alle passasjerene i toget, nytter det ikke å iterere over hele toget og kalle på tellePassasjerer-metoden, fordi toget består også av godsvogner, som vil gi oss en error i itereringen. Da kan vi bruke casting:
for (togvogn vogn : tog) {
if (vogn instanceof passasjervogn){
passasjervogn pvogn = (passasjervogn)vogn;
antallPassasjerer += pvogn.tellePassasjerer();
}
Husker fra `Iterable<T>`-grensesnittet at denne forløkken gjelder for alle vogner av typen togvogn i en collection tog. instanceof sjekker om vognene er av typen passasjervogn, som legger til rette for selve castingen, (passasjervogn)vogn. Her tar vi en passasjervogn-instanse av vogna, ettersom vi først har sjekket instanceof. Deretter kan vi hente tellepassasjerer-metoden.
# Collection-rammeverket
Collection er et stort rammeverk som består av lister, set, maps, iterator/iterable-grensesnitt, HashMap, ArrayList m.m. Felles for Collection er en rekke metoder; her er noen av de viktigste:
|| add(T elm) || Legger til element elm av typen T til collection ||
|| addAll(Collection<T> c) || Legger alle elementene i Collection c av type T til en annen collection ||
|| clear() || Fjerner alle elementer fra collection ||
|| contains(object obj) || Returnerer true hvis col. inneholder obj||
|| containsAll(Collection<T> c) || Returerer true hvis alle elementene i c ligger i collection ||
|| isEmpty() || Returnerer true hvis collection er tom ||
|| size() || Returnerer en int med antall elementer i collection ||
|| toArray() || Returnerer en Object[] array med alle elementene i lista ||
|| remove(Object obj) || Fjerner objektet fra collection ||
## HashMap<K,V>
HashMap i Java minner veldig om Dictionary fra Python. Man har en type keys K og en type verdi V som den husker på.
Når man skal bruke remove-metoden med en HashMap, er det først og fremst nøkkelverdien som brukes:
remove(Object key),
men kan også spesifisere objekt:
remove(Object key, Object obj).
## Interface Set<E>
Et Set i Java er veldig likt et set i Python, det kan ikke inneholde duplikater. For eksempel om man har en ArrayList med mange duplikter og ønsker å finne antall unike objekter i lista kan man legge alle elementene fra ArrayList’n til et Set.
List<String> liste = new ArryList<>();
liste.add(Obj...obj);
Set<String> set1 = new HashSet<>();
set1.addAll(liste);
Og eventuelt putte det tilbake til ArrayList om man absolutt må:
liste.clear();
liste.addAll(set1);
Den mest vanlige typen Set er HashSet, som er en usortert Set med verdier, i motsetning til f.eks. TreeSet som er sortert.
# Grensesnitt
Generelt kan en si at grensesnittet til en klasse er det som er synlig for andre brukere av klassen. Vi bruker dog begrepet grensesnitt om en type som bare består av `public` konstanter og tomme metoder. Et slikt grensesnitt kan implementeres av andre klasser.
## Interface Comparable<T>
Ved å implementere Comparable-grensesnittet, så kan objekter sorteres med bruk av Java sine innebygde sorteringsfunksjoner. Comparable har kun èn metode:
int compareTo(T objekt)
Sammenligner dette objektet med et annet objekt. Logikken fungerer slik: for å bli plassert før objektet du sammenligner deg med i sorteringsrekkefølgen, må metodekallet returnere en int < 0. Om det returnerer en int > 0, blir objektet du sammenligner med satt først i sorteringsrekkefølgen. Om compareTo returnerer 0, sorterer den inkonsekvent. Comparable sorterer alltid i stigende rekkefølge. Brukes av en klasse slik:
public class testKlasse implements Comparable<T>{
// som må inneholde metoden
public int compareTo(T objekt){......}}
## Interface Comparator<T>
Er ment å implementeres av en annen klasse enn den som skal sorteres. Den kan da også ha friere regler på utfallet av sammenligninger. For å bruke en Comparator sendes den enkelt med som et andre argument til kallet til Collections.sort(). Comparator-grensesnittet krever at du har implementert metoden compare(o1,o2). Denne har samme logikk for returverdien som compareTo(), men tar en inn to argumenter istedet for at et argument sammenlignes med "this". I motsetning til Comparable, kan man bestemme sorteringsrekkefølge, utenom dette er logikken den samme. [Eksempelkode](https://www.ntnu.no/wiki/display/tdt4100/Sortering+med+Comparable+og+Comparator)
Comparator går hånd i hånd med lambda-uttrykk, som man kan [lese mer om her](https://www.ntnu.no/wiki/display/tdt4100/Lambda-uttrykk+og+funksjonelle+grensesnitt+i+Java+8)
Det handler om at Comparator er et funksjonelt grensesnitt (altså kun èn metode), slik at man kan skrive om instansieringen av en Comparator i et collection.sort kall, f. eks:
persons.sort(new Comparator<Person>() {
@Override
public int compare(Person a, Person b) {
return a.getAge() - b.getAge();
});
blir til:
persons.sort((a, b) -> a.getAge() - b.getAge());
Men serr, les mer på [wiki-sida](https://www.ntnu.no/wiki/display/tdt4100/Lambda-uttrykk+og+funksjonelle+grensesnitt+i+Java+8).
## Interface Iterable<T>
Ved å implementere Iterable-interface i en klasse, kan man iterere mye enklere gjennom en Collection med instanser av denne klassen, slik at man kan bruke klassen på høyreside av kolon i en slik for-løkke:
for (T obj : c){
action();
}
Denne koden kan leses slik: “For alle objekter av typen T i collection c, utfør følgende handling. Husk å lage:
@Override
public Iterator<T> iterator() { return c.iterator();}
## Interface Iterator<E>
En instans av iterator-grensesnittet, altså en Iterator, kan brukes til å iterere gjennom elementer til et annet objekt. Det viktige å merke seg her er at en Iterator husker hvor langt man har kommet i itereringen, og at etter itereringen er gjennomført er iteratoren oppbrukt. Iterator-grensesnittet ligger i Collection-rammeverket og har følgende tre metoder:
boolean hasNext()
som returnerer true hvis det er flere elementer igjen å iterere gjennom.
<E> next()
returnerer neste elementer og tar et steg videre i iteratoren. Ikke bruk denne med mindre hasNext() returnerer true.
void remove()
som fjerner det siste elementet som ble returnert av next() fra collection-kilden om den støtter det.
# Exceptions
## Checked exception
Løsningen på checked exceptions er ofte å legge koden i en try/catch:
try{
// etellerannet
## Unchecked exception
# Input og output
Her benytter vi oss stort sett av subklasser av InputStreamReader og OutputStreamWriter. Her snakker jeg om Reader og Writer, som igjen arver til FileReader og FileWriter. Personlig foretrekker jeg PrintWriter, da den har en utvidet print-metode som dekker boolean, strings, chars, ints alt. Veldig kjekk metode er println (leses: print-line), som først printer og deretter går til ny linje.
## JFileChooser
Når man skal velge fil fra datamaskin som det skal skrives til, kan man bruke JFileChooser (ikke veldig eksamensrelevant).
JFileChooser chooser = new JFileChooser();
if(chooser.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
File selectedFile = chooser.getSelectedFile();
// deretter kan man
try {
PrintWriter outprint = new PrintWriter(selectedFile);
outprint.println(“Hello World”);
out.close();
catch (FileNotFoundException e) {
e.printStackTrace();
}
Samme gjelder når man skal lese fra fil, men da bruker man FileReader og .read() isteden for PrintWriter og println().
## Scanner
Brukes for å lese data fra fil, eks. `InputStream`. Hvis man har en `InputStream` input, lages scanneren basert på denne:
Scanner scanner = new Scanner(input);
Ved iterering brukes ofte `while(scanner.hasNextLine())`, forså å lage en ny streng av neste linje og manipulere denne: `String line = scanner.nextLine();` Må alltid huske å lukke scanneren etter bruk `scanner.close();`
# Diverse
## vararg
Når en metode skal ta inn et uspesifisert antall elementer, skriver man slik:
public Constructor(Obj...obj)
som lager en String array obj[]. Deretter kan man for eksempel lage en ArrayList slik:
this.someList = new ArrayList<Obj>(Arrays.asList(obj));
## Funksjonelle grensesnitt
Et funksjonelt grensesnitt har èn abstrakt metode (kalt den funksjonelle metoden), som legger til rette for lambda-uttrykk.
## Ternary operator
? : kan brukes som en forkortelse på if-else-then for enkle variabel-designeringer.
if (a>b) {
max = a;
} else {
max = b;
}
kan forkortes til den enkle koden:
max = (a > b) ? a : b;
Leses: Er a større enn b? true gir a. ellers: b.
## Instansvariabel/felt
Et __felt__ er en linje i klassen som ikke er en metode eller en konstruktør. eks:
private int age = 12;
I en løkke: bruk break for å stoppe itereringen, og bruk continue for å hoppe til neste ledd i iterasjonen.
# Diagrammer
## Objektdiagram
Objektdiagrammer viser tilstanden til objekt(struktur)er, med verdiene til attributter og referanser som knytter objekter sammen.
## Objekttilstandsdiagrammer
Objekttilstandsdiagrammer viser hvordan objekt(struktur)er endres over tid, når en kaller metoder.
## Klassediagrammer
Klassediagrammer er en illustrasjon av innholdet i og sammenhengen mellom klasser, som et supplement til tekslig kode. Et klassediagram viser klasser som bokser, attributter og operasjoner som tekstlinjer inni boksene (i hver sine deler) og assosiasjoner og arv som streker med. I tillegg annoteres assosiasjonsstreker med informasjon om navn og såkalt multiplisitet (også kalt kardinalitet).
## Sekvensdiagram
Et sekvensdiagram viser kall mellom objekter.
# Testing med JUnit
Dette handler om å sjekke at koden fungerer slik den skal; hyppig bruk av assertEquals. For eksempel, hvis vi har en Number-klasse med en metode getNumber() som alltid skal returnere 1. Da kan man ha en testklasse som ser slik ut:
public class NumberTest extends junit.framework.TestCase {
public void TestNumber(){
Number number = new Number();
assertEquals(1, number.getNumber());
}
}
Andre test-metoder inkluderer assertFalse, assertTrue m.m.
# Bruk av this()
Hvis man for eksempel har en Person-klasse med følgende felt og konstruktør:
private final String firstName, lastName;
private final int age, height;
public Person(String firstName, String lastName, int age, double height){
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.height = height; }
Så kan man også legge til en konstruktør som ikke tar inn alle parameterene og bruke this() til å designere standard-verdier og referere til en annen konstruktør i den samme klassen:
public Person(){
this(“Sondre”, “Stai”, 22, 194); }
Husk at konstruktører ikke kan arves, men man kan kalle på konstruktøren til en superklasse med `super(param)`;