CS F213 Objected Oriented Programming Labsheet 4

Spring 2024

Using Objects as Parameters and Returning Objects

So far, we have only been using simple types as parameters to methods. However, it is both correct and common to pass objects to methods. This can be used to make copies of an object or if an object has too many attributes, instead of passing each of them individually we can pass the object and access them directly in the method.

Example:

import java.util.Scanner;
public class Student {
   private String name;
   private int age;
   public Student(){}
   public Student(String name, int age){
      this.name = name;
      this.age = age;
   }
   public Student copyObject(Student std){ 
    //method of Student data type taking an object of Student data type as parameter
      this.name = std.name;
      this.age = std.age;
      return std;
   }
   public void displayData(){
      System.out.println("Name : "+this.name);
      System.out.println("Age : "+this.age);
   }
}
class Main
{
   public static void main(String[] args) {
      Scanner sc =new Scanner(System.in);
      System.out.println("Enter your name ");
      String name = sc.next();
      System.out.println("Enter your age ");
      int age = sc.nextInt();
      Student std = new Student(name, age);
      System.out.println("Contents of the original object");
      std.displayData();
      System.out.println("Contents of the copied object");
      Student copyOfStd = new Student().copyObject(std);
      copyOfStd.displayData();
   }
}

Output:

Enter your name
John
Enter your age
20
Contents of the original object
Name : John
Age : 20
Contents of the copied object
Name : John
Age : 20

Argument Passing

In general, there are two ways that a computer language can pass an argument to a subroutine. The first way is call-by-value. This approach copies the value of an argument into the formal parameter of the subroutine. Therefore, changes made to the parameter of the subroutine have no effect on the argument. The second way an argument can be passed is call-by-reference. In this approach, a reference to an argument (not the value of the argument) is passed to the parameter. Inside the subroutine, this reference is used to access the actual argument specified in the call. This means that changes made to the parameter will affect the argument used to call the subroutine. Please note that Java uses call by value. When passing a reference type (e.g. objects), the reference is copied in the called method. Both the copies of the references refer to the same object, hence the changes are reflected from one to the other. Let's look at an example

Example:

class Data {
    int value;
    Data(int value) {
        this.value = value;
    }
}
class CallByExample {
    static void callByValue(int x, int y) {
        x *= 2;
        y /= 2;
    }
    static void callByReference(Data data) {
        data.value *= 2;
    }
}

class Main
{
    public static void main(String[] args) {
        int a = 15, b = 20;
        CallByExample ob= new CallByExample();
        System.out.println("a and b before callByValue: " + a + " " + b);
        ob.callByValue(a, b);
        System.out.println("a and b after callByValue: " + a + " " + b);

        // Call by reference example (for objects)
        Data obj = new Data(15);
        System.out.println("obj.value before callByReference: " + obj.value);
        ob.callByReference(obj);
        System.out.println("obj.value after callByReference: " + obj.value);
    }
}

Output:

a and b before callByValue: 15 20
a and b after callByValue: 15 20
obj.value before callByReference: 15
obj.value after callByReference: 30

Recursion

Recursion is the process of defining something in terms of itself. As it relates to Java programming, recursion is the attribute that allows a method to call itself. A method that calls itself is said to be recursive. The classic example of recursion is the computation of the factorial of a number. The factorial of a number N is the product of all the whole numbers between 1 and N

Example:

class Factorial {
 // this is a recursive method
 int fact(int n) {
 int result;
 if(n==1) return 1;
 result = fact(n-1) * n;
 return result;
 }
}
class Recursion {
 public static void main(String[] args) {
 Factorial f = new Factorial();
 System.out.println("Factorial of 3 is " + f.fact(3));
 System.out.println("Factorial of 4 is " + f.fact(4));
 System.out.println("Factorial of 5 is " + f.fact(5));
 }
} 

Output:

Factorial of 3 is 6
Factorial of 4 is 24
Factorial of 5 is 120

Working of the above Program

