Skip to content

Latest commit

 

History

History
560 lines (363 loc) · 28.2 KB

Chapter_3.adoc

File metadata and controls

560 lines (363 loc) · 28.2 KB

Meet Classes - the Main Language Constructs

In the real world you see and use various objects, and each of them is belongs to some kind such as toys, food, animals, electronics et al. In Java, instead of saying what kind of objects, we say what class of objects. In other words, each object belongs to a class. A class is like a blueprint of an object. In this chapter you’ll get familiar with classes - the main constructs of the Java language.

Any program that you’ll write will have at least one class. You’ll learn how to declare a class and how to create one or more objects based on the class declaration. You’ll also learn about a class’s internals – what a class can consist of. Finally, I’ll show you how to create a class that inherits features of another class.

Java defines a number of data types. Some of them are simple, like int, which represents integer numbers. Some of them are more complex - they are called classes, like System, which can be used for things like printing text on the screen, exiting a program, or cleaning a computer’s memory.

When you installed Java on your computer, you installed thousands of Java classes. Your Java programs will also consist of classes that can represent objects from the real world. If a class is a data type, an object is a representative of a specific type. For instance, you can see ten dogs on the street, and they all represent the class Dog.

Programmers start working on any application by deciding which Java classes to include in the program and how many of each object type to create. Say, they can define a class Player in the gaming application and create two object instances of this type.

Dissecting Classes and Objects

When programmers talk to each other, they may say "Do something with the class Player" or "Do something with the object Player". We need to learn the difference between the meaning of the words class and object.

Classes

Let’s see what Java classes can consist of. A very simple class can have nothing inside. Take a look at this class declaration:

class VideoGame {

}

This class is empty. It knows nothing and can’t do anything because there is no code between the curly braces. From the Java syntax perspective, this class is legitimate and compiler won’t complain. There is a valid keyword class followed by its name VideoGame. The opening curly brace has the matching closing one. We need classes that can do stuff. We can place methods and attributes inside the class.

  • Methods define actions that a class can perform.

  • Attributes describe various properties of a class.

A method consists of a callable code fragment, that can be called (and executed) by name from another part of the program. For example, a class can have a method named saveScore that contains a dozen of lines of code to save the game score. Let’s add some methods and attributes to the class VideoGame. This class may have several methods, which can tell what an object of this class can do: start the game, stop it, save the score, ask for additional lives on Facebook, and so on. This class also may have some attributes (a.k.a. fields): color, price and others. An object that represents the class VideoGame can look like this:

class VideoGame {

  String color;
  int price;

  void start () {
     // The code to start the game goes here
  }

  void stop () {
     // The code to stop the game goes here
  }

  void saveScore(String playerName, int score) {
     // The code to save score goes here
  }
}

This class has two attributes color and price. The color attribute has the String data type, which is used for storing any text. The price attribute is of an int type, which is used for storing integer numbers. The class VideoGame has three methods: start, stop, and saveScore. These are the actions that our video game should perform. Each of these method declarations start with a keyword void, which means that these methods do not return a value to the caller of the method. In the next chapter I’ll show you a class Fish, which will include a method dive that returns a value.

At this point each of these methods has just a single line that starts with two slashes //.

Note
If a line starts with two slashes, it contains a single line comment - just the description of a code fragment. If you need to write several lines of comments, just type in a slash followed by the asterisk /*, then key in as many lines of a text as you want followed by */.

Our class VideoGame can be used as a starting building block for pretty much any video game – all game consoles have the color attribute, all of them allow to save score, and all of them have price tags.

fig 3 01
Figure 1. The Video Game Object

We can be more specific and create another Java class called PlayStation4. It also belongs to the family of video games, but has some properties that are specific to the model Play Station 4, for example sharing on Facebook and Twitter. So among other things, the class PlayStation4 will have methods shareOnFacebook and shareOnTwitter.

class PlayStation4{

    int hardDiskSize;

  // Some other attributes and methods go here

  void shareOnFacebook(){
     // Code to share on Facebook go here
  }

  void shareOnTwitter(){
     // Code to share on Twitter go here
  }
}

Objects

We’ll often use the phrase create an instance of an object, which means to create an object in a computer’s memory based on some class declaration. If a game factory will produce ten thousand games, a programmer can say that they created ten thousand instances of the class PlayStation4.

