Contents:
Part 2: Choose a Development Environment
Part 3: Obtaining files from GitLab
Part 4: Fix Buggy Code—HolaWorld
Part 5: A new Java class—RandomHello
Part 6: Testing Java Code with JUnit
Part 7: Answering Questions About the Code
Part 9: Implementing Code with Invariants— Finder and Evaluator
Overview
The purpose of this homework is to help you set up your development environment, get reacquainted with Java, and start getting familiar with tools you will use for the rest of the course. Although the homework description is long, we expect the step-by-step instructions will make doing the homework less overwhelming than reading it may be.
This homework links to (the middle of) some other handouts at many points. For convenience, here is the list of these handouts:
- Project Software Setup
- Editing, Compiling, Running, and Testing Java Programs
- Version Control Reference
- Assignment Submission
- Google Java Style Guide
(It also links to various Java files and API documentation files that are not listed above.)
Part 1: Reasoning Worksheet
This part of the homework is separate from Parts 2-9. You are free to work on this worksheet before/while/after you work on the rest of the assignmnet. Note that your solutions to the worksheet should be submitted to Gradescope.
Complete the reasoning problems given in this worksheet.
Feel free to print out the worksheet or rewrite the problems on a separate sheet. You may submit a scanned copy of any hand-written document with the problems and your solutions as long as it is legible.
Submit your solution as a PDF in Gradescope. Be sure to indicate in Gradescope which pages of your PDF solve each problem.
Part 2: Choose a Development Environment
Read the Project Software Setup document and decide where you will do your work.
Also choose what Java development tools you will use. We recommend using the IntelliJ IDE in CSE 331, but the choice is up to you. We also provide instructions for working from the Linux command line. If you choose a different IDE, you will need to figure out how to do the operations from it, or use the command line. IntelliJ is the most popular Java IDE. If IntelliJ is new to you, allocate some time to learn its features. That time will pay off — probably this quarter, and definitely in the future.
Once you have chosen and prepared a development environment, you should set it up for CSE 331 use. See Configuring IntelliJ for 331 and follow any instructions that are applicable. (Note: if you switch development environments later in the quarter, revisit these instructions.)
Part 3: Obtaining files from GitLab
Your homework for this quarter will all be stored and tracked in a Git repository. Since this is the first time you're using this repository, you'll need to "clone" it to get a copy on your local computer. Follow the cloning instructions in the Project Software Setup handout. Also familiarize yourself with the other tools and commands listed in that document, and the concepts of Git in general. Note that, if you attended section, you've already done this with the group.
Part 4: Fix Buggy Code—HolaWorld
For this problem, you will fix some buggy code we provide.
Editing and Compiling Source Files
See Editing and Compiling Source Files to help learn how to perform the following basic tasks: adding new files to your directory structure, compiling Java code, and reading the Java compiler's output (which may indicate errors).
Also read the Google Java Style Guide, which describes some standards for Java style used by Google. We expect you to have consistent, readable style throughout the quarter. This style guide may be a reasonable place to start if you're not sure how to achieve that, but we're not requiring that you exactly follow any specific part of that guide. The most important thing to have is consistent style, including style that is consistent with the starter code you may be modifying.
Fixing HolaWorld
Try to compile the provided code in HolaWorld.java
. You should
see compilation errors for this file. (And possibly in
RandomHelloTest.java
too; if so, ignore these for now since we
will fix them in the next part.) In particular, the lines:
System.out.println(world.);
and:
return SPANISH_GREE;
are problematic. (You might see the second error only after you've fixed the
first one.) If you are using IntelliJ, these errors will be marked with red
squiggly lines in HolaWorld.java
.
Fix these errors and run HolaWorld
by running the
setup:runHolaWorld
gradle target. See the
Editing and Compiling Source Files
handout for more information on running gradle targets.
After you've fixed the errors and run the code, it would be a good time to commit your changes to your repository and push them to GitLab.
Part 5: A new Java class—RandomHello
Create a Java class with a main
method that will randomly
choose, and then print to the console, one of five possible greetings.
Create the file
RandomHello.java
, which will define a Java class
RandomHello
that will reside in the Java package
setup
; that is, its file name is
.../cse331-QUARTER-
YourCSENetID
/hw-setup/src/main/java/setup/RandomHello.java
.
Java requires every runnable class to contain a main
method
whose signature is public static void main(String[] args)
(for example, see the main
methods in HelloWorld
and in HolaWorld
).
A code skeleton for the RandomHello
class is shown below.
IntelliJ will generate an empty class skeleton for you if you use it to
create the new RandomHello
class. You're welcome to use that
and fill in the entire class yourself, or just copy/paste the skeleton we
provide below and work from there.
RandomHello.java
:
package setup; /** RandomHello selects and prints a random greeting. */ public class RandomHello { /** * Prints a random greeting to the console. * * @param args command-line arguments (ignored) */ public static void main(String[] args) { RandomHello randomHello = new RandomHello(); System.out.println(randomHello.getGreeting()); } /** @return a greeting, randomly chosen from five possibilities */ public String getGreeting() { // YOUR CODE GOES HERE } }
This skeleton is meant only to serve as a starting point; you are free to organize your code as you see fit.
No Need to Reinvent the Wheel
Don't write your own random number generator to decide which greeting to select. Instead, take advantage of Java's Random class. (This is a good example of the adage “Know and Use the Libraries” as described in Chapter 9 of Joshua Bloch's Effective Java, 3rd edition. Learning the libraries will take some time, but it's worth it!)
Add the following to the body of your getGreeting()
method:
Random randomGenerator = new Random();
This line creates a random number generator (not a
random number, but a Java object that can
generate random numbers). In IntelliJ, your
code may be marked as an error by a red underline. This is because the
Random
class is defined in a setup that has not yet been
imported (java.lang
and setup
are the only
packages that are implicitly imported). Java libraries are organized as
packages and you can only access Java classes in packages that are imported.
To import java.util.Random
, add the following line under the
package setup;
declaration):
import java.util.Random;
This will import the class
Random
into your file. In IntelliJ, to automatically add all necessary imports and
remove unused imports, either choose "Code | Optimize Imports" on the main
menu, or type Ctrl+Alt+O to optimize your imports
(documentation for optimizing imports).
Because there is only one class named Random
, IntelliJ will
figure out that you mean to import java.util.Random
and will
add the above line of code automatically. (If the name of the class that
needs to be imported is ambiguous — for example, there is a
java.util.List
as well as a
java.awt.List
— then IntelliJ will prompt you to choose the one to import.)
Using java.util.Random
Read the documentation for Random
's nextInt(int n)
method by going to the
Java API
and searching for Random
using the search bar in the top
right-hand corner. In IntelliJ, you can hover over the class or method name
and press a hotkey to view documentation (check your IntelliJ
settings/preferences, under "Keymap" to find your specific hotkey for
"Quick Documentation").
Use the nextInt(int n)
method to choose your greeting. You
don't have to understand all the details of its behavior specification, only
that it returns a random number from 0 to n-1.
One way to choose a random greeting is using an array. This approach might look something like:
String[] greetings = new String[5];
greetings[0] = "Hello World";
greetings[1] = "Hola Mundo";
greetings[2] = "Bonjour Monde";
greetings[3] = "Hallo Welt";
greetings[4] = "Ciao Mondo";
The main
method in the skeleton code above prints the value
returned by getGreeting
. So after you complete
getGreeting
, when the class is run the main
method will print that greeting.
When you are finished writing your code and it compiles, run it using the
runRandomHello
target several times to ensure that all five
greetings can be displayed.
Again, now would be a good idea to add your new class to version control, commit it, and push it.
Part 6: Testing Java Code with JUnit
Testing is essential for writing quality software. JUnit is a framework for creating unit tests in Java. A unit test checks that one specific, small piece (a "unit") of functionality is working correctly. (Junit can be used to create other types of tests, too.) This problem provides a quick overview and simple example of how JUnit works. (Testing will be more significant in later assignments.)
Open both hw-setup/src/main/java/setup/Fibonacci.java
and
hw-setup/src/test/java/setup/FibonacciTest.java
. From the
comments, you can see that FibonacciTest
is a test of the
Fibonacci class.
Now run the JUnit test
setup.FibonacciTest
. You'll notice that it's failing its tests!
Examine the test code and gradle's test output to understand why the tests
are failing, then use that information to find and fix the bugs in Fibonacci
so it's passing all the tests. The tests are correct, so you shouldn't
modify FibonacciTest at all, but it's probably useful to look at the code
and see what it's doing.
Now look at the JUnit tests that we wrote for HolaWorld
and
RandomHello
. They are called HolaWorldTest
and
RandomHelloTest
, respectively. Ensure that your modified code
passes these tests before you turn in your homework.
Part 7: Answering Questions About the Code
Locate the answers.txt
file in your repo; you'll find it in
hw-setup/src/main/java/setup/
. In that file, write your answers
to the following questions using a few sentences max for each question.
- Why did Fibonacci fail the testThrowsIllegalArgumentException test? What (if anything) did you have to do to fix it? If you did not have to change anything to fix this issue, explain why.
- Why did Fibonacci fail the testBaseCase test? What (if anything) did you have to do to fix it? If you did not have to change anything to fix this issue, explain why.
- Why did Fibonacci fail the testInductiveCase test? What (if anything) did you have to do to fix it? If you did not have to change anything to fix this issue, explain why.
Part 8: Debugging Tutorial
In this part, you will learn about two methods for debugging - printing and IntelliJ's built-in debugger. Printing is an easy way to check the state of variables in a certain point in the program, allowing you to quickly detect problems. A debugger can help you debug your program by allowing you to "watch" your program as it executes, one line at a time, and inspect the state of variables along the way. In many cases, using a debugger can be much more powerful and convenient than littering your program with print statements.
One of the most useful features of a debugger is the ability to set breakpoints in your program. When you run your program through the debugger, it will pause when it reaches a line with a breakpoint. You can inspect the current state of variables, then continue running your program normally or step through one line at a time to watch the variables change.
You should reference the IntelliJ Debugger Documentation throughout this section to learn how to use the debugging tools in IntelliJ. We won't be explaining every step of using the debugger in this section, so this is a good opportunity to practice reading documentation!
As you follow the instructions below, write answers to the questions below
in the answers.txt
file.
-
Open
Adder.java
. This simple program is supposed to print the sum of two user-provided integers. Try running it a few times (or reading the source code) and you'll see that it doesn't behave as expected. You can run the program by clicking the play button next to its main method, or by running the gradle targetrunAdder
. -
Locate the line
int sum = computeSum(x, y);
(line 32). Right between this line and the next one, write the print statement printing the variablesx
,y
, andsum
. Then, run the main method, enter 5 as the first integer and 4 as the second one. You should see thatx
is not the first integer you entered.Question 1: What are the values of
x
,y
, andsum
printed by your statement? Why wasx
not equal to the first integer entered here, and how could we fix this? -
Change the main method so that
x
is always equal to the first integer entered. Make sure to only change the main method in this step. -
Now we will start using the debugger. Click in the left-hand margin of
the class next to the line
return x - y;
A red circle should appear, indicating a breakpoint. (Clicking again removes the breakpoint.) -
Run the program in debug mode by right-clicking on
Adder.java
and clicking “Debug...”. As before, enter two ints (say, 3 and 4) in the console when prompted. When your program hits the breakpoint, IntellIJ will pause the program and a debugging window will show up at the bottom of your screen. -
The debug perspective looks overwhelming at first, but don't worry!
Look at the “Variables” pane and you'll see the names and
values of all variables in the current context (which, at this
breakpoint, is the
computeSum
method). The left panel in the frames tab shows where the program is currently paused, where the current method was called, and so on. (This is called the stack trace). Double-click on a method name to see the corresponding line in your source code. Finally, the console tab (next to “Debugger,” right above the “frames” area of the debugger) shows the console window.Question 2: What are all the names and values listed in the “Variables” panel? What does the “frames” tab list as the current method and line number? (Write down the text that was highlighted when the Debug perspective first opened.)
-
Immediately above the “Debugger” panel, and on the left are
several groups of buttons for running your program. Mouse over each one
for a description. “Resume” (green arrow) causes your
program to continue executing normally until it finishes or hits another
breakpoint. If you want to monitor what happens shortly after the
breakpoint, use “Step Into” and “Step Over”
(blue arrows near the top of the debugger). “Step Over”
executes the current line and pauses on the next line. “Step
Into” enters any method called on the current line so you can
execute that method line-by-line. (To finish the current method and jump
back to the caller, use “Step Out.”) Hit “Step
Over” once to execute the
return
statement and exitcomputeSum
. Hit “Step Over” again to progress to the next line.Question 3: What are all the names and values listed in the “Variables” panel after each of the two step overs?
- Hit “Resume” to allow the program to finish executing.
Being able to step through each piece of code and examine what's going on is a very powerful way to understand what your code is doing any why things may be going wrong. The IntelliJ debugger can to so much more than just what we've discussed here, so make sure to read the documentation linked above to learn more about what it can do to help you fix any problems you might have in your code.
Part 9: Implementing Code with Invariants— Finder and Evaluator
In the last two problems of HW1, you verified the correctness of two pieces of your by reasoning. You were given loop invariants in each question, which helped in your reasoning. In this question, you will do the opposite - given different loopinvariants or pre/postconditions, you will write code that matches them.
Implementing Variants of Finder
Locate the Finder.java
file in your repo, and open it. Here,
the find1
method is the code given in HW1 Problem 7. There are
also the methods find2, find3, find4
with incomplete parts.
Follow the instructions given by TODO
comments to complete
these methods. Each version, when completed, will have the same
functionality as find1
(i.e., it will produce the same outputs
when given the same, valid inputs), but it will accomplish that in a
slightly different way, as described by its own, particular loop invariant.
You shouldn't make changes other than those denoted by the TODO
comments while implementing these methods. Also, make sure to remove any
TODO
comments after you are done, so that it's clear no more
work needs to be done in them. There are JUnit tests we wrote for this class
in FinderTest
, your final implementation should pass those tests
as well. Since we put assert
statements in your code, passing
these tests would also likely mean that your code satisfies the given
invariant. However, you should already have confirmed this by reasoning.
Implementing Variants of Evaluator
Locate the Evaluator.java
file in your repo, and open it.
Similar to Finder.java
, the first method
(evalPoly1
) is the code given in HW1 Problem 8. The method
evalPoly2
is incomplete, with different functionality. Make
sure to read the comments to see how the intended behavior for this method
is different than the first one. Then, complete this method by making
changes as described in the TODO
comments. When completed, the
loop invariant and the postcondition given in the method should hold at
those points.
You shouldn't make changes other than those denoted by the TODO
comments while implementing these methods. Make sure to remove any
TODO
comments after you are done, so that it's clear no more
work needs to be done in them. There are JUnit tests we wrote for this
class in EvaluatorTest
, your final implementation should pass
those tests as well.
Unlike Finder.java
, this file doesn't contain any
assert
statements. Therefore, your implementation is not
guaranteed to satisfy the given invariant just because it passes the given
tests. (Your code could have the correct behavior but be using a different
invariant.) You will need to rely on reasoning alone to confirm that your
code satisfies the given invariant.
How to Turn In Your Homework
Your answers in Part 1 should be submitted in Gradescope, as a PDF file. This should be the only part you are submitting in Gradescope.
At the end of each assignment, you must refer to the Assignment Submission Handout and closely follow the steps listed to submit the rest of your assignment. Do not forget to double check your submission as described in that handout - you are responsible for any issues if your code does not run when we try to grade it.
Use the tag name hw2-final for this assignment.
To verify your assignment on attu, you can use any of the gradle tasks
described elsewhere in this assignment: hw-setup:runHelloWorld
,
hw-setup:runHolaWorld
, and hw-setup:runRandomHello
.
Don't forget to write your answers to part 7 and 8 in the provided answers.txt file in your repo.
We should be able to find the following in the hw-setup/src
directory of your repository:
-
HolaWorld.java
that works as described in Part 4 with no compilation errors -
RandomHello.java
that prints out one of five random messages when itsmain
method is executed -
Fibonacci.java
that passes the three tests inFibonacciTest.java
(Note that you should not editFibonacciTest.java
to accomplish this task.) -
Finder.java
andEvaluator.java
, where the implemented parts satisfy the given invariants and/or assertions. These classes should also pass the tests written for them, and you should not edit any test files to accomplish this task.