Object-Oriented Programming with Java - Wrox Press Java Programming 24-Hour Trainer 2nd (2015)

Wrox Press Java Programming 24-Hour Trainer 2nd (2015)

Lesson 3. Object-Oriented Programming with Java

Starting with this lesson, you study various elements of the Java language with brief descriptions to get you started with programming in the shortest possible time. But you are certainly encouraged to refer to the extensive Java SE documentation that’s available online athttp://docs.oracle.com/javase/8/.

Classes and Objects

Java is an object-oriented language, which means that it has constructs to represent objects from the real world. Each Java program has at least one class that knows how to do certain things or how to represent some type of object. For example, the simplest class, HelloWorld, knows how to greet the world.

Classes in Java may have methods and fields (also known as attributes). Methods represent actions or functions that a class can perform. Up until Java 8, every function had to be represented as a method of some class. Lambda expressions (see working_with_streams) give more freedom to functions, but for now the focus is on the Java foundation — classes, methods, and fields.

Let’s create and discuss a class named Car. This class will have methods, describing what this type of vehicle can do, such as start the engine, shut it down, accelerate, brake, lock the doors, and so on.

This class will also have some fields: body color, number of doors, sticker price, and so on.

Listing 3-1: Class Car

class Car{

String color;

int numberOfDoors;

void startEngine() {

// Some code goes here

}

void stopEngine() {

int tempCounter=0;

// Some code goes here

}

}

In some code samples you’ll see the comments, “Some code goes here.” I do this to avoid distracting you from something that is not relevant to the subject of discussion. At this point, you couldn’t care less about the algorithm of starting the engine. You’re getting familiar with the structure of a Java class.

Car represents common features for many different cars: All cars have such attributes as color and number of doors, and all of them perform similar actions. You can be more specific and create another Java class called JamesBondCar. It’s still a car, but with some attributes specific to the model created for James Bond (see Listing 3-2). You can say that the class JamesBondCar is a subclass of Car, or, using Java syntax, JamesBondCar extends Car.

Listing 3-2: Class JamesBondCar

class JamesBondCar extends Car{

int currentSubmergeDepth;

boolean isGunOnBoard=true;

final String MANUFACTURER;

void submerge() {

currentSubmergeDepth = 50;

// Some code goes here

}

void surface() {

// Some code goes here

}

}

As you can guess from the method names, the James Bond’s car not only drives, but it can go under water and then resurface. But even after defining all the attributes and methods for the class JamesBondCar, you can’t “drive it," even on the computer screen. A Java class is like a blueprint in construction or engineering; until you build real objects based on this blueprint, you can’t use them.

Creating objects, also known as instances, based on classes is the equivalent of building real cars based on blueprints. To create an instance of a class means to create the object in the computer’s memory based on the class definition.

To instantiate a class (to put a car on the road), you declare a variable of this class’s type, and use the new operator for each new instance of the car:

JamesBondCar car1 = new JamesBondCar();

JamesBondCar car2 = new JamesBondCar();

Now the variables car1 and car2 can be used to refer to the first and second instance of the JamesBondCar, respectively. To be precise, declaring the variables pointing at the instances is needed if you are planning to refer to these instances in the program. The variables car1 and car2 become your access points to the corresponding instance of a Car, as Figure 3-1 depicts.

image

Figure 3-1: Instantiating two Car objects

The statement new JamesBondCar() creates the instance of this class in heap memory. In the real world, you can create many cars based on the same specification. Even though they all represent the same class, they may have different values in their attributes — some of them are red and some yellow, some of them have two doors whereas others have four, and so on.

Variables and Data Types

Some values representing an object can change over the program’s lifetime (variables) and some remain the same (constants). This section sheds more light on the use of both types.

Declaring Variables

Java is a statically typed language: A program variable must be declared (given a name and a data type) first, and then you can assign them values either at the time of declaration or later on in one of the class methods. For example, the variable isGunOnBoard has been initialized during its declaration in Listing 3-2, and currentSubmergeDepth got its value in the method submerge().

The class Car from Listing 3-1 defines a variable color of type String, which is used to represent text values; for example, “Red,” “Blue,” and so on.

Final Variables

To store the value that never changes, you need to declare a final variable (or constant); just add the keyword final to the declaration line, as in Listing 3-2:

final String MANUFACTURER = "J.B. Limited";

