read%me% 1 - shippensburg university school of engineeringdjmoon/dp/dp-materials/cs1text4.pdf ·...

235

Upload: others

Post on 27-Jan-2021

0 views

Category:

Documents


0 download

TRANSCRIPT

  • READ  ME   1  THE  LABORATORY  APPROACH   1  HOW  TO  READ  THIS  TECHNICAL  MATERIAL   2  SETTING  UP  ECLIPSE   3  

    LAB  1  -  READING  CODE   6  BACKGROUND   6  TYPES  OF  LANGUAGES  AND  THE  JAVA  VIRTUAL  MACHINE   6  INTEGRATED  DEVELOPMENT  ENVIRONMENTS   8  SETTING  THINGS  UP   8  SYNTAX  ERRORS   9  KEYWORDS   9  WRITING  SOME  CODE   9  RUNNING  THE  CODE   10  THE  ECLIPSE  DEBUGGER   11  USING  THE  DEBUGGER  TO  PAUSE  THE  PROGRAM   11  THE  DEBUG  PERSPECTIVE   12  VARIABLES   14  ASSIGNMENT  STATEMENTS   15  WATCHING  AN  ASSIGNMENT   16  WHILE  LOOPS   16  THE  LOOP  IN  OUR  CODE   18  SIMPLE  OUTPUT   18  WATCHING  CONDITIONALS   19  

    LAB  2  –  READING  ARRAYS   22  GENERATING  RANDOM  NUMBERS   22  LAB  BACKGROUND   23  ARRAYS   23  FOR  LOOPS   25  SETTING  THINGS  UP  TO  PLAY  WITH  ARRAYS   26  WHAT  DOES  IT  DO?   28  CONSTANTS   28  HOW  DOES  OUR  PROGRAM  DO  THAT?   29  MATHEMATICAL  OPERATORS   31  THE  OUTPUT   32  MORE  TRIALS?   32  WHERE  ELSE  CAN  WE  USE  THIS?   32  WHAT  IF  THE  RANGE  GOES  NEGATIVE?   33  CONCLUSION   35  

    LAB  3  –  PRINTING  GRAPHS  AND  METHODS   37  METHODS   37  OUR  FIRST  METHODS   37  FIRST  A  SIMPLE  GRAPH   41  COMPUTING  THE  HORIZONTAL  POSITION   43  

  • ROUNDING  ERRORS   45  IT  WORKS  FOR  OTHER  FUNCTIONS,  TOO   46  METHODS  CAN  HAVE  MULTIPLE  PARAMETERS   46  GRAPHING  THE  AREA  BETWEEN  TO  FUNCTIONS   47  

    LAB  4  –  CLASSES  AND  OBJECTS   53  INTRODUCTION   53  STRUCTURE  OF  A  CLASS   54  SETTING  UP  THE  CARD  CLASS   54  CREATING  AND  USING  OBJECTS   55  CONSTRUCTORS   57  VARIABLE  INITIALIZERS   58  EXPLORING  OBJECTS   59  WATCHING  THE  CONSTRUCTOR  WORK   61  THE  TOSTRING()  METHOD   62  FIXING  OUR  OUTPUT   62  NAMING  CONVENTIONS   63  OUTPUTTING  THE  SUITS   64  ONE  MORE  TIME!   65  THE  DECK  CLASS   66  NESTED  FOR  LOOPS   68  WATCHING  THE  CREATION  OF  THE  DECK   69  NOW  FOR  THE  OUTPUT   71  LET’S  SHUFFLE  THINGS  UP   71  THE  SHUFFLE  CODE   72  CONCLUSION   73  

    LAB  5  –  DETAILS  ABOUT  VARIABLES   76  INTRODUCTION   76  VISIBILITY  MODIFIERS   76  SETTING  UP  OUR  CLASS   77  GETTERS  AND  SETTERS   78  ADDING  GETTERS  TO  OUR  CODE   79  SIZE  LIMITATIONS   81  PLAYING  WITH  SIZE   83  WATCHING  THE  OVERFLOW   83  LET’S  TRY  LONG  VARIABLES   84  IF-THEN-ELSE  STATEMENTS   84  MAKE  THE  SEQUENCE  WRAP   85  BIGINTEGERS   87  LEARNING  ABOUT  BIGINTEGER   87  JAVADOC  COMMENTS   88  SETTING  UP  ECLIPSE  TO  GENERATE  JAVADOC  COMMENTS   89  PRIMITIVE  VS.  REFERENCE  TYPES   89  USING  BIGINTEGERS   91  

    LAB  6  –  TEST  DRIVEN  DEVELOPMENT  AND  MORE  ABOUT  VARIABLES   96  TEST  DRIVEN  DEVELOPMENT   96  

  • DESIGNING  OUR  CLASSES   97  SETTING  UP  OUR  TESTS   97  MAKE  THE  TEST  COMPILE   98  MAKE  THE  TEST  PASS   100  CLEAN  UP  THAT  MESS!   100  TDD  IN  MORE  DETAIL   101  CHECKING  OUT  A  BOOK  (PHASE  1)   101  CHECKING  OUT  A  BOOK  (PHASE  2)   102  SCALE  IN  TDD   103  CHECKING  A  BOOK  BACK  INTO  THE  LIBRARY   103  VARIABLE  SCOPE  AND  LIFETIME   104  INSTANCE  VARIABLES   105  LOCAL  VARIABLES   105  METHOD  PARAMETERS   106  FOR  LOOP  VARIABLES   106  IMPLICATIONS  OF  SCOPE   107  THE  KEYWORD  THIS   108  @BEFORE  IN  JUNIT   109  REFACTORING  THE  SET  UP  OF  OUR  TESTS   109  NUMBER  OF  TIMES  A  BOOK  HAS  BEEN  CHECKED  OUT  (PHASE  1)   110  NUMBER  OF  TIMES  A  BOOK  HAS  BEEN  CHECKED  OUT  (PHASE  2)   111  NUMBER  OF  TIMES  A  BOOK  HAS  BEEN  CHECKED  OUT  (PHASE  3)   111  TEST  DRIVEN  DEVELOPMENT  REVIEW   112  

    LAB  7  -  TEST  DRIVEN  DEVELOPMENT  AND  CONDITIONALS   114  REMINDER:  TDD   114  SET  UP   114  OUR  FIRST  TEST   115  MAKE  THE  TEST  PASS   115  CLEAN  UP   116  BORDER  CASES   116  IF-THEN-ELSE  STATEMENTS   116  WRITING  OUR  FIRST  CONDITIONAL   117  NESTED  CONDITIONALS   118  THE  DANGLING  ELSE  PROBLEM   119  MORE  TAX  LEVELS   120  SIMPLE  DEPENDENT  DEDUCTION   120  VARYING  DEDUCTIONS   121  PROBLEM  DECOMPOSITION   122  MAKE  TWO  LEVEL  DEDUCTIONS  PASS   122  FIRST  SPOUSE  TAX   122  SECOND  SPOUSE  TAX   123  NOW  THINGS  ARE  GETTING  TRICKY   124  THE  TRICKIEST  TAX  OF  THEM  ALL   124  

    LAB  8  –  LOOPS  AND  STRINGS   127  INTRODUCTION   127  SET  UP   127  COUNT-CONTROLLED  LOOPS   131  

  • THINGS  WE  CAN  DO  WITH  STRINGS   131  COUNTING  BLANKS   132  MORE  DETAILS  ABOUT  UNICODE   133  COUNTING  LOWER  CASE  LETTERS   133  BORDER  CASES  FOR  LOWER  CASE  LETTERS   134  SENTINEL-CONTROLLED  LOOPS   134  THE  POSITION  OF  THE  FIRST  BLANK   134  STRINGS  WITH  NO  BLANKS?   135  MORE  COMPLEX  CONDITIONS   136  EARLY  EXIT  CONDITIONS   138  TWO  THINGS  TO  STOP  THE  LOOP   139  WHERE’S  A  SPECIFIC  BLANK?   140  OFF  THE  END  OF  THE  STRING  AGAIN   140  STRING  CONTAINMENT   141  MORE  THINGS  YOU  CAN  DO  WITH  STRINGS   141  STRINGS  CONTAIN  STRINGS   142  PARTIAL  MATCHING  WITH  REGULAR  EXPRESSIONS   143  

    LAB  9  –  WRITING  METHODS  TO  SORT  AN  ARRAY   146  BACKGROUND   146  SETTING  THINGS  UP   146  BUBBLE  SORT   147  BUILDING  BUBBLESORT   149  ESTIMATING  RUN-TIME   151  SELECTION  SORT   152  BUILDING  SELECTION  SORT  (PHASE  1)   152  MORE  TESTS   154  BUILDING  SELECTION  SORT  (PHASE  2)   155  RUN-TIME  ANALYSIS  OF  SELECTION  SORT   156  INSERTION  SORT   157  BUILDING  INSERTION  SORT   159  RUN-TIME  ANALYSIS  OF  INSERTION  SORT   160  BEST  CASE  ANALYSIS   162  

    LAB  10  –  ITUNES  SONGS   164  INTRODUCTION   164  GETTING  STARTED   165  SONG  TOSTRING()   165  SONG  TOSTRING()  AGAIN   166  ARRAYS  OF  OBJECTS   166  CREATING  ALBUMS   167  TEST  ADDING  ONE  SONG   169  TEST  FILLING  THE  ALBUM   171  TESTING  DURATION  (PHASE  1)   171  TESTING  DURATION  (PHASE  2)   172  TESTING  DURATION  (PHASE  3)   172  TESTING  TOSTRING()   173  CONCLUSION   173  

  • LAB  11  –  ARRAY  PRACTICE  WITH  ZIP  CODE  ENCODING   175  A  REVIEW  OF  STRINGS   175  ZIP  CODE  ENCODINGS   175  SET  THINGS  UP   176  START  WITH  THE  CONSTRUCTOR   177  CONSTRUCTOR  BORDER  CASES  PART  1   178  TOSTRING()  PHASE  2   178  MORE  TOSTRING()  TESTING   178  TESTS  FOR  COMPUTING  CHECK  DIGITS   179  THE  MOD  OPERATOR   179  COMPUTING  THE  CHECK  DIGIT   179  BORDER  CASES  FOR  CHECK  DIGITS?   180  BAR  CODES  -‐  THE  TEST   180  REFACTORING  HOW  WE  STORE  THE  ZIP  CODE  (BACKGROUND)   181  REFACTORING  HOW  WE  STORE  THE  ZIP  CODE  (PHASE  1)   182  REFACTORING  HOW  WE  STORE  THE  ZIP  CODE  (PHASE  2)   182  REFACTORING  HOW  WE  STORE  THE  ZIP  CODE  (PHASE  3)   183  INITIALIZING  ARRAYS  AT  DECLARATION   183  BACK  TO  BAR  CODES   184  BAR  CODE  GENERATION  (PHASE  1)   184  BAR  CODE  GENERATION  (PHASE  2)   185  BAR  CODE  GENERATION  (COMPLETION)   185  LET'S  BE  THOROUGH   185  

    LAB  12  –  METHOD  OVERLOADING  AND  THROWING  EXCEPTIONS  WITH  ZIP  CODE  DECODING   188  INTRODUCTION   188  METHOD  OVERLOADING   188  BUILDING  THE  NEW  CONSTRUCTOR   189  DECODING  THE  BAR  CODE  (PHASE  I)   189  DECODING  THE  BAR  CODE  (PHASE  II)   190  DECODING  THE  BAR  CODE  (PHASE  III)   190  WHAT  COULD  GO  WRONG?   190  THROWING  EXCEPTIONS   191  TESTING  FOR  EXCEPTIONS   192  TESTING  FOR  BAR  CODES  THAT  ARE  TOO  SHORT   192  MORE  BARCODE  EXCEPTIONS   193  BONUS  –  MORE  EXCEPTIONS   194  

    LAB  13  –  BUILDING  AN  APPLICATION  AND  CATCHING  EXCEPTIONS   195  INTRODUCTION   195  PREPARING  FOR  USER  INPUT   195  SCANNER   196  GETTING  USER  INPUT   196  HANDLING  CONVERSIONS  IN  BOTH  DIRECTIONS   196  WHAT  IF  THE  BAR  CODE  IS  INVALID?   197  CATCHING  EXCEPTIONS   198  HANDLING  BAD  BAR  CODE  INPUT   200  

  • LET  THE  USER  ENTER  CODES  MORE  THAN  ONCE   201  

    LAB  14  –  HUMANS  AND  ALIENS   203  INTRODUCTION   203  HIT  THOSE  ALIENS!   203  RANDOMNESS   203  DON’T  FORGET  THE  BORDER  CASES   205  ALIENS  CAN  RECOVER   205  

    STRANGE  SITUATIONS  IN  RECOVERY   205  PLANNING  FOR  HUMANS   206  INHERITANCE   206  CREATING  OUR  HIERARCHY   208  START  MAKING  HUMANS   213  WHAT  ABOUT  TAKING  HITS?   213  TEST  SUITES   214  WEAPONS   214  LET’S  PICK  UP  CAREFULLY   215  USING  WEAPONS   215  CAN’T  SHOOT  WITHOUT  A  WEAPON   216  OVERRIDING  METHODS   216  HUMANS  ARE  SMARTER  THAN  ALIENS   217  PICKING  UP  MORE  WEAPONS   218  ANOTHER  BORDER  CASE   219  SWITCHING  WEAPONS   219  

    GLOSSARY   221  

    INDEX   226  

  • 1

    Read Me

    The Laboratory Approach This book uses a unique approach to teaching Java programming. Using a sequence of laboratory exercises, the text guides you through three distinct phases designed to move you from a novice to a basic level Java programmer. The goal is for you to spend more time “doing” than “reading.”

    To get some insight into the approach we have taken with this book, imagine the skills required to be a chef. Chefs need to know about many individual ingredients like egg whites, lemons, and cream of tartar. Chefs also gain experience from following existing recipes. However, knowledge of ingredients and the ability to follow existing recipes only qualifies them as cooks. In order to be a chef, they have to be able to create entirely new recipes from scratch. That requires additional knowledge about how the ingredients interact with each other and a sense of the particular culinary experience they want to create. It also requires experimentation to develop one’s own insight into what makes a recipe “good.”

    The cook/chef comparison captures the goal of this book. If we only wanted to teach you to be a coder, then teaching you the details of the language would suffice. A textbook for a coder would include a reference library to all possible language constructs (the ingredients) and rote examples showing how the various constructs solve specific problems (recipes). However, as computer scientists and software engineers, you are much more than coders. You are, in practice, chefs. You need to be able to use the language constructs to solve problems that have never been solved before (create new recipes).

    In addition, computer scientists and software engineers need to acquire the skill of learning new features of a language as the language changes. So, memorization of all of the details is not the goal. Instead, you will learn about the language in much the same way that engineers learn about new features on the job. Instead of listing every Java detail in this text, we will often refer you to the standard Java documentation; so you will learn how to find and incorporate new ideas into your work. So remember, your goal is not to be able to follow someone else’s work; your goal is to be able to create completely new solutions.

    The first phase of this book is focused on reading Java programs. During this phase, you’ll learn various Java language constructs and how they can be used to evoke particular behaviors. You will not develop code on your own, yet, and you don’t need to know how the author wrote the code. The only goal is for you to understand how the language causes the behavior that you see. You will observe these behaviors with an application called Eclipse that is used by software engineers to develop code in industry. Eclipse has a feature called a debugger that will let you watch the code execute one statement at a time displaying exactly what each statement does.

    The second phase of this book will teach you the basics of writing code. You’ll develop small pieces of code for very specific problems. These problems are designed to help you learn to write code with each of the major Java constructs. In addition, when your code isn’t behaving the way you expected, you’ll learn to use the debugger to help you find and fix the problems yourself.

    Finally, the third phase of the text is focused on writing solutions to more complex problems with increasingly less support from the text. You’ll use the knowledge you gained from the first two phases to apply the Java constructs to solve these much more interesting problems. This section will continue to help you develop your debugging skills and will also cover a variety of strategies for how to develop an entire application.

  • 2

    In addition to the programming activities, we will explore some of the types of questions computer scientists and software engineer wrestle with in real world situations.

    How to Read This Technical Material Each chapter of this book is based on a lab activity with the necessary instructional content interspersed. You will notice that the text will change between three different fonts representing the three elements of this text: content, descriptions of the lab activities, and code. Color is used extensively to distinguishing the parts of code. We use the same color scheme found in Eclipse, so code you write in Eclipse will appear the same as it does in this text.

    The font you have been reading is used for content.

    This is the font used when lab instructions are given. The font will change again when code is presented as follows: int x; while (x < 2) { System.out.println(x); x = x + 1; } System.out.println("Finished");

    We have attempted to make the material in this text as readable as possible; however, there are some specific strategies you can use to get the most out of the time you spend reading. Reading technical material isn’t a passive process. To truly understand the subject you must actively process the text.

    In Literature classes, you were taught to look for symbolism and foreshadowing. In order to do that, you couldn’t just read the words. As you read, you had to think about what the author intended with each sentence. You were trying to get into the mind of the author. You should take the same approach when you are reading technical material.

    As authors, we have carefully thought about the overall structure of this text and about each sentence and example it includes. In order to truly understand what you are reading, it is helpful to ask yourself questions like, “Why would the author put this topic here?”, “Why did the author pick that example?”, “What is the important point being made here?”, and “Why is it important that the concept is true?” Asking those types of questions will help you see beyond the facts on the page. You will comprehend their relevance. That analysis will help you truly learn the material in this text.

    This text includes example code with descriptions. The descriptions of each piece of code are essential to understanding the code. Reading them completely will give you insight that will decrease the time it takes you grasp how the code works. However, reading the book’s descriptions isn’t enough. When you are reading a section of code, pretend you are the computer, and execute the code by hand to see exactly how it works. This may take a little more time, but it is critical to your understanding of the material. If you can’t mimic the examples, you will not be able to write your own code.

    The content portions of the chapters assume that you are doing the laboratory activities as you read them, especially once you are writing your own code. If a section of the lab stumps you, it is often helpful to re-read the previous content section. If that doesn’t help, reading the next section

  • 3

    might help, but you shouldn’t go too far before you get the lab activity to work. That will assure that you have learned the material in that section.

    Setting Up Eclipse One thing that software engineers agree on is that good tools are critical to efficiently developing software applications. Therefore, some of the labs in this text specifically involve the Eclipse Integrated Development Environment (IDE). This is an open source piece of software that you can download and install from http://www.eclipse.org . The installation instruction should be found there.

    After you have installed Eclipse and brought it up, it should show you a window offering tutorials. Feel free to play with the tutorials. When you finish with those, Eclipse will take you to the workbench. At this time, we would like you to change Eclipse’s default formatting. The examples in this book format code slightly differently from the basic conventions of Java. We made this choice because we have found that novice programmers often read code more easily in the non-default format.

    Do the following: select Project->Properties from the pull-down menus at the top of the screen. That will bring up another window that lets you change many aspects of Eclipse’s configuration. On the left side of that window, click on the triangle next to “Java Code Style.” Under those options, click on “Formatter.” Your window should look like this:

    Since we want to change the setting for all of your projects, click on “Configure Workspace Settings” near the top right corner of the screen. Then you should see this:

  • 4

    Eclipse won’t let you change the built in settings, but you can create your own profile by clicking the “New” button. Give your profile a name, and click OK. That should bring up this window:

    Here you can change many aspects of the way Eclipse will format your code, but we only need to change one thing to match the examples in this text. Click on the “Braces” tab and change all of the pull-downs to the value “Next line” like this:

  • 5

    Click OK until you are back at the desktop and you’re ready to go!

  • 6

    Lab 1 - Reading Code

    Background One definition of computer science is the study of algorithms. Informally, an algorithm is a sequence of steps that accomplish a particular task. We use algorithms every day. For example, the algorithm for brushing your teeth could be:

    1. Put some tooth paste on the tooth brush

    2. Use the brush in an up-and-down motion to clean the front and back of every tooth

    3. Spit some of the tooth paste into the sink

    4. Use the brush to clean the chewing surfaces of your teeth

    5. Spit the remaining tooth paste into the sink

    6. Rinse off the tooth brush and put it away

    That could be considered to be an algorithm because it is sequential (you do the steps in order) and it gets the job done.

    When we build an algorithm, we have to be aware of how the algorithm will be executed because it will only work if the executer (person or machine) understands the algorithm. This requires two things. First, the algorithm has to be specified in a language that the executer understands. If you tell an American how to brush his teeth in German, you are unlikely to get the result you’re hoping for. Second the algorithm has to be specified at an appropriate level of detail. Consider this algorithm for washing your hair (which is on many shampoo bottles):

    1. Lather

    2. Rinse

    3. Repeat

    These steps actually require that the executer knows something about the goal of the algorithm. The first instruction doesn’t specify what to lather, how much shampoo to use, how long to lather, or even describe what a lather is so you know when you have done it. The last instruction is even more vague: are we supposed to repeat all three steps forever, the first two steps, or just the rinse? It isn’t very specific.

    Computer programming is the process of specifying an algorithm in sufficient detail so that the computer will understand exactly what to do. That means we have to write it in a language the computer understands (in our case that will be Java) and at a level of detail appropriate to the computer. You will quickly find that computers are terribly naïve. They won’t do anything you don’t tell them to do, and they will do exactly what you tell them to do even if it isn’t really what you meant!

    Types of Languages and the Java Virtual Machine Computer scientists are in the business of finding ways to improve computing complex tasks or simplifying tedious tasks. As it turns out, once a solution is well thought out, the very act of writing code that the computer can read is a tedious job in itself. To simplify the process of writing code, computer scientists have developed several pieces of software that act as tools for software developers.

  • 7

    When you look at the details, the processors in our computers don’t understand Java at all. In fact, they only understand a very primitive language made up only of ones and zeros called machine code. When computers were first built, programmers had no choice but to program in ones and zeros. That was very tedious and error prone. The first improvement we made was to create assembly languages that gave alphabetic names to various parts of the instructions the machine understood. Then, for example, the programmers could use the command ‘ADD’ instead of the machine code ‘01011’. They built pieces of software called assemblers that translated the alphabetic names into the ones and zeros the machine needed.

    Although assembly language is a great improvement over machine code, the programmer still needs to understand the low level operations of the target machine (the machine that will execute the code). This is because there was a one-to-one relationship between assembly language instructions and machine instructions for each particular machine. If you moved an application from one machine to another whose instruction set was different, you would be required to completely redevelop the application for the second machine.

    For this reason, programming in assembly language requires a strong understanding of the underlying machine and is error prone because of the level of detail required when we specify an algorithm. As a result, the next tools developed were high-level languages, like C/C++ and Java. These languages are different from assembly languages because one statement in a high-level language may be translated into many machine instructions. They are designed to make programming less tedious and error prone. However, in order for these languages to work, we had to build programs, called compilers, which translate the high-level language programs into the machine language.

  • 8

    While the language output by the compiler is the same as the one output by the assembler, we often call the code produced by a compiler object code.

    High-level languages were a great improvement over assembly languages and significantly raised the scope of the problems software engineers could address. However, they have one Achilles heel: the object code runs on exactly one type of processor and may even depend on the operating system. This is why, when you purchase software (you buy object code that the computer can read, not source code that we can read), you have to buy the version that is built for your machine. While software engineers architect systems to try to minimize the amount of high-level source code that depends on the processor or the operating system, significant resources are spent on the process of changing the source code so that the compiler of a new machine can translate it successfully. This is known as porting code.

    Java was developed in the age of the Internet and was designed to hide the details of the target machine from the compiler. In order to do this, they specified a software “machine” called the Java Virtual Machine (JVM). The operations supported by this machine are standard, but there is a different JVM for each target machine. The Sun engineers who develop java produce most of these virtual machines (except the one for Macs that is developed by Apple). The Java compiler translates java source code into Java byte code, the language that the JVM understands.

    There is one significant difference that results from this strategy: instead of the compiler building object code that runs directly on the machine, the object code produced by the Java compiler is interpreted by the JVM. This means that, as the JVM is running the object code, it is actually translating it to the machine language understood by the target machine. When Java was originally developed, this made execution of java programs slower than for other high level languages. However, over the years, the Java developers have optimized the JVMs to eliminate this problem.

    Integrated Development Environments Whenever computers scientists can, they build tools that make the job of building systems easier. In recent years, these tools have been packaged together into software applications called Integrated Development Environments (IDE). These applications group the actions of writing code, running code, and diagnosing problems in the code all in one application.

    Setting Things Up In order to experiment with some code, we’re going to use a powerful tool called Eclipse. Software engineers use Eclipse to build systems and we’ll learn many of its capabilities. This lab uses its “debugging” tools to show how Java code works.

  • 9

    To start, bring up Eclipse and create a new Java project using File -> New -> Project. Open the ‘Java’ wizard folder, select ‘Java Project’ and click ‘Next’. You can name the project anything you like. Leave all default values and click ‘Finish’.

    Select your new project in the Package Explorer on the left side of the screen.

    Create a new class by clicking the "new" button ( ) near the top left of the screen. In the ‘Java’ wizard folder, select ‘Class’ and click ‘Next’. Name the class "Potatoes" and click "Finish".

    Eclipse will start you out with code that looks something like this: public class Potatoes { }

    Your Eclipse settings may have caused it to build slightly more interesting code. In particular, you might see sections of code that start with ‘/**’ and end with ‘*/’. These are called comments and are completely ignored by the computer. Their only purpose is to help humans read and understand the code.

    Syntax Errors All languages of some overarching grammatical structure, called its syntax. For example, in English, there are very specific rules that govern the use of commas. Computer languages also have specific syntax rules that all programs must obey. As you type, Eclipse is checking your program against those rules. When your code breaks these rules, the errors are called syntax errors and Eclipse marks them with red squiggles. Sometimes, Eclipse will jump ahead and give you a red squiggle before you finish typing a statement – those you can just ignore.

    Also, sometimes it’s difficult to tell when the Eclipse re-checks your code when you try to fix the squiggles. If you think you’ve fixed the problem and you want to force the compiler to run, just save the file (the little disk icon at the top of the screen).

    Keywords In addition to the structural rules defining its syntax, Java gives special meanings to some words that are called keywords. Eclipse will color the keywords in maroon, so you can see that our program is using two keywords so far: public and class. In general, keywords are special in that they are specific instructions to the compiler. Sometimes they are called reserved words because you cannot use them in other ways in your program; the word “public” means something special and you can only use it in specific situations as defined by Java.

    Writing Some Code Now we’re ready to put some real code into our class. Make your class look like this:

  • 10

    /** * This program prints out the words to a rhyme used by children * to select someone. */ public class Potatoes { /** * The sequence of instructions that should execute when we

    * run the program * * @param args just ignore these for now! */ public static void main(String[] args) { int potatoNumber; potatoNumber = 1; while (potatoNumber < 8) { System.out.print(potatoNumber);

    System.out.print(" potato"); System.out.println(""); potatoNumber = potatoNumber + 1; } System.out.println("More"); System.out.println("You're out!"); } }

    If you make any errors in this, Eclipse will underline them with red squiggles (like spelling errors in Word). Remember that the computer is truly stupid; capitals and all punctuation must be exactly as they are shown here.

    Also, notice that Eclipse is trying to be helpful by doing the indenting for you. While the whitespace (tabs and blanks) between words doesn’t matter to the computer, it does make the code easier for people to read. As we play with this code, you’ll see the motivation for the indentation.

    Don’t go on until your code looks like the example and there are no red squiggles.

    Running the Code Before we analyze this too far, let’s just let the machine run the code to see what happens. Right click on the name of the class in the package explorer and pick Run As -> Java Application. (Click ‘OK’ if a “Save and Launch’ window appears.)

  • 11

    It may take a second or two for the JVM to initialize itself and run the code, but when it does, Eclipse will show you the results in the Console tab near the bottom of the screen. It should look like this: 1 potato 2 potato 3 potato 4 potato 5 potato 6 potato 7 potato More You’re out!

    Though the output isn’t exactly correct, our program is “chanting” a rhyme from childhood! Don’t go on until you see this output.

    The Eclipse Debugger One of the really powerful capabilities of Eclipse is its debugger. A debugger is a tool that stops the execution of the code and let’s us watch it work one statement at a time. This is an excellent way to figure out what the code is doing.

    One of the common ways to use the debugger is to set breakpoints. A breakpoint marks a line in the code telling the debugger to pause the execution of the program at that line. So, when we run the program using the debugger, it will run normally until it hits a breakpoint. At the breakpoint, the debugger will stop executing the program and Eclipse will change perspectives to let us take full advantage of the debugger’s capabilities. While the debugger has the program paused, we’ll be able to look at what the program has been doing, and, once the program is paused, we’ll be able to make the debugger pause after every statement. That way, we’ll be able to see what each statement does.

    Using the Debugger to Pause the Program In order to watch how this code runs, we want to set a breakpoint right where the program starts running. Now, look at your code and try to guess where it starts. (You’d be surprised how easy it is – just read the code from the top and ignore the stuff that doesn’t make any sense).

    We’ve already talked about the comments (the text between /* and */). Since that is for human consumption only, that can’t be where the program starts.

    There are two statements that start with public; at this point, they probably don’t mean anything to you. For now, just consider them to be instructions to the compiler letting it know what our code should be named.

    The next statement we could consider is “int potatoNumber;”. We’ll come back to that one in a minute. For now, just know that it’s more instructions to the compiler.

  • 12

    Finally, we see the statement “potatoNumber=1; ”. That should look like algebra to you. Just like in algebra, we have a variable, and its value is 1. That statement actually does something, so that’s where we want to put the breakpoint.

    To put a breakpoint on a line, you just have to double click on the blue edge of the frame at that line. When you’ve done that, you should see a blue dot on that point:

    Now we are ready to run the debugger. Right click on the name of the class in the package explorer and select Debug As -> Java Application. If it asks you to save the file, let it save everything. When it runs, it should ask you for permission to change to the debug perspective. Let it make that change and the layout of the frames within Eclipse will change. That means the debugger has paused the execution of the program and we’re ready to look around.

    The Debug Perspective In Eclipse, the debug perspective lets us control the debugger and shows us what is happening in the program. In the middle of the window, you see your program with a greenish line showing where the debugger has paused it. The frame at the bottom of the window can show you a number of types of output from your program. If you click on the “Console” tab, you’ll see the same output you saw in the Java perspective. Our program has not output anything yet, so that will be blank for now.

    The frames at the top of the screen are the heart of the debugger. In the left, you should see this:

  • 13

    That shows us that one program is running (the one we told it to run!) and it is stopped in “main” at line 18 (if you put a different number of blank lines into your code, the line number may be different). Look back at the code and find the word main. It’s in one of the “public . . .” statements. Essentially, it is naming the section of code that is running and that section is contained within the curly brackets (“{“ and “}”) that follow that declaration.

    This part of the debug perspective also lets us control the debugger. At the top of this frame there are buttons to control how the program runs. In the first section, you see DVD-like controls that let you run, pause, and stop the program. If we wanted to just let it finish, we could use those. However, we want to watch it run one step at a time; the next section of buttons tell the debugger to run one

    statement. For now, we just want to use the second of these buttons: which we will call “step over.” Essentially, it runs the one statement that the debugger is paused on and stops on the next statement (it is stepping “over” the current statement). Click that button once and you should see two things change: the line number in the debug frame should change, and the greenish line in the frame showing your code should move to the “while” statement.

    There’s one more important part of the debug perspective: The variables view in the upper right hand frame:

  • 14

    This view shows us the values of all of the variables in use at the time the debugger stops the program. For now, ignore the “args” variable. The second line shows our variable “potatoNumber” and that it has a value of 1.

    Variables Variables let the computer store information that changes as the program runs (i.e. their values vary). Variables are made up of three things:

    1. a name – we can name a variable with any continuous sequence of characters that follows these rules:

    • it isn’t a keyword

    • it starts with a letter, a dollar sign ($), or an underscore (_)

    • it does not contain any white space

    2. a value – we say a variable “holds” a value

    3. a memory location – a place the compiler puts the value to be remembered

    It’s important to remember that the compiler cares about capitalization. For example, a variable named “isMine” is different from a variable named “ismine”. There is a convention that, when a variable name contains multiple words, the words are not separated by anything (some languages prefer that the words be separated by a character like underscore) and all but the first word are capitalized. This is called camel capitalization. (ex. “thisVarUsesCamelCaps”)

    In order for the compiler to set up the memory required for a variable, we have to “declare” the variable to the compiler. This declaration tells the compiler the name of the variable and the type of information the variable will hold. Then the compiler knows how much memory to allocate for that variable. For example, this statement: int count;

    declares a variable whose name is count and whose type is “int”. This means that the variable will hold an integer. Memory diagrams have a particular structure and can help us envision how the computer is storing information. For this declaration, the memory diagram would be this:

  • 15

    That diagram shows that the variable named count is holding a zero (and the lack of a decimal point implies that the value is an integer). We will use memory diagrams to demonstrate how various pieces of code manipulate the values in the computer’s memory.

    There are quite a few other types of variables that the Java compiler understands. For now, you need only worry about these:

    • int – holds positive and negative integers

    • double – holds positive and negative real numbers

    • boolean – holds the values true and false

    Assignment Statements We can change the value that a variable holds with an assignment statement. These statements have a very specific structure: = ;

    For example, if count is a variable that is declared to be an integer, count = 32;

    will store the value 32 in the variable named count resulting in this memory diagram:

    Notice that, while this statement uses an equals sign, it isn’t really declaring that count and 32 are equal. It’s better to read it as “count gets the value 32.” In other words, the left hand side of the equals must always be the name of a single variable where we want to store the information and the right hand side evaluates to the value we want to store.

    The expressions we can use in these statements can include a variety of types of operations: mine = 32; count = mine + 2;

    In the first statement, the 32 is stored into the variable named mine. In the second statement, in order to evaluate the right hand side of the statement, the computer will get the value stored at mine (which is 32) and then add 2 to that value. The result (34) will be stored into the variable named count. Again, an assignment statement does these things (in this order):

    1. evaluate the right hand side and

    2. store the result in the variable on the left hand side.

    If you incorrectly think of assignment statements as “equals,” they will may seem very confusing. For example, look at this code:

  • 16

    int count; count = 2; count = count + 4;

    The first statement declares a variable named count that holds an integer. The second statement stores a 2 into that variable. The third statement is where things get interesting. If you read it as “equals” it looks like it is patently wrong. How can count be equal to count+4? That would mean 2 equals 6. However, reading it as “count gets the value of count+4” gives a much better perspective on what the computer will do. First, it must evaluate the right hand side of the statement by getting the value stored in count (2) and adding 4 to it resulting in the value 6. Second, it stores that result (6) into the variable named on the left hand side of the statement (count). In the end, count will have the value 6.

    Watching An Assignment Notice that the statement that you stepped over (potatoNumber = 1;) was an assignment statement and that’s how the variable potatoNumber got the value 1. Also, if you look a little further up in the code, one of the statements we ignored was int potatoNumber;

    That declared our variable potatoNumber and told the compiler that it would hold an integer.

    While Loops We’ve seen that the JVM executes our instructions sequentially (in order), but sometimes we want to do something more than once. One way we can accomplish that is by using a while loop. As an example, look at this code: int count; while (count < 2) { System.out.println(count); count = count + 1; } System.out.println("Finished");

    When JVM executes a while statement, it will evaluate the condition and, if it is true, will execute all of the statements inside the curly brackets. Then it will go back to the condition and, if it is still true, it will again execute all of the statements in the loop. It will continue to do this until the condition is false and then the JVM will continue executing the code after the loop.

    In our example, this is what the JVM would do:

  • 17

    Java Statement JVM’s action Value of count after statement is complete

    int count; Create space for the variable count;

    0

    while (count < 2) Evaluate (count< 2).

    Since that is true, decide to execute the instructions inside the loop.

    0

    System.out.println(count); Print the value of count

    to the console. (The output will be “0”)

    0

    count = count + 1; Evaluate “count+1”

    which will be 1 and store it in the variable count.

    1

    while (count < 2) Evaluate (count< 2).

    Since that is true, decide to execute the instructions inside the loop.

    1

    System.out.println(count); Print the value of count to the console. (The output will be “1”)

    1

    count = count + 1; Evaluate “count+1”

    which will be 2 and store it in the variable count.

    2

    while (count < 2) Evaluate (count

  • 18

    The Loop in Our Code Now, look at the statement the debugger is paused on: while (potatoNumber < 8)

    That looks like it’s going to let something happen while the value of the variable potatoNumber is less than the value 8. Right now, potatoNumber has the value 1, so somewhere that value must change or the while statement will loop forever. Look further down in the code to find the code that changes that value.

    In order to figure out what that while statement is really doing, use the step over button as many times as you like to watch the code run. After each step, look at where the program is stopped and what the value of the variable potatoNumber is. If you want to start the program over, first, stop it by clicking on the stop button

    and then you can restart it by right clicking in the view that shows your code and selecting “Debug As -> Java Application” again. It will remember the breakpoint that we have set at the beginning of the program.

    Watch the code run until you can answer these questions:

    1. How does the value of potatoNumber change? 2. What is the while statement doing? 3. What is the purpose of the curly brackets? 4. What is the purpose of the way we indented the code?

    Simple Output If you look carefully, there are two statements that are causing things to be output to the console: System.out.print and System.out.println.

    In these statements, the expression inside the parentheses is evaluated and put out to the console. Watch these statements execute until you think you understand the difference between print and println. For each of the following, make a hypothesis about what would happen. Then make a change to the code and run it to test your hypothesis:

    1. What is the difference between print and println? 2. What would the output look like if we made them all be “print”? if we made

    them all be “println”? 3. Why does the potatoNumber in the first statement not have quotes, while

    all of the rest of the things being output are in quotes? 4. In the statement that is outputting “ potato”, how would the output change

    if the blank inside of the first quote was missing? 5. What is the effect of System.out.println(“”);

  • 19

    Make it Right! The correct output for this program is: 1 potato 2 potato 3 potato 4 5 potato 6 potato 7 potato more You’re out!

    We’d like it to not print out “potato” if potatoNumber is 4. Let’s add an if statement to make the output correct. To do that, make your code look like this:

    int potatoNumber; potatoNumber = 1; while (potatoNumber < 8) { System.out.print(potatoNumber); if (potatoNumber != 4) { System.out.print(" potato");

    } System.out.println("");

    potatoNumber = potatoNumber + 1; } System.out.println("More"); System.out.println("You're out!");

    Watching Conditionals Run your code and step over your code to watch how it skips parts of the code. In Java, if statements allow the program to change its behavior depending on the information in the system.

    Watch your program run until you can answer these questions:

    1. How do you know which code will be executed if the conditional is true? 2. What is the purpose of the indentation there?

    Note: when you use curly brackets in Eclipse you may see that the ending bracket is automatically included. It is wise to check for proper pairing of brackets making sure that only the statements you want executed are within the pair.

    Also, if you change the program and you attempt to run the code again, Eclipse may warn you that your class contains obsolete methods. This means that you have left the debugger stopped at a breakpoint. Click on “Terminate” to clean that up.

  • 20

    Vocabulary Assembler Integrated Development

    Environment Output

    Assembly Language Interpreted Reserved Word Assignment Statement Java Byte Code Source Code Breakpoint Java Virtual Machine Syntax Comments Keyword Target Machine Compiler Loops Variable Conditional Statement Machine Language While Loop Debugger Memory Diagram High Level Language Object Code

    Lab Questions 1. Explain what this code does:

    int i;

    2. What does a while loop do? 3. How did you change the code so that the “potato” didn’t get output with 4? Are there

    other ways you could have modified the code to get the same result?

    4. Explain what this code accomplishes: x = x + 1;

    5. Draw a memory diagram showing how this program changes the contents of the variables over time.

    6. In the code given in the lab, which brackets may be removed between the while statement and the final print statement without changing the behavior of the program?

    Chapter Questions 1. What is the difference between a compiler and an assembler? 2. What is the difference between an interpreted and a compiled language? 3. How does the Java Virtual Machine change the way object code is executed? 4. What is the purpose of a breakpoint? How do you think it might be useful when you start

    writing code? 5. How do you set a breakpoint? 6. What is the difference between “Run As . . .” and “Debug As . . .”? 7. What is the debugger and how do you use it to watch what your code is doing? 8. Draw the memory diagram for each of the following and specify what the value of the

    variable x will be at the completion: a. int z;

    z = 30000; int x; x = 32; if (z

  • 21

    b. int y = 1; int x = 2; while (y

  • 22

    Lab 2 – Reading Arrays

    Generating Random Numbers Often, we want to generate a random number in a particular range. For example, if we were building a game that included dice, we might want to simulate the rolling of a single die. We could do that by generating a random integer from 1 to 6.

    Java only gives us one way to generate random numbers; we can call Math.random() which will return a randomly generated real number (a double) whose value is between 0 and 1. That value could be zero, but it cannot be one. In other words, it generates a real number that has a zero to the left of the decimal point. While that may initially seem quite limited, we can actually use it to generate random numbers in any range.

    In our dice rolling example, we want to generate a random integer from 1 to 6. We can do that using the sequence of steps in this diagram:

    Math.random() gives us a number between 0 and 1 (not quite getting to 1). Think about what happens when we multiply that number by six. The values near zero stay near zero, zero stays zero, but the numbers close to one will result in a value close to six. By multiplying by six, we have essentially increased the range of our results. Now, our random number will be between zero and six (not quite getting to six).

    Then, if we add one to our result, that will shift the range of possible results one position to the right on our number line. Now the range is from one to almost seven. Notice that we could get the value 1, but we won’t get the value seven.

    Finally, we need our result to be an integer, so we need to convert our double to an int. We can do this by putting(int)in front of our expression. This is called typecasting the value to an int. We’ll study this technique more in a later lab.

  • 23

    Here’s the expression for simulating our dice roll: int roll = (int)(Math.random() * 6) + 1);

    In general, these are the steps to generate a random number between two values (max and min):

    1. Call Math.random() 2. Multiply that result by (max – min + 1) to make the spread of values the correct size 3. Add min to shift the possible values into the correct range

    So, in general, our expression to generate a random integer between max and min is: (int) Math.random() * (max – min + 1) + min

    Notice that, in this case, we wanted each number to be equally likely to be selected. In other words, we wanted the random number to be uniformly distributed across the values. The original random number that we got by calling Math.random() was uniformly distributed between 0 and 1, so our resulting values from our expression will also be equally likely. If you have a situation where you do not want the values to be distributed uniformly, your task will be much harder!

    Lab Background In this lab, we are going to experiment with the generation of random numbers to test how uniformly distributed they really are. In order to do that, we are going to generate thousands of random numbers and count how many times each value is generated. If the random number generator is distributed uniformly, then every value should be generated about the same number of times.

    Arrays Up to now, we have used variables that hold exactly one piece of information. For example, when we declare a variable with this statement: double x;

    we are telling the compiler that the variable x will hold one real number.

    Sometimes, however, we’d like something more flexible. For example, say you want to hold the ages of four people. We could declare four integer variables: int age1; int age2; int age3; int age4; age1 = 32; age2 = 35; age3 = 15; age4 = 52;

    Given that set up, if we wanted to compute the average age, we could do this: (Note: We need to define a place to store the result as a double, since dividing integers may gives us a decimal remainder.) double averageAge; averageAge = (age1 + age2 + age3 + age4)/4;

    However, this strategy has a number of weaknesses. Imagine that we want to average the ages of 100 people. How much code would that require just to define the variables? Also, each time we

  • 24

    add another age, we would have to change the formula for averageAge. If we were working with the ages of 100 people, that formula would become very long.

    Clearly, storing each age in a separate variable doesn’t handle either of these situations very well. Instead, we use a different type of variable called an array. An array can hold a number of values as long as all of those values are of the same type. If we store our four ages in an array, it would look like this:

    The four ages are stored in the four positions of the array and the positions are numbered from 0 to 3. The offset associated with a position is an index into the array associated with that position. For example, the position holding the 15 has an index of 2. (Note: Computer scientists count from 0, so the third person’s age is stores in index 2.) [Think of this as a book about ages, with four pages each holding one age and the four pages are numbered starting at zero.]

    Here is the code that builds that array: int[] ages; ages = new int[4]; ages[0] = 32; ages[1] = 35; ages[2] = 15; ages[3] = 52;

    The first line of the code declares ages as an array (that’s what the square brackets mean) that holds integers. At this point, the compiler doesn’t know how much memory to allocate because we haven’t told it how many ints will be stored in the array. So, the memory diagram would look like this:

    The variable ages is declared so that it will be able to hold the pointer to the array, but, for now, it doesn’t point to anything (that’s what “null” means). [Think of this as telling a librarian that you plan to write a book about ages, and they should reserve a reference card for your book.]

    The second line is an assignment statement. (Remember, the equal sign means “is assigned the value.”) The right hand side of the equals sign is a new statement. We use it to tell the computer to allocate some memory. In this case, it is creating the array to hold four integers. When the new statement completes, it returns a reference to the memory that was allocated and the assignment statement stores that reference into the ages variable. [Think of this as placing your book with 4 blank pages on the shelves, and now the librarian can fill in the reference card to direct people to where your book is located. You first have to find the book before you can read or edit one of its pages. Similarly, you must address your array before you can read or write to one of its indexed memory locations.]

    At this point, the memory diagram would look like this:

  • 25

    Once the array has been allocated, each position in the array behaves like a variable and we access it using the index associated with that position. For example, ages[2] is the third position in the array. (Remember that we always number the positions starting with zero.) The last four lines of the code above put values into each of the positions in the array. We now have the final memory diagram:

    Storing things in arrays gives us the ability to use loops to “walk through” the array and do something to each value in the array. For example, after a year passes, we would like to update everyone’s age. The following loop adds one to each position of the ages array: (Note: We are using another type of comment here. When you type two slashes, the compiler will ignore everything in the rest of that line. We will explain the code in more detail below.) int i=0; // set i to starting value while (i < ages.length) { ages[i] = ages[i] + 1; // increase the ith age by 1 i = i + 1; // increment i }

    When a variable is declared as an array, we can use .length to find the number of positions in the array. In this loop, i will start with the value 0 and each time through the loop, it will get larger by one (by executing the statement i = i + 1). The loop will stop when i has the value 4 (the length of the array). At each pass through the loop, ages[i] will be the value in the position associated with index i and it’s value will be increased by 1.

    For Loops If you look at the while loop in the previous section, there are really three statements that affect how the loop works. The first statement sets up a variable to start the loop. The second statement is the condition within the while statement itself that tells the loop to proceed or be skipped over. The final statement is the incrementing of i that updates the condition for the next evaluation of the loop.

    For situations where you can identify those three pieces of loop control logic, a simpler syntax can be used that will result in the same behavior as demonstrated by the while loop: for (int i=0; i < ages.length; i=i+1) { ages[i] = ages[i] + 1; }

    This is called a for loop. It puts all of the loop control logic in one line of code. There is a direct translation between these two ways of specifying a loop. The while loop structure

  • 26

    while () { )

    translates to this for loop structure

    for (; ; ) { }

    While any while loop can be translated into a for loop, we generally prefer for loops in situations where we are counting at each pass through the loop and while loops are preferred for more general situations.

    When we have an array, “walking through the array” requires a variable that counts through the indices of the array, so there is a standard for loop structure for this situation:

    for (int i=0; i< array.length; i++) { }

    It is common to use the shorter i++ which is equivalent to i=i+1. The effect of both of those statements is that the value stored in the variable gets bigger by one. This is called incrementing the variable.

    Setting Things Up To Play With Arrays In Eclipse, create a new Java project (File -> New -> Project). Select your new project in the Package Explorer on the left side of the screen. Create a new class by clicking the "new" button () near the top left of the screen. Name the class "RandomNumberChecker" and click "Finish". Eclipse will start you out with this code (your code may contain more comments): public class RandomNumberChecker { } But you need a lot more code than that! Make your class look like this (the code is longer than one page):

  • 27

    /** * An experiment in how uniformly distributed are the results our * random number generation algorithm. * * @author Merlin */ public class RandomNumberChecker { static final int MAX_VALUE = 6; static final int MIN_VALUE = 1; static final int NUMBER_OF_VALUES =

    (MAX_VALUE - MIN_VALUE + 1); static final int NUMBER_OF_TRIALS = 1000; /** * @param args */ public static void main(String[] args) { int[] count; count = new int[NUMBER_OF_VALUES]; for (int i=0;i

  • 28

    As in the previous lab, if you make any errors in this, Eclipse will underline them with red squiggles (like spelling errors in Word). Remember that the computer is truly stupid; capitals and all punctuation must be exactly as they are shown here. You can add or remove white space except that you cannot eliminate all of the space between words or add spaces within words.

    What Does It Do? To start, let's just run it. Right click on the name of the class in the package explorer and pick Run As -> Java Application. It should run and produce this output in the console window at the bottom of the screen: (Remember that we are generating numbers randomly, so the counts you see won’t exactly match these. However, they should be close.) Counts 1: 161 2: 153 3: 168 4: 169 5: 166 6: 183 The expected count is 166 Distance From Average 1: 5 2: -13 3: 2 4: 3 5: 0 6: 17

    Don't go on until you see similar output.

    Constants Variables typically hold values that vary as the program runs (hence the name variable). However, sometimes we want to use a value that never changes. For example, in the example of computing averages of ages, let’s say we need to add one year to everyone’s age. We could just write the code making it aware of the fact that there are exactly four things in the array like this: for (int i = 0; i< 4; i++) ages[i] = ages[i] + 1;

    When we use a value the way we used that 4, we call it hard-coding the value into the code. The problem with hard-coding is that, if we want to change the value later, we may need to change it in many places and problems can result if we don’t find them all. As a better alternative, a constant is a variable whose value cannot be changed (yes, that sounds contradictory!). We declare a constant in the same way that we declare a variable, but with the modifier “final” which means the value cannot be change. For this reason, we must include the value in the declaration. For example, we could declare the constant for our array of ages like this:

  • 29

    final int NUMBER_OF_AGES = 4;

    Notice that we capitalize all characters of a constant. With that declaration, we can use our constant throughout our code: ages = new int[NUMBER_OF_AGES]; for (int i = 0; i< NUMBER_OF_AGES; i++) // do something to each position of the array of ages

    In this way, if we later decide that we need to change the number of ages we are working with, we can just change the value in the declaration of the constant. In addition, by marking the variable as “final” (making it a constant), we ensure that no one will accidentally change its value when the system is running.

    How Does Our Program Do That? As a first step in looking at this program, look at the declarations of constants. Four constants define the range of random numbers we are going to generate and the number of random numbers we’re going to generate. In particular, we are going to generate the integers from 1 to 6 inclusive. This would be equivalent to simulating the roll of a die. For now, just ignore the modifier “static”; we’ll explain that in a later lab. It’s also important to pay special attention to this declaration: static final int NUMBER_OF_VALUES =(MAX_VALUE - MIN_VALUE + 1);

    Notice that this constant is being assigned a value that results from a computation. Since NUMBER_OF_VALUES is a constant, we can only do this if the computation depends only on other constants (and not other variables). Furthermore, the constants used in the computation must already be defined.

    Now, let's use the debugger to watch how this RandomNumberChecker program really works. Put a breakpoint at the first line of the main method by double clicking on the blue vertical bar to the right of that line like you did in the previous lab.

    Since we have just run our program, we can run it in the debugger by clicking on the debug button ( ) in the toolbar near the top of the screen. When you click that, it will probably ask if you want to switch into the debug perspective - say ok! The debugger has stopped the execution of the program. The editor will put the cursor on the line that will be executed next. Click the “step over” button just once. You have executed "count = new int[NUMBER_OF_VALUES];" Look at variable count in the variable pane:

  • 30

    The "int[6]" means that count is an array that holds six integers, and the triangle on the left can be used to expand it to show all of those values. Click on the triangle and you should see this (you may have to scroll to see all of the values):

    Inside the first for loop, you see three variable declarations and these statements: randomReal = Math.random() * NUMBER_OF_VALUES + MIN_VALUE; random = (int)(randomReal); position = random - MIN_VALUE; count[position] = count[position]+1;

    The first line generates a real random number between 0 and NUMBER_OF_VALUES (it could be zero, but it can’t quite be NUMBER_OF_VALUES) and then adds MIN_VALUE to shift the values to the right range. The second line truncates that result to convert the real number to an integer. Together, those statements match the explanation of how to generate random numbers at the beginning of this lab and you should be able to explain exactly how it works.

    The third line calculates a position in the array. To understand what this is doing, we need to look carefully at how the code is using the count array. Remember that the array has indices 0 to 5, but we are generating numbers from 1 to 6. In order for the count of each possible value to have a position in our array, the code will subtract 1 (the minimum value we are generating) from the number generated to compute the index associated with that number. In other words, we’ll store the count of occurrences of 1 at index 0, the count of occurrences of 2 at index 1, the count of occurrences of 3 at index 2, and so on.

    So, each time we go through that loop, we will generate one random number and count its occurrence.

  • 31

    Use step over to watch how that loop is working. Each time you execute the loop you should see variables changing in the variable pane. When you reach the first line inside of the loop, the value for i (the loop count) will be displayed. When the second line is executed, the position value is displayed in the variables tab. As you return to the for loop statement, the count array has been incremented by 1 at the position (index) you saw before. (Note that the line your debugger has stopped on doesn’t happen until you step to the next statement.)

    Mathematical operators Many of the mathematical operators we use when we program are the same as those you learned in elementary school. For example, ‘+’ and ‘-‘ mean ‘add’ and ‘subtract.’ However, not everything is so straightforward in programming. Multiplication is denoted with the ‘*’ operator because keyboards don’t have a “dot” operator and just putting the variables next to each other would make it difficult for the compiler to translate the code, and we have a number of operators that aren’t common in mathematics. Here is a table of the common mathematical operators in Java:

    Operator Number of Operands

    Example Expression

    Meaning

    + 2 3 + 4 Addition

    - 2 3 – 4 Subtraction

    * 2 3 * 4 Multiplication

    / 2 3 / 4 Division

    ++ 1 i++ Increment (add one)

    -- 1 i-- Decrement (subtract one)

    % 2 4 % 3 Modulus (remainder after division)

    - 1 -3 Negation

    + 1 +3 Positive

    We can use these operators to build complex mathematical calculations. In addition, we can use parentheses to group sub-expressions just like we do in algebra. We’ll learn more about building expressions in a later lab.

    In algebra, remember that multiplication and division operations are completed before addition and subtraction. For example, 3+2*4 has the value 11. The same is true in Java. Every computer language contains precedence rules that specify the order in which operations will be computed.

  • 32

    Here are those rules for Java:

    Precedence Group Operators

    Highest Subexpression ( )

    Second Highest Unary Operators Unary (one operand) + and -

    Third Highest Multiplicative Operators *, /, %

    Lowest Additive Operators +, -

    The Output All that’s left to explore in this code is the two loops that produce the output. They are both our standard “walk though the array” type of loop. Watch them execute and justify why the System.out.println statements result in the output you see. Hint: Math.abs(x) evaluates to the absolute value of x, Math.abs(-3) would evaluate to 3 and Math.abs(3) would also evaluate to 3.

    More Trials? One of the benefits of using constants instead of hard-coding values is that it is easy to change their values and see how that affects the results. Play with the constant NUMBER_OF_TRIALS to change the number of trials. If you make it small, do the results support the idea that the random number generator is equally likely to generate each numbers? As the number of trials grows, you will probably become more confident in your conclusions about the random number generator.

    Where Else Can We Use This? We use random numbers in lots of computer applications. In particular, games use them so that the behavior of the game isn’t the same each time we run it. For example, in a game, when we hit an enemy with a weapon, it doesn’t always do the same amount of damage. Suppose we had a weapon that could cause between 20 and 50 points of damage. Let’s see if Java will generate those values uniformly.

    We have constants that define the range of values we want to generate: MIN_VALUE and MAX_VALUE. Modify those values to be 20 and 50 and run the program. You should see that it generates that range of values uniformly.

    At this point, the constant NUMBER_OF_VALUES has become important. You should be able to explain why the calculation of NUMBER_OF_VALUES includes “+1”.

  • 33

    What if the Range Goes Negative? In the game Final Fantasy 10, if you use black magic on one of the “undead,” it has the potential to heal them instead of hurting them. That would mean that the amount of damage may be negative. Suppose we had a magic spell which, when used on the “undead” caused a range of damage from -5 to 5. Let’s see if our code works for that range. Change the constants to match that range and run the program. You should see output that looks like this: Counts -5: 0 -4: 914 -3: 955 -2: 884 -1: 934 0: 1876 1: 873 2: 907 3: 894 4: 891 5: 872 The expected count is 909 Distance From Average -5: -909 -4: 5 -3: 46 -2: -25 -1: 25 0: 967 1: -36 2: -2 3: -15 4: -18 5: -37

    That doesn’t look like the values are being evenly distributed. We never see the smallest value and we see about twice as many zeros as we should. Something must be very wrong! Let’s use the debugger to see what is happening.

    There are two possibilities for the source of this problem: the way we convert random numbers to the positions in the count array and they way we generate the random numbers has a problem when one of the endpoints is negative.

    First, let’s think about how the counts for these numbers will be stored in the count array. We are generating the numbers from -5 to 5. That means that NUMBER_OF_VALUES is 11 and our array will have 11 positions. The position of each number is found by: position = random - MIN_VALUE;

    In this experiment, MIN_VALUE is -5, so the position will be the random number plus five. This means that count will look like this:

  • 34

    That seems to work and doesn’t explain why we are getting twice as many zeros as we should be getting.

    Since we’ve explained away our first concern, let’s investigate our second concern: that the bug is in the generation of random numbers. First, let’s think about how it ought to work. Look back at the description at the beginning of this lab and draw the diagram for how it should work when the min and max values are -5 and 5.

    Now let’s use the debugger to see if it is working the way we expect it to. Put a breakpoint on the line that calculates position so that we can look at the values for randomReal and random. Run the program in the debugger. When it stops, look at those values and compare them to the diagram you drew in the last paragraph. Do they map correctly?

    One run of the program showed these values:

    That means that, in the last step of generating the number, -0.7 is being converted to 0. In the diagram, when we typecast to int, it always mapped the real number to the next lower integer (because the typecast is just throwing away the decimal part of the number). However, when that real number is negative, dropping the decimal part of the number gives us the int greater than our number! In other words, since the typecasting is just dropping the decimal part, all of the numbers from -0.9999999 to 0.9999999 will become zero. That is why we have twice as many occurrences of zero than anything else.

    In order to fix this problem, we are going to have to change the typecast to something that will always give us the integer below our real value. The “floor” function is exactly that: Math.floor(x)

    will evaluate to the largest integer that is less than or equal to x. Here are some examples to demonstrate:

  • 35

    Math.floor(1.5) is 1 Math.floor(1) is 1 Math.floor(-1.5) is -2 Math.floor(-1) is -1

    For some (unexplained) reason, Math.floor() returns a double (even though it will have zeroes in the decimal portion), so we still have to typecast the result to an int. Change your code to look like this: random = (int) Math.floor(randomReal);

    Stop the code that is running in the debugger by clicking the red square. Then run your program without the debugger to verify that the values are now uniformly generated.

    It seems we have fixed a problem, but we should be careful to make sure we haven’t broken anything. Change the constants back to generate values for rolling a standard die and verify that the output is still correct.

    Conclusion The underlying motivation for this lab was to practice reading code that contained arrays. However, as a side effect, we’ve done an interesting experiment on random number generation in Java. When you look at the results, do you think each of the values are roughly equally likely? In other words, is the random number generation uniform across the values?

    Vocabulary

    array increment precedence

    constant index typecasting

    hard-coding new statement

    Lab Questions 1. Explain how we used the count array. How did it relate to the random numbers that were

    generated? 2. What did this code do?

    for (int i=0;i

  • 36

    int random; random = (int)(Math.random() * NUMBER_OF_VALUES +

    MIN_VALUE);

    4. What other values of the constants did you play with? Explain how changing each of the constants affected the behavior of the program.

    Content Questions 1. Draw the memory diagram for each of the following pieces of code:

    a. int x; x = 3;

    int y; y = x;

    int z; z= = y+x;

    b. int x; x = 3+4*5; // specify the exact value of x

    c. int[] x;

    x = new int[6]; x[2] = 3;

    2. What are the two steps of creating an array?

  • 37

    Lab 3 – Printing Graphs and Methods

    Methods As our programs grow larger, they can be easier to understand if we divide them into smaller pieces. We can name sections of code and, then use them other places. We call these named sections of code methods. (Soon, we will see many other ways methods help us architect quality systems).

    For example, here is a method that outputs the message “Foo.”1 void foo() { System.out.print(“F”); System.out.println(“oo”); }

    A method can contain any number of statements and those statements get executed when another piece of the code refers to the name of the method. For example, this code: int y; y = 3; foo(); System.out.println(“Finished”);

    declares a variable y, gives it a value of 3, and then invokes the method named foo. Notice the parentheses after the name foo; they tell the compiler that foo is a method (as opposed to a variable). When we use the name of the method in our code, we say we are calling the method. When the JVM executes the line that contains the methods name, it jumps to that method, executes the code within that method and then jumps back to the line that called the method. In this case, the execution looks like this: int y; y = 3; foo(); System.out.print(“F”); System.out.println(“oo”); System.out.println(“Finished”);

    When the JVM sees the call on foo, it jumps to that method and the two statements in that method are executed. Then it jumps back to the line that called it (which is now completed) and then continues executing from that point.

    Our First Methods In this lab, we are going to build a program that “draws” a bar graph to the screen. When we are finished, it will draw this graph of the function sin(x) from -π to π.

    1 To use this code in the types of classes we’ve been looking at, the declarations of the methods in this section would need to be preceded by the word “static.” As we learn more, that will no longer be necessary. We only need to use that when the methods are being called from our main() method.

  • 38

    Simple Bar Graphs Without Graphics -1 0 1 ------------------------------------------------------------------- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

    To start, create a project and in that project create a class named GraphPrinter. Make that class look like this:

  • 39

    /** * Print out a graph using characters from the keyboard * @author Merlin * */ public class GraphPrinter { private static final int MAX_SCALE = 65; /** * Print the heading and scale of our graph */

    public static void printHeading() { System.out.println(" Simple Bar Graphs Without Graphics"); System.out.print("-1"); for (int i=0;i

  • 40

    to execute the whole method and stop on the line after the method call. In previous labs, we have always used “Step Over” because, without us being aware of it, we’ve been calling methods all along; every time we called System.out.println(), we were calling a method provided by Java.

    Use “Step Over” to watch printHeading() print out the heading of our graph until you are back in the main() method.

    Passing Information Into Methods The foo() method is very simple, but methods can be much more useful than that. For example, look at this method: void foofoo(int x) { System.out.print(“Foo ”); System.out.println(x); }

    The “int x” in the declaration of the method foofoo is a special kind of variable declaration. It declares a variable named x whose type is int, but x is special because its value is set when the foofoo method gets called. The variables that are declared as part of the method declaration are called the parameters of the method.

    When a method with parameters is called, the values that we want those parameters to have are put into the parentheses in the calling statement. For example, foofoo(3);

    calls the foofoo method and, as that call is executed, the 3 in the calling statement is the value given to the x in the declaration of the method. In some ways, you can think of passing parameters like assignment statements. In this case, the compiler essentially executes x=3 at the beginning of the foofoo method and the output will be “Foo 3”.

    We can pass any expression whose value matches the type of the parameter in the call to the method. All of these are valid calls on foofoo: int p; p = 3; foofoo(p); // will pass a 3 into x foofoo(p+4); // will pass a 7 into x

    However, double z; z = 3.2; foofoo(z);

    will result in a syntax error because z’s type is not int and passing a double into an int would cause the loss of all of the fractional part of z’s value. This example demonstrates that Java is a strongly typed language. This means that the compiler checks to make sure that the types of variables in assignment statements and parameter lists match the types of values being given to them. This is a safety feature because it ensures that you do not accidentally lose information.

  • 41

    First a Simple Graph Printing each row of our graph requires a similar strategy: print blanks to move to the right in the row and then print an asterisk. The only difference in each row is how many blanks we print before the asterisk. Since this is something we want to do repeatedly and it isn’t trivial, let’s make a method that prints a single row. Add this method to your class: /** * Print one row of our graph * @param height the number of blanks we should print before the * asterisk */ public static void printBar(int height) { // print one blank to match the - sign on the -1 in

    // the scale System.out.print(" "); for (int i=0;i

  • 42

    it and clicking on the single X icon. The double X icon will delete all of the

    breakpoints. Now, you can click the “resume” button which will tell the debugger to run the program until either it hits another break point or to the end of the program. Your output should look like this: Simple Bar Graphs Without Graphics -1 0 1 ------------------------------------------------------------------- * * * * * * * * * * * * *

    Now we would like to write some more code, but to do that, we want Eclipse to be in the “Java” perspective. You can switch back and forth between the Java and debug perspectives with these icons at the top right of the Eclipse window (if the option you want isn’t shown, try clicking on the double arrows):

    Instead of outputting the line of asterisks we currently have, our goal was to print out the sine function. The challenge in that is that the range of the sine function is -1 to 1, but the number of blanks we can print before the asterisk is zero to 65. We’re going to need to make a conversion between those ranges.

    Methods That Compute Things Up to this point, all of our methods have performed some action (output some information), but methods can also make computations and return values. private int positive(int x) { if (x >0) { return x; }

    return -1*x; }

    In this case, the method positive() is used to compute the absolute value of the integer passed into it. In its declaration, notice the type “int” before the name of the method. That is called the return type of the method and tells you the type of variable that the method computes. In all of our previous method examples, the keyword “void” has been in that place. That meant that those methods were designed to perform an action, but not to compute a value.

  • 43

    When a method computes a value, a “return” statement makes the method stop execution and the value given in that statement is the result of the method. For example, study this code: int p; p = positive(-3);

    The second statement is an assignment statement. That means that the computer will evaluate the right hand side of the equals and store the result in the variable on the left hand side of the equals. When the computer evaluates a method call, the resulting value is the value in the return statement executed inside the method. In this case, when positive() is called, x will be given the value -3. Since x is less than 0, the condition (x > 0) will be false, so the body of the if statement will be skipped and the “return -1*x” statement will be executed. Since x has a value of -3, position(-3) will evaluate to 3 and that value will be stored into the variable p.

    Computing the Horizontal Position In order to print the sin function, we need to build a method that will take a number from -1 to 1 and convert it to a position in one line of our output. To start, add the definition of these constants below the definition of MAX_SCALE: private static final double MIN_POINT = -1.0; private static final double MAX_POINT = 1.0;

    That defines the range of values of the function we are going to graph and MAX_SCALE defines how many positions each row of output contains. We can use those to make the necessary conversion using this method: /** * Compute the number of blanks that is in proportion to x * @param x the value we want to convert * @return the number of blanks */ private static int calculateHeight(double x) { double shifted; double rangeSize; double numBlanks;

    shifted = x - MIN_POINT; rangeSize = MAX_POINT - MIN_POINT;

    numBlanks = shifted/rangeSize * MAX_SCALE; return (int) numBlanks;

    }

    Let’s analyze the definition of that method: private static int calculateHeight(double x)

    The first two words are modifiers that the compiler needs. We’ll learn more about them later. The next word, int, means that the method is going to calculate a value that is an integer and return that value to the code that called the method. The methods name is calculateHeight and it has one parameter, named x, which holds a real number.

  • 44

    Inside the curly bracket of the method are five statements that are executed when the method is called: double shifted; double rangeSize;

    int numBlanks;

    These statements declare three variables, shifted and rangeSize, each of which can hold a real number and numBlanks which holds an integer.

    shifted = x - MIN_POINT;

    After this statement, shifted essentially shifts x so that it will be a positive number. We have to do this, because we can’t print out a negative number of zeroes. So, since x has a range of -1 to 1, shifted has a range of 0 to 2.

    rangeSize = MAX_POINT - MIN_POINT;

    The variable rangeSize is on the same scale as shifted, but holds the maximum value shifted could have. So, shifted/rangeSize will be a fraction that is proportional to how far shifted is into rangeSize. At this point, an example might help. Supposed that x was -0.5. Since our range is from -1 to 1, we’d like x to be plotted about one quarter of the way across our line of 65 spaces. The value of shifted will be 0.5 and rangeSize will be 2. So shifted/rangeSize will be 0.25 (there’s our “one quarter”). numBlanks = shifted/rangeSize * MAX_SCALE;

    This statement multiplies MAX_SCALE (the number of blanks we can print) by shifted/rangeSize to determine the number of blanks we should print. In our example where x was 0.5, numBlanks will get the value 48.75.

    return (int) numBlanks;

    The return statement causes the method to stop running a