Classes and Objects: Commentary

Steven Zeil

Last modified: Aug 19, 2016
Contents:

This lesson discusses the basics of working with classes and objects in Java.

1 Classes

We’ve seen this example before.

1.1 Declaring Classes

Syntax alert: Note that Java class declarations do not end with a semi-colon (‘;’) like C++ classes do.

1.2 Declaring Member Variables

Declaring Member Variables

Except for the initial modifiers, there’s no surprises here.

Access Modifiers

public and private in Java mean the same thing that they do in C++, and are used for the same purpose.

The difference in that in Java public and private are modifiers that are attached to individual declarations. In C++ we use them to introduce regions of the class within which all declarations have that access:

In Java:

public class Bicycle {

private➀ int cadence;
private➁ int gear;
private int speed;

public➂ Bicycle(int startCadence,
int startSpeed,
int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}

public➃ int getCadence() {
return cadence;
}
⋮    

But in C++:

class Bicycle {
private:  ➀      
int cadence;
int gear;
int speed;
public: ➁       
Bicycle(int startCadence,
int startSpeed,
int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}

int getCadence() {
return cadence;
}
⋮

Java features the same three access controls as C++: public, private, and protected, with the same meaning. (If you are unfamiliar with protected, it denotes a declaration that is accessible to code within the same class or within classes that inherit from this class, but that is not accessible to code in unrelated classes.

Java adds a fourth access control, “package access”. If a declaration is not marked as private, public or protected, then that declaration is accessible to code within the same package as this class, and is inaccessible to any code within other packages.

1.3 Defining Methods

No changes from C++.

I’m not sure that I agree with the Note at the bottom of this page. I’ve worked extensively in languages where overloading was not permitted. Programmers in these languages have to go to great lengths to make up unique names for every function, usually resulting in numerous names that are just slightly different from one another for purely artificial reasons. To me, that’s a major hit to readability. So I would say that you should not be afraid to use overloading wherever it makes sense.

1.4 Providing Constructors for Your Classes

The idea of constructors and the syntax for providing them should look familiar to you.

Be aware that you cannot use initialization lists in Java as you can in C++:

Java:

public Bicycle(int startCadence,
int startSpeed,
int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}

C++, a similar version:

Bicycle(int startCadence,
int startSpeed,
int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}

C++, using initialization lists:

Bicycle(int startCadence,
int startSpeed,
int startGear) 
: gear(startGear), cadence(startCadence), speed(startSpeed)
{
}

Of course, if your C++ style leans toward the first version above, then you may never notice the difference.

“You don’t have to provide any constructors for your class, but you must be careful when doing this. The compiler automatically provides a no-argument, default constructor for any class without constructors.”

Savvy C++ programmers will recall that the C++ compiler does the same thing.

1.5 Passing Information to a Method or a Constructor

Parameter passing is simpler in Java than in C++. You do not have the option in Java of passing by value or by reference (using the ‘&’) as in C++. Everything is passed by value. But, some of those “things” that are passed by value are themselves references (addresses) for variables in the calling routine, and passing a reference by value is tantamount to passing a variable by reference.

Technically, the same is true in C++/ C++ has reference types denoted by the & appended to a type name. You can declare variables like this:

int myNumber = 23;  // an ordinary integer
int& youNumber = myNumber;// a reference to an int, initialized with
// address of myNumber

Technically, when you have a reference parameter in C++:

void foo (int& aReferenceParam);

you are actually passing an int& (a reference type) by value, which is equivalent to passing an int by reference.

What makes this worth mentioning is that C++ gives you the option of having some variables contain an actual value and others contain references to locations containing the actual values. Java does not give you that option, Instead, as we will see, all primitive variables (int, double, char, …) hold actual values, but all class variables hold references to an actual value somewhere on the heap.

Consequently, all function parameters of Java primitive types behave as if passed by value (specifically, they are inputs only to the function and cannot be used to supply outputs back to the caller) while parameters of a Java class type behave as if passed by reference, meaning that the function can change their value and in so doing, alter the value seen by the caller when the function returns.

2 Objects

2.1 Creating Objects

OK, this is where we come to grips with the single biggest difference between Java and C++. I’ve already hinted at it a couple of pages ago:

“All primitive variables (int, double, char, …) hold actual values, but all class variables hold references to an actual value somewhere on the heap.”

So when you see Java code like this:

Point originOne = new Point(23, 94);
Rectangle rectOne =
new Rectangle(originOne, 100, 200);
Rectangle rectTwo =
new Rectangle(50, 100);

you can tell just by looking at it that Point must have a constructor that takes two integer parameters and that Rectangle must have one constructor that takes a point and two integers and a second constructor that takes two integers as parameters.

But if i had given you that information about the constructors and asked you to declared these variables in C++, you probably would have written:

Point originOne (23, 94);
Rectangle rectOne (originOne, 100, 200);
Rectangle rectTwo (50, 100);

That works in C++ because the variables originOne, rectOne, and rectTwo all contain the actual Point and Rectangle values. But in Java, because Point and Rectangle are classes, all variables of type Point and Rectangle are restricted to holding a reference (address/pointer) to a value allocated on the heap. The “= new” is clearly suggestive of allocating on a heap, so the closer C++ equivalent to the above code would be:

Point* originOne = new Point(23, 94);
Rectangle* rectOne =
new Rectangle(originOne, 100, 200);
Rectangle* rectTwo =
new Rectangle(50, 100);

You might wonder why I chose to use C++ pointers instead of C++ references. After all, Java calls them “references”. But if we compare the basic operations on Java references to C++ pointers and references:

Java References C++ Pointers C++ References
Declaring: Point p; Point* p; Point& p
Creating new value: Point p = new Point(); Point* p = new Point(); (not possible)
Accessing members: int k = p.x; int k = p->x; int k = p.x;
Sharing: Point p2 = p; Point* p2 = p; Point& p2 = p;
Reassigning: p2 = p; p2 = p; (not possible)
Set to null: Point p = null; Point* p = 0; // or nullptr (not possible)
Recover memory: (automatic) delete p; (not possible)

You can see that, for one operation (accessing members), Java references and C++ references look like one another. However, there are several things that you just can’t do with reference types in C++: you can’t use them to allocate values, you can’t change the address that they contain after they have been initialized, and you can’t set or initialize them to null. But all of these things can done with Java references, making them much more like C++ pointers than like C++ references.

It’s kind of interesting that you often read claims that Java is simpler than C++ because Java does not have pointers. But, truthfully, the only reason that “Java doesn’t have pointers” is simply because Java calls them “references” instead of “pointers”, and Java code is absolutely swimming in references/pointers, whereas C++ code is far more selective about the use of pointers.

That’s bad news if you are one of those C++ programmers who is allergic to pointers and avoids using them whenever possible. In that case, Java is going to be a shock for you. But, in fairness, even C++ programmers find that, as they move into more Object-Oriented styles of work, that they find more and more pointers creeping in to their code.

But, beware! The most innocent looking statements can mean very different things depending on which language you are in. For example, the following code would compile in either language:

void foo (Point p1)
{
   p1.x = 0;
   p1.y = 1;
   Point p2 = p;
   p2.x = 2;
   ⋮

but what is the value of p1.x at the end of this sequence? Well, if we compiled this as part of a C++ program, then p1 and p2 are distinct Points and p1.x is therefore 0. But if we compiled this as part of a Java program, then the value of p1.x is 2. That’s because declaration of p2 actually assigns to p2 the address already stored in p1, so that p1 and p2 are now pointing to the same location and the assignment to p2.x overwrites the shared value that can also be accessed via pointer p1.

2.2 Using Objects

We’ve seen that it’s probably best to think of Java references as C++ pointers, but to note that, because “pointers” are no nearly universal in Java, the Java designers decided not to bother with the * and & and -> operators that we use for pointer declaration and manipulation in C++. Instead, the ’*‘ and ’&‘ are dropped altogether, and ’->‘ is replaced by the lowly ’.’.

The Garbage collector, described at the bottom of the tutorial page, explains why I stated earlier that memory recovery was automatic in Java. In fact, Java does not have an equivalent of the C++ delete statement. Instead, we just abandon our old pointers and wait for the garbage collector to pick them up.

Other programming languages have featured automatic garbage collection. In many cases, the implementations were crude and could cause a program to suddenly pause for several seconds or longer while the collector was running. This has led to a deep-set prejudice against automatic garbage collection by programmers worried about performance.

That’s a little bit unfair. This prejudice is rooted in collection algorithms that are nearly 50 years old, and a lot of progress has been made since then in developing collectors that work unobtrusively. Given how common it is for even experienced programmers to get into trouble with missing or incorrectly coded deletes, there’s little doubt that the majority of programming projects can benefit greatly from the availability of automatic garbage collection for what turns out to be a very small performance hit.

3 More on Classes

3.1 Returning a Value from a Method

No comments.

3.2 Using the this Keyword

The idea of this as a pointer to the object being operated upon is nearly identical to C++. If you haven’t encountered in C++ yet, you’ve just been lucky so far.

3.3 Controlling Access to Members of a Class

We’ve already talked about the 4 access levels in Java versus the three in C++.

One interesting difference between C++ and Java is that one can declare an entire class as public/private in Java.

The closest equivalent to that in C++ occurs when we nest classes (described later in this lesson). For example, if I wanted to implement a mailing list as a linked list, I could make the node structure private like this:

class MailingList {
private:
   struct Node {
     Contact data;
     Node* next;
   };
   Node* first;
public:
   MailingList() {first = 0;}

   ⋮

And we can do pretty much the same in Java:

public class MailingList {

private class Node {
   Contact data;
   Node* next;
}
private Node first;

public MailingList() {first = null;}

⋮

But we can’t make a top-level class in C++, such as MailingList, private. We can accomplish much the same effect, however, by declaring a top-level class in a compilation unit (a .cpp file) instead of in a header (.h file). In that case, no other compilation unit will ever see the class declaration.

3.4 Understanding Instance and Class Members

Although you may not have seen much use of static (class) members in C++, they are fairly common and mean exactly the same as described here for Java. There is a difference when calling a static member function (or using a static data member) from some other class. In C++ this is done via the “::” operator. In Java, we just use “.”.

One interesting note: Java does not have the notion of a standalone function. All functions must be members of some class. The same is true of variables or constants. So if you have functions that don’t actually operate on objects, e.g. in C++:

double PI = 3.14159;

double hypotenuse (double a, double b)
{
  return sqrt(a*a + b*b);
}
⋮
int main (int argc, char**argv)
{
  double circ = 2.0 * PI * hypotenuse(12.0, 10.0);
  cout << "Answer: " << circ << endl;
  return 0;
}

In Java these functions have to be put inside a class, but since they don’t operate on any instance of an object, they are most easy handled as static functions.

We could do this (halfway) in C++:

class MyMath {
public:
static double PI = 3.14159;

static double hypotenuse (double a, double b);
};

double MyMath::hypotenuse (double a, double b);
{
  return sqrt(a*a + b*b);
}
⋮
int main (int argc, char**argv)
{
  double circ = 2.0 * MyMath::PI * MyMath::hypotenuse(12.0, 10.0);
  cout << "Answer: " << circ << endl;
  return 0;
}

but in Java we would have to do this:

public class MyMath {
public static double PI = 3.14159;

  public static double hypotenuse (double a, double b)
  {
    return Math.sqrt(a*a + b*b);
  }
}

⋮
public class Application {
  static public int main (String[] argv)
  {
    double circ = 2.0 * MyMath.PI * MyMath.hypotenuse(12.0, 10.0);
    System.out.println ("Answer: " + circ);
    return 0;
  }
}; 

Even the utility function sqrt is, in Java, a static member of a class (Math).

3.5 Initializing Fields

C++ does not have static blocks. In fact, C++ requires that static members be initialized outside of the class declaration (typically in the .cpp part of the compilation unit).

4 Nested Classes

Although you may not have done it yourself very often, C++ also permits class nesting. There’s not much difference from Java.

The one thing worth mentioning is that, once again, Java uses a simple “.” where C++ would use “::”.

In Java,

public class MailingList {

private classs Node {
  Contact data;
  Node* next;
}
private Node first;

public MailingList() {first = null;}

⋮

the full name of the linked list node class is MailingList.Node.

In C++,

class MailingList {
private:
   struct Node {
      Contact data;
      Node* next;
   };
   Node* first;
public:
   MailingList() {first = 0;}

⋮

the full name of that struct/class is MailingList::Node.