The factory description of PlayStation4 relates to an actual game the same way as a Java class relates to its instance in memory. The process of building actual games based on this description in the game factory is similar to the process of creating instances of PlayStation4 objects in Java. For example, the next code sample creates one instance of the class PlayStation4 and calls its method shareOnFacebook.

public class CreatePlayStation4Objects {

  public static void main(String[] args) {

    // create one instance of PlayStation4 class
    PlayStation4 firstPlayStation = new PlayStation4();

    // call the method shareOnFacebook
    firstPlayStation.shareOnFacebook();
  }
}

In many cases, a program can use a Java class only after creating the instance of an object. The same with manufacturers - they create thousands of game copies based on the same description. Even though these copies represent the same class, they may have different values in their attributes - some of them are black while others can be silver. Some of them have 500GB hard disk dive while some are upgraded to 1TB.

fig 3 02
Figure 2. One Class, Two Instances

In programmer’s jargon, you can create multiple instances of the PlayStation4 objects with different values in the hardDiskSize attribute.

Java Data Types and Variables

If you ever covered algebra at any point in school, you are familiar with expressions like x=2, which means that the variable x represents the number 2. In programming, we use variables a lot. In programming a variable can store a value in computer’s memory so you can access these values from various methods in your classes. Using the same algebra example, we can say that we assign the value of 2 to the variable x. The equal sign represents the assignment of the value located to the right of the equal sign to the variable located on the left.

Java variables not only can store numbers, but other types of data as well so we need to declare the variable type, the name, and then assign some value to it. For example, the class attribute that has a value of the disk size can be stored in the variable of type String that’s called hardDiskSize, which can be declared like this:

String hardDiskSize;

If the size of the hard disk is 500GB, we can assign this value to the variable hardDiskSize as follows:

hardDiskSize="500GB";

String values are placed inside the quotes, and the numbers aren’t.

The method parameters (a.k.a. arguments) are also stored in variables. The method saveStore has two arguments: playerName of type String and score of type int:

void saveScore(String playerName, int score)

Primitives

Simple Java data types are called primitives. Their keywords are written in small letters (e.g. int, long, char, boolean). So the score is of a primitive data type, while the playerName is not. In Java you have to declare the type and the name of a variable first and then use it.

Remember algebra equations like y=x+2? In Java you’d need to start with declaring the variables x and y of some numeric data type like int, float or double:

int x;
int y;

Now you can use the variables. The next two lines show how you can assign them values. If your program assigns the value of five to the variable x, after evaluating x+2 the variable y will be equal to seven:

x=5;
y=x+2;

In Java you are also allowed to change the value of a variable in a somewhat unusual way. The following two lines change the value of the variable y from five to six:

int y=5;
y++;

Despite the two plus signs, JVM is still going to increment the value of the variable y by one. The y++ means read the value of the variable y, increment it by one, and assign it back to y. The longer syntax to achieve the same result would be y=y+1. After the next code fragment, the value of the variable myScore is also equal to six:

int myScore=5;
myScore=myScore+1;

You can also use multiplication, division and subtraction the same way. Look at the following piece of code:

int myScore=10;

myScore--;
myScore=myScore*2;
myScore=myScore/3;

System.out.println("My score is " + myScore);

What do you think this code prints? IntelliJ IDEA has a useful tool called Groovy Console (it’s under the Tools menu). It allows you to test any code snippet (like the one above) without even creating a class or methods. If you’ll copy the above code fragment into Groovy Console and hit the green button play, you see that the value of the variable myScore is 6.

fig 3 03
Figure 3. Running the code fragment in Groovy Console
To see the result of the score calculations, just  click on the console tab at the bottom of the screen:

My score is 6

In this example the argument of the method println was constructed from two pieces – the text “My score is ” and the value of the variable myScore. Putting two or more strings together is called concatenation. Even though myScore is a number, Java is smart enough to convert this variable into a String and then attach it to the text "My Score is ".

Look at some other ways of changing the values of the variables:

myScore=myScore*2; // it's the same as `myScore*=2;
myScore=myScore+2; // it's the same as `myScore+=2;
myScore=myScore-2; // it's the same as `myScore-=2;
myScore=myScore/2; // it's the same as `myScore/=2;

