We could create objects for the Student and initialize the variables of the object using the constructor. But after that, we can no longer change the values the variables hold. The following statement would cause an error since the variables were declared to be private.

s1.name = “Ravi”;

The solution lies in providing public methods through which new values can be assigned to these variables and the values they hold can be accessed. These are commonly known as get and set methods. Get methods provide access to the value a variable holds while set methods assign values to the variables of the objects.

First, we shall see how we write a method. In order to set a variable to a new value, the method needs arguments similar to how the parameterized constructors required details such as rollNumber, name, and marks. These required arguments are specified in the method header within the parentheses by specifying the variable name and a data type for that variable name. The set method for name looks as shown below:

public void setName ( String n ) {
name = n;
}

It is a convention to name the setter methods starting with the word ‘set’ followed by the variable name that is going to be set. If we wish to set the name for the first Student object s1 which we have created, we use the following statement:

s1.setName(“Ravi”);

Now, we shall provide a get method for the variable name. As we have earlier said, methods are capable of returning values. These values can be of any primitive data type or reference type as a class. We will be returning String here. The data type that is going to be returned in specified in the method header instead of the word void. We have used the word void until now to indicate that a method does not return any value. The get method for the name would be the following:

public String getName() {
return name;
}

The value to be returned is specified by using the ‘return’ keyword. The variable or value that is to be returned follows the return keyword. We can specify not just a variable name but also a constant or an expression. The following return statements would also be equally fine.

return “XYZ”;

return “Name: “+name;

However, for the current scenario, we require that only the name be returned and not anything in addition. Also, the return statement should be the last line in a method that returns a value. Once the return statement is reached, the lines of code that follow the return statement would not be executed. In this particular case, we cannot write any statements after the return statement. This is because the unreachable code generates compilation errors. Unreachable code refers to those statements which can never be executed. Consider the following get method for a name.

public String getName() {
return name;
name = “No name”;
}

The statement name=”No name” is an unreachable statement. This is because, once the return statement is encountered, the remaining lines of code in that method would be ignored. Therefore, the above code produces compilation errors. However, in certain circumstances, we can provide a return statement and also write code following that statement when we use decision-making statements. We will look into it later on.

Now, that we have written a get method, we shall see how we can use it. We can invoke the getName() method a Student object, and the returned value can be stored in a variable, can be printed on the screen, or can be used for any other purpose.

In the following statement, the value returned by getName is stored in the variable aName.

String aName = s2.getName();

As we have earlier initialized the name of s2 object with “Sai”, aName will hold the value “Sai”. We can also directly print the returned value by invoking the method within the parentheses in the following way:

System.out.println (“Name of s2: “+ s2.getName() );

In a similar way, we can provide get and set methods for all other variables of the class. But what exactly is the use of these get and set methods? Instead of providing these methods, we could have simply made the variables public and they could be accessed in the following way instead of using get and set methods.

s1.name=”Ravi”;
System.out.println(“Name of s1: ” +s1.name);

Such a simpler approach would reduce the work of providing a get and a set method for each of the five variables, greatly reducing coding work. But that isn’t the preferred way for programmers. When data is easily accessible in that way, data isn’t secure. One who uses the class can easily assign the marks1 of a Student with the integer 1000 where in reality, the maximum marks are 100. In order to restrict access and to check data before it is assigned to the variables, we provided the get and set methods.

We haven’t done such checking for the name variable. We shall see how it can be done for the marks1 variable. For this purpose, we use decision making statements. We shall see two of them for now: the if statement and the if-else statement. Following is the syntax of the if statement:

if ( condition ) {
// code
}

And here is the syntax for if else statement

if ( condition ) {
// code1
} else {
// code2
}

We hope that you are able to guess what the purpose of the above statements is. To make sure that you’ve got it right, let’s look at them individually. If the condition stated in the parentheses of if the statement is true, then the code in the block is executed. In a similar way, for the if-else statement, if the condition is true, the first block of code is executed. If the condition is false, the statements in the else block are executed. As already said, a block is a set of statements within a pair of curly braces.

They indicate the ownership of a piece of code. Here, the blocks are used to indicate that the code contained in them is a part of the corresponding if or else statements. If the code which is to write for an if or else statement has just a single line or a single statement, there is no need to put the braces. Another important thing is that variables declared within a block are not accessible outside the block. Here, if we declare a new variable ‘var’ in the if block and try to print its value in the else blocks, we will be getting a compilation error.

