15 Sep 2024
3.2 If Statements and Control Flow

popcorn hack
create test cases that do not satisy the condition above. you can copy/paste the code into the new code cell
public static void main(String[] args) {
int myAge = 16;
System.out.println("Current age: " + myAge);
if (myAge >= 16) {
System.out.println("You can start learning to drive!");
}
System.out.println("On your next birthday, you will be " + (myAge + 1) + " years old!");
}
If statements can be used to create chatbots
–> Magpie Lab


- the user’s input affects the flow of the program
int myAge = -2;
System.out.println("Current age: " + myAge);
if (myAge >= 15) {
System.out.println("You can start learning to drive!");
}
System.out.println("On your next birthday, you will be " + (myAge + 1) + " years old!");
Current age: -2
On your next birthday, you will be -1 years old!
Polymorphism
Learning Targets:
- What is Polymorphism
- Difference between Static and Dynamic Data Types
- Compile Time vs Run time
- “A reference variable is polymorphic when it can refer to objects of different classes in the code”
- “A method is polymorphic when it is overriden in at least one subclass”
- “Polymorphism is the act of executing an overriden
non-static
method from the correct class at runtime based on the actual object type”
public class Shape {
protected String name;
private int length;
private int width;
// Parameterized constructor
public Shape(String name, int length, int width) {
this.name = name;
this.length = length;
this.width = width;
}
// Method to calculate the area
public double calc_area() {
return this.length * this.width;
}
}
public class Triangle extends Shape {
private int side1;
private int side2;
private int side3;
// Constructor that takes a name and three side lengths
public Triangle(String name, int s1, int s2, int s3) {
super(name, 0, 0); // Call to Shape constructor to set the name
this.name = "triangle";
this.side1 = s1;
this.side2 = s2;
this.side3 = s3;
}
@Override
public double calc_area() {
double s = (side1 + side2 + side3) / 2.0; // Semi-perimeter
return Math.sqrt(s * (s - side1) * (s - side2) * (s - side3));
}
}
//creates a "Shape" class using a "Triangle" constructor
Shape triangle = new Triangle("Equilateral",1,1,1);
// Therefore the calc_area is overriden
System.out.println(triangle.calc_area());
How Does This Work?

Let’s start with this line:
Shape myObject = new Triangle();
It may seem like you are magically creating a Shape Object with the Triangle, but you are not. Instead you actually are creating a Triangle Object. The difference is that the Shape variable is referencing the Shape parts of the Triangle.
- “A reference variable can store a refernece to its declared class or any subclass of its declared class”
This also means that if the data type is the SuperClass and you try to call a method that doesn’t exist in the SuperClass, it will return an error. But you can cast the variable to the SubClass because the refrence is the SubClass. After casting if you call a method that is only in the subclass then it won’t return an error.
Next running methods:
myObject.calc_area() == Triangle.calc_area();
When you run a method that the Shape has, it starts at the referenced object and checks if there is an override, if not it moves up the ancestry chain until it either finds an override or finds the orginal method.
Popcorn Hacks
If you want some more information and examples of Polymorphism see the examples further down
Static vs Dynamic Types
static types: static types is simply the type.
dynamic types: dynamic type is the type of the value during runtime
class Shape {
String getName(){
return "Shape"
}
}
class Square extends Shape{
@Override
String getName(){
return "Square"
}
}
Shape myShape = new Square();