There are eight primitive data types in Java, and you have to decide which ones to use depending on the type and size of data you are planning to store in your variables. These are Java primitives:

  • Four data types for storing integer values – byte, short, int, and long.

  • Two data types for values with a decimal point – float and double.

  • One data type for storing a single character values – char.

  • One logical data type boolean that allows only two values: true or false.

You can assign an initial value to a variable during its declaration, and this is called variable initialization, for example:

char grade = 'A';
int chairs = 12;
boolean playSound = false;
double nationalIncome = 23863494965745.78;
float gamePrice = 12.50f;
long totalCars =4637283648392l;

In the last two lines the values have letters at the end. The f is for float and l means long.

If you declare, but won’t initialize primitive variables, Java will do it for you by assigning zero to each numeric variable, false to boolean variables, and a special code ‘\u0000’ to variables of type char.

Classes as Data Types

Not all variables have primitive data types. Each primitive data type has a corresponding wrapper class, for example Integer, Double, Boolean, etc. These classes also can store the values of the corresponding primitive type, but also have useful methods to convert data from one type to another.

In some cases there is no primitive type that suits your needs. For example, there is no primitive type for storing long texts. While the char data type is used to store only one character, Java also has a class String for working with a longer text, for example:

String lastName="Smith";

If you declare your own class, consider it a new data type. You can start declaring variables of this type, for example, you can declare the variable named myFirstGame of type VideoGame:

VideoGame myFirstGame

Java programmers have agreed to name variables starting with small letters. To make the variable name more meaningful, it can consist of several words, and each word starts with the capital letter as in myFirstGame. You’re also allowed to start variable names with the dollar sign like $myMoney or underscore: _myBestFriend. Variable names cannot contain spaces.

Java also has a special keyword final, and if it’s used in a declaration of a variable, it means that you can assign a value to this variable only once, and this value cannot be changed afterwards. In Java we usually name final variables using capital letters:

final String STATE_CAPITAL="Washington";

How Much Memory is Needed

Variables are stored in a computer’s memory and occupy more or less space depending on the data type. We measure memory in bits, bytes, kilobytes (1024 bytes), megabytes (1024 kilobytes or Kb), gigabytes (1024 Mb) and so on. A bit is the smallest piece of data that can be stored in memory. It can hold either 1 or 0. A byte consists of eight bits.

  • A char variable occupies two bytes in memory.

  • An int and a float take four bytes of memory each.

  • Variables of long and double types use eight bytes each.

  • Only one bit is required to store the value of boolean variable.

Numeric data types that take more bytes can store larger numbers.

Creation of a Pet

Let’s design and create a class Pet. First we need to decide what actions our pet will be able to do. How about eat, sleep, and speak? We’ll program these actions in the methods of the class Pet. We’ll also give our pet the following attributes: age, height, weight, and color.

It’s time for hands-on work. Start IDEA IDE and create a new project as explained in Chapter 2. This time name it MyPet instead of Hello. Click on the src folder in IDEA and create a Java class called Pet using the menu File | New | Java Class.

Your screen should look similar to this one:

fig 3 04
Figure 4. MyPet Project

Now we are ready to declare attributes and methods in the class Pet. Java classes and methods enclose their bodies in curly braces.

To declare variables for class attributes we should pick data types for them. I suggest an int type for the age, float for weight and height, and String for the pet’s color.

public class Pet {

    int age;
    float weight;
    float height;
    String color;
}

The next step is to add some methods to this class. Before declaring a method you should decide if it should take any arguments and should return a value. This is what I suggest:

  • The method sleep will just print a message Good night, see you tomorrow – it does not need any arguments and will not return any value.

  • The same is true for the method eat.It will print the message I’m so hungry, let me have a snack like nachos!.

  • The method talk will not be printing any messages, but will prepare a message based on the word or a phrase passed to this method as an argument. This method will build a phrase using the argument and will return it back to the calling program.

The new version of the class Pet will look like this:

public class Pet {

    int age;
    float weight;
    float height;
    String color;

    public void sleep(){
        System.out.println(
                "Good night, see you tomorrow!");
    }

    public void eat(){
        System.out.println(
         "I’m so hungry, let me have a snack like nachos!");
    }

    public String talk(String aWord){
        String petResponse = "OK!! OK!! " +aWord;
        return petResponse;
    }
}

This class represents these friendly little monsters from the "real world":

fig 3 05
Figure 5. Little Monster

