Java 101: Foundations

Java 101: Classes and objects in Java

Learn how to make classes, fields, methods, constructors, and objects work together in your Java applications

1 2 3 Page 3
Page 3 of 3

You can prevent external code from accessing Book's title and pubYear fields so that any attempt to access these fields from beyond Book will result in a compiler error message. Accomplish this task by prepending private to their declarations, as demonstrated below:

class Book
{
   // fields

   private String title;
   private int pubYear; // publication year

   // ...
}

To show you why it's a good idea to hide access to fields, I've created another example that expands the Book class to include an author field, getter/setter methods for accessing this field, and another constructor that lets you also specify an author's name. The following example shows the modified parts of the updated Book class:

class Book
{
   // ...

   private String author;

   // ...

   Book(String title, int pubYear)
   {
      this(title, pubYear, "");
   }

   Book(String title, int pubYear, String author)
   {
      setTitle(title);
      setPubYear(pubYear);
      setAuthor(author);
      ++count;
   }

   // ...

   String getAuthor()
   {
      return author;
   }

   // ...

   void setAuthor(String author)
   {
      this.author = author;
   }

   // ...
}

If you recall, the Book(String title) constructor executes this(title, -1); to avoid code duplication. Similarly, I've designed Book(String title, int pubYear) to execute this(title, pubYear, "");, which calls the newest constructor, to avoid code duplication.

You can easily create a new Book object that includes the author name and obtain this name via the getAuthor() method:

Book book = new Book("A Tale of Two Cities", 1859, "Charles Dickens");
System.out.println(book.getAuthor()); // Output: Charles Dickens

Consider that the upgraded Book class doesn't allow for multiple authors. You cannot change the constructor or getAuthor()/setAuthor() headers to make this enhancement because doing so would change the interface and cause external code that relies on this interface to stop working. Instead, you would need to rework the implementation, as follows:

class Book
{
   // ...

   private String[] authors;

   // ...

   Book(String title, int pubYear, String author)
   {
      this(title, pubYear, new String[] { author });
   }

   Book(String title, int pubYear, String[] authors)
   {
      setTitle(title);
      setPubYear(pubYear);
      setAuthors(authors);
      ++count;
   }

   // ...

   String getAuthor()
   {
      return authors[0];
   }

   String[] getAuthors()
   {
      return authors;
   }

   // ...

   void setAuthor(String author)
   {
      setAuthors(new String[] { author });
   }

   void setAuthors(String[] authors)
   {
      this.authors = authors;
   }

   // ...
}

I changed the previous author field to an authors field. Also, I changed its type from String to the String[] array type.

I modified the Book(String title, int pubYear, String author) constructor to call the Book(String title, int pubYear, String[] authors) constructor. Because that constructor's third parameter is of type String[], the this() call converts its String author argument to a single-element array consisting of itself via new String[] { author }. The new operator is used to create a single-element String array and assign the author string reference to this element.

The original getAuthor() method accesses the first element in the authors array. I modified the original setAuthor() method to convert its single String argument to a single-element String array, then call setAuthors() with this array as the argument.

Finally, I added new getAuthors() and setAuthors() methods to return the authors array reference or assign a new reference to authors.

You can create a new Book object that includes multiple author names by calling the new constructor. You can then obtain only the first name via getAuthor() or obtain all names via getAuthors():

Book book = new Book("Grimms' Fairy Tales", 1812, 
                     new String[] { "Jacob Grimm", "Wilhelm Grimm" });
System.out.println(book.getTitle()); // Output: Grimms' Fairy Tales
System.out.println(book.getPubYear()); // Output: 1812
System.out.println(book.getAuthor()); // Output: Jacob Grimm
String[] authors = book.getAuthors();
for (int i = 0; i < authors.length; i++)
   System.out.println(authors[i]); // Output: Jacob Grimm Wilhelm Grimm (on successive lines)

If the original author field wasn't marked private, you wouldn't be able to expand Book this way. Furthermore, external code would be able to directly access this field (e.g., book.author) and would break when you changed the field name.

Hiding constructors or methods

As well as hiding fields, you might need to hide constructors or methods. You typically hide a constructor to prevent a utility class from being instantiated. You typically hide a method when it only exists to service another method (or a constructor). Such a method is known as a helper method. Below is a short example:

// compute permutation of n as n! / (n - r)!.

public long perm(long n)
{
   return fact(n) / fact(n - r);
}

// fact() is hidden because it has no use outside
// of the class that computes the permutation.

private long fact(long n)
{
   // code that computes and returns n!
}

In conclusion

As you've learned in this article, an object-based application encapsulates state and behaviors inside classes and objects. In contrast, an object-oriented application also supports inheritance. My next article will focus on programming with inheritance. In the meantime, if you want to go a little further with fields and methods, download the free primer.

1 2 3 Page 3
Page 3 of 3