factorial(5) 
   factorial(4) 
      factorial(3) 
         factorial(2) 
            factorial(1) 
               return 1 
            return 2*1 = 2 
         return 3*2 = 6 
      return 4*6 = 24 
   return 5*24 = 120

Recursive versions of many routines may execute a bit more slowly than the iterative equivalent because of the added overhead of the additional method calls. A large number of recursive calls to a method could cause a stack overrun. Because storage for parameters and local variables is on the stack and each new call creates a new copy of these variables, it is possible that the stack could be exhausted. If this occurs, the Java run-time system will cause an exception.

Static Keyword

There will be times when you will want to define a class member that will be used independently of any object of that class. Normally, a class member must be accessed only in conjunction with an object of its class. However, it is possible to create a member that can be used by itself, without reference to a specific instance. To create such a member, precede its declaration with the keyword static. When a member is declared static, it can be accessed before any objects of its class are created, and without reference to any object. You can declare both methods and variables to be static.

Example: main() function of class is declared static because it must be called before any objects exist.

If method and variables of a class are declared static, then we don't need to create an object to call them.

Example:

class StaticDemo {
 static int a = 42;
 static int b = 99;
 static void callme() {
 System.out.println("a = " + a);
 }
}


class Main {
 public static void main(String[] args) {
 StaticDemo.callme(); //calling method callme() without an object
 System.out.println("b = " + StaticDemo.b); //accesiing b without object
 }
} 

Output:

a = 42
b = 99

Characteristics of static:

  • Shared memory allocation

  • Accessible without object instantiation

  • Associated with class, not objects

  • Cannot access non-static members

  • Can be overloaded, but not overridden:

Final Keyword

The final keyword is a non-access modifier used for classes, attributes and methods, which makes them non-changeable (impossible to inherit or override).

The final keyword is useful when you want a variable to always store the same value, like PI (3.14159...)

The final keyword can be applied with the variables, a final variable that have no value it is called blank final variable or uninitialized final variable. It can be initialized in the constructor or during declaration only. The blank final variable can be static also which can be initialized in the static block only.

Example:

class Bike{  
 final int speedlimit=90;//final variable  
 void run(){  
  speedlimit=400;  
  }  
 }
 
 class Main
 {
   public static void main(String args[]){  
   Bike obj=new  Bike();  
   obj.run(); 
   } 
 } 

Output:

Main.java:4: error: cannot assign a value to final variable speedlimit
  speedlimit=400;  
  ^
1 error

Arrays.length

There is a special array attribute to calculate the size of an array, that is, the number of elements that an array can hold, it is found in its length instance variable

Example

int[] a = {4, 3, 2, 1};
System.out.println("length of array is " + a.length); 

Output:

length of array is 4

Arrays.sort

Arrays.sort() method consists of two variations one in which we do not pass any arguments where it sort down the complete array be it integer array or character array but if we are supposed to sort a specific part using this method of Arrays class then we overload it and pass the starting and last index to the array.

Example for sorting an integer array using Arrays.sort without arguments()

import java.util.Arrays;
 
class Sort {
    public static void main(String args[])
    {
        int[] arr = { 5, -2, 23, 7, 87, -42, 509 };
        System.out.println("The original array is: ");
        for (int num : arr) {
            System.out.print(num + " ");
        }
        Arrays.sort(arr);
        System.out.println("\nThe sorted array is: ");
        for (int num : arr) {
            System.out.print(num + " ");
        }
    }
}

Output

The original array is: 
5 -2 23 7 87 -42 509 
The sorted array is: 
-42 -2 5 7 23 87 509 

Example for sorting an integer array using Arrays.sort with arguments()

import java.util.Arrays;
 
public class Sort {
    public static void main(String[] args)
    {
        int[] arr = { 13, 7, 6, 45, 21, 9, 2, 100 };
        // Sort subarray from index 1 to 4, i.e.,
        // only sort subarray {7, 6, 45, 21} and
        // keep other elements as it is.
        Arrays.sort(arr, 1, 5);
        System.out.println("Modified arr[] : "
                           + Arrays.toString(arr));
    }
}