Shape is a static type, but at runtime myShape
is referencing Sqaure, so my myShape
is the dynamic type of Square.
Compile-Time vs Run-Time methods
Compile time is when you are writing your code. Once you have written your code it is compiled into something the computer can run.
Run-time is when you are actually running the code. This is after the compiler is complete and when the code starts exectuting.
There are some differences between Compile-Time and Run-Time. The case we will be going over breifly is the difference in methods. When you create a Shape with a Square you can’t run the methods built solely into the square, it must be methods from the shape first. But why?
This is the difference between Compile-Time and Run-Time
During Runtime:
When you are creating a dynamic reference of a different with a type than the static type, it searches the superclass for the attributes of the static type, then overrides any that the child has overriden. But it doesn’t include the methods from the child.
So basically even if you have methods that exist on the referenced object, in run-time those methods may be ignored because the static type doesn’t include them.
If you want to run a method that is only in the child class then you can use down-casting.
class Shape {
String getName(){
return "Shape";
}
}
class Square extends Shape{
@Override
String getName(){
return "Square";
}
int getSides(){
return 4;
}
}
Shape myShape = new Square(); //this does not have access to the Sqaure methods despite referencing a sqaure
Square mySquare = (Square) myShape; //down-cast the Shape to a Sqaure to run the Sqaure specific methods
System.out.println(mySquare.getSides());//after down-casting you can now run the Square methods