Let’s talk now about the signatures of each method in detail starting with the method sleep:

public void sleep()

This method signature tells us that sleep can be called from any other Java class ( because it’s public). This method does not return any data (the keyword void). The empty parentheses mean that this method does not take any arguments - it does not need any data from the outside world to print the same text.

The signature of the method eat is similar to sleep.

The signature of the method talk looks like this:

public String talk(String aWord)

This method can also be called from any other Java class, but has to return some text, which is prescribed by the keyword String in front of the method name. Besides, it expects some text data from outside, hence the argument String aWord.

fig 3 06
Figure 6. Little Monster eats

How do you decide if a method should or should not return a value? If a method performs some data manipulations and has to give the result of these manipulations back to the calling class, it has to return a value. You may say that the class Pet does not have any calling class! That’s correct, so let’s create one called PetMaster. We’ll create it in the same IDEA project MyPet.

This class will need a method main to become a runnable program (the class Pet didn’t have one). Remember from Chapter 2, typing psvm followed by the Tab key will quickly create the method main. We’ll place the code communicating with the class Pet inside the method main.

Note
Do not forget to press Ctrl-S (CMD-S on Mac) to save and compile this class!
public class PetMaster {

    public static void main(String[] args) {

        String petReaction;      (1)

        Pet myPet = new Pet();   (2)

        myPet.eat();             (3)

        petReaction = myPet.talk("Tweet!! Tweet!!"); (4)

        System.out.println(petReaction);  (5)

        myPet.sleep();        (6)
    }
}
  1. The variable petReaction will store the value returned by the method say.

  2. This is how to create an instance of the object Pet using the Java operator new. This line declares a variable of type Pet - that’s right, you can treat any classes created by you as new Java data types.

  3. Calling the method eat on the object Pet. After the instance of Pet was created, the variable myPet knows its address in memory. So you can call the method eat (as well as any other) on this particular instance of Pet.

  4. If a method returns a value, specify a variable that will store returned value to the left of the equal sign. In our code it’s petReaction of type String. Such variable should have the same type as the return value of the method.

  5. The value of the variable petReaction is passed as an argument to the method prinln for output.

  6. Calling the method sleep on the object Pet.

To run the class PetMaster right-click on its name in the Project view in IDEA, and select the option Run PetMaster.main(). This program will output the following:

I’m so hungry,let me have a snack like nachos! OK!! OK!! Tweet!! Tweet!! Good night, see you tomorrow!

fig 3 07
Figure 7. Tweet, Tweet!

At this point the project MyPet consists of two Java classes: Pet and PetMaster. The role of the class Pet is to represent attributes and behavior of a pet, while the class PetMaster starts the program, instantiates the class Pet and calls its methods. Your typical projects will consist of multiple Java classes, but only one of them will have have the main method, which is the entry point of the application.

Inheritance – a Fish Can Be a Pet Too

In the real life, every person inherits some features from his or her parents. Similarly, in the Java world you can also create a new class, based on the existing one. The class Pet will help us learn yet another important feature of Java called inheritance.

The class Pet can include behaviors and attributes that are shared by many pets – all of them eat and sleep, some of them make sounds, their skin has different colors, and so on. On the other hand, not all pets are the same. Dogs bark, fish swim and don’t make sounds, and parakeets speak better than dogs. But all of them eat and sleep. That’s why it’s easier to create a class Fish that will inherit some common behavior and attributes from the class Pet, rather than creating Dog, Parrot or Fish from scratch every time.

Java has a special keyword extends that will do the trick. Create a new class Fish in IDEA and then add to its declaration extends Pet so it’ll look as follows:

class Fish extends Pet{

}

This class Fish is a subclass of the class Pet. Accordingly, the class Pet is a superclass of the class Fish. In other words, you use the class Pet as a template for creating a class Fish. The keyword extends defines relationship between classes. Since our Fish extends a Pet, we can say that a fish is a pet. But we can’t say that a pet is a fish.

Even if you will leave the class Fish empty as it is now, you can still use every method and attribute inherited from the class Pet, because a fish is a pet! Take a look, we’re creating an instance of the object Fish and are calling its method sleep:

Fish myLittleFish = new Fish();
myLittleFish.sleep();

