252-0027 EinführungindieProgrammierungI 10.0Systema=schesProgrammieren ThomasR.Gross DepartmentInforma=k ETHZürich Copyright(c)Pearson2013andThomasGross2016 Allrightsreserved. Uebersicht ! 10.0Beispiel ! 10.1DarstellungvonVariablenundObjekten ! 10.1AsserIons ! 10.2Pre/PostCondiIons(fürStatement) ! 10.3Pre/PostCondiIons(fürFolgenvonStatements) 2 RaIonaleZahlen ! BeispieleinersinnvollenKlasse ! Idee:KlasseRational fürdieDarstellungvonraIonalen Zahlen ! ReelleZahldiealsVerhältniszweierganzerZahlendargestelltwird ! Warum:Diedouble DarstellungistnureineApproximaIon double x = (1.0 / 2.0) + (1.0/3.0) + (1.0/6.0); System.out.println(x); RaIonaleZahlen ! BeispieleinersinnvollenKlasse ! Idee:KlasseRational fürdieDarstellungvonraIonalen Zahlen ! ReelleZahldiealsVerhältniszweierganzerZahlendargestelltwird ! Warum:Diedouble DarstellungistnureineApproximaIon double x = (1.0 / 2.0) + (1.0/3.0) + (1.0/6.0); System.out.println(x); Output: 0.9999999999999999 RaIonaleZahlen ! ExakteDarstellung ! 0.1==1/10 ! KlasseRaIonalunterstütztdieüblichenarithmeIschen OperaIonen Addition:" a c + = b d ad+bc bd Multiplication:" a x c = b d Subtraction:" a c – = b d ad–bc bd Division:" ad a .. c = bc b d ac bd 5 ImplementaIonvonRaIonal ! VorschauaufdienächstenSeiten ! VerschiedeneKonstruktoren ! Overloading ! Default:0,einArgument:ganzeZahl;zweiArgumente:QuoIent ! DieKonstruktorenstellensicherdassZählerundNennerauf diekleinstmöglichenWerte“gekürzt”werden ! DasichdieInstanzenderKlassenieändernbleibtdieseEigenschae bestehen ImplementaIonvonRational ! DieOperaIonen(add,subtract,mulIply,unddivide)fürdie vierGrundoperaIonenverwendendiedot-NotaIon– ! EinerderOperandenistderimpliziteParameter,deranderederinder DefiniIonaufgeführteParameter ! AufdenimplizitenParameterwirddurchthis zugegriffen. KlasseRaIonal /** * The Rational class is used to represent rational numbers, which * are defined to be the quotient of two integers. */ public class Rational { /** Creates a new Rational initialized to zero. */ public Rational() { this(0); } /** * Creates a new Rational from the integer argument. * @param n The initial value */ public Rational(int n) { this(n, 1); } skipcode KlasseRational /** /** * a newclass Rational withto the value x / y. * Creates The Rational is used represent rational numbers, which * @param x The numerator of the rational number * are defined to be the quotient of two integers. * */@param y The denominator of the rational number */ public class Rational { public Rational(int x, int y) { int g = Math.abs(y)); /** Creates a gcd(Math.abs(x), new Rational initialized to zero. */ num = x / g; public Rational() { den = Math.abs(y) / g; this(0); if (y < 0) num = -num; } } /** /** * Creates a new Rational from the integer argument. * the number r to this one and returns the sum. * Adds @param n rational The initial value * @param r The rational number to be added */ * @return The sum of the number and r public Rational(int n) current { */ this(n, 1); public Rational add(Rational r) { } return new Rational(this.num * r.den + r.num * this.den, this.den * r.den); } KlasseRational /** * Subtracts Creates a the new rational Rational number with the r from valuethis x / one. y. * @param r x The rational numeratornumber of theto rational be subtracted number * @return @param yThe Theresult denominator of subtracting of the rational r from the number current number */ public Rational Rational(int subtract(Rational x, int y) { r) { return int g =new gcd(Math.abs(x), Rational(this.num Math.abs(y)); * r.den - r.num * this.den, num = x / g; this.den * r.den); } den = Math.abs(y) / g; if (y < 0) num = -num; /**} * Multiplies this number by the rational number r. /** * @param r The rational number used as a multiplier * @return Adds theThe rational resultnumber of multiplying r to thisthe onecurrent and returns number the bysum. r */ * @param r The rational number to be added * public @returnRational The sum multiply(Rational of the current number r) {and r */ return new Rational(this.num * r.num, this.den * r.den); } public Rational add(Rational r) { return new Rational(this.num * r.den + r.num * this.den, this.den * r.den); } skipcode KlasseRational /** * Divides Subtracts this thenumber rational by the number rational r fromnumber this one. r. * @param r The rational number used to beas subtracted a divisor * @return The result of dividing subtracting thercurrent from the number current by r number */ public Rational divide(Rational subtract(Rational r)r) { { return new Rational(this.num * r.den, r.den -this.den r.num * * this.den, r.num); } this.den * r.den); } /** /** * Creates a string representation of this rational number. * @return Multiplies Thethis string number representation by the rational of this number rational r. number */ * @param r The rational number used as a multiplier * public @returnString The result toString() of multiplying { the current number by r */ if (den == 1) { publicreturn Rational "" + multiply(Rational num; r) { } return else { new Rational(this.num * r.num, this.den * r.den); } return num + "/" + den; } skipcode KlasseRational /** * Calculates Divides this the number greatest by the common rational divisor number usingr.Euclid's algorithm. * @param x r First The rational integernumber used as a divisor * @param @returnyThe Second result integer of dividing the current number by r * */@return The greatest common divisor of x and y */public Rational divide(Rational r) { private return int new gcd(int Rational(this.num x, int y) { * r.den, this.den * r.num); } int r = x % y; while (r != 0) { /** x = y; * Creates y = a r; string representation of this rational number. * @return r = The x % string y; representation of this rational number */ } public return String y; toString() { } if (den == 1) { return "" + num; /* Private } else instance { variables */ private return int num; num + "/" /* The + den; numerator of this Rational */ private } int den; /* The denominator of this Rational */ } } skipcode SimulaIon ! WirwollensehenwieeineinfachesProgrammdreiraIonale Zahlenaddiert 1 2 + 1 3 + 1 6 ! DanachwerfenwireinenBlickhinterdieKulissen ! DasErgebnisdieserBerechnungsoll(te)exaktsein1sein ! ImGegensatzzurRechnungmitdouble public void run() { Rational Rationalaa==new newRational(1, Rational(1,2); 2); public Rational add(Rational Rational(int x, int y)r) { { 36 5 Rational Rationalbb==new newRational(1, Rational(1,3); 3); int g = gcd(Math.abs(x), Math.abs(y)); Rational cc==new Rational newRational(1, Rational(1, 6); public x, this.num int y)6); { * r.den + r.num * this.den , return Rational( num = Rational(int xnew / g; Rational sum ==a.add(b).add(c); Rational sum a.add(b).add(c); int g = gcd(Math.abs(x), Math.abs(y)); this.den * r.den ); den = Math.abs(y) / g; System.out.println(a " " + + " " + + b c + + " " + = " " + + c sum); + " = " + sum); println(a +/ "g;+ " + + b + num if (y =< x0) num = -num; }} den = Math.abs(y) 36 6 temporary / g; } a b c sum result= -num; if (y < 0) num } 1 1 1 5 this r 1 2 1 1 6 x 3 y 6 gnum num 5 1 this den 6 den1 6 2 3 2 3 x 1 y 6 g 3 num 1 5 den 1 36 5 36 6 36 1 6 TestRational 1/2 + 1/3 + 1/6 = 1 BasedonEricRoberts,The ArtandScienceofJava skipsimula9on Darstellungsfragen ! Warumergibteigentlich double x = (1.0 / 2.0) + (1.0/3.0) + (1.0/6.0); System.out.println(x); Output: 0.9999999999999999 ! WertemüssenimSpeicherdesComputersdargestelltwerden ! SpeicherorganisiertalseineGruppevonBits ! Bit:binarydigit 20 Darstellungsfragen ! EinBithatzweiZustände:normalerweisemit0und1 bezeichnet ! Gruppenvon8BitsbildeneinByte 0 0 1 0 1 0 1 0 ! NurzurVereinfachungderImplementaIondesRechners ! (Ganzeundreelle)ZahlenwerdenineinerFolgevonBytes gespeichert ! EineFolgevonBytesheissteinWord.ComputerkönnenoeeinWord schnellverarbeiten(schnellerals4beliebigeaufeinanderfolgendeBytes) 21 Darstellungsfragen ! EineganzeZahlwirdoeineinemWordausvierBytes gespeichert ! MancheComputerbezeichnenalseinWordalsFolgevon8 Bytes ! UndspeicherndanneineganzeZahlin64Bits 22 Binärdarstellung ! WennwirdieBitseinesBytesalsBinärzahlinterpreIerenso könnenwirineinemByteeineganzeZahldarstellen ! BinärzahlensindwieDezimalzahlenaberverwendeneine andereBasis ! DezimalzahlenBasis==10 ! DasGewichteinerZifferistdas10-fachedesGewichtsderZiffer zurRechten ! BinärzahlenBasis==2 ! DasGewichteinerZifferistdasDoppeltedesGewichtsderZiffer zurRechten Binärdarstellung 0 0 1 0 1 0 1 0 0×1 0 1×2 2 0×4 0 1×8 8 0×16 0 1×32 32 0×64 0 0×128 0 42 DarstellungeneinerZahl ! DieBinärdarstellung00101010istäquivalentzur Dezimaldarstellung42 ! WenndieBasiswichIgist,dannwirdsiealsIefgestellt ! 001010102=4210 ! AberesistimmerdieselbeZahl ! ZahlenhabenkeineBasis,nurDarstellungen 25 Oktal-undHexadezimalschreibweise ! 8Bitssindmühsamzuschreiben ! Leichtetwaszuübersehen(00101010oder00101011) ! InformaIkerverwendendaheroedieOktalschreibweise (oderdieHexadezimalschreibweise) ! Oktal:Ziffern0…7 ! Hexadezimal:Ziffern0….9ABCDEF 26 Oktal-undHexadezimalschreibweise ! DieZahl00101010inOktal-undHexadezimalschreibweise octal 5 hexadecimal 2 2 2 5 x x 1 8 2 40 42 A 10 x 1 02 x 16 10 32 42 ! DerVorteildieserDarstellungenistdasssieleichtindie Binärdarstellungumgewandeltwerdenkönnen ! JedeZifferkanneinzelnumgewandeltwerden 28 Uebung:Zahlendarstellungen ! WasistdieDezimaldarstellungfürdieseZahlen 100012 AD16 1778 17 1 0 0 0 1 1 0 0 0 1 x x x x x 1 2 4 8 16 1 0 0 0 1 17 Uebung:Zahlendarstellungen ! WasistdieDezimaldarstellungfürdieseZahlen 100012 AD16 1778 127 1 7 7 7 7 1 x x x 1 8 64 7 56 64 127 Uebung:Zahlendarstellungen ! WasistdieDezimaldarstellungfürdieseZahlen 100012 1778 AD16 173 A D 13 x 1 10 x 16 13 160 173 Startjeder.classFile ! Diessinddieersten16Bitsjeder.classfile 1 1 0 0 1 0 1 0 1 1 1 1 1 1 1 0 ! WasistdieseZahlinHexadezimalnotaIon? 1 1 0 0 1 0 1 0 1 F CAFE16 1 1 1 1 1 1 0 od–tx1Foo.class [Linux] SpeicherundAddressen • Jedes Byte im Speicher hat eine Adresse – eineZahlzwischen0undeinerObergrenze 0000 0004 0001 0008 0002 000C 0003 0010 0004 0014 0005 0018 0006 001C 0007 • Im Beispiel sind Adressen in HexadezimalnotaIonmit4Stellenangezeigt • ImJavaSystemsinddieAdressenunsichtbar unddasBespielzeigtwillkürlicheWerte • IllustraIonen mit Bytes sind unübersichtlich daher fassen wir 4 Bytes zu einem Wort zusammen und zeigen immer Wortadressen (diedannin4-erSchrixenansteigen) 0020 0008 0024 0009 . . . . . . 0028 000A 002C 000B FFD0 FFF4 FFD4 FFF5 FFD8 FFF6 FFDC FFF7 FFE0 FFF8 FFE4 FFF9 FFE8 FFFA FFEC FFFB FFF0 FFFC FFF4 FFFD FFF8 FFFE FFFC FFFF SpeicherplatzfürVariable ! FüreinedeklarierteVariablemussdasLaufzeitsystem Speicherplatzfinden ! DerSpeicherdesComputersistinverschiedeneBereiche unterteilt ! Genauer:derTeildesSpeichersdenunserJavaProgrammnutzendarf ! EinBereichistreserviertfürdieVariablendieimmer exisiteren(währenddergesamtenLaufzeitdesProgramms) ! DazugehörtenKonstanten,InformaIonenüberKlassen,etc. ! DieserBereichheisst“staIcdata” SpeicherplatzfürVariable ! ObjektediedurchdennewOperatorerschaffenwerden, werdenim“heap”(Halde)abgelegt. ! FürjedeaufgerufeneMethodestelltdasLaufzeitsystemeinen neuenBereichzurVerfügung ! Dieserheisst“StackFrame” ! AlleStackFrameswerdenineinemStackorganisiert ! Variable,dieineinerMethodedeklariertwurden,findenPlatzimStack Frame ! Parameterüberigensauch… 36 staIc data heap stack 0000 staIc data 0000 heap ! ManchmalsindStackundHeapso angeordnet,dasssiegegeneinander wachsen ! JedeRegionkannsogrosswiemöglich werden stack FFFF StackundHeap ! EineinfachesModelldieserBereichehile,verschiedene AspektevonJavazuverstehen ! DaProgrammeoeineinerMethodeVariabledeklarieren,die aufInstanzenverweisen,müssenwirsowohldenHeapals auchdenStackimAugebehalten ! IndenfolgendenFolienzeigenwirlinksdenHeapundrechts denStack ! GetrenntdurchLinie ! Stack-HeapDiagramm 39 StackundHeap ! WenneineMethodeaufgerufenwird,dannmussdasneue StackFramegenugPlatzfüralle(lokalen)Variablenhaben ! WenneineMethodeferIgist,dannkannihrStackFrame wiederverwendetwerden ! WenneineObjekInstanzgeschaffenwird,dannbrauchenwir SpeicherplatzfüralleAxribute(Zustandsvariablen) ! DazubrauchenwirextraPlatzfürJava-interneZwecke–Overheadden wirnichtkontrollierenkönnen 40 Verweise(References)aufObjekte ! FürjedeInstanzhältdasJavaSystemdieAdressederInstanz fest ! DieseAdresse(ggf.mitweiterenInformaIonen)wirdineinerVariable gespeichert(wirsagendazuauch“Referencevariable”–dieVariable enthältdenVerweis,d.h.dieReference,aufeinObjekt). ! Nehmenwiran,eineMethodeenthältdieDeklaraIon Rational r1 = new Rational(1, 2); fürdie Klasse Rational (wievorhereingeführt) 41 Verweise(References)aufObjekte ! WenndieDeklaraIon Rational r1 = new Rational(1, 2); ausgeführtwird,brauchenwirPlatzfüreineneueRationalInstanz. ! NehmenwirandiesenfindenwirmitderAdresse1000 42 Verweise(References)aufObjekte ! WenndieDeklaraIon Rational r1 = new Rational(1, 2); ausgeführtwird,brauchenwirPlatzfüreineneueRationalInstanz. ! NehmenwirandiesenfindenwirmitderAdresse1000 heap stack 1000 num den 1 1004 2 1008 r1 1000 FFFC ! DieVariabler1wirdimStackFramegespeichertunderhältdenWert 1000–dieAdressederneuenInstanz 45 InZeitlupe public void run() { Rational a = new Rational(1, 2); public Rational add(Rational r) { 36 5 Rational b = new Rational(1, 3); 2 1 3 1 Rational c = new Rational(1, 6); return new Rational( this.num * r.den + r.num * this.den , Rational sum = a.add(b).add(c); this.den * r.den ); println(a + " + " + b++""++""++bc++""+=""++csum); System.out.println(a + " = " + sum); }} 36 6 heap stack TestRa=onal 1000 num den 1 1004 1004 2 1008 1008 num den 1 1010 1010 3 1014 1014 num den 1 101C 101C 6 1020 1020 num den 5 1028 1028 6 102C 102C num den 1 1034 1034 1 1038 1038 100C 100C 1018 1018 1/2 + 1/3 + 1/6 = 1 All objects are created in the heap. This object is a temporary value used only during the100C calculation. FFE0 r 1018 1000 1024 sum c b a 1030 FFEC 1018 FFF0 100C 1000 FFF4 This stack frame is created for the add method. FFE8 1024 1024 1030 1030 FFE4 this This stack frame is created for the run method. FFF8 FFFC skip simulation ExplizitePointer ! DieSkizzelinkszeigtdenZustanddesSpeichersamEndeder Methode run ausTestRational. ! DasBildrechtszeigtdenselbenZustandmiteinemPfeil (“Pointer”)anstellederAdresse(PointerModell) heap stack heap stack 1000 num den 1 1004 2 1008 num den 1 num den 1 num den 1 num den 5 num den 1 2 100C num den 1 1010 3 1014 3 1018 num den 1 101C 6 1020 6 1024 num den 5 1028 6 102C num den 1 1034 1 1038 1030 sum c b a 1030 FFEC 1018 FFF0 100C FFF4 1000 FFF8 FFFC 6 1 sum c b a Adressenvs.Pointer BeideSkizzen(mitAdressenoderPointern)zeigendenselbenZustand aberbetonenunterschiedlicheAspekte. – AdressenverdeutlichendassVerweise(References)eineZahlenthalten. – DasPointerModellbetontdieBeziehungzwischenReferenz(variable) undObjekt(instanz). heap stack heap stack 1000 num den 1 1004 2 1008 num den 1 num den 1 num den 1 num den 5 num den 1 2 100C num den 1 1010 3 1014 3 1018 num den 1 101C 6 1020 6 1024 num den 5 1028 6 102C num den 1 1034 1 1038 1030 sum c b a 1030 FFEC 1018 FFF0 100C FFF4 1000 FFF8 FFFC 6 1 sum c b a Nicht-erreichbareObjekte Das Pointer Modell macht klar das es keinen Verweis auf die Rational Instanz5/6gibt.DieserWertistjetzt“Garbage”. DasJavaLaufzeitsystemführtvonZeitzuZeiteineSpeicherbereinigungdurch(“garbagecollec9on”)–automaIsch heap Instanz die temporär gebraucht wurde aber jetzt unerreichbar ist. num den 1 num den 1 num den 1 num den 5 num den 1 stack 2 3 6 6 1 sum c b a Uebung:Stack-HeapDiagramm GegebensindKlassen Point undLine wiefolgt public class Point { public Point(int x, int y) { cx = x; cy = y; } . . . other methods appear here . . . } public class Line { public Line(Point p1, Point p2) { start = p1; finish = p2; } . . . other methods appear here . . . private int cx; private int cy; } private Point start; private Point finish; Zeichnen Sie ein Stack-Heap Diagramm für den Zustand desSpeichersbevordieMethoderun auyört. public void run() { Point p1 = new Point(0, 0); Point p2 = new Point(200, 200); Line line = new Line(p1, p2); } Lösung AddressModel PointerModel heap stack heap stack 1000 cx cy 0 1004 0 1008 cx cy 0 cx cy 200 0 100C cx cy 200 1010 200 1014 1018 start finish 1000 101C 100C 1020 line p2 p1 1018 FFF0 100C FFF4 1000 FFF8 FFFC start finish 200 line p2 p1 BasistypenvsObjekte ! EinBasistypParameterwirdnachdenRegelnderValue Seman9csübergeben. ! EinParameterfüreineReferenzvariablefolgtdenRegelnder ReferenceSeman9cs. ! NuraufdenerstenBlickscheintes,dassBasistypenund Referenztypenunterschiedlichbehandeltwerden. 52 ! WennwireineBasistypVariablealsParameterübergeben, dannkopierenwirdenWertderVariable ! VeränderungeninderMethodehabendaherkeineWirkung ! WennwireinReferenzvariablealsParameterübergeben,so kopierenwirdieReferenz(mitderAdresse) ! DaherkönnenwirdieInstanz,diewirüberdieAdresseerreichen, verändern ! EntwederüberAccessor/MutatorMethodenoderdurchdirekten ZugriffaufeinAxribut 53 Verantwortlichkeiten ! SehenwirunsnochmaldieMethodegcd(inRational)an: /** * Calculates the greatest common divisor * using Euclid's algorithm. * @param x First integer * @param y Second integer * @return The greatest common divisor of x and y */ int gcd(int x, int y) { int r = x % y; while (r != 0) { x = y; y = r; r = x % y; } return y; } 54 int gcd(int x, int r = x % while (r != x = y; y = r; r = x % } return y; int y) { y; 0) { y; } ! WasfürAnnahmenmachtdieseMethode? ! LiefertsieimmerdasgewünschteErgebnis? ! WasfürFällesolltenwiruntersuchen? ! SchreibenSiedieFälleauf,dieSiefür"interesssant"halten ! SimulierenSiedieAusführung 55 Verantwortlichkeiten ! DieseImplementaIondesggTerwartet ! x≥0 ! y>0 ! Weristdafür"verantwortlich"? ! Klient? ! (OK,hieristderKlientdieselbeKlasseRational) ! DieMethode? 56 Verantwortlichkeiten ! (Teil)Lösunghier gcd(Math.abs(x), Math.abs(y)); ! Kommentarwärenichtverfehlt ! gcd kanndavonausgehendassx≥0 ! "x≥0"isteineAussagediehierimmergilt ! Nichtgelöstistdassy≠0seinmuss ! Beispielsollnichtüberladenwerden 57