Java developers usually name final variables in upper case. If you are wondering how Java developers agree on naming conventions, check out one of the coding standards guides. For example, Google publishes coding standards at https://code.google.com/p/google-styleguide/ for various languages.

The value of a constant can be assigned only once, and because you are creating an instance of a specific car, its manufacturer is known and can’t change during the life span of this object. Declare a final variable and initialize it right away, as shown earlier.

Primitive Data Types

When you’re declaring a class, you create a new data type and can declare variables of this type as you saw above with the class Car. But these are not a simple data type as they can include fields and methods describing the object of this type. On the other hand, Java has predefined data types for storing simple values, such as an integer number or a character.

There are eight primitive data types in Java: four are for integer values; two are for values with a decimal point; one is for storing single characters; and one is for boolean data that allows only either true or false as a value. Following are some examples of variable declarations and initializations:

int chairs = 12;

char grade = 'A’;

boolean cancelJob = false;

double nationalIncome = 23863494965745.78;

float hourlyRate = 12.50f; // add an f at the end of

//float literals

long totalCars = 4637283648392l; // add an l at the end

// of long literals

The last two literals in the preceding list end with the letters f and l to indicate that you want to store these data as float and long data types correspondingly. The double data type fits most of the needs in non-integer calculations.

Each primitive data type occupies a certain amount of memory and has a range of values that it can store. The following table summarizes some characteristics of the Java data types.

Primitive Type

Size

Min Value

Max Value

Wrapper Class

byte

8 bits

-128

127

Byte

short

16 bits

-32,768

32,767

Short

int

32 bits

-2,147,483,648

2,147,483,647

Integer

long

64 bits

-9,223,372,036,854,775,808

9,223,372,036,854,775,807

Long

float

32 bits

Single-precision floating point; see Java language specification at http://bit.ly/9nlwjh

Single-precision floating point; see Java language specification at http://bit.ly/9nlwjh

Float

double

64 bits

Double-precision floating point; see Java language specification at http://bit.ly/9nlwjh

Double-precision floating point; see Java language specification at http://bit.ly/9nlwjh

Double

char

16 bits

Unicode 0

Unicode 2 in a power of 16 value

Character

boolean

-

false (not a min)

true (not a max)

Boolean

Have you noticed that the char data type uses two bytes of memory to store the data? This enables you to store character sets that have a lot more symbols than traditional alphabets because a single byte can only represent up to 256 different characters, whereas two bytes can represent 65,536 characters.

If you need to store very large numbers, Java has a class BigDecimal, but it’s not a primitive data type.

Variable Scope

If you declare a variable inside any method or a code block surrounded with curly braces, the variable has a local scope (for example, tempCounter in Listing 3-1 is local). This means that it’s only visible for the code within the method stopEngine(). A local variable is accessible within the method only after the variable is declared, and only within the block in which it is declared. For instance, a variable declared inside a for loop is not accessible outside the for loop even within the same method.

When the method completes its execution, all local primitive variables are automatically removed from stack memory. If a variable was pointing to an instance of an object (for example, car1 on Figure 3-1), the corresponding object instance is removed from heap memory by Java’s Garbage Collector (GC), but it won’t happen immediately. Periodically GC walks around the heap memory and removes all objects that have no reference variables.

If a variable has to be accessible from more than one class method, declare it on a class level. Listing 3-1 shows the class Car, where color and numberOfDoors are class or member variables. These variables remain “alive” while the instance of the Car object exists in memory. They can be shared and reused by all methods within the class, and they can even be visible from external classes (read about access levels in Chapter 7). There are some differences in passing primitive variables and those that point at object instances. Read the section Passing by Value or by Reference in the next chapter.

NOTE If a variable is declared with a static qualifier (see Chapter 4) it will be shared by all instances of the class. Instance variables (without static) store different values in each object instance.

Wrappers, Autoboxing, and Unboxing

All primitive data types have corresponding wrapper classes that contain useful methods dealing with respective data types. The wrapper classes serve two purposes:

1. They contain a number of useful functions for manipulation with their primitive counterparts. For example, the class Integer offers such useful methods as conversion of a String into an int, or turning an int into a float, and more. The Integer class also enables you to set the minimum and maximum values for the number in question.

2. Some Java collections can’t store primitives (such as ArrayList), so primitives have to be wrapped into objects. For example:

3. ArrayList myLotteryNumbers = new ArrayList();

4. myLotteryNumbers.add(new Integer(6));

myLotteryNumbers.add(new Integer(15));