The same holds true for methods also. For instance, if we declare a new variable in the setName() method and try to access it in the getName() method, we would be stopped by compilation errors. The following code is erranous.

public void setName ( String n ) {
name = n;
int var = 34;
}

public String getName() {
var = 7; // incorrect var is not known here
return name;
}

Now, it’s time to see how these decision-making statements can be used to enforce data validation in the set methods. But before that, we need to learn how we can write conditions in a language that is understandable by the computer. To make it short, we should be able to tell the computer ‘If marks are greater than or equal to zero and less than or equal to 100, assign the value to the variable marks, else set the marks to zero’.

In order to write conditions, we use relational and logical operators. We have already come across another category of operators – mathematical operators – and we have used them widely. Now, we shall see what the relational and logical operators are. Here are some of the operators in each of the categories that we would be used widely from now onwards.

Relational operators:
> Greater than
< Less than
>= Greater than or equal to
<= less than or equal to
== equal to
!= not equal to
Logical operators
&& Logical and
|| Logical or
! Logical not

Relational operators are used to comparing the values of two data items – variables or constants. Each of the variables requires two operands and the result returned would be either true or false. These operators are similar to what we come across in mathematics; the only difference lies in their representation. Look at the following code to see how these relational operators work. As already said, the result of a relational operation is a boolean value: true or false. Hence, we need a boolean variable to store the result.

int a=3, b=4, c=4;
boolean res1 = a>b ; // res1 holds false
boolean res2 = b<10; // res2 holds true
boolean res3 = b>=c; // res3 holds true
boolean res4 = c<=34; // res4 holds true
boolean res5 = b==c ; // res5 holds true
boolean res6 = 3!=a; // res6 holds true

Now, coming to logical operators – we have three of them. ‘Logical and’ and ‘logical or’ require two operands while ‘logical not’ require a single operand. The operands in all the cases are of type boolean and the result obtained is also a boolean. The ‘logical operator and’ (&&) returns true when both the operands are true, otherwise returns false. ‘Logical or’ (||) returns true when at least one of the operand is True. And lastly ‘logical not’ (!) returns true if the operand is false and returns false if the operand is true. The following are the truth tables of these operators while are helpful in knowing the value of the returned boolean when the values of the operands are known.

Logical operator and &&
Operand 1 Operand 2 Result
true true true
true false false
false true false
false false false

Logical operator or ||
Operand 1 Operand 2 Result
true true true
true false true
false true true
false false false

Logical operator not !
Operand Result
true false
false true

The following code shows the use of logical operators:

boolean a = true;
boolean b = false;
boolean res1 = a&&b; // false
boolean res2 = a||b; //true
boolean res3 = !a; //false
boolean res4 = !false; // true
boolean res5 = true&&a; //true

Now, to frame the conditions that we would use in the if statements, we make combined use of the relational and logical operators. Operands for a logical operation can be expressions too. We frame these expressions using relational operators. For example, in order to check that the marks are both >= 0 and <= 100, we use the following condition:

marks1 >= 0 && marks1 <=100

In the above expressions, the conditions marks1>=0 and marks1<=0 are evaluated first, and then the result of these evaluations are used to find the result of the ‘logical and’ operation. The order in which the operations are done depends on the precedence of operators. Relational operators have higher precedence than logical operators. Hence they are evaluated first. Further, the different relational and logical operators also have a precedence order among themselves. For a complete list of operator precedence, visit this page. When you are not sure about the precedence, use parentheses so that the expression will be evaluated in the way you would like. The above expression could have been written more clearly as:

(marks1 >= 0 ) && ( marks1 <=100)

The following is the complete setMarks1() method. This method takes an integer argument. If the argument satisfies the conditions, then marks1 are initialized to that argument, otherwise, we initialize marks1 to zero.

public void setMarks( int m) {
if ( m>=0 && m<=100 ) {
marks1 = m;
} else {
marks1 = 0;
}
}

We again emphasize the fact that Java is a free form of language. You can break a statement into two lines at any valid point and also write over one statement one single line. If you feel that reading the above code is inconvenient to you, we suggest the following alternative form.

