Learn Programming in Java

[Preliminary, this needs work]

<<Previous | ToC | Next >>

Lesson #6: Classes & Objects

Java is called an object-oriented programming (OOP) language because the main feature its promoters (myself excepted) like to brag about is the encapsulation of code into classes, which are instantiated into objects, and the only way to run your code is by reference to those objects. That's helpful when you have large teams of anarchistic programmers (and all of us are ;-) working on the same project and you are trying to manage its orderly development. For small or single-person projects, OOPS mostly just gets in the way and slows you down. That is a good thing, because computer bugs happen most often from failing to think through the problem domain, so thinking slower is much better. Anyway, all the small programs have already been written, so if you get into this profession, you must work on teams, and the encapsulation feature of OOPS helps to minimize (but not completely eliminate, as we learned in our AVP efforts) the friction between team members.

Another claimed benefit of OOPS is code re-use, but as you already know, you can do that without even knowing about OOPS, and often OOPS programs are bigger and slower than non-OOPS programs that do the same thing. The good that OOPS does for you is slow you down (because many problems you want to program do not fit naturally into an OOPS structure, requiring extra thinking to figure out how to do it that way) so that you think more clearly about what you are trying to do, and that better thinking does help you to notice opportunities for code re-use that you should have seen anyway. You will actually make faster progress (fewer bugs, so less time debugging) if you spend more time up-front thinking about what you are doing. Embrace the slow-down, it is your friend.

Consequently, almost the whole back half of this course is spent learning how to do things in OOPS. Today I want to just open the door a crack so you can figure it out on your own using public resources (including this website).

When you opened the StartHere program in BlueJ, I encouraged you to ignore everything except the one line marked "REPLACE THIS LINE". Now we will look outside that space at the rest of the program. Everything in Java is in classes, and the StartHere program has two classes, one of them named "Hello" where we did all our programming, and the other named "Zystem" which I sort of left as a black box. We also used a built-in class "System" for output to the console window and (indirectly, through Zystem) input from it. There are a zillion Java library classes that do all kinds of interesting and useful stuff. When you read through other people's Java code -- which I strongly encourage you to do -- you will see that they "import" numerous classes to use the methods defined within those classes.

Most of the time, the methods associated with a particular class work on private data defined in that class. The elements of that private data are called instance variables and they look just like the local variables we used in our own code, except they are defined outside the methods and visible to all the methods in the class -- or at least the methods that occur later in the code than the variable declarations. For example, you might want to display something in a window on the screen, so you'd use a class named something like Window to do the work for you. Inside the class, not visible to your code, are all the things a window needs to manage in order to be seen on the screen, such as its size and location, and what else is visible on it, plus the window frame and title (if any), and so on. There are methods to access that information in a well-defined way, so you can't crash the Java run-time environment or (worse) the whole computer system.

When you define your own class in Java, you get to specify the data elements -- which should all be "private" -- and the methods to operate on those elements. Some methods are "public", that is, they are visible to (and can be called by) users of your class; other methods -- if the users cannot do useful things with them -- should be private. When we did the two subroutines for the TTT game, they probably should have been private, but then I would have needed to explain that before its time.

I mentioned that some kinds of data do not fit into the class structure at all -- the built-in library class Math is one of them -- and others are clumsy at best as objects -- the built-in library class String is an example. Methods that do not make sense applied to objects (like the magical method "main" and the two TTT subroutines we did in the previous lesson) can be qualified as "static" and then they work as if they are ordinary subroutines unrelated to the class structure -- except that you need to use the class name to call them from outside the class. Class String and others that have been force-fit into a class structure where it makes little or no sense, well, you just need to work around the goofy APIs (for "Application Programming Interfaces," which is what we now call what used to be simply "subroutine libraries"). There are worse things in the world, for example trying to live in the region that used to be Iraq. A professional programmer works with the tools available, and despite all its remaining flaws, Java is a far better language to be working in than C.

OOPS helps you to think about your data and what kinds of things should be done to it -- or rather, OOPS makes it harder to not think about those important topics. It's still possible to evade that responsibility, but you should resist the temptation. This is more or less how you go about that important task...

