Step by step, your programming tasks are becoming more complex, so are your methods. Though you can create a complex program that is wrapped in one solid method or even in a main method, it is better to divide a program into a number of more specific methods that are easy to read and understand. The approach of dividing a complex program into subroutines is called functional decomposition.
Functional decomposition is simply a process of decomposing a problem into several functions or methods. Each method does a particular task so that we can perform these methods in a row to get the results we need. When we look at a problem, we need to think which actions we may want to repeat multiple times or, alternatively, perform separately. This is how we get the desired methods. As a result, these methods are easier to read, understand, reuse, test, and debug.
// method that turns the music on and off
public static void controlMusic() {
Scanner scanner = new Scanner(System.in);
System.out.println("on/off?");
String tumbler = scanner.next();
if (tumbler.equals("on")) {
System.out.println("The music is on");
} else if (tumbler.equals("off")) {
System.out.println("The music is off");
} else {
System.out.println("Invalid operation");
}
}
// method that verifies the password and gives access to Smart home actions if the password is correct
public static void accessSmartHome() {
Scanner scanner = new Scanner(System.in);
final int password = 76543210;
System.out.println("Enter password: ");
int passwordInput = scanner.nextInt();
if (passwordInput == password) {
chooseAction();
} else {
System.out.println("Incorrect password!");
}
}
public static void main(String[] args) {
accessSmartHome();
}
Example 1:
The template for this function is defined below. Let's decompose it!
Your task is to create three additional methods f1, f2, and f3 for each case and complete the method f. Each method should accept x as an argument with double type.
import java.util.Scanner;
class MultipleFunction {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double x = scanner.nextDouble();
System.out.println(f(x));
}
public static double f(double x) {
//call your implemented methods here.
if (x <= 0) {
return f1(x);
} else if (0 < x && x < 1) {
return f2(x);
} else {
return f3(x);
}
}
//implement your methods here
public static double f1(double x) {
return x * x + 1;
}
public static double f2(double x) {
return 1 / (x * x);
}
public static double f3(double x) {
return x * x - 1;
}
}
Input
0.5
Output
4.0
Example 2:
In the code template below, you can see a program that creates a full name from the first and last names for several people. Wouldn't it be much easier if we had a method that could do the same?
Your task is to create the method createFullName that takes firstName and lastName (in this order) and returns the full name.
// Don't delete scanner import
import java.util.Scanner;
class Name {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String firstName = scanner.next();
String lastName = scanner.next();
System.out.println(createFullName(firstName, lastName));
}
}
//implement your method here
public static String createFullName(String firstName, String lastName) {
return firstName + " " + lastName;
}
}
Input
John
Lennon
Output
John Lennon
Example 3:
In a template below you have a simple calculator that subtracts, sums, divides, and multiplies the two numbers inside the switch statement. Now, we've decided to upgrade it to perform more complex tasks, such as logarithmic functions. For that, separate methods are a better solution. Let's start with decomposing what we have.
Take a look at the template and decompose operations of the calculator into four methods: subtractTwoNumbers(long a, long b) for subtraction, sumTwoNumbers(long a, long b) for adding, divideTwoNumbers(long a, long b) for integer division and multiplyTwoNumbers(long a, long b) for multiplication. Each method should print the result of calculations.
Note that you can't divide by zero. In case your second argument is zero, you should print the "Division by 0!" message.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class jetBrains_calculator {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String input = br.readLine();
long a = input.charAt(0) - '0';
long b = input.charAt(4) - '0';
char s = input.charAt(2);
switch (s) {
case '-':
subtractTwoNumbers(a, b);
break;
case '+':
sumTwoNumbers(a, b);
break;
case '/':
divideTwoNumbers(a, b);
break;
case '*':
multiplyTwoNumbers(a, b);
break;
}
}
public static void subtractTwoNumbers(long a, long b) {
System.out.print(a - b);
}
public static void sumTwoNumbers(long a, long b) {
System.out.print(a + b);
}
public static void divideTwoNumbers(long a, long b) {
if (b == 0) {
System.out.print("Division by 0!");
} else {
System.out.print(a / b);
}
}
public static void multiplyTwoNumbers(long a, long b) {
System.out.print(a * b);
}
}
Input
1 + 2
Output
3
In Java, all data types are separated into two groups: primitive types and reference types.
Java provides only eight primitive types. They are built-in in the language syntax as keywords. The names of all primitive types are lowercase. The most commonly used type is int which represents an integer number.
int num = 100;
The number of reference types is huge and constantly growing. A programmer can even create their own type and use it like standard types. The most frequently used reference types are String, Scanner and arrays. Remember that Java, like most programming languages, is case sensitive.
In this topic, we will focus on String, which is a common example of the reference type.
The basic difference between primitive and reference types is that a variable of a primitive type stores the actual values, whereas a variable of a reference type stores an address in memory (reference) where the data is located. The data can be presented as a complex structure that includes other data types as their parts.
The following picture simply demonstrates this difference. There are two main memory spaces: stack and heap. All values of primitive types are stored in stack memory, but variables of reference types store addresses of objects located in heap memory.
When you need to process multiple objects of the same type, you can save them in an array and then process them together as a single unit. It is a very convenient approach if you do not know how many objects the program will process during runtime.
You may consider an array as a collection of elements of the same type. All elements are stored in the memory sequentially.
The collection provides one name for its elements. The possible number of elements to be stored is established when the array is created and cannot be changed. But a stored element can be modified at any time.
- An array is a reference type;
- All array elements are stored in the memory sequentially;
- Each element of the array is accessed by its numerical index, the first element has the index 0;
- The last element is accessed by the index equal to array size – 1;
- It is possible to create an array to store elements of any type.
The most general way to create an array is to use the special keyword new and specify the necessary number of elements:
int n = ...; // n is a length of an array
int[] numbers = new int[n];
This form is useful when the number of elements is known before starting the program. When we create an instance of the array object with indicated length like [n] or [5] and don't enumerate its elements explicitly, the array is initialized with default values of its type.
Now, the array has n elements. Each element is equal to zero (the default value of the type int). Next, we should make an explicit initialization of elements.
The standard library of Java provides a lot of useful methods for processing strings:
- isEmpty() returns true if the string is empty, otherwise – false;
- toUpperCase() returns a new string in uppercase;
- toLowerCase() returns a new string in lowercase;
- startsWith(prefix) returns true if the string starts with the given string prefix, otherwise, false;
- endsWith(suffix) returns true if the string ends with the given string suffix, otherwise, false.
- contains(...) returns true if the string contains the given string or character;
- substring(beginIndex, endIndex) returns a substring of the string in the range: beginIndex, endIndex - 1;
- replace(old, new) returns a new string obtained by replacing all occurrences of old with new that can be chars or strings.
- trim() returns a copy of the string obtained by omitting the leading and trailing whitespace. Note that whitespace includes not only the space character, but mostly everything that looks empty: tab, carriage return, newline character, etc.
String text = "The simple text string";
boolean empty = text.isEmpty(); // false
String textInUpperCase = text.toUpperCase(); // "THE SIMPLE TEXT STRING"
boolean startsWith = textInUpperCase.startsWith("THE"); // true
/* replace all space characters with empty strings */
String noSpaces = textInUpperCase.replace(" ", ""); // "THESIMPLETEXTSTRING"
String textWithWhitespaces = "\t text with whitespaces !\n \t";
String trimmedText = textWithWhitespaces.trim(); // "text with whitespaces !"
When programmers are writing a real program, they use standard classes as building blocks. However, they often need to declare new program-specific classes to better represent the domain area. In this topic, we will see how you can create a custom class in Java.
A new class is declared with the class keyword followed by the name of the class. For example, this is how you would create a class named Nothing:
class Nothing {
// empty body
}
A class body can include fields, methods, and constructors. Fields store data, methods define behavior and constructors allow us to create and initialize new objects of the class. Not all Java classes have fields and methods so sometimes you will see classes without them.
The source code of a class is placed in a .java file. Usually, a source code file contains only one class and has the same name as that class, but sometimes a file can contain more classes.
A field is a variable that stores data. It may have any type, including primitive types (int, float, boolean and so on) and classes (even the same class). A class can have as many fields as you need.
Let's declare a class Patient:
/**
* The class is a "blueprint" for patients
*/
class Patient {
String name;
int age;
float height;
String[] complaints;
}
This class represents a patient in a hospital information system. It has four fields for storing important information about the patient: name, age, height, and complaints. All objects of the class Patient have the same fields, but their values may be different for each object.
Let's create an instance of the class Patient using the keyword new:
Patient patient = new Patient();
When you create a new object, each field is initialized with the default value of the corresponding type.
System.out.println(patient.name); // it prints null
System.out.println(patient.age); // it prints 0