public void setMarks( int m)
{
if ( m>=0 && m<=100 )
{
marks1 = m;
}
else
{
marks1 = 0;
}
}

Also, in this case, since the if and else blocks contain only a single statement, you can omit the braces and write the code in the following way:

public void setMarks( int m)
{
if ( m>=0 && m<=100 )
marks1 = m;
else
marks1 = 0;
}

Another remainder regarding eth accessibility of a variable: Variables defined within a block are accessible in that block only. We know these conditions as the scope and lifetime of variables. Scope refers to the part of the code in which the variable is accessible while lifetime refers to the duration for which variable exits before it is destroyed by deallocating the memory allotted to it while declaring the variable.

If we declare a variable within the if block, the lifetime ends once we come off if the if block. Any attempt to access that variable would give an error that the requested variable is not defined. If there is a need to declare variables inside such blocks and access them even after we move out of the blocks, then we need to declare these variables before the starting of the block and outside the block. The following pieces of code show an incorrect way and a correct way of using such variables.

public void setMarks( int m)
{
if ( m>=0 && m<=100 )
int temp = m;
else
int temp = 0;
marks1 =temp; // Incorrect: temp is not known here
}
public void setMarks( int m)
{
int temp;
if ( m>=0 && m<=100 )
temp = m;
else
temp = 0;
marks1 =temp; // Correct: temp is known here
}

In the second piece of code, the variable temp is accessible since it was declared outside the if and else blocks in the same block as that of the method. Variables declared within methods are known as local variables. For example, here temp is a local variable. Such variables are accessible within that particular method only. The scope and lifetime of local variables is the method that blocks itself.

Now that we have learned how to use set and get methods to enforce the restriction on the data that can be assigned to variable, we present a complete program below. We also provide a getAverage() method which computes the average if the Student. We use this getAverage() method in the printDetails() function. Then we have a test class, where the object is initialized with values entered by the user. And then, we display the details of the student in two ways- one by using the printDetails() method and the other by using the get methods provided in the class.

Given below is the Student class

public class Student {

private int rollNumber;
private String name;
private int marks1;
private int marks2;
private int marks3;

public void setName(String s) {
name = s;
}

public String getName() {
return name;
}

public void setRollMumber(int r) {
if (r > 0) {
rollNumber = r;
} else {
rollNumber = 1;
}
}

public int getRollNumber() {
return rollNumber;
}

public void setMarks1(int m) {
if (m >= 0 && m <= 100) {
marks1 = m;
} else {
marks1 = 0;
}
}

public int getMarks1() {
return marks1;
}

public void setMarks2(int m) {
if ((m >= 0) && (m <= 100)) {
marks2 = m;
} else {
marks2 = 0;
}
}

public int getMarks2() {
return marks2;
}

public void setMarks3(int m) {
if ((m >= 0) && m <= 100) {
marks3 = m;
} else {
marks3 = 0;
}
}

public int getMarks3() {
return marks3;
}

public double getAverage() {
return (marks1+ marks2 + marks3) / 3.0;
}

public void printDetails() {
System.out.println(“Roll Number: ” + rollNumber);
System.out.println(“Name: ” + name);
System.out.println(“Marks in first subject: ” + marks1);
System.out.println(“Marks in second subject: ” + marks2);
System.out.println(“Marks in second subject: ” + marks3);
System.out.println(“Average: ” + getAverage());
}
}

Go through the above class carefully and note the following points. The condition expressions in each of the three methods for setting marks were written in a slightly different manner. The parentheses used in them were redundant i.e. it would have made no difference even if the parentheses were omitted. The following are the three different forms we have used. All of them are equivalent:

m >= 0 && m <= 100
(m >= 0) && (m <= 100)
(m >= 0) && m <= 100

We can also write the condition in the following way

m >= 0 && ( m <= 100)

Also, note how the if-else conditions were written. All the blocks have a single statement. Therefore, enclosing a single statement in curly braces is optional. Therefore all three forms of if-else we have written for the different methods are equivalent. If we want to do so, we may also enclose the other statement in curly braces and leave the if statement without a pair of curly braces.

Another thing is regarding the computation of average. We need to write 3.0 and not 3. If we write it as 3, then the result will be an integer and the fractional part would be lost. We could also have written the statement as:

(marks1 + marks2 + marks3) / (double)3;

