Course notes Programming in Java version September 2001 Jeroen Fokker Department of Information and Computer Sciences Utrecht University i ii 1. 1.1 Programming 1 Computers and programs 1 Computer: processor plus memory 1 Instruction: direction to modify memory 1 Program: long sequence of instructions 1 Programming language: notation for programs 1 1.2 1.3 Order out of chaos 1 2.5 2.6 2.7 Programming paradigms 2 2.8 1.6 2.9 2.1 Java 8 Program environment 8 3. 3.1 3.2 2.3 2.4 3.3 Method definition 9 Method header and body 9 public: may be used by the environment 10 Variables 19 Calculations 20 Expressions having an int value 20 Use of variables and expressions 20 Operators 21 Operator precedence 21 Class definition 9 Class header and body 9 public: may be used by the environment 9 extends: extension of previous work 9 Graphics 18 Storage in memory 19 Assignment statement 19 Variable declaration 19 Location of declarations 19 The type int 20 Advantages of declarations 20 Program structure 8 Program: enumeration of classes 8 Class: enumeration of methods 9 Method: enumeration of statements 9 Calculated drawings 18 Graphic output 18 Methods in class Graphics 18 Classes describe object capabilities 18 The Color class 18 Java-applets 8 Java applications 8 JavaScript is not Java 8 2.2 Programming environments 12 Java applets on a web page 12 Source code, byte code and HTML code 12 Java2 Software Development Kit (SDK) 13 Edit-compile-run using SDK only 13 Integrated developing environments 13 Developing programs using JCreator 13 Interactive documentation 17 Using Wizards 17 In the small: Edit-Compile-Run 6 In the large: Model-Specify-Implement 7 2. Library classes 11 Importing classes 11 Package: group of classes 12 Program translation 5 Programming 6 Names 11 Rules for naming 11 Conventions for naming 11 Programming languages 3 Assembler 5 Compiler 5 Interpreter 5 Compiler+interpreter 6 Methods and parameters 11 Parameters in a method definitions 11 Parameters in a method call 11 Objects of type Graphics 11 Imperative languages: Assembler, Fortran, Basic 3 Procedural languages: Algol, Pascal, C 4 Object-oriented languages: Simula, Smalltalk, C++, Java 4 1.5 Statements 10 Method call 10 Object treated 10 Method name 10 Method parameters 10 Size of memory 1 Variable: named memory location 2 Object: group of variables 2 Size of programs 2 Method: named group of instructions 2 Class: named group of methods 2 Imperative programming: instruction based 2 Declarative programming: function based 3 Procedural programming: imperative + methods 3 Object oriented programming: procedural + objects 3 1.4 void: has effects, but no calculation result 10 Method name 10 Method parameters 10 3.4 Program layout 21 Comments 21 Distribution over lines 22 White space 22 iii 4. 4.1 Method definition 24 Order out of chaos 24 New methods 24 Methods involve an object 25 Method parameters 25 The this object 25 4.2 Importing everything 35 Constructing and using Scollbar objects 35 Object variables 36 New methods 24 6.5 In search for parameters 26 Parameters make methods more flexible 26 Flexibility carried to extremes 26 Flexibility in the large 27 4.3 Methods having a result 27 Function calculation 27 A method’s result type 27 The return statement 27 Method calls 28 5. 5.1 7. 7.1 7.2 Class String 30 7.3 Utility classes 30 Calculations involving rectangles 30 Class Integer 30 Class Math 30 Type double 31 Static methods 31 Calculations with rectangles 31 6. 6.1 User interaction 33 Interaction via objects 33 A color mixer 33 Four ways to acquire an object 33 6.2 6.3 Interaction components 35 Class Button 35 Class Scrollbar 35 iv The for statement 42 Special cases of iteration 43 Zero repetitions 43 Infinitely many repetitions 43 Iterated iteration 43 7.5 Case study: interest calculation 44 Interest on interest 44 Input via a TextField 44 8. 8.1 Choice 46 The if statement 46 Conditionally executing statements 46 An alternative part following else 46 Conditionally executing groups of statements 46 Many alternatives 46 Stop when found 47 8.2 The class Applet 34 Redefining method paint 34 Calling paint indirectly via repaint 34 Redefining method init 34 Calling method add 35 6.4 7.4 Creating new objects 33 Constructor methods and new 33 The new construction is an expression 33 New color objects 34 Boolean values 41 Shorthand of incrementing counters 42 Counting automatically 42 Declarations of object references 30 String expressions 30 5.3 The while statement 40 Comparison operators 41 Logic operators 41 The type boolean 41 Method paint has a single parameter 29 Class extensions inherit methods 29 Methods in class Applet 29 5.2 Iteration 40 Executing statements more than once 40 Repeating more than one statement 40 Repetition using a counter 40 Accumulation of a result 41 External input 29 Parameterizing applets 29 User interaction 36 Event: action by the user 36 Event listener: object which is informed 37 Event listeners are notified by a method call 37 Announcing methods using implements 37 Fulfilling the promise made by implements 37 Exploiting the promise made by implements 38 Reacting to Scrollbar events 38 The notion interface 38 Applications 47 Determining which button is pressed 47 Password checking 48 Minimum/maximum thermometer 49 8.3 Case study: Graph and zeroes of a parabola 50 Description of the case 50 Conversion if double-values 50 Structure of the program 52 Structure of the program 53 Finding the zeroes 53 Drawing the graph 53 Scaling 53 9. 9.1 Objects and classes 54 Special char values 70 Doing arithmetic with char 71 Example: counting words 71 11.2 Class: description of an object 54 Object: named group of variables 54 Class: declaration of variables plus method definitions 54 Objects and object references 54 Programs with multiple classes 56 9.2 Application: moving particles 56 Description of the case 56 Class Space 56 Class Particle 57 Design of the classes 57 The constructor method of Space 58 De methods of class Particle 58 Methods of class Simulation 59 Methods of class Space 60 9.3 Animation 61 Automatic movement 61 Class Thread 61 Method sleep 61 Calling sleep should be done in a try-catch statement 61 Controlling the animation 62 The value null 62 10. Inheritance 65 10.1 Subclasses 65 Subclass: defining additional variables/methods 65 extends generates subclasses 65 Inheritance of methods and variables 65 Redefinition of methods 66 The not-yet-extended object super 66 10.2 10.3 11.3 12. Designing the interface 77 12.1 12.2 Strings and characters 69 Class TextArea 69 Example: how many characters typed? 69 Class String 69 Primitive type char 70 History of char 70 Quote symbols 70 Example: Calculator 78 Description of the case 78 Division in classes 78 A. Reserved words 80 B. Operators and syntax 81 C. Summary Java-packages 82 12.1 12.2 12.3 package java.lang 82 package java.util 82 package java.awt 83 Layout managers 83 Interaction-components 84 12.4 package java.awt.event 85 Event-listeners 85 Event-objects 85 12.5 12.6 package java.net 85 package java.io 86 Non-stream 86 Byte streams 86 Character streams 87 11. Strings and Arrays 69 11.1 Layout of the user interface 77 Layout managers 77 Available Layout managers 77 Class Panel 78 Class hierarchies in Java libraries 67 Interface components 67 Event-listeners 67 Events 68 Everything in one hierarchy 68 Example: Text analysis with letter frequencies 74 Counting of individual character frequencies 74 Separating contents from the user interface 74 Class hierarchies 66 Extends: “is a” 66 Object-variables: “has a” 67 Arrays 71 Array: many variables having the same type 71 Creation of arrays 72 Using array values 72 Arrays as a parameter 73 Arrays of objects 73 Arrays versus strings 74 12.7 12.8 Application 87 Primitive types 87 v vi 1. Programming 1.1 Computers and programs Computer: processor plus memory A computer consists of many components, the architecture of which is a subject in itself. But roughly speaking, the architecture of a computer can be described by two words: processor and memory. Memory exists in many forms, varying in access time and throughput rate. Some types of memory are both readable and writable, some are readable and only writable with some more effort, and some are only readable. Also, memory that is only writable can be imagined. Input/output devices, (keyboard, mouse, CRT, printer etc.) may seem not to be comprised in the category “processor” and “memory”. But when viewed abstractly, they can be thought of as “memory”: a keyboard is “read only” memory, and a CRT is “write only” memory. Modems, network adapters, and even sound cards can all be regarded as memory. The processor, however, is fundamentally different form memory. It is the processor’s job to execute instructions. The effect of executing instructions is that memory is modified. With our broad definition of “memory”, almost every instruction has the effect of modifying memory. Instruction: direction to modify memory Thus, an instruction is a direction to modify memory. The instructions themselves are stored in memory as well (on disk, and when they are about to be executed in RAM memory as well). In theory a program could contain instructions to modify the program itself. That idea has been quite fashionable for some time (and expectations in the field of artificial intelligence were high), but programs like that turned out to be very hard to write: the program changes before you notice! Let’s therefore assume that the program is located in a part of memory that is separate from the part of memory that is being modified. Of course, prior to execution, the program is loaded into memory. Doing that is the task of a special program, usually called an operating system (or, in some circumstances, a virus). Program: long sequence of instructions We’ve come to the definition of a program: a program is a (long) sequence of instructions which, when executed by the processor, modify memory. Programming is the task of writing a program. This requires some imagination, for you need to imagine what will happen at the time the program is being executed. Examples of “programs” in real life are numerous, if you widen the definition of “memory” somewhat more: recipes, directions of use, legislation, the protocol of visiting the queen: all of these are sequences of instructions, which when executed, affect the world. Programming language: notation for programs De instructions comprising the program must be phrased in some way or another. This could be done with diagrams or hand waiving, but in practice this is done by coding the instructions in textual form. There are numerous formalisms to express a program. Such a formalism is called a programming language. Quite many have been designed is recent past, for each time someone designs yet another cute notation for denoting programs, a new programming language is born. In a recently published list of languages that have a name and to which at least one publication is devoted, some 3000 were distinguished. The actual number varies with your interpretation of what you count as a language and what you consider to be a mere dialect. But in any case quite many languages exist. Learning all these languages is futile. Fortunately, it isn’t necessary, because many languages have many resemblances. However, languages have evolved over the last 50 years. Initially, the use of new features available on new computers was dominant; nowadays, controlling the complexity of programs is the main concern of programming languages. 1.2 Order out of chaos Size of memory Few things have grown as spectacularly as computer memory size. In 1948, a proposal by Alan Turing to build a single computer with 6 kilobytes of memory was rejected (too ambitious and too expensive!). These days, that is the amount of memory of the grocer’s chip card. Also, in recent history memory size in increasing: ten years ago a typical PC was equipped with 640 kilobyte of memory, instead of today’s 128,000 kilobyte. For disk drives, similar numbers apply: ten years ago 20 megabyte was reasonable, these days 2,000 megabytes is called a “budget model”. Ask yourself what we will think in ten years of today’s 640megabyte compact disks… 1 Variable: named memory location For programs, memory is addressable in the form of variables. A variable is a named memory location. An instruction could be to change some particular, named variable. For small programs, this is feasible: a programmer can manage a few dozens of variables. But if we would fill all these recently acquired megabytes with variables, we would loose control. That is why using old languages, it is next to impossible to comply to presentday’s program standards (window interface, fully configurable, what you see is what you get, use of every imaginable peripheral, language independence, independence of cultural background and alphabet, integrated online help, wizards for frequently occurring chores…) Object: group of variables There is a well-known solution when things tend to grow too complex because of their number: group items, and name the groups. The trick works for persons in clubs, and for clubs in federations; it works for counties in provinces, for provinces into states, for states into countries, for counties into unions; for employees in departments, for departments in divisions, for divisions in companies, and for companies in holdings; it works for professors in departments, departments in faculties, faculties in universities, and universities in regional clusters. The trick should be applicable to variables as well. A group of variables, belonging together and addressable by a name, is known as an object. In so-called object oriented programming languages, objects can be stored in variables, an as such be part of even bigger objects. This way, programs can manipulate greater structures, without having to deal with the full complexity of details. Size of programs Programs are stored in memory as well, and because memory is so abundant, programs tend to grow large. Fifteen years ago, operating system, programming language and text processor together fitted in a ROM of 16 kilobyte; present-day word processors come on three CDs 640 megabyte each. There are so many instructions in a program, that for a single person it isn’t possible to grasp all the details. Worse: even a team cannot handle the program, because they have to discuss over and over the interaction of the instructions. 2 Method: named group of instructions We can use the same solution as e used for variables: create order out of chaos by grouping instructions, and referring to them by a name. By just mentioning a single name, we can easily address large amounts of instructions, without facing their full complexity over and over again. This is the only way to deal with complexity with relative ease. This principle has been in use since the beginning of programming, but is known by many names (the name of each particular instruction group is established by the programmer, but we are referring here to the name of the naming activity…). In the 1950s a named group of instructions was known as a subroutine. In he 1960s it was called a procedure. In the 1980s the word function was in vogue, and in the 1990s you should call it a method to be taken seriously. We’ll stick to the word method, but by whichever name you refer to it: most important is that long sequences of instructions become manageable by grouping them, and naming the groups. Class: named group of methods For decades, procedures were quite adequate. But with ever expanding programs a new problem arose: the number of procedures became too large to be manageable. Again, we apply the same solution: group procedures that belong together, and treat them as a whole such a group is called a class. As if to stress this new point of view, the grouped sets of instructions that were formerly called procedures where coined methods; the word “method” was introduced together with the word “class”. 1.3 Programming paradigms Imperative programming: instruction based In the realm of programming languages, we could use some order out of chaos, too. Programming languages sharing characteristics are said to belong to the same programming paradigm. (The word “paradigm” is stolen from the field of Philosophy of Sciences, where it denotes the common framework of theory in a certain period; thus the term is quite apt). A large number of languages belong to the imperative paradigm, and are therefore called imperative programming languages. The word “imperative” refers to the imperative verb form, using which you can issue commands. Imperative languages are based on commands or instructions (which, however, are commonly called statements) intended to change memory contents. Therefore, imperative languages are directly based on the computer architecture model involving processor and memory. In this course, an imperative language is used, which explains the name of the course. Declarative programming: function based The very existence of the adjective “imperative” suggests that other paradigms exist, where no commands or statements are involved. Would that be possible? What’s the use of a processor, if it’s not executing commands? The answer is that although a processor executes commands, this fact need not necessarily be reflected in the programming language. Think of designing a complicated spreadsheet, where relations between cells on a worksheet are made. This activity could be called “programming”, and the blank spreadsheet can be thought of as a “program”, ready to process input data. The “program” is based on defining functional relations between cells, rather than on issuing commands. Languages like the spreadsheet-language are called functional languages. Then we have a third paradigm: languages that are based on predicate calculus are called logic programming languages. Together, the functional and logic languages are called declarative languages. Interesting as they might be, they are not the topic of this course. Imperative Assembler Declarative Fortran Basic Functional Lisp Procedural Algol Excel Haskell Pascal Objectoriented C++ C Simula Java Figuur 1.1: Programming paradigms Logic Prolog Procedural programming: imperative + methods Programming languages where procedures (or methods, as the fashionable term is) play a role, are parts of the procedural paradigm. All procedural languages are imperative: after all, procedures are groups of statements, and the presence of statements is what makes a language imperative. Object oriented programming: procedural + objects Yet another extension of procedural languages is the paradigm of object oriented languages. Here, not only statements are grouped into procedures (or rather: methods), but also variables are grouped into objects. Sometimes the procedural and object oriented paradigm are contrasted (“are you in favor of procedural or object oriented languages?”). Such a question is beside the point: the question should be: “are you in favor of object oriented languages as well as procedural ones?”. 1.4 Programming languages Imperative languages: Assembler, Fortran, Basic The first computers were programmed by entering instructions, coded as numbers, directly into memory. Soon, it turned out to be useful to use mnemonic names for instructions rather than obscure numbers. This was the birth of the first real programming language, around 1950. It was called Assembler, because you could easily build (“assemble”) programs using it. Each processor has its own instruction set, and that makes Assembler processor dependent. That’s why you can’t say “the language Assembler”, but rather “a language belonging to the class of Assembler languages”. Of course, having a different language for each processor type is not convenient, because the introduction of a new processor type makes existing programs obsolete. In 1955, a new innovation was made: a language called Fortran (which is an abbreviation of “formula translator”). De statements of Fortran are not targeted at a particular processor, but could be translated (using a dedicated program) to various processor types. The language was used a lot for technical and scientific applications. It is still used in that field. Modern languages would also fit the purpose, but over years, many Fortran programs have been developed, and people tend to be conservative about the language they learned first. Fortran is not really accessible for beginner programmers. Initially, this was no problem, because beginners did not operate an expensive machine like a computer. 3 But over time (around 1965), the need arose for a language which was easier to use. This was the birth of Basic (“Beginner’s All-purpose Symbolic Instruction Code”). This language became immensely popular because it was the native language for “personal” computers: the Apple II in 1978, the IBM-PC in 1979, en all their successors. Unfortunately, the language was not standardized, and hence each company used its own dialect, and programs were not exchangeable among machines. Procedural languages: Algol, Pascal, C Meanwhile, it was recognized that for writing larger programs, the use of procedures could not be avoided. The first language that can really be called procedural is Algol (a rather strange acronym of “Algorithmic Language”). The language came into existence in 1960, and it had an official definition, which was very beneficial for the portability of programs among computers. A special notation was used for describing the “grammar” of the language (BNF). This notation is still in used, and has survived Algol itself. In the euphoria on technology of the 1960s, in 1968 a new version of Algol was designed: Algol68. A large committee tried an incorporated various new ideas in the language. There were so many new ideas, that it became terribly hard to write compilers for Algol68. There have been only a few, and Algol68 shared the fate of the dinosaurs: it has gone extinct because of its sheer complexity. Language designers learned from the experience: one should not strive for a language with many features, but rather for a simple and compact design. The first simple, yet procedural, language was designed by one person in 1971: Pascal (not an acronym, but named in honor of the philosopher Blaise Pascal). Designer Niklaus Wirth designed the language for teaching programming at the Zürich Polytechnic. Soon, the language was used for real applications (of course, because people tend to be conservative about the language they learned first). However, for large projects Pascal was too limited. A large project was the development of the Unix operating system in the late 1970s at Bell Labs. It was an innovation anyhow to write an operating system using a procedural language (until then, this was done using Assembler languages). For the purpose, a new language was designed: C (said to be the successor of earlier prototypes A and B). Unix’ philosophy was that users could write their own extensions (e.g., editors). For these extensions, people tended to use C as well. This made C the most important imperative language of the 1980s, even for other operating systems than Unix. 4 Object-oriented languages: Simula, Smalltalk, C++, Java In 1975, a researcher called Ole-Johan Dahl (Bergen, Norway) was interested in programs doing simulations (of queues in post offices, traffic circulation, etc.). In those days, it was not uncommon to design your own programming languages, and thus Simula was born as an extension of Algol60. One of the extensions was the existence of objects as a language feature. It was useful, because it could be used to model a person in the post office, or a car in a traffic jam. It made Simula the world’s first object oriented language. Simula had a marginal (yet persistent) life. However, the idea of objects as a language feature was picked up by researchers of Xerox at Palo Alto, who (prior to Apple and Microsoft) experimented with windowing systems and a mouse. Their language (called “Smalltalk”) used objects to model windows, buttons, scrollbars and similar identifiable objects. However, Smalltalk carried things to extreme: literally everything was modeled as an object, even numbers. This was not accepted by the masses. Yet it was evident that objects were a useful language feature. A C-like language with objects was deemed to come into existence. Soon, the language C++ was designed (the double plus sign means “successor” in C, so every C programmer understood that C++ was meant to be the successor of C). The first version of C++ is from 1978; the official standard was published in 1981. The language was very useful for writing window-based programs, which at the time became fashionable. But the success of C++ is also due to the fact that it was a true successor of C: (almost) all C programs are also acceptable as C++ program. This was important, because people tend to be conservative about the language they learned first. Although C++ was standardized, the method libraries needed to write windowbased programs were not. Programming a window on an Apple, Windows, or Unix machine was quite different. Consequently, programs could not be ported to other machines. Initially, this was not really a problem. Things changed, however, when in the mid 1990s, Internet became increasingly prominent: it was a pity that programs published on the internet could only be used by that part of the audience possessing the same operating system. A new programming language was due to arrive, this time one designed with portability among operating systems in mind. The language should resemble C++, because people tend to be conservative about the language they learn first. However, this would be a good occasion to drop some features which C++ inherited form C. The Java language fills the niche (no acronym, no philosopher, but named after the favorite coffee shop of the designers. For this language to have success in a world of Internet users spoilt with free software, it should be useable for free. Which company would be willing to invest in products to be distributed freely? Hardware manufacturer Sun showed the benevolence, of course not without a hidden agenda: an operating system independent language could help penetrating the market dominated by rival Microsoft. It is interesting to read the “small print” of the Java license: everything that is usually prohibited with software is allowed: use, copy, distributed – all is free. Only one thing is strictly prohibited: adding new features to the language specifically targeted at one operating system. Of course, that is the rule Microsoft tries to circumvent (e.g., by calling their compiler “J” instead of “Java”). Microsoft’s answer to Java was to introduce a language of its own, called C#. It is hard to predict what will be the outcome of this turmoil in programming language land. Will Java be the standard language of the next decade? Will C++ become obsolete, or is there still a need for it? Will Java rival the execution speed of C++? Will C# become as popular as Java is? Will Java remain standardized, or will dialects emerge? Anyhow, Java is easier to learn than C++ (which, because of compatibility to C is rather complex); so using Java, you can soon write interesting programs. Object orientation is a prominent feature of Java, which is certainly important to learn. Viewed as an imperative and procedural language, Java is certainly not worse than Pascal. Having a Java background, you can easily learn other object-oriented languages (such as C++ and C#, but also languages that do not exist as of now). That might prove useful, because there is no sensible reason to be conservative about the language you learn first… 1.5 assembler assembler Figuur 1.2: Translation by an assembler Compiler The advantage of all languages other than Assembler is that, at least in theory, the can be written machine independently. Thus, only one program is needed, that can be translated to machine code. The translator program is called a compiler. The compiler itself is machine specific: it should know about the machine code to produce. However, the program written by the programmer (the source code, or source for short) is machine independent. Translation by a compiler is the usual approach for procedural languages, like Pascal, C and C++. compiler Program translation Assembler A computer program needs to bee translated for use on a specific computer. The program that does the translation is called an assembler, compiler or interpreter, depending on the specific situation. An assembler is used to translate Assembler programs to machine code. Because an Assembler program is specific to a certain processor, you need different program for each target processor, which is assembled by an assembler for that specific processor. compiler Figuur 1.3: Translation by a compiler Interpreter A more direct way to translate is by means of an interpreter. An interpreter is a program which reads the source code and directly executes the statements, without first translating them to machine code. The interpreter is machine specific, but the source code is machine independent. 5 The word “interpreter” could be taken literally, if you consider the analogous situation for human languages: an interpreter translates sentences immediately when spoken, as opposed to a translator who processes a text as a whole. The advantage of an interpreter over a compiler is that no separate compilation phase is necessary. The disadvantage, however, is that translation is slower, and that possible errors in the program are not caught in an early stage by the compiler. Translation by an interpreter is common for relatively simple languages like Basic, but also for HTML and the language JavaScript that is embedded therein. (Note: do not confuse JavaScript with Java; these are different languages!). interpreter interpreter Figuur 1.4: Translation by an interpreter Compiler+interpreter For Java a hybrid approach is taken. Java programs are meant to be communicated via the Internet. Distribution of compiled machine code would not be adequate, as machine code is machine specific: you would have to distribute separate versions for every machine conceivable. Distribution of source code, however, might not be acceptable: the source code would be readable for everyone, which is an invitation to infringe on the copyright. A common situation is that users are allowed to use the program, but not to read or modify the source code. Machine code is a safeguard for that. This is why Java takes a hybrid approach. There is a compiler, which translates the source code. However, the target language is not machine code, but a machine independent intermediate language called byte code. The byte code can be distributed via the Internet. Subsequently, it can be executed on the user’s machine by means of a byte code interpreter. Byte code is simple enough for making simple interpreters. Also, interpreters can be easily embedded in Internet browsers. Most of the translation job is done by the 6 compiler, so interpretation of byte code can be done relatively fast. Execution of “real” machine code will however always be faster. interpreter compiler interpreter Figuur 1.5: Translation by a compiler and an interpreter via an intermediate byte code 1.6 Programming In the small: Edit-Compile-Run The source code of a program being text, implementation usually begins by typing in the source code using a text editor. Once completed, a compiler processes the source code. If nothing goes wrong, the compiler produces byte code, which can subsequently be interpreted. Normally, things do go wrong. The source code should be legal Java code; you can’t expect the compiler to translate nonsense to sensible byte code. This is why the compiler checks whether the source code conforms to standards: if not, it produces an error message, and doesn’t emit byte code. In general, you will try hard to compile real Java source code, but it is only human to make typing mistakes, or to sin against program syntax rules. Be prepared to revise the program a couple of times using the editor. Sooner or later, the compiler does not complain any more, and emit byte code. Then the next stage begins: running (or executing) the program. Some (many?) times you’ll notice that the program does not do what you had in mind. Of course you tried hard to correctly phrase your intentions, but to err is only human. The remedy is to return to the editor, and modify the program. Then you should compile the program again (hoping that you didn’t introduce new typing mistakes) and run again. Only to see that the program behaves differently this time, but still not as you had in mind. Back to the editor… In the large: Model-Specify-Implement When programming projects are more ambitious, it is not a good idea to just sit at the computer and start typing source code. Prior to implementing (the actual writing and testing of the program as described above) are two more phases. First, you will have to state your problem in terms of a program that processes input from a user and shows results. This phase, modeling of the problem, might be the most difficult one. Once it is clear which tasks should be performed by the computer, the next step is to list the classes that are necessary, and the methods contained by them. In this phase you plan what methods to write, but not how this is done. Bear in mind that you do not specify the impossible, because they will have to be implemented sooner or later… When the specification of the program is complete, the implementation phase starts. The edit-compile-run cycle described above will most probably be traversed repeatedly. After implementation, the program can be transferred to the customer (which might be you). In many cases the customer will reply that the program is nice, but the problem that should have been solved was slightly different. Then you can start all over again, revising the model, revising the specification, and making a new implementation, and then… 7 2. Java 2.1 Program environment Java-applets One of Java’s raisons d’être is distributing programs via the Internet. This is done most directly by executing programs from the Internet browser. Part of the browser’s window is allocated to the program and can be used by the program for communicating with the user. Java programs that behave like this are known as an applet (for “small application”). In HTML, the markup language for web pages, it is possible to indicate that an applet should be included in a web page. Analogous to including an image using the <IMG>-tag, you can include an applet by means of an <APPLET>-tag. The byte code itself doesn’t take part of the HTML-file, but is located in a separate file, in the same fashion that an image is located in a separate gif- or jpeg-file. Java applications Java being a complete, general purpose programming language, it is possible to create stand-alone programs, which can be run without an Internet browser. As opposed to an applet, a stand-alone program is called an application. The source code for an application differs slightly from that of an applet. E.g., an application must create it’s own window, where an applet can use the browser’s window. An application can, if needed create more than one window, which is not possible in an applet. Furthermore, an application can use local files, which is not allowed in applets for security reasons. But it is possible for applets (as well as for applications) to read files form the Internet. For the larger part of a program, however, it is not relevant whether the program is to be used as an application or as an applet. An applet can be easily modified to an application (by adding statements that create a window, in which the applet output is shown). The other way around might be more involved, as the application may not conform to the restrictions that apply for applets. JavaScript is not Java Confusingly, there is one more language that is intended to use in an Internet based environment. We are referring to JavaScript, which has, although the name suggests otherwise, not much in common with Java. 8 Apart from the name, Java and JavaScript have in common that they are both imperative, machine independent languages, that can be used with an Internet browser in a secure way, such that the user need not worry that his local files will be corrupted. There are, however, quite a few differences: Java is object oriented, JavaScript is “only” procedural. Java is compiled (to byte code, which is subsequently interpreted); JavaScript is interpreted directly. Java byte code is located in a separate file; JavaScript source code can be embedded in the HTML-file. Java-applets are restricted to the part of the window that is allocated to them; JavaScript programs can interfere with the rest of the browser (change background color, surf to a different location, etc.) Java programs can use an extensive library of classes and methods; JavaScript programs can only use a limited number of built in methods. In short, Java is used mainly by programmers, and JavaScript is used mainly by web designers. In this course we will only use Java, because that language will provide more long-term insight in object oriented programming languages. Thus, you can easily learn other languages in the future; after all, there is no need to be conservative about the language that you learn first. (Occasionally, we might envy JavaScript programmers a bit, because of the cute graphical effects that can be easily programmed with JavaScript). 2.2 Program structure Program: enumeration of classes Because Java is an imperative language, a program consists of statements (instructions) that need to be executed at run time. Because Java is also procedural, the statements are grouped in methods. And because Java is object oriented as well, the methods are grouped in classes in turn. Thus, a program consists of an enumeration of all classes needed (each containing some methods (each containing some statements)). In listing 2.1 one of the shortest Java programs possible is given. It is an applet that writes Hello! to a window. There is only one statement, which nevertheless need to be embedded in a method, which is the sole inhabitant of a class. Each class bears a name, invented by the programmer. The example class is named Hello. import java.awt.Graphics; import java.applet.Applet; public class Hello extends Applet { public void paint(Graphics g) { g.drawString("Hallo!", 30, 20); } } Listing 2.1: Hello.java Class: enumeration of methods A class mainly consists of an enumeration of member methods. In the example program, there is only one method. Just as in the case of the class, the name of the method is determined by the programmer. In the example, the method is named paint. Method: enumeration of statements In methods, the statements proper are contained. In the example program there is only one statement, viz. a statement to display a certain text at a specific location of the screen. We will now study the example program more closely. 2.3 Class definition Class header and body Each class definition consists of a header (the first line) and a body. The header of the Hello class in the example program is public class Hello extends Applet The body which follows is enclosed between braces: { and } . This way, it is clear which methods belong to this class. Usually, the braces are aligned vertically, and placed on a separate line. All text in between is indented. Using these layout conventions, the end of the class can easily be found. public: may be used by the environment The header of a class mentions the word class, to indicate that we’re dealing with a class definition. Adjacent to the word class, the name the programmer chose is written, in this case Hello. Preceding the word class, the word public can be written. This indicates that the class may be used by the outside world. The example program being an applet, the class need indeed be public, because the class is used by the browser. Instead of public, the word private might have been used. A private class is only for internal use by other classes of the program. In the example program this would not be a good idea (there are not even other classes!). Leaving out the word public or private altogether makes a class private automatically. extends: extension of previous work Following the name of the class, the word extends may be written, followed by the name of another, already existing class. That way, we can indicate that a class is not built from scratch, but that it is an extension of another class. Because the example program will be an applet, we have our class extend the existing class Applet. Thus, we need only describe in which way our program differs from an “empty” applet. 2.4 Method definition Method header and body A class body consists of methods belonging to it. In the example program there is only one: a method named paint. Analogous to a class, a method consists of a header and a body. This time, of course, the word class is omitted form the header. The header of the example method is: public void paint(Graphics g) The method body must be enclosed in braces. That way, it is shown which statements are parts of the method. In the example program this happens to be just one statement. For clarity we align the braces vertically, and indent the body that is enclosed by them. 9 public: may be used by the environment As was the case with a class header, a method header begins with an indication of the protection: the word private or public. The example method is used by the browser, and must therefore be public. void: has effects, but no calculation result The second word of the method header is the method’s type. Some methods may calculate a result value; the type indicates what kind of value that will be. But some methods are just there to execute statements because of their effect. The example method is one of them. In these cases, the word void is used as a type. Literally, this means “empty”, which is an adequate description of the non-existing result value of the method. 2.5 Statements Method call In Java, there are a dozen of different statement forms. We’ll begin with one of the most important: the method call. When the processor executes a method call, it will start executing the statements that are in the body of the method mentioned. Only after that has been completed, the processor continues with the statement following the method call. Interestingly, the statements of the method called may be method calls themselves. Compare this to delegation of work to others: when a method is too lazy to carry out some work, another method is called to do the job. In the example program, the sole statement in the body of the paint method is a method call: g.drawString("Hello!", 30, 20); Method name The third word in the method header is the method’s name. In principle, the programmer may determine the name. However, when an applet is started by the browser, it will begin executing the statements in a method called paint. If we want our method indeed being started by the browser, we’d better call it paint. Method parameters Next to the name of the method, an enumeration of so-called parameters follows. They are enclosed in parentheses: plain, round parentheses, not braces. The programmer may determine the number of parameters: zero, one, two or more. But even if there are zero parameters, the parentheses are there, enclosing nothing. In the case of the paint method, there is no choice, because the browser (who calls the method) expects it to have exactly one parameter. The parameters indicate the type of variables that are available for used in the method. In the case of paint, there is no choice: the single parameter needs necessarily be a Graphics-object. As a programmer, we may however freely choose a name for the parameter. In the example, the name g was chosen. The purpose of parameters will soon become clear. But first, we’ll investigate the method body. 10 We’ll analyze this statement in detail now. Object treated Each method that is called treats a particular object. In a method call, that object is mentioned first – after all, the paradigm is object oriented! In the example the object g, which we got as a parameter, will be the object treated by the method that is called. Method name Next to the object that will be treated, a dot is written, followed by the method that needs to be called. In the example, we want our object g to draw something, which is why we call the method drawString. Method parameters With the call of drawString we need to specify some details: which text to draw, and where this is supposed to happen. The text under consideration is "Hello!", the position 30 pixels (image dots) form the left-hand side edge, and 20 pixels from the top edge of the window. Parameters must be enclosed in parentheses. Finally, a semicolon completes the method call. The text is written between quotation marks, to indicate that this text is to be taken literally. Without quotation marks, the compiler would comply that there is no text named Hello, and that a spurious exclamation mark is no legal Java code. Between quotation marks however, everything is acceptable. Quotation marks mark the boundaries of the text; all symbols in between of them are taken literally. 2.6 Methods and parameters Parameters in a method definitions Parameters are the way methods communicate: in a method header you can specify that a method expects parameters when it is called; in a method call you need to indicate what value the parameter has (which should be of the type that is expected by the method). The header of the paint method was: public void paint(Graphics g) Here, we specify that anyone who wants to call this method, needs to provide an object of type Graphics. The browser, when he calls paint, indeed does so. Parameters in a method call In a method call, values for the parameters specified by the method should be mentioned. In the method header of drawString it is specified that this methods expects three parameters: a text and two numbers. When calling drawString, we therefore need to provide a text value and two number values. Both in the method header and in a method call, the parameters are enclosed in parentheses. But the thing that is enclosed in them, differs: in a method header, a type and a programmer determined name is given, in a method call only a value for the parameter is given. Objects of type Graphics What, now, is a Graphics object, that is received as parameter by the paint method? Best way to think of a Graphics object is a “drawing apparatus”. It is an object that, upon request, can draw texts (and other things) for us, namely by calling the drawString method, mentioning the object before the dot. Magically, the browser is in possession of such a drawing apparatus, and is benevolent enough to pass it as a parameter when calling the paint method. In the method header of paint we accept the parameter, labeling it with the name g. In the method body we can use the object g thus acquired, using it as a the object that is treated with the drawString method. 2.7 Names Rules for naming Many things in Java are indicated by their name: classes, methods, object types, parameters. Sometimes the name is chosen by the programmer, sometimes it is the name of an already existing class or method. Anyway, the name should conform to the following rules: the name consists of one or more letters, digits, and/or underscore signs and dollar signs; the first symbol is a letter; some fifty words are taboo, because they have a special meaning in Java (examples are public, class, extends, and void). Capitals and lower case letters are considered to be different; de method in the example program needs to be named paint, not Paint or PAINT. These rules are obligatory; when you violate them, the compiler complains. Conventions for naming Furthermore, there are some conventions for naming that most programmers conform to. These rules are not strict in the sense that the compiler enforces them, however it shows good taste to conform to them: when all members of a team do so, programs will have a uniform “look”. The conventions are: names of classes start with a capital (for example our own class Hello, and the existing class Applet), as do object type names (e.g., Graphics); names of methods start with a lower case letter (e.g., paint and drawString) as do parameter names (such as g); when a name consists of multiple words, the second and later words begin with a capital (as in drawString); funny acronyms should be avoided—rather use a multi word name (thus, don’t use ann but aNiceName). 2.8 Library classes Importing classes In the example program, two classes are used form a library of Java standard classes: Applet, because our class Hello is an extension of it Graphics, because it is the object type of the parameter of paint 11 To be able to use these classes, they have to be “imported” in the program. This is done by a special import directive at the start of the program: import java.awt.Graphics; import java.applet.Applet; As opposed to the method call further down the program, these are not statements: the processor need not execute them at run time. Rather, they are directives for the compiler that the classes mentioned will be used in the program. That is the reason we call them “directives” rather than “statements”. Package: group of classes The classes in the Java libraries are organized in so-called packages. (Computer scientist sort of like hierarchic orderings). In import directives, the name of the package needs to be mentioned. Class Applet is in package java.applet (in which besides Applet, some more classes having to do with applets reside). Class Graphics is in package java.awt. In these packages, all classes belonging to the abstract window toolkit are located. The toolkit is called abstract because the classes are not designed with a particular operating system in mind. Therefor you can use them for making cross platform programs. 2.9 Programming environments Java applets on a web page Java applets are part of a web page. To test an applet, you’ll need to prepare an HTML-file pointing to the applet. In listing 2.2 is an example HTML-file, which wraps the Hello applet. This is done with an <APPLET>-tag, where the name of the byte code file, the width end the height are specified. A corresponding closing tag </APPLET> is also needed. Text appearing between opening and closing tag is shown when the browser is incapable of running applets. <HTML> Here is a <B>simple</B> applet: <BR> <APPLET code = Hello.class width = 100 height= 50> </APPLET> </HTML> Listing 2.2: Hello.html 12 Source code, byte code and HTML code The name of the file that holds the source code needs to have the same name as the class described therein, completed with a .java extension. The program named Hello thus is stored in file Hello.java. Not all compilers enforce this rule, but some do, and it is a useful convention anyhow. import java.awt.Graphics; import java.applet.Applet; public class Hello extends Applet { public void paint (Graphics g) { g.drawString(“Hello!”, 20, 20); } } Hello.java compiler Hello.class <HTML> Here is a <B>simple</B> applet: <BR> <APPLET code = Hello.class width = 100 height= 50 > </APPLET > </HTML> Hello.html Figure 2.1: source code, byte code and HTML code When compiling the source code, the compiler produces a file with .class extension, viz. Hello.class. This is quite confusing, as the class is described in a java-file, and the class-file holds byte code… The byte code file is not readable using an editor. It need not be, because it is intended to be used by the interpreter that is built in in the web browser. (You might find it interesting to inspect a class-file using a hexadecimal editor: you’ll recognize some method names, but not the statements you wrote). The class-file and html-file should be placed on a web server. The java-file need not be located on the server; it can be kept secret if desired. When someone visits the web page, the applet is started and its output will be visible to the user (because the browser calls the paint method). The result is depicted in figure 2.1. Java2 Software Development Kit (SDK) Sun Computer Inc. provides a bundle of software, known as the Java2 Software Development Kit, containing (amongst others) the following programs: javac, a compiler translating Java source code to byte code java, an interpreter for running stand alone Java applications appletviewer, an interpreter for applets, with which you can test applets even without a browser. Formerly it was known as the ‘Java Development Kit’ or JDK, which went through versions 1.0 and 1.1. With the advent of version 1.2, it was renamed SDK, and for marketing reasons the product was named ‘Java2’. After that, the product evolved into version 1.3, of which the full name would be ‘Java2 SDK, version 1.3, Standard Edition’. This year, a betatest version 1.4 became available. Edit-compile-run using SDK only There is no editor in the SDK. Instead, you can edit the source code and HTML code using your favorite editor. A simple editor like notepad or pfe will do. The compiler can be run from a DOS box, issuing the command javac Hello.java Error messages, if any, also appear in the DOS box. The compiler mentions the line number of the error; you can search for this line in the editor, and correct the mistake. When the compiler detects no errors, it will generate a .class-file. You can then run your applet by entering the command appletviewer Hello.html where Hello.html is the HTML-file containing the <APPLET>-tag referring to the file Hello.class. When the program is an application rather than an applet, you can run it with java Startclass Here Startclass is the name of the class (not the name of the file!) containing the method that builds the main window. For the moment, however, we will not use this, because all programs in the next few chapters will be applets. developing environment (IDE), which combines compiler, interpreter and editor in one program. An IDE provides facilities for: clicking error messages, upon which the cursor is automatically located at the line containing the error; recognizing certain Java keywords and constructs, showing them in a distinctive color; automatical completion of closing braces when you type an opening brace; browsing through methods and classes compile and run programs with a single keypress reading online help Various IDEs for Java are commercially available: Webgain Visual Cafe, Inprise JBuilder, Microsoft J++, and IBM Visual Age, Oracle Jdeveloper, Metroworks CodeWarrior, and Tek-tools Kawa, to name a few. Most IDEs do not have a built-in compiler, but behind the scenes use the compiler that comes with SDK. The IDE is a mere “shell” around SDK. This has the advantage that you can still use the IDE when newer versions of SDK will be available. In this course, we will use a freeware IDE called Jcreator from Xinox software, which is quite as powerful as the commercial products, and moreover is, well, free. Developing programs using JCreator Compiling and running a program using an IDE might look complicated from this description, but is actually a lot easier to use than a separate editor, compiler and appletviewer. We will go through 10 steps. Some of these steps might be unneccessary in some circumstances, some of them might need te be repeated. Step 1: start the IDE by clicking the icon on the desktop, or be selecting Start>Programs>Jcreator>Jcreator. The JCreator main window will appear. In this window, a few empty panes are visible at the left, and the usual wealth of menu options and buttons are available near the top of the window. Integrated developing environments Although the SDK is in principle all you need to develop Java programs, the ease of use is rather limited. More services are provided by a so-called integrated 13 Step 2: Because we work in a networked environment, the first thin we have to do is to instruct JCreator that it should store files in your personal H disk, not on the C disk of the computer that you happen to work at. To do so, choose Configure>Options. There are many items that you can specify options for – you might want to go through them some day to adapt JCreator to your personal taste. But for now, choose Directories, and next to ‘Default Project Directory’ type a path on the H disk. You might want to create a new directory, as you will create a lot of files in the near future, which will clutter up your documents directory. You can leave the other two directories (‘Syntax’ and ‘Project Templates’) as they are. 14 Step 3: Even a simple program needs more than one file. Files that belong together (e.g., the java-file and the html-file) are bundled in a so-called project. So the first thing to do when writing a new program is to create a new project. Choose Project>New Project or File>New>Projects. You can choose from three types of projects. For now, choose ‘Empty Project’, and type of a filename for the project, e.g. Hello. In the pane near the left of the window, you see that yo have a ‘workspace’ now, containing a single project named ‘Hello’. Step 4: Now is the time to create the java-file for the program. From the menu, choose File>New>Files, select the ‘Java file’ icon, and type a filename. You can use Hello here again, as JCreator will automatically add the .java extension. It won’t harm to type the extension yourself, though. Step 5: The next step is typing the program. In the window that appeared when you created the file in step 4, you can type in the Java program from listing 2.1. When you’re done, choose File>Save to save your work to disk. As always, it is a good idea to frequently save your work, as you might loose unsaved work when the computer crashes. (see top figure in the next column) Step 6: The same procedure is used to create the HTML-file. Again choose File>New>Files, this time click the ‘HTML file’ icon. Again, you can name it Hello, as the extension is automatically added. Type the HTML file from listing 2.2 and save your work. 15 Step 7: Before whe can test our program, it needs to be compiled. This is done by choosing Build>Compile Project. You can get rid of the appletviewer window by clicking the X in the top right corner. The DOS box will also disappear. It is quite probable that the program contains errors. You might have introduced a typo, like the misspelled word ‘Grahpics’ (which should have been ‘Graphics’) in the example above. In the example, that’s why the compiler complains that it ‘cannot resolve the symbol’. Other common sources of errors are confusing lower case characters and capitals: in Java (unlike HTML), there is a difference between ‘paint’ and ‘Paint’. When you double click on the error message, the cursor will jump to the line containing the error. Especially in long programs, this is quite convenient. Next, you can correct the error, and compile again choosing Build>Compile Project. This should be repeated until there are no more errors, and the the Build window just mentions ‘Process completed’. Step 8: Once the program is successfully compiled, you can run the program to see its output. For this, select Build>Execute Project. A black DOS box appears, which you can ignore, but also an Appletviewer window appears, which shows the output of the applet. Finally we have our Hello message on the screen… 16 Step 9: Alternatively, you could run the applet by loading the HTML-file to the web browser. Locate the HTML file using Windows Explorer and double click it. The output will be visible in an Internet Explorer window. Notice that, this time, you will see all of the HTML file, including the text ‘this is a simple applet’. The applet per se appears in a separate (grey) rectangle. This is in contrast to the appletviewer approach shown in step 8, which only shows the output of the applet (and the text ‘Applet started’). Well, there are easier ways to show ‘Hello’ in an Internet Explorer window (just type it in the HTML window). But remember that this is just a silly example to show you all steps of compiling and running a program – we’ll meet more involved programs in the next chapters. Interactive documentation You might wonder what classes are avaliable in the standard packages, and what methods reside in these classes. In this course, we’ll encounter some of them. They are summarized in appendix C. But there are many more, and mentioning all details of them is far beyond the scope of these course notes. You will need some of the unmentioned methods, though. For this purpose, there is online documentation available. The easiest way to access it is to highlight a class name (for example ‘Graphics’) in the source text, and then choosing Help>JDK Help. (Or alternatively, right click the word and select Show JDK Help form the context menu, or alternatively, press Ctrl-F1.) You will be presented a reference manual for the class you selected. Some parts of it might be incomprehensible by now, but if you browse through it, you will encounter useful information – for example, an index of all methods in the class. Using Wizards All applets have the same structure as the simple Hello program shown in this chapter. Each applet should have an accompagnying HTML-file, where its size is determined. In steps 3 through 6 we created the project, the Java file and the HTML file by hand. In practice, it is easier to let a wizard assist you in going through these steps. When you choose File>New Project, you can select the ‘Basic Applet’ icon. You’ll obtain a ready-to-run applet quite similar to our Hello example, which is a good starting point for making modifications to. 17 3. Calculated drawings 3.1 Graphics Graphic output A program just writing “Hello!” to the screen is not that interesting (this could have been done even without Java, by putting the text “Hello!” directly in the HTML-file…) Fortunately, Graphics objects recognize more methods than just drawString. By calling various methods in the body of paint, an applet can construct complicated drawings. For example, the program in listing 3.1 draws a painting in the style of Mondriaan’s “composition with red and blue”. Figure 3.1 shows the running applet. Methods in class Graphics Objects of type Graphics, as provided as parameter of paint, can be used with the following methods: drawString ( t, x, y ) draw text t at position (x,y ) drawLine (x1, y1, x2, y2 ) draw a line from position (x1,y1 ) to (x2,y2 ) drawRect (x, y, w, h ) draw a rectangle having width w and height h drawOval (x, y, w, h ) draw an oval in the described rectangle fillRect (x, y, w, h) as drawRect, but filled in setColor (c ) use color c in subsequent calls of draw… All sizes and positions are measured in pixels, counted from the top left corner. Thus, x-coordinates range from left to right, and y-coordinates from top to bottom (as opposed to graphs in mathematics). In listing 3.1 we use method fillRect to color a number of rectangles. For colored rectangles we first call setColor. Classes describe object capabilities You can call all methods from class Graphics, provided that you have an object of type Graphics. In the body of paint this is no problem, as a Graphicsobject is given as a parameter, which may be used in the body. This illustrates the role of class definitions. It is not just an enumeration of methods: the methods can be used to perform some operation upon the objects 18 having the class as type. In as sense a class describes the capabilities of an object: a Graphics-object “is able to” draw texts, lines, rectangles and ovals. Objects have memory of their own. This becomes clear from studying setColor: after having called it, the object “remembers” the color to be used in subsequent method calls. This is consistent with the definition we gave of objects in section 1.2: an object is a group of variables. Meanwhile, we’ve seen that a class (section 1.2: named group of methods) describes the capabilities of an object. The “behavior” an object exhibits is even more interesting than an enumeration of component variables. You can see this from the way we’ve been using the Graphics-object: we don’t even know of which variables the object is composed, but we are able to use the object once we know what methods to call. Usage of library classes is ruled by the motto: “benefit without wondering”. The Color class The method setColor in class Graphics needs some more explanation. When calling the method, we need to supply a color as a parameter. In Java, a color is an object in itself, which poses the question: where can we get color objects? There is a library class Color, in which some methods are available that can manipulate Color objects. But we cannot do so as long as we don’t have a Color object yet… But there is more to use in classes than just methods. In some classes, ready-to-use objects are available. Indeed, in the class Color there are some Color objects there to be used. You can retrieve these constants by mentioning the name of the class and the name of the constant, separated by a dot. For example: Color.blue Note that this time in front of the dot is not the name of an object (as in method calls), but the name of a class. The Color object thus retrieved can be used as a parameter when calling setColor: g.setColor( Color.blue ); Here, g is the Graphics object that in subsequent method calls must draw in blue. In the Color class the following constants are available: black darkGray red green blue gray white lightGray cyan magenta yellow orange pink “Cyan” is the opposite of red: the blueish/green color of cyanide salts. “Magenta” is the opposite of green: a kind of bright pink. When you want to use the Color class, you need to import it at the start of the program. Just as Graphics, the Color class is in package java.awt, so the import directive is: import java.awt.Color; 3.2 Variables Storage in memory In the example program, three black vertical bars are drawn. This could have been done using the following statements: g.fillRect(10, 0, 10, 100); g.fillRect(50, 0, 10, 100); g.fillRect(90, 0, 10, 100); The first two parameters denote the position of the bars: 10, 50 and 90 pixels from the left edge, 0 pixels from the top edge. The third and fourth parameters denote the width (10) and height (100) of the bars. After some experimenting, it might become clear that a width of 12 pixels rather than 10 is esthetically more pleasing. A problem is, that we cannot change every “10” in “12” using the search-and-replace function of the editor, because this would also change the x-coordinate of the first bar, and moreover would change the height of the third bar from 100 to 120. (If you think that an easy way out would be to change the figures manually, you are right, but think of an experiment on a more complicated picture, say the Victory Boogie-Woogie). A solution is the use of variables. In lieu of the two constants 10 and 100, we use two variables, say bar en height: g.fillRect(10, 0, bar, height); g.fillRect(50, 0, bar, height); g.fillRect(90, 0, bar, height); We will make sure that these variables have value 10 and 100, respectively, in such a way that they can be easily changed when needed. Assignment statement In Java, variables can be changed using an assignment statement. This is done as follows: bar = 10; height = 100; Thus, an assignment statement consists of: the name of the variable to change; the symbol = , to bepronounced “becomes”; the new value of the variable; a semicolon. If the variable has received a value before (say, with another assignment statement) the old value is lost. And contrariwise: the new value is preserved until the next assignment statement to the same variable. That explains why the symbol = should be pronounced as “becomes” rather than “is”: the value of the bar variable is not (yet) 10 (necessarily), but it becomes so by execution of the assignment statement. We’ve seen two statement forms by now: the method call from section 2.5, and the assignment statement. Variable declaration You may not use variables at will in a program. The variables that are used must have been announced. This is done by a so-called declaration, or in other words: the variables need to be declared. A declaration looks like this: int bar, height; A declaration consists of: the type of the variables; a name or names of the variable(s), separated by commas; a semicolon. A declaration is not a statement: there is nothing to do at run time. A declaration is rather a directive for the compiler that certain variables will be used in the program, and are of certain type. Location of declarations Declarations are written in the body of the method where they are needed, that is after the opening brace. In fact, they may appear anywhere in the method body, in between of the statements. In practice, they are listed at the beginning of the method body, in the same fashion as a recipe starts with listing the ingredients. After the declarations, assignment statements follow that assign values to the variables. After that, the variables may be used: public void paint(Graphics g) { int bar, height; 19 bar = 10; height = 100; g.fillRect(10, 0, bar, height); g.fillRect(50, 0, bar, height); g.fillRect(90, 0, bar, height); } Variable declarations resemble to parameter declarations in the method header. In fact, those are declarations as well. But there are some differences: variables are declared in the method body, parameters are declared in parentheses in the method header; variables receive a value by means of an assignment statement, parameters automatically receive a value at the time a method is called; in a variable declaration more than one variable can be declared, writing the type only once; in a parameter declaration the type need be repeated for every parameter (even if it’s the same) variable declarations end with a semicolon, parameter declarations don’t. The type int By means of a declaration (both of variables in the body, and of parameters in the header of a method), a name is connected to a type. In many instances, this is an object type: the parameter of paint, that we’ve been naming g throughout, is a Graphics object. The variables bar en height in the variable declaration have type int. This type is not an object type, and thus bar en height are not objects. Instead, they are numbers, which are primitive values in Java. These values are built in in Java, without having a class that lists their properties. Primitive values, being not objects, cannot be treated by methods. They can, however, be used as parameters to methods, as is the case in the call of fillRect. The corresponding parameter must have been declared, in the definition of the method, as an int. Primitive values are not objects, and thus primitive types are not classes. That is why int, in contrast to object types, is not written with a capital. The type int is built in in the language, and therefore needs not be imported. The word int is not even a name, in contrast to class names indicating object types. The word int, and the other primitive types, are “keywords” of the languages, just as “public” and “extends” are. Variables (and parameters) of type int are numbers. Their value must be integral, there can not be digits “following the decimal point”. The value can be positive or 20 negative. The largest value possible is 2147483647, the smallest is –2147483648; the range is thus roughly plus or minus two billion. There are only a few primitive types. Others that we will meet later are double (numbers that can have a decimal point), char (characters) and boolean (truthvalues). All other types are object types; their properties are described in a class. Advantages of declarations There are various advantages of declarations: because of declarations, the compiler knows the intended type of each variable; using that information it can check whether method calls make sense (calls of fillRect only make sense when they are treating Graphicsobjects, not when other object types or int values are written before the dot); in method calls, the compiler can check whether parameters are of the right type; if you would accidentally swap the text and position parameters of drawString, the compiler would notice the error; when you mistype the name of a variable (for example height instead of height), the compiler will not accept this. 3.3 Calculations Expressions having an int value At various places in a program, an int value is required, for example: as a parameter of a method call taking int-parameters as right hand side in an assignment statement In these locations, you can write a constant number, like 37, or the name of an int variable, like height. But it is also possible to write a formula involving addition or multiplication, for example height+5. When the statement in which the formula appears is executed, the value is calculated (using the current values of the variables). The outcome is used in the statement. A formula is usually called an expression in Java and other languages. Use of variables and expressions In the example program in listing 3.1, variables and expressions come in handy. To make the program easily adaptable, variables are not only used for the height and width of the drawing and the width of the black bars, but also for the positions of the bars. The x-coordinate of the three vertical bars is stored in variables x1, x2 and x3, whereas the y-coordinate of the two horizontal bars is stored in variables y1 en y2 (note that digits may be part of variable names, provided that it begins with a letter). Using assignment statements all variables are assigned a value: width = 200; x1 = 10; x2 = 50; x3 = 90; etcetera. When drawing bars, no constants are used any more (except for the value 0): all values are stored in variables: g.fillRect(x1, 0, bar, height); g.fillRect(0, y1, width, bar); Using expressions we can denote the positions of the colored fields in the drawing. The blue field to the left is located directly below the first bar. The y-coordinate of this filed is thus one bar-width bigger than the y-coordinate of the first bar: g.setColor(Color.blue); g.fillRect(0, y1+bar, …, …); Because the blue field is adjacent to the left edge, its width is equal to the xcoordinate of the first vertical bar. Even its height can be calculated using a formula: it is the difference of the y-coordinates of the two horizontal bars, minus own bar width. The blue filed can thus be drawn using: g.fillRect(0, y1+bar, x1, y2-(y1+bar) ); The red field at the top can be described in a similar way. Operators In int expressions you can use the following arithmetic operators: + addition - subtraction * multiplication / division % remainder after division For multiplication, an asterisk is used, because the usual mathematical symbols ( of ) are not on the keyboard. Leaving the multiplication operator out altogether, as is sometimes done in mathematics, is not allowed, as this would be confusing in the presence of multi-letter variable names. When using the division operator / the result is rounded, because the result of an operator on two int values is again an int value in Java. Round-off is done by leaving out figures after the decimal point; positive values are therefore always rounded “down” (and negative values always “up”). For example: the value of the expression 14/3 is 4. The unusual operator % calculates the remainder after division. The result of 14%3 for example is 2, and the result of 456%10 is 6. De result will always be a number between 0 and the right hand side operand. The result is 0 if the division is exactly possible. Operator precedence When more than one operator appears in an expression, the usual operator precedence holds: “multiplication precedes addition”. Thus, the result of 1+2*3 is 7, not 9. Addition and subtraction have equal precedence, and multiplication and the two forms of division as well. When operators of equal precedence are used in one expression, it is evaluated left to right. Thus, the result of 10-5-2 is 3, not 7. If you want to digress from these precedence rules, you can use parentheses in an expression, as in (1+2)*3 or 3+(6-5). In practice, expressions involving only constants are rare, because you could just as well have written the result (9 or 4) yourself. A superfluous pair of parentheses is allowed: 1+(2*3), and the compiler not even objects to exaggeration, as long as the parentheses are balanced: ((1)+(((2)*3))). Of course, expressions like this are harder to read for the human reader. 3.4 Program layout Comments For human readers of a program (a co-programmer, or you yourself a couple of months later, when you’ve forgotten about the details) it is very useful if the program is explained with comments. Comments are ignored by the compiler, but make the program easier to understand. In Java, there are two ways to write comments: put text between the combination of symbols /* and the subsequent */ (possibly a few lines further down) put text between the combination of symbols // and the end of the line 21 import java.awt.Graphics; import java.awt.Color; import java.applet.Applet; /* This applet draws a Mondrian-like "composition with red and blue” */ public class Mondri extends Applet { public void paint(Graphics g) { int width, height, bar, x1, x2, x3, y1, y2; // positions of the bars width = 200; x1 = 10; x2 = 50; x3 = 90; height = 100; y1 = 40; y2 = 70; bar = 10; // background g.setColor(Color.white); g.fillRect(0, 0, width, height); // black bars g.setColor(Color.black); g.fillRect(x1, 0, bar, height); g.fillRect(x2, 0, bar, height); g.fillRect(x3, 0, bar, height); g.fillRect(0, y1, width, bar); g.fillRect(0, y2, width, bar); // colored fields g.setColor(Color.blue); g.fillRect(0, y1+bar, x1, y2-(y1+bar) ); g.setColor(Color.red); g.fillRect(x3+bar, 0, width-(x3+bar), y1); } } Things that are useful to comment upon are: groups of statements belonging together, methods as a whole and the intention of the parameters, and even classes as a whole. It is not really helpful to rephrase the Java statement in English; you may assume that the reader understands Java. In the example program, the following comment is used: // positions of the bars x1 = 10; x2 = 50; rather than // make variable x1 equal to 10, and x2 to 50 x1 = 10; x2 = 50; During the test phase of the program, comment symbols may be used to disable statements temporarily. Remember to remove statements that are “commented out” from the final program. Distribution over lines There are no general rules for the distribution of Java source code over the lines of a file. The compiler enforces no strict rules. It is customary, though, to write each statement on a separate line. But when it improves readability, the programmer may decide to write more than one statement on one line (in the example program this is done with some relatively short assignment statements). For very long statements, on the other hand, it is a good idea to use multiple lines. Readability is improved furthermore by leaving a line blank once in a while, for example between methods, en between groups of statements (and their accompagning comments) that belong together. White space Also, for the placement of spaces there are no strict rules. The only place where they are strictly needed is between adjacent words: public static void may not be written as publicstaticvoid. In reverse, you may not add extra spaces in the midst of a word, for it would split the word in two. Text in quotes is always taken literally, including spaces. There is a difference between g.drawString("hello", 10, 10 ); g.drawString("hello", 10, 10 ); and Listing 3.1: Mondri.java 22 g.drawString("h e l l o ", 10, 10 ); Apart from these situations, extra spaces are allowed everywhere. Most programmers put extra spaces following each comma and semicolon (but not preceding it) left and right of the = sign in assignment statements at the start of lines, indenting bodies of classes and methods (normally, 4 positions) relative to the braces that delimit the class or method. Figure 3.1: The Mondri program running 23 4. New methods 4.1 Method definition Order out of chaos Drawing a square and two slanted lines on top of it makes a simple house. The example applet in this chapter draws three houses, as shown in figure 4.1. These three houses could have been drawn using the paint method below: public void paint(Graphics g) { // small leftmost house g.drawRect(20,60,40,40); g.drawLine(20,60,40,40); g.drawLine(40,40,60,60); // small house in the middle g.drawRect(70,60,40,40); g.drawLine(70,60,90,40); g.drawLine(90,40,110,60); // large rightmost house g.drawRect(120,40,60,60); g.drawLine(120,40,150,10); g.drawLine(150,10,180,40); } Despite the comments number numbness strikes us. For example, what numbers should be changes when on second thoughts the leftmost, rather than the rightmost house should have been the large one? To accommodate this change, you’d need to rethink all statements, and if you are not careful while doing so, there is a fair chance that one of the roofs ends up in the air. Besides, this program only draws three houses; extending the program to draw ten houses is downright boring. We are going to create some order out of this chaos, using methods. New methods Methods are intended to group statements that belong together, and treat them as a whole. When the group of statements need to be executed, you can do so by calling the method. 24 Figure 4.1: applet Houses running In the example, clearly the three statements that draw each house belong together (the call to drawRect and the two calls to drawLine). These three statements therefore are a candidate to group in a method: in the paint method, the will only be three calls to this newly defined method left. The setup is as follows: public class Houses extends Applet { private void drawHouse(…) { ….drawRect(…); ….drawLine(…); ….drawLine(…); } public void paint(Graphics g) { ….drawHouse(…); ….drawHouse(…); ….drawHouse(…); } } Two methods are defined: next to the obligatory paint, there is another method that draws only one house, which is why it is called drawHouse. The name may be chosen at will, and it is a good habit to choose the name descriptively. Whenever the browser needs to draw the applet, it calls paint. It is irrelevant whether the method is or isn’t the first method in the class: whatever is in the class, it is the method called paint which is called first. Only when the paint method does a call to the drawHouse method, the statements in the body of drawHouse are executed. After that, paint continues with the next statement, which happens to be another call to drawHouse – hence a second house is drawn. The third call in paint draws a house as well, and only after that program execution is continued on the location from which paint was called. Methods involve an object The program skeleton is complete by now, but there are some details to be filled in (indicated in the skeleton by ellipses). First, we consider the question: what is written in front of the dot in the calls to drawRect, in the body of drawHouse? Each method that is called involves a particular object (the paradigm is object oriented, after all); it is the object mentioned before the dot. For example, the drawRect method involves a Graphics-object. Up till now, we’ve been using the Graphics-object that was available as a parameter in the paint method (and that we’ve been calling g). That parameter, however, is not available in the body of the drawHouse method. Method parameters We need to make sure that in the body of the drawHouse method, a Graphicsobject is available as well. We can do so by providing drawHouse with a Graphics-object parameter. In the body, that object can be used as the object involved when calling drawRect and drawLine: private void drawHouse(Graphics gr, …) { gr.drawRect(…); gr.drawLine(…); gr.drawLine(…); } For a change, we called the parameter gr instead of g. After all, as a programmer you are free to choose the names of the parameters. Of course, when you use the parameter in the method body, you have to use the same name, so in the calls to drawLine we use the Graphics-object gr. The name of the parameter type can not be chosen at will: the object type Graphics is an existing library class, which may not be called PencilSet or whatever. Now that we have specified that drawHouse takes a Graphics object as its first parameter, we need to make sure that when calling drawHouse, indeed a Graphics object is provided. Luckily, the call to drawHouse is done in the paint method, which has a Graphics-object available: the object that the browser passes as a parameter to paint. The calls to drawHouse look as follows: public void paint(Graphics g) { ….drawHouse(g, …); ….drawHouse(g, …); ….drawHouse(g, …); } Method drawHouse is only called by paint, not by the browser (that is, not directly). Therefore, drawHouse can be declared to be a private method: it is for “internal” use only. Note that there is only one Graphics-object, that is called differently by the various methods: in paint, the object is called g, whereas in drawHouse it is called gr. Probably the browser called the object, before it passed it as a parameter to paint, by yet another name. Or maybe not… It doesn’t really matter: methods may choose themselves what the name of their parameters is, and callers of the method need not know these names. Method callers need only to make sure that the parameters they provide are of the right type. The this object The next detail to be filled in is the object in front of the dot in the calls to drawHouse. Which object is involved at this call? Which object is involved, for that matter, in executing the paint method itself? It is certainly not a Graphics-object. Graphics-objects, after all, only know draw… and fill… methods, not a paint method, let alone a drawHouse method. The object involved with method calls is of the object type that appears in the header of the class in which the method is defined. For example, method drawRect involves a Graphics object, because drawRect is defined in class Graphics. Now, as paint and drawHouse are defined in class Houses, they consequently involve a “Houses-object”. Such an object is created by the browser, after which the browser calls the paint method, involving that object. In the body of paint, we need to use the very same object to be involved with the call to drawHouse. But how do we address “the” object involved? That object is not a parameter, so we didn’t have the opportunity to give it a name in the method header. The solution is, that in Java the object involved with a method can be referred to using the word this. This can be written at each occasion where “the object involved” is needed. It comes in useful in the body of paint, because drawHouse involves the same method that is involved with paint: 25 public void paint(Graphics g) { this.drawHouse(g, …); this.drawHouse(g, …); this.drawHouse(g, …); } The word this is exclusively reserved for the purpose in Java. You may not use it as a name of a variable or things alike (as was neither the case with other reserved words, such as class, extends, void, public, private and int). In each method, this refers to an object. Its object type is the class mentioned in the header of the class in which the method is defined. 4.2 In search for parameters Parameters make methods more flexible The boring details –making sure that all methods can access the necessary Graphics- and Houses-objects– are filled in now, and the fun part starts: the hunt for the remaining parameters. Up till now, we assumed that the house-drawing statements (drawRect and two times drawLine) were the same in all three houses, and could therefore be executed with three calls to drawHouse. But the house-drawing statements are not exactly the same: for each house, the values passed as a parameter to drawRect and drawLine are different. Let’s have a look at the calls to drawRect in the initial (chaotic) version of the program: g.drawRect( 20, 60, 40, 40); g.drawRect( 70, 60, 40, 40); g.drawRect(120, 40, 60, 60); The first two numbers are the coordinates of the rectangle’s top left corner, the other two numbers are the width and height of the rectangle, respectively. Because we’re drawing squares, width and height are equal: 40 for the small houses, 60 for the big one. So the width (also acting as height) is not the same in the three instances. However, if the desired width were available as a parameter, we could specify the desired width in each call. The same holds for the coordinates: they being different for each call, we let the caller of drawHouse specify them, as well. For the caller, it is probably more convenient to specify the lower left corner rather than the upper left corner: the 26 coordinates of the upper corners are different for houses of different size, whereas the y-coordinate for houses in a row are the same regardless of house size. This can be settled for: we arrange that the y-coordinate specified is the position of the base line, and the y-coordinate of the top left corner can be calculated with an expression: private void drawHouse(Graphics gr, int x, int y, int w) { gr.drawRect(x, y-w, w, w); gr.drawLine(…); gr.drawLine(…); } public void paint(Graphics g) { this.drawHouse(g, 20, 100, 40); this.drawHouse(g, 70, 100, 40); this.drawHouse(g, 120, 100, 60); } The parameters for the two calls of drawLine (the coordinates of starting and ending points of the two lines forming the roof) are different for each of the three houses. However, it is not necessary to provide additional parameters for them, as the coordinates can be calculated form the position and size of the square, which are already available as parameter. The coordinates of the roof top point are needed twice: as ending point of the first line, and as starting point of the second line. To avoid doing the calculation twice, we use variables to store these coordinates temporarily. These variables are only needed in method drawHouse, and therefore declared locally: private void drawHouse(Graphics gr, int x, int y, int w) { int topx, topy; topx = x + w/2; topy = y - 3*w / 2; gr.drawRect(x, y-w, w, w); gr.drawLine(x, y-w, topx, topy); gr.drawLine(topx, topy, x+w, y-w); } Flexibility carried to extremes Now that we’ve decided to specify the lower left corner of the house (rather than the upper left corner of the wall), the y-coordinate turns out to be equal in all three calls to drawHouse, namely 100. In hindsight, the parameter turns out to be superfluous, as we could have written 100 in the body of drawHouse instead of y. However, it is no problem to have “too many” parameters: who knows, in the future we might wish to draw houses at another base line than 100, in which case the method would be prepared for the situation. The question is how flexible a method needs to be made by adding parameters. The method drawHouse as developed can only draw houses with a square facade. It is feasible to pass width and height of the facade as separate parameters, because we might want to draw non-square houses in the future, and we’d be prepared for the situation. And maybe the roof height should be specifiable as well, in order to draw houses with flat or steep roofs. And a color-object could also be passed as parameter, in order to be able to draw colored houses. Or make that two colors: one for the facade, and one for the roof… The price of all the extra parameters is that they have to be specified at each call. When the caller has no intention to use the degrees of freedom possible, the extra parameters are an unnecessary burden. Balance should be found between the expenses of extra parameters (both for the method writer and for the method caller), and the probability that the extra flexibility is needed in the future. Flexibility in the large The same dilemma occurs with programs as a whole. Users like flexible software, which can be configured to their needs. But when they need to enter long lists of options before they can use the program, they tend to be annoyed. Also, unnecessary options make the program complex and (therefore) expensive. It is easy to judge in hindsight, but could programmers have foreseen that in the future there would be a need for a 4-digit year rather than a 2-digit one? (Yes). Should we, in our present software, be prepared for a future decision to introduce a 13th month, each month having 28 days? (Well, no). Should the user of financial software be able to enter the current VAT rate? Or should the user, when the rate changes, be forced to buy a new version of the program? Should the software be prepared for a “medium” VAT rate, next to the current “low” and “high” rates? And that the currency unit might change? Or the currency unit symbol? In programs where time is involved, should the user be able to enter the day on which daylight saving time ends? Or should the rule for it (last Sunday of October) be built in in the program? And what if the rule changes (which happened a few years ago)? Or should the user be able to specify the DST-end date rule himself? If so, could/should he first choose the language in which to spell “October”? 4.3 Methods having a result Function calculation Using parameters, a method caller can specify values (int values as well as objects) to a method, to be used in the method’s body. However, this is one-way communication: the method can not “talk back” to the caller. Sometimes, this is needed nevertheless. Compare mathematics, where functions are calculated. You can “call” a method, such as square, by providing it with a parameter: square(5). The function calculates the result, which is 25 in this case. The answer is available to the function’s caller. In Java methods can, as can functions in mathematics, calculate a value to be used by the method’s caller. Just as in mathematics, functions can have multiple parameters, but only one result value. A result value may be a primitive value such as an int, but a result value may also be an object value. A method’s result type Analogous to parameters, method result values have a type. The result type might be int, if the method returns an integer number, but also e.g. the object type Color, if the method returns a Color object. The method’s result type needs to be specified in the method header, immediately in front of its name. For example, the header of a method square could look like this: private int square(int x) If the method does not have a result, the word void serves as result type. The return statement In the body of a method having a result, somehow the result should be described. Java has a special statement form for the purpose: the return-statement. The body of the square method has a single return statement as its body: private int square(int x) { return x*x; } The return statement consists of the word return (which is reserved for this purpose), followed by an expression. As are assignment statements, a return statement is terminated with a semicolon. 27 When a return statement is executed, the expression is evaluated, using the current values of variables and parameters. That value is returned as a result to the caller of the method. Preceding the return statement, other statements might appear, doing some preparations. For example, the following method calculates (x+1)3 given a parameter x: private int successorsThirdPower(int x) { int s; s = x+1; return s*s*s; } In theory, a return statement could appear in the midst of a method body; also, two return statements might appear in one method: private int strange(int x) { return x*x; return x*x*x; } The method is called “strange”, for what is it that this method returns: the square of the parameter, or its third power? At the first return statement the square is calculated and given back to the caller, and the method is terminated at the same time. The strange method never lives long enough to calculate any third power. Although it is allowed in theory to write return statements at other than the last line of a method, in practice this is never sensible: statements following the first return statement can never be executed. The compiler will warn you that the method contains “unreachable code”. The word “return” can be understood in two ways: “give back”: the value of the expression in the return statement is given back to the caller of the method; “go back”: after executing the return statement the processor goes back to the caller, regardless any possible statements following the return statement. Method calls Calls of methods with a result value differ form calls of methods whose result type is void. This is because the caller must accept the value that is returned, somehow. Calling a void method takes the form of a statement in itself, e.g.: g.drawString("hello", 10, 10); 28 Calling a non-void method, however, takes the form of an expression. The expression does not stand alone, but can be used in a statement, for example the right hand side of an assignment statement: k = this.square(5); But a call of the square method can also occur on other places where an int-value is needed, for example as a parameter of another method: g.drawString("hi", this.square(3), this.square(4)); You can use the result value of a method as part of a bigger expression. Finally, the big expression will be needed in a statement (be it an assignment statement, or call of a void method): p = this.square(4) + this.square(this.square(3)); // This applet draws three houses of various size public class Houses extends Applet { // draw a house with lower left corner (x,y) private void drawHouse(Graphics g, int x, int y, int w) { int topx, topy; topx = x + w/2; topy = y - 3*w / 2; g.drawRect(x, y-w, w, w); g.drawLine(x, y-w, topx, topy); g.drawLine(topx, topy, x+w, y-w); } public void paint(Graphics g) { int x, y; x = 20; y = 100; this.drawHouse(g, x, y, 40); x = x+50; this.drawHouse(g, x, y, 40); x = x+50; this.drawHouse(g, x, y, 60); x = x+70; } } Listing 4.1: Houses.java 5. External input 5.1 Parameterizing applets Method paint has a single parameter Up till now, each applet had the same output every time it is run. That way, an applet have no advantages for an HTML-page over an image, included with an <IMG>-tag. It would be an enhancement, if you could provide an applet with parameters: you could start the applet twice, with different parameters in each instance. It is not possible to add extra parameters to the paint method. The browser assumes that paint has exactly one parameter, being of type Graphics. When the header of the paint method is not exactly as expected by the browser (public, result type void, one Graphics object as parameter), it is not recognized, and nothing will happen when you run the program. It is possible, though, to provide an applet with parameters at the call from the HTML-file. We will make an applet again that puts “Hello!” on the screen; this time the message is personalized for a specific person, for example “Hello, Jeroen!”. The name to be used is specified in the HTML-file, and as such this program is an example of parameterizing an applet. Class extensions inherit methods To understand how this is done, we need to investigate the meaning of extends in the class header. A class header like class Houses extends Applet indicates that a number of methods follow, that involve Houses-objects. The phrase extends Applet means that Houses-objects in addition know all methods defined in class Applet. Using extends in the class header, you can extend existing classes: each Houses-object is an Applet-object as well: quite a special Applet-object in fact, that knows about some more methods. methods is needed for passing information from the HTML-file to the applet: the getParameter method. Multiple parameters can be passed to an applet. By calling getParameter more than once, you can get one at a time. The getParameter method takes as its (method-)parameter the name of the (program-)parameter that you want to get. As its method result it returns the value of the (program-)parameter requested. Both the name and the value of the program-parameter are texts. Texts play such an important role in Java programs, that there is a library class in which some methods dealing with texts are located. The class is called String; a String-object thus represents a text. Both parameter and result of getParameter are of type String. The String return as a result, can be temporarily stored in a variable, declared to have String type: String person; person = this.getParameter("firstname"); Now we have to make sure that the HTML-file indeed supplies a program parameter with that name. The HTML-tag <PARAM> serves the purpose, to be placed in between of the <APPLET>-tag and the </APPLET>-tag, as in: <APPLET code=Greeting.class width=200 heigth=50> <PARAM name=firstname value=Jeroen> </APPLET> The name of the program parameter (in this example: firstname), must be represented in the Java program as a string, and therefore appears in quotes: "firstname". The value which appears in the HTML-file following value= (in the example: Jeroen) is available to the Java program as the result value of getParameter. The situation is rather complicated, because two kinds of parameters are taking part of the scene: method-parameters and program-parameters. Especially, note that the name of a program-parameter (in the example: firstname) is not a Java-variable or –parameter, and thus needs not be declared in the program. The name of a program-parameter is treated as a text in the Java program, hence the quote marks in "firstname" in the Java program. Methods in class Applet It is worthwhile to learn about Applet’s methods: they can be used from each method of extension classes of Applet, using the object this. One of Applet’s 29 5.2 Class String Declarations of object references Using declarations, you can create variables, to which you can subsequently assign values using assignment statements. The value can be a constant, but also the result of a method call. Some variables are of primitive type, such as int: int x, y; x = 37; x = this.square(5); But some variables have an object type, like String: String s, t; s = "hello"; t = this.getParameter("firstname"); In the case of object variables, the variable in fact does not contain the object directly, but rather a reference to the object. In the assignment to variable t in the fragment above, not a copy is made of "Jeroen" (or whatever String is returned by getParameter), but a new reference is created to an already existing Stringobject. Assignments to variables having an object type always take the same (short) time, whether the object be large are small. Variables of object type always consume the same amount of memory (4 bytes). The same holds for parameters of object type: not the objects themselves are passed as parameters, but references to them are. String expressions For primitive values of type int, various expression forms exist: constants, variables, operator expressions (see section 3.3), and calls of methods having an int result (see section 4.3). Values of object type String can be denoted in different ways as well: constant texts in quotes, like "hello" variables declared as a String call of methods with String result use of operator + between two Strings The latter possibility is interesting: you can “add” two String objects, the result of the addition being a String object, consisting of the two texts joined together. Those two strings can themselves be constants, variables, results of method calls, or even of addition of two more strings. “Addition” may not be an apt word for this operation; “joining” is a better description. 30 All four expression forms occur in the final listing for the personal greeting, as appears in listing 5.1. 5.3 Utility classes Calculations involving rectangles As a more elaborate example of program parameters, we introduce another applet. The program is called Rectang, and calculates some properties of a rectangle (perimeter, area and diagonal length). The width and height of the rectangle are specified as program parameters to the applet. Furthermore, the program is an example of the use of a few important library classes. Listing 5.2 shows the complete program, and figure 5.2 shows the running applet. Class Integer Strings and int values are of different type. There is an essential difference between the number 37 (an int value) and the text "37" (a String value, which happens to contain digits only). The difference becomes clear when you try to add them: 37+1 is 38, whereas "37"+"1" is "371". In the example program width and height of the rectangle are provided as program parameters, which are String objects. Before we can use the values in calculations (area, for example, is width times height), we’ll have to convert the String objects to int values. Luckily, there is a library method that does the conversion. The method is named parseInt, and resides in class Integer. Class Integer is in package java.util, but you need not import this class, as all classes in package java.util are imported automatically. Class Math For more serious mathematical operations, a class Math exists, loaded with methods calculating all kinds of mathematical functions. Among others, the following methods can be found in class Math: abs absolute value sqrt square root sin sine of an angle (in radians) exp e raised to the power of… pow base raised to the power (base and exponent given as parameter) log logarithm Type double Next to type int, type double exists. This is a primitive type as well: no methods are applicable to double values. In a variable of type double you can store a number, which may have digits following the decimal point. Mathematical operators +, -, * en / can be used on double values as well as on int values. Only operator % can be used solely on int values. All methods from class Math mentioned in the previous paragraph have parameters of type double, and return a double result. Where a double value is needed, however, you may provide an int value. The value is converted automatically. The other way around is not possible, for you cannot convert a double to an int without loosing information (the digits following the decimal point). When using arithmetical operators, the result is a double value when at least one of the operands is. The result is only an int value if both operands are. Method parseInt from class Integer is static as well. It can be called as in: Static methods With all these library methods, you might ask what is the object that is involved. Consider for instance the square-root calculating method sqrt: it takes a double as parameter, returns a double result, but no further objects are involved. So what object should we write in front of the dot, in a call to sqrt? Writing this is no solution. When we want to calculate a square root from within paint, the word this is a denotation for the object that is involved with paint, that is an (extension of an) Applet-object, which is not a Math-object. Then, what is a Math-object supposed to be? The solution is that no Math-object whatsoever is needed. The methods in class Math are so-called static methods. Static methods, as opposed to normal methods, do not treat an involved object; the methods receive all necessary information via their parameters. Related differences are: in the method header, the word static appears in front of the result type; with method calls, you need not write an object before the dot; instead, you write the name of the class; in the body of the method you may not use the word this: after all, there is no object involved. A call to method sqrt could look as: "Dimension " + length String lengthAsText; int lengthAsNumber; lengthAsText = this.getParameter("length"); lengthAsNumber = Integer.parseInt(lengthAsText); Calculations with rectangles The rectangle program is straightforward now. First, the program parameters length and width are retrieved by calls to getParameter. Because they return String values, the results need to be converted to numbers by calls to parseInt. The numbers are used for the calculations. The sqrt method is used to calculate the diagonal, according to Pythagoras’ theorem. Finally, the results are drawn on the screen using drawString. The strings to be shown are built using the + operator, in its “joining” sense. Even when one of the operands of + is a number (int or double), strings are joined, as in the expression In situations like this, the number is automatically converted to string first. double diagonal; diagonal = Math.sqrt(2); 31 import java.awt.Graphics; import java.applet.Applet; public class Greeting extends Applet { public void paint(Graphics g) { String person, greeting; person = this.getParameter("firstname"); greeting = "Hello, " + person + "!"; g.drawString(greeting, 50, 20); import java.awt.Graphics; import java.applet.Applet; public class Rectang extends Applet { public void paint(Graphics g) { String lengthText, widthText; int length, width, perim, area; double diag; lengthText = this.getParameter("length"); widthText = this.getParameter("width"); } } length width perim = 2*(length+width); area = length*width; diag = Math.sqrt(length*length+width*width); Listing 5.1: Greeting.java <HTML> Here is an applet, that calculates some properties of rectangles: <BR> <APPLETcode=Rectang.class width=200 height=120> <PARAM name=length value=12> <PARAM name=width value=8> </APPLET> <HR> Here is the applet again, this time for another rectangle: <BR> <APPLET code=Rectang.class width=200 height=120> <PARAM name=length value=7> <PARAM name=width value=3> </APPLET> </HTML> Listing 5.2: Rectang.html 32 = Integer.parseInt(lengthText); = Integer.parseInt(widthText); g.drawString("RECTANGLE", g.drawString("Dimensions " +" times " g.drawString("Perimeter is " g.drawString("Area is " g.drawString("Diagonal is " } } Listing 5.3: Rectang.java 50, 20); + + + + + length width, perim, area, diag, 50, 40); 50, 60); 50, 80); 50,100); 6. User interaction 6.1 Interaction via objects A color mixer In the previous chapter the program was parameterized: by specifying values in the HTML-file, the behavior of the program could be influenced. Once started, the execution of the program was fully determined, and the user could do nothing but watch. In most programs, however, the user is enabled to actively influence the program execution. For this purpose, interaction components are available on the screen, such as buttons, sliders, editable texts, menus, etc. In this chapter we’ll study a program where the user can determine the color of plane using three sliders; thus the program is a color mixer. Furthermore, there is a button labeled “black”, which when pressed resets all sliders. The program is quite short, but many programming principles are discussed. The techniques used in this program are needed in every program interacting with a user. All interaction components (the three sliders and the button) are modeled in the program as separate objects. In the Java library there are classes for all standard interaction components, so we’ll certainly need to know about these classes. Four ways to acquire an object Here is a summary of ways to get an object. Up till now, we’ve seen three of them: the object is given as a parameter to a method (e.g., the Graphics parameter of paint); the object is returned as the result value of a method (e.g., the method getParameter, which returns a String object); the object is returned as the result value of an operator (e.g., the operator + which, given two strings, returns a string). There is, however, a fourth way to get access to an object: the object is created from scratch. 6.2 Creating new objects Constructor methods and new Java has a dedicated mechanism for creating brand new objects, without using other, already existing objects in the creation process. In most classes a so-called constructor method is available for the purpose, which can be used to create new objects of the class. When calling a constructor method, the following happens: somewhere in memory, space is reserved for storing the new object; the new object is treated by executing the statements of the constructor method, in order to put the object in some sensible state; the new object (or rather, a reference to it) is returned as the result of the constructor method. The constructor method always has the same name as the class (which begins, as an exception to the general rule for method names, with a capital). For example: the constructor method to create new Color objects is named Color, and the constructor method to create new Button objects is named Button. Calling a constructor method is done in a special way. The usual form of method call (object-dot-method name) is not possible, because there is not (yet) an object available to write before the dot: it is the object we are about to create! To call a constructor method, you write the word new, followed by the name of the constructor method, followed by possible parameters. Note that there is no dot between the word new and the method name. The word new is yet another reserved word, only to be used in this specific situation. Here are two examples of calls to constructor methods: new Color(0,0,0) new Button("press here") The new construction is an expression Calling a constructor method using new is a form of expression. The two examples above consequently are not statements (which you can also tell from the fact that they were not terminated by semicolons). It is an expression, because a constructor method always has a result value: the newly created object (or more precisely: a reference to it). The result value can be stored in a variable using an assignment statement. The variable needs to be declared first. Some examples are: 33 Color c; Button b; c = new Color(0,0,0); b = new Button("press here"); But you can also use the newly created object in different ways, for example as a parameter of a method expecting an object of that type: g.setColor( new Color(0,0,0) ); New color objects As can be seen from the examples above, the constructor method of Color takes three int values as parameter. The values must be between 0 and 255. They represent the amount of red, green and blue light in the new color. Imagine the color to be mixed from three colored spotlights (which works the other way around as you would expect from mixing paint). Examples of mixed colors are: new Color( 0, 0, 0) no light, i.e. black new Color(255, 0, 0) intense red new Color( 0,255, 0) intense green new Color( 0, 0,255) intense blue new Color(128, 0, 0) between black and red, i.e. dark red new Color(255,255, 0) red light plus green light gives yellow new Color(255,128, 0) between red and yellow, i.e. orange new Color(255,255,255) all spotlights on makes white new Color(128,128,128) between black and white is gray new Color(255,128,128) between red and white is pale red new Color(123, 37, 95) try and see… If you are not satisfied with the thirteen standard colors discussed in section 3.1 (Color.blue etc.), you can mix every shade you like calling the constructor method Color. In total, 256*256*256=approx. 16 million colors are possible, which is known as “true color” in hardware jargon. 6.3 The class Applet Redefining method paint In class Applet, there are a number of methods that are important for interacting with the user. 34 To start with, there is method paint, having a Graphics parameter. The method is defined in Applet, but has an empty body. Consequently, when the method is called, nothing happens. When you want to make an applet that does paint something, you need to make an extension class of Applet, in which you define the method again. This is what we did in all earlier examples. Remember that in an extension class, you can add new methods (like method drawHouse in class Houses), but also inherit all existing methods (see section 5.1). But you can also define a method again, which was already inherited. When the method is called, the statements in the body of this redefinition are executed. Redefinition of method paint in extension classes of Applet is a clever trick. The browser “naively” calls in method paint; by having redefined it, we’ve successfully trapped it, and now we are in control of the program… Calling paint indirectly via repaint Method paint (the redefinition, or if it was not redefined, the empty default definition existing in Applet) is called automatically by the browser each time the screen needs to be drawn. This is the case at the beginning of program execution, but also in situations where the window has been obscured by another window and is re-exposed. Sometimes, we would like to call paint from within the program. For example, when the user has used an interaction component, and in reaction to that the screen image needs to be modified. Unfortunately, you cannot call paint directly: it needs a Graphics object as a parameter, which is not easy to get. But the situation was anticipated. It is possible to call method repaint, available in Applet. This method magically obtains a Graphics object, clears the screen, and subsequently calls paint. If paint was redefined, it calls the redefined version, which is the desired effect. Redefining method init Apart from paint there is another Applet method called by the browser, namely the parameterless method init. Like paint, it has an empty body. It is meant to be redefined in extension classes of Applet. That way, we can set another trap for the browser. Method init is called only once by the browser when the applet is first created, even before paint is called first. This makes init the ideal place for statements that need not be executed each time the window is drawn, but only once. Creation of interaction components (calling new Button) is typically done in init. Calling method add In programs where a button is needed, method init can be redefined as follows: public void init( ) { Button b; b = new Button("press here"); However, this is not the full story. The button is present in memory after execution of this statement (reachable via object reference b), but this does not imply that the button is visible. In order to do that, it is necessary to call method add. That method is, like init, member of class Applet, and hence can be called involving the object this. As a parameter, add takes the interaction component that must be visualized: this.add( b ); } Interaction components thus added to the applet are automatically drawn, so you need not care about them in paint. 6.4 Interaction components Class Button In package java.awt (“abstract window toolkit”) there are a number of classes modeling interaction components. Class Button models “push buttons” shown on the screen. The program can be set up to execute designated statements when the user “presses” (that is: clicks with the mouse) the button. This way, the program can react on user actions. As described in the previous section, by calling the constructor method (also named Button), you can create new objects for every push button needed in the program. Two methods form class Button are of particular interest: Button: the constructor method setLabel: to modify the text appearing on the button Both methods take a string parameter, which is shown on the button as soon as it is added to the applet calling method add. Class Scrollbar Another interaction component we’ll use in the example program is Scrollbar. Scrollbars are often used at the edge of windows, to scroll the contents of the window. But it is also possible to use stand-alone scrollbars. Then, it acts like a kind of slider, using which the user can determine a value. Three methods of Scrollbar are of particular interest: Scrollbar: the constructor method, having no less than five parameters: Orientation of the scrollbar: horizontal or vertical. You can specify the desired orientation by using one of two constants available for this purpose in class Scrollbar: Scrollbar.HORIZONTAL or Scrollbar.VERTICAL. The initial value The amount of change when the user clicks next to the slider De minimum value (slider to the left) De maximum value (slider to the right) setValue: a method to change the value of the scrollbar, and hence the position of the slider. There is one int parameter, indicating the desired value. The value should be bounded by the minimum and maximum value as indicated when calling the constructor method. getValue: a method to retrieve the current value of the slider. The method takes no parameters, but does have a return value. Importing everything Programs often use many classes from the awt library. In principle, they should all be imported: import java.awt.Button; import java.awt.Scrollbar; etcetera. In practice, the list of imports tends to become long. Therefore, it is allowed to import all classes from a library in one go: import java.awt.*; You’ll also import the classes you don’t need, but normally the will do no harm (unless you’ve used one of the imported class names for other purposes). Constructing and using Scollbar objects In the example program, we’ll create three scrollbar objects, for letting the user control the amounts of red, green and blue light. Therefore, we declare three variables: Scrollbar red, green, blue; 35 In method init these variables are made to refer to newly created objects: red = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 255); gren = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 255); blue = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 255); By three calls to add they are made visible: this.add(red); this.add(green); this.add(blue); declared here are permanently part of the object; in fact, they are the object. That is why they are called object variables. The program skeleton is thus as follows: Class Mixer extends Applet { Scrollbar red, green, blue; public void init() { red = new ScrollBar(…,…,…,…,…); this.add(red); … } In method paint we will draw a colored rectangle with color as specified by the sliders. To know te position of the slides, we can call getValue: int rv, bv, gv; rv = red .getValue(); gv = green.getValue(); bv = blue .getValue(); Using the three values obtained we create a new Color object, to determine the color of the rectangle: Color c; c = new Color(rv, gv, bv); gr.setColor(c); gr.drawRect(0,0,100,100); Here, gr is the Graphics object available in paint. Object variables A problem arises with this approach: where to put the declarations of the three Scrollbar objects red, green en blue? These variables are needed in init (in the assignment statements, and as parameter of add). But they are also needed in paint (to request the current value of the sliders). If we would declare the variables in one of the two methods, the compiler will complain that they are not declared in the other method. But declaring them in both methods is not a way out: declarations are only valid locally, so variables declared in different methods are not related, even if they happen to have the same name. The solution is to declare the variables in neither of the two methods. Variables can also be declared outside the methods, that is directly in the class. Variables 36 // object variables public void paint(Graphics gr) { int rv; rv = red.getValue(); … } } Object variables can be inspected and modified in all methods. This is what we’ve been describing rather vaguely by saying that an object is “involved” with each method, or that a method “treats” an object. The only exception are static methods: they do not involve an object, or more precisely: the may not use object variables. Each object has its own object variables. These come into existence when the object is created, and remain available as long as the object is reachable via reference variables. It is a good habit to try and minimize the number of object variables: declare variables locally whenever possible. That way, you will be sure that methods don’t interfere with each other in an unwanted way. 6.5 User interaction Event: action by the user With the statements in init and paint as described, the program is ready to use, but is still doesn’t react on user actions, like pressing the button or sliding the sliders. An action of the user is known as an event. We’ll have to set up the program in such a way that it does event handling. Event listener: object which is informed The user generates events by manipulating interaction components. To react on that, an event listener needs to be associated to the interaction component. An event listener is an object that is notified when the event occurs. Associating an event listener to an interaction component is done by calling a method of that component. The name varies by component type: class Button has a method addActionListener class ScrollBar has a method addAdjustmentListener As a parameter, these methods get the event listener to be notified in the case of user events. Methods init will be as follows (assuming that a Button black and a Scrollbar red have been declared as object variables): public void init() { black = new Button("revert to black"); red = new Scrollbar(…,…,…,…,…); this.add(black); this.add(red); black.addActionListener(…); red .addAdjustmentListener(…); } The question remains what object will act as an event listener, and thus is passed as a parameter in the latter two calls. Event listeners are notified by a method call Interaction components, like buttons and scrollbars, notify their associated event listeners whenever the user triggers an event. The notification is done by calling a special method of the event listener: A button calls method actionPerformed of its action listener A scrollbar calls method adjustmentValueChanged of its “adjustment listener”. They trust that the event listeners indeed have the methods as described. How can we guarantee that? Announcing methods using implements Methods can be bundled in groups. This is no news; you know that such a group is known as a class. News is that method headers can be grouped as well. A group of method headers is known as an interface. Interfaces are needed to describe what an action listener is. The following is part of the Java library: interface ActionListener { public void actionPerformed(ActionEvent e); } in other words: ActionListener needs to know a method actionPerformed with an ActionEvent parameter and void result. In class headers you may promise that you will indeed define a method like that. Including the text implements ActionListener in the class header makes the promise. In the example program we’ll make the promise in the single class that we have (the header grows quite large, as it already contains the text extends Applet, but that’s no problem; if necessary you can split the header in two lines): public class Mixer extends Applet implements ActionListener { With this header, we promise that class Mixer will contain all methods as listed in interface ActionListener. In this case, there is only one such method, namely actionPerformed. Fulfilling the promise made by implements Promises need to be fulfilled. That is why in de body of mixer, we indeed define the method that was promised: public void actionPerformed(ActionEvent e) { This method is called when the user presses the button. How do we want to react? Right, by resetting the sliders to their initial position: red .setValue(0); green.setValue(0); blue .setValue(0); To update the screen according to the new situation, we force a call to paint by calling repaint (see section 6.3). This completes handling the action event. this.repaint(); } 37 Exploiting the promise made by implements Now that we have fulfilled the promise that Mixer implements ActionListener, any Mixer object may act like an action listener. The place where we needed an action listener was in the init method, as a parameter to addActionListener: public void init() { … black.addActionListener(…); } Method init is itself in class Mixer, which means that a Mixer object is involved. That object can be referred to by the word this. Therefore, this is acceptable as an action listener. Now the initialization of the button is complete: creation of the button, adding it to the applet, and associating an action listener with it: public void init() { … black = new Button("revert to black"); this.add(black); black.addActionListener(this); } Reacting to Scrollbar events Reacting to events the user triggers with one of the scrollbars is done in roughly the same way as reacting on button presses: In method init, the Mixer object this is associated as event listener to the scrollbars: red .addAdjustmentListener(this); green.addAdjustmentListener(this); blue .addAdjustmentListener(this); This is only allowed if Mixer objects indeed are able to behave as adjustment listeners should, so that’s what we promise in the header of class Mixer: public class Mixer extends Applet implements ActionListener, AdjustmentListener To fulfill the promise, we define the method adjustmentValueChanged which is listed in interface AdjustmentListener: public void adjustmentValueChanged (AdjustmentEvent e) 38 { } this.repaint(); Handling scrollbar adjustments is done by simply calling repaint. In turn, that will call paint, which determines current slider values using getValue. After scrollbar adjustments, at least one of the three values will have been changed, which is visualized by paint by drawing a rectangle of the color specified. The notion interface The notion interface is used in different contexts in computer science. On first sight the following three meanings bear no relationship: In hardware jargon, the connections at the rear of a computer are called the interface. A printer can be connected to the parallel interface, a mouse to the serial interface. The interface is the “face” the computer shows to peripheral components. In designers’ jargon, windows, buttons, dialogues, in short: interaction components shown to the user are known as the graphical user interface (GUI). The interface is the “face” the program shows to the user. In programmers’ jargon, the methods which can be called involving a particular object form its interface, also known as API (application programmer’s interface). The interface is the “face” a class shows to the programmer using the class. When reading close, in each of these three instances a “face” is shown by [the computer / the program / the class] to the outside world. The use of the same term is thus justified. Be aware, though, of the context of the term! import java.awt.*; import java.awt.event.*; import java.applet.Applet; // ...continuation of class Mixer public void paint(Graphics g) { int rv, gv, bv; rv = red .getValue(); gv = green.getValue(); bv = blue .getValue(); g.drawString("R=" + rv + " G=" + gv + " B=" + bv, 20, 40); g.setColor(new Color(rv, gv, bv)); g.fillRect(20, 60, 260, 220); } public class Mixer extends Applet implements AdjustmentListener, ActionListener { private Scrollbar red, green, blue; private Button black; public void init() { red = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 255); green = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 255); blue = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 255); black = new Button("Black"); this.add(red); this.add(green); this.add(blue); this.add(black); red .addAdjustmentListener(this); green.addAdjustmentListener(this); blue.addAdjustmentListener(this); black.addActionListener(this); } // to be continued... public void adjustmentValueChanged(AdjustmentEvent e) { this.repaint(); } public void actionPerformed(ActionEvent e) { red .setValue(0); green.setValue(0); blue .setValue(0); this.repaint(); } } Listing 6.1: Mixer.java (continued) Listing 6.1: Mixer.java 39 7. Iteration 7.1 The while statement Executing statements more than once Now that we have event listeners we can let the program react upon user actions, but after the associated method is completed, the program will be idle until the next user event. To keep the program busy for a longer time, many statements would be necessary, if there was not a statement that can execute a (or a few) statements repeatedly. This statement form is known as the while statement. An example of the use of a while statement is the following: public static void paint(Graphics gr) { int x; x = 1; while (x<1000) x = 2*x; gr.drawString("final value: " + x, 10, 10); } In this method there is a declaration, an assignment statement, then a while statement, and finally a call to the method drawString. The program fragment while (x<1000) x = 2*x; counts as only one statement, consisting of a sort of a header: while (x<1000) and a body: x=x*2; . The header consists of the word while followed by a parenthesized condition. The body is a statement in its own right: here it happens to be an assignment statement, but another possibility would have been a method call. When executing a while statement, the body is executed over and over again. This continues as long as the condition in the header remains true. That’s why it is called a “while” statement: the body is executed again and again while the condition holds. In the example, the variable x initially is assigned the value 1. That is certainly smaller than 1000, so the body is executed. In the body, the value of x is doubled; hence the value of x becomes 2. That value is still smaller than 1000, so the body is executed again, making x equal to 4. Again, this is smaller than 1000, so x is doubled resulting in 8, which is smaller than 1000, etcetera. The value of x will 40 become 16, 32, 64, 128, 256 and 512 in succession. The value 512 is still smaller than 1000, hence the body will be executed again, making x equal to 1024. But now, 1024 is not smaller than 1000, and so the iteration finally comes to an end. It is only now that the next statement will be executed: the call to drawString. As a result of that statement, the value of x after all the doubling (1024) will be displayed on the screen. Repeating more than one statement The body of a while statement may consist of more than one statement. In that case, the body is written in braces. For example, the following program fragment determines how often you can double 1 until it is bigger than 1000: int x, n; x = 1; n = 0; while (x<1000) { x = 2*x; n = n+1; } gr.drawString(n + " times doubled", 10, 10); Here, we use a variable n to count the number of iterations. Before the while statement, the count is zero, and hence the value of n is initialized to zero. Each time that x is doubled in the body of the while statement, we increment the value of n, so that it keeps track of the number of doubling actions. Hence, when the while statement is completed, the variable n holds the total count. Note two tings in these program fragments: The variables which are used in the body, need to be initialized (receive an initial value) before the repetition starts; The condition mentioned in the header had better to be dependent of a variable that is changed in the body: otherwise, the iteration would either immediately, or never be terminated. Repetition using a counter Variables counting the number of iterations are useful for controlling the continuation of the repetition. For example, using a counter, you can execute a statement exactly 10 times. The following code (part of the body of paint) will draw 10 smileys on the screen: int n; n = 0; while (n<10) { gr.drawString( ":-)", 0, 20*n ); n = n+1; } Apart from counting the iterations, the counter proves useful here to determine the position of the n-th smiley: the y-coordinates 0, 20, 40, 60 etc. can be calculated easily from n. Accumulation of a result Often, in a while statement some sort of result is accumulated. An example is the following method, which calculates the factorial of a number, which is passed as a parameter. (The factorial of a number is the products of all numbers between one and that number, e.g. factorial 4 is 1*2*3*4=24.) Apart from a counter, this method uses a variable result, in which the result is accumulated: private static int factorial(int x) { int n, result; n=1; result=1; while (n<=x) { result = result*n; n = n+1; } return result; } 7.2 Boolean values Comparison operators The condition in the header of a while statement is an expression, which when evaluated yields a truth-value: “yes” or “no”. The repetition is continued while the outcome of the evaluation is “yes”. In conditions, you can use comparison operators. The following are available: < less than <= less than or equal to / at most > bigger than >= bigger than or equal to / at least == equal to != not equal to These operators can be used on two numbers, ints as well as doubles. Left and right of the operator appear constants, variables, or full expressions using addition and multiplication etc. Note that the test for equality is written using two equal signs: this is necessary, because the single equal sign is already in use as the assignment operator. The difference is essential: x=5; x==5 statement expression make x to be equal to 5 ! is x equal to 5 at the moment ? Logic operators A condition is what is called in mathematical logic a predicate. Operators used in mathematical logic to combine predicates (“and”, “or” and “not”) can be used in Java as well. The funny symbols used in mathematics are unfortunately not present on the keyboard, so we’ll have to make do with more primitive representations: && logical “and” || logical “or” ! logical “not” The type boolean Expressions in which comparison operators are used, or in which comparisons are combined using logical operators, have a type of their own. The outcome of an expression is, after all, a value: one of the two possible truth-values “yes” or “no”. Logicians call these values “true” and “false”. You can use logical expressions in other contexts than just conditions in a while statement. A logical expression is nothing special really: it is just an expression with a particular type. Therefore, you can do anything with a logical expression that you can do with, say, an int-expression: store its value in a variable, or yield it as a method result. The type of logical values is known as a boolean. This is one of Java’s seven primitive types, so it is not an object type. The type was named after the British logician George Boole (see figure 7.1). Here is an example of the declaration of a boolean variable, and a subsequent assignment to it: boolean test; test = x>3 && y<5; 41 7.3 The for statement Shorthand of incrementing counters In bodies of while statements, especially when a counter is involved, frequently assignment statements occur which increment the counter value. This can be done using the statement: n = n+1; (As an aside: particularly in this situation, it would be unwise to pronounce assignment as “is”, because the value of n is not equal to n+1, but rather becomes equal to the (old) value of n+1.) Statements incrementing the value of a variable occur so frequently, that there is a special shorthand notation: n++; Figuur 7.1: George Boole (1815-1864) A more realistic example is a method having a boolean value as a result. For example a method that answers the question whether a number is divisible by 7: private static boolean isSevenFold(int x) { return x%7==0; } A number is divisible by 7 if the remainder after division by 7 is 0. De result of that expression is therefore the result of the method. The method can be used to, say, find the first 7-divisible number bigger than 1000: n = 1000; while ( ! isSevenFold(n) ) n = n+1; This example illustrates that the condition of a while statement need not necessarily be a comparison, provided that it has a boolean type. Conversely, conditions are not the only place where comparisons are useful: the can be used in other contexts where a boolean value is required. An adequate pronunciation for ++ would be “is incremented”. For increments with more than one there is another notation: n += 2; is shorthand for n = n+2; Counting automatically Many while statements use a counting variable, and have the following structure: int n; n = initialValue; while (n<finalValue) { do something useful, using the value of n n++; } Such a “counting repetition” is rather common. Therefore, a special shorthand notation is provided for it: int n; for (n=initialValue; n<finalValue; n++) { do something useful, using the value of n } The meaning of this so-called for statement is the same as that of the while statement above. The advantage is that the three things controlling the counter 42 (initial value, final value and the incrementing) are grouped neatly together in the header. This reduces the risk that you forget one of them. In cases where the payload (“do something useful”) consists of only one statement you may omit the braces, making the notation even more compact. 7.4 Special cases of iteration Zero repetitions It might be the case that the condition in the header is false right at the beginning. This is the case in the following: x=1; y=0; while (x<y) x++; In this case the body of the while statement is not executed at all, not even once. In the example, the value of x remains 1. Infinitely many repetitions A caveat when using while statements is that the might not end at all (the execution, that is – the body is, of course, finite). A statement with this behavior is easily written: while (1==1) x = x+1; The value of x is incremented forever. De condition 1==1 will remain true forever, and the statement is thus executed over and over again. In this program this behavior was probably intended, but often a while statement gets stuck because of a programming error: x = 1; count = 0; while (count<10) x = x*2; count = count+1; // erroneous! It was intended that the value of x is doubled ten times. Unfortunately, the programmer forgot the braces around the two statements that should form the body. This interpretation is suggested by the lay out, but the compiler pays no attention to lay out. Therefore, the only statement that is repeated is x=x*2; . As this does not change the value of count, it will never reach a situation where the condition is false. After finishing the while statement, the assignment count =count+1; would be executed once, but that statement is never executed, because the while statement takes infinite time. Of course, the programmer’s intention was: while (count<10) { x = x*2; count = count+1; } // correctly. It would be a waste to discard the computer that has gone into coma due to a forgotten pair of braces. Fortunately, there is a way to force termination of program execution, even when it is not yet completed. The way in which this is done differs with operating system. When you test your program using appletviewer you can just close the window. When using the Kawa development environment, you can choose “Stop Run” form the “Project” menu. Program execution is stopped immediately, and you can start looking for the error that made the program run infinitely long. As a rule of the thumb, when a program seems not to react, it is a good idea to investigate each while statement more closely. An infamous error is forgetting t increment the counting variable. Iterated iteration The body of a while statement or a for statement is a statement at its own. It can be an assignment statement, or a method call, or a group of statements bundled together using braces. But also, the body can be a while statement or for statement itself. For example: int x, y; for (y=0; y<10; y++) for (x=0; x<y; x++) gr.drawString("+", 20*x, 20*y); In this fragment, the variable y counts from 0 to 10. For each of these values, the body is executed, which happens to be a for statement as well, controlled by variable x. The upper bound of x is the current value of y. Therefor the “inner” repetition will last longer as y increases. The statement that is executed again and again, is the drawing of a plus-symbol at a position proportional to x and y. The result is a triangle shaped tableau of plus-signs: 43 + ++ +++ ++++ +++++ ++++++ +++++++ ++++++++ +++++++++ On the first row, there are no plus signs at all. The value of y is 0 at that moment, and hence the valued of x is varied from 0 up to, but not including 0; i.e., not at all. This zero-times-iteration nicely fits in the regular scheme. 7.5 Case study: interest calculation Interest on interest Many of the constructs discussed are used in the applet given in listing 7.1, which is shown running in figure 7.1. The applet allows the user to enter an amount of money and an interest rate, and will show how a capital () of debt () will build up in the forthcoming 10 years. Because of the “interest on interest” effect, there is not a fixed increment each year. Instead, the capital/debt increases with an increasing amount. Increment of the capital is modeled by the assignment statement: capital *= (1 + 0.01*interest); The operator *= used here means “is multiplied by”, just as += means “is incremented by”. It is a shorthand notation for capital = capital * (1 + 0.01*interest); For example, when the interest rate is 5, the capital is multiplied by 1.05. In a for statement this statement is executed eleven times, after each intermediate capital is drawn on the screen. Input via a TextField The user may enter the initial amount and interest rate. This can be done using interaction components of type TextField. Therefore, two TextField objects are created in method init. Because these objects are needed in paint as well, they are declared as object variables. Parameters of the constructor method are the text shown in the textfield initially, and the width of the textfield. As is the case for buttons, an action listener can be associated to a textfield. The user generates events by hitting the Enter key when the cursor is in the textfield. In 44 method actionPerformed we specified (by calling repaint) that method paint will be called as a consequence. In that method, we retrieve the values which were entered by a call to getText , and after that convert the Strings obtained to numbers (the strings are not even stored in variables first) using method parseInt. The calculation is done using the values thus obtained. import java.applet.Applet; import java.awt.*; import java.awt.event.*; public class Interest extends Applet implements ActionListener { TextField startText, rateTekst; public void init() { startText = new TextField("100", 8); rateText = new TextField("5", 4); this.add(startText); this.add(rateText); startText.addActionListener(this); rateText .addActionListener(this); } public void actionPerformed(ActionEvent e) { this.repaint(); } public void paint(Graphics gr) { int start, rate, year; double capital; start = Integer.parseInt( startText.getText() ); rate = Integer.parseInt( renteText.getText() ); capital = start; for (year=0; year<=10; year++) { gr.drawString( "After " + year + " year: " + capital, 10, 50+15*year); capital *= (1 + 0.01*rate); } } } Listing 7.1: Interest.java 45 8. Choice 8.1 The if statement Conditionally executing statements Statements are normally executed in sequential order. Using a while statement, you can repeat statements when necessary, but after that the next statement in the list is executed (provided that the while statement terminates). However, sometimes it is necessary that not all statements are executed, but that some statements are only executed in particular circumstances. The circumstances ultimately depend on the input the user has given. For example, suppose that a variable named temperature has a value, maybe because the user entered it by means of a textfield. Using a special statement form we now can let the program comment on the whether, but only if that is applicable: if (temperature<0) gr.drawString("It’s freezing!", 10, 10); The structure of this if-statement resembles that of a while statement: there is a header with a condition, and a statement forming the body. The statement in the body is only executed if the condition is true. An alternative part following else In some situations, it is desirable that another statement is executed in the case that the condition is not true. For this, the if-statement can be augmented with an elsepart, as in: if (temperature<0) gr.drawString("It’s freezing!", 10, 10); else gr.drawString("It’s thawing.", 10, 10); Note that the whole of “if+condition+statement+else+statement” is considered as one statement itself. The whole, with or without else-part, can thus appear for example as the body of a for-statement, without need for braces: for (n=1; n<20; n++) if (n%3==0) gr.drawString(n+" is divisible by 3",…,…); 46 else gr.drawString(n+" is not divisible by 3",…,…); Conditionally executing groups of statements If more than one statement needs to be executed conditionally, they can be grouped by braces, just as in the case of a while-statement, like in: if (temperature<0) { gr.drawString("It’s freezing,", 10, 10); gr.drawString("I’m cold!", 10, 25); } The statement following else can be a compound statement in the same fashion. Many alternatives When there are many value categories, a sequence of if-statements can find out which of the categories applies. The second test is placed following the else belonging to the first test, to have it executed only in the case the first category does not apply. Following the second test’s else-part may follow a third test, etc. The example below determines the railway fare to be paid by an individual having a given age. The result is placed in a textfield. which we assume to be available in the object variable tf. if (age<4) tf.setText("Free"); else if (age<12) tf.setText("Junior"); else if (age<65) tf.setText("Regular"); else tf.setText("Senior"); Each else (except for the last one) is followed by another if-statement. For infants, the text “Free” is showed, and the rest is skipped (because it follows “else”). For senior citizens, on the other hand, all tests are evaluated (less than 4? less than 12? less than 65?) before we can decide for “Senior”. Using indentation with tabs or spaces, it is shown in the program which else belongs to which if. For long sequences of tests, the screen might not be wide enough to accommodate for all those tabulations. As an exception to our habit of indenting sub-statements, we will use a different layout for sequences of ifstatements: if (age<4) tf.setText("Free"); else if (age<12) tf.setText("Junior"); else if (age<65) tf.setText("Regular"); else tf.setText("Senior"); After all, this layout is aesthetically pleasing as well, the alternatives being clearly discernable. Stop when found An if statement can appear in the body of a method. You could use it to conditionally execute a return-statement (by which a method makes its result available to the caller). Here is the railway fare example again, this time in the form of a method: private static String tariff(int age) { if (age<4) return "Free"; if (age<12) return "Junior"; if (age<65) return "Regular"; return "Senior"; } 8.2 Applications Determining which button is pressed In listing 8.1 and figure 8.1 (code and snapshot) a program is shown which draws a green circle to the screen. There are two buttons, labeled “shrink” and “grow”. Pressing these buttons, the user can shrink and grow the circle, provided that it would not become invisible or unreasonably large. Program structure is as usual for interactive programs: method init creates the interaction components, there is an action listener that forces a call to paint, and there is a method paint that draws the picture. Variables referring to the two button objects are declared as object variables, because they need be accessed in both init and in actionPerformed. Furthermore, an int variable radius is declared as an object variable. The radius is needed in all three methods: in init it is initialized, i.e. receives an initial value in paint it is used to determine the size of the circle to be drawn in actionPerformed it is incremented or decremented, depending on the button that was pressed. Because the return-statement immediately returns to the caller of the method, it is not necessary to write the second test in the else-part of the first. In cases that the first test is true, the second test will not be evaluated, because the method has already returned. In section 4.3 we remarked that it is senseless to have more than one returnstatement in a method body, because the code following the return-statement would be unreachable. In this case, however, multiple return-statements are sensible, because the first return-statement is not always executed, so the statements following it might be reachable in some circumstances. 47 For the decision whether to increment or to decrement the radius, an if-statement can be used. To know which button was pressed, we can inquire the parameter of actionPerformed. It is an object of type ActionEvent, which may be asked all kinds of details about the event the user caused. Most important detail is import java.awt.*; import java.awt.event.*; import java.applet.Applet; public class Circle extends Applet implements ActionListener { Button shrink, grow; int radius; public void init() { shrink = new Button("Shrink"); grow = new Button("Grow"); this.add(shrink); this.add(grow); shrink.addActionListener(this); grow .addActionListener(this); radius = 100; } public void actionPerformed(ActionEvent e) { if (e.getSource()==shrink && radius>10) radius -= 10; if (e.getSource()==grow && radius<150) radius += 10; this.repaint(); } public void paint(Graphics gr) { gr.setColor(Color.green); gr.fillOval(150-radius, 150-radius, 2*radius, 2*radius); } } Listing 8.1: Circle.java the identity of the interaction component the user touched. It can be retrieved by calling ActionEvent’s method getSource. By writing two if-statements, we test whether the “source” of the event is the “grow” or the “shrink” button. Part of the condition (using the “and” operator) is a check that the radius would not become too big or small, respectively. 48 Note that the == operator not only numbers, but also objects can be tested for equality. The operator != can be used on objects in the same way, to test whether two objects are not equal. However, it is not possible to use the ordering operators, like < and >= on objects. Password checking In listing 8.2 is the code that draws a nice picture, but only if the user has typed a password in a textfield. As object variables, we have a variable referring to the textfield, and two more variables: a string holding the secret key, and a boolean value open, indicating whether the “lock” has already been opened. In method init, the key string is assigned a value (which, being secret, we will not disclose here ), and variable open is assigned value false, because initially the lock is closed. Method actionPerformed is called when the user presses Enter in the textfield, after entering a password. In actionPerformed, we check whether the password entered is correct. If so, we assign true to variable open, to indicate that the lock is now open. In method paint, which is called indirectly via repaint, we inspect the value of open. Only if it is true, the drawing is made. As open is a boolean variable, it can appear as a condition directly: the condition, after all, needs to be a boolean expression: when its value is true, the first statement is executed, when it is false, the statement following else is executed. Note the special way the password entered is compared to the key. We retrieve the password form the textfield with password.getText(), and the key is available in variable key. The two strings are not compared, however, with if ( password.getText() == key ) // wrong! but with if (password.getText() .equals(key) ) // right. Should we’ve done the comparison using ==, we would have tested whether the two string expressions refer to the same object (i.e., to the same memory location). Here, that would always be false: key refers to a constant string, whereas the result of getText is a newly created string, which certainly resides in another memory location. Instead, we do the test using method equals from class String. This method compares the contents of two strings, character by character. The method has access to one string (because it is a method of class String); the second string is passed as a parameter. The result is a boolean value. In the code, the method calls are cleverly combined. We could have done the retrieval of the text, the comparison, and the check in the if-statement as successive steps: import java.awt.*; import java.awt.event.*; import java.applet.Applet; String entered; boolean ok; entered = password.getText(); ok = entered.equals(key); if (ok) public class Secret extends Applet implements ActionListener { TextField password; boolean open; String key; // this is… // more complicated… // than necessary! public void init() { password = new TextField(20); this.add(password); password.addActionListener(this); password.setEchoChar('*'); key = "secret"; open = false; } All these extra variables make the code long and tedious. Instead, we can treat the string that is the result of getText directly by method equals. The boolean value which is the result of that, can be used as the condition of the if-statement if (password.getText().equals(key) ) // concise. As a finishing touch we make the textfield into a real password field. By a call to setEchoChar it is arranged that while typing, the user sees only asterisks, as is common for password fields. Once the key is correctly entered, we can remove the textfield using method setVisible. public void actionPerformed(ActionEvent e) { if ( password.getText().equals(key) ) { open = true; password.setVisible(false); this.repaint(); } else password.setText(""); } Minimum/maximum thermometer In listing 8.3 a program is given that acts like a maximum/minimum thermometer. Using a scrollbar, the user can set a temperature. The maximum and minimum values that have ever been reached are shown on the screen. Also, there is a pushbutton labeled “Reset”. When it is pressed, maximum and minimum are reset to the current temperature. Central to this program is the if-statement in adjustmentValueChanged. Is the current value bigger than the maximum-up-till-now? Then the variable which holds the maximum needs to be adjusted! Variables minimum and maximum are needed in all three methods, and hence are declared as object variables. Also, the variable referring to the scrollbar is needed more than once and thus declared as an object variable. The button reference however, is only needed in method init, and hence can be declared locally. public void paint(Graphics gr) { if (open) { gr.setColor(Color.green); gr.fillOval(50,50,100,100); gr.setColor(Color.blue); gr.fillOval(81,85,8,8); gr.fillOval(111,85,8,8); gr.drawArc(75,75,50,50,225,90); } else gr.drawString("please enter password", 50, 50 ); } } Listing 8.2: Secret.java 49 import java.awt.*; import java.awt.event.*; import java.applet.Applet; public class Thermo extends Applet implements ActionListener, AdjustmentListener { Scrollbar meter; int maximum, minimum; public void init() { Button reset; reset = new Button("Reset"); meter = new Scrollbar(Scrollbar.HORIZONTAL,0,1,-50,50); maximum = 0; minimum = 0; this.add(reset); this.add(meter); reset.addActionListener(this); meter.addAdjustmentListener(this); } public void adjustmentValueChanged(AdjustmentEvent e) { int value; value = meter.getValue(); if (value>maximum) maximum = value; if (value<minimum) minimum = value; this.repaint(); } public void actionPerformed(ActionEvent e) { maximum = meter.getValue(); minimum = maximum; this.repaint(); } public void paint(Graphics gr) { gr.drawString("hoogste: " + maximum, 50,50); gr.drawString("laagste: " + minimum, 50,70); } } Listing 8.3: Thermo.java 50 8.3 Case study: Graph and zeroes of a parabola Description of the case In this section we’ll develop a more complicated program, in which both choice and iteration play an important role. Furthermore, interaction components with event-listeners are used, methods and parameter, local declarations and object variables. Also, we’ll see some aspects of handling double values. The program will draw a parabola, that is the graph of a mathematical function described by y = a x2 + b x + c, where a, b and c are constants to be supplied. In the applet the user can determine the constants by typing them in textfields, and immediately see the resulting graph. A classic subject of high school calculus is finding the zeroes of a parabola. These are given by applying the so-called abc-formula (named after the constants a, b and c which appear in it). The applet will use this formula to calculate the zeroes, and show them to the user. Conversion if double-values In the program, the constants a, b and c are declared as double variables: they are real numbers, possibly with digits following a decimal point. In the program they are not constant at all: the user can change them! But their value remains fixed during the drawing of one particular parabola. When dealing with double values, we need two things that we haven’t used before: conversion of strings to double-values, and conversion of double-values to intvalues. For the conversion from strings to int-values, there was a method parseInt: String s; int n; n = Integer.parseInt(s); Unfortunately, there is no analogous method parseDouble. There is, however, an indirect way to do the conversion. First, we use the static method getValue from class Double. The result is not a primitive double-value, but a Double-object (note the capital D in the type!). From that double-object, we subsequently call method doubleValue, by which we finally get the desired primitive double-value. String s; Double dobj; double d; dobj = Double.valueOf(s); d = dobj.doubleValue(); The extra variable dobj is not strictly necessary, because you can combine the two calls: the Double-object returned by valueOf can immediately be used to call the method doubleValue: d = Double.valueOf(s).doubleValue(); As for the conversion back, from double-values to strings: actually we’ve seen this before, because we’ve been using operator + for concatenating strings and doublevalues. By using a zero-character string as left operand of +, we can force conversion of the other operand from double to string: s = "" + d; The trick also works for conversion form int-values to strings. 51 // Parabool.java (continued) import java.awt.*; import java.awt.event.*; import java.applet.Applet; private void zeroes(Graphics gr) { double discriminant, denominator, root; discriminant = b*b-4*a*c; denominator = 2*a; public class Parabola extends Applet implements ActionListener { TextField abox, bbox, cbox; double a, b, c; if (denominator==0) gr.drawString("straight line!", 50,50); else if (discriminant<0) gr.drawString("no zeroes", 50, 50); else if (discriminant==0) gr.drawString("one zero: "+ -b/denominator, 50, 50); else { root = Math.sqrt(discriminant); gr.drawString("two zeroes: " + + (-b-root)/denominator + " and " + (-b+root)/denominator , 50, 50); } public void init() { a = 0.5; b = 2.0; c = -4.0; abox = new TextField(""+a, 8); bbox = new TextField(""+b, 8); cbox = new TextField(""+c, 8); this.add(abox); this.add(bbox); this.add(cbox); abox.addActionListener(this); bbox.addActionListener(this); cbox.addActionListener(this); } private void axes(Graphics gr) { gr.drawLine(0,250,500,250); gr.drawLine(250,0,250,500); } } public void actionPerformed(ActionEvent e) { a = Double.valueOf(abox.getText()).doubleValue(); b = Double.valueOf(bbox.getText()).doubleValue(); c = Double.valueOf(cbox.getText()).doubleValue(); this.repaint(); } private void graph(Graphics gr) { int xpixel, ypixel, oldy; double xvalue, yvalue, scale; scale = 0.03; oldy = 0; for (xpixel=-1; xpixel<500; xpixel++) { xvalue = (xpixel-250)*scale; yvalue = this.parabola(xvalue); ypixel = (int) (250-(yvalue/scale)); public void paint(Graphics gr) { this.zeroes(gr); gr.setColor(Color.red); this.axes(gr); gr.setColor(Color.blue); this.graph(gr); } double parabola(double x) { return a*x*x + b*x + c; } // see continuation… 52 if (xpixel>=0) gr.drawLine(xpixel-1, oldy, xpixel, ypixel); oldy = ypixel; } } } Listing 8.4: Parabola.java Structure of the program Object variables are declared for three textfields, in which the user can enter values for a, b and c. In method init, the textfield objects are created. Three more variables are declared for the values a, b, and c themselves. In method init they are assigned a default value; thus the user can view an initial parabola without having to enter values first. We’ll make sure that the double-values of a, b, and c always correspond to the values that are entered in the three textfields. For a start, when creating the textfields, the initial text which is displayed in this textfield are the string-conversions of the values of a, b, and c. Whenever the users presses “Enter” in one of the textfields, method actionPerformed is called (because actionPerformed is defined in class Parabola, which is announced in the class header by implements ActionListener, which makes that every Parabola-object behaves as an action-listener; in particular the object this does so, it being a Parabola-object, and can therefore be added as an action-listener to the three textfields). As a reaction, we modify the values of a, b, and c to make them correspond to the possibly changed contents of the textfields. Now that the values of a, b, and c are up-to-date again, actionPerformed calls method repaint, thus forcing a call to paint. Heart of the program is method paint: finding the zeroes, and drawing the graph. Because these are separate tasks, we define two separate methods for doing the job. A third method is defined for drawing the axes. This way, the method paint is relatively simple: what remains are only choosing the colors, and calling the three auxiliary methods. The auxiliary methods receive the Graphics-object, which paint has available, as a parameter, in order to be able to write things to the screen. Finding the zeroes In method zeroes we can concentrate entirely on finding the zeroes, without the fuss involved with handling textfields. We just assume that variables a, b, and c contain appropriate values. We can calculate the zeroes by applying the abc-formula, provided that they exist. In the formula, the square root is taken from b 2-4ac, so if that value happens to be negative, there are no (real) solutions. Furthermore, a division occurs by 2a, so if that value is 0 special actions have to be taken. Using if-statements, all these cases are distinguished, and an appropriate message is written to the screen in each case. Drawing the graph For drawing the graph, the idea is that for each possible x-value we calculate the corresponding y-value. At that location, we could place a small dot: for (xpixel=0; xpixel<500; xpixel++) { ypixel = this.parabola(xpixel); gr.fillRect(xpixel, ypixel, 1, 1); } However, in places where the graph is rather steep, and y-coordinates of adjacent x-coordinates differ more than the dot thickness, the dots will not be connected. Therefore, instead of drawing a dot we’d better draw a line from the preceding point. For that purpose, the y-coordinate of the previous point is kept in variable oldy. An if-statement makes sure that for the first point no line is drawn, because at that moment, there is not yet a “previous” point. We need not to keep the old x value, because the x-coordinate of the previous point is of course x-1. for (xpixel=-1; xpixel<500; xpixel++) { ypixel = this.parabola(xpixel); if (xpixel>=0) gr.drawLine(xpixel-1, oldy, xpixel, ypixel); oldy = ypixel; } The compiler is not yet satisfied with this program, and insists in correcting the error that “variable oldy may not have a value when used”. This is because the compiler is aware of the fact that in the body of the while-statement, the variable oldy seems to be used before it is assigned a value to. The compiler is not smart enough to see that the if-statement is there to prevent this situation. To satisfy the compiler, we write an extra assignment to oldy at the beginning of the whilestatement. Scaling The interval [0,500] is not the most interesting range to study parabolas for the more common values of a, b, and c. More interesting would be the interval [-7,7]. That is why, in the program, calculation is not done with the value of xpixel, but rather with a translated and scaled version of it. The resulting y-value is scaled back, translated back, and corrected for the upside-down Java coordinate system. The details of this process are easier to describe using formulas than words, which is exactly what is done in the program. So check out listing 8.4 for the details… 53 9. Objects and classes 9.1 Class: description of an object Object: named group of variables An object is a group of variables belonging together. You may not be consciously aware of the fact, but each time you create a new object using the new keyword, you create a group of variables. For example, when you create a new button with the expression new Button("press here"); a group of variables comes into existence that does the necessary bookkeeping for managing a button: recording its size, position on the screen, color, status (pressed or not), the identity of the corresponding action-listener, the caption, etc. Mostly, you are not dealing with these variables directly: to be able to use a button object in your program, you don’t even need to know of which variables a button object is composed, and what their names are. It is however important to know which methods are available that can manipulate the object. For a button, available methods are amongst others: setLabel (to modify the caption) and addActionListener. Via these methods you can indirectly modify the member variables of the object, without explicitly assigning values to them. Class: declaration of variables plus method definitions A class definition is a description of the objects of that class. Therefore, a class definition consists of two parts: a declaration of the member variables of the object a definition of the methods by which the object can be manipulated. This holds for the classes in the standard libraries, but also for classes that you define yourself. For example, when the definition of your class Thermo begins as follows: class Thermo extends Applet { private Scrollbar meter; private int min, max; then every Thermo object that comes into existence will be composed of three variables: a reference to a scrollbar object, and two integer numbers. Moreover, as it is an extension of Applet, each Thermo object contains all variables that were 54 declared in Applet. Such a Thermo object is created by the browser, whenever it encounters a web page containing (the byte code) of this class as code. When writing the code of the program, you might have the illusion that of each of the variables declared in the class, only one is present. Indeed, that is the case within one object, but it may occur that the browser creates two instances of the same class. For example, this will happen when in the HTML file, there are two <APPLET> tags with the same code. We have seen an example of that in section 5.3, where in one HTML-file two instances of Rectang-applets were created. Each of these applets has its own group of variables. Objects and object references To get some idea of what happens in memory when objects are created, we’ll investigate in detail what happens when the program Thermo (the minimum/maximum thermometer from section 8.2, listing 8.3) is run. When processing the <APPLET> tag the browser creates a Thermo object. The browser possesses a reference, which will point to the new object. The Thermo object consists of the variables that were declared in the class: two integer numbers named min and max, and a reference to a scrollbar object, named meter. But as Thermo is an extension class of Applet, the Thermo object also consists of the variables that were declared in the Applet class. Next, the browser calls method init. In that method, a local variable is declared: a reference to a Button-object, named reset. Local variables are in memory as well, but not as part of an object. Hence, you can visualize the situation in memory which is occurs at this point as: reset browser’s current applet this Thermo inherited declared meter by us min max Method init has access to an object via the pseudo-variable this. The expression this can be thought of as a kind of variable that is automatically present in each method, the difference with a normal variable is that assignment to it is neither needed nor possible. In the scheme above, it is not shown of which variables are inherited from Applet; you need not know this for library classes. Note that the local variable reset, and the variable meter that is part of the Thermo object, are references to objects, which at the moment do not yet refer to any object. This will happen only when the assignment statements in the body of init are executed: reset meter max = min = = new Button("Reset"); = new Scrollbar(Scrollbar.HORIZONTAL,0,1,-50,50); 0; 0; After that, the situation is as follows: Button The next two statements in method init are calls to method add, using the two newly created objects as a parameter: this.add(reset); this.add(meter); Method add is defined in class Applet. Although the pointer this refers to a Thermo object, but part of that object (the part that was inherited) is a fully equipped Applet object. It is that (part of the) object that is treated by add. Method add is part of the standard library, so we cannot know what variables are modified by add, as we don’t know what variables are part of the Applet object. So in order to visualize what is going on, we’ll have to do a bit of guessing. The idea is that an Applet object will keep a copy of the object references that were passed as a parameter to add, to have them available for later use. The situation will be roughly as follows: (In reality, the row of variables almost certainly is not named added. However, we neither can, nor need, nor should want to know what the real name is). Button reset browser’s current applet reset this Thermo Scrollbar browser’s current applet this Thermo Scrollbar added meter min 0 max meter 0 min The four variables to which an assignment has been done, now have a value. In the case of min and max, this is an int-value, in the case of reset and meter, it is a reference to a newly created object. The new objects contain variables, which are declared in the library classes Button and Scrollbar respectively. In the scheme, the details of that are not shown. In a method, you may use both local variables (in this case: the variable reset) and object variables of the object that is reachable via this (here: meter, min and max). In a situation where the name of a local variable happens to coincide with the name of an object variable, the local variable has precedence. 0 max 0 In the final two statements in the body of init, event listeners are added to the button and scrollbar objects: reset.addActionListener(this); meter.addAdjustmentListener(this); This time, not the this object, but two other objects are treated. Again, we can only guess what will happen exactly. However, it is reasonable to assume that in both cases, a copy of the parameter is stored for later use. If at a later moment the 55 button is pressed, the button “knows” which object is its action-listener, that is: of which object the method actionPerformed should be called in response. As these were the last statements of method init, now the local variables are not present any more: the local variable reset, and also the pseudo-variable this are disposed of. The situation that is created after the call to init is completed is as follows: Button browser’s current applet actListener Thermo Scrollbar added adjListener 0 value meter min 0 max 0 Note that the local variables are disposed of, but not the objects to which they referred. These objects are still reachable indirectly, via the copies of the references to them, that were created by the calls to method add. Programs with multiple classes Up till now, we’ve been writing programs consisting of just one class. This class was an extension of Applet in all examples, and was written with the intention that the browser would create (at least) one object of that class. More objects were used in the example programs, but their type was a library class in all cases (Button, Scrollbar, Graphics, String etc.). In the next application this will change. Apart from the, by now well known, extension class of Applet, we’ll define some more classes. In the program, we’ll use objects having these classes as their type. This way, we can use “home brew” objects of our own design in the program. 56 9.2 Application: moving particles Description of the case The program that we’ll develop is a simulation of moving particles in a confined space. You may think of molecules in a bottle, or of balls on a (frictionless) billiard table, or of rats in a cage. For ease of drawing, we will display the particles as small colored circles, and the space as a large rectangle. In the running program, three spaces will be visible, each having different dimensions. In each space, three particles are present, having different colors. The particles can move: they’ll do one “step” whenever the user presses a button labeled “Step”. Moreover, there is a button “Start”. When it is pressed, the particles will start moving continuously, which is perceived by the user as an animation. The caption of the button is changed to “Stop”; by pressing the button again, the user can pause the animation. Figure 9.1 shows a snapshot of the running program. Class Space In the simulation’s window (see figure 9.1) five things are visible: three spaces containing particles, and two buttons. It would be sensible to create the five things in the same fashion: by creating new objects, which are subsequently added to the display. For the buttons, this is no problem, but of course, there is no library class Space. We can however define the class ourselves. We need not do that from rock bottom: instead, we can extend an existing class named Canvas. A Canvas is an interaction object, just as Button and Scrollbar are. As such, it can be added to an Applet using add. A Canvas object is like an artist’s canvas: you can make a drawing on it. A Canvas object has a size and a background color of its own, so we do not need to worry about that. In class Space, which is an extension of Canvas, we need only to declare extra variables needed for the colored particles, and methods that let them move. Class Particle Each moving particle in this program has a set of properties: a color, a position in a space, and a direction of movement. For each of these properties, we can declare a variable: the color is a reference to a Color object, the position consists of two int values x and y, and the direction of movement can be described by the distances dx and dy to travel at each step. Each particle has its own color, position and direction. That’s why we define a class Particle, in which these variables are declared. Having done that, in the main program we can create a Particle object for each particle, in which the variables are bundled. Class Particle is not an extension of any existing class. It is defined entirely by ourselves. Each Particle object consists solely of the variables that we declare in the class; no variables are inherited. Design of the classes The program will consist of three classes: the usual extension of Applet that we’ll name Simulation this time, and the auxiliary classes Space and Particle. First, we’ll investigate the variables that are needed; the methods are dealt with later. class Simulation extends Applet { Button step, auto; Space s1, s2, s3; // methods still to be written } class Space extends Canvas { Particle p1, p2, p3; // methods still to be written } class Particle { Color color; int x, y, dx, dy; // methods still to be written } We now need to create objects having these classes as their type, thus creating a network of references. The desired situation can be depicted as follows: Simulation Button browser’s current applet stap auto r2 r1 Space d1 Button r3 Space d2 d3 d1 Space d2 d3 d1 d2 d3 x x x x x x x x x y y y y y y y y y dx dx dx dx dx dx dx dx dx dy dy dy dy dy dy dy dy dy color color color color color color color color color Particle Particle Particle Particle Particle Particle Particle Particle Particle The browser creates a Simulation-object. It consists partly of variables, which are inherited from Applet, and partly of the variables that are declared in class Simulation: step, auto, r1, r2 and r3. It’s the job of method init to let these variables refer to newly created objects. As usual, this is done by means of assignment statements: step auto r1 = r2 = r3 = = new Button("Step"); = new Button("Start"); new Space(…); new Space(…); new Space(…); As a consequence, the variables step and auto will refer to newly created Space-objects. These Space-objects partly consist of Canvas variables, and partly of the three variables that were declared in class Space: d1, d2 and d3. However, these variables do not yet refer to objects. 57 The constructor method of Space When a new-expression is evaluated, two things happen: memory is allocated for the new objects the constructor method, as defined in the class, is called. All statements appearing in the constructor method are thus automatically executed when a new object is created. Therefore, the constructor method is the natural place to initialize object variables and do other prepatory work. There are two differences between a constructor method and other methods in a class: the name is the same as the name of the class, and thus begins with a capital letter a constructor method does not have a result type, not even void. This is because a constructor method can only be called by way of the special newexpression, and therefore has always the newly created object as a result. In our class Space we’ll write a constructor method, in which a newly created Space object is initialized. First task of the constructor method is to set the size of the Space. This is done by way of a call to setSize, which was inherited from Canvas (not that Space is an extension of Canvas!). When calling setSize, the width and height are specified. Because the three Space objects need to have different dimensions, we’ll pass the dimensions as a parameter to the constructor method of Space. When creating a Space object (in Simulation’s init method), the dimension can be chosen by passing the corresponding values. So, this is the beginning of the constructor method of class Space (note that there is not a result type between public and the method name): public Space(int width, int height) { this.setSize(width, height); Next thing to do is to set the background color. This can also be done via a method that was inherited form Canvas: setBackground. Because the color of all three canvases is the same, we need not pass it as a parameter to the constructor method. We can just call: this.setBackground(Color.lightGray); Now it’s time to initialize the object variables: de references to Particle objects d1, d2 and d3. The objects they’ll refer to are newly created by: 58 d1 = new Particle(…); d2 = new Particle(…); d3 = new Particle(…); So, the three Particle objects are created immediately during initialization of each Space object. Class Particle has a constructor method of its own. We therefor shift our attention to the methods of class Particle. De methods of class Particle Class Particle doesn’t extend anything. A Particle objects just consists of the variables declared in the class: x, y, dx, dy and color. The question is what kind of things we’d like to do with such a Particle object, in other words: which methods are needed? Some things which are necessary are: a constructor method for initializing the object variables a method setPos, which sets the position variables x and y of the particle a method setDir, which sets the direction of movement dx and dy a method doStep, which changes the position of the particle, thus letting it move in the direction indicated by dx and dy a method draw, which draws the particle on a Graphics-object, which is supplied as a parameter. Simplest method is setPos. It takes two parameters, specifying the desired location; in the method body the corresponding object variables are changed: public void setPos(int x0, int y0) { x = x0; y = y0; } Analogously, method setDir sets the direction variables dx and dy. Method draw takes a Graphics object, so that it is able to draw things. Before drawing the circle, we set the color that is stored in the Particle object: public void draw(Graphics gr) { gr.setColor(color); gr.fillOval(x-3, y-3, 7, 7); } Here, we trust that variable color holds a color value. The place to initialize that variable is the constructor method by giving the constructor method a Color parameter, each particle can have its own color. Method doStep is the most interesting one. By calling it the Particle will be able to move. Because both the current position and the direction of movement are stored in the Particle object, it doesn’t need any parameters. Movement is done by incrementing the position variables x and y with the corresponding values dx and dy: public void doStep() { x += dx; y += dy; Now it may be the case (when dx or dy is negative) that the coordinate become negative. In that case, the particle needs to bounce off the wall. The coordinate will become positive again, but also, the direction of movement is inverted. For example, when bouncing to the left wall, the direction of movement changes from leftbound to rightbound. Bouncing to the left and upper walls is handled with the following code: if (x<0) { x = -x; dx = -dx; } if (y<0) { y = -y; dy = -dy; } Bouncing to the right and bottom walls is more difficult. For testing whether the x coordinate grows too big, we need to know the size of the space in which the particle moves. However, this cannot be determined form the object variables (x, y, dx, dy and color) alone! There are two solutions to this problem. Either we can pass the necessary information as a parameter to doStep, or we do so once in the constructor method, and store that information in additional object variables. In the program, we take the latter approach. We pass a reference to the Space-object to which the Particle belongs as a parameter of the constructor method. Then, we’re able to call a method, which Space inherited form Canvas: public Particle(Space r, Color k) { color = k; maxx = r.getSize().width; maxy = r.getSize().height; } We’ve introduced an object variable maxx, which comes in useful for testing wheter the right wall was been hit in doStep: if (x>=maxx) … It is left as an exercise to the reader to see what assignment is needed to handle the bouncing (the solution is in listing 9.3). Methods of class Simulation Class Simulation is only used for the creation of a single object, but nevertheless is important. The object models the complete applet, and thus is similarly to other programs that we’ve seen: there is a method init for doing the initializations, and an event-listener to handle user actions. A method paint is not necessary, as nothing is drawn on the background of the applet. The two buttons and the three canvas objects draw themselves and their contents. Part of method init has already been discussed: creation of the five objects to which the object variables should refer: public void init() { step = new Button("Step"); auto = new Button("Start"); r1 = new Space(100,196); r2 = new Space(196,150); r3 = new Space(60,75); When creating the Space objects, we pass the necessary width and height, as required by the constructor method of Space that we defined. As usual, next step is to add to the applet’s user interface: this.add(step); this.add(auto); this.add(r1); this.add(r2); this.add(r3); The applet as a whole will act as an action listener for both buttons: step.addActionListener(this); auto.addActionListener(this); } 59 The object this may only act as an action listener if we promise that ability in the class header (using implements ActionListener), and fulfill the promise: public void actionPerformed(ActionEvent e) { if (e.getSource()==step) this.doStep(); else … } For clarity, we’ve put the proper handling of pressing the Step-button in a separate method. Th name of that method is doStep (not to be confused with the method in class Particle bearing the same name!). In Simulation’s doStep method, we could call the Particle’s doStep methods. However, we’re not going to do that for each of the nine particles. Instead’, we’ll delegate that work to the three Space objects, by calling a method which is (again!) named doStep. Let’s not forget that we need to write it later… private void doStep() { r1.doStep(); r2.doStep(); r3.doStep(); The method is defined to be private, as it is only called by one of its fellow methods, and not directly from some other class. After the three Space objects have moved their respective particles, the new situation needs to be made visible. Therefore we call method repaint, which the Space objects have inherited form Canvas: r1.repaint(); r2.repaint(); r3.repaint(); } Methods of class Space What remains to be done is the writing of the methods in class Space. We already wrote part of the constructor method: public Space(int width, int height) { this.setSize(width, height); this.setBackground(Color.lightGray); Also, the three Particle objects are created. By now, we know what parameters are needed for Particle’s constructor method: a Space-object and a Color-object. For 60 the Space object only this is a sensible choice. For the Color parameter we choose some nice colors: d1 = new Particle(this, Color.red); d2 = new Particle(this, Color.green); d3 = new Particle(this, Color.blue); Now the particles do have a color and know the size of their bounding spaces, but they also need to have a position and a direction. We call the methods responsible for that, immediately after the particles are created: d1.setPos(30,40); d2.setPos(100,80); d3.setPos(200,60); d1.setDir(10,10); d2.setDir(5,-10); d3.setDir(8,2); } Still on our wish list is to include a method doStep in Space: after all, we call it from method doStep in Space. It is quite simple: we just call the doStep method of the three particles. public void doStep() { d1.doStep(); d2.doStep(); d3.doStep(); } Final method which needs to be written in class Space is paint. This is a redefinition of the default method (which does nothing) in Canvas. As we’re used to in Applet, paint is automatically called when the canvas needs to be drawn. Also, calls can be forced by calling repaint, which we did in Simulation’s method doStep. Drawing the canvas is actually quite simple: we just call the method draw that is present in Particle, to draw all three particles: public void paint(Graphics gr) { d1.draw(gr); d2.draw(gr); d3.draw(gr); } Now the program is almost complete. What remains is handling the “auto” button, which deserves a chapter of its own. 9.3 Animation Automatic movement Continuously pressing the Step button, the user can keep the particles moving. It would however be easier if this could be automated, so that the user can sit back and relax while seeing the particles moving. In other words: we want the program to behave as an animated movie. Making animations is quite easy in Java (as opposed to other languages like Basic, Pascal, C and C++). Actually, we need only a few extra lines in the program. But be careful not to loose thread, because the mechanism is subtle! Class Thread At the core of the animation mechanism is a class named Thread in package java.util. When you want to do animation, you’ll need an object of that class: a Thread-object. When constructing a Thread, you need to pass an object as a parameter: this is a common choice. Thus, we proceed as follows: Thread animation; animation = new Thread(this); After the object is created, you can start the animation by calling method start : animation.start(); The Thread-object will react with calling method run from the object that was passed as a parameter during creation of the thread. How can it be so sure that a method run indeed exists for that object? That’s simple: the compiler will check that the object is of a class that implements Runnable. So, we’ll promise that in the header, and fulfill the promise by indeed defining a method run. Now why do we take all this trouble? Why not just call that run method directly: this.run(); We could have done so, but there is an important difference between the direct and the indirect way of calling run: the Thread object calls run, but does not wait until it’s finished. So method start puts run to work, but immediately returns afterwards. So from that moment on, two things happen simultaneously: method run is executed, but also the statement following the call to start is executed. We’ll exploit this fact, by keeping method run busy for a long time: public void run() { while (1==1) this.doStep(); } Instead of the always true expression 1==1 we could even have used the boolean constant true). This way, doing the steps is done automatically. Method sleep But wait a second, now the animation runs so quickly that you can hardly see it. We’d better pause for a short time after doing a step. This can be done by calling the static method sleep from class Thread. As a parameter, we pass the number of milliseconds the pause should last. So, our new version of run is: public void run() { while (true) { this.doStep(); Thread.sleep(50); } } The 50 millisecond pause makes sure that a step is performed 20 times per second, resulting in a smooth animation. Calling sleep should be done in a try-catch statement The compiler will object to a call to method sleep as described above: “exception must be caught”. It happens to be the case that in exceptional cases the pause can be interrupted before the full time has elapsed. In this program that will not happen, because we don’t use the possibility. Nevertheless, the compiler insists that we be prepared for the situation. Handling exceptional cases (exceptions) is done in a so-called try/catch statement. This is explained in detail in a later chapter. For now, suffice it to say that the call to sleep should be done from the body of a try-statement, with a corresponding catch-part having an empty body. You can use the following code: try { Thread.sleep(50); } catch (Exception e) 61 { } As opposed to the bodies of while- and if-statements, here the braces around the body of try are obligatory, even if there is only one statement. Controlling the animation The Thread-mechanism is initiated when the user presses the button labeled “Start”: public void actionPerformed(ActionEvent e) { if (e.getSource()==auto) { animation = new Thread(this); animation.start(); To enable the user to stop the animation at a later stage, we change the caption of the button when the animation is started: auto.setLabel("Stop"); When the button is pressed again, it should of course be handled differently. So we need to revise the body of actionPerformed. We’ll use a boolean variable moving, and make sure it has value true while the animation is running. It is declared in the class, and initialized to false in method init. Now handling the button is done as follows: public void actionPerformed(ActionEvent e) { if (e.getSource()==auto) { if (moving) { // stop the animation moving = false; auto.setLabel("Start"); } else { // start the animation moving = true; animation = new Thread(this); animation.start(); auto.setLabel("Stop"); } } else … } 62 Merely setting a variable to false will not stop the animation. But we can change the method run for doing so. Instead of running eternally, we can check the value of our boolean variable carefully each time a step is done: public void run() { while (moving) { this.doStep(); try {Thread.sleep(50);} catch (Exception e){} } } Normally, the condition of a while-statement will not change if no assignments to it are done in the body of the while-statement. However, here we are in a situation where to processes run in parallel, influencing each other: while the animation is running, the user can press a button, which will set the value of moving to false. The value null Using an assignment statement you can let object variables refer to actual objects. In some circumstances, you might want to undo the reference, that is let the object variable refer to no object at all. For this purpose, there is a special constant: null. This is a neutral value for object references; it is the value all object variables have before an assignment to them is made. The nice thing is that you can test whether or not a variable has value null. Using that, we can revise our program so that a boolean variable moving is not necessary. Instead, we’ll use the variable animation that we need anyway. It is the reference to the Thread-object. We’ll make it equal to numm whenever the user presses the Stop button. To know whether the animation is still running, we can test the value of variable animation for non-null-ness. So here is the final version of the button handling method: public void actionPerformed(ActionEvent e) { if (e.getSource()==auto) { if (animation!=null) { animation = null; auto.setLabel("Start"); } else { animation = new Thread(this); animation.start(); auto.setLabel("Stop"); } } else this.doStep(); } See listing 9.1 for the revised version of run, which checks continuously wheter animation is non-null. import java.awt.*; import java.awt.event.*; import java.applet.Applet; // continuation of Simulation.java public void actionPerformed(ActionEvent e) { if (e.getSource()==step) { this.doStep(); } else if (e.getSource()==auto) { if (animation==null) { animation = new Thread(this); animation.start(); auto.setLabel("Stop"); } else { animation = null; auto.setLabel("Start"); } } } public class Simulation extends Applet implements ActionListener, Runnable { Space r1, r2, r3; Button step, auto; Thread animation; public void init() { r1 = new Space(100,196); r2 = new Space(196,150); r3 = new Space( 60, 75); step = new Button("Step"); auto = new Button("Start"); animatie = null; this.add(r1); this.add(r2); this.add(r3); this.add(step); this.add(auto); stap.addActionListener(this); auto.addActionListener(this); public void run() { while (animatie!=null) { this.doStep(); try { Thread.sleep(50); } catch (Exception e) { } } } private void doStep() { r1.doStep(); r2.doStep(); r3.doStep(); r1.repaint(); r2.repaint(); r3.repaint(); } } } // continued… Listing 9.1: Simulation.java 63 import java.awt.*; import java.awt.*; class Particle { int x, y, dx, dy, maxx, maxy; Color color; class Space extends Canvas { Particle d1, d2, d3; public Space(int b0, int h0) { this.setSize(b0, h0); this.setBackground(Color.lightGray); d1 = new Particle(this, Color.red); d2 = new Particle(this, Color.green); d3 = new Particle(this, Color.blue); public Particle(Space r, Color k) { color = k; maxx = r.getSize().width; maxy = r.getSize().height; } public void setPos(int x0, int y0) { x = x0 % maxx; y = y0 % maxy; } d1.setPos(30,40); d2.setPos(100,80); d3.setPos(200,60); d1.setDir(10,10); d2.setDir(5,-10); d3.setDir(8,2); public void setDir(int dx0, int dy0) { dx = dx0; dy = dy0; } } public void doStep() { d1.doStep(); d2.doStep(); d3.doStep(); } public void doStep() { x += dx; y += dy; if (x >= maxx) { x = 2*maxx-x; } else if (x<0) { x = -x; } if (y >= maxy) { y = 2*maxy-y; } else if (y<0) { y = -y; } dx = -dx; } dy = -dy; Listing 9.3: Space.java dy = -dy; } public void draw(Graphics gr) { gr.setColor(kleur); gr.fillOval(x-4, y-4, 9, 9); } } Listing 9.2: Particle.java 64 public void paint(Graphics gr) { d1.draw(gr); d2.draw(gr); d3.draw(gr); } dx = -dx; 10. Inheritance 10.1 Subclasses Subclass: defining additional variables/methods Using class libraries can save you quite a lot of work. Even the worse it is, if there is a library class that does almost what you want, but not exactly so. Or you’ve written a class like the one you need before, but you want to make a slight enhancement. How to proceed? A solution that was used before object oriented languages existed (that is, in languages like Pascal and C) was to make a copy of the original program, and modify the copy where needed. At least, that saved the author a lot of typing. However this “cut and paste” strategy had a disadvantage: when the original program was improved (e.g., bugs fixed, or efficiency improved) after the copying had been done, the improvements had to be carried out separately in the copy. And if the copy in turn was copied again, the improvements have also to be made in the third generation copies. In short, a version management problem exists: various versions are hard to manage together. A small improvement in the original program (for example: storing years using four digits instead of two) may thus grow into a multi-billion Euro project. So what is a better way to build upon past work? When you want to extend existing classes, rather than to make a copy you should just state that you want to write an extension. In Java, this is done by mentioning “extends” in the header of the class, followed by the name of the class that you want to extend. We’ve seen many examples of this: class Hello extends Applet class Simulation extends Applet class Space extends Canvas {…} {…} {…} extends generates subclasses The extended class is also known as a subclass of the original class. Conversely, the original class is referred to as the superclass of the extension. A class can have only one (direct) superclass, but it can have many subclasses. For example, class Applet has both Hello and Simulation as its subclasses, but Hello has only one superclass (namely Applet). However, you can again extend the extended classes, thus creating a sub-subclass of the original class. The sub-subclass has one direct superclass, but indirectly is a subclass of its super-superclass. The whole can be thought of as a “genealogy” of classes. Objects of a subclass can also be regarded as objects of its superclass. For example, a Space-object (as defined in chapter 9) is a valid Canvas-object as well. Quite a special Canvas object indeed, namely one that shows colored particles! In mathematical jargon: a Space-object is a special case of a Canvas-object. Inheritance of methods and variables Objects of a subclass may use the methods of its superclass, and of the superclass thereof, etc. Those methods are said to be inherited from the superclass. The same holds for the variables that were declared in the superclass: they are also present in each object of the subclass. This is why, you can call method add in subclasses of Applet, even if add is not defined in the subclass. The method is known in Applet, and is inherited by all its subclasses. When you look for methods in the manual, you should realize that they may be inherited from other classes, and thus do not appear in the class where you’d expected it. For example, method add does not appear in the manual of class Applet: it is defined in Applet’s super-superclass Container. Inheritance can also be important in your own classes. Suppose that you’ve defined a class Ball. A Ball object has a position and a diameter. In the class, methods setPos, grow and draw are defined: class Ball { int x, y, diam; void setPos(int x0, int y0) { x = x0; y = y0; } void grow() { diam++; } void draw(Graphics gr) { gr.fillOval(x,y,diam,diam); } } Later, you might want to extend the class in a class ColorBall, in which the class is extended with a variable color and a method to set the color: 65 class ColorBall extends Ball { Color color; void setcolor(Color c) { color = c; } Once you have an object of class ColorBall, you can call method setColor, but also methods setPos and grow. After all, each ColorBall is a Ball as well, and for the growing process it doesn’t matter whether or not the Ball possesses a color. The converse is not possible: if you’ve just a Ball object, you cannot call setColor, because a Ball has no color component (unless it happens to be a ColorBall, but you cannot be sure of that). Redefinition of methods With the inheritance of method draw by class ColorBall a problem arises. A ColorBall needs to be drawn in a different way than an ordinary Ball: for the drawing, the color is important. For situations like this, it is allowed to define a method again in a subclass, a so-called redefinition. So, in class ColorBall we can redefine method draw: void draw(Graphics gr) { gr.setColor(color); gr.fillOval(x,y,diam,diam); } } The not-yet-extended object super It is a pity that, as soon as you decide to redefine a method, you need to rewrite the entire body. For example, in the redefinition of draw, the call to fillOval had to be copied form the original version. Now for one statement this is not that hard, but it is a nuisance if the drawing would have been complicated (for example, when drawing houses instead of balls). And what’s worse: a version management problem is introduced. What we’d like to do is to call the original method using this.draw(gr); However, that is not possible, because it would call the redefined method, which calls the method again, and again, and again… 66 As a way out, Java provides a special constant named super. It is like the constant this, but with a difference: super refers to the current object, as if it has the superclass as its type. So the redefinition of draw can be done as follows: void draw(Graphics gr) { gr.setColor(color); super.draw(gr); } 10.2 Class hierarchies Extends: “is a” Using subclasses we can try and categorize the world. If you read “extends” as “is a”, you can feel whether your class hierarchy has the right design. Here is a hierarchy of some means of transportation: class Transport class Vehicle extends Transport class Plane extends Transport class Boat extends Transport class MotorVehicle extends Vehicle class Bicylce extends Vehicle class MotorBoat extends Boat class SailBoat extends Boat class SteamBoat extends MotorBoat class Car extends MotorVehicle class Van extends Car For each class in this hierarchy, you can define the relevant variables and/or methods. For example, the variable numberOfWheels typically belongs in Vehicle and not in Transport, as boats have no wheels. Variable altitude belongs in Plane, and boolean variable hasBell in class Bicycle. The hierarchy can be presented nicely in a chart: In creating the hierarchy, you take all sorts of design decisions. It is a matter of taste which is the “best”. In the example, we first subdivided the means of Transport according to the medium in which they move: air, land or water. Only then the subdivision according to motorization is made. We could have done it the other way around. For some classes the position n the hierarchy is not obvious: is a MotorCycle a special kind of icycle (viz., a motorized one) or a special kind of MotorVehicle (viz., one with only two wheels)? However, there should be no doubt which is the superclass and which is the subclass. A Van is a Car, but a Car is not (always) a Van. Hence, Van is a subclass of Car. Similarly, a Bicycle is a Vehicle, but not every Vehicle is a Bicycle. Object-variables: “has a” Not every chart showing a hierarchic relationship is a class diagram! When dealing with object there is another hierarchy involved: which objects are parts of other objects. For example, in the Simulation program of chapter 9: Chart Title browser's current Simulatie stap Button auto Button r1 Space d1 Particle d2 Particle r2 Space d3 Particle d1 Particle d2 Particle r3 Space d3 Particle d1 Particle d2 Particle d3 Particle This diagram ought to be interpreted differently. Here, lines should be read form top to bottom, and read as “has a”. The diagram shows the relationships between objects that are created in a particular program, but it is not a class diagram. After all, a Particle is not special kind of a Space (but it is a part of it). The boxes in this diagram are not classes, but objects. That is why (objects having the same) class can occur more than once in the diagram, which is another difference with class diagrams, where that is not the case. 10.3 Class hierarchies in Java libraries Interface components All classes in Java packages are ordered in hierarchies, just as in the Transport example. All interaction components of which a graphical user interface is built are (directly or indirectly) a subclass of class Component. Methods that are applicable to any component are defined in class Container. Examples are setBackground (to change the background color) and getBounds (to query the size of the component). Methods that are only applicable to components Component Button Panel Applet Container Canvas Label Window Frame TextComponent TextArea Scrollbar TextField Dialog FileDialog where the user can type text, like getText, are defined in class TextComponent. Methods that address the subdivision of texts into lines are however placed in TextArea, because they are not applicable to TextField objects. Event-listeners To react on user inputs, you can add event listeners to components. The type of event listener may vary among components: a Button and a TextField may have an ActionListener, but a Scrollbar has an AdjustmentListener. There are differences between an ActionListener and an AdjustmentListener, but they also have things in common. That’s why they are organized in a hierarchy. Informally we have already done so, by referring to them as “event listeners”. Indeed, that’s the name of the superclass: EventListener ActionListener AdjustmentListener ComponentListener MouseListener More precisely, an EventListener is not a class, but an interface: the methods are not defined in it, but they are merely wish lists, to be implemented by other classes. Nevertheless, they can be ordered in hierarchies. (The boxes are marked with double-line borders to indicate the distinction). 67 Events An eventlistener puts other objects to work by calling methods like actionPerformed and adjustmentValueChanged. As a parameter, an object is passed to these methods specifying some details about the event. The type of that object varies, but always have something in common: they describe an “event”. That’s why there is a hierarchy of event-describing objects types: EventObject AWTEvent ActionEvent AdjustmentEvent ComponentEvent InputEvent KeyEvent FocusEvent MouseEvent Object Everything in one hierarchy The higher up in the hierarchy, the more general is the description of a class. It is hard to describe a class high in the hierarchy without being vague. Try and describe what a Component is, without resorting to examples (“things like buttons and scrollbars and the like”). It is a nice pastime to look for similarities between seemingly unrelated classes. What do String and Component have in common? And an Image and an ActionEvent? Not that much, but in any case: all of these are descriptions of an object. Reason enough to give them a superclass in common, which is simply called Object. Methods in class Object are of a very general nature. To mention two: clone: makes an exact copy of the object toString: converts the object to a readable String Method toString is automatically called when you try to use operator + between a String and an object. That’s why in many classes toString is redefined, in order to easily show objects to the user. 68 String Component EventObject Image 11. Strings and Arrays b.addActionListener(this); } In the method called upon user actions, we grab the text form the TextArea, count the number of symbols, and show the result on the TextField: 11.1 Strings and characters Class TextArea We’ve been using TextField objects for enabling the user to type in values. However, in a TextField you can enter only one line. For multiple-line input, you can use a TextArea object. You can use a TextArea both for input and for output. The following methods are available in TextArea: TextArea(int r, int c): creates a TextArea having the indicted number of rows and columns void setText(String s): make a string visible on the TextArea String getText(): get the text which was entered by the user as a string void append(String s): append a string to the text already present on the TextArea void setEditable(boolean b): specify whether or not the user is allowed to change the text. As opposed to TextField, a TextArea object cannot have an ActionListener. For a TextField, an action event occurs when the user hits the Enter key. For a TextArea, the Enter key will proceed to the next line, but not generate an action event. To let the user indicate that he completed the text, therefor often a Button is used in conjunction to a TextArea. Example: how many characters typed? As an example, we show a program that enables the user to type in a text in a TextArea. Whenever the user presses a button, the length of the text is shown in a single-line TextField. The class header of an Applet-extension should be familiar by now, so we’ll proceed to the init method, where the interaction components are created: public void init() { input = new TextArea(5, 40); output = new TextField(40); b = new Button("Tel"); this.add(input); this.add(output); this.add(b); public void actionPerformed(ActionEvent e) { String s; int n; s = input.getText(); n = s.length(); output.setText("you typed " + n + " symbols"); } More interesting would be to not only show the number of characters, but also the number of words. For that, we need not only inspect the string’s length, but also its individual characters, in search of spaces. For that, we need some more string methods. Class String In class String, amongst others, the following methods are available: int length(): determines a String’s length String substring(int x,int y): selects the part of a string between two positions, and returns that part as a result String concat(String s): concatenates the string with another string, and returns the joined string boolean equals(String s): compares a string to another on character by character char charAt(int n): determines which symbol is at a particular position Calling substring, you can select part of a string, for example the first five letters: head = s.substring(0,5); output.setText(head + " is the head of the string"); Numbering of characters in a string is rather peculiar: the first character appears on position 0, the second at position 1, etc. The parameters of substring are the position of the first desired character, and the position of the first character that is next to the end of the selection. So the call s.substring(0,5) returns the characters at position 0, 1, 2, 3 and 4; in other words: the first five characters. You can get the first character of a string by: 69 String initial; initial = s.substring(0,1); The result is a string of length 1. However, there is another way to retrieve single characters form a string: calling method charAt. The result of that is not a string object, but a primitive value of type char. As a primitive value, it can be stored in a variable directly: char first; first = s.charAt(0); An advantage of char over strings which happen to have length 1, is that you can test characters for equality using operator ==, whereas for strings you need to call method equals. Primitive type char Just like other primitive values, you can store char values in variables, pass them as parameters to methods, yield them as result value of methods, make them part of an object, etc. There is a special notation to denote constant char values in a program: you just type the desired symbol, enclosed in single quotes. Confusion with string constants in not possible, as they are enclosed in double quotes, as in: char asterisk; String railroad; asterisk = ’*’; railroad = "####"; In single quotes, precisely one symbol must appear; in double quotes, you may write many symbols, but also one or no symbol at all. History of char The number of different symbols representable in a char has increased in history (and in different programming languages): In the 1970s one thought that 26=64 different symbols would be enough: 26 letters, 10 digits and 28 punctuation marks. Alas, there was no room for differentiation between capitals and lower case. In the 1980s one used 27=128 different symbols: 26 capitals, 26 lower case, 10 digits, 33 punctuation marks and 33 special symbols (newline, tab, beep, etc). It was known as ASCII: de American Standard Code for Information 70 Interchange. Useful for Americans, but not for Françaises, Deutsche Mitbürger, and people form España and the Fær-Œr isles. In the 1990s a coding scheme with 28=256 symbols was introduced by ANSI (American National Standards Institute), later adopted by ISO (International Standards Organization). However, there was no representation for Greek and Cyrillic letters, the Indian Devangari alphabet and Japanese Kanji-symbols. In the 2000s the symbols set was extended to 2 16=65536 different symbols, which will do for a while. The coding scheme is known as Unicode. The first 256 symbols coincide with ISO-coding, which remains valid therefore. In Java Unicode is used for characters. You cannot display all the new characters on all hardware, but at least we are prepared for the future. Quote symbols When using strings and chars, be careful not to forget to write the quotes. When you omit them, the text is not interpreted as literal text, but as Java code. And there is a big difference between the literal string "hello" and the variable name hello the literal string "boolean" and the type name boolean the literal string "123" and the int value 123 the literal char-value ’+’ and the addition operator + the literal char-value ’x’ and the variable name x the literal char-value ’7’ and the int value 7 Special char values Special values are, being special, not representable in the standard way. For some special symbols therefore a special notation is in used, involving a reversed slanted line (backslash): ’\n’ for newline ’\t’ for tabulation This introduces a new problem: how to denote a backslash that is really a backslash? This is done by doubling the backslash: the first backslash means that something special follows, the next thing is the special thing. This also solves the problem of how to represent the quote symbols themselves: ’\\’ for the backslash ’\’’ for a single quote sign ’\"’ for a double quote sign Although there are two glyphs between the quotes, they represent only one symbol. public void actionPerformed(ActionEvent e) { String s; s = input.getText(); Doing arithmetic with char Unicode-symbols are ordered: each symbol has an ordinal number. For example: the ordinal number of ’A’ is 65, of ’a’ it is 97. Note that the ordinal number of ’0’ is not 0, but 48. Also the space is not numbered 0, but 32. The symbol having code 0 is a special symbol having no visual representation. You can determine the ordinal number of a char by assigning a char value to an integer variable: For the counting, we need to inspect each individual character of the string. We’ll count the number of spaces and newlines; after all, that’s what separates words. Therefore we declare two variables that will hold the respective counts, and one variable by which we can indicate a position: char c; int i; c = ’*’; i = c; int spaces, lines, position; Using a for-statement we can let variable position indicate all possible positions in the string. Note that the count starts at 0, and continues up to (but not including) the length of the string: spaces = 0; lines = 0; for (position=0; position<s.length(); position++) or directly: i = ’*’; This is always possible; after all, there are only 65536 different symbols, where an int can hold values up to 2 billion. For the conversion back form int to char, you need to guarantee that the value is in range. This is done by writing the parenthesized word (char) before the value: c = (char) i; This way, you can do arithmetic with characters: the symbol following ’z’ is (char)(’z’+1), and the capital version of c is the c-’A’+1-th letter of the alphabet. This “range guaranteeing”-notation is known as a cast. We’ve been using it for converting double values to int values, agreeing that the value would be truncated if non-integral: double d; int i; d = 3.14159; i = (int) d; Example: counting words Now we can adapt the previous example program, to show not only the number of characters typed, but also the number of words. To start with, we again grab the text that was entered from the input TextArea: In de body of the for-statement we get a character by calling charAt, and check whether it is a space or a newline: { if (s.charAt(position)==’ ’ ) spaces++; if (s.charAt(position)==’\n’) lines++; } Finally we can show the result to the user: output.setText( spaces+lines + " words\n" + "on " + lines + " lines" ); } Note that we use the newline symbol "\n" to split the output on two lines. 11.2 Arrays Array: many variables having the same type In the next section we’ll write a program which not only counts frequencies of spaces and newlines, but of every letter of the alphabet. Hence the output will be something like “23 A’s, 7 B’s, 3 C’s, 8 D’s …”. Instead of having two counters for holding the number of spaces and newlines, we could declare 26 variables for holding the counts for each individual letter. The declaration is rather large: int as, bs, cs, ds, es, fs, gs, hs, is, js, ks, ls, … 71 and what’s worse: in the body of the for-statement we need 26 if-statements for checking against all letters: if if if if (s.charAt(position)==’a’ (s.charAt(position)==’b’ (s.charAt(position)==’c’ (s.charAt(position)==’d’ ) ) ) ) as++; bs++; cs++; ds++; // etcetera… Fortunately, there is an easier way to declare many variables. You can create a row of numbered values, each addressable using its sequence number. Such a row is known as an array. Creation of arrays An array has many characteristics of an object. First, you need to declare a variable that will hold the reference to the array. For an array holding int-values, the declaration looks like: int [] table; The square brackets indicate that we are not dealing with a single int-variable, but rather with an array of values. The variable table is not the array itself: it is a reference variable, which might refer to an array in the future. table To indeed let the variable refer to an array, we need an assignment statement. As with object, we need a new-expression for creating the object. The notation differs slightly form objects: after new, the type of the elements follows, and their number in brackets: table = new int[5]; The situation in memory is now as follows: table 5 length 0 1 2 3 4 72 The array object created consists of an int-variable storing the length, and a sequence of numbered variables of the desired type (which in this case happens to be int as well). The numbering starts at 0, and that’s why the last number is one less than the length. Using array values You can assign values to the component values of an array by mentioning the reference variable, followed by the index number in square brackets: table[2] = 37; in the same way, you can use array elements in an expression: x = table[2] + 5; In short, array elements can be used as any variable. The variable length, which is also an attribute of an array, can also be used in expressions, for example if (table.length < 10) … You can, however, not change the length. Once created, the length of an array is fixed. The real power of arrays lies in the fact that the number indicating the desired array element can be denoted by an expression. Take, for instance, the situation where all array elements need to get the same value. You could do so with a long series of assignment statements: table[0] = 0; table[1] = 0; table[2] = 0; table[3] = 0; table[4] = 0; but that’s rather tedious (especially for long arrays). Instead, we’d better exploit the pattern in these statements: instead of the index (which is 0, 1, 2, 3 or 4) we can write a variable. In a for-statement the variable traverses all values in the range. The length of the array can be used as an upper bound for the count: int number; for (number=0; number<table.length; number++) table[number] = 0; Arrays as a parameter You can pass arrays as a parameter to a method. It’s not really the array that is passed but rather the reference to it. The array object itself is still in the same place in memory where it was created. It’s reasonable to expect that the method will be called with a reference to an existing array (thus, doesn’t have null as its value), and furthermore that all components are initialized. The situation might be as follows: table 5 particles length 12 0 95 1 11 2 length 23 3 3 15 4 Now we can write a method that takes such an array as a parameter, and for example determines the smallest element in the array: static int smallest(int [] table) First guess is that the very first value of the array is the smallest. In the example, that value is 12. That is not the smallest of all (because 11 is smaller), but we’ve not yet inspected that value. { Arrays of objects Elements of an array can be of any desired type. It could be a primitive type, like int, double, boolean or char, but also an object-type. You can thus create arrays of Strings, Buttons, TextFields, or of objects for which you defined the class yourself, like Particle in chapter 9. Here is an array of Particle objects, which could have been used instead of the separate variables d1, d2 and d3 in chapter 9: int result; result = table[0]; Now, we use a for-statement to inspect all values. For each value we check whether it is smaller than what we thought was the smallest up till now. If so, we adapt the result value: int number; for (number=0; number<table.length; number++) if (table[number] < result) result = table[number]; After the for-statement is completed, we can safely return the result value, because it has been checked against all values in the array. 0 1 2 x y dx dy color x y dx dy color x y dx dy color Particle Particle Particle The reference variable can be declared with: Particle [] particles; The array proper can be created with: particles = new Particle[3]; But beware! We have created the array now, but not the individual particle objects. This should be done in a for statement, creating each individual particle: int number; for (number=0; number<particles.length; number++) particles[number] = new Particle(); return result; } 73 Arrays versus strings Arrays having characters as elements have much in common with string objects: both in an array-of-char and in a String, characters can be stored. Yet there are differences between an array-of-char and a String, the most prominent being: In an array, individual elements can be changed writing a[x]=…; which is not possible in a String. With a String you can call various methods, like equals and substring. Furthermore you can “add” Strings using the plus-operator. For arrays, neither of these is possible. But there are also similarities, although the notation might be different: Both from an array and from a String you can select the n-th element, using the notation a[x] and s.charAt(x) respectively. Both from an array and from a String you can determine the length. For an array this is done by inspecting the object variable length: a.length , for a String it is done by calling a method: s.length() Note that for an array no parentheses are needed, but for a String they are. in counter number 26. Capitals and lower case letters are treated separately; other symbols are just ignored. for (n=0; n<s.length(); n++) { c = s.charAt(n); if (c>=’A’ && c<=’Z’) table[c-’A’]++; else if (c>=’a’ && c<=’z’) table[c-’a’]++; } After the entire string is checked, we can display the results. We’ll step through the entire array, for each counter displaying an applicable text, by appending it to the output TextArea: output.setText(""); for (n=0; n<26; n++) { c = (char)(n+’A’); output.append (c + ": " + table[n] + " times\n" ); } 11.3 Example: Text analysis with letter frequencies Counting of individual character frequencies We can now adapt the running example, this time counting not only the frequencies of spaces and newlines, but also the frequencies of each individual character in a text. We’ll revise method actionPerformed once more: public void actionPerformed(ActionEvent e) { String s; int n; char c; s = input.getText(); For the 26 counters we create an array of integers. It must be declared, created, and initialized to zero: table = new int[26]; for (n=0; n<26; n++) table[n] = 0; Now we can inspect all characters of the string. For each symbol that we encounter, we increment the corresponding counter in the array. The number of the counter is determined by subtracting the Unicode-code of constant ‘A’ from the symbol taken from the string. That way, the number of A’s is counted in counter number 0, the number of B’s in counter number 1, and the number of Z’s 74 } Separating contents from the user interface Meanwhile, the method has grown rather complex. What is especially complicated, is that various problems play a role: grabbing the text from the TextArea, creation of the array, the actual checking, and the layout of the message displayed. It would increase readability if we could outsource some of these tasks to a separate class. For example, we could define a class Checker, which could provide a method check for checking all characters in a string. If we could also ask it to return the final message as one string, the whole method actionPerformed would consist of only four lines: public void actionPerformed(ActionEvent e) { Checker c; c = new Cheker(); c.turf( input.getText() ); output.setText( c.toString() ); } Of course, such a Checker class does not exist. However, we can define one for our purpose. Various tasks are put in separate methods: in the constructor method: creating and initializing the array in method check: the actual counting in method toString: layouting the result We can write a fourth method, that handles the checking of a single character, taking into account the capital/lower case distinction. In the check method, we can call that method for all characters in the string. In the main class, apart from the short version of actionPerformed, only the creation of the user interface remains. Thus, all communication with the user is done in one class, and all “contentual” matter is done in another class. Such a separation of concerns is always a good idea. In listing 11.1 and 11.2 the complete program is given. 75 import java.applet.Applet; import java.awt.*; import java.awt.event.*; class Checker { int counters[]; int total; public class Text extends Applet implements ActionListener { TextArea input, output; Button b; public Checker() { counters = new int[26]; } public void init() { input = new TextArea( 5,30); output = new TextArea(28,20); b = new Button("Count"); output.setEditable(false); this.add(input); this.add(b); this.add(output); b.addActionListener(this); } private void check(char c) { if (c>='A' && c<='Z') { counters[c-'A']++; total++; } else if (c>='a' && c<='z') { counters[c-'a']++; total++; } } public void actionPerformed(ActionEvent e) { Checker c; c = new Checker(); c.check( input.getText() ); output.setText( c.toString() ); } public void check(String s) { for (int i=0; i<s.length(); i++) check( s.charAt(i) ); } } Listing 11.1: Text.java Listing 11.2: Checker.java Figuur 11.1: snapshot of the Text program 76 public String toString() { String s = ""; for (int i=0; i<26; i++) s += (char)(i+'A') + ": " +counters[i]+ "times\n"; s += "total: " + total; return s; } } 12. Designing the interface 12.1 Layout of the user interface Layout managers Interaction components that are added to the interface appear all next to each other. When they don’t fit in the row, they are continued in a next row. The spaces available for the applet may vary. When running an applet with appletviewer, this can even be done while the program is running. When running an applet with a browser, the dimensions are fixed, but can be changed in the HTML file. If the dimensions are changed, interaction components are reshuffled: there may fit more components next to each other, or fewer. By cleverly choosing sizes, you can force a cute layout (as was done in Simulation, where the three Space objects just fit in the first row, so that the Button objects appear in the second row). The positioning is done automatically, and is done by a so-called LayoutManager, which is associated with the Applet. If you are not satisfied with the layout, you can choose another layout manager. This is done by calling method setLayout (which is available in Component and all its subclasses). As a parameter you pass the desired manager, which is (of course!) an object itself. for example: this.setLayout( new BorderLayout() ); Because you need to reference it only once, you need not to first store the layout manager object in a variable, but you can immediately pass it to setLayout. Available Layout managers In the library, some four layout managers classes are available. All of them are implementations of interface LayoutManager, which is what setLayout expects: LayoutManager BorderLayout FlowLayout GridLayout The standard layout manager is FlowLayout. It positions the components next to each other/below each other from left to right, from top to bottom. Instead, you could choose for a GridLayout, to order components in a fixed grid. As parameters to the constructor method of GridLayout you specify the number of rows and columns, and the number of pixels between them: this.setLayout( new GridLayout(2,3,5,5) ); The manager changes the shape of the individual components. Available space is evenly distributed among components. If there is not enough space, you are out of luck (see the button with caption “kortschildkever”). A BorderLayout orders its components along the borders of the available space: four at the various edges, and a fifth in the center. There cannot be more than five components in this case. The position is mentioned as an additional parameter to add: a string specifying a compass direction or “Center”: this.setLayout( new BorderLayout() ); this.add( "North", new Button("koe") ); this.add( "West", new Button("varken") ); this.add( "Center",new Button("kortschildkever") ); this.add( "East", new Button("kip") ); this.add( "South", new Button("olifant") ); North and south edges get as much vertical space as necessary, but are stretched in width. West and east get as much horizontal space as is needed, and are stretched vertically. The remaining space is for the center. A final possibility is having no layout manager at all. To get rid of the default FlowLayout, you specify null as a layout manager. The price is that you’ll have to 77 position every component yourself by calling setBounds. You’ll have complete freedom of layout, but nothing is rearranged when the size of the window changes! Your init method will contain statements like b =new Button("koe"); b.setBounds(10,10,70,20); this.add(b); b =new Button("varken"); b.setBounds(25,50,50,30); this.add(b); b =new Button("kortschildkever"); b.setBounds(40,30,100,15); this.add(b); 12.2 Example: Calculator Description of the case We’ll create a simple 4-function calculator, which the user can control via buttons on the screen: The result will look like: Class Panel You can achieve more complex layouts by using Panel objects, which have a layout manager of their own. A Panel is a component to which you can add subcomponents, which are arranged with a local layout. So the applet can have a BorderLayout, with a Panel appearing in the center, which in turn has a GridLayout. You can even nest Panels within Panels, thus creating complicated “dashboards” for controlling your program. 78 Division in classes As in the previous chapter we’ll write two classes: a class Calc which creates the interface and handles the button presses a class Proc doing all arithmetic, but which does not deal with user interaction. The program appears in listing 12.1. import java.applet.Applet; import java.awt.*; import java.awt.event.*; class Proc { long value, prev, screen; char operator; public class Calc extends Applet implements ActionListener { Label result; Panel knoppen; Proc proc; Proc() { clear(); } void clear() { value = 0; prev = 0; operator = '+'; screen = 0; } public void init() { Button knop; String opschrift; proc = new Proc(); result = new Label( "0", Label.RIGHT ); knoppen = new Panel(); result.setFont( new Font("Arial", Font.BOLD, 20) ); void calc() { switch(operator) { case '+': prev case '-': prev case '*': prev case '/': prev } screen = prev; value = 0; } this. setLayout( new BorderLayout() ); knoppen.setLayout( new GridLayout(4,4,6,6) ); for (int n=0; n<16; n++) { opschrift = "789/456*123+0C=-".substring(n,n+1); knop = new Button(opschrift); knop.addActionListener(this); knoppen.add( knop ); } this.add( "North", result ); this.add( "Center", knoppen ); } } value; value; value; value; break; break; break; break; void digit(int n) { value = 10*value+n; screen = value; } public void actionPerformed(ActionEvent e) { Button b; char c; b = (Button) (e.getSource()); c = b.getLabel().charAt(0); if (c=='C') proc.clear(); else if (c=='=') proc.calc(); else if (c>='0'&&c<='9') proc.digit( c-'0' ); else proc.operation(c); result.setText( ""+proc.screen ); += -= *= /= void operation(char c) { calc(); operator = c; } } } 79 A. Reserved words kind statements parts of statements primitive types 80 keyword break continue do for if return switch throw try while case catch default else finally boolean byte char double float int long short void see section 12.2 kind special values 7.3 8.1 4.3 12.2 program structure 9.3 7.1 12.2 9.3 12.2 8.1 modification of classes and methods 7.2 11.1 8.3 3.2 historic remains 2.4 keyword false instanceof new null super this true class extends import interface package throws abstract final native private protected public static synchronized threadsave transient byvalue const goto see section 7.2 6.2 9.3 10.1 4.1 7.2 2.3 2.3 2.8 6.5 4.2 2.4 5.3 B. Operators and syntax prio 14 14 14 13 13 13 13 12 12 12 11 11 10 10 10 9 9 9 9 8 8 7 6 5 4 3 2 1 1 operator () [] . ++ -! * / % + << >> >>> < > <= >= == != & ^ | && || ? : = op= position post post in pre/post pre/post pre pre in in in in in in in in in in in in in in in in in in in mix in in semantics method call array selection component selection increment decrement bitwise “not” logical “not” multiplication division Remainder after division Addition, or: concatenating strings Substraction Shifting bits tot the left Shifting bits tot the right ditto, filling with zeroes less than bigger than at most at least equal to unequal to bitwise “and” bitwise “exclusive or” bitwise “or” logical “and” logical “or” if – then – else assignment calculate and assign program item class method par-decl declaration statement expression item* import expression ; class class name [extends name] { [declaration]* method* } [modification]* name ( [par-decl [ , pardecl]*]) { statement* } type name type [[]]* name type name [ , name]* ; type [[]]* name [, [[]]* name ] ; declaration expression ; if ( expression ) statement [ else statement ] while ( expression ) statement for ( [expression] ; [expression] ; [expression] ) statement switch ( expression ) statement try { statement* } [catch ( par-decl ) { [statement]* }]* break ; return [expression] ; { statement* } Number ’ symbol ’ ”[symbol]*” variable expression operator expression expression postfix-operator ( expression ) expression . expression expression [ expression ] expression ( expression ) class . expression ( type ) expression new expression new type [ expression ] Parts in […] may be omitted Parts followed by * may be repeated Parts in typewriter type must appear literally 81 class Thread © Thread void start static void sleep interface Runnable void run C. Summary Java-packages 12.1 package java.lang class Object String toString class String boolean equals boolean equalsIgnoreCase boolean compareTo int length String substring String substring String concat char charAt int indexOf String toUpperCase int hashCode class Integer static int parseInt class Double static Double valueOf double doubleValue class System static void exit static InputStream in; static PrintStream out; class Math static double PI, E; static int abs static double abs static double sqrt, log, exp static double sin, cos, tan static double pow static int min, max static double min, max static double random 82 () (String s) (String s) (String s) () (int from) (int from, int to) (String s) (int pos) (char c) () () 8.2 9.5 9.5 9.5 9.5 9.5 13.4 5.3 (String s) () 8.3 8.3 (int n) (double d) (double d) (double d) (double base, double expo) (int a, int b) (double a, double b) () 9.3 9.3 9.3 () 9.3 12.2 package java.util (String s) (int x) (Runnable r) () (long milliseconds) 13.2 13.5 13.5 5.3 5.3 5.3 5.3 5.3 15.2 class StringTokenizer © StringTokenizer (String tosplit, String separators) String nextToken () boolean hasMoreTokens () class Vector © Vector () void addElement (Object x) void setElementAt (Object x, int n) Object elementAt (int n) int size () boolean isEmpty () class Stack © Stack () void push (Object x) Object pop () boolean empty () class Calendar static Calendar getInstance () int get (int field) int set (int field, int value) static int SECOND,MINUTE,HOUR,AM_PM,HOUR_OF_DAY; // fields static int DATE,MONTH,YEAR; // fields static int DAY_OF_WEEK,DAY_OF_MONTH,DAY_OF_YEAR; // fields static int WEEK_OF_MONTH,WEEK_OF_YEAR; // fields static int JANUARY,...,DECEMBER; // values field MONTH static int SUNDAY,...,SATURDAY; // values field DAY_OF_WEEK void setMinimalDaysInFirstWeek (int value) void setFirstDayOfWeek (int value) int getMaximum (int field) int getActualMaximum (int field) 9.5 9.5 9.5 16.3 16.3 16.3 16.3 16.3 16.3 16.3 16.3 16.3 16.3 9.5 9.5 12.3 package java.awt class Graphics void drawString (String s, int x, int y) 2.5 void drawLine (int x1, int y1, int x2, int y2) 3.1 void drawRect (int x, iny y, int b, int h) 3.1 void drawOval (int x, int y, int b, int h) 3.1 void drawImage (Image im, int x, int y) 15.1 void fillRect (int x, int y, int b, int h) 3.1 void fillOval (int x, int y, int b, int h) 8.1 void setColor (Color c) 3.1 class Color © Color (int r, int g, int b) 6.2 static Color white, gray, black, red, green, blue, yellow, magenta, cyan, orange, pink; 3.1 class Font © Font (String name, int style, int size) 12.2 static int BOLD, ITALIC, PLAIN; // styles 12.2 class Dimension int width, height; 9.2 class Toolkit static Toolkit getDefaultToolkit () 15.1 Image getImage (String filename) 15.1 class Image 15.1 int getWidth (ImageObserver x) // e.g. null 15.1 int getHeight (ImageObserver x) // e.g. null 15.1 class BufferedImage extends Image © BufferedImage (int b, int h, int type) static int TYPE_INT_RGB, TYPE_INT_ARGB, TYPE_BYTE_GRAY, ... ; // types void setRGB (int x, int y, int rgb) 9.5 int getRGB (int x, int y) 9.5 Graphics getGraphics () 9.5 class MediaTracker © MediaTracker (Component parent) 15.1 void addImage (Image im, int id) 15.1 void waitForID (int id) 15.1 void waitForAll () 15.1 class PixelGrabber © PixelGrabber (Image im, int x, int y, int b, int h, int [] target, int offset, int scansize) 15.1 void grabPixels () 15.1 class AudioClip void play void loop void stop class MenuBar © MenuBar class Menu © Menu void add void addSeparator class MenuItem © MenuItem void addActionListener () () () 9.5 9.5 9.5 () 13.2 (String s) (MenuItem m) () 13.2 13.2 13.2 (String s) (ActionListener a) 13.2 6.5 Layout managers LayoutManager BorderLayout FlowLayout GridLayout interface LayoutManager class BorderLayout implements LayoutManager © BorderLayout () static String NORTH,SOUTH,EAST,WEST,CENTER; // add-directions class FlowLayout implements LayoutManager © FlowLayout () class GridLayout implements LayoutManager © GridLayout (int rows, int cols, int dx, int dy) 12.1 12.1 12.1 83 Interaction-components Component Button Panel Applet Container Canvas Window Frame Label TextComponent TextArea Scrollbar TextField Dialog FileDialog class Component void addMouseListener (MouseListener m) void addMouseMotionListener (MouseMotionListener m) void addKeyListener (KeyListener k) void setVisible (boolean b) void setSize (int b, int h) void setBounds (int x, int y, int b, int h) void setBackground (Color c) void setFont (Font f) Dimension getSize () void paint (Graphics g) void update (Graphics g) void repaint () class Container extends Component void add (Component c) void add (Component c, Object richting) void setLayout (LayoutManager m) class Panel extends Container © Panel () class Applet extends Panel void init () String getParameter (String name) Image getImage (URL base, String name) class Window extends Container void addWindowListener (WindowListener w) void show () 84 15.2 15.2 8.2 9.2 12.1 9.2 12.2 9.2 2.4 15.2 6.3 6.3 12.1 12.1 12.1 6.3 5.1 15.1 13.2 13.4 class Frame extends Window void setMenuBar (MenuBar b) void setTitle (String s) class Dialog extends Window class FileDialog extends Dialog © FileDialog (Frame parent, String title, int typ) String getFile () class Button extends Component © Button (String s) void setLabel (String s) void addActionListener (ActionListener a) class Canvas extends Component © Canvas () class Label extends Component © Label (String s) © Label (String s, int alignment) static int LEFT, CENTER, RIGHT; // alignments void setText (String s) class Scrollbar extends Component © Scrollbar (int alignment, int value, int step, int minimum, int maximum) static int HORIZONTAL, VERTICAL; // alignments void addAdjustmentListener (AdjustmentListener a) void setValue (int x) int getValue () class TextComponent extends Component String getText () void setText (String s) void setEditable (boolean b) class TextField extends TextComponent © TextField (int cols) © TextField (String s, int cols) void setEchoChar (char c) void addActionListener (ActionListener a) class TextArea extends TextComponent © TextArea (int rows, int cols) void append (String s) 13.2 13.4 13.2 13.4 6.2 6.4 6.5 9.2 9.5 12.2 12.2 6.4 6.4 6.5 6.4 6.4 7.5 8.1 11.1 7.5 7.5 8.2 6.5 11.1 11.1 Event-objects 12.4 package java.awt.event EventObject Event-listeners EventListener ActionListener AdjustmentListener ComponentListener AWTEvent ActionEvent MouseListener AdjustmentEvent ComponentEvent InputEvent interface EventListener interface ActionListener extends EventListener void actionPerformed (ActionEvent e) interface AdjustmentListener extends EventListener void adjustmentValueChanged (AdjustmentEvent e) interface WindowListener extends EventListener void windowClosing (WindowEvent e) void windowOpened (WindowEvent e) void windowClosed (WindowEvent e) void windowActivated (WindowEvent e) void windowDeactivated (WindowEvent e) void windowIconified (WindowEvent e) void windowDeiconified (WindowEvent e) interface MouseListener extends EventListener void mousePressed (MouseEvent e) void mouseReleased (MouseEvent e) void mouseClicked (MouseEvent e) void mouseEntered (MouseEvent e) void mouseExited (MouseEvent e) interface MouseMotionListener extends EventListener void mouseMoved (MouseEvent e) void mouseDragged (MouseEvent e) interface KeyListener extends EventListener void keyPressed (KeyEvent e) void keyReleased (KeyEvent e) void keyTyped (KeyEvent e) KeyEvent 6.5 6.5 13.2 13.2 13.2 13.2 13.2 13.2 13.2 15.2 15.2 15.2 15.2 15.2 15.2 15.2 FocusEvent MouseEvent class EventObject Object getSource () 8.1 class AWTEvent extends EventObject class ActionEvent extends AWTEvent class AdjustmentEvent extends AWTEvent int getValue () int getAdjustmentType () static int UNIT_INCREMENT, BLOCK_INCREMENT, TRACK,...; // types class ComponentEvent extends AWTEvent class InputEvent extends ComponentEvent boolean isAltDown () 15.2 class KeyEvent extends InputEvent char getKeyChar () int getKeyCode () static int VK_LEFT,VK_RIGHT,VK_UP,VK_DOWN,VK_HOME,VK_END, static int VK_PAGE_UP,VK_PAGE_DOWN,VK_F1,...,VK_F24,...; // keycodes class MouseEvent extends InputEvent int getX () 15.2 int getY () 15.2 12.5 package java.net class URL © URL (String specification) 14.3 © URL (String prcol, String host, String file)14.3 URLConnection openConnection () 14.3 class URLConnection InputStream getInputStream () 14.3 85 12.6 package java.io Non-stream class File © File © File © File static String pathSeparator static char pathSeparatorChar String getName File getParentFile boolean exists boolean isFile boolean isDirectory void delete long length File[ ] listFiles File[ ] listRoots class RandomAccessFile © RandomAccessFile int read void write long getFilePointer void seek Byte streams class InputStream int read int read long skip void close class OutputStream void write void write void flush void close 86 (String pathname) (String parent, String child) (File parent, String child) () () () () () () () () () () () 13.4 13.4 13.4 9.5 9.5 9.5 13.4 13.4 9.5 13.4 13.4 (File f, String mode) (byte[ ] b) (byte[ ] b) () (long pos) () (byte[ ] b) (long number) () 13.4 13.4 (int b) (byte[ ] b) () () 13.4 13.4 class FileInputStream extends InputStream © FileInputStream (String name) © FileInputStream (File f) class ByteArrayInputStream extends InputStream © ByteArrayInputStream (byte[ ] b) class FilterInputStream extends InputStream class BufferedInputStream extends FilterInputStream © BufferedInputStream (InputStream is) class DataInputStream extends FilterInputStream © DataInputStream (InputStream is) int readInt () double readDouble () // etcetera class FileOutputStream extends OutputStream © FileOutputStream (String name) © FileOutputStream (File f) class ByteArrayOutputStream extends OutputStream © ByteArrayOutputStream () byte[ ] toByteArray () class FilterOutputStream extends OutputStream class BufferedOutputStream extends FilterOutputStream © BufferedOutputStream (OutputStream os) class DataOutputStream extends FilterOutputStream © DataOutputStream (OutputStream os) void writeInt (int n) void writeDouble (double d) // etcetera class PrintStream extends FilterOutputStream // no constructor, use PrintWriter! void print (Object o) void println (Object o) 13.4 13.4 13.4 13.4 13.4 13.4 13.4 13.4 13.4 13.4 13.4 13.5 13.5 13.5 Character streams class Reader int read int read class Writer void write void write void write void close () (char[ ] c) 13.4 13.4 (int c) (char[ ] c) (String s) () 13.4 class InputStreamReader extends Reader © InputStreamReader (InputStream is) © InputStreamReader (InputStream is, String encoding) class FileReader extends InputStreamReader © FileReader (String name) © FileReader (File f) class StringReader extends Reader © StringReader (String s) class BufferedReader extends Reader © BufferedReader (Reader r) String readLine () class LineNumberReader extends BufferedReader © LineNumberReader (Reader r) int getLineNumber () class FilterReader extends Reader class PushbackReader extends FilterReader © PushbackReader (Reader r) void unread (int c) class OutputStreamWriter extends Writer © OutputStreamWriter (OutputStream os) class FileWriter extends OutputStreamWriter © FileWriter (String name) © FileWriter (File f) class StringWriter extends Writer © StringWriter () String toString () class BufferedWriter extends Writer © BufferedWriter (Writer w) 13.4 class FilterWriter extendsWriter class PrintWriter extendsWriter © PrintWriter © PrintWriter void print void print void println void println (Writer w) (OutputStream os) (Object o) (allPrimTypes x) (Object o) (allPrimTypes x) 13.4 13.4 13.4 12.7 Application 13.4 one classof the program contains method: static void main (String [ ] ps) 13.1 13.4 13.4 13.4 13.4 13.4 13.4 12.8 Primitive types type void boolean char byte short int long contains nothing truth unicodesymbol integer number integer number integer number integer number default false (char)0 0 0 0 0 size 0 bits 1 bit 16 bits 8 bits 16 bits 32 bits 64 bits float double floating point floating point 0.0 0.0 32 bits 64 bits range false, true (char)0 .. (char)65535 –128 .. 127 –32768 .. 32767 –2147483648 .. 2147483647 –9223372036854775808 .. 9223372036854775807 ±1.4E–45 .. ±3.4028235E+38 ±4.9E–324 .. ±1.7976931348623157E+308 13.4 13.4 13.4 87