objects - ePaperPress

advertisement
More About Objects
Static Methods
Let's start with two classes, a Driver class, containing method main, and a Calc class, containing
method sum.
// file Calc.java
public class Calc {
private int tmp;
public int sum(int a, int b) {
tmp = a + b;
return tmp;
}
}
// file Driver.java
public class Driver {
public static void main(String[] args) {
Calc c = new Calc();
int x = c.sum(2, 3);
// x = 5
int y = c.sum(4, 5);
// y = 9
}
}
The driver instantiates a Calc class with the new operator, and computes two sums. Let's make
the sum method static and see what happens.
// file Calc.java
public class Calc {
private int tmp;
public static int sum(int a, int b) {
tmp = a + b; // can't reference instance variable
return tmp;
}
}
This causes a compiler error, as sum is unable to reference tmp. The reason is that static
methods have no this pointer, so it is impossible for sum to determine which tmp to process.
One solution is to make tmp a local variable in sum.
// file Calc.java
public class Calc {
public static int sum(int a, int b) {
int tmp;
tmp = a + b;
return tmp;
}
}
The program now compiles and runs successfully.
Since sum is now static, no this pointer is required for sum, and therefore we don't need to
allocate the Calc class to execute sum.
/////////////////
// old version //
/////////////////
public class Calc {
private int tmp;
public int sum(int a, int b) {
tmp = a + b;
return tmp;
}
}
public class Driver {
public static void main(String[] args) {
Calc c = new Calc();
int x = c.sum(2, 3);
int y = c.sum(4, 5);
}
}
/////////////////
// new version //
/////////////////
public class Calc {
public static int sum(int a, int b) {
int tmp;
tmp = a + b;
return tmp;
}
}
public class Driver {
public static void main(String[] args) {
int x = Calc.sum(2, 3);
int y = Calc.sum(4, 5);
}
}
This is why you can reference methods in some classes without allocating the class. For
example,
int x = Math.round(y);
does not require allocating the Math class. That's because the Math.round method is a static
method and does not reference any instance variables in the Math class.
You can also include static member functions in the Driver class. Let's move our sum method to
the Driver class.
public class Driver {
private static int sum(int a, int b) {
int tmp;
tmp = a + b;
return tmp;
}
public static void main(String[] args) {
int x = sum(2, 3);
int y = sum(4, 5);
}
}
We now have a single file, Driver.java, with two static methods: main and sum. If you have
repetitive code and would like to place it in a method, static methods are often an attractive
choice.
We could also define tmp as a static data member.
public class Driver {
private static int tmp;
private static int sum(int a, int b) {
tmp = a + b;
return tmp;
}
public static void main(String[] args) {
int x = sum(2, 3);
int y = sum(4, 5);
}
}
Suppose sum referenced an instance variable.
public class Driver {
private int tmp;
private static int sum(int a, int b) {
tmp = a + b;
// can't reference instance variable
return tmp;
}
public static void main(String[] args) {
int x = sum(2, 3);
int y = sum(4, 5);
}
}
Obviously, sum can't reference tmp because sum is a static method, and static methods can't
reference instance variables. So let's remove the static attribute on sum.
public class Driver {
private int tmp;
private int sum(int a, int b) {
tmp = a + b;
return tmp;
}
public static void main(String[] args) {
int x = sum(2, 3);
// error, sum is not static
int y = sum(4, 5);
}
}
Now sum compiles okay, but references to sum fail. That's because sum is not a static method,
so we must allocate the class that contains sum. This will, in turn, allocate an instance of the
variable tmp that sum uses.
public class Driver {
private int tmp;
private int sum(int a, int b) {
tmp = a + b;
return tmp;
}
public static void main(String[] args) {
Driver d = new Driver();
int x = d.sum(2, 3);
int y = d.sum(4, 5);
}
}
This logic may look a bit strange at first. We're in the Driver class and we allocate ourselves!
However, remember that when you instantiate a class, you allocate memory for data, not code.
Method main is static, so it compiles okay as long as we don't reference variable tmp from main.
Method sum does reference tmp, so we need to allocate Driver before we invoke sum. This is
exactly what we do in the main method. Then, all references to sum must go via the allocated
class, and a this pointer is passed to sum indicating the location of the classes' data.
Math Class
The predefined Math class contains several static methods and constants. For example, Math.PI
accesses a public constant that contains 3.141592653..., or the value for π. The interface for
several methods is listed below:
// square root
double sqrt(double x)
// power
double pow(double x, double y)
// absolute value
int abs(int x)
long abs(long x)
float abs(float x)
double abs(double x)
// minimum
int min(int a, int b)
long min(long a, long b)
float min(float a, float b)
double min(double a, double b)
// maximum
int max(int a, int b)
long max(long a, long b)
float max(float a, float b)
double max(double a, double b)
// rounding
int round(float x)
long round(double x)
Since these are static mehods, there's no need to allocate an instance of the Math class before
calling a function. e.g.,
int i = Math.max(3, 5);
returns 5, the maximum number.
Overloading
If two methods in the same class have the same name, but different parameter types, we say the
function is overloaded. The compiler can determine which function to call based on the actual
parameter type. For example:
class Math {
public static int min(int i, int j) {
return i < j ? i : j;
}
public static double min(double i, double j) {
return i < j ? i : j;
}
}
class Test {
public static void main(String[] args) {
int i, j, k;
double a, b, c;
...
i = Math.min(j, k);
// call int version
a = Math.min(b, c);
// call double version
}
}
The compiler does not make its decision based on the return type. Consider the following:
int f(int);
int f(double);
int g();
double g();
f(g());
Which version of g will be called? Which version of f ? For this reason it's illegal to have two
functions with identical parameter types in the same class.
Constructors
Recall we had two ways to establish data in the Employee class. We could make data members
public, and assign directly to the members as follows:
class Employee {
public String name;
public double pay;
}
Employee e = new Employee();
e.name = "John";
e.pay = 100.00;
Alternatively, we could protect our data by designating it private, and code accessor methods to
assign values to the data members:
class Employee {
private String name;
private double pay;
public void setName(String name) {
this.name = name;
}
public void setPay(double pay) {
this.pay = pay;
}
}
A third possibility exists. We could establish values while we're allocating the class using the
classes' constructor.
class Employee {
private String name;
private double pay;
public Employee(String name, double pay) {
this.name = name;
this.pay = pay;
}
}
Employee e = new Employee("John", 100.00);
If you don't include a constructor then Java generates one for you that simply does nothing. And,
just as for any other method, constructors can be overloaded. Consider the following:
class Employee {
private String name;
private double pay;
public Employee(String name, double pay) {
this.name = name;
this.pay = pay;
}
public Employee(String name) {
this.name = name;
this.pay = 0.00;
}
Employee e1 = new Employee("John", 100.00);
Employee e2 = new Employee("Mary");
Employee e3 = new Employee();
// error
If you have a constructor, then you must explicitly define the default constructor if you wish to
support this call.
Comparing Classes
What output does the following program produce?
public class Number {
public int value;
}
public class Driver {
public static void main(String[] args) {
Number a = new Number();
a.value = 1;
Number b = new Number();
b.value = 1;
if (a == b)
System.out.println("equal");
else
System.out.println("not equal");
}
}
What output does the following program produce?
public class Number {
public int value;
boolean equals(Number t) {
return t.value == value;
}
}
public class Driver {
public static void main(String[] args) {
Number a = new Number();
a.value = 1;
Number b = new Number();
b.value = 1;
if (a.equals(b))
System.out.println("equal");
else
System.out.println("not equal");
}
}
What output does the following program produce?
public class Driver {
public static void main(String[] args) {
String a, b;
a = "123";
b = "123";
System.out.println("a=" + a + ", b=" + b + ", " + (a == b));
}
}
Assigning Classes
What output does the following program produce?
public class Number {
public int value;
}
public class Driver {
public static void main(String[] args) {
Number a = new Number();
a.value = 1;
Number b = new Number();
b.value = 2;
System.out.println("a = " + a.value + ", b = " + b.value);
a = b;
System.out.println("a = " + a.value + ", b = " + b.value);
a.value = 3;
System.out.println("a = " + a.value + ", b = " + b.value);
}
}
Java supports a Cloneable interface that does a bitwise copy of a class. The following is code for
the Cloneable interface included in the Java library:
public interface Cloneable {
protected Object clone() throws CloneNotSupportedException;
}
The phrase implements Cloneable, in the Number class shown below, specifies that this class
has implemented the Cloneable interface.
public class Number implements Cloneable {
public int value;
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException) {
return null;
}
}
}
public class Driver {
public static void main(String[] args) {
Number a = new Number();
a.value = 5;
Number b = (Number)a.clone();
System.out.println("a = " + a.value + ", b = " + b.value);
}
}
In the Driver class we call the Number's clone method. The clone method in Number calls the
protected method in Object. All classes are derived, directly or indirectly, from the Object class.
The clone method in Object determines the size of the class, allocates memory for a new object,
and does a bitwise copy, and returns a reference to the new object.
C++ supports multiple inheritance. That is, we can inherit from several base classes. e.g.,
class derived: public base1, public base2 { ... }
This is a controversial feature that some feel is confusing an non-intuitive. Java does not support
multiple inheritance. However a Java class can implement multiple interfaces.
Wrapper Classes
Wrapper classes are provided for the primitive types as follows:
byte
char
int
long
float
double
Byte
Character
Integer
Long
Float
Double
A wrapper is used on a primitive type when it's desirable to associate methods with the value. For
example, if you wish to convert integer 35 to a string, it would be nice to say 35.toString(). Of
course this is illegal as there is no class named 35. However there is an Integer class and you
can accomplish this conversion as follows:
String s = Integer.toString(35);
This simply invokes the toString static method of the Integer class that converts an integer to a
string and returns a String. Here's another way to do the same thing:
Integer x = new Integer(35);
String s = x.toString();
In this case we're allocating an instance of the Integer class and invoking a constructor that
expects an integer as an argument.
Code for the Integer wrapper may look similar to the following:
class Integer {
int v;
public Integer(int v) {
this.v = v;
}
public static String toString(int v) {
// convert v to string and return string
}
public String toString() {
return toString(v);
}
}
There is only one constructor, so you must pass a value to instantiate the class. Then call the
toString method, without parameters, to obtain a string value. Alternatively, skip instantiating the
class and simply call the static method to do the conversion.
// method 1
Integer x = new Integer(35);
String s = x.toString();
// method 2 uses static method
String s = Integer.toString(35);
All the primitive wrappers include a toString method. There's also a valueOf method to obtain
the value stored in the wrapper.
Integer k = new Integer(50);
System.out.println(Integer.toString(k.valueOf()));
Here we called k's valueOf method that returns 50, the value stored in k, and then convert it to a
String. Wrapper classes also include a parse method that converts strings to a primitive type. For
the
Integer
class,
Integer.parseInt("12")
will
return
an
integer
12,
and
Double.parseDouble("12.3") will return a double (see p. 114). Several useful methods are
supported by the Character wrapper class that convert between lower and upper case and test
for digits or letters (see p. 322).
Wrappers are immutable. Once established they may not be changed. For example,
Integer k = new Integer(25);
Integer k now contains 25. There is no way you can change the value stored in variable k. That's
because this value is private to Integer, and there are no accessor methods that will allow you to
change its value. Well, there's almost no way to change the value. Consider the following:
Integer k = new Integer(25);
....
k = new Integer(35);
Here we allocated a new Integer, so k now points to a new value, 35. The old value is garbagecollected by Java.
The String class is also immutable. Once a value is stored in a String, the only way you can
change it is to allocate another String.
Download