Note that the return keyword in getAverage() method is followed by the above expression and not a variable as in the other get methods. Variables, constants, and expressions can be returned. Before returning the expression is first evaluated. We can write the getAverage() method in the following way also:

public double getAverage() {
double average =(marks1 + marks2 + marks3) / 3.0;
return average;
}

Also, note that the return type specified in the method header of the getAvearge() method is double. This type needs to match the data type of the value being returned.

The expressions have been written in different styles, using parentheses at some places, omitting them at a few other places, using curly braces at some points, and so on. This has been done to show that all the forms of coding are acceptable. However, when you write a program, use the same style throughout. It makes your program appeal to others.

Also given below is the Test program. In this program, we use the second constructor that accepts a roll number and a name to create the object. And the marks are set using different set marks methods. And then we print data; first by invoking printDetails() method and then by using the different get methods.

The only thing worth mentioning here is the manner in which we have taken the input:

s1.setMarks1(s.nextInt());

This statement is correct. As already said, arguments passed are first evaluated before they are passed to the method. So s.nextInt() method will be executed and then the integer will be passed to setMarks1() method.

The following is the StudentTest class.

import java.util.Scanner;

public class StudentTest {

public static void main(String[] args) {
Student student = new Student();
Scanner scanner = new Scanner(System.in);
System.out.print(“Enter Name: “);
String name = scanner.nextLine();
student.setName(name);
System.out.print(“Enter roll number: “);
int rollNumber = scanner.nextInt();
student.setRollMumber(rollNumber);
System.out.print(“Enter marks1: “);
int marks1 = scanner.nextInt();
student.setMarks1(marks1);
System.out.print(“Enter marks2: “);
int marks2 = scanner.nextInt();
student.setMarks1(marks2);
System.out.print(“Enter marks3: “);
int marks3 = scanner.nextInt();
student.setMarks1(marks3);
System.out.println();
System.out.println(“Printing details using printDetails() method: =”);
student.printDetails();
System.out.println();
System.out.println(“Printing details without using printDetails() method:”);
System.out.println(“Name: ” + student.getName());
System.out.println(“Roll Number: ” + student.getRollNumber());
System.out.println(“Marks1: ” + student.getMarks1());
System.out.println(“Marks2: ” + student.getMarks2());
System.out.println(“Marks3: ” + student.getMarks3());
System.out.println(“Average: ” + student.getAverage());
}
}

We have named the Student object as student. We can give any name including the same name as that of the class, capitalization included. For instance, the following statement is also correct.

Student Student = new Student();

Here is a sample input where the user enters all the correct details.

Enter name: Sai
Enter roll number: 34
Enter marks1: 98
Enter marks2: 97
Enter marks3: 100

Printing details using printDetails() method:
Roll Number: 34
Name: Sai
Marks in first subject: 98
Marks in second subject: 97
Marks in second subject: 100
Average: 98.33333333333333

Printing details without using printDetails() method:
Name: Sai
Roll Number: 34
Marks1: 98
Marks2: 97
Marks3: 100
Average: 98.33333333333333

Here is a sample input where the user enters incorrect details.

Enter name: Sai
Enter roll number: 34
Enter marks1: 100
Enter marks2: -34
Enter marks3: 347

Printing details using printDetails() method:
Roll Number: 34
Name: Sai
Marks in first subject: 100
Marks in second subject: 0
Marks in second subject: 0
Average: 33.333333333333336

Printing details without using printDetails() method:
Name: Sai
Roll Number: 34
Marks1: 100
Marks2: 0
Marks3: 0
Average: 33.333333333333336

Now, try creating a Student object using the constructor where all the values all passed in the following way.

Student s = new Student ( “sai”,34,100,-34,347);

Now invoke the printDetails() method. You will see that marks2 and marks3 were not set as zero but as -34 and 347. The reason is that in the constructor, we have not provided data validation. We have directly assigned the values. Hence, we need to modify the constructors and call the set method from the constructors instead of directly assigning the values. Following is a modified version of the constructor. The name variable can be either directly initialized or by calling the set method. Either way it gives the same result because no data validation was provided for the name variable. The following is a modified version of the constructor.

public Student(String n, int rn, int m1, int m2, int m3) {
name = n;    setRollNumber(rn);
setMarks1(m1);
setMarks2(m2);
setMarks3(m3);
}