CISC-124 20180129 20180130 20180201 This week we continued to look at some aspects of Java and how they relate to building reliable software. Multi-Dimensional Arrays Like most languages, Java permits the declaration of multi-dimensional arrays. We spent a bit of time talking about the syntax, but it is summarized by a simple example: int[][] mytable; int x = 4; int y = 7; mytable = new int[x][y]; int[][][] my3dtable = new int[23][4][12]; By convention we describe the first dimension of a 2-dimensional array as the rows and the second dimension as the columns. In the 2-dimensional array defined above, each row has exactly the same number of elements. A member of the class pointed out that it is legal in Java to specify just the number of rows in a 2-dimensional table at the time of initialization, and then initialize each row to a 1- dimensional array of a different length. Please see the posted code for an example. Built-in Classes We have seen that Java provides software packages from which we can import pre-existing object definitions. For example, we have used import java.util.scanner; to import the definition of the Scanner object. There are hundreds of these pre-existing objects so many that if Java automatically imported all of them, our programs would be huge. So Java
makes these available but we have to import the ones we want. This helps keep our programs small. Part of the challenge of becoming a successful Java programmer is gaining familiarity with the types of objects that are available for import. There are a few classes that are always included (ie available without importing them). We have already seen one: the System class, which we use in output statements such as System.out.println( It was the best of times; it was the worst of times ); The System class contains an enormous variety of methods that let our programs interact with the computer (such as exchanging information through input and output, obtaining information about files and directories, creating new files, etc.) During the course we will look at a few of these - but as always I encourage you to go beyond the minimum and expand your knowledge of this material. We looked briefly at the Math class. It may seem strange that methods for mathematical computation are separated out from the basic language and stored in a separate class. However this is the Java philosophy. When we want to do mathematics beyond simple addition, multiplication, subtraction and division we need to remind Java that the methods belong to the Math class. Like System, Math provides a huge collection of methods. It also includes some useful final values such as PI. We can use the Math methods by putting Math. in front of the method name. For example the Math method pow(x,y) returns the value of x raised to the power of y. We use this method in lines like this: double z = Math.pow(3.6, 1.2); which assigns z the value of It is important to note that many of the methods in Math return double values even if you pass them integer parameters. For example, Math.pow(3,2) returns the double value, even though The documentation for the Math class specifies what type of value is returned by each method. It s a lot to remember Eclipse and other IDE s often have this information built-in and will tell you what type will be returned. One Math method with a surprising return type is Math.round(x), which rounds a real number to the nearest integer. We know that the standard type for real numbers is double and the standard type for integers is int. But if you enter a line such as
int a = Math.round(9.999); you get an error... because Math.round(x) returns a long integer when x is a double, and Java treats all literal real values as doubles. The easy way to fix this is to cast the return value to type int like this: int a = (int) Math.round(9.999); Wrapper Classes The primitive data types have Object classes associated with them. For example, the int type has the Integer class. We call these associated Objects wrappers because they wrap up a primitive data value and provide methods and final values that are related to the primitive data type. For example we have already seen the Integer.parseInt(s) method that attempts to interpret its parameter value as an int value. The Integer class contains final values such as MAX_VALUE and MIN_VALUE. Each Integer object has a field called value that can hold an int value. We can create Integer objects in the same way as we created Dog objects before: Integer int1 = new Integer(4533); // stores the int value 4533 in the object int1 and we can also initialize the Integer object by giving it a String version of an int, as in Integer int2 = new Integer( 4533 ); Now we can use the equals() method that all Integer objects own: System.out.println(int1.equals(int2)); // output is True You should experiment with the posted code that shows some examples of Integer objects, and look up the other methods provided by the Integer class. Floats, doubles, booleans, longs, etc. all have their own wrapper classes with similar properties to the Integer class. You may be wondering what the point is of having a wrapper class for each primitive type. As with so much of Java s design philosophy, the answer is that it gives us a convenient way to gather together all the methods and final values that relate to a certain type of thing in this case, the primitive data types. It turns out that the Character wrapper class is full of really useful methods that let us test properties of characters. For example, this class includes methods such as isdigit(), isletter(),
isuppercase() and so on. We put the isdigit() method to good use by developing a method to check an input string to see if it can be translated to an integer. The basic approach we adopted was to declare a Scanner and use its next() method to get an input String from the console. We then check each character in the String to see if it is a digit. In our first version, if the String forms a valid integer we return that value, and if it doesn t then we return the value 0. In the next version, we added a test on the first character of the String to allow for a leading minus sign. In a third version, we established a loop that would continue getting the next item from the input until a valid integer is entered. This method became the basis for our discussion of method overloading. Method Overloading Java allows us to have multiple methods in a class with the same name... as long as their parameter lists are different either in number or type or both. When we call the method, Java decides which one to execute based on the number and type of the arguments we provide. For example, we could have these methods: public static void multi(){ System.out.println( No parameters );} public static void multi(int x) {System.out.println( One int parameter );} public static void multi(int x, int y) {System.out.println( Two int parameters );} public static void multi(double x) {System.out.println( One double parameter );} etc If there is no version of a method that perfectly matches the types of the arguments, Java will choose the version that best matches using the standard rules of assigning values of one type to variables of another type. For example, suppose we have these two versions of a method (just showing the headers): public static void m2(int x, int y) { public static void m2(double x, double y) {
Now if we call m2 with m2( 15, 22.9 ); (ie with one int and one double) Java decides that it can t use the first version because it can t assign 22.9 to the int y parameter, but it can and does use the second version because it is ok to assign the value 15 to the double x parameter In the posted code you will see several versions of the getinteger() method. The value of this is that we can write a collection of methods with the same name, that can be useful in a wide variety of different situations. This makes it more likely that we will be able to re-use the code in the future, which is a big part of good software design. One thing to think about when writing overloaded methods is the problem of duplicated code in all the versions of the method. It is often better to have one generic version of the method if possible and use the multiple versions to set up calls to the generic version. The advantage of this is that if we make a change to the algorithm in the method we only have to make the change in one place. The posted code for getinteger takes this approach. Try writing your own overloaded method to calculate the area of a rectangle with the following parameter combinations: one int parameter means the rectangle is a square return the area as an int two int parameters means the parameters are the sides of a general rectangle return the area as an int one double parameter and two double parameters are interpreted the same way as the int versions, but the return is a double. StringTokenizer Class We looked very briefly at the StringTokenizer class. A StringTokenizer object is given a String when it is created, and it breaks the String into tokens. Tokens are substrings that are separated by delimiters. The default delimiters are space ( ), tab (\t), new line (\n) and carriage return (\r), which are collectively represented by the String \t\n\r. With the default delimiters, the StringTokenizer breaks the String up into what we can think of as words. But the StringTokenizer constructor is an overloaded method we can specify our own preferred delimiters. The posted code shows how we can access the tokens created by s StringTokenizer.