BM214E Object Oriented Programming Lecture 8
Instance vs. Class Declarations
Instance vs. Class Declarations Don t be fooled. Just because a variable might be declared as a field within a class that does *not* imply that it is a class variable. It could be: 1. a class variable, or 2. an instance variable! Likewise, each method is indeed declared within a class, but that does *not* imply that it is a class method. It could be: 1. a class method, or 2. an instance method. Class variable and class method both mean something very specific.
Instance vs. Class Declarations A distinction that applies to both variables and methods An instance <variable or method> is one that belongs to each object of a class. A class <variable or method> is one that belongs only to the class itself. (N.B. It will be accessible to the objects) The keyword static: indicates a class variable or class method. absence of the keyword static indicates an instance variable or instance method.
Instance vs. Class Variables Suppose we wanted to track the total number of objects created. Consider: class Human { String name; int population = 0; public Human (String name) { this.name = name; population++; //WRONG! } // of constructor Declares a name String for each instance. Thus, each Human will have its own name. But... Also declares a population counter for each instance of Human. } // of Human Thus, each Human will have its own population variable, each having a value of 1. This makes no sense!
Instance vs. Class Variables class Human { String name; static int population = 0; public Human (String name) { this.name = name; population++; } // of constructor } // of Human One change! NOTE: Each Human does not get a population counter. This declares a single population counter for the class Human itself. It is a class variable. Thus, each Human will increment this single shared counter by 1. Each instance will still have a String name Thus, each Human will have its own name.
Instance vs. Class Variables: When to Use Use instance variables whenever each object should have its own variable. E.g., attributes of the particular object. name, ssn, age, weight Use a class variable whenever the class itself should maintain a single copy of information pertaining to all instances of the class. E.g., population counts assigning lot numbers summary data shared resources.
Instance vs. Class Variables We now see the reason behind declaring most constants as static as well as final. Constants Revisited: class ConstantExample { final int imaxsize = 10; } // of ConstantExample Declares a different-butidentical constant for each instance of the class. Wasteful with zero benefit. class ConstantExample { static final int imaxsize = 10; } // of ConstantExample Declares a single constant for use by all instances of the class.
Quiz Yourself Consider why the following code will not compile: public class Test { public void sayhello() { } System.out.println ( Hello ); } public static void main (String arg[]){ } sayhello();
The error produced is: Classes and Objects Can't make static reference to method void sayhello() in class test. Why? The answer lies in the difference between objects and classes. As noted in previous slides, classes are composed of member objects, primitives and methods. When properly designed, classes provide state and behavior for objects. As an OO language, Java emphasizes the use of objects to manipulate data. Thus, we must make instances of a class in order to drive a program.
Another Perspective public class Test { t1 public void sayhello() { public class Test { t2 System.out.println ( Hello ); } public void sayhello() { public static public System.out.println void class main Test (String { ( Hello ); arg[]) { sayhello(); } public // void WRONG! sayhello() { } public static System.out.println void main (String ( Hello ); arg[]) { }//class Test sayhello(); } // WRONG! } public static void main (String arg[]) { }//class } Test Test sayhello(); // WRONG! } } }//class Test t3 When we create the class Test, there are potentially an infinite number of instances available. Test t1 = new Test(); Test t2 = new Test(); // etc.
t1 public class Test { public void sayhello() { t2 public System.out.println class Test { ( Hello ); } public void sayhello() { public static System.out.println void main (String ( Hello ); arg[]) { sayhello(); } // WRONG! } public static void main (String arg[]) { }//class Test sayhello(); // WRONG! } }//class Test So, when Java encounters our class, it sees: * a potentially infinite number of Test instances, each with their own behavior (sayhello() ), and * only one (static) main shared between all the classes
public class Test { public void sayhello() { System.out.println ( Hello ); } public static void main (String arg[]) { sayhello(); // WRONG! } }//class Test Potentially many Only one Thus, the error of making a static reference to a method might be understood as a problem of ambiguity. We have not told Java which of the many possible instances of class Test we intend to call sayhello() on.
Class vs. Instance Members The class or static members are defined for all instances. Every instance has access to static members, and can change them for all other members. CLASS INSTANCES class test { public void sayhello(){ // etc. etc } class test { public void sayhello(){ // etc. etc. } class test { public void sayhello(){ // etc. etc. } class test { public void sayhello(){ // etc. etc. } public static void main (String[] argv) { SHARED BY ALL } }//class test }//class test }//class test }
Solution I One merely needs to resolve this ambiguity to correct the program. Thus, just make an instance of the class, and reference/invoke the object s members. public class Test { public void sayhello() { System.out.println ( Hello ); } public static void main (String arg[]) { Test t = new Test(); t.sayhello(); // Corrected } }//class Test
OR
Solution 2 One merely needs to eliminate this ambiguity to correct the program. Thus, just make the method static. public class Test { public static void sayhello() { System.out.println ( Hello ); } public static void main (String arg[]) { sayhello(); // Corrected } }//class Test
Do you follow this? public class Ouch { int w = 0; static int x = 0; final int y = 0; final static int z = 0; public static void main(string [] args) { Ouch one = new Ouch(); Ouch two = new Ouch(); Ouch three = new Ouch(); // see questions } // main } // Ouch How many w s are in memory? How many x s? How many y s? How many z s? Are any constant?
Little secrets Class methods and class variables exist and can be used even when *no* objects have been instantiated! Instance methods and instance variables do not exist on their own, but only as part of an instance. Objects can be used to access both class and instance members. Classes can be used to access only class members!
Questions?
Strings vs. Objects Every String is an instance of Java s built-in class String. Thus, Strings are objects. Java provides extra support for Strings as a convenience because of the frequency with which Strings are used. (That s why they can seem like primitives but they are not.) Three obvious String support features: 1. You need not explicitly instantiate Strings with the new keyword. Java automatically instantiates a String object when it encounters a text string within double-quotes. For example...
Strings vs. Objects Assignment w/references to Strings/Objects: Code: Memory: String str1; Box box1; str1 = Hello World ; box1 = iln, iwd, iht; str1 box1 str1 Hello World box1 ERROR: must use new new and call constructor str1 = Hello World ; box1 = new Box(iLn, iwd, iht); str1 box1 Hello World iln, iwd, iht str2 = new String(); str2?default? str2 = Glib Folksies ; str2?default? Glib Folksies
Strings vs. Objects Again, Java automatically creates a new String object whenever it encounters text within double-quote marks. Thus, the code: String str1 = Hello World ; accomplishes three things: 1. It creates str1 as a reference to a String. 2. It creates an instance of a String. 3. It initializes that String to Hello World. This is inconsistent with how Java treats standard objects. With standard objects, you must explicitly: instantiate (via new), and initialize (via a constructor).
String Stuff Three obvious String support features: 1. You need not explicitly instantiate Strings. 2. The + operater overloaded for Strings, to support concatenation, e.g., System.out.println( This string is an example of + one that is too long to fit on one line. Your TAs take off points + for lines that exceed 80 column characters. ); 3. Several predefined methods provided in built-in class String. Among them are: length( ) // a string knows its length note it is a method! charat(iindex) // returns letter at position iindex; 1st char is position 0 substring(istartindex) // returns the substring fromposition // istartindex to end of string substring(istartindex, iendindex) // returns substring from position // istartindex until but NOT INCLUDING position iendindex continued
String Stuff -- Examples String strexample = Hello ; 0 1 2 3 4 H e l l o char c = strexample.charat(1); // c gets e String strbritishhowdy = strexample.substring(1); strbritishhowdy ---> ello String strtemperatureoutside = strexample.substring(0, 4); strtemperatureoutside --> Hell
Strings vs. Objects Also... One cannot change contents of a String object. (We say: Strings are immutable.) You may think you are modifying a String. But, what happens in memory is: 1. a new String is created 2. you change the String reference to refer to that new one 3. your old String may be garbage collected ; you no longer have a reference to the old String For example: String str1 = Hello World str1 = str1.substring(4) str1 str1 Hello World Hello World o World Optional: see class StringBuffer for ways to work around this limitation.
Objects and References: Review of the normal case Remember the general pattern of our previous example: box1 = new Box(1, 2, 3); box2 = new Box(8, 5, 7); box1 = box2; System.out.println(box1 == box2); // prints true // Does box1 reference the same object that box2 references? System.out.println(box1.equals(box2)); // prints true // Does box1 reference an object that has the same contents // as the object referenced by box2? memory box1 L=1, W=2, H=3 box2 L=8, W=5, H=7
Equality with References to Objects: Strings are special As part of the Java Language Specification (the rules for the Java Language), Strings have a special characteristic. It turns out that in some circumstance (BUT NOT ALL), you can use == to compare Strings, in addition to.equals(). Consider: String strhello1 = Hello ; String strhello2 = Hello ; We would expect these lines to produce the following memory changes strhello1 strhello2 Hello Hello In fact, it produces the following results in memory: strhello1 strhello2 Hello
Huh? Why are Strings treated differently? Strings are sometimes a special case of equivalency in Java. When the compiler encounters the lines: String strhello1 = Hello ; String strhello2 = Hello ; the compiler is smart enough to know that the two Strings are identical. So, it decides it will save a few bytes of memory and point to the same location in memory. The same result would occur even if you wrote: String strhello2 = Hell + o ; This means that for the above lines of code, equals() and == both work: System.out.println (strhello1.equals(strhello2)); // true System.out.println (strhello1 == strhello2); // also true, but // dangerous...
Exception to the exception with String But this special case for == comparison of Strings DOES NOT ALWAYS WORK... Consider: If one of the Strings were created with use of the new keyword, the two Strings would no longer share memory. (That is, == would be false, but equals() would still be true, if the contents of the Strings are the same.) So, there s an exception to the exception for Strings when you don t use the String exception to object instantiation. Confusing? Exceptionally so! LESSON: DON T USE THE EXCEPTION. Don t compare Strings, or any Object, with ==, even if you think the exception will apply. REMEMBER: Just compare primitives with ==, Objects, including Strings, with equals() -- and it always works fine!
tostring() or not to String... Debugging:
Incremental Programming: Debugging Strategies The Idea: Find and repair bugs in the small before you have a program with several components. The hardest thing is finding the errors. So, find them as you create each class. Thus, do not: write your entire program, then type in your entire program, then attempt to test your entire program Instead: design your program at high level, then focus on one class at a time, for each class, write and test before going on to the next one. This way, you deal with bugs when it s easiest!
Debugging Strategies Potential problems with state objects: State incorrectly modified by modifer methods. State incorrectly represented by accessor methods. Need: A way to see the state of the object. Means: public String tostring ( ) Create one tostring method per class. Now we can use a reference to an object directly as an argument in: System.out.println ( );
Debugging Strategies Example of using tostring: If we have a method for Class Box: public String tostring ( ) { String returnstr; returnstr = new String( Box: length = + ilength +, Width = + iwidth + height = + iheight); return (returnstr); } // of tostring Then we can do: Box subwooferbox = new Box(40, 50, 60); System.out.println ( subwooferbox ); // what???????? // wait a minute where was the call to tostring????
Special invocation of tostring method Using an object reference when a String is needed will implicitly invoke the object s tostring method. Bonus! This is why it is critical that you actually provide a meaningful tostring method for practically every class you create. Excellent for debugging!
Debugging Strategies One main per Class According to Java, you only need one main per program... not one per class. But Java doesn t know how to program! To test/debug a class, create a main method for the class as part of the class... Include in test mains the: declaration of variables the invocation of methods the generation of output (e.g. using tostring()) that will allow you to see what the class actually does! Then, invoke the main as part of your testing process. The main can stay there as part of your class... invoked only when you call it for purpose of testing.
What? Suppose you have class A, class B, and class C; each has a main method for debugging purposes. Class C has the real main for your overall program, and uses classes A & B. How can you invoke a particular main? javac A.java java A javac B.java java B javac C.java java C <- compile class A <- invokes A s main for testing A <- compile class B <- invokes B s main for testing B < compile class C <- invokes C s main
Debugging Strategies Define a boolean constant in every class called DEBUG public static final boolean DEBUG = true; Wherever appropriate insert... if(debug) System.out.println("method>var = "+var); Customize as necessary!
Questions?