01 Prove
Learning Java with Three Examples
Objectives
Create a Simple Calculator in Java
Create a Form Letter in Java
-
Test and Modify an existing Encryption Program
The three programs that you will write in this first assignment are intended to be a tutorial of the Java language. All the steps for coding the solutions can be found in this assignment. You should follow all the steps and create all three programs as described. You will need to submit the code that you write.
Future assignments will not provide all of this specific detail. You will need to learn to find additional resources online and ask questions to your teacher and peers. You should also refer to the additional examples and notes provided as support each week. You can find these examples in the weekly modules within I-Learn.
Before starting this assignment, you should complete the 01 Teach: Team Activity individually and (if teams are established already) as a team to ensure you have IntelliJ working and can do "Hello World".
If you need a good and simple reference manual for Java, consider using the W3Schools Java Tutorial.
Problem 1: Simple Calculator
The purpose of this program is to write a simple four function calculator using two classes. The first class will be called Calculator
and the second class will be called TestCalculator
. The TestCalculator
class will have the main
function and will be used to test the Calculator
.
Create a new project in ItelliJ called
Calculator
.Create a new package inside the
src
folder of your project. Use your last name for the package name.Create two new Java Classes in your package called
Calculator
andTestCalculator
. Notice that IntelliJ will put thepackage
statement and the empty class definition in both of the files for you.Our calculator will maintain a current result and do basic math operations. Here is the summary of what you want in the class:
In the
Calculator
class, first write the member data:If there is member data, then you need a constructor to initialize the member data. Constructors in Java are named the same as the class and should be public. Create a constructor for the
Calculator
class after the member data. Note that0.0f
means float and0.0
means double.Implement the
add
,subtract
,multiply
, anddivide
functions described in the table after the constructor. Note that each parameter includes the data type. Also note that the return type of the function is also specified. If the function does not return anything then you usevoid
. Thedivide
function must use anif
statement to ensure you do not try division by zero.Implement the
reset
function. This function has no inputs.Implement the
getResult
function. This is called a getter function and will provide a copy of the private data. In IntelliJ, you can automatically create this function by going to menu option Code, select Generate, and select Getter.Now that you have the
Calculator
class written, you will write some test code in theTestCalculator
class. Create amain
function in theTestCalculator
class.In your
main
function, create aCalculator
object using thenew
keyword. All objects are created dynamically on the heap in Java. You don't have to delete them when you are done. Java will use "garbage collection" to delete unused objects. TheCalculator
object variable that you create is holding a reference or pointer to the object.Print out the current result of the calculator by calling the
getResult
function.Run the program by pressing the green play button in IntelliJ to the left of the
main
function code. It should display "Result = 0.0".Change the code to add 5, multiply by 3, subtract 1, and then divide by 2. Run it again and you should get "Result = 7.0".
Create a
do while
loop in yourmain
function (after the code you have already written) to allow the user to select an operation. Thedo while
loop will keep running until the user selects the exit option. Reading from the keyboard requires the use of theScanner
class provided by Java.Scanner
allows you to read a line of text. Notice that when you create theScanner
object, IntelliJ will show an error until you provide theimport
statement. Java requires that you specify which libraries you are using. You only have to create theScanner
object once before the loop. When you ask for values from the user, we need to convert theString
to afloat
usingFloat.parseFloat
. Note thatparseFloat
is a static function which is why you don't need aFloat
object to call it. Also note that you can not use==
to compare strings in Java. You must use theequals
function.Test your code with different inputs to make sure everything works. Remember to test divide by 0.
Member Data / Attributes | Data Type | Scope | Purpose |
---|---|---|---|
result | float | private | The current result stored in the calculator |
Member Functions / Methods | Parameters | Return Type | Scope | Purpose |
---|---|---|---|---|
add | float value | void (don't return anything) | public | Add a value to the current result |
subtract | float value | void | public | Subtract a value from the current result |
multiply | float value | void | public | Multiply a value with the current result |
divide | float value | void | public | Divide a value with the current result. If the value is 0, then set the result to 0. |
reset | None | void | public | Reset the result back to 0 |
getResult | None | float | public | Return a copy of the result |
public class Calculator {
private float result;
}
public Calculator() {
result = 0.0f;
}
public void add(float value) {
result += value;
}
public void subtract(float value) {
result -= value;
}
public void multiply(float value) {
result *= value;
}
public void divide(float value) {
/* Check for divide by zero */
if (value == 0.0f) {
result = 0.0f;
} else {
result /= value;
}
}
public void reset() {
result = 0.0f;
}
public float getResult() {
return result;
}
public class TestCalculator {
public static void main(String[] args) {
}
}
public static void main(String[] args) {
Calculator calc = new Calculator();
}
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println("Result = " + calc.getResult());
}
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println("Result = " + calc.getResult());
calc.add(5.0f);
calc.multiply(3.0f);
calc.subtract(1.0f);
calc.divide(2.0f);
System.out.println("Result = " + calc.getResult());
}
import java.util.Scanner;
public class TestCalculator {
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println("Result = " + calc.getResult());
calc.add(5.0f);
calc.multiply(3.0f);
calc.subtract(1.0f);
calc.divide(2.0f);
System.out.println("Result = " + calc.getResult());
Scanner scanner = new Scanner(System.in);
String choice;
do {
System.out.println();
System.out.println("Result = " + calc.getResult());
System.out.print("Operation (add, subtract, multiply, divide, reset, exit): ");
choice = scanner.nextLine();
if (choice.equals("add")) {
System.out.print("Enter value: ");
float value = Float.parseFloat(scanner.nextLine());
calc.add(value);
} else if (choice.equals("subtract")) {
System.out.print("Enter value: ");
float value = Float.parseFloat(scanner.nextLine());
calc.subtract(value);
} else if (choice.equals("multiply")) {
System.out.print("Enter value: ");
float value = Float.parseFloat(scanner.nextLine());
calc.multiply(value);
} else if (choice.equals("divide")) {
System.out.print("Enter value: ");
float value = Float.parseFloat(scanner.nextLine());
calc.divide(value);
} else if (choice.equals("reset")) {
calc.reset();
} else if (choice.equals("exit")) {
System.out.println("Goodbye!");
} else {
System.out.println("Invalid choice.");
}
} while (!choice.equals("exit"));
}
}
Problem 2: Form Letter
The purpose of this program is to learn how to use a simple data structure called the ArrayList. The ArrayList
is like the Vector
in C++ or the list
in Python. When you create an ArrayList
(like other data structures in Java) you have to specify what it will hold. You are going to hold an ArrayList
of Account
objects. You will use these Account
objects to populate form letters. This will allow you to demonstrate loops.
Create a new project in ItelliJ called
FormLetter
.Create a new package inside the
src
folder of your project. Use your last name for the package name.Create two new Java Classes in your package called
Account
andLetterGenerator
. Notice that IntelliJ will put thepackage
statement and the empty class definition in both of the files for you.The
Account
Class will contain the following member data.Write the member data in the
Account
class and write a construtor to intialize all the member data. This time you want the constructor to accept the three pieces of member data as parameters. Just like the getters, you can use IntelliJ to auto-generate a constructor for the class. Specify that you want all three parametersname
,lastPurchase
, anddaysSincePurchase
to be included in the constructor. Note the use of the keywordthis
. The keywordthis
is used when you want to refer to your object. Python used the keywordself
.Using IntelliJ, auto-generate the getters and the setters for the
Account
class.The
LetterGenerator
class will have the following member data and member functions:Add the member data and provide a constructor that initializes the
ArrayList
to an empty list and theformLetter
to a example letter. Notice that when you create theArrayList
, IntelliJ will show an error until you provide theimport
statement. Java requires that you specify which libraries you are using.Implement the
addAcccount
function. This function will use aScanner
to read in the information and create anAccount
object. You will add theAccount
object to ouraccounts
list by using theadd
function.Start the implementation of the
generateLetters
function. This function will loop through each account in the accounts list. Thefor loop
defines a variable calledcurrAccount
which will be populated with the individualAccount
objects in the list.In the loop, replace "[name]" with the
name
fromcurrAccount
. To get thename
from thecurrAccount
, call thegetName
function that you created in theAccount
class. Do the same for "[days]" and "[purchase]". Notice that you need to convert the daysint
to aString
usingString.valueof
. TheString
class in Java has lots of functions available to use. Look in the JavaDoc to see more available functions (good place to bookmark).Create a
main
function in theLetterGenerator
to test out your code. CalladdAccount
three times with different information and then callgenerateLetters
. Check to make sure the form letter is populated properly each time.
Member Data / Attributes | Data Type | Scope | Purpose |
---|---|---|---|
name | String | private | The account holder name |
lastPurchase | String | private | The name of the last thing purchased |
daysSincePurchase | int | private | The number of days since the last purchase |
public class Account {
private String name;
private String lastPurchase;
private int daysSincePurchase;
public Account(String name, String lastPurchase, int daysSincePurchase) {
this.name = name;
this.lastPurchase = lastPurchase;
this.daysSincePurchase = daysSincePurchase;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastPurchase() {
return lastPurchase;
}
public void setLastPurchase(String lastPurchase) {
this.lastPurchase = lastPurchase;
}
public int getDaysSincePurchase() {
return daysSincePurchase;
}
public void setDaysSincePurchase(int daysSincePurchase) {
this.daysSincePurchase = daysSincePurchase;
}
Member Data / Attributes | Data Type | Scope | Purpose |
---|---|---|---|
accounts | ArrayList<Account> | private | A collection of account objects. |
formLetter | String | private | A form letter than contains replaceable tags |
Member Functions / Methods | Parameters | Return Type | Scope | Purpose |
---|---|---|---|---|
addAccount | nothing | void | public | Prompt the user to enter in account information and then add it to the accounts list. |
generateLetters | nothing | void | public | Generate the letters for each account in the list |
import java.util.ArrayList;
public class LetterGenerator {
private ArrayList<Account> accounts;
private String formLetter;
public LetterGenerator() {
accounts = new ArrayList<Account>();
formLetter = "Dear [name], its been [days] since your last purchase " +
"of the [purchase]. We hope to see you again soon!";
}
}
public void addAccount() {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter name: ");
String name = scanner.nextLine();
System.out.print("Enter last item purchased: ");
String item = scanner.nextLine();
System.out.print("Enter days since last purchase: ");
int days = Integer.parseInt(scanner.nextLine());
Account account = new Account(name, item, days);
accounts.add(account);
}
public void generateLetters() {
for (Account currAccount : accounts) {
}
}
public void generateLetters() {
for (Account currAccount : accounts) {
String letter = formLetter.replace("[name]", currAccount.getName());
letter = letter.replace("[days]", String.valueOf(currAccount.getDaysSincePurchase()));
letter = letter.replace("[purchase]", currAccount.getLastPurchase());
System.out.println(letter);
System.out.println();
}
}
public static void main(String [] args) {
LetterGenerator letterGenerator = new LetterGenerator();
letterGenerator.addAccount();
letterGenerator.addAccount();
letterGenerator.addAccount();
letterGenerator.generateLetters();
}
Problem 3: Enryption Code
The purpose of this problem is to practice using existing code. The existing code is a class called NSALoginController
which will encrypt and validate passwords. Your first step will be to copy the code into your project. In the second step, you will write some test code to make sure the NSALoginController
is working. In the third step, you will modify the NSALoginController
to provide for more strict password requirements.
Part I - Import Existing Code
Start by creating a new Project in IntelliJ called
NSALogin
Create a package using your last name.
Download the files for this project. After unzipping this file, look for
NSALoginController.java
. You can drag and drop this file into your package within IntelliJ. When you do this, it will change the package statement inNSALoginController
.Browse through the code in the
NSALoginController
class to see what functions are available. Consider making a UML diagram to better understand the class. You don't need to understand how all this code works to do encryption. Take special note of thehashUserPassword
andverifyPassword
functions. You will be testing those in the next section. Also note that these functions arestatic
. Astatic
function does not require an object to call. Also note that each of these functions hasthrows Exception
in the function declaration line. You will be required to catch these potential exceptions in your test code.The purpose of
hashUserPassword
is to take aUser
object (you will need to create that class later), read its password, and generate a salt and encrypted hashed password. The generated salt is used to encrypt the password. It will then delete the password for security. The salted and hashed password are the only things we need to save.The purpose of
verifyPassword
is to take aUser
object (with the salt and hashed password still populated along with the password we are testing) and return aboolean
indicating whether or not the user's password is correct.
Part II - Test the EXISTING CODE
Create a new class called
Test
. You will put yourmain
function in here.Create a new class called
User
. TheNSALoginController
requires that you have aUser
object with the member data shown in the table below. Note that theNSALoginController
expets these variables (and their getters and setters) to be correctly named.Create a constructor that passes in the
password
and sets thesalt
andhashedPassword
to empty strings. Use IntelliJ to auto-generate the getters and setters.In the
main
function of theTest
class, you will first test thehashUserPassword
function inNSALoginController
. You will first need create aUser
object since both functions require aUser
object. You should add some debug code to make sure our user object was created correctly.Pass the
User
object to thehashUserPassword
. Since the function might throw anException
, we are required tocatch
that potential exception. If something fails in thetry
block (e.g.hashUserPassword
throws an exception) then execution will be immediately diverted to thecatch
block. Again, we should add some debug code to see if the hash operation worked. Take note that thepassword
should be cleared after thehashUserPassword
function is called.To test the
verifyPassword
, you will ask the user for theirpassword
. We will add thepassword
into our previously createdUser
object. That object still has thesalt
andhashedUserPassword
populated. TheverifyPassword
function will compare thepassword
with thesalt
andhashedUserPassword
values. This function can throw an exception too so we will keep it in thetry
block. The function also returns aboolean
. If it returnstrue
, then the password matched.Run your code several times to prove that
NSALoginController
is working properly.
public class Test {
public static void main(String[] args) {
}
Member Data / Attributes | Data Type | Scope | Purpose |
---|---|---|---|
password | String | private | The raw password from the user |
salt | String | private | A value generated for the user that provides variation in the hashedpassword generation. This is done for security. |
hashedPassword | String | private | The encrypted version of the password generated with the salt. |
public class User {
private String password;
private String salt;
private String hashedPassword;
public User(String password) {
this.password = password;
this.salt = "";
this.hashedPassword = "";
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getHashedPassword() {
return hashedPassword;
}
public void setHashedPassword(String hashedPassword) {
this.hashedPassword = hashedPassword;
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter password: ");
String password = scanner.nextLine();
User user = new User(password);
// Only Password should be set at this point
System.out.println("Password: " + user.getPassword());
System.out.println("Salt: " + user.getSalt());
System.out.println("Hashed Password: " + user.getHashedPassword());
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter password: ");
String password = scanner.nextLine();
User user = new User(password);
// Only Password should be set at this point
System.out.println("Password: " + user.getPassword());
System.out.println("Salt: " + user.getSalt());
System.out.println("Hashed Password: " + user.getHashedPassword());
try {
// hashUserPassword is a static function (no NSALoginController object needed)
// This function will set the salt and hashedPassword in the user object
NSALoginController.hashUserPassword(user);
// Only salt and hashed password set at this point
System.out.println("Password: " + user.getPassword());
System.out.println("Salt: " + user.getSalt());
System.out.println("Hashed Password: " + user.getHashedPassword());
}
catch(Exception e) {
System.out.println("General Exception: " + e.toString());
}
}
try {
// hashUserPassword is a static function (no NSALoginController object needed)
// This function will set the salt and hashedPassword in the user object
NSALoginController.hashUserPassword(user);
// Only salt and hashed password set at this point
System.out.println("Password: " + user.getPassword());
System.out.println("Salt: " + user.getSalt());
System.out.println("Hashed Password: " + user.getHashedPassword());
// Run a test of the hashed password.
// Ask the user for the password and set it in the user object
System.out.print("Enter password: ");
password = scanner.nextLine();
user.setPassword(password);
// verifyPassword will check the password in the user object against the salt
// and hashed password.
if (NSALoginController.verifyPassword(user))
{
System.out.println("Verified!");
}
else
{
System.out.println("Not Verified!");
}
}
catch(Exception e) {
System.out.println("General Exception: " + e.toString());
}
Part III - Modify the EXISTING CODE
Once you have proven that the
NSALoginController
is working, you can now modify it. You need to change it to require a password length of at least 8 and a requirement to have at least one digit in the password. We can use the.length
property on aString
to determine the length. To determine if there is a digit, we can loop through each letter of the password and use theCharacter.isDigit
function.If password checking has failed, then we have to exit the function and notify the calling function. However, recall that this function is throwing exceptions (which we had to catch in our main function). One solution is to throw a new
Exception
object.In Java, it can be useful to create new exception classes for special scenarios like this one. Creating your own exception class provides for better readability of the code and allows you to respond to errors less generically. To create your own exception class, you have to inherit the base
Exception
class.Create a new class called
WeakPasswordException
. When the class is created, modify it toextends
theException
class. This is how you inherit a base class.This class will be very simple. It will not have any member data within it. You are allowed, but in this case you don't have any need. The only additional requirement is that you provide a constructor that will pass the customized error message up to the constructor of the
Exception
base class. You can call the parent constructor by usingsuper
.With the creation of
WeakPasswordException
, we can now use it in ourhashUserPassword
function. Note you also need to state that you canthrows Exception, WeakPasswordException
.Since this function can throw multiple types of exceptions now, our
main
function code can list them out separately. You can have multiplecatch
blocks in your code for a singletry
blockTest out your code to make sure that the "Weak Password Error" message is displayed if a weak password is provided by the user.
public static void hashUserPassword(User user) throws Exception {
// Get the next random salt value to use for this password
byte[] salt = getNextSalt();
char[] password = user.getPassword().toCharArray();
// Verify password rules:
// 1) Length at least 8 characters
// 2) Contain at least one digit
if (password.length < 8) {
// Do Something!
}
boolean hasDigit = false;
for (Character c : password) {
if (Character.isDigit(c)) {
hasDigit = true;
}
}
if (!hasDigit) {
// Do Something!
}
public static void hashUserPassword(User user) throws Exception {
// Get the next random salt value to use for this password
byte[] salt = getNextSalt();
char[] password = user.getPassword().toCharArray();
// Verify password rules:
// 1) Length at least 8 characters
// 2) Contain at least one digit
if (password.length < 8) {
throw new Exception("Password must be at least 8 characters long.");
}
boolean hasDigit = false;
for (Character c : password) {
if (Character.isDigit(c)) {
hasDigit = true;
}
}
if (!hasDigit) {
throw new Exception("Password must contain at least 1 digit.");
}
public class WeakPasswordException extends Exception {
}
public class WeakPasswordException extends Exception {
public WeakPasswordException(String errorMessage)
{
super(errorMessage);
}
}
public static void hashUserPassword(User user) throws Exception, WeakPasswordException {
// Get the next random salt value to use for this password
byte[] salt = getNextSalt();
char[] password = user.getPassword().toCharArray();
// Verify password rules:
// 1) Length at least 8 characters
// 2) Contain at least one digit
if (password.length < 8) {
throw new WeakPasswordException("Password must be at least 8 characters long.");
}
boolean hasDigit = false;
for (Character c : password) {
if (Character.isDigit(c)) {
hasDigit = true;
}
}
if (!hasDigit) {
throw new WeakPasswordException("Password must contain at least 1 digit.");
}
try {
// Only Password should be set at this point
System.out.println("Password: " + user.getPassword());
System.out.println("Salt: " + user.getSalt());
System.out.println("Hashed Password: " + user.getHashedPassword());
// hashUserPassword is a static function (no NSALoginController object needed)
// This function will set the salt and hashedPassword in the user object
// We expect that the password will be cleared.
NSALoginController.hashUserPassword(user);
// Only salt and hashed password set at this point
System.out.println("Password: " + user.getPassword());
System.out.println("Salt: " + user.getSalt());
System.out.println("Hashed Password: " + user.getHashedPassword());
// Run a test of the hashed password.
// Ask the user for the password and set it in the user object
System.out.print("Enter password: ");
password = scanner.nextLine();
user.setPassword(password);
// verifyPassword will check the password in the user object against the salt
// and hashed password.
if (NSALoginController.verifyPassword(user)) {
System.out.println("Verified!");
}
else {
System.out.println("Not Verified!");
}
}
catch (WeakPasswordException e) {
System.out.println("Weak Password Error: " + e.getMessage());
}
catch(Exception e) {
System.out.println("General Exception: " + e.toString());
}
Submission
To submit this assignment (and future individual assignments), you need to use the following procedure:
Upload your code to the "Code Submission" assignment in I-Learn. You only need to submit your Java files. You can zip them up or submit them individually.
Take a multiple choice, self-evaluation "Assessment" of the functionality in I-Learn.
Once you have submitted the assessment, a link to the instructor's solution to this assignment becomes available. You are expected to look through the instructor's code, which is heavily commented to explain why certain choices were made. Keep in mind that this is not the only way to complete the assignment.
If desired, you can use the instructor's solution to help guide you through completing anything you were not able to finish. Then, you can re-submit the code and multiple choice assessment above, and an average of the two scores will be used. Thus, if you got 80% the first time, but after looking at the instructor's solution, you were able to complete everything for a 100%, your final score will be 90% for that portion.
Please note that the "Code Submission" element does not receive a score itself in the gradebook. It is provided so the instructor can refer to it, but the "score" is attached to the multiple choice self assessment.
Future assignments will follow this same procedure.