Important Announcement
PubHTML5 Scheduled Server Maintenance on (GMT) Sunday, June 26th, 2:00 am - 8:00 am.
PubHTML5 site will be inoperative during the times indicated!

Home Explore Learn Java in One Day and Learn It Well ( PDFDrive )

Learn Java in One Day and Learn It Well ( PDFDrive )

Published by THE MANTHAN SCHOOL, 2021-06-16 09:27:25

Description: Learn Java in One Day and Learn It Well ( PDFDrive )

Search

Read the Text Version

Calculating Pay… Pay = 4800 As -10 is not a valid value for hoursWorked, the setter method does not update the field for Jane (staff2). Hence, when we use the getter method to get the value of hoursWorked, we realise that it remains as 160. The example above shows how we can use setter methods to control what values our fields can take. We should always do these data validations inside the class that contains the private field itself. If we do not do that, we will have to rely on whoever is using the Staff class to do the validation which is risky. 7.4 Static We’ve covered some pretty complicated concepts in this chapter. You now know what a class is, including what fields, methods and constructors are. You also learned how to declare and use a class. If you are new to object oriented programming, I strongly suggest that you download the complete program for this chapter from http://www.learncodingfast.com/java and play around with it. Study the code and make sure you fully understand the topics covered in this chapter so far before moving on. In this section, we’ll look at another keyword that is used in object oriented programming – the keyword static. Previously, we looked at how we can use the Staff class to create our staff1 and staff2 objects. We then used these objects to call the methods inside the Staff class (e.g. staff1.calculatePay(1000, 400)). Now suppose we want to call some methods or access some fields in the Staff class without creating a Staff object. Would that be possible? The answer is yes. We have to use the static keyword.

