Computer Science 15-100 (APEA Section E), Summer 2008
Class Notes:  Ch 3:  Using Classes and Objects (1 of 2)


Logistics

  1. Schedule
    1. Quizzes
      1. Quiz 2 today  [covers mainly Ch 2]
      2. Quiz 3 Friday [covers mainly Ch 3]
    2. Hws
      1. Hw3 due Thursday
         
  2. Reading:
    1. L&L Chapter 3:  Using Classes and Objects
      Sections 3.1 - 3.5 (today)
      Sections 3.6 - 3.8 (next lecture)

Topic Outline:

  • Ch 3:  Using Classes and Objects
     

    1. Using Objects
      We already do this!
         Polygon p1 = new Polygon();
         p1.addPoint(10,10);
         p1.addPoint(40,80);
         p1.addPoint(70,10);
      
         Polygon p2 = new Polygon();
         p2.addPoint(10,110);
         p2.addPoint(40,180);
         p2.addPoint(70,110);
      
         page.setColor(Color.blue);
         page.fillPolygon(p1);
      
         page.setColor(Color.red);
         page.fillPolygon(p2);
    2. Aliases:  Two References to the Same Object
         Polygon p1 = new Polygon();
         p1.addPoint(10,10);
         p1.addPoint(40,80);
         p1.addPoint(70,10);
      
         Polygon p2 = new Polygon();
         p2.addPoint(10,110);
         p2.addPoint(40,180);
         p2.addPoint(70,110);
      
         Polygon p = p1;
         p.addPoint(40,30);
      
         page.setColor(Color.blue);
         page.fillPolygon(p1);
      
         page.setColor(Color.red);
         page.fillPolygon(p2);
    3. More on Aliases
         Polygon p1 = new Polygon();
         p1.addPoint(10,10);
         p1.addPoint(40,80);
         p1.addPoint(70,10);
      
         Polygon p2 = new Polygon();
         p2.addPoint(10,110);
         p2.addPoint(40,180);
         p2.addPoint(70,110);
      
         Polygon p = p1;
         p.addPoint(40,30);
      
         p = p2;
         p.addPoint(40,30);
      
         page.setColor(Color.blue);
         page.fillPolygon(p1);
      
         page.setColor(Color.red);
         page.fillPolygon(p2);
    4. The "==" operator

      a)  For integer values

      class MyCode {
        public static void main(String[] args) {
          int a = 2;
          int b = 2;
          System.out.println(a == b); // true
        }
      }

      b)  For floating point values

      We have already seen the dangers in using "==" with floating point values.  They are approximate, and "==" will not work right with them.  So:  do not use "==" with floating point values (that is, floats and doubles).

      c)  For Objects

      import java.io.File;
      class MyCode {
        public static void main(String[] args) {
          File a = new File("foo.txt");
          File b = new File("foo.txt");
          System.out.println(a == b);      // false
          System.out.println(a.equals(b)); // true
        }
      }

      Q:  Why not use the equals method on integer values (or other primitive data types)?

      A:  Because you cannot call ANY methods on primitive data types!  Try this:

      class MyCode {
        public static void main(String[] args) {
          int a = 2;
          int b = 2;
          System.out.println(a == b);
          System.out.println(a.equals(b));
        }
      }

      Error: int cannot be dereferenced.

      d)  For Weird Cases (Objects that are sometimes cached by the compiler)

      d.1.)  String literals are cached (because Strings are immutable):

      class MyCode {
        public static void main(String[] args) {
          String a = "foo";
          String b = "foo";
          System.out.println(a == b);      // true  <-- caching!
          System.out.println(a.equals(b)); // true
        }
      }

      d.2.)  Strings resulting from string operations are not cached

      class MyCode {
        public static void main(String[] args) {
          String a = "foo";
          String b = "" + a;
          System.out.println(a == b);      // false <-- no caching for String b
          System.out.println(a.equals(b)); // true
        }
      }

      Another example -- the "new" operator:

      class MyCode {
        public static void main(String[] args) {
          String a = "foo";
          String b = new String("foo");
          System.out.println(a == b);      // false <-- no caching for String b
          System.out.println(a.equals(b)); // true
        }
      }

      d.3.)  ... unless the string operation is performed at compile-time

      class MyCode {
        public static void main(String[] args) {
          String a = "foo";
          String b = "f" + "o" + "o";
          System.out.println(a == b);      // true <-- still caching!
          System.out.println(a.equals(b)); // true
        }
      }

      d.4.)  Though we won't study them until next lecture.... Integers (the wrapper class, as opposed to primitive integer types) are sometimes cached, too (for values in [-128, +127] that are autowrapped)

      e)  The Moral of the Story
      *
      Use "==" without worries for integers and booleans.
      *
      Do not use "==" with other types (unless you are certain it is appropriate, and then Be Careful!).
      *
      For doubles, use your own static "approximatelyEquals" method:  approximatelyEquals(d1,d2)
      * For objects, use the "equals" method:  obj1.equals(obj2)
       

    5. Garbage Collection
      Polygon p1;
      p1 = new Polygon();
      p1 = new Polygon(); // What happened to our first polygon?
    6. The String Class and String Methods

      See Figure 3.1 (p. 119), along with some additional methods:

      Method Description
      charAt Returns the character at the given index of this string, where the first index is 0 and the last index is at (length()-1).  For example:
            String s = "abcd";
            System.out.println(s.charAt(0)); // prints: a
            System.out.println(s.charAt(1)); // prints: b
            System.out.println(s.charAt(s.length()-1)); // prints: d
      A common mistake is to use length() rather than (length()-1) as the last index.  This is one type of off-by-one error.  For example:
            String s = "abcd";
            System.out.println(s.charAt(s.length())); // DOES NOT WORK -- off-by-one error!
      Compile and run this.  Read the error carefully.  It says StringIndexOutOfBoundsException, and even tells us that the offending index is 4 (whereas the largest legal index is 3 for the string "abcd").
      compareTo Note:  In Java, we would like to compare two strings the way we compare to numbers.  That is, something like this:
      Unfortunately, you cannot use relational operators (<, <=, >=, >) with strings, and while you can use equality operators (==, !=) with strings, you generally should not do so as they often do not work as expected.  Instead of these operators, you should use the compareTo method described here and the equals method described below.

      This method compares "this" string to a second string (which we will call "that" string). The comparison is lexicographic, which basically means that it works the way words are sorted in the dictionary.  Returns a negative number if "this < that" -- that is, if "this" string would occur in a dictionary prior to "that" string.  Returns 0 if "this" equals "that".  And returns a positive number if "this > that".  For example:
            System.out.println("abc".compareTo("def")); // prints -3, so "abc" < "def"
            System.out.println("abc".compareTo("abc")); // prints 0 , so "abc" equals "def"
            System.out.println("def".compareTo("abc")); // prints 3 , so "def" > "abc"

      The comparison uses Unicode values.  As Unicode 'Z' is less than Unicode 'a', we see that all uppercase letters occur before the lowercase letters:
            System.out.println("ABC".compareTo("abc")); // prints -32, so "ABC" < "abc"
            System.out.println("Z".compareTo("a"));     // prints -7 , so "Z"   < "a"


      Also, as Unicode '9' is less than Unicode 'A', we see that all digits occur before the uppercase or lowercase letters:
            System.out.println("123".compareTo("ABC")); // prints -16, so "123" < "ABC"

      To continue, we see that all whitespace characters occur before digits and uppercase or lowercase letters:
            System.out.println(" ".compareTo("123")); // prints -17, so " " < "123"

      Finally, we see that punctuation and other characters occur somewhat unpredictably:
            System.out.println("%".compareTo("123")); // prints -12, so "%" < "123"
            System.out.println("~".compareTo("xyz")); // prints +6 , so "~" > "xyz"

      Again, it is important to remember this rule:

      Do not use relational or equality operators with strings.  Instead, use the compareTo and equals methods.
       

      compareToIgnoreCase This method works the same as compareTo, except that it ignores case (as its name implies), so that uppercase letters are considered equal to lowercase letters.  For example:
            System.out.println("ABC".compareToIgnoreCase("abc")); // prints 0, so "ABC" equals "abc"
      concat Returns a new string -- the result of concatenating the argument to the end of "this" string:
            System.out.println("ABC".concat("def")); // prints ABCdef
      This method is not often used, as we can achieve the same result using string concatenation:
            System.out.println("ABC" + "def");       // prints ABCdef
      contains Returns the boolean true if "this" string contains the argument, and false otherwise:
            System.out.println("ABC".contains("BC")); // prints true
            System.out.println("ABC".contains("CB")); // prints false
            System.out.println("ABC".contains("AC")); // prints false
      In the last example, we see that "ABC" does not contain "AC" -- even though "A" and "C" are contained, they are not adjacent, so "AC" is not contained in "ABC".
      endsWith Returns the boolean true if "this" string ends with the argument, and false otherwise:
            System.out.println("ABC".endsWith("BC")); // prints true
            System.out.println("ABC".endsWith("C"));  // prints true
            System.out.println("ABC".endsWith(""));   // prints true
            System.out.println("ABC".endsWith("B"));  // prints false
      equals Returns the boolean true if "this" string equals the argument, and false otherwise:
            System.out.println("ABC".equals("ABC")); // prints true
            System.out.println("ABC".equals("AB"));  // prints false
            System.out.println("ABC".equals("abc")); // prints false
      equalsIgnoreCase This method works the same as equals, except that it ignores case (as its name implies), so that uppercase letters are considered equal to lowercase letters.  For example:
            System.out.println("ABC".equalsIgnoreCase("abc")); // prints true
      indexOf Returns the starting index where the argument first occurs in "this" string, or -1 if it does not occur.  Note that the argument can be either a char or a string.  For example, here we find where chars occur:
            System.out.println("ABC".indexOf('A'));  // prints 0
            System.out.println("ABC".indexOf('B'));  // prints 1
            System.out.println("ABC".indexOf('D'));  // prints -1
      And here we find where strings occur:
            System.out.println("ABC".indexOf("BC")); // prints 1
            System.out.println("ABC".indexOf("CD")); // prints -1

      Note that indexOf can take a second parameter, the fromIndex -- in this case, the method looks for the argument starting from that index (and so the method will not find the argument if it only occurs to the left of the index).  For example:
            System.out.println("ABCDBC".indexOf("BC",1)); // prints 1
            System.out.println("ABCDBC".indexOf("BC",2)); // prints 4
            System.out.println("ABCDBC".indexOf("BC",5)); // prints -1
      lastIndexOf Returns the starting index where the argument last occurs in "this" string, or -1 if it does not occur.  Note that is argument can be either a char or a string.  For example, here we find where chars last occur:
            System.out.println("ABCAB".lastIndexOf('A'));  // prints 3
            System.out.println("ABCAB".lastIndexOf('B'));  // prints 4
            System.out.println("ABCAB".lastIndexOf('D'));  // prints -1
      And here we find where strings last occur:
            System.out.println("ABCAB".lastIndexOf("AB")); // prints 3
            System.out.println("ABCAB".lastIndexOf("CD")); // prints -1

      Note that lastIndexOf can take a second parameter, the fromIndex -- in this case, the method looks for the argument starting from that index (and so the method will not find the argument if it only occurs to the right of the index).  For example:
            System.out.println("ABCDBC".lastIndexOf("BC",4)); // prints 4
            System.out.println("ABCDBC".lastIndexOf("BC",3)); // prints 1
            System.out.println("ABCDBC".lastIndexOf("BC",0)); // prints -1
      length Returns the length of the string -- that is, the number of characters in the string:
            System.out.println("abcdef".length()); // prints 6
            System.out.println("g    h".length()); // prints 6
            System.out.println("".length());       // prints 0
      replace Returns a new string resulting from replacing all occurrences of its first argument with its second argument.  Note that the arguments can be either strings or chars.  For example, here we will replace one char with another:
            System.out.println("abcabc".replace('a','d')); // prints dbcdbc
      And here we replace one string with another:
            System.out.println("abcabc".replace("bc","e")); // prints aeae

      You cannot replace a string with a char, or a char with a string:
            System.out.println("abcabc".replace("bc",'e')); // will not compile!

      Note that you can use this method to remove all occurrences of a string by replacing them with the empty string ("").
            System.out.println("abcabc".replace("bc","")); // prints aa

      Finally, note that you cannot remove all occurrences of a char by replacing them with the empty char (''), because there is no such thing!
            System.out.println("abcabc".replace('c','')); // will not compile!

      So, again:  Even though there is an empty string (""), there is no empty char ('').
      startsWith Returns the boolean true if "this" string starts with the argument, and false otherwise:
            System.out.println("ABC".startsWith(""));   // prints true
            System.out.println("ABC".startsWith("A"));  // prints true
            System.out.println("ABC".startsWith("AB")); // prints true
            System.out.println("ABC".startsWith("BC")); // prints false
      substring Returns a new string composed of characters from its first argument (the beginIndex) up to, but not including, its second argument (the endIndex).  For example:
            System.out.println("ABCD".substring(0,1));   // prints A
            System.out.println("ABCD".substring(0,2));   // prints AB
            System.out.println("ABCD".substring(1,2));   // prints B
            System.out.println("ABCD".substring(1,3));   // prints BC
      A common use of substring is to find a suffix -- that is, a substring from some beginIndex until the end of the string.  Because the endIndex is not included in the string, we can use string.length() as the endIndex, as such:
            System.out.println("ABCD".substring(2,4));                 // prints CD
            System.out.println("ABCD".substring(2,"ABCD".length()));   // prints CD

      Actually, suffixes are so commonly used that they have their own form of substring -- if you only include one argument, the beginIndex, then the method returns the suffix starting from this beginIndex:
            System.out.println("ABCD".substring(2));   // prints CD
      toLowerCase Returns a new string where all uppercase characters are converted to lowercase, and all other characters are unchanged.  For example:
            System.out.println("ABcd123".toLowerCase());   // prints abcd123
      toUpperCase Returns a new string where all uppercase characters are converted to lowercase, and all other characters are unchanged.  For example:
            System.out.println("ABcd123".toUpperCase());   // prints ABCD123
      trim Returns a new string which is the same as "this" string with leading and trailing whitespace omitted (but with all other whitespace included).  For example:
            String s = "   ab   cd   ";
            System.out.println("[" + s.trim() + "]"); // prints [ab   cd]

       

    7. Packages

      See Figure 3.2 (p. 123)
       

    8. The Random Class and Random Methods
      import java.util.Random;
      class MyCode {
        public static void main(String[] args) {
          Random random = new Random();
          int a = random.nextInt();      // in range [Integer.MIN_VALUE, Integer.MAX_VALUE]
          int b = random.nextInt(100);   // in range [0,100) <-- exclusive!
          double d = random.nextFloat(); // in range [0,1) <-- exclusive!
          System.out.println("a             = " + a);
          System.out.println("b             = " + b);
          System.out.println("d             = " + d);
          System.out.println("Coin flip:  " + (random.nextFloat() < 0.5));
          System.out.println("Coin flip:  " + (random.nextFloat() < 0.5));
        }
      }
    9. The Math Class and Math Methods

      Note:  The Math class is static.  You do not create instances.  You call the methods by prefacing them with "Math.".  For example:

      class MyCode {
        public static void main(String[] args) {
           int a = (int)(10 * Math.random());  // in range [0,10) <-- exclusive!
           int b = (int)(10 * Math.random());  // ditto
           System.out.println("a             = " + a);
           System.out.println("b             = " + b);
           System.out.println("Math.max(a,b) = " + Math.max(a,b));
           System.out.println("Math.min(a,b) = " + Math.min(a,b));
           System.out.println("Math.pow(a,b) = " + Math.pow(a,b));
           System.out.println("Math.sqrt(a)  = " + Math.sqrt(a));
           System.out.println("Math.random() = " + Math.random());
           System.out.println("Math.random() = " + Math.random());
        }
      }

      See Figure 3.4 (p. 128), , along with some additional methods:

      Method Description
      Math.sqrt Returns the square root of its argument.  For example:
            System.out.println(Math.sqrt(5));  // prints 2.23606797749979
      Math.random Returns a random number between 0 (inclusive) and 1 (exclusive).  For example:
            System.out.println(Math.random());
      This prints a different random number every time you run it.
      Math.min Returns the minimum of its two arguments.  For example:
            System.out.println(Math.min(3,4)); // prints 3
            System.out.println(Math.min(4,3)); // also prints 3

      Note that the minimum of two ints is also an int, whereas the minimum of two doubles is a double.  So we have:
            System.out.println(Math.min(3,4));     // prints 3
            System.out.println(Math.min(3.0,4.0)); // prints 3.0 (not 3)
      Math.max Returns the maximum of its two arguments.  For example:
            System.out.println(Math.max(3,4)); // prints 4
            System.out.println(Math.max(4,3)); // also prints 4

      Note that the maximum of two ints is also an int, whereas the maximum of two doubles is a double.  So we have:
            System.out.println(Math.max(3,4));     // prints 4
            System.out.println(Math.max(3.0,4.0)); // prints 4.0 (not 4)
      Math.abs Returns the absolute value of its argument.  For example:
            System.out.println(Math.abs(5));  // prints 5
            System.out.println(Math.abs(-5)); // also prints 5

      Note that the absolute value of an int is also an int, whereas the absolute value of a double is a double.  So we have:
            System.out.println(Math.abs(5));    // prints 5
            System.out.println(Math.abs(-5.0)); // prints 5.0 (not 5)
      Math.ceil Returns the "ceiling", or the smallest (closest to negative infinity) integer that is greater than or equal to its argument.  For example:
            System.out.println(Math.ceil(2.01)); // prints 3.0
            System.out.println(Math.ceil(2.00)); // prints 2.0
            System.out.println(Math.ceil(1.99)); // prints 2.0

      Note that the value computed by Math.ceil cannot be assigned directly into an integer variable, because the value is actually a double value that happens to be an integer.  So:
            double d = Math.ceil(1.2); // ok
            int x = Math.ceil(1.2);    // WILL NOT COMPILE

      Math.floor Returns the "floor", or the largest (closest to positive infinity) integer that is less than or equal to its argument.  For example:
            System.out.println(Math.floor(2.01)); // prints 2.0
            System.out.println(Math.floor(2.00)); // prints 2.0
            System.out.println(Math.floor(1.99)); // prints 1.0

      Note that the value computed by Math.floor cannot be assigned directly into an integer variable, because the value is actually a double value that happens to be an integer.  So:
            double d = Math.floor(1.2); // ok
            int x = Math.floor(1.2);    // WILL NOT COMPILE

      (int)
      Math.round
      Returns the rounded integer, or the closest integer value to its argument.  For example:
            System.out.println(Math.round(2.50)); // prints 3
            System.out.println(Math.round(2.49)); // prints 2
            System.out.println(Math.round(2.01)); // prints 2
            System.out.println(Math.round(2.00)); // prints 2
            System.out.println(Math.round(1.99)); // prints 2

      Note that, unlike Math.ceil and Math.floor, the value computed by Math.round is not a double.  Curiously, it also it not an "int".  Instead, it is a "long", which is a special kind of integer.  This is a type of variable that we generally do not use.  As such, it is recommended that you always convert the result of Math.round from a long to an int, by preceding the call with "(int)", as such:
            System.out.println((int)Math.round(2.50)); // prints 3
            System.out.println((int)Math.round(2.49)); // prints 2
            System.out.println((int)Math.round(2.01)); // prints 2
            System.out.println((int)Math.round(2.00)); // prints 2
            System.out.println((int)Math.round(1.99)); // prints 2

      Note that the value computed by Math.round cannot be assigned directly into an integer variable unless you convert it as just described.  So:
            double d = Math.round(1.2);   // ok
            int x = (int)Math.round(1.2); // also ok
            int y = Math.round(1.2);      // WILL NOT COMPILE

      Math.pow Returns the value of the first argument raised to the power of the second argument.  For example:
            System.out.println(Math.pow(2,0)); // prints 1.0 (which is 2^0)
            System.out.println(Math.pow(2,1)); // prints 2.0 (which is 2^1)
            System.out.println(Math.pow(2,2)); // prints 4.0 (which is 2^2)
            System.out.println(Math.pow(2,3)); // prints 8.0 (which is 2^3)

      Note that we can use Math.pow to compute the square root of a number:
            System.out.println(Math.sqrt(5));     // prints 2.23606797749979
            System.out.println(Math.pow(5,0.5));  // also prints 2.23606797749979

      Note that the value computed by Math.pow cannot be assigned directly into an integer variable, even when raising an integer to an integer power, because the value is actually a double value that happens to be an integer.  So:
            double d = Math.pow(1,2); // ok
            int x = Math.pow(1,2);    // WILL NOT COMPILE

      Math.exp Returns e (Euler's number, the base of the natural logarithms) raised to the power of its argument.  For example:
            System.out.println(Math.exp(1)); // prints 2.7182818284590455 (which is E^1)
            System.out.println(Math.exp(2)); // prints 7.38905609893065 (which is E^2)

      Note that we can use Math.pow with the constant Math.E in place of Math.exp:
            System.out.println(Math.exp(2));        // prints 7.38905609893065 (which is E^2)
            System.out.println(Math.pow(Math.E,2)); // also prints 7.3890560989306495(which is E^2)

      Note however that double math is approximate, and so computing Math.exp with Math.pow produces very close but not identical results:
            double d1 = Math.exp(2);
            double d2 = Math.pow(Math.E,2);
            System.out.println(d1 == d2); // prints false, they differ
            System.out.println(d1 - d2);  // prints 8.881784197001252E-16
      So we see that the two approaches differ by about one-quadrillionth in this case.  They are very, very close.  But not identically the same.

      Math.log Returns the natural logarithm (base e) of its argument.  That is, the number x such that ex equals its argument.  For example:
            System.out.println(Math.log(Math.E)); // prints 1.0 (since e1.0 == e)
            System.out.println(Math.log(1));      // prints 0.0 (since e0.0 == 1)
            System.out.println(Math.log(0));      // prints -Infinity (since e-infinity == 0)
            System.out.println(Math.log(-1));     // prints NaN (since ex is never negative)

      You can use this method to compute the logab for any base a by applying this rule:
            logab = logeb / logea
      For example, the following line prints out the log381, which is the value x such that 3x == 81 (which is 4):
            System.out.println(Math.log(81)/Math.log(3)); // prints 4.0 (since 34.0 == 81)
      Actually, due to the approximate nature of double math, this prints out 4.000000000000001 rather than 4.0.  Close enough!
      Math.log10 Returns the base-10 logarithm of its argument.   That is, the number x such that 10x equals its argument.  For example:
            System.out.println(Math.log10(100));  // prints 2.0 (since 102.0 == 100)
            System.out.println(Math.log10(0.01)); // prints -2.0 (since 10-2.0 == 0.01)
      As noted above, you can use Math.log to compute Math.log10, so this method is provided merely as a convenience.
        Note:  in general, you do not need to know much trigonometry for this course.  However, we will make simple use of some trig functions in our graphics programs, so these methods are included here mostly for reference purposes.
      Math.sin
      Math.cos
      Math.tan
      These methods return the trigonometric functions of their argument, which is an angle in radians.  For example:
            System.out.println(Math.sin(0));          // prints 0.0
            System.out.println(Math.cos(Math.PI/3));  // prints (about) 0.5
            System.out.println(Math.tan(-Math.PI/4)); // prints (about) -1.0
      Math.asin
      Math.acos
      Math.atan
      These methods return the inverses of the trigonometric functions -- that is, the "arcsine", "arccosine", and "arctangent".  They return an angle in radians.  For example, we see how the previous examples are inverted:
            System.out.println(Math.asin(0.0));  // prints 0.0
            System.out.println(Math.acos(0.5));  // prints (about) π/3
            System.out.println(Math.atan(-1.0)); // prints (about) -
      π/4
      Math.toDegrees
      Math.toRadians
      These methods convert between degrees and radians, where 180 degrees equals π radians..  They are especially useful because many students are more comfortable dealing with degrees than radians.  For example:
            System.out.println(Math.toDegrees(Math.PI/3)); // prints (about) 60
            System.out.println(Math.toRadians(-30));       // prints (about) -π/6

      Naturally, these are often used in conjunction with the trigonometric functions.  For example:
            System.out.println(Math.cos(Math.toRadians(60)));    // prints (about) 0.5
            System.out.println(Math.toDegrees(Math.acos(0.5)));  // prints (about) 60

       


    carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem