1. "Hello, World!"
  2. Variables and Types
  3. Arrays
  4. While, If, For
  5. ...Problem Set 0
  6. Static Methods
  7. Static Fields
  8. String Conversion
  9. Objects
  10. Threading
  11. Strings
  12. ...Problem Set 1.5
  13. Packages
  14. Complex Numbers
  15. Abstract classes
  16. Interfaces
  17. Autoboxing
  18. ...Problem Set 1
  19. enum
  20. Inner Classes
  21. Polymorphism
  22. Tanks!
  23. Callbacks
  24. Exceptions
  25. File I/O
  26. ...Problem Set 2
  27. Regular Expressions

Objects

Monster.java
1public class Monster {
2    String name;
3    int damage;
4 
5    // A constructor
6    public Monster(String n,int d) {
7        name = n;
8        damage = d;
9    }
10    // Another constructor
11    public Monster(String n) {
12        name = n;
13        damage = 9; // default
14    }
15 
16    public int getDamage() { return damage; }
17    public String getName() { return name; }
18 
19    public String toString() {
20        return "{name:"+name+", damage:"+damage+"}";
21    }
22 
23    public static void main(String[] args) {
24        Monster m1 = new Monster("Ogre",18);
25        Monster m2 = new Monster("Basilisk",4);
26        System.out.println("m1="+m1+", m2="+m2);
27        m2.damage = 35;
28        System.out.println("m1="+m1+", m2="+m2);
29    }
30}
$ javac Monster.java
$ java Monster
m1={name:Ogre, damage:18}, m2={name:Basilisk, damage:4}
m1={name:Ogre, damage:18}, m2={name:Basilisk, damage:35}
  1. The fields "damage and "name" do not have keyword static. This means that each instance of a "Monster" object has its own copies of these values.
  2. The method toString() is not static. It accesses the the data of "name", and "damage" of a specific object. The toString() method is special in that it is what java uses to convert an object to a string.
  3. Note that there are two special methods above that have no return types. These are called constructors, and are used to initialize fields on an object. These methods are invoked in a special way (see lines 24 and 25) along with the "new" operator.
  4. Terminology: On lines 24 and 25 we "instantiate" (synonym for create) new objects. The variables m1 and m2 "contain instances" of object type Monster.
  5. On line 27 we assign a new value of damage for m2.
Regenerator.java
1public class Regenerator extends Monster {
2    int healRate;
3 
4    public Regenerator(String name,int damage,int healRate) {
5        super(name,damage);
6        this.healRate = healRate;
7    }
8 
9    public String toString() {
10        return "{name:"+name+", damage:"+damage+", heal:"+healRate+"}";
11    }
12 
13    public static void main(String[] args) {
14 
15        // Create an array of monsters
16        Monster[] monsters= new Monster[]{
17            new Monster("Ogre",18),
18            new Monster("Basilisk",4),
19            new Regenerator("Troll",10,3)};
20 
21        for(int i=0;i<monsters.length;i++) {
22            Monster m = monsters[i];
23            if(m.getDamage() > 8) {
24                System.out.print(m);
25                System.out.println();
26            }
27        }
28    }
29}
$ javac Regenerator.java
$ java Regenerator
{name:Ogre, damage:18}
{name:Troll, damage:10, heal:3}

This is an example of what is called inheritance. Briefly, the idea is that we create a new class from an older one by adding and changing a few things. In this case we added a new field called "healRate" and modified the "toString()" method to print it out.

Terminology: We say that the type "Regenerator" subclasses or extends Monster. Alternatively, Monster is the superclass of Regenerator.

Because a "Regenerator" is a type of "Monster" it is okay to assign a Regenerator to a variable of type Monster. We can't do it the other way, because a Monster might not be a Regenerator.

On line 6 we used keyword "this". Keyword this is a variable with the same type as the class being defined. In line 6 "this.healRate" refers to the variable named "healRate" that is a member of the Regenerator class. Without the "this." in front, it refers to the argument named "healRate" defined on line 4. Hopefully that's not too confusing.