To understand the static keyword, consider the code below. Launch NetBeans and create a new Java application called StaticDemo. Replace the code with the code below: package staticdemo; class MyClass { //Non static field and method public String message = \"Hello World\"; public void displayMessage() { System.out.println(\"Message = \" + message); } //Static field and method public static String greetings = \"Good morning\"; public static void displayGreetings() { System.out.println(\"Greeting = \" + greetings); } } public class StaticDemo { public static void main(String[] args) { MyClass sd = new MyClass(); System.out.println(sd.message); sd.displayMessage(); System.out.println(MyClass.greetings); MyClass.displayGreetings(); } } In the example above, we created two classes called MyClass and StaticDemo. In this example, we created them within one file. If you prefer, you can separate the two classes into separate files. It is considered good practice to

separate classes into separate files whenever possible. However for simplicity, we’ll use a single file in this example. In addition, we declared the fields as public to shorten the code. In practice, you are strongly encouraged to make your fields private and use getter and setter methods instead. In our example, MyClass contains one non static field message and one non static method displayMessage(). It also contains one static field greetings and one static method displayGreetings(). To access the non static members of MyClass from another class, we need to instantiate an object as before. We did that inside the main() method of the StaticDemo class. MyClass sd = new MyClass(); System.out.println(sd.message); sd.displayMessage(); However, to access the static members, we do not need to create any object. We simply use the class name to access them as shown below. System.out.println(MyClass.greetings); MyClass.displayGreetings(); If you run the code above, you will get the following output Hello World Message = Hello World Good morning Greeting = Good morning This is the main difference between a static field/method vs a non static field/method. For the former, you do not have to create an object to access it; you use the name of the class itself. For the latter, an object is necessary.

Some of the pre-written methods in Java are declared as static. An example is the methods in the Arrays class. To access the methods in this class, we use the name of the class. For instance, to use the sort() method, we write Arrays.sort(). 7.5 Advanced Methods Concepts 7.5.1 Using Arrays in Method Before we end this chapter, I would like to discuss two more concepts about methods. The first is regarding using arrays in methods. Previously, we learned how to use primitive data types like int as parameters to a method. In addition to using primitive data types, we can also use arrays. To use an array as a parameter, we add a square bracket [] after the parameter’s data type in the method declaration. An example is shown below. public void printFirstElement(int[] a) { } To call this method, we need to declare an array and pass it in as an argument to the method. We’ll see an example of this later. In addition to using arrays as parameters, we can also return an array from a method. To return an array from a method, we add a square bracket [] after the return type in the method declaration. An example is public int[] returnArray() { int[] a = new int[3]; //Some code for updating values in the array

return a; } To use this method, we need to declare an array and assign the method’s result to it. To fully understand how we can use arrays in methods, let’s create a new Java application called ArrayMethodDemo and replace the code with the following: 1 package arraymethoddemo; 2 3 import java.util.Arrays; 4 5 class MyClass{ 6 7 public void printFirstElement(int[] a) 8{ 9 System.out.println(\"The first element is \" + a[0]); 10 } 11 12 public int[] returnArray() 13 { 14 int[] a = new int[3]; 15 for (int i = 0; i < a.length; i++) 16 { 17 a[i] = i*2; 18 } 19 return a; 20 } 21 22 } 23 24 public class ArrayMethodDemo { 25 public static void main(String[] args) { 26 27 MyClass amd = new MyClass(); 28 29 int[] myArray = {1, 2, 3, 4, 5}; 30 amd.printFirstElement(myArray); 31 32 int[] myArray2 = amd.returnArray(); 33 System.out.println(Arrays.toString(myArray2));

34 35 } 36 } In this example, we included two classes – MyClass and ArrayMethodDemo – in the same file again for simplicity. Inside the class MyClass (lines 5 to 22), we have two methods. The first method printFirstElement() shows how you can use an array as a parameter. The second method returnArray() shows how you can return an array from a method. To use these two methods, we initialized a MyClass object called amd in the main() method (line 27). We then declared an array and passed it in as an argument to the printFirstElement() method on lines 29 and 30. int[] myArray = {1, 2, 3, 4, 5}; amd.printFirstElement(myArray); In addition, we also declared a second array in the main() method and assigned the result of the returnArray() method to it on line 32. int[] myArray2 = amd.returnArray(); Finally, on line 33, we printed the contents of the array. If you run the program above, you’ll get the following output: The first element is 1 [0, 2, 4] 7.5.2 Passing Primitive Type vs Reference Type Parameters

Now that you know how to pass an array to a method, let us look at the difference between a primitive type parameter vs a reference type parameter (such as an array). There is an important difference. When you pass in a primitive type variable, any change made to the value of that variable is only valid within the method itself. Once the program exits the method, the change is no longer valid. On the other hand, when you pass in a reference type variable, any change made to the value of that variable is valid even after the method ends. To understand how this works, add the following two methods to MyClass in ArrayMethodDemo.java. public void passPrimitive(int primitivePara) { primitivePara = 10; System.out.println(\"Value inside method = \" + primitivePara); } public void passReference(int[] refPara) { refPara[1] = 5; System.out.println(\"Value inside method = \" + refPara[1]); } The first method has a primitive type parameter (int primitivePara) and tries to change the value of that parameter. It then prints the value of the parameter. The second method has a reference type parameter (an array) and tries to change the value of the second element in the array. It then prints the value of that element. In our main() program, add the following lines of code: int number = 2; System.out.println(\"number before = \" + number);

amd.passPrimitive(number); System.out.println(\"number after = \" + number); System.out.print(\"\\n\"); System.out.println(\"myArray[1] before = \" + myArray[1]); amd.passReference(myArray); System.out.println(\"myArray[1] after = \" + myArray[1]); If you run the program, you will get the following additional output number before = 2 Value inside method = 10 number after = 2 myArray[1] before = 2 Value inside method = 5 myArray[1] after = 5 As you can see, the value of number stays the same before and after the method call. On the other hand, the value of myArray[1] changes after the method call. The reason for this is because when you pass in a reference type variable, you are passing in the address of the variable. The compiler can go to the address that you passed in and make the relevant changes to the variable. On the other hand, when you pass in a primitive type variable, you are passing in the value of the variable and not the address. For instance, if the value of number is 2, writing amd.passPrimitive(number); is the same as writing amd.passPrimitive(2);

This value 2 is assigned to the parameter primitivePara. Since we are not passing in the address of number, any change that occurs inside the method does not affect number. This is an important difference that you have to understand when you pass in a primitive type variable (e.g. int, float etc) vs a reference type variable (such as an array) to a method.

Chapter 8: Object-Oriented Programming Part 2 Now, let us move on to some of the more advanced topics in object-oriented programming. In this chapter, we’ll learn about inheritance, polymorphism, abstract classes and interfaces. 8.1 Inheritance Inheritance is one of the key concepts in object-oriented programming. Simply stated, inheritance allows us to create a new class from an existing class so that we can effectively reuse existing code. In fact, all classes in Java are inherited from a pre-written base class known as the Object class. The Object class consists of a number of pre-written methods that we can use whenever we work with classes. One such method is the toString() method that returns a string that represents the object. We used the toString() method previously when we discussed arrays. We’ll be looking at the toString() method again when we work on our project. So what exactly is inheritance? 8.1.1 Writing the Parent Class Let us work through a small program together to illustrate the concept of inheritance. Suppose we are writing a program for a fitness club that has two types of membership – VIP and Normal. To do that, let’s launch NetBeans and create a new Java Project called InheritanceDemo. Add a new class called Member to the inheritancedemo package. Refer to Chapter 7.2 for instructions on how to add a new class if necessary.

We’ll be getting user input in the Member class. Hence, we need to add the statement import java.util.Scanner; to our class. Add this statement after the line package inheritancedemo; Now, we are ready to start working on the class. Add the following lines to the Member class (inside the curly braces). public String welcome = \"Welcome to ABC Fitness\"; protected double annualFee; private String name; private int memberID; private int memberSince; private int discount; Here, we declare six fields for the Member class. The first is a public field that stores a welcome message. We initialise this field with the string “Welcome to ABC Fitness”. The other five fields include one protected field and four private fields. We’ll talk more about protected fields in a later section. For now, just know that a protected field is accessible within the class in which it is declared, any class that is derived from it and any class that is in the same package. Derived classes will be covered in the next section. Next, let’s add two constructors to our class. public Member() { System.out.println(\"Parent Constructor with no parameter\"); }

public Member(String pName, int pMemberID, int pMemberSince) { System.out.println(\"Parent Constructor with 3 parameters\"); name = pName; memberID = pMemberID; memberSince = pMemberSince; } The first constructor merely prints the line “Parent Constructor with no parameter”. The second constructor is more interesting. It prints the line “Parent Constructor with 3 parameters” and assigns its parameters to three of the private fields in the Member class. After writing the constructors, we’ll move on to the getter and setter methods. We’ll add a getter and setter method for the private field discount so that other classes can access this field. public double getDiscount(){ return discount; } public void setDiscount(){ Scanner input = new Scanner(System.in); String password; System.out.print(\"Please enter the admin password: \"); password = input.nextLine(); if (!password.equals(\"abcd\")) { System.out.println(\"Invalid password. You do not have authority to edit the discount.\"); }else { System.out.print(\"Please enter the discount: \"); discount = input.nextInt();

} } The getter method simply returns the value of the discount field. The setter method is more complex. It prompts the user to enter the admin password before he/she can edit the discount field. This method demonstrates how a setter method can be used to prevent unauthorised access to a private field. However, suffice to say, this code will be way too easy for a hacker to crack. In real life, you’ll need stronger security measures than just a simple password to protect your data. Now, let us add two more methods to our Member class. The first method is a public method called displayMemInfo(). It uses a series of println() statements to display information about a member. public void displayMemInfo(){ System.out.println(\"Member Name: \" + name); System.out.println(\"Member ID: \" + memberID); System.out.println(\"Member Since \" + memberSince); System.out.println(\"Annual Fee: \" + annualFee); } The second method is a public method called calculateAnnualFee(). It will be used to calculate the annual fee of a member. public void calculateAnnualFee() { annualFee = 0; } Notice that we set the annual fee of a member to zero? Don’t worry about this at the moment; we’ll update this method later. Once we are done with the above, the Member class is complete.

8.1.2 Writing the Child Class Now that we have completed the Member class, let us learn how to derive a class from it. Derived classes are known as child classes or subclasses, while the classes from which they are derived are known as parent classes, base classes or superclasses. To recap, our parent class (Member) has the following contents: Fields public String welcome = \"Welcome to ABC Fitness\"; protected double annualFee; private String name; private int memberID; private int memberSince; private int discount; Constructors public Member() public Member(String pName, int pMemberID, int pMemberSince) Methods public double getDiscount() public void setDiscount() public void displayMemInfo() public void calculateAnnualFee() We shall derive two classes – NormalMember and VIPMember – from the Member class. First, let’s declare the child class NormalMember. Add a new Java class to the inheritancedemo package and name it NormalMember. Notice that when you generate a new class, NetBeans

automatically declares the class as public class NormalMember { } for you? We need to indicate that NormalMember is derived from the Member class by adding the words extends Member to the class declaration as shown below: public class NormalMember extends Member{ } extends is a Java keyword used to indicate that one class is inherited from another class. In our example, the NormalMember class is inherited from the Member class. We have to use the extends keyword whenever we want to show that one class is inherited from another class. The only exception is when inheriting from the Object class. As all classes in Java are inherited from the Object class, there is no need for us to state this inheritance explicitly. When one class is inherited from another class, it inherits all the public and protected fields and methods from the parent class. This means that the child class can use these fields and methods as if they are part of its own code; we do not have to declare these fields and methods again in the child class. In other words, even though we have not started coding our child class yet, it already has two fields (welcome and annualFee) and four methods (getDiscount, setDiscount, displayMemInfo and calculateAnnualFee) inherited from the parent class. This facilitates code reuse and is especially valuable if the parent class has a large number of public/protected fields and methods that the child class can use. However, the child class does not inherit the private fields and methods of

the parent class. This means that the child class will not be able to access these private fields and methods directly, it’ll have to access them using other methods. We’ll see an example of this later. Now that we have declared NormalMember as a child class of Member, we need to write the constructor for the child class. Whenever we write the child class constructor, it is a must for us to call the parent class’ constructor first. If we do not do that, Java will automatically call the parameterless constructor in the parent class for us. For instance, add the following constructor to the NormalMember class. public NormalMember() { System.out.println(\"Child constructor with no parameter\"); } When we declare our constructor as above, Java looks for a parameterless constructor (i.e. a constructor with no parameter) in the parent class and calls that first before executing the code in the child constructor. If you use this constructor to create a child object, the following two lines will be displayed on the screen Parent Constructor with no parameter Child constructor with no parameter The first line is from the parent constructor while the second line is from the child constructor. If you want to call a non parameterless constructor in the parent class, you have to use the super keyword. An example is shown below: public NormalMember(String pName, int pMemberID, int pMemberSince) { super(pName, pMemberID, pMemberSince); System.out.println(\"Child Constructor with 3 parameters\"); }

When we use the super keyword to call a non parameterless constructor in the parent class, the statement super(pName, pMemberID, pMemberSince); must be the first statement in the child constructor. If we fail to do that, Java will give us an error. In the example above, we used the super keyword to call the second constructor in the parent class (i.e. the constructor with 3 parameters). We passed in the values of pName, pMember and pMemberSince to the parent constructor. When we create a child object with this constructor, we write something like NormalMember myChildMember = new NormalMember(\"James\", 1, 2010); You will get the following output when you run the code Parent Constructor with 3 parameters Child Constructor with 3 parameters Behind the scene, the values \"James\", 1 and 2010 are assigned to the fields name, memberID and memberSince respectively. At this point, some of you may be wondering where the fields name, memberID and memberSince come from? Since these are private fields in the Member class and we mentioned that private fields are not inherited, why would the child class have these fields? The reason for this is that when we say private fields are not inherited, it simply means the child class cannot access these fields directly. However, these fields do exist in the child class. The only difference is that the child class cannot access them directly but has to use constructors or setter and getter methods instead. We’ll see an example of how we can use getter and

setter methods to access these private fields later. Overriding a Method Now that we have created the constructors for our child class, let us move on to create a method to calculate the annual fee of a normal member. Recall that we have already coded a calculateAnnualFee() method in the parent class earlier? As this parent class method is public, it is inherited by the child class. Hence, the child class can use this method as if it is part of its own code. However, recall that the calculateAnnualFee() method in the parent class sets the annual fee of a member to zero? What if we want to use a different formula for calculating the annual fee in the child class? In this case, we have to override the inherited method. Overriding a method simply means writing a new version of the method in the child class. To override the calculateAnnualFee() method, add the following code to the NormalMember class. @Override public void calculateAnnualFee() { annualFee = (1-0.01*discount)*(100 + 12*30); } Notice that there is a red squiggly line under the word discount when you save your code? This indicates an error. If you hover your mouse over the line, you’ll get the message “discount has private access in Member”. This is because discount is a private field in Member and is thus not accessible directly by NormalMember. In order for NormalMember to access the discount field, we have to use the getter method declared in Member. Change the code above to

annualFee = (1-0.01*getDiscount())*(100 + 12*30); Once you replace discount with getDiscount(), the error goes away. Annotations Next, notice the @Override line above the method declaration? This is known as an annotation. In Java, annotations are metadata that we add to our code to provide extra information to the compiler. When we use the @Override annotation, we are informing the compiler that the method that follows is meant to override the calculateAnnualFee() method declared in the base class (i.e. the Member class). When overriding a method, there are some rules that we must follow. For instance, the method in the child class must have the same parameter list as the method in the parent class. If the method in the child class fails to override the method in the parent class correctly, the compiler will generate an error. If that happens, you can hover your mouse over the error for more information. Using the @Override annotation is optional when overriding a method. However, you are strongly encouraged to do so to prevent compilation errors. Annotations follow a fixed syntax and are case sensitive. @override, @overriding or @ThisMethodOverrideAnother are all invalid annotations. Java comes with a number of predefined annotations. You can find the full list of predefined annotations here https://docs.oracle.com/javase/tutorial/java/annotations/predefined.html. That’s all for our child class NormalMember. The class has the following contents:

Fields Inherited from parent class: public String welcome = \"Welcome to ABC Fitness\"; protected double annualFee Constructors public NormalMember() public NormalMember(String pName, int pMemberID, int pMemberSince) Methods Inherited from parent class: public void setDiscount() public double getDiscount() public void displayMemInfo() Overriden in child class: public void calculateAnnualFee() Next, we shall add another class – VIPMember – to our inheritance package. This class also inherits from Member. To do that, add a new class to the inheritancedemo package and name it VIPMember. Replace the code with the code below: package inheritancedemo; public class VIPMember extends Member { public VIPMember(String pName, int pMemberID, int pMemberSince) { super(pName, pMemberID, pMemberSince); System.out.println(\"Child Constructor with 3 parameters\"); } @Override

public void calculateAnnualFee() { annualFee = (1-0.01*getDiscount())*1200; } } This class has one constructor (with 3 parameters) and one method calculateAnnualFee(). The calculateAnnualFee() method here uses a different formula for calculating annual fee from the calculateAnnualFee() method in the NormalMember class. It is alright for the two methods to share the same name (and signature) as they are in different classes. VIPMember class has the following contents: Fields Inherited from parent class: public String welcome = \"Welcome to ABC Fitness\"; protected double annualFee Constructors public VIPMember(String pName, int pMemberID, int pMemberSince) Methods Inherited from parent class: public void setDiscount() public double getDiscount() public void displayMemInfo() Overridden in child class: public void calculateAnnualFee() 8.1.3 The main() method Now that we have written the three classes that we need, let’s write the code for the main() method. Switch over to the InheritanceDemo.java file and add

the following two lines to the main() method. NormalMember mem1 = new NormalMember(\"James\", 1, 2010); VIPMember mem2 = new VIPMember(\"Andy\", 2, 2011); Here, we are creating two objects for the two derived classes. mem1 is created using the 3 parameters constructor from the NormalMember class. mem2 is created using the 3 parameters constructor from the VIPMember class. Now we’ll use the calculateAnnualFee() methods in the respective classes to calculate the annual fee for each member. mem1.calculateAnnualFee(); mem2.calculateAnnualFee(); As mem1 is an instance of the NormalMember class, the calculateAnnualFee() method from that class is executed. The annual fee for mem1 is thus 100 + 12*30 = 460. For mem2, the annual fee is 1200 as it uses the method from the VIPMember class. Finally, let’s use the displayMemberInfo() method from the parent class (Member) to display the information on our screen. We write mem1.displayMemInfo(); mem2.displayMemInfo(); Since the displayMemberInfo() method belongs to the parent class and is public, both mem1 and mem2 have inherited the method and are thus able to use it in the main() method. You’ll get the following output when you run the program: Parent Constructor with 3 parameters Child Constructor with 3 parameters Parent Constructor with 3 parameters

Child Constructor with 3 parameters Member Name: James Member ID: 1 Member Since 2010 Annual Fee: 460.0 Member Name: Andy Member ID: 2 Member Since 2011 Annual Fee: 1200.0 Now, we shall try to apply some discount to the annual fees for mem1. As discount is a private field in the parent class, we cannot alter its value by writing something like mem1.discount = 100; Instead, we have to use the setDiscount() method. To do that, add the following line to the main() method. mem1.setDiscount(); Next, we’ll add the following statements to recalculate the annual fee after the discount and display the information. mem1.calculateAnnualFee(); mem1.displayMemInfo(); Run the program again. When prompted, enter “abcd” for password and 30 for discount. You’ll get the following lines added to the original output. Please enter the admin password: abcd Please enter the discount: 30 Member Name: James Member ID: 1 Member Since 2010 Annual Fee: 322.0 The annual fee is now 322 because a discount of 30% is applied.

8.2 Polymorphism Now that we have seen an example of how inheritance works, let us move on to discuss another topic that is closely related to inheritance - the concept of polymorphism. Polymorphism refers to a program’s ability to use the correct method for an object based on its run-time type. The best way to explain polymorphism is through an example. Let’s expand on our fitness club example above. First, delete or comment out all the code in the previous main() method and add the following lines to it. Member[] clubMembers = new Member[6]; clubMembers[0] = new NormalMember(\"James\", 1, 2010); clubMembers[1] = new NormalMember(\"Andy\", 2, 2011); clubMembers[2] = new NormalMember(\"Bill\", 3, 2011); clubMembers[3] = new VIPMember(\"Carol\", 4, 2012); clubMembers[4] = new VIPMember(\"Evelyn\", 5, 2012); clubMembers[5] = new Member(\"Yvonne\", 6, 2013); Here, we declare an array of Member objects and add 6 members to it. The first three members are objects of the NormalMember class, the next two are objects of the VIPMember class while the last is an object of the Member class. Although clubMembers is declared to be an array of Member type, we can assign objects of NormalMember and VIPMember to it as they are child classes of the Member class. We do not need to declare separate arrays for NormalMember and VIPMember objects. Next, we’ll use an enhanced for statement to calculate the annual fee of each member and display the information. To do that, we write for (Member m : clubMembers) {

m.calculateAnnualFee(); m.displayMemInfo(); } Now save and run the program. You’ll notice that the annual fee for the first three members (NormalMember) is $460, the annual fee for the next two (VIPMember) is $1200 and the annual fee for the last member is $0. This is the result of polymorphism. At run time (i.e. when the program runs), the program determines that the first three members of clubMembers are of NormalMember type and executes the calculateAnnualFee() method from that class. It also determines that the next two members are of VIPMember type and executes the method from that class. Finally, it determines that the last member is of Member type and executes the method from the parent class. We say that the runtime type of the first three elements of clubMembers is NormalMember, the runtime type of the next two is VIPMember and the runtime type of the last element is Member. In contrast, the declared type of all the 5 elements is Member. Polymorphism simply means that at run time, although all objects are declared to be of Member type, the program is smart enough to use the correct calculateAnnualFee() method based on the runtime type of the element. 8.3 Abstract Classes and Methods Next, let us move on to discuss two special types of “parent class” in Java - abstract classes and interfaces. We’ll first look at abstract classes. An abstract class is a special type of class that is created strictly to be a base class for other classes to derive from. They cannot be instantiated. In other words, if FourWheelVehicles is an abstract class, the statement FourWheelVehicle myVeh = new FourWheelVehicle();

will give you an error as you cannot create an object of an abstract class. Abstract classes may have fields and methods just like any other classes. In addition, they can have a special type of method known as abstract methods. Abstract methods are methods that have no body and MUST be implemented in the derived class. They can only exist in abstract classes. In the fitness club example above, we declared a method called calculateAnnualFee() in the parent class (Member) and set the annual fee to zero. This method does not make any logical sense as the annual fee is zero. Instead of adding such senseless method implementations in our parent class, a better way would be to declare the method as an abstract method. Let’s see how we can do that in our fitness club example. We’re going to have to make some changes to our program. Changes to the parent class First, we need to replace the lines public void calculateAnnualFee() { annualFee = 0; } in the Member class with abstract public void calculateAnnualFee(); We add the abstract keyword in the method declaration above to indicate that this is an abstract method. In addition, we do not add braces { } after the method declaration as abstract methods have no body. Instead, we end the declaration with a semi-colon (;). Next, we need to declare our Member class as an abstract class. This is

because abstract methods can only exist in abstract classes. To do that, we add the abstract keyword to the Member class declaration like this: abstract public class Member That’s all the changes that we need to make to the Member class. Changes to the InheritanceDemo class Next, we need to make one change to the InheritanceDemo class. As abstract classes cannot be instantiated, the line clubMembers[5] = new Member(\"Yvonne\", 6, 2013); in the InheritanceDemo.java file will give us an error. To remedy that, we can change the last item in our clubMember array to either a NormalMember or a VIPMember object. This works well with the logic of our program as a member should either be a NormalMember or a VIPMember, not just a Member. In our example, we’ll change the item to a VIPMember object. Change the line to clubMembers[5] = new VIPMember(\"Yvonne\", 6, 2013); That’s all there is for the InheritanceDemo class. Changes to the sub classes Finally, let’s move on to the subclasses. As abstract methods have to be implemented by subclasses, we need to ensure that we have implemented the

calculateAnnualFee() method in both our subclasses. In our previous example, we have already implemented this method in both of the subclasses. Hence, we do not need to make any changes to the subclasses. We are now ready to save and run the program. Everything should run normally as before except for the output for last member (Yvonne). The annual fee should now be $1200 as shown below. Member Name: Yvonne Member ID: 6 Member Since 2013 Annual Fee: 1200.0 This is the gist of abstract classes. In summary, an abstract class is a special type of base class that cannot be instantiated. It can contain abstract methods that have no implementation details and MUST be implemented in the child class. 8.4 Interfaces Next, let’s look at interfaces. Interfaces are much like abstract classes in that they cannot be instantiated. Instead, they must be implemented by classes or extended by other interfaces. When a class implements an interface, it has to implement all the abstract methods in that interface. Up until Java 7, interfaces can only contain abstract methods (i.e. methods with no bodies) and constants (i.e. fields declared as final). All methods in an interface are implicitly public while all constant values are implicitly public, static, and final. You do not have to specify these modifiers. One of the key differences between an abstract class and an interface is that a class can only extend one abstract class but can implement multiple interfaces. However, we won’t be showing an example of multiple interfaces implementation in this book as that is an advanced topic beyond the scope of

the book. Another difference between an abstract class and an interface is that an interface can only contain abstract methods (up until Java 7) while an abstract class can contain non-abstract methods. Let us first look at an example of how interface works up until Java 7. Launch NetBeans and create a new Java Project called InterfaceDemo. Replace the code in InterfaceDemo.java with the following: 1 package interfacedemo; 2 3 public class InterfaceDemo { 4 5 public static void main(String[] args) { 6 MyClass a = new MyClass(); 7 a.someMethod(); 8 9 System.out.println(\"The value of the constant is \" + MyInterface.myInt); 10 } 11 } 12 13 class MyClass implements MyInterface 14 { 15 @Override 16 public void someMethod() 17 { 18 System.out.println(\"This is a method implemented in MyClass\"); 19 } 20 } 21 22 interface MyInterface{ 23 24 int myInt = 5; 25 void someMethod(); 26 27 } In the example above, the interface is declared on lines 22 to 27. On line 24,

we declared a field (which is implicitly public, static and final) and on line 25, we declared a method. There is no need to use the abstract keyword when declaring a method in an interface. It is abstract by default. On lines 13 to 20, we have a class called MyClass that implements the interface. We use the implement keyword to indicate this relationship. Inside the class, we implement the method someMethod() from line 15 to 19. From line 3 to 11, we have the InterfaceDemo class which contains the main() method. Inside the main() method, we instantiated a MyClass object and use that to invoke someMethod() on line 7. On line 9, we printed out the value of the constant myInt. Note that because constants in interfaces are static, we access them using the name of the interface (MyInterface.myInt) and not the name of the MyClass object. If you run this program, you’ll get This is a method implemented in MyClass The value of the constant is 5 The example above shows a simple example of how interfaces work up until Java 7. However, things changed a bit in Java 8. Prior to Java 8, an interface can only contain abstract methods. In Java 8, Java added support for default and static methods in interfaces. To see how default and static methods work, add the following two methods to MyInterface. Note that both methods are implemented in the interface itself. public static void someStaticMethod() { System.out.println(\"This is a static method in an interface\"); } public default void someDefaultMethod() {

System.out.println(\"This is a default method in an interface\"); } Next, add the following lines to the main() method. a.someDefaultMethod(); MyInterface.someStaticMethod(); Now, save and run the program. You’ll get the following output: This is a method implemented in MyClass The value of the constant is 5 This is a default method in an interface This is a static method in an interface This is how interfaces work from Java 8 onwards. We are now allowed to add method implementations in our interfaces. However, only default and static methods can be implemented in an interface. Java added default and static methods in interfaces mainly to ensure binary compatibility. Simply stated, binary compatibility means that when you change your interface, you do not need to make any changes to the classes that implement it. For instance, suppose we want to add a new method to our interface MyInterface. If we simply add the method declaration to the interface as shown below, we will get an error. interface MyInterface{ int myInt = 5; void someMethod(); void someNewMethod(); } This is because the class that implements this interface (MyClass) did not implement the new method. A class that implements an interface must implement all the abstract methods in the interface. Hence, if you add an abstract method to your interface, you need to ensure that all classes that

implement your interface implement the new method. This is often impossible to do as you may have no idea which classes implemented your interface. To overcome this problem, Java added support for default and static methods for interfaces. This allows us to add methods to our interfaces without having to make any changes to the classes that implemented it. 8.5 Access Modifiers Revisited Now that we have covered various topics related to inheritance, let us take a second look at the concept of access modifiers in object oriented programming. Earlier, we learnt that an access modifier is like a gate-keeper. It controls who has access to a certain field or method. Java comes with 3 access modifiers: private, public and protected. If an access modifier is not stated, the access level is taken to be package-private. To understand how private, public, protected and package-private work, let’s consider the example below. We’ll be using fields to demonstrate the concept. The same applies to methods. Create a new NetBeans project and name it ModifierDemo. Replace the code in ModiferDemo.java with the following package modifierdemo; public class ModifierDemo { public int publicNum = 2; protected int protectedNum = 3; int packagePrivateNum = 4; private int privateNum = 1; } Now create another class in the modifierdemo package and name it ClassesInSamePackage. Replace the code in ClassesInSamePackage.java

with the following package modifierdemo; public class ClassesInSamePackage { //just an empty class } class ClassA extends ModifierDemo { public void printMessages() { //This is ok System.out.println(publicNum); //This is ok System.out.println(protectedNum); //This is ok System.out.println(packagePrivateNum); //This is NOT ok System.out.println(privateNum); } } class ClassB { public void printMessages() { ModifierDemo p = new ModifierDemo(); //This is ok System.out.println(p.publicNum); //This is ok System.out.println(p.protectedNum); //This is ok System.out.println(p.packagePrivateNum);

//This is NOT ok System.out.println(p.privateNum); } } In the code above, we added two classes to the ClassesInSamePackage.java file. ClassA extends the ModifierDemo class while ClassB does not. In ClassA, the first two println() statements will not give us any error as a derived class can access any public and protected fields in the parent class. In addition, the third println() statement will also not give us an error as both files (ModifierDemo.java and ClassesInSamePackage.java) are inside the same package. By default, a field declared without any access modifier is package-private. A package-private field is accessible to all files in the same package. However, the fourth statement gives us an error as privateNum is a private field and is thus only accessible in ModifierDemo itself. In ClassB, as the class is not derived from ModifierDemo, we need to instantiate a ModifierDemo object p in order to access the fields of ModifierDemo. As before, the first and third println() statements do not give us any error. This is because a public field can be accessed by any class while a package- private field can be accessed by any class in the same package. The second println() statements also does not give us any errors. Even though ClassB is not derived from ModifierDemo, it can access protectedNum as protected fields are not only accessible to all subclasses of the class in which they are declared, they are also accessible to all classes in the same package (similar to package-private). The fourth println() statement gives us an error as a private field is only accessible in the class that it is declared.

Now, let’s look at what happens when two classes are not in the same package. Create a new package for the project. To do that, right click on the project name in the Project Explorer and select New > Java Package… (refer to image below). Name this package anotherpackage. When you create a new package for the ModifierDemo project, you are essentially creating a sub-folder under the main project folder. If you browse to the main project folder now, you’ll see two sub-folders created – one for the default package modifierdemo (which was created by NetBeans when you created the project) and the other for the newly created package, anotherpackage. If you can’t find the main folder, you can right- click on the project name in the Project Explorer in NetBeans and select Properties. This will bring up a dialogue box that shows where your project is stored. Now, add a new class to this package. To do that, right-click on the anotherpackage package in the Project explorer and select New > Java Class… You may need to click on the + sign in the Project Explorer if you do not see this package. It is very important that you click on the correct package when creating a new class in NetBeans.

Name this class ClassesInAnotherPackage. If you have done it correctly, you should see the following structure in your Project explorer. Replace the code in ClassesInAnotherPackage.java with the following: package anotherpackage; import modifierdemo.ModifierDemo; public class ClassesInAnotherPackage { //just an empty class } class MyClassC extends ModifierDemo{ public void printMessages() { //This is ok System.out.println(publicNum); //This is ok System.out.println(protectedNum); //This is NOT ok

System.out.println(packagePrivateNum); //This is NOT ok System.out.println(privateNum); } } class MyClassD { public void printMessages() { ModifierDemo p = new ModifierDemo(); //This is ok System.out.println(p.publicNum); //This is NOT ok System.out.println(p.protectedNum); //This is NOT ok System.out.println(p.packagePrivateNum); //This is NOT ok System.out.println(p.privateNum); } } In the example above, ClassC can access publicNum and protectedNum but not packagePrivateNum and privateNum. It can access protectedNum because it is a child class of ModifierDemo even though it is not in the same package as ModifierDemo. On the other hand, it cannot access packagePrivateNum because this field is only accessible by classes in the same package as ModifierDemo. Next, for ClassD, it can only access publicNum. It cannot access protectedNum as it is neither a subclass of ModifierDemo nor in the same package. Similarly, it cannot access packagePrivateNum and privateNum. In short, anything that is declared as public is accessible everywhere; there

are no restrictions on accessing public fields. On the other hand, anything declared as private is only accessible within the class in which it is declared. Anything declared as protected is accessible within the class in which it is declared, any class that is derived from it and any class that is in the same package as the class that it is declared in. Finally, anything declared without an access modifier is package-private and is only accessible within the package in which it is declared.

Chapter 9: Collections Congratulations on making it thus far. We’ve come a long way. In the last two chapters, you learned how to write your own classes and instantiate objects. In this chapter, we are going to look at a framework that Java provides to enable us to easily store and manipulate these objects. Specifically, we’ll be looking at the Java Collections Framework. 9.1 The Java Collections Framework The Java Collections Framework is a set of pre-written classes and interfaces that Java provides to help us organise and manipulate groups of objects. Using the Collections Framework, we can choose to organise our objects in different formats such as lists, sets, queues or maps. We’ll be looking at how to use lists in this chapter. The Java Collections Framework standardizes the way in which groups of objects are handled. Hence, once you know how to use lists, you’ll find it easier to learn other collections such as sets or queues. 9.2 Autoboxing and Unboxing Before we can discuss the Java Collections Framework, we need to first discuss the concept of autoboxing and unboxing. Previously in Chapter 3, we learned about the 8 primitive types in Java. These 8 primitive types are basic data types and not objects. However, Java is an object-oriented language and much of language revolves around the idea of treating everything as an object. Hence, oftentimes, we

find it necessary to convert a primitive type into an object. To facilitate this conversion, Java provides us with what is known as wrapper classes. Each primitive type in Java has a corresponding wrapper class. These wrapper classes contain a number of useful methods that we can use. The wrapper classes for boolean, char, byte, short, int, long, float and double are Boolean, Character, Byte, Short, Integer, Long, Float and Double respectively. Converting from a primitive data type into a wrapper class object is easy. For instance, to convert int into an Integer object, we do the following: Integer intObject = new Integer(100); Here, we declare and instantiate an Integer object by passing an int value of 100 to the Integer class constructor. This constructor accepts the value and creates an Integer object based on that value. If we want to convert the Integer object back to an int, we use the intValue() method. The code is as follows: int m = intObject.intValue(); The intValue() method returns an int type which we assign to m. As you can see, converting from a primitive data type to an object and vice versa is relatively straightforward. However in practice, it is actually simpler than what is shown above. Java provides us with two mechanisms known as autoboxing and unboxing. This allows for automatic conversion.

To convert from int to Integer, instead of writing Integer intObject = new Integer(100); we can simply write Integer intObject = 100; Here, we simply assign the value 100 to intObject. We do not need to pass this int value to the Integer constructor; Java does it for us behind the scene. This process is known as autoboxing. To convert from Integer to int, instead of writing int m = intObject.intValue(); we can simply write int m = intObject; We do not need to explicitly use the intValue() method. When we assign an Integer object to an int variable, Java automatically converts the Integer object to int type. This process is known as unboxing. As we can see, wrapper classes provide us with a convenient way to convert primitive types into objects and vice verse. Besides this purpose, wrapper classes also have another major use – they provide us with methods for converting strings into primitive types. Suppose you want to convert a string into an int, you can use the parseInt() method in the Integer class as shown below: int n = Integer.parseInt(\"5\"); Note that \"5\" is a string as we use double quotes. The Integer.parseInt(\"5\") method returns the int value 5, which we then assign to n.

If the string cannot be converted into an int, the method will throw a NumberFormatException. For instance, the statement below will give us an error: int p = Integer.parseInt(\"ABC\"); In addition to converting a string into an int, we can also convert a string into a double using the parseDouble() method found in the Double class: double q = Double.parseDouble(\"5.1\"); Similarly, we’ll get an error if the string cannot be converted into a double. 9.3 Lists Now that we are familiar with wrapper classes, let us look at lists. A list is very similar to an array, but is more flexible. Specifically, its size can be changed. Previously, in Chapter 4, we learned that the size of an array cannot be changed once we initialize the array or if we state the number of elements when declaring it. For instance, if you declare the array as int[] myArray = new int[10]; myArray can only hold 10 values. If you write myArray[10] (which refers to the 11th value since array index starts from zero), you will get an error. If you need greater flexibility in your program, you can use a list. Java comes with a pre-written List interface that is part of the Java Collections Framework. This interface is implemented by a few classes. The most commonly used classes that implement the List interface are the ArrayList and LinkedList classes. We’ll look at ArrayList first.

9.4 ArrayList The ArrayList class is a pre-written class that implements the List interface. Like all other collections in the Java Collections Framerwork, an ArrayList can only be used to store objects (not primitive data types). Hence, if we want to declare a list of integers, we have to use Integer instead of int. Whenever you use an ArrayList, you have to import the java.util.ArrayList class using the statement below import java.util.ArrayList; The syntax for declaring and instantiating an ArrayList is as follows: ArrayList<Type> nameOfArrayList = new ArrayList<>(); For instance, to declare and instantiate an ArrayList of Integer objects, we write ArrayList<Integer> userAgeList = new ArrayList<>(); On the left side of the statement, we declared an ArrayList variable. ArrayList is a keyword to indicate that you are declaring an ArrayList <Integer> indicates that this ArrayList is used to store Integer objects userAgeList is the name of the ArrayList. On the right side, we instantiate a new ArrayList object using the new keyword and assign it to userAgeList. If you want the ArrayList to store String objects instead, you declare and instantiate it as

ArrayList<String> userNameList = new ArrayList<>(); In both examples above, we specifically declared an ArrayList and assigned an ArrayList to it. However, if you prefer, you can also choose to declare a List instead and assign an ArrayList to it. This is allowed because List is the superinterface of ArrayList. To declare a List and assign an ArrayList to it, we write List<String> userNameList2 = new ArrayList<>(); You need to import java.util.List if you do it this way. 9.4.1 ArrayList Methods The ArrayList class comes with a large number of pre-written methods that we can use. All methods discussed below can be used whether you declared an ArrayList or you declared a List and assigned an ArrayList to it. add() To add members to a list, use the add() method. userAgeList.add(40); userAgeList.add(53); userAgeList.add(45); userAgeList.add(53); userAgeList now has 4 members. You can use System.out.println() to print out the members of a list. If you write System.out.println(userAgeList);

you’ll get [40, 53, 45, 53] as the output. To add members at a specific position, do the following: userAgeList.add(2, 51); This inserts the number 51 into index 2 (i.e. the third position). userAgeList now becomes [40, 53, 51, 45, 53]. set() To replace an element at a specified position with another element, use the set() method. For instance, to change the element at index 3 to 49, do the following: userAgeList.set(3, 49); The first argument is the index of the element that you want to replace and the second argument is the value that you want to replace it with. userAgeList now becomes [40, 53, 51, 49, 53]. remove() To remove member at a specific position from the list, use the remove() method. The remove() method takes in the index of the item to be removed as argument. For instance, if we write

userAgeList.remove(3); The element at index 3 is removed. userAgeList becomes [40, 53, 51, 53]. get() To get the element at a specific position, use the get() method. userAgeList.get(2); gives us the number 51. size() To find out the number of elements in the list, use the size() method. userAgeList.size() gives us 4 as there are 4 elements in the ArrayList at the moment. contains() To check if a list contains a certain member, use the contains() method. To check if userAgeList contains ‘51’, we write userAgeList.contains(51); we will get true as the result. If we write userAgeList.contains(12); we will get false as the result.

indexOf() To get the index of the first occurrence of a certain element, use the indexOf() method. If the element does not exist in the ArrayList, the method returns -1. For instance, if we write userAgeList.indexOf(53); we’ll get 1 as the result. Even though 53 appears in the ArrayList twice, we’ll only get the index of the first occurrence. If we write userAgeList.indexOf(12); we’ll get -1 as the number 12 does not exist in the ArrayList. toArray() To get all the elements in the ArrayList, use the toArray() method. This method returns an array of Object type containing all of the elements in the ArrayList in proper sequence (from first to last element). For instance, we can write Object[] myArray = userAgeList.toArray(); to convert userAgeList to an Object[] array. (Recall that the Object class is the parent class of all classes in Java.) If you want toArray() to return a specific type of array instead, you can pass the type of array in as an argument. For instance, if you want toArray() to return an Integer[] array instead of the default Object[] array, you can

write Integer[] myIntArray = userAgeList.toArray(new Integer[0]); Here we pass an Integer array of size 0 (new Integer[0]) to the toArray() method. When we do this, the toArray() method returns an Integer array which we can then assign to myIntArray. clear() To remove all items in a list, use the clear() method. If we write userAgeList.clear(); we will have no elements left in the list. For a complete list of all the ArrayList methods available in Java, check out this page https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html 9.5 LinkedList Next, let’s look at LinkedList. A LinkedList is very similar to an ArrayList and is thus very similar to use. They both implement the List interface. The main difference between a LinkedList and an ArrayList is their implementation. An ArrayList is implemented as a resizable array. As more elements are added, its size is increased dynamically. Whenever we add a new element to the middle of an ArrayList, all the elements after it have to be shifted. For instance, if myArrayList has three elements as shown below myArrayList = {\"Hello\", \"Good\", \"Morning\"};

and we want to insert the string “World” into position 1 (i.e. after “Hello”), all the elements after “Hello” have to be shifted. Similarly, when we delete an element from an ArrayList, all the elements after the deleted element have to be shifted up. This can result in a significant delay if the ArrayList is large. If there is a need for frequent addition and deletion of elements from a List, it is better to use a LinkedList. A LinkedList stores the addresses of the elements before and after each element. For instance, suppose a LinkedList has three elements \"Hello\", \"Good\" and \"Morning\" at addresses 111, 122 and 133 respectively. The diagram below shows how a LinkedList is implemented. As “Hello” is the first element, there is no element before it. Hence the box on the left shows an arrow pointing to null (null simply means it is pointing to nothing). The box on the right stores the address 122, which is the address of the next element. For the element “Good”, the box on the left stores the address 111 (the address of the previous element) and the box on the right stores the address 133 (the address of the next element). Now suppose we delete the element “Good”. The diagram below shows how the addresses are updated (the underlined addresses).

For the element “Hello”, the box on the right now stores the address 133, which is the address of the element “Morning”. When the List is implemented as such, there is no need to shift any element when elements are added or deleted. The addresses are simply updated when necessary Since there is now no element pointing to the element “Good”, the Java Virtual Machine will eventually delete this element so as to free up any memory that it may be using. When to use a LinkedList over an ArrayList? Generally, we use a LinkedList when there is a need to add or remove elements frequently. In addition, the LinkedList class implements the Queue and Deque interfaces on top of the List interface. Queue and Deque are two other interfaces in the Java Collections Framework. Hence, you will have to use a LinkedList instead of an ArrayList if you want to use any of the methods from these two interfaces (such as offer(), peek(), poll(), getFirst(), getLast() etc). However, note that a LinkedList has higher memory consumption than an ArrayList as memory is needed to store the addresses of the neighbouring elements. It is also more time-consuming to find a specific element in a LinkedList as you have to start from the first element in the list and follow the references until you get to that item. This is in contrast to an ArrayList


Like this book? You can publish your book online for free in a few minutes!
Create your own flipbook