Homeworks

Unit 6: Arrays

  • Arrays are important to store a "list" of data in the same type
  • Watch out for errors: especially ArrayIndexOutOfBoundsException
  • Important to know algorithms with arrays -> using for loops
    • Enhanced for loops can be utilized but has limitations
    • Algorithms can be like finding max, min, average, sorting, etc.

Algorithm for arrays

  • Many algorithms can be done using for loops in combinations with arrays
  • Here is an algorithm example of counting numbers that multiples of 5 but not 3
int[] array = {1, 2, 3, 4, 5, 10, 15};

for (int num : array) {
  if (num % 5 == 0 && num % 3 != 0) {
    System.out.println(num);
  }
}
5
10

Unit 7: ArrayList

  • An arraylist is like an array but the length can be changed
  • For arraylists you must use a wrapper class rather than a primitive
    • Due to arraylist using generics
  • Arraylists can also work with enhanced for loops and have many convenient functions

Wrapper Classes

  • Must be used with arraylist for primitives, usually a capital version
  • Java can automatically convert between wrappers and primitives
// using "Integer" wrapper class
ArrayList<Integer> listOfIntegers = new ArrayList<>();

// Explicitly creating integer
listOfIntegers.add(new Integer(10));

// automatically converts to Integer
listOfIntegers.add(1);

// using toString method of ArrayList
System.out.println(listOfIntegers);
[10, 1]

Unit 8: 2D Arrays

  • Arrays can be placed inside arrays, creating 2D array
    • Useful for representing 2d space, or text (as 2d)
  • Defined by using two pairs of square brackets after the type
  • Can be traversed using nested for loops

Nested For loops

  • Concept of putting one for loop inside another
  • Really useful for traversing 2d arrays
// defining 2d array
int[][] arr2d = {
  {1, 2, 3},
  {4, 5, 6, 7},
  {8, 9, 10}
};

// using nested for loops
for (int[] row : arr2d) {
  for (int val : row) {
    System.out.print(val + " ");
  }
  System.out.println();
}
1 2 3 
4 5 6 7 
8 9 10 

Unit 9: Inheritance

  • Inheritance can be used when two classes share similar functionality
    • Allows super class to have base functionality (ie. car)
    • Sub class adds additional functionality to the base (ie. tesla car)
  • "extends" and "abstract" keywords can be used to define inheritance in Java

Extends key word

  • Defines a sub class that inherits all the methods from the super class
  • Useful because you don't need to redefine everything from super class
public class Animal {
  String color;
  int age;

  public Animal () {}

  public Animal (String color, int age) {
    this.color = color;
    this.age = age;
  }

  public void sayHello () {
    System.out.println("hello, I am " + color + " and I am " + age + " years old.");
  }

  public void walk () {
    System.out.println("walking...");
  }

  public void eat () {
    System.out.println("eating");
  }
}

public class Cat extends Animal {
  String owner;

  public Cat (String color, int age, String owner) {
    this.color = color;
    this.age = age;
    this.owner = owner;
  }

  public void sayOwner () {
    System.out.println("my owner is " + owner);
  }
}

Cat c = new Cat("green", 2, "joe");

// using method from parent class
c.sayHello();
// using method from child class
c.sayOwner();
hello, I am green and I am 2 years old.
My owner is joe

Subclass constructor, super Keyword

  • The constructor in a subclass can use "super" to access the parent class constructor
  • "super" can also be used to access parent class methods
public class Animal {
  String color;
  int age;

  public Animal () {}

  public Animal (String color, int age) {
    this.color = color;
    this.age = age;
  }

  public void sayHello () {
    System.out.println("hello, I am " + color + " and I am " + age + " years old.");
  }

  public void walk () {
    System.out.println("walking...");
  }

  public void eat () {
    System.out.println("eating");
  }
}

public class Cat extends Animal {
  String owner;

  public Cat (String color, int age, String owner) {
    // reduce code duplication
    super(color, age);
    this.owner = owner;
  }

  public void sayOwner () {
    System.out.println("my owner is " + owner);
  }

  public void sayOwnerAndHello() {
    super.sayHello();
    sayOwner();
  }
}

Cat c = new Cat("green", 2, "joe");

c.sayOwnerAndHello();
hello, I am green and I am 2 years old.
my owner is joe

Overriding (part of Polymorphism)

  • Allows you to define a method in the parent class, but then change it in child class
  • Really useful to change functionality in child class
public class Animal {
  String color;
  int age;

  public Animal () {}

  public Animal (String color, int age) {
    this.color = color;
    this.age = age;
  }

  public void sayHello () {
    System.out.println("hello, I am " + color + " and I am " + age + " years old.");
  }

  public void walk () {
    System.out.println("walking...");
  }

  public void eat () {
    System.out.println("eating");
  }
}

public class Cat extends Animal {
  String owner;

  public Cat (String color, int age, String owner) {
    // reduce code duplication
    super(color, age);
    this.owner = owner;
  }

  public void sayOwner () {
    System.out.println("my owner is " + owner);
  }

  // Adding more functionality in say hello for cat
  @Override
  public void sayHello() {
    super.sayHello();
    System.out.println("meow...");
  }
}

// Cat uses cat method
Cat c = new Cat("green", 2, "joe");
c.sayHello();