Output

Modified arr[] : [13, 6, 7, 21, 45, 9, 2, 100]

Nested and Inner Classes

It is possible to define a class within another class; such classes are known as nested classes. The scope of a nested class is bounded by the scope of its enclosing class. Thus, if class B is defined within class A, then B does not exist independently of A. A nested class has access to the members, including private members, of the class in which it is nested. However, the enclosing class does not have access to the members of the nested class.

There are two types of nested classes:

  1. static - A static nested class is one that has the static modifier applied. Because it is static, it must access the non-static members of its enclosing class through an object. That is, it cannot refer to non-static members of its enclosing class directly.

  2. non-static - The second type of nested class is the inner class. An inner class is a non-static nested class. It has access to all of the variables and methods of its outer class and may refer to them directly in the same way that other non-static members of the outer class do.

Example of Static Nested Class

class OuterClass {
  int x = 10;

  static class InnerClass {
    int y = 5;
  }
}

public class Main {
  public static void main(String[] args) {
    OuterClass.InnerClass myInner = new OuterClass.InnerClass();
    System.out.println(myInner.y);
  }
}

Output

5

Example of Inner Nested Class

class OuterClass {
  int x = 10;

  class InnerClass {
    int y = 5;
  }
}

public class Main {
  public static void main(String[] args) {
    OuterClass myOuter = new OuterClass();
    OuterClass.InnerClass myInner = myOuter.new InnerClass();
    System.out.println(myInner.y + myOuter.x);
  }
}

Output

15

Java String

String is probably the most commonly used class in Java’s class library. The obvious reason for this is that strings are a very important part of programming. The first thing to understand about strings is that every string you create is actually an object of type String.

Once you have created a String object, you can use it anywhere that a string is allowed, so it works as a variable but it actually is an object of String class.

String Length

A String in Java is actually an object, which contain methods that can perform certain operations on strings. For example, the length of a string can be found with the length() method.

Example

String txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
System.out.println("The length of the txt string is: " + txt.length());

Output

The length of the txt string is: 26

String Concatenation

The + operator can be used between strings to combine them. This is called concatenation:

Example

String firstName = "John";
String lastName = "Doe";
String fullName = firstName + lastName;
System.out.println(fullName);

Output

John Doe

String charAt() Method

The Java String charAt() method returns the character at the specified index. The index value should lie between 0 and length() – 1.

Example

String s = "Hello World";

char ch = s.charAt(3);
System.out.println(ch);

ch = s.charAt(0);
System.out.println(ch);

Output

l
H

String equals() Method

The equals() method compares two strings, and returns true if the strings are equal, and false if not.

Tip: Use the compareTo() method to compare two strings lexicographically.

Example

String myStr1 = "Hello";
String myStr2 = "Hello";
String myStr3 = "Another String";
System.out.println(myStr1.equals(myStr2)); // Returns true because they are equal
System.out.println(myStr1.equals(myStr3)); // false

Output

true
false

Varargs: Variable-Length Arguments

Modern versions of Java include a feature that simplifies the creation of methods that need to take a variable number of arguments. This feature is called varargs and it is short for variablelength arguments. A method that takes a variable number of arguments is called a variablearity method, or simply a varargs method.

A variable-length argument is specified by three periods (…)

If a vararg variable is declared it must be treated as an array inside the method.

Example:

class VarArgs {
 // vaTest() now uses a vararg.
 static void vaTest(int ... v) {
 System.out.print("Number of args: " + v.length +
 " Contents: ");
 for(int x : v) // for-each loop
 System.out.print(x + " ");
 System.out.println();
  }
 }
 class Main
 {
   public static void main(String[] args)
   {
    VarArgs obj= new  VarArgs();
   // Notice how vaTest() can be called with a
   // variable number of arguments.
   obj.vaTest(10); // 1 arg
   obj.vaTest(1, 2, 3); // 3 args
   obj.vaTest(); // no args
  }
}