Regenerator2.java
1public class Regenerator2 {
2 
3    public static void main(String[] args) {
4 
5        // Create an array of monsters
6        Monster[] monsters= new Monster[4];
7        monsters[0] = new Monster("Ogre",18);
8        monsters[1] = new Monster("Basilisk",4);
9        monsters[2] = new Regenerator("Troll",10,3);
10 
11        for(int i=0;i<monsters.length;i++) {
12            Monster m = monsters[i];
13            if(m.getDamage() > 8) {
14                System.out.print(m);
15                System.out.println();
16            }
17        }
18    }
19}
$ javac Regenerator2.java
$ java Regenerator2
{name:Ogre, damage:18}
{name:Troll, damage:10, heal:3}
Exception in thread "main" java.lang.NullPointerException
	at Regenerator2.main(Regenerator2.java:13)

We get an error called a NullPointerException on line 13. What happened?

You may have spotted it. We declared the array to be of size 4, but only initialized the first 3 values. The value "null" means something like uninitialized, and it is a special value that any object (as distinguished from primitive types) can have.

Regenerator3.java
1public class Regenerator3 {
2 
3    public static void main(String[] args) {
4 
5        // Create an array of monsters
6        Monster[] monsters= new Monster[4];
7        monsters[0] = new Monster("Ogre",18);
8        monsters[1] = new Monster("Basilisk",4);
9        monsters[2] = new Regenerator("Troll",10,3);
10 
11        for(int i=0;i<monsters.length;i++) {
12            Monster m = monsters[i];
13            if(m == null)
14                continue;
15            if(m.getDamage() > 8) {
16                System.out.print(m);
17                System.out.println();
18            }
19        }
20    }
21}
$ javac Regenerator3.java
$ java Regenerator3
{name:Ogre, damage:18}
{name:Troll, damage:10, heal:3}

Here we test to see if p is equal to null, avoiding the error.

Regenerator4.java
1public class Regenerator4 {
2 
3    public static void main(String[] args) {
4 
5        // Create an array of monsters
6        Monster[] monsters= new Monster[4];
7        monsters[0] = new Monster("Ogre",18);
8        monsters[1] = new Monster("Basilisk",4);
9        monsters[2] = new Regenerator("Troll",10,3);
10 
11        for(int i=0;i<monsters.length;i++) {
12            Monster m = monsters[i];
13            if(m == null)
14                continue;
15            if(m instanceof Regenerator) {
16                Regenerator r = (Regenerator)m;
17                System.out.println(r.name+" "+r.healRate);
18            } else if(m.damage > 10) {
19                System.out.println(m.name+" is tough!");
20            }
21            if(m.getDamage() > 8) {
22                System.out.print(m);
23                System.out.println();
24            }
25        }
26    }
27}
$ javac Regenerator4.java
$ java Regenerator4
Ogre is tough!
{name:Ogre, damage:18}
Troll 3
{name:Troll, damage:10, heal:3}
  1. The "instanceof" operator is a tool we can use to determine whether a given Monster is a Regeneratorr. Only one item in our array matches.
  2. On line 16 we use a "cast" (the word Regenerator in parenthesis) to convert the type of the variable from Monster to a Regenerator.
Cast.java
1public class Cast {
2   public static void main(String[] args) {
3       Monster m = new Monster("Orc",9);
4       Regenerator r = (Regenerator)m;
5   }
6}
$ javac Cast.java
$ java Cast
Exception in thread "main" java.lang.ClassCastException: Monster cannot be cast to Regenerator
	at Cast.main(Cast.java:4)

The above code illustrates the usefulness of the "instanceof" check. Since an orc is not a regenerator, the cast has to fail.

Cast2.java
1public class Cast2 {
2   public static void main(String[] args) {
3       float f = (float)9.8;
4       int pi = (int)3.14;
5       char c = (char)65;
6       System.out.println("c="+c+" f="+f+" pi="+pi);
7   }
8}
$ javac Cast2.java
$ java Cast2
c=A f=9.8 pi=3

Casting can also perform certain type conversions on primitive types.

Problems:

  1. Create a subclass of Regenerator called Respawner. The Respawner is just like a regular Regenerator, but has an additional field called "resurrectionCount". This field should be initialized in the constructor and displayed by the toString() method.
  2. Create a subclass of Monster called Boss. The Boss should have an additional argument to its constructor that is an array of Monsters that it controls. The toString() method for Boss should display a count of the monsters it manages.