// Animal uses animal method
Animal a = new Animal("blue", 3);
a.sayHello();
hello, I am green and I am 2 years old.
meow...
hello, I am blue and I am 3 years old.

Abstract Class, Abstract Method

  • Abstract class means a class cannot be instantiated, it is simply a template
  • Abstract methods define the signature but the method must be implemented in child class
  • For example, lets say we don't want an animal to be created...
abstract class Animal {
  String color;
  int age;

  public Animal () {}

  public Animal (String color, int age) {
    this.color = color;
    this.age = age;
  }

  // must be defined in child class
  abstract void sayHello ();

  public void walk () {
    System.out.println("walking...");
  }

  public void eat () {
    System.out.println("eating");
  }
}

public class Cat extends Animal {
  String owner;

  public Cat (String color, int age, String owner) {
    // reduce code duplication
    super(color, age);
    this.owner = owner;
  }

  public void sayOwner () {
    System.out.println("my owner is " + owner);
  }

  // Defining method that was abstract
  @Override
  public void sayHello() {
    System.out.println("meow...");
  }
}

// Cat uses cat method
Cat c = new Cat("green", 2, "joe");
c.sayHello();
meow...

Late binding of object (part of polymorphism)

  • Allows you to use the type of a superclass but have an object of the subclass
  • Useful if you know it will be part of the superclass, but don't know which subclass it is
    • I know it is an animal but I don't know which type
  • Works well with abstract methods
abstract class Animal {
  String color;
  int age;

  public Animal () {}

  public Animal (String color, int age) {
    this.color = color;
    this.age = age;
  }

  // must be defined in child class
  abstract void sayHello ();

  public void walk () {
    System.out.println("walking...");
  }

  public void eat () {
    System.out.println("eating");
  }
}

public class Cat extends Animal {
  String owner;

  public Cat (String color, int age, String owner) {
    // reduce code duplication
    super(color, age);
    this.owner = owner;
  }

  public void sayOwner () {
    System.out.println("my owner is " + owner);
  }

  // Defining method that was abstract
  @Override
  public void sayHello() {
    System.out.println("meow...");
  }
}

// Defining cat as an animal!
Animal c = new Cat("green", 2, "joe");

// can use because abstract method guarentees implentation in child class
c.sayHello();
meow...

Overloading (part of polymorphism)

  • Allows you to have a method, but different sets of arguments
  • The method which is called is determined at compile time (early binding) based on the arguments passed in
abstract class Animal {
  String color;
  int age;

  public Animal () {}

  public Animal (String color, int age) {
    this.color = color;
    this.age = age;
  }

  // must be defined in child class
  abstract void sayHello ();

  public void walk () {
    System.out.println("walking...");
  }

  public void eat () {
    System.out.println("eating");
  }
}

public class Cat extends Animal {
  String owner;

  public Cat (String color, int age, String owner) {
    // reduce code duplication
    super(color, age);
    this.owner = owner;
  }

  public void sayOwner () {
    System.out.println("my owner is " + owner);
  }

  // Defining method that was abstract
  @Override
  public void sayHello() {
    System.out.println("meow...");
  }

  // same method with different arguments
  public void sayHello(String person) {
    System.out.println("meow... hello " + person);
  }
}

Cat c = new Cat("green", 2, "joe");

// two different argument sets for same method
c.sayHello();
c.sayHello("mark")
meow...
meow... hello mark

Standard methods: toString(), equals(), hashCode()

  • All objects automatically inherit from Object class and have some methods
    • toString should list properties, equals should compare properties, hashCode should give unique identifier
abstract class Animal {
  String color;
  int age;

  public Animal () {}

  public Animal (String color, int age) {
    this.color = color;
    this.age = age;
  }

  // must be defined in child class
  abstract void sayHello ();

  public void walk () {
    System.out.println("walking...");
  }

  public void eat () {
    System.out.println("eating");
  }
}

public class Cat extends Animal {
  String owner;

  public Cat (String color, int age, String owner) {
    // reduce code duplication
    super(color, age);
    this.owner = owner;
  }

  public void sayOwner () {
    System.out.println("my owner is " + owner);
  }

  // Defining method that was abstract
  @Override
  public void sayHello() {
    System.out.println("meow...");
  }

  // same method with different arguments
  public void sayHello(String person) {
    System.out.println("meow... hello " + person);
  }

  // Overriding the to string
  @Override
  public String toString () {
    return "[Cat]: " + "color=" + color + ", " + "age=" + age + ", " + "owner=" + owner;
  }
}

// Object superclass is automatically inherited
Object c = new Cat("green", 2, "joe");

// System.out.println uses toString method internally
System.out.println(c);
[Cat]: color=green, age=2, owner=joe

Unit 10: Recursion

  • Recusion can be used in situations where you need to repeatedly do something, instead of loops
  • Recursion must call itself and have a base case
    • Base case allows the recursion to end at some point

Big O Notation

  • Used to describe the most time it would take for a function to run (without constants)
  • For example, the recursion below would take O(n) time as it has to go through n iterations to calculate the factorial
public int factorial (int n) {
  if (n == 0 || n == 1) {
    return 1;
  }
  return n * factorial(n-1);
}

System.out.println(factorial(5));
120