
Note: These notes are a mixture of lecture material, textbook material and private research. All credit to the University of Melbourne.
These are my notes for when I was learning Java.
Downloading JDK gives you a java compiler
$ javac
Write java with .java extension
All java files must have a class:
class Apples{
// To run a java file need a 'main method'
public static void main(String args[]){
System.out.println("Hello Youtube");
}
}
Compiling converts .java
to .class
$ javac youtube.java
Now have a .class file. Apples is the name of the class $ java Apples Hello Youtube!
Conventions
Two blank lines should always be used in the following circumstances:
- Between sections of a source file
- Between class and interface definitions
One blank line should always be used in the following circumstances:
- Between methods
- Between the local variables in a method and its first statement
- Before a block (see section 5.1.1) or single-line (see section 5.1.2) comment
- Between logical sections inside a method to improve readability
// A keyword followed by a parenthesis should be separated by a space. Example:
while (true) {
...
}
// The expressions in a for statement should be separated by blank spaces. Example:
for (expr1; expr2; expr3)
// Returning values
// 1)
if (booleanExpression) {
return true;
} else {
return false;
}
// should be:
if booleanExpression;
// 2)
if (condition) {
return x;
}
return y;
// should be:
return (condition ? x : y);
Week 1
//import scanner class
import java.util.Scanner;
//create scanner object called keyboard
Scanner keyboard = new Scanner(System.in);
.next();
.nextLine();
+;
Week 2
//arithmetic operators
+ - * / %;
//comparison operators
< <= > >= == !=;
//&& - iff (if and only if), || - or
&& || !;
//defining variables
String str = "string";
//using double quotes in strings
String str = "Statement with \"double\" quotes or a backslash (\\)";
//newline, , , tab
\n \r \b \t;
System.out.println("1 + 1 = " + 1 + 1);
//prints "1 + 1 = 11"
System.out.println("1 + 1 = " + (1 + 1));
//prints "1 + 1 = 2"
//can declare multiple variables of same type with comma seperated list
String s, s2; int i, j;
//string methods
s.length();
s.toUpperCase();
s.toLowerCase();
s.substring(i, j); //from i to j-1
s.equals(s2);
s.indexOf(s2); //index of the start of s2
x = x + 1;
x += 1;
x *= 1;
x -= 1;
x /= 1;
done |= x > 10;
done &= x == 0;
//casting - take object of one type and turning into another
type1 vartype1 = (cast type1)vartype2
int sum; int count;
double average = (double)sum / count;
System.out.printf(format-string, args);
System.out.printf("Average: %5.2f", average);
System.out.printf("Money: $%.2f", money);
String s1 = sc.next();
int x = sc.nextInt();
//read string and int. print string with spacing of 15 left justified
//and int with 3 digits and preceding 0's
System.out.printf("%-15s%03d\n", s1, x);
%d %s %c %f %e %g % n;
//main class method
public static void main(String[] args) {
System.out.println("Arg1 = " + arg[0] + ", Arg2 = " + args[1]);
}
Integer.parseInt(string);
//
Scanner keyboard = new Scanner(System.in);
String line = keyboard.nextLine();
.next();
.nextInt();
.nextDouble();
Week 3
//if-else statement
if (expr1) {
statement1;
} else if (expr2) {
statement2;
}
else {
statement3;
}
//if-else expression - ternary operator
expr1 ? expr2 : expr3;
//switch
switch (expr) {
case value1:
statement1;
break;
case value2:
statement2;
break;
default:
statement3;
break;
}
//while loop - executes 0 or more times
while (expr) {
statement;
}
//do while loop - executes 1 or more times
do {
statement;
} while (expr);
public static void main(String args[]) {
statements;
}
//increment after
i++;
//increment before
++i;
//increment var by i and store
var += i;
//print
System.out.println("String1" + "String2");
//for loop
for (init ; test ; update) {
statement;
}
//don't use == or != in (avoids infinite loops)
//terminates innermost loop
break;
//returns to top of innermost loop
continue;
//exit program, 0 for success
System.exit(0);
//exit program, >0 for error
System.exit(1);
//for loop - n times
for (i=0 ; i < n ; ++i) statement;
for (i=1 ; i <=n ; ++i) statement;
//assert - stops program when test fails
assert(test);
Week 4
//using class methods
class.method(expr1, expr2, arg1, arg2 ...);
//don't need to state class
.method(expr1, arg2 ...);
//math class
Math.abs(int);
.ceil(double);
.E();
.max(int, int);
.min(int, int);
.floor(double);
.PI()
.pow(double, double);
.sqrt(double);
//each primitive has a wrapper class
Byte x;
Short x;
//primitve to string
Byte.toString(x);
Short.toSring(x);
//string to primitive
String s;
Byte.parseByte(s);
Short.parseShort(s)
//main class method
public static void main(String[] args) {statements}
vis class/instance type name(type1 var1, type2, var2 ...) {statements}
//defining constants
public static final int DAYS_PER_WEEK = 7;
//class variables declared inside class
public class ClassVarExample {
//use private, class variable is local to the class
private static String name = "Someone";
public static void main(String[] args) {
statements;
}
}
//local variables - declared in methods, constructors or blocks
public class Test {
public void method() {
//don't declare public or private
int var = 0;
//scope limited to this method
}
}
int methodOverload1(int Year, int Month);
int methodOverload1(int Year);
int methodOverload2(int Year);
int methodOverload2(float Year);
Week 5
//creating objects
ClassName object = new ClassName(expr1, expr2 ...);
//using an object, send a msg to it
object.msg(expr1, expr2 ...);
//objects can be passed as an argument of msg to another object
object1.msg(object2);
//instance variables/methods
public class ClassName {
//instance vars, use private
private String instanceVarName1;
private String instanceVarName2;
//instance method (no 'static')
public String instanceMethod() {
return instanceVarName1 + ", " + instanceVarName2;
}
//need an object to call an instance method
public static void main(String[] args) {
ClassName p = new ClassName;
p.instanceMethod();
}
}
//accessors/getter - method to access private instance variable
public String getInstanceVar() {
return instanceVar;
}
//constructor - method to initialise instance variables when creating object
//same as creating method, but method name needs to be same as class
public ClassName(type 1 var1, ...) {
var1 = value;
}
//if we don't do this, variable doesn't have a value therefore can't use
//doing this allows us to use variable as soon as we create object
//now pass variable in as parameter when creating new object
//e.g. tuna newTuna = new tuna('var');
//this - holds the object the current message is sent to inside method
//only instance messages can use this, class messages can't
//constructor
public Person(String givenName, String familyName) {
//this refers to Person object
this.givenName = givenName;
this.familyName = familyName;
}
//final - finality makes objects immutable
public class ImmutablePerson {
//immutable instance variables
private final String familyName;
private final String givenName;
//constructor
public ImmutablePerson(String familyName, String givenName) {
this.familyName = familyName;
this.givenName = givenName;
}
//adding this method gives a compiler error
public void bogus(String s) {
this.familyName = s;
}
}
//null - can be used in place of an object signifying there's no object
String s = "string1";
if (s != null) {
System.out.println("The string is " + s);
}
Week 6
//changing objects
public class Person {
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public String toString() {
return name + ", age " + age;
}
public static void main(String[] args) {
Person foo = new Person(5, "Bo Peep");
//can't just reassign foo, need to point to new object
foo = new Person(3, "Shaun Sheep");
//Java "collects garbage" automatically - old foo object removed
//can also mutate objects
}
}
//mutator/setter - change objects instance vars without creating new object
//for mutable classes e.g. instance vars are not final
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) {
Person foo = new Person(5, "Bo Peep");
foo.setAge(6);
}
//mutating aliased objects
public static void main(String[] args) {
Person foo = new Person(5, "Bo Peep");
Person bar = foo;
foo.setAge(6);
//foo and bar both point to updated object of age 6
}
//class invariants - a constraint on the objects of a class
public class MissingPerson {
private Person seeker;
private String location;
private Person sought;
public MissingPerson (Person seeker, String location, Person sought) {
//ensure that all instances have name/location properties
if (seeker.getName().isEmpty() || location.isEmpty()) {
System.out.println("Missing name/location");
System.exit(1);
}
this.seeker = seeker;
this.location = location;
this.sought = sought;
}
}
//shared mutable object issues
public static void main(String[] args) {
Person p1 = new Person(5, "Bo Peep");
Person p2 = new Person(3, "Shaun Sheep");
MissingPerson mp = new MissingPerson(p1, "Field", p2);
System.out.println(mp);
//prints: "Bo Peep, age 5, at Field, seeks Shaun Sheep, age 3"
p1.setName("Bad Wolf");
p1.setAge(10);
System.out.println(mp);
//prints: "Bad Wolf, age 10, at Field, seeks Shaun Sheep, age 3"
//p1 object argument for mp object, when p1 changes so does mp
}
//copy constructor - makes new object exact copy of input argument
//argument is same type as object being created
public Person(Person orig) {
this.age = orig.age;
this.name = orig.name;
}
//deep copy - when instance vars are mutable classes, copy also
public MissingPerson(MissingPerson orig) {
this.seeker = new Person(orig.seeker);
this.location = orig.location; //strings are immutable
this.sought = new Person(orig.sought);
}
//example - plugging privacy leak
//stopping methods from other classes getting hold of mutable objects
//stored in private instance variables through deep copying
public class MissingPerson {
private Person seeker;
private String location;
private Person sought;
public MissingPerson (Person seeker, String location, Person sought) {
//ensure that all instances have name/location properties
if (seeker.getName().isEmpty() || location.isEmpty()) {
System.out.println("Missing name/location");
System.exit(1);
}
this.seeker = seeker;
this.location = location;
this.sought = sought;
}
//deep copy of MissingPerson
public MissingPerson(MissingPerson orig) {
this.seeker = new Person(orig.seeker);
this.location = orig.location; //strings are immutable
this.sought = new Person(orig.sought);
}
//accessors
public Person getSeeker() {
return new Person(seeker);
}
public String getLocation() {
return location;
}
public Person getSought() {
return new Person(sought);
}
//mutators
public void setSeeker(Person seeker) {
this.seeker = new Person(seeker);
}
public void setLocation(String location) {
this.location = location;
}
public void setSought(Person sought) {
this.sought = new Person(sought);
}
//Shaun Sheep now safe from Bad Wolf!
}
Week 7
//declare array - baseType is for every array element
baseType[] varName;
//declare method returning array
baseType[] methodName(type name, ...);
//declaring an array doesn't create it - need to initialise
//initialise array to a constant
baseType[] varName = {value, ...};
//arrays can be public/private, static or not, final or not
public static final int[] DAYS_IN_MONTH = {31,28,31,30,31,30,31,31,30,31,30,31};
//{...} not an expression, can only be used in variable initialisations
//creating arrays
double[] data = new double[10];
//array type doesn't incl size, new expression does
//size can be a calculation
//size fixed at time of creation, cannot be expanded/shrunk
//initialisations: number=0 or 0.0, boolean=false, chars '\0', objects=null
//accessing array
array[index];
args[2];
//assign to array element
data[2] = data[1] * 10;
//can specify array index as an expression
//very common to index an array with a variable
public static void main(String[] args) {
int month = Integer.parseInt(args[0]);
int day = Integer.parseInt(args[1]);
int yearday = 0;
for (int i = 0 ; i < month - 1 ; ++i) {
yearday += daysInMonth[i];
}
System.out.println(yearday + day);
}
//length - size of an array is it's length
array.length
public class ArgCount {
public static void main(String[] args) {
System.out.printf("You entered %d arguments%n", args.length);
}
}
//length useful for iterating over whole array
for (int i = 0 ; i < a.length ; ++i) {
... a[i] ... ;
}
//arrays are mutable (cannot be made immutable)
//copying arrays - cannot write copy constructor, but can write static method
public static String[] copyStringArray(String[] orig) {
String[] copy = new String[orig.length];
for (int i = 0 ; i < orig.length ; ++i) {
copy[i] = orig[i];
}
return copy;
}
//need to write a seperate copy method for each base type of array to copy
//arrays of objects - creates array of nulls, need to initialise
Person[] people = new Person[args.length];
for (int i = 0 ; i < people.length ; ++i) {
//create arrays of Person objects, age 0 and name from command line args
people[i] = new Person(0, args[i]);
}
//cannot resize arrays length
arr.length = arr.length + 1;
//error
//can create new array and copy to it
int[] tmp = new int[arr.length+1];
for (int i = 0; i < arr.length; ++i) {
tmp[i] = arr[i];
}
tmp[arr.length] = 42;
arr = tmp;
//partially filled - variable holds meaningful elements
public class ExpandingIntArray {
public static final int INITIAL_SIZE = 10;
public static final int EXPANSION = 2;
private int[] data = new int[INITIAL_SIZE];
private int length = 0;
public void append(int val) {
if (length >= data.length) {
int[] tmp = new int[EXPANSION*data.length];
for (int i = 0; i < data.length; ++i) {
tmp[i] = data[i];
}
data = tmp
}
data[length] = val;
length++;
}
}
//foreach loop
for(elementType name : array) body
//don't have to worry about array indices
int daysInYear = 0;
for (int monthLength : DAYS_IN_MONTH) {
daysInYear += monthLength;
}
//multidimensional arrays - not supported, need to make array of arrays
int[][] tTable = new int[10][];
for (int i = 0; i < 10; ++i) tTable[i] = new int[10];
for (int i = 0; i < tTable.length; ++i) {
for (int j = 0; j < tTable[i].length; ++j) {
tTable[i][j] = (i+1)*(j+1);
}
}
for (int[] row : tTable) {
for (int n : row) System.out.printf("%4d", n);
System.out.println();
}
//enum - enumerated type, all values specific constants
enum typeName {value, value2 ...};
enum DayOfWeek {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY};
//place at top level inside class, or in a file by itself (typename.java)
public static boolean isWeekend(DayOfWeek day) {
return day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY;
}
//enums as classes
String toString(); //returns value as string
static type valueOf(String); //returns enum value of string
int ordinal(); //return 0 for first value, 1 for second etc
static type[] values(); //returns array of all enum values
//values allows us to loop over all values of enum type
//equals and compareTo - defined for classes and enums, not arrays
boolean equals(t); //returns true iff object is same as argument
int compareTo(t); //returns negative if less, 0 same, positive greater
public class DayOfWeekTest {
enum DayOfWeek {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY};
public static void main(String[] args) {
DayOfWeek d = DayOfWeek.valueOf(args[0]);
System.out.println(d + ": " + (d.ordinal()+1) + "th day of week");
int next = d.ordinal() + 1;
next %= DayOfWeek.values().length;
d = DayOfWeek.values()[next];
System.out.println("Next day is " + d);
}
}
Week 8
Inheritance
//declaration of child class
extends BaseClass
public class LostPerson extends Person {
private String lastSeenLocation;
private Time lastSeenTime;
}
//inherits all the instance variables and methods of the Person class
//and adds it's own
Week 9
Polymorphism, abstract classes and interfaces
// an abstract classes cannot be instantiated, but can have a constructor
// that enforces rules for the concrete class extending it.
abstract class Dog{
private String name;
// name variable must be created in concrete instantiation
public Dog(String name){
this.name = name;
}
// this method must be overwritten
abstract public boolean isBig();
public String getName(){
return name;
}
}
class BigDog extends Dog{
private boolean big;
public BigDog(String name, boolean big){
super(name);
this.big = big;
}
// overwriting abstract class
@Override
public boolean isBig(){
return this.big;
}
}
// abstract methods and classes must be overridden when implemented
// abstraction in OO is not the same as abstract classes in java
public abstract class LocatedPerson extends Person {
public LocatedPerson(int age, String name) {
super(age, name);
}
public abstract String contactInstructions();
// interfaces are more abstract than abstract classes
// allow unrelated classes to implement common methods
// defining an interface
public interface iface {
// all members are implicitly public
// all methods are implicitly abstract
// all variables are implicitly static final
}
public class name implements iface {
// name must define all iface's abstract methods;
}
}
Polymorphism allows us to call the same method on objects of varying types and have the appropriate method called depending on the type. the superclass must declare the method, and the subclasses have to override this method. the objects can then be instantiated as the superclass type, but call the method of the subclasses depending which object it is.
Composition and Aggregation
// Composition implies that an object is completely encapsulated by another
// class.
final class Car {
private final Engine engine;
Car(EngineSpecs specs) {
engine = new Engine(specs);
}
void move() {
engine.work();
}
}
// Aggregation implies a "HAS-A" relationship, but one can exist without the
// other, unlike composition.
final class Car {
private Engine engine;
void setEngine(Engine engine) {
this.engine = engine;
}
void move() {
if (engine != null) {
engine.work();
}
}
}
Extra
public class Parent {
public int methodOverride() {
return 3;
}
}
public class Child extends Parent {
public int methodOverride() {
return 4;
}
}
// -- Lists --
// create string array
String[] alphabet = {"a", "b", "c", "d"};
// create list array
List<String> alphabetList = new ArrayList<String>();
//assign each arrya element to list
for (String character : alphabet) {
alphabetList.add(character);
}
//print elements of list
for (int i = 0; i < alphabetList.size();i++) {
System.out.println(alphabetList.get(i));
}
//using scanner.nextLine() to "eat up" the newline character
int input = scanner.nextInt();
scanner.nextLine();
//mutable class example
/* An example of writing a mutable class.
An immutable class cannot be modified after its creation, which is useful
for avoiding privacy leaks - the ability for another object to modify ours
without our permission, or in a way that we can't control.
A mutable class, however, can be modified, so it is up to the programmer
to ensure that bugs and "strange behaviour" are not introduced as a result
of privacy leaks.
Author: Matthew De Bono
Date: 24/8/15
*/
import java.util.*;
public class MutablePerson {
// We always use private for instance variables
private String familyName;
private String givenName;
private int age;
// This variable is a mutable object, so be careful about privacy
private MutablePerson parent;
public static void main(String args[]) {
// Let's create a few objects
MutablePerson p1 =
new MutablePerson("", "Mr. Incredible", 37, null);
MutablePerson p2 = new MutablePerson("", "Elastigirl", 35);
MutablePerson p3 = new MutablePerson("Parr", "Dash", 13, p1);
MutablePerson p4 = new MutablePerson("Parr", "Violet", 17, p1);
MutablePerson p5 = p1;
// We can use toString to print a representation of the object
System.out.println("\nTESTING TOSTRING METHOD");
System.out.println(p1.toString());
// Java is smart enough that you don't actually need to call the method
System.out.println(p2);
System.out.println(p3);
// Now let's try the equals method; this should print false
System.out.println("\nTESTING EQUALS METHOD");
System.out.println("Testing p1 with p4, expecting false as they are " +
" different objects: " + p1.equals(p4));
System.out.println("Testing p4 with p1, expecting false as they are " +
" different objects: " + p4.equals(p1));
System.out.println("Testing p1 with p2, expecting false as they are " +
" different objects: " + p1.equals(p2));
// This is true
System.out.println("Testing p1 with p1, expecting true as they are " +
" the same objects: " + p1.equals(p1));
System.out.println("Testing p1 with p5, expecting true as they " +
"point to the same object: " + p1.equals(p5));
System.out.println("Testing parent of p3 with parent of p4, " +
"expecting true, same parent: " +
p3.getParent().equals(p4.getParent()));
// We can now modify our objects because of the set methods
System.out.println("\nTESTING SETTERS");
// Here is the starting object
System.out.println(p1);
// Now we modify it and check again
p1.setAge(20);
System.out.println(p1);
p1.setFamilyName("Parr");
System.out.println(p5);
// Let's make sure we've prevented privacy leaks
System.out.println("\nTESTING PRIVACY LEAKS");
System.out.println(p3);
System.out.println(p4);
p1.setFamilyName("");
System.out.println(p3);
System.out.println(p4);
System.out.println("\nUh oh...");
}
// Constructors are used to construct or build a class
public MutablePerson(String familyName, String givenName, int age,
MutablePerson parent) {
// Here we make use of 'this' to differentiate between variables
// with the same name
this.familyName = familyName;
this.givenName = givenName;
this.age = age;
this.parent = parent;
}
// We can even have multiple constructors
// Constructors don't need to take information for every variable
public MutablePerson(String familyName, String givenName, int age) {
// Here we make use of 'this' to differentiate between variables
// with the same name
this.familyName = familyName;
this.givenName = givenName;
this.age = age;
// We should always initialise every variable,
// even if we aren't given all the information
this.parent = null;
}
public MutablePerson(String familyName, String givenName) {
this.familyName = familyName;
this.givenName = givenName;
this.age = 0;
this.parent = null;
}
// A copy constructor is useful for avoiding privacy leaks
public MutablePerson(MutablePerson person) {
this.familyName = person.familyName;
this.givenName = person.givenName;
this.age = person.age;
if (person.parent != null) {
// When we have mutable objects as instance variables, we need to be
// sure to get copies of those too, otherwise we will create aliases
this.parent = new MutablePerson(person.parent);
} else {
this.parent = null;
}
}
// Accessors/Getters
// These methods are fine, as they don't modify the class
public String getFamilyName() {
return familyName;
}
public String getGivenName() {
return givenName;
}
public int getAge() {
return age;
}
// We should never return references to mutable objects
// Instead, we return a copy of our object so it remains private
public MutablePerson getParent() {
if (parent == null) {
return null;
}
return new MutablePerson(parent);
}
// Mutators/Setters
// These methods directly modify the class, so it is no longer immutable
public void setFamilyName(String familyName) {
this.familyName = familyName;
}
public void setGivenName(String givenName) {
this.givenName = givenName;
}
public void setAge(int age) {
this.age = age;
}
// When a mutable object is given as an argument, it's always safest to
// make a copy of it
public void setParent(MutablePerson parent) {
this.parent = new MutablePerson(parent);
}
// It's always useful to define these methods
// toString should return a String that represents the object
public String toString() {
String out = givenName + " " + familyName + " is "
+ age + " years old.";
if (parent != null) {
out += "\nParent: " + parent.toString();
}
return out;
}
// equals should return true only when the two objects are considered equal
// Not all variables need to be equal though, you can decide on equality
// however you like
public boolean equals(MutablePerson p) {
if (!this.familyName.equals(p.getFamilyName())) {
return false;
}
if (!this.givenName.equals(p.getGivenName())) {
return false;
}
if (this.age != p.getAge()) {
return false;
}
if (this.parent != null && p.getParent() != null &&
!this.parent.equals(p.getParent())) {
return false;
}
if ((this.parent == null && p.getParent() != null) ||
(this.parent != null && p.getParent() == null)) {
return false;
}
return true;
}
}