UP | HOME

第二课 Java 对象与构造函数

Table of Contents

1 OBJECTS AND CONSTRUCTORS

String s;           // Step 1:      declare a String variable.
s = new String();   // Steps 2, 3:  construct new empty String; assign it to s.
At this point, s is a variable that           ---     ------
references an "empty" String, i.e.          s |.+---->|    |
a String containing zero characters.          ---     ------
String s = new String();  // Steps 1, 2, 3 combined.

s = "Yow!";               // Construct a new String; make s a reference to it.
  ---     ----------
s |.+---->|  Yow!  |
  ---     ----------
String s2 = s;            // Copy the reference stored in s into s2.
  ---     ----------     ---
s |.+---->|  Yow!  |<----+.| s2
  ---     ----------     ---

Now s and s2 reference the same object.

s2 = new String(s);       // Construct a copy of object; store reference in s2.
  ---     ----------       ---     ----------
s |.+---->|  Yow!  |    s2 |.+---->|  Yow!  |
  ---     ----------       ---     ----------

Now they refer to two different, but identical, objects.

Think about that. When Java executes that line, it does the following things, in the following order.

  • Java looks inside the variable s to see where it's pointing.
  • Java follows the pointer to the String object.
  • Java reads the characters stored in that String object.
  • Java creates a new String object that stores a copy of those characters.
  • Java stores a reference to the new String object in s2.

We've seen three String constructors:

  1. new String() constructs an empty_string–it's a string, but it contains zero characters.
  2. "Yow!" constructs a string containing the characters Yow!.
  3. new String(s) takes a parameter s. Then it makes a copy of the object that s references.

Constructors always have the same name as their class, except the special constructor "stuffinquotes". That's the only exception.

Observe that "new String()" can take no parameters, or one parameter. These are two different constructors–one that is called by "new String()", and one that is called by "new String(s)". (Actually, there are many more than two–check out the online Java API to see all the possibilities.)

2 METHODS

Let's look at some methods that aren't constructors.

s2 = s.toUppercase();       // Create a string like s, but in all upper case.
   ---     ----------
s2 |.+---->|  YOW!  |
   ---     ----------
String s3 = s2.concat("!!");             // Also written:  s3 = s2 + "!!";
   ---     ------------
s3 |.+---->|  YOW!!!  |
   ---     ------------
String s4 = "*".concat(s2).concat("*");  // Also written:  s4 = "*" + s + "*";
   ---     ------------
s4 |.+---->|  *YOW!*  |
   ---     ------------

Now, here's an important fact: when Java executed the line

s2 = s.toUppercase();

the String object "Yow!" did not change. Instead, s2 itself changed to reference a new object. Java wrote a new "pointer" into the variable s2, so now s2 points to a different object than it did before.

Unlike in C, in Java Strings are immutable–once they've been constructed, their contents never change. If you want to change a String object, you've got to create a brand new String object that reflects the changes you want. This is not true of all objects; most Java objects let you change their contents.

You might find it confusing that methods like "toUppercase" and "concat" return newly created String objects, though they are not constructors. The trick is that those methods calls constructors internally, and return the newly constructed Strings.

2.1 I/O Classes and Objects in Java

Here are some objects in the System class for interacting with a user:

  • System.out is a PrintStream object that outputs to the screen.
  • System.in is an InputStream object that reads from the keyboard.
  • [Reminder: this is shorthand for "System.in is a variable that references an InputStream object."]

But System.in doesn't have methods to read a line directly. There is a method called readLine that does, but it is defined on BufferedReader objects.

  • How do we construct a BufferedReader? One way is with an InputStreamReader.
  • How do we construct an InputStreamReader? We need an InputStream.
  • How do we construct an InputStream? System.in is one.

(You can figure all of this out by looking at the constructors in the online Java libraries API–specifically, in the java.io library.)

Why all this fuss?

InputStream objects (like System.in) read raw data from some source (like the keyboard), but don't format the data.

InputStreamReader objects compose the raw data into characters (which are typically two bytes long in Java).

BufferedReader objects compose the characters into entire lines of text.

Why are these tasks divided among three different classes? So that any one task can be reimplemented (say, for improved speed) without changing the other two.

Here's a complete Java program that reads a line from the keyboard and prints it on the screen.

import java.io.*;

class SimpleIO {
  public static void main(String[] arg) throws Exception {
    BufferedReader keybd =
          new BufferedReader(new InputStreamReader(System.in));
    System.out.println(keybd.readLine());
  }
}

Don't worry if you don't understand the first three lines; we'll learn the underlying ideas eventually. The first line is present because to use the Java libraries, other than java.lang, you need to "import" them. java.io includes the InputStreamReader and BufferedReader classes.

The second line just gives the program a name, "SimpleIO".

The third line is present because any Java program always begins execution at a method named "main", which is usually defined more or less as above. When you write a Java program, just copy the line of code, and plan to understand it a few weeks from now.

2.2 Classes for Web Access

Let's say we want to read a line of text from the White House Web page. (The line will be HTML, which looks ugly. You don't need to understand HTML.)

  • How to read a line of text? With readLine on BufferedReader.
  • How to create a BufferedReader? With an InputStreamReader.
  • How to create a InputStreamReader? With an InputStream.
  • How to create an InputStream? With a URL.
import java.net.*;
import java.io.*;

class WHWWW {
  public static void main(String[] arg) throws Exception {
    URL u = new URL("http://www.whitehouse.gov/");
    InputStream ins = u.openStream();
    InputStreamReader isr = new InputStreamReader(ins);
    BufferedReader whiteHouse = new BufferedReader(isr);
    System.out.println(whiteHouse.readLine());
  }
}

2.3 Postscript: Object-Oriented Terminology (not examinable)

In the words of Turing Award winner Nicklaus Wirth, "Object-oriented programming (OOP) solidly rests on the principles and concepts of traditional procedural programming. OOP has not added a single novel concept … along with the OOP paradigm came an entirely new terminology with the purpose of mystifying the roots of OOP." Here's a translation guide.

Procedural Programming Object-Oriented Programming
record / structure object
record type class
extending a type declaring a subclass
procedure method
procedure call sending a message to the method [ack! phthhht!]

I won't ever talk about "sending a message " in this class. I think it's a completely misleading metaphor. In computer science, message -passing normally implies asynchrony: that is, the process that sends a message can continue executing while the receiving process receives the message and acts on it. But that's NOT what it means in object-oriented programming: when a Java method "sends a message " to another method, the former method is frozen until the latter methods completes execution, just like with procedure calls in most languages. But you should probably know that this termology exists, much as it sucks, because you'll probably run into it sooner or later.