Popcorn Hacks
- Define down-casting in your own words
down casting is when an object of a class is created, but doesn’t have access to methods due to it extending another class. Hence, casting it required for it to access the proper methods
- add an example of down-casting to your previous polymorphism example
see code in other blog page
Examples of Polymorphism and the effects
Here are some examples of Polymorphism.
1- Executing the overriden method from the referenced subclass in the datatype of the superclass.
class Water {
public String typeOfWater(){
return "water";
}
}
class Lake extends Water {
@Override
public String typeOfWater(){
return "lake";
}
}
//creates a "Water" class using a "Lake" constructor
Water lake = new Lake();
// Therefore the typeOfWater method is overriden
System.out.println(lake.typeOfWater());
2- You can pass a subclass into an argument that is asking for the parent class.
class Shape{
public int getSize(){
return 1;
}
}
class Square extends Shape{
@Override
public int getSize(){
return 2;
}
}
int getSizePlusOne(Shape s){ //argument of class "Shape"
return s.getSize() +1;
}
Shape myShape = new Shape();
//passes through a "Shape"
System.out.println(getSizePlusOne(myShape));
Square mySquare = new Square();
//passes through a "Square" as a "Shape" with the square's "getSize()" method
System.out.println(getSizePlusOne(mySquare));
3- You can cast from the superclass to the subclass (down-casting). The superclass must be referencing the subclass.
class Electronic{
public void playSound(){
System.out.println("Beep boop");
}
}
class Computer extends Electronic{
@Override
public void playSound(){
System.out.println("Click clack");
}
}
//creates a "Electronic" class using a "Computer" constructor
Electronic device = new Computer();
//casts the "Electronic" to a "Computer"
Computer computer = (Computer)device;
computer.playSound();
class Electronic{
public void playSound(){
System.out.println("Beep boop");
}
}
class Computer extends Electronic{
@Override
public void playSound(){
System.out.println("Click clack");
}
}
Electronic device = new Electronic(); //creates a "Electronic" class using a "Electronic" constructor
Computer computer = (Computer)device; //cannot cast the "Electronic" to a "Computer"
---------------------------------------------------------------------------
java.lang.ClassCastException: class REPL.$JShell$25$Electronic cannot be cast to class REPL.$JShell$26$Computer (REPL.$JShell$25$Electronic and REPL.$JShell$26$Computer are in unnamed module of loader jdk.jshell.execution.DefaultLoaderDelegate$RemoteClassLoader @704a52ec)
at .(#28:3)
4- Down-casting doesn’t mean changing the reference to the obejct, so polymorphism will still apply
class Furniture{
String getName(){
return "Furniture";
}
}
class Table extends Furniture{
@Override
String getName(){
return "Table";
}
}
class CoffeeTable extends Table{
@Override
String getName(){
return super.getName() + " Coffee";
}
}
Furniture myTable = new CoffeeTable();
//runs "CoffeeTable" method
System.out.println(myTable.getName());
Table myOtherTable = (Table)myTable;
//method exectuted doesn't change despite casting-down
System.out.println(myOtherTable.getName());
Table Coffee
Table Coffee
5- you can cast upward and polymorphic behaviour will apply.
class Sport{
public int numberOfPlayers(){
return (int)Double.NaN; //ends up being 0 but whatever
}
public String name(){
return "Sport";
}
}
class Soccer extends Sport{
@Override
public int numberOfPlayers(){
return 11;
}
@Override
public String name(){
return "Soccer";
}
}
//creates a a soccer object
Soccer mySoccer = new Soccer();
Sport mySoccerSport = (Sport)mySoccer;
System.out.println(mySoccerSport.numberOfPlayers());
Polymorphism with the Object class
Polymorphism also applies with the Object Superclass.
Techically any class or object is an Object
.
class Fruit{
@Override
public String toString(){
return "I'm a Fruit!";
}
}
class Banana extends Fruit{
@Override
public boolean equals(Object obj){ //overrides the equals
return true;
}
}
Object banana = new Banana();
System.out.println(banana.toString());
//if ".equals()" was not overriden from the "Object" this should always return false
System.out.println(banana.equals(null));
13 Sep 2024
String Objects - Concatenation, Literals, and More
Creating Strings:
public class StringObjects
{
public static void main(String[] args)
{
String name1 = "Skibidi";
String name2 = new String("Sigma");
String name3 = new String(name1);
System.out.println(name1);
System.out.println(name2);
System.out.println(name3);
}
}
StringObjects.main(null);
📝 Three Ways to Create a String: Let’s Break it Down!
First Option:
String name1 = "Skibidi";
This method of creating a string consists of the following three parts:
-
Class Name — Defines the type of variable (String, Integer, Array, etc.)
-
Variable Name — The name assigned to the variable. This is how the variable will be referenced in the rest of the program.
-
String Value — The actual value you’d like to assign to the variable
Second Option:
String name2 = new String("Sigma");
This method is similar to option one but consists of a few extra components (indicated in pink coloring below):
-
Class Name — Again, defines the type of the variable (String, Integers, Arrays, etc.)
-
Variable Name — How the variable will be referenced in the rest of the program
-
new — A Java keyword which is used to explicitly instantiate a new object.
-
Class Name (Part 2) — The ‘new’ key word must be followed with the class name of the object being created (in this case, String)
-
String Value — The actual value you’d like to assign to this variable
Third Option:
String string1 = "YIPEE"
String name2 = new String(string1);
Finally, a string can be created using the value of another, existing string object. The components needed for this declaration are as follows:
-
Class Name — Again, type of variable will it be? (String, Integers, Arrays, etc.)
-
Variable Name — The name of the variable
-
Java Keyword ‘new’ — The new keyword in Java is used to explicitly create a new object.
-
Class Name (Part 2) — As indicated before, the new keyword has to be followed with the class name of the object being created.
-
Variable Name (Part 2) — The name of the variable whose value you want the new variable to take on.
Importantly, regardless of which creation method you choose, String objects are immutable. This means that every time you attempt to change the value of some string variable, under the hood, a new string object is created with the updated value, and your variable name is used to reference this new object.
📝 What is concatentation?
Concatenation allows you to add strings together.
There are two primary ways to combine strings. Assume a
, b
, and c
are previously created string variables.
a += b
: Appends the string value stored in b
to the string value stored in a
. In the processs, a
is redefined to equal this newly appended string.
c = a + b
: Joins the string values of a
and b
together, but doesn’t redefine either a
or b
. Instead, the resultant value is assigned to c
.
public class Concatentations
{
public static void main(String[] args)
{
String name1 = "Skibidi";
String name2 = new String("Sigma");
String name3 = new String(name1);
int number1 = 1;
int number2 = 2;
String combine = name1 + "" + number1;
name1 += number2;
System.out.println(name1);
System.out.println(combine);
}
}
Concatentations.main(null);
Let’s do an exercise for practice! What will the following code segment print?
public class Concatentations
{
public static void main(String[] args)
{
String name1 = "Skibidi";
String name2 = new String("Sigma");
String name3 = new String(name1);
name1 += "!!"
String mystery = name1 + name2 + name3
System.out.println(mystery);
}
}
// Uncomment the following method call to run the code and check your answer!
// Concatentations.main(null);
📝 Backwards and Forwards Slashes
In Java, there are a few characters with pre-assigned purposes. Backwards and forwards slashes are such characters, and they can be easy to mix up, so it’s important to pay close attention to them!
\
: Starts escape sequences. In other words, it can allow you to add special characters to your string.
/
: Usually used as a division operator. Two forward slashes indicate the beginning of a comment.
Backslashes
\"
= “Escapes” the quote, allowing for you to have quotes within a string without ending the string
\\
= Indicates a literal backslash in the string.
\n
= A newline character
public class SlashDemo {
public static void main(String[] args) {
// Using backslashes for escape sequences:
System.out.println("This is a double quote: \""); // Prints a double quote
System.out.println("This is a backslash: \\"); // Prints a backslash
System.out.println("This prints on a new line:\nSecond line starts here");
// Using forward slashes for division and comments:
int a = 10;
int b = 2;
int result = a / b; // Division operation
System.out.println("Result of 10 / 2 is: " + result); // Prints the result of the division
}
}
SlashDemo.main(null)
📝 String methods
The following are some important methods that can be used on String objects.
Method |
Description |
String(String str) |
Creates a new String object with the same sequence of characters as the specified string str |
int length() |
The number of characters in the String object |
String substring(int from, int to) |
Returns the substring beginning at index from and ending at index to - 1. In other words, the start is inclusive and the end is exclusive. |
String substring(int from) |
Returns substring(from, length()) |
int indexOf(String str) |
Returns the index of the first occurrence of str ; returns -1 if not found |
boolean equals(String other) |
Returns true if the calling string is equal to other ; returns false otherwise |
int compareTo(String other) |
Returns a value < 0 if the calling string is alphanumerically less than other; returns 0 if it is equal to other; returns a value > 0 if it is alphanumerically greater than other. |
public class StringMethodDemo {
public static void main(String[] args) {
String wordOfDay = new String("Skibidi");
System.out.print("Characters in the word 'Skibidi' — ");
System.out.println(wordOfDay.length());
System.out.println("\nThis should return -1, since there is no n in the string");
System.out.println(wordOfDay.lastIndexOf("n"));
System.out.println("\nThis should display the index of d (5)");
System.out.println(wordOfDay.lastIndexOf("d"));
// NOTE: Start is inclusive, end is exclusive
System.out.println("\nThis should display the letters between the 2nd and 6th");
System.out.println(word.substring(2,6));
}
}
StringMethodDemo.main(null)
Brief Aside: Substring, like python slicing has the first index be inclusive and the last one be exclusive.

Quick, let’s do an exercise for practice! What will the following code segment return?
public class SubstringOfDemo {
public static void main(String[] args) {
String word = new String("skibidi");
System.out.println("\nWhat is printed if we only pass one parameter into the substring method?");
System.out.println(word.substring(2));
}
}
// Uncomment the following method call to run the code and check your answer!
// SubstringOfDemo.main(null)
public class CompareToDemo {
public static void main(String[] args) {
String word = new String("skibidi");
String word2 = new String("skibidi1");
String word3 = new String("skibidi");
System.out.println("\nIf word is < word2, a negative value will be printed. If they are equal, 0 will be printed, and if word > word2, a positive value is printed");
System.out.println(word.compareTo(word2));
System.out.println("\nComparison between word and word3");
System.out.println(word.compareTo(word3));
}
}
CompareToDemo.main(null)
public class EqualToDemo {
public static void main(String[] args) {
String word = new String("skibidi");
String word2 = new String("skibidi1");
String word3 = new String("skibidi");
System.out.println("\nThis displays if word1 = word2, if false it returns false, if true it returns true");
System.out.println(word.equals((word2)));
System.out.println("\nThis displays if word1 = word3, if false it returns false, if true it returns true");
System.out.println(word.equals((word3)));
}
}
EqualToDemo.main(null)
This displays if word1 = word2, if false it returns false, if true it returns true
false
This displays if word1 = word3, if false it returns false, if true it returns true
true