Even though we have not declared any methods in the class Fish yet, we call (invoke) the method sleep, which was declared in its superclass Pet! In Java all classes are automatically inherited from the class Object. Even though we’ve declared the class Pet without using the keyword extends it still extends the class Object. With the keyword extends you can build class hierarchies. For example, many animal classes can extend the class Pet.

fig 3 08
Figure 8. Class Hierarchy

Let’s not forget, however, that we’ve created the class Fish as a subclass of Pet, because we wanted to add some additional features that only fish have and reuse some of the code that we wrote for a general pet.For instance, not all pets can dive, but fish certainly can. Let’s add a new method dive to the class Fish.

public class Fish extends Pet {

   int currentDepth=0;

   public int dive(int howDeep){

    currentDepth=currentDepth + howDeep;
    System.out.println("Diving for " +
                     howDeep + " feet");
    System.out.println("I'm at " + currentDepth +
                       " feet below sea level");

    return currentDepth;
  }
}

The method dive has an argument (a.k.a. parameter) howDeep that tells the fish how deep it should go. We’ve also declared a class variable currentDepth that will store and update the current depth every time you call the method dive. This method returns the current value of the variable currenDepth to the calling class. The plus sign in the pintln lines are not about addition - we’re building strings of characters by concatenating their pieces.

Using IDEA, create another class named FishMaster that will look like this:

public class FishMaster {

  public static void main(String[] args) {

    Fish myFish = new Fish();

    myFish.dive(2);
    myFish.dive(3);

    myFish.sleep();
  }
}

The method main instantiates the object Fish and calls its method dive twice with different arguments - two and three. After that, it calls the method sleep. When you run the program FishMaster, it will print the following messages:

Diving for 2 feet
I'm at 2 feet below sea level
Diving for 3 feet
I'm at 5 feet below sea level
Good night, see you tomorrow

Have you noticed that beside the methods defined in the class Fish, the FishMaster also calls method sleep from its superclass Pet? This is what inheritance is all about – you do not have to copy and paste code from the class Pet – just use the keyword extends, and the class Fish can use Pet’s methods!

fig 3 09
Figure 9. What’s the current depth?

One more thing, even though the method dive returns the value of currentDepth, our FishMaster does not use it. That’s fine, because our class`FishMaster` does not use the returned value, but there may be some other classes that will also use the class Fish, and they may find knowing the current depth useful. For example, think of a class FishTrafficDispatcher that has to know positions of other fish under the sea before giving permissions to dive to avoid traffic accidents.

Method Overriding

As you know, fish do not speak, but our class Fish has been inherited from the class Pet that has the method talk. This means that nothing stops you from writing myFish.talk();.

Do you really want our fish to talk? If you do not want this to happen, the class Fish has to override the Pet’s typical behavior - the method talk. If you declare a method with exactly the same signature in a subclass as in its superclass, the subclass' method will be used instead of the method of the superclass. Let’s add the following method talk to the class Fish.

public String talk(String something){
  return "Don't you know that fish do not talk?";
}

Now add the following three lines to the method main of the class FishMaster (after creating the instance of the Fish):

String fishReaction;
fishReaction = myFish.talk("Hello");
System.out.println(fishReaction);

Run the program and it’ll print

Don’t you know that fish do not talk?

This proves that Pet’s method talk has been overridden, or in other words, suppressed.

Wow! We’ve learned a lot in this chapter – let’s just take a break.

Challenge Yourself

  1. We’ve used the variable of type int to to store the price in the class VideoGame. Is there a more suitable primitive data type for storing prices?

  2. Create a new project in IDEA named chapter3 and a new Java class Car with the following methods:

    public void start()

    public void stop()

    public int drive(int howlong)

    The method drive has one argument - the driving time. The method has to return the total distance driven by the car for the specified time. Use the following formula to calculate the distance: distance = howlong*60;. Use the statement return distance as the last line in the method drive.

  3. Run this program by using the menu Run in IDEA.

  4. Write another class CarOwner with the method main that creates an instance of the object Car and call its methods. The result of each method call has to be printed using System.out.println().

  5. Create a subclass of Car named JamesBondCar and override the method drive there. Now use the following formula to calculate the distance: distance = howlong*180;

    Be creative, print some funny messages from the method drive!

  6. Create an instance of the class JamesBondCar in the main method of CarOwner. Re-run the program CarOwner to see that it calls the overridden method drive.