Java has a feature called autoboxing, which spares you from explicitly creating a new instance for every primitive as in the preceding code snippet. You can simply write myLotteryNumbers.add(6); and the primitive value 6 is automatically wrapped into an instance of the Integer class.

On the same note, the next line is also valid:

int luckyNumber= myLotteryNumber.get(23);

Even though get(23) returns the value of the 24th element (the numbering in the Java collections starts with zero) as an Integer object, that object is automatically converted into a primitive. This is called unboxing.

Program Comments

While writing code in Java, you should add comments, which is the text that explains what the program does. Programs are being read a lot more often than they are being written. At some point, other software developers will read and try to understand your code. Be nice and make their jobs easier. A typical software developer doesn’t like writing comments (regardless of what programming language he or she uses).

I suggest you use a simple technique: Write comments first and then write the code. By the time your program is written it already has a comment. You can write comments pretty much everywhere — before or inside the class, or inside the methods.

In Java you can use three types of comments:

· Block comments contain more than one line of text located between the symbols /* and */. For example:

·/* This method will calculate the cost of shipping, handling,

· and all applicable taxes

*/

The compiler ignores the text in comments and you can write whatever you want.

· If you want to write a short comment that fits on a single line, start this line with two forward slashes (//). You can also place comments with two forward slashes at the end of the line.

For example:

// Calculate the cost of shipping

int cost = calcShippingCost(); // results depends on country

· Some comments start with /** and end with */. These are used by a special utility, javadoc, that can automatically extract the text from these comments and create program documentation. Javadoc also allows the use of special annotations (for example, @param, @return, @see) that allow producing professional-looking program documentation. To get a feeling for what javadoc can generate, read Oracle’s whitepaper on writing javadoc comments at http://goo.gl/imDMU.

First Useful Program

It’s time to write a program that does something more useful than print “Hello World.” This program emulates the calculation of state tax. The goal is to show you how Java classes communicate, how methods are called, and how variables can be used.

First you need to decide what Java class(es) you need to write for the task at hand. Then think about the attributes (class variables) and methods (behavior) these classes should have.

Declaring a Tax Class

Because you are planning to calculate tax, it doesn’t take a rocket scientist to figure out that you need to define a class called Tax. Start with the class name and curly braces — this is the simplest class you can create:

class Tax{

}

What data does this class need to perform tax calculations? You definitely need to know the gross income of a person for the tax year. Gross income is a good candidate for an attribute of this class. Attributes in Java are represented by variables. Pick one of the numeric data types. Gross income is not always an integer number, so use the double data type, as it’s a number with a decimal point. You could use float instead, but using double enables you to be ready to process larger incomes, too:

class Tax{

double grossIncome;

}

You also need to know what state the person lives in; taxation rules vary by state. These are a few of the abbreviations for the states in the USA: NY, NJ, CT. Use the data type String for storing text data:

class Tax{

double grossIncome;

String state;

}

Add one more attribute for dependents of the taxable person. Integer works just fine here — a person can’t have two-and-a-half dependents:

class Tax{

double grossIncome;

String state;

int dependents;

}

Adding a Method to the Tax Class

Variables store data, and methods perform actions. It’s time for actions. The first method, calcTax(), calculates the state tax based on the values of gross income, number of dependents, and state:

Listing 3-3: Class Tax

class Tax{

double grossIncome;

String state;

int dependents;

public double calcTax() {

return 234.55;

}

}

The calcTax() method signature tells the following:

· Any external class can access this method (public).

· This method returns a value of type double.

· The name of the method is calcTax.

The empty parentheses after the method name mean that it does not have any arguments, or, in other words, it does not need any values from outside Tax to perform calculations. As a matter of fact, this version of calcTax() doesn’t even use the values from class variables for tax calculation. It just always returns a hard-coded tax value of 234.55.

How do you decide if a method should return a value? If your method performs some calculations and has to give a value back to a calling program, it has to return a value. If a method directly modifies the class variables or simply outputs data somewhere (monitor, disk, server) it may not need to return any values. You still need to declare a “no return” in a method signature by using a special keyword, void:

public void printAnnualTaxReturn() {

//Code goes here

}

With the Java return statement, a method can return data contained in a variable to a calling program, for example:

return calculatedTax;

Keep in mind that if you declare a return type in the method signature but forget to include the return statement in the body of the method, the Java compiler will give you an error.

Declaring Another Class: TestTax

Tax will know how to calculate tax, but in a real-world application you’ll have many classes that represent the various workflows of this process. For example, you may need to create a class called Customer. Depending on the type of employment or income, accountants use many different forms to file taxes, and each form can be represented by a separate class: Form1040, Form1099, and so on.

Each of these classes represents some entity, but none of them is an executable program; that is, none of them will have the method main(). You need to create one more class to start the application and instantiate other classes as needed. I’m calling this class TestTax. The class TestTax should be able to perform the following actions:

· Create an instance of the class Tax.

· Assign the customer’s data (gross income, state, dependents) to the class variables of the class Tax.

· Call the method calcTax().

· Print the result on the screen.

The class TestTax is stored in a separate file named TestTax.java.

Listing 3-4: Class TestTax

class TestTax{

public static void main(String[] args){

Tax t = new Tax(); // creating an instance

// assigning the values to class members

t.grossIncome= 50000;

t.dependents= 2;

t.state= "NJ”;

double yourTax = t.calcTax(); //calculating tax

// Printing the result

System.out.println("Your tax is ” + yourTax);

}

}

In the preceding code you’ve declared a variable, t, of type Tax. The method main() is an entry point to the tax-calculation program. This method creates an instance of the class Tax, and the variable t points to a place in your computer’s memory where the Tax object was created. From now on, if you want to refer to this object use the variable t. Take another look at Figure 3-1, which shows a similar situation to what you have here.

The following three lines assign values to the fields of the object Tax:

t.grossIncome= 50000;

t.dependents= 2;

t.state= "NJ”;

After that you can calculate tax on your object represented by t by calling the method calcTax(), and the result returned by this method will be assigned to the variable yourTax. The method calcTax() still returns the hard-coded value, but you fix this in the “Try It” section of this lesson. The last line just displays the result on the system console.

At this point you already have two classes communicating with each other (TestTax and Tax). The class TextTax creates an instance of Tax, initializes its variables, and calls its method calcTax(), which returns the value back to the class TextTax.

Conditional Statement if

In the real life we make decisions all the time: “If she says this I’ll answer with that, otherwise I’ll do something else.” Java has an if statement that determines whether some condition is true or false. Based on the answer to this question the execution of your program will be routed.

In the following code snippet, if the condition expression (totalOrderPrice > 100) evaluates to true then the code between the first curly braces is executed; otherwise the code after the else statement takes place:

if (totalOrderPrice > 100){

System.out.println("You’ll get a 20% discount”);

}

else{

System.out.println("Order books for more than a” +

" $100 to get a 20% discount”);

}

Because this code sample has only one statement to execute in the if and else clauses, using curly braces is not a must, but they make the code more readable and prevent you from introducing hard-to-find bugs if, later on, you need to add more code in an if statement.

switch Statement

The switch statement is an alternative to if. The case label in the switch condition (taxCode) is evaluated and the program goes to one of the following case clauses:

int taxCode=someObject.getTaxCode(grossIncome);

switch (taxCode){

case 0:

System.out.println("Tax Exempt”);

break;

case 1:

System.out.println("Low Tax Bracket”);

break;

case 2:

System.out.println("High Tax Bracket”);

break;

default:

System.out.println("Wrong Tax Bracket”);

}

// Some other code goes here

The preceding code invokes only one of the println() methods and continues with the execution with the other code below the closing curly brace, if any. Do not forget to put the break at the end of each case statement so the program jumps out of the switch statement after processing a case; otherwise the code “falls-through” and prints more than one line even though a taxCode can have only one value. For example, the following code prints both “Tax Exempt” and “Low Tax Bracket” even if the value of the taxCode is zero:

switch (taxCode){

case 0:

System.out.println("Tax Exempt”);

case 1:

System.out.println("Low Tax Bracket”);

break;

case 2:

System.out.println("High Tax Bracket”);

break;

default:

System.out.println("Wrong Tax Bracket”);

}

Starting from Java 7 you can use String values in the case expression:

switch (yourState){

case "NY”:

System.out.println("Taxing by NY law”);

break;

case "CA”:

System.out.println("Taxing by CA law”);

break;

case "FL”:

System.out.println("Taxing by FL law”);

break;

default:

System.out.println("Wrong state”);

}

Inheritance

In object-oriented languages, the term inheritance means an ability to define a new class based on an existing one (not from scratch).

Imagine that the class Tax calculates tax properly in all states except New Jersey, which has introduced new educational tax deductions. If you have a kid in college, this makes you eligible for an additional $500 deduction from your taxes. In this case you have to either change the methodcalcTax() in the class Tax to introduce a special case for New Jersey, or create another class based on Tax, and add this new functionality there.

Every person inherits some features from his or her parents. A similar mechanism exists in Java. The special keyword extends is used to indicate that one class has been inherited from another:

class NJTax extends Tax{

}

The class NJTax has all the features of the class Tax, plus you can add new attributes and methods to it. In such a setup, the class Tax is called a superclass, and NJTax is called a subclass. You can also use the terms ancestor and descendent, respectively. This new class has access to all variables and methods of its superclass, unless those have a private or package access level, which is discussed in Chapter 5.

Let’s extend the behavior of the class Tax in NJTax. The latter has a method called adjustForStudents():

Listing 3-5: Class NJTax

class NJTax extends Tax{

double adjustForStudents (double stateTax){

double adjustedTax = stateTax - 500;

return adjustedTax;

}

}

To use this new method, the TestTax class should instantiate NJTax rather than Tax as it did in Listing 3-4:

NJTax t= new NJTax();

Now you can call methods defined in the class Tax as well as those from NJTax using the reference variable t; for example:

NJTax t= new NJTax();

double yourTax = t.calcTax();

double totalTax = t.adjustForStudents(yourTax);

I’ve added a new functionality to the tax-calculation program without changing the code of the class Tax. The preceding code fragment also shows how you can pass a result of processing from one method to another. The value of the variable yourTax was calculated by calcTax() and then passed to the method adjustForStudents() as an argument.

Method Overriding

Yet another important term in object-oriented programming is method overriding. Imagine class Tax with 20 methods. Most of them work fine for all states, but there is one method that is not valid for New Jersey. Instead of modifying this method in the superclass, you could create another method in the subclass with the same name and argument list (also known as signature). If a subclass has that method with the same signature, it overrides (suppresses) the corresponding method of its ancestor.

Method overriding comes in handy in the following situations:

· The source code of the superclass is not available, but you still need to change its functionality.

· The original version of the method is still valid in some cases and you want to keep it intact.

· You use method overriding to enable polymorphism, which will be explained in Chapter 7.

You have a chance to try method overriding in the “Try It” section. In Chapter 4 you read about method overloading, which is a completely different animal.

Additional Materials

Java Garbage Collector Basics http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

Try It

In this section, you create in Eclipse the tax-calculation application described in this lesson, and then modify it to replace the hard-coded value returned by the method calcTax() with some calculations. After this is done, you subclass the class Tax and override calcTax().

Lesson Requirements

For this lesson you must have Eclipse IDE installed.

NOTE You can download the code and resources for this “Try It” from the book’s web page at www.wrox.com/go/javaprog24hr2e. You can find them in Lesson3.zip.

Hints

This lesson has only brief introductions to basic Java language constructs. The online Java tutorial may be handy while completing this and future assignments. It’s available at http://download.oracle.com/javase/tutorial/java/index.html.

Step-by-Step

1. In Eclipse, create a new project named Lesson3.

2. Create a new Tax class (File→New→Class). Enter the code shown in Listing 3-3.

3. Create another class, TestTax, and input the code from Listing 3-4.

4. Save both classes and run TestTax (right-click and select Run As→Java Application). The console view should display “Your tax is $234.55.”

5. Replace the return of a hard-coded value with some tax calculations. Let’s say that if the gross income was less than $30,000 you deduct 5% for state tax. If it’s greater than $30,000 you deduct 6%. Modify the code of the method calcTax as follows. Run the program several times, modifying the values of the class variables of the class Tax. Make sure that the tax value on the console is properly calculated:

6. public double calcTax() {

7. double stateTax=0;

8. if (grossIncome < 30000) {

9. stateTax=grossIncome*0.05;

10. }

11. else{

12. stateTax= grossIncome*0.06;

13. }

14. return stateTax;

}

15. Create the NJTax class shown in Listing 3-5.

16. Change the functionality of calcTax() by overriding it in NJTax. The new version of calcTax() should lower the tax by $500 before returning the value.

17. Modify the code of the TestTax class to instantiate NJTax instead of Tax. Observe that the $500 deduction is properly calculated.

To get the sample database files, you can download Chapter 3 from the book’s website at www.wrox.com/go/javaprog24hr2e.

TIP Please select the videos for Lesson 3 online at www.wrox.com/go/javaprog24hr2e. You will also be able to download the code and resources for this lesson from the website.