Output

 Number of args: 1 Contents: 10
 Number of args: 3 Contents: 1 2 3
 Number of args: 0 Contents:

Some key points:

  1. A method can have “normal” parameters along with a variable-length parameter. However, the variable-length parameter must be the last parameter declared by the method.

int doIt(int a, int b, double c, int ... vals) //valid

int doIt(int a, int b, double c, int ... vals, boolean stopFlag)//invalid

  1. There must be only one varargs parameter.

int doIt(int a, double c, int ... vals, double ... morevals) //invalid

  1. You can overload a method that takes a variable-length argument. For example, the following code overloads vaTest 2 times.

class VarArgs3 {
    static void vaTest(int ... v) {
        System.out.print("vaTest(int ...): " +
        "Number of args: " + v.length +
        " Contents: ");
        for(int x : v)
        System.out.print(x + " ");
        System.out.println();
    }
    static void vaTest(String msg, int ... v) {
        System.out.print("vaTest(String, int ...): " +
        msg + v.length +
        " Contents: ");
        for(int x : v)
        System.out.print(x + " ");
        System.out.println();
    }
}
class Main
{   
    public static void main(String[] args) {
        VarArgs3 obj= new VarArgs3();
        obj.vaTest("Testing: ", 10, 20);
        obj.vaTest(1, 2, 3);
    }
}

Output

vaTest(String, int ...): Testing: 2 Contents: 10 20
vaTest(int ...): Number of args: 3 Contents: 1 2 3

Note the ambiguities which can arise when using varargs as discussed in the lectures.

Exercises

Exercise 1

Implement a Java program that defines a class Point2D representing a point in a 2D space with x and y coordinates. Then, create a method translatePoint that takes a Point2D object and two integers (dx and dy). This method should translate the point's coordinates by adding dx to the x coordinate and dy to the y coordinate. The method should return a new Point2D object representing the translated point.

Write a main method to test your implementation by taking initial inputs and translation parameters dx, dy from the user, creating an initial Point2D object, displaying its coordinates, calling the translatePoint method, and displaying the coordinates of the translated point.

Sample Input

Enter initial x coordinate: 2
Enter initial y coordinate: 3
Enter dx: 1
Enter dy: 2

Sample Output

Initial Point: (2, 3)
Translated Point: (3, 5)

Exercise 2

Implement a Java program that calculates the sum and multiplication of elements in an array within a specified range using recursion. The program should take user input for the array size, array elements, starting index, and ending index. Ensure the validity of both indices and then use two recursive methods to calculate the sum and multiplication of array values within the specified range (both indices inclusive).

Sample Input

Enter the size of array: 10
Enter the array values: 1 2 3 4 5 6 7 8 9 10
Enter Starting Index: 3
Enter Ending Index: 9

Sample Output

Sum of elements from index 3 to 9 = 42
Multiplication of elements from index 3 to 9 = 181440

Exercise 3

You are asked to implement a program that checks if two strings are anagrams. An anagram is a word or phrase formed by rearranging the letters of another. Your task is to create a Java program with the following specifications:

Implement a class named AnagramChecker with the following methods:

areAnagrams(String str1, String str2): A static method that checks whether two strings are anagrams. The method should return true if the strings are anagrams and false otherwise. Use the .equals() method for string comparison. Implement an inner class named StringUtil within the AnagramChecker class with the following methods:

sortString(String str): A method that takes a string as input, sorts its characters, and returns the sorted string. You can use any sorting algorithm or convert the string to a character array and then use Arrays.sort on that to implement this.

In the main method of the program: Take user input for two strings. Use the AnagramChecker class to check if the entered strings are anagrams. Display the result.

(Assume that there are no white blank spaces present in both the strings)

Sample Input

Enter the first string: listen
Enter the second string: silent

Sample Output

The entered strings are anagrams.

Last updated