There are two parts to this analysis: what is the data, and what your program needs to do to the data. If the data has many numbers or other parts to a single entity, then you call that entity an object (defined by a class) and define those numbers and/or other components to be instance variables in that class. For example, we might define a "class Person" in a database for an organization like a school or office workplace, with all the things that we need to know about that person, like name and birthdate and what they do and maybe a picture -- which itself is an object with height and width and perhaps a list of drawing commands, or  else the raw pixels (for an example of a Picture class, see Cay Horstmann's "Simple Java Graphics"). The methods of this class are all the things you might want to do with this object (Person), such as change values if they are found to be in error or the person changes job or the student graduated to the next grade, stuff like that, perhaps like this:

public class Person {
  private String name;
  private int birthDate;
  private String whatTheyDo;
  private Picture seeEm;

  public void setName(String nuname) {
    name = nuname;
  } end of setName

  /// other accessor methods TBD

  public Person() {} // constructor (use default)
} // end of class Person

Traditionally Java class names are capitalized, while variables and method names start with lower case (but if a name should be read as two more English words, the additional words may be capitaliized or set off with underscores. After the class is defined, Person can be used as a data type just like int or String or boolean. So let's say you declare a variable mary of type Person, and you initialize it, either by getting her out of the database, or else by creating a new object, thus:

Person mary = new Person;

If the class describes students in school, one of the methods might be to "public void graduate(int tograde)" so when it's time for Mary to graduate to 8th grade, your program would call "mary.graduate(8);" mary is a variable known to be type Person, and graduate is a method of class Person, so the compiler knows how to do what you just told it. Remember, mary is only the name of a variable, not the actual living breathing person (Mary) you know and talk to. It is the name of a place in memory that is initially an undefined value. If you called a method mary.setName("Bill"); Java would have no trouble putting all wrong values in there. The computer does exactly what you tell it to do, even if you didn't want to tell it to do that. As we get farther into this, you will get some experience setting up and using your own classes.

Suppose you aren't ready to create a new object of type Person. Maybe you want to read objects off the database looking for every person whose birthday is tomorrow, and what if you didn't find any? There is a magical value that any object variable can have, sort of like zero, but it's spelled "null". If the variable mary is null, then there is no object to find the setName method in, which is an error. Do that and your program will crash (fail) -- except there's a way to recover from those kinds of runtime errors that we will cover in the next lesson. Generally you should protect yourself against that kind of error:

if (mary != null) mary.setName("Bill");

It is the nature of the OOPS way of doing things that you need classes for everything (usually except numbers). So the database you have all these students in is itself a class, and the school is a separate class. Some of these will be defined as APIs in the class library, others you need to invent yourself. This is the hard part, but you can always go back and add more classes and more instance variables and more methods. God got His creation perfect in six days, the rest of us need to work at it and rework it and keep on working it when (not if) it doesn't behave properly. Hmm, I guess God did that too.

It's the nature of creativity, and the modern industry word for it is "agile," where you build a minimal version of what you have in mind, then get feedback from users, then improve it. It's not really Darwinian, because you are purposefully designing both the initial idea (which didn't evolve out of nothing), and also each incremental iteration. That's an important difference between the American approach to engineering -- the idea springs fully-formed (or at least half-baked) from the mind of the designer -- and the Japanese model, where they take an existing product that works and incrementally make it better. Anybody can improve on things, but the Japanese do it well; on the other hand, you are in the US of A, we create new ideas that didn't exist before. We are the leaders, they are the followers. The Japanese do agile and do it well; we pretend to do agile, while actually inventing new stuff that didn't increment from anything. Don't tell the agile folks I said that, they prefer to believe that they didn't invent anything, it just evolved, but they're wrong, they're Americans, and they (the successful ones) did invent new stuff, then improved it incrementally. "Go thou and do likewise." But that's next week.

For the rest of today we will be using existing library classes and not a little magic* to do something interesting.

[I gotta rework Chomp as a Java example here, probly intro it earlier up-page]
[The original idea was to have them build a window with some widgets, so clicks do something, but preliminary research suggests that this may be too steep for this stage in their learning... Better: Chomp already does a window with widgets, so they get to look at it and make small changes]

Next: Extras

<<Previous | ToC | Next >>

Revised: 2019 December 27

* Magic is a technical term, it means "Happens by means unspecified."