How create objects in Java

how objects are created in java internally,how to count objects in java,how to remove duplicate objects in arraylist java,how objects behave in java, how to build objects in java pdf free download
JohenCorner Profile Pic
JohenCorner,France,Professional
Published Date:02-08-2017
Your Website URL(Optional)
Comment
Objects in Java In this chapter, we get to the heart of Java and explore the object-oriented aspects of the language. The term object-oriented design refers to the art of decomposing an application into some number of objects, which are self-contained application components that work together. The goal is to break your problem down into a number of smaller prob‐ lems that are simpler and easier to handle and maintain. Object-based designs have proven themselves over the years, and object-oriented languages such as Java provide a strong foundation for writing applications from the very small to the very large. Java was designed from the ground up to be an object-oriented language, and all of the Java APIs and libraries are built around solid object-based design patterns. An object design “methodology” is a system or a set of rules created to help you break down your application into objects. Often this means mapping real-world entities and concepts (sometimes called the “problem domain”) into application components. Var‐ ious methodologies attempt to help you factor your application into a good set of re‐ usable objects. This is good in principle, but the problem is that good object-oriented design is still more art than science. While you can learn from the various off-the-shelf design methodologies, none of them will help you in all situations. The truth is that there is no substitute for experience. We won’t try to push you into a particular methodology here; there are shelves full of 1 books to do that. Instead, we’ll provide some common-sense hints to get you started. The following general design guidelines will hopefully make more sense after you’ve read this chapter and the next: 1. Once you have some experience with basic object-oriented concepts, you might want to look at Design Pat‐ terns: Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson, and Vlissides (Addison- Wesley). This book catalogs useful object-oriented designs that have been refined over the years by experience. Many appear in the design of the Java APIs. 131 a • Hide as much of your implementation as possible. Never expose more of the in‐ ternals of an object than you need to. This is key to building maintainable, reusable code. Avoid public variables in your objects, with the possible exception of con‐ stants. Instead define accessor methods to set and return values (even if they are simple types). Later, when you need to, you’ll be able to modify and extend the behavior of your objects without breaking other classes that rely on them. • Specialize objects only when you have to—use composition instead of inheritance. When you use an object in its existing form, as a piece of a new object, you are composing objects. When you change or refine the behavior of an object (by sub‐ classing), you are using inheritance. You should try to reuse objects by composition rather than inheritance whenever possible because when you compose objects, you are taking full advantage of existing tools. Inheritance involves breaking down the encapsulation of an object and should be done only when there’s a real advantage. Ask yourself if you really need to inherit the whole public interface of an object (do you want to be a “kind” of that object?) or whether you can just delegate certain jobs to the object and use it by composition. • Minimize relationships between objects and try to organize related objects in pack‐ ages. Classes that work closely together can be grouped using Java packages, which can hide those that are not of general interest. Only expose classes that you intend other people to use. The more loosely coupled your objects are, the easier it will be to reuse them later. Classes Classes are the building blocks of a Java application. A class can contain methods (func‐ tions), variables, initialization code, and, as we’ll discuss later, other classes. It serves as a blueprint for making class instances, which are runtime objects (individual copies) that implement the class structure. You declare a class with theclass keyword. Methods and variables of the class appear inside the braces of the class declaration: class Pendulum float mass; float length = 1.0f; int cycles; float getPosition ( float time ) ... ... The Pendulum class contains three variables: mass, length, and cycles. It also defines a method calledgetPosition(), which takes afloat value as an argument and returns 132 Chapter 5: Objects in Java a a float value as a result. Variables and method declarations can appear in any order, but variable initializers can’t make “forward references” to other variables that appear later. Once we’ve defined the Pendulum class, we can create a Pendulum object (an in‐ stance of that class) as follows: Pendulum p; p = new Pendulum(); Recall that our declaration of the variable p doesn’t create a Pendulum object; it simply creates a variable that refers to an object of type Pendulum. We still had to create the object, using thenew keyword, as shown in the second line of the preceding code snippet. Now that we’ve created a Pendulum object, we can access its variables and methods, as we’ve already seen many times: p.mass = 5.0; float pos = p.getPosition( 1.0 ); Two kinds of variables can be defined in a class: instance variables and static variables. Every object instance has its own set of instance variables; the values of these variables in one instance of an object can differ from the values in another object. We’ll talk about static variables later, which, in contrast, are shared among all instances of an object. In either case, if you don’t initialize a variable when you declare it, it’s given a default value appropriate for its type (null, zero, orfalse). Figure 5-1 shows a hypotheticalTextBook application that uses two instances ofPendu lum through the reference-type variables bigPendulum and smallPendulum. Each of thesePendulum objects has its own copy ofmass,length, andcycles. As with variables, methods defined in a class may be instance methods or static methods. An instance method is associated with just one instance of the class, but the relationship isn’t quite as simple as it is for variables. Instance methods are accessed through an object instance, but the object doesn’t really have its own “copy” of the methods (there is no duplication of code). Instead, the association means that instance methods can “see” and operate on the values of the instance variables of the object. As you’ll see in Chapter 6 when we talk about subclassing, there’s more to learn about how methods see variables. In that chap‐ ter, we’ll also discuss how instance methods can be “overridden” in child classes—a very important feature of object-oriented design. Both aspects differ from static methods, which we’ll see are really more like global functions, as they are associated with a class by name only. Accessing Fields and Methods Inside a class, we can access variables and call methods of the class directly by name. Here’s an example that expands on ourPendulum: class Pendulum ... void resetEverything() Classes 133 a mass = 1.0; length = 1.0; cycles = 0; ... float startingPosition = getPosition( 0.0 ); ... Figure 5-1. Instances of the Pendulum class Other classes access members of an object through a reference, using the dot selector notation that we discussed in the last chapter: class TextBook ... void showPendulum() Pendulum bob = new Pendulum(); ... int i = bob.cycles; bob.resetEverything(); bob.mass = 1.01; ... ... Here we have created a second class, TextBook, that uses a Pendulum object. It creates an instance inshowPendulum() and then invokes methods and accesses variables of the object through the referencebob. 134 Chapter 5: Objects in Java a Several factors affect whether class members can be accessed from another class. You can use the visibility modifierspublic,private, andprotected to control access; classes can also be placed into a package, which affects their scope. The private modifier, for example, designates a variable or method for use only by other members of the class itself. In the previous example, we could change the declaration of our variable cy cles toprivate: class Pendulum ... private int cycles; ... Now we can’t accesscycles fromTextBook: class TextBook ... void showPendulum() ... int i = bob.cycles; // Compile-time error If we still need to accesscycles in some capacity, we might add a publicgetCycles() method to thePendulum class. (Creating accessor methods like this is a good design rule because it allows future flexibility in changing the type or behavior of the value.) We’ll take a detailed look at packages, access modifiers, and how they affect the visibility of variables and methods in Chapter 6. Static Members As we’ve said, instance variables and methods are associated with and accessed through an instance of the class (i.e., through a particular object, likebob in the previous exam‐ ple). In contrast, members that are declared with the static modifier live in the class and are shared by all instances of the class. Variables declared with thestatic modifier are called static variables or class variables; similarly, these kinds of methods are called static methods or class methods. We can add a static variable to ourPendulum example: class Pendulum ... static float gravAccel = 9.80; ... We have declared the new float variable gravAccel as static. That means that it is associated with the class, not with an individual instance and if we change its value (either directly or through any instance of aPendulum), the value changes for allPendu lum objects, as shown in Figure 5-2. Classes 135 aFigure 5-2. Static variables shared by all instances of a class Static members can be accessed like instance members. Inside our Pendulum class, we can refer togravAccel like any other variable: class Pendulum ... float getWeight () return mass gravAccel; ... However, since static members exist in the class itself, independent of any instance, we can also access them directly through the class. We don’t need aPendulum object to get or set the variablegravAccel; instead, we can use the class to select the variable: Pendulum.gravAccel = 8.76; This changes the value of gravAccel as seen by all instances. Why would we want to change the value ofgravAccel? Well, perhaps we want to explore how pendulums would work on different planets. Static variables are also very useful for other kinds of data that is shared among classes at runtime. For instance, you can create methods to register your object instances so that they can communicate, or so that you can keep track of all of them. It’s also common to use static variables to define constant values. In this case, we use the static modifier along with the final modifier. So, if we cared only about 136 Chapter 5: Objects in Java a pendulums under the influence of the Earth’s gravitational pull, we might change Pen dulum as follows: class Pendulum ... static final float EARTH_G = 9.80; ... We have followed a common convention here and named our constant with capital letters. The value of EARTH_G is a constant; it can be accessed through the class Pendu lum or its instances, but its value can’t be changed at runtime. It’s important to use the combination ofstatic andfinal only for things that are really constant. That’s because the compiler is allowed to “inline” such values within classes that reference them. This means that if you change a static final variable, you may have to recompile all code that uses that class (this is really the only case where you have to do that in Java). Static members are useful as flags and identifiers, which can be accessed from anywhere. They are also useful for values needed in the construction of an instance itself. In our example, we might declare a number of static values to represent various kinds ofPendulum objects: class Pendulum ... static int SIMPLE = 0, ONE_SPRING = 1, TWO_SPRING = 2; ... We might then use these flags in a method that sets the type of aPendulum or in a special constructor, as we’ll discuss shortly: Pendulum pendy = new Pendulum(); pendy.setType( Pendulum.ONE_SPRING ); Again, inside the Pendulum class, we can use static members directly by name, as well; there’s no need for thePendulum. prefix: class Pendulum ... void resetEverything() setType ( SIMPLE ); ... ... Constants versus enumerations In the previous section, we saw two uses for static final variables (constants). The first was to create true constants; in that case, it was the numeric constant EARTH_G, but it could easily have been a String or Date value. The second usage was to create a fixed Classes 137 a set of identifiers, SIMPLE, ONE_SPRING, etc., whose actual values were not as important as their uniqueness and, perhaps, their particular order. Enumerations were added to the Java language to replace this identifier usage with a mechanism that is both safer and, in some cases, more efficient. We could have declared our pendulum types as an enumeration like so: public enum PendulumTypes Simple, OneSpring, TwoSpring This enumeration creates not only the values, but also a new type, PendulumTypes, whose value is limited to one of the three discrete identifiers. Calling code can refer to the values as it did through our class:PendulumTypes.Simple. We’ve changed our case convention here to diverge from the convention for integer constants, but you can stick with uppercase if you prefer. Later, when we talk about importing classes and packages, we’ll discuss the static im‐ port feature of Java, which allows us to import static identifiers and enumerations (which, as we’ve seen, are related) into a class so that we can use them by their simple names. For example: new Pendulum(OneSpring ); We’ll go into detail about enumerations later in this chapter after we’ve covered objects in more depth. Methods Methods appear inside class bodies. They contain local variable declarations and other Java statements that are executed when the method is invoked. Methods may return a value to the caller. They always specify a return type, which can be a primitive type, a reference type, or the typevoid , which indicates no returned value. Methods may take arguments, which are values supplied by the caller of the method. Here’s a simple example: class Bird int xPos, yPos; double fly ( int x, int y ) double distance = Math.sqrt( xx + yy ); flap( distance ); xPos = x; yPos = y; return distance; ... In this example, the class Bird defines a method, fly(), that takes as arguments two integers:x andy. It returns adouble type value as a result, using thereturn keyword. 138 Chapter 5: Objects in Java a Our method has a fixed number of arguments (two); however, methods can have variable-length argument lists, which allow the method to specify that it can take any number of arguments and sort them itself at runtime. We provide more details later in this chapter. Local Variables Ourfly() method declares a local variable called distance, which it uses to compute the distance flown. A local variable is temporary; it exists only within the scope (the block) of its method. Local variables are allocated when a method is invoked; they are normally destroyed when the method returns. They can’t be referenced from outside the method itself. If the method is executing concurrently in different threads, each thread has its own version of the method’s local variables. A method’s arguments also serve as local variables within the scope of the method; the only difference is that they are initialized by being passed in from the caller of the method. An object created within a method and assigned to a local variable may or may not persist after the method has returned. As with all objects in Java, it depends on whether any references to the object remain. If an object is created, assigned to a local variable, and never used anywhere else, that object is no longer referenced when the local variable disappears from scope, so garbage collection removes the object. If, however, we assign the object to an instance variable of an object, pass it as an argument to another method, or pass it back as a return value, it may be saved by another variable holding its reference. We’ll discuss object creation and garbage collection in more detail shortly. Shadowing If a local variable and an instance variable have the same name, the local variable shadows or hides the name of the instance variable within the scope of the method. In the fol‐ lowing example, the local variables xPos and yPos hide the instance variables of the same name: class Bird int xPos, yPos; int xNest, yNest; ... double flyToNest() int xPos = xNest; int yPos = yNest: return ( fly( xPos, yPos ) ); ... When we set the values of the local variables in flyToNest(), it has no effect on the values of the instance variables. Methods 139 aThe “this” reference You can use the special referencethis any time you need to refer explicitly to the current object or a member of the current object. Often you don’t need to usethis, because the reference to the current object is implicit; such is the case when using unambiguously named instance variables inside a class. But we can usethis to refer explicitly to instance variables in our object, even if they are shadowed. The following example shows how we can use this to allow argument names that shadow instance variable names. This is a fairly common technique because it saves having to make up alternative names. Here’s how we could implement ourfly() method with shadowed variables: class Bird int xPos, yPos; double fly ( int xPos, int yPos ) double distance = Math.sqrt( xPosxPos + yPosyPos ); flap( distance ); this.xPos = xPos; // instance var = local vra this.yPos = yPos; return distance; ... In this example, the expression this.xPos refers to the instance variable xPos and as‐ signs it the value of the local variablexPos, which would otherwise hide its name. The only reason we need to usethis in the previous example is because we’ve used argument names that hide our instance variables, and we want to refer to the instance variables. You can also use thethis reference any time you want to pass a reference to “the current” enclosing object to some other method; we’ll show examples of that later. Static Methods Static methods (class methods), like static variables, belong to the class and not to in‐ dividual instances of the class. What does this mean? Well, foremost, a static method lives outside of any particular class instance. It can be invoked by name, through the class name, without any objects around. Because it is not bound to a particular object instance, a static method can directly access only other static members (static variables and other static methods) of the class. It can’t directly see any instance variables or call any instance methods, because to do so we’d have to ask, “on which instance?” Static methods can be called from instances, syntactically just like instance methods, but the important thing is that they can also be used independently. Our fly() method uses a static method: Math.sqrt(), which is defined by the java.lang.Math class; we’ll explore this class in detail in Chapter 11. For now, the im‐ portant thing to note is that Math is the name of a class and not an instance of a Math object. (It so happens that you can’t even make an instance of the Math class.) Because 140 Chapter 5: Objects in Java a static methods can be invoked wherever the class name is available, class methods are closer to C-style functions. Static methods are particularly useful for utility methods that perform work that is useful either independently of instances or in working on instances. For example, in ourBird class, we could enumerate all of the available types of birds that can be created: class Bird ... static String getBirdTypes() ... Here, we’ve defined a static method, getBirdTypes(), that returns an array of strings containing bird names. We can use getBirdTypes() from within an instance of Bird, just like an instance method. However, we can also call it from other classes, using the Bird class name: String names = Bird.getBirdTypes(); Perhaps a special version of theBird class constructor accepts the name of a bird type. We could use this list to decide what kind of bird to create. Static methods also play an important role in various design patterns, where you limit the use of the new operator for a class to one method—a static method called a factory method. We’ll talk more about object construction later, but suffice it to say that it’s common to see usage like this: Bird bird = Bird.createBird( "pigeon" ); Initializing Local Variables In the flyToNest() example, we made a point of initializing the local variables xPos and yPos. Unlike instance variables, local variables must be initialized before they can be used. It’s a compile-time error to try to access a local variable without first assigning it a value: void myMethod() int foo = 42; int bar; bar += 1; // compile-time error, bar uninitialized bar = 99; bar += 1; // would be OK here Notice that this doesn’t imply local variables have to be initialized when declared, just that the first time they are referenced must be in an assignment. More subtle possibilities arise when making assignments inside conditionals: Methods 141 a void myMethod int foo; if ( someCondition ) foo = 42; ... foo += 1; // Compile-time error, foo may not be initialized In this example, foo is initialized only ifsomeCondition istrue. The compiler doesn’t let you make this wager, so it flags the use of foo as an error. We could correct this situation in several ways. We could initialize the variable to a default value in advance or move the usage inside the conditional. We could also make sure the path of execution doesn’t reach the uninitialized variable through some other means, depending on what makes sense for our particular application. For example, we could simply make sure that we assignfoo a value in both theif andelse branch. Or we could return from the method abruptly: int foo; ... if ( someCondition ) foo = 42; ... else return; foo += 1; In this case, there’s no chance of reachingfoo in an uninitialized state, so the compiler allows the use offoo after the conditional. Why is Java so picky about local variables? One of the most common (and insidious) sources of errors in C or C++ is forgetting to initialize local variables, so Java tries to 2 help out. If it didn’t, Java would suffer the same potential irregularities as C or C++. Argument Passing and References In the beginning of Chapter 4, we described the distinction between primitive types, which are passed by value (by copying), and objects, which are passed by reference. Now that we’ve got a better handle on methods in Java, let’s walk through an example: void myMethod( int j, SomeKindOfObject o ) ... 2. As withmalloc’ed storage in C or C++, Java objects and their instance variables are allocated on a heap, which allows them default values once, when they are created. Local variables, however, are allocated on the Java virtual machine stack. As with the stack in C and C++, failing to initialize these could mean successive method calls could receive garbage values, and program execution might be inconsistent or implementation- dependent. 142 Chapter 5: Objects in Java a // use the method int i = 0; SomeKindOfObject obj = new SomeKindOfObject(); myMethod( i, obj ); The chunk of code calls myMethod(), passing it two arguments. The first argument, i, is passed by value; when the method is called, the value ofi is copied into the method’s parameter (a local variable to it) named j. If myMethod() changes the value of j, it’s changing only its copy of the local variable. In the same way, a copy of the reference toobj is placed into the reference variableo of myMethod(). Both references refer to the same object, so any changes made through either reference affect the actual (single) object instance. If we change the value of, say, o.size, the change is visible both aso.size (insidemyMethod()) or asobj.size (in the calling method). However, if myMethod() changes the reference o itself—to point to another object—it’s affecting only its local variable reference. It doesn’t affect the caller’s variableobj, which still refers to the original object. In this sense, passing the reference is like passing a pointer in C and unlike passing by reference in C++. What if myMethod() needs to modify the calling method’s notion of the obj reference as well (i.e., make obj point to a different object)? The easy way to do that is to wrap obj inside some kind of object. For example, we could wrap the object up as the lone element in an array: SomeKindOfObject wrapper = new SomeKindOfObject obj ; All parties could then refer to the object as wrapper0 and would have the ability to change the reference. This is not aesthetically pleasing, but it does illustrate that what is needed is the level of indirection. Another possibility is to usethis to pass a reference to the calling object. In that case, the calling object serves as the wrapper for the reference. Let’s look at a piece of code that could be from an implementation of a linked list: class Element public Element nextElement; void addToList( List list ) list.addToList( this ); class List void addToList( Element element ) ... element.nextElement = getNextElement(); Methods 143 a Every element in a linked list contains a pointer to the next element in the list. In this code, the Element class represents one element; it includes a method for adding itself to the list. The List class itself contains a method for adding an arbitrary Element to the list. The methodaddToList() callsaddToList() with the argumentthis (which is, of course, an Element). addToList() can use the this reference to modify the Ele ment’snextElement instance variable. The same technique can be used in conjunction with interfaces to implement callbacks for arbitrary method invocations. Wrappers for Primitive Types As we described in Chapter 4, there is a schism in the Java world between class types (i.e., objects) and primitive types (i.e., numbers, characters, and Boolean values). Java accepts this tradeoff simply for efficiency reasons. When you’re crunching numbers, you want your computations to be lightweight; having to use objects for primitive types complicates performance optimizations. For the times you want to treat values as ob‐ jects, Java supplies a standard wrapper class for each of the primitive types, as shown in Table 5-1. Table 5-1. Primitive type wrappers Primitive Wrapper void java.lang.Void boolean java.lang.Boolean char java.lang.Character byte java.lang.Byte short java.lang.Short int java.lang.Integer long java.lang.Long float java.lang.Float double java.lang.Double An instance of a wrapper class encapsulates a single value of its corresponding type. It’s an immutable object that serves as a container to hold the value and let us retrieve it later. You can construct a wrapper object from a primitive value or from a String representation of the value. The following statements are equivalent: Float pi = new Float( 3.14 ); Float pi = new Float( "3.14" ); The wrapper constructors throw a NumberFormatException when there is an error in parsing a string. 144 Chapter 5: Objects in Java a Each of the numeric type wrappers implements thejava.lang.Number interface, which provides “value” methods access to its value in all the primitive forms. You can retrieve scalar values with the methods doubleValue(), floatValue(), longValue(), intVal ue(),shortValue(), andbyteValue(): Double size = new Double ( 32.76 ); double d = size.doubleValue(); // 32.76 float f = size.floatValue(); // 32.76 long l = size.longValue(); // 32 int i = size.intValue(); // 32 This code is equivalent to casting the primitivedouble value to the various types. The most common need for a wrapper is when you want to pass a primitive value to a method that requires an object. For example, in Chapter 11, we’ll look at the Java Col‐ lections API, a sophisticated set of classes for dealing with object groups, such as lists, sets, and maps. All the Collections APIs work on object types, so primitives must be wrapped when stored in them. We’ll see in the next section that Java makes this wrapping process automatic. For now, however, let’s do it ourselves. As we’ll see, a List is an extensible collection ofObjects. We can use wrappers to hold numbers in aList (along with other objects): // Simple Java code List myNumbers = new ArrayList(); Integer thirtyThree = new Integer( 33 ); myNumbers.add( thirtyThree ); Here, we have created anInteger wrapper object so that we can insert the number into theList, using theadd() method, which accepts an object. Later, when we are extracting elements from theList, we can recover theint value as follows: // Simple Java code Integer theNumber = (Integer)myNumbers.get(0); int n = theNumber.intValue(); // 33 As we alluded to earlier, allowing Java to do this for us makes the code more concise and safer. The usage of the wrapper class is mostly hidden from us by the compiler, but it is still being used internally: // Java code using autoboxing and generics ListInteger myNumbers = new ArrayListInteger(); myNumbers.add( 33 ); int n = myNumbers.get( 0 ); This example will make more sense as you read the next section on autoboxing and unboxing of primitive values. Methods 145 aAutoboxing and Unboxing of Primitives The Java compiler automatically wraps primitives in their wrapper types and unwraps them where appropriate. This process is called autoboxing and unboxing the primitive. It happens when primitives are used as arguments and return values in methods and on simple assignment to variables. For example: // Simple assignments Integer integer = 5; int i = new Integer(5); // Method arguments and return types Double multiply( Double a, Double b ) return a.doubleValue() b.doubleValue(); double d = multiply( 5.0, 5.0 ); In the first case, Java simply wrapped the value 5 into anInteger for us. In the second case, it unwrapped our Integer object to its primitive value. Next, we have a method that multiplies twoDouble wrapper objects and returns the result as aDouble wrapper. This example actually has three cases of boxing and one case of unboxing. First, the two double primitive values are boxed toDouble types in order to call the method. Next, the return statement of the method is actually being called on a primitive double value, which the compiler turns into aDouble before it leaves the method. Finally, the compiler unboxes the return value on assignment to the primitive double variabled. Performance implications of boxing Gauging performance is tricky. For the vast majority of applications, the time it takes to perform tasks like creating a small object or calling a method is miniscule compared to other factors, such as I/O, user interaction, or the actual logic of the application. As a general rule, it’s not wise to worry too much about these detailed performance issues until the application is mature (no premature optimization). However, we can anticipate that allowing Java to box and unbox primitives in performance-critical areas will not be as fast as using primitives directly. One aspect of this to consider is how many new objects are being created and reclaimed by the garbage collector. While in general Java may be forced to create a new object for each boxed primitive, there are optimizations for a small range of values. Java guarantees that the Boolean values true andfalse, as well as “small” valued numeric types ranging from 0 to 127 for bytes and chars and from –128 to 127 for shorts and integers, are interned. Saying that they are interned means that instead of creating a new object each time, Java reuses the same object on subsequent boxings. This is safe because primitive wrappers are immutable and cannot be changed. Integer i = 4; Integer j = 4; System.out.println( i == j ); // This object equality is true only for small // values. 146 Chapter 5: Objects in Java a The effect of this, as shown in this code snippet, is that for small identical values the boxed primitives are actually the same object. Java also attempts to intern string values in Java classes. We’ll talk about that in Chapter 10. Variable-Length Argument Lists As we mentioned earlier, Java methods may have variable-length argument lists or “var‐ args” that allow them to take any number of arguments when invoked. The most com‐ mon example usage of varargs is for theprintf() style printing method, which allows any number of tags to be embedded in a string and takes an argument for each tag to be printed. For example: System.out.printf("My name is %s and my age is %s\n", "Bob", 21 ); System.out.printf("Get the %s out of %s before I %s\n", item, place, action ); Varargs allow theprintf() method to accept any number of items to print (from zero to dozens, as awkward as that would be). A method accepting a variable argument list is equivalent to a method accepting an array of some type of object. The difference is that the compiler makes the method call accept individual, comma-separated values, and then packs them into the array for us. The syntax for declaring the varargs method uses ellipses (...) where the square brack‐ ets of an array might go. For example: void printObjects( Object ... list ) // list is an Object for( Object o : list ) System.out.println( o ); Inside the printObjects() method, the variable list is actually an Object type. We could find out how many arguments were passed to us by asking the array for its length in the usual way: System.out.println( "Number of arguments:" + list.length ); If the caller passed no arguments, the array will be empty. In the case of ourprintObjects() method, we could pass a mix of primitive values as well as object types because the compiler would automatically box the primitives to their wrapper types for us before placing them into theObject . The variable argument list does not have to be of type Object. It can be of any type, including primitive types. For example: printInts( int ... list ) // list is an int // usage Methods 147 a printInts( 1, 2, 3, 4 ); printStrings( String ... list ) // list is a String // usage printStrings( "foo", "bar", "gee" ); TheprintInts() method receives anint array of primitiveint values. Theprint Strings() method receives a String as its argument. The actual arguments must all be assignable (possibly after numeric promotion or boxing) to the type of the variable argument list. In other words, theprintInts() method can only be called with numbers assignable toint, and theprintStrings() method can only be called withStrings. Varargs methods may also have any number of fixed arguments before the varargs dec‐ laration. This is how the printf() method guarantees that its first argument is the format string: void printf( String format, Object ... args ) ... Of course, a method can have only one varargs declaration, and it must come last in the method signature. Method Overloading Method overloading is the ability to define multiple methods with the same name in a class; when the method is invoked, the compiler picks the correct one based on the arguments passed to the method. This implies that overloaded methods must have dif‐ ferent numbers or types of arguments. (In Chapter 6, we’ll look at method overriding, which occurs when we declare methods with identical signatures in different classes.) Method overloading (also called ad-hoc polymorphism ) is a powerful and useful feature. The idea is to create methods that act in the same way on different types of arguments. This creates the illusion that a single method can operate on many types of arguments. The print() method in the standard PrintStream class is a good example of method overloading in action. As you’ve probably deduced by now, you can print a string rep‐ resentation of just about anything using this expression: System.out.print( argument ) The variableout is a reference to an object (aPrintStream) that defines nine different, “overloaded” versions of the print() method. The versions take arguments of the fol‐ lowing types:Object,String,char,char,int,long,float,double, andboolean. class PrintStream void print( Object arg ) ... void print( String arg ) ... void print( char arg ) ... ... 148 Chapter 5: Objects in Java a You can invoke the print() method with any of these types as an argument, and it’s printed in an appropriate way. In a language without method overloading, this requires something more cumbersome, such as a uniquely named method for printing each type of object. In that case, it’s your responsibility to figure out what method to use for each data type. In the previous example,print() has been overloaded to support two reference types: Object andString. What if we try to callprint() with some other reference type? Say, a Date object? When there’s not an exact type match, the compiler searches for an acceptable, assignable match. SinceDate, like all classes, is a subclass ofObject, aDate object can be assigned to a variable of type Object. It’s therefore an acceptable match, and theObject method is selected. What if there’s more than one possible match? For example, we try to print a subclass of String called MyString. (The String class is final so it can’t really be subclassed, but let’s use our imaginations.) MyString is assignable to either String or to Object. Here, the compiler makes a determination as to which match is “better” and selects that method. In this case, it’s theString method. The intuitive explanation for this is that theString class is “closer” toMyString in the inheritance hierarchy. It is a more specific match. A slightly more rigorous way of spec‐ ifying it would be to say that a given method is more specific than another method if the argument types of the first method are all assignable to the argument types of the second method. In this case, theString method is more specific to a subclass ofString than theObject method because typeString is assignable to typeObject. The reverse is not true. If you’re paying close attention, you may have noticed we said that the compiler resolves overloaded methods. Method overloading is not something that happens at runtime; this is an important distinction. It means that the selected method is chosen once, when the code is compiled. Once the overloaded method is selected, the choice is fixed until the code is recompiled, even if the class containing the called method is later revised and an even more specific overloaded method is added. This is in contrast to overrid‐ den methods, which are located at runtime and can be found even if they didn’t exist when the calling class was compiled. In practice, this distinction will not usually be relevant to you, as you will likely recompile all of the necessary classes at the same time. We’ll talk about method overriding later in the chapter. Object Creation Objects in Java are allocated on a system “heap” memory space. Unlike other languages, however, we needn’t manage that memory ourselves. Java takes care of memory allo‐ cation and deallocation for you. Java explicitly allocates storage for an object when you Object Creation 149 a create it with thenew operator. More importantly, objects are removed by garbage col‐ lection when they’re no longer referenced. Constructors Objects are allocated with the new operator using an object constructor. A constructor is a special method with the same name as its class and no return type. It’s called when a new class instance is created, which gives the class an opportunity to set up the object for use. Constructors, like other methods, can accept arguments and can be overloaded (they are not, however, inherited like other methods; we’ll discuss inheritance in Chapter 6). class Date long time; Date() time = currentTime(); Date( String date ) time = parseDate( date ); ... In this example, the class Date has two constructors. The first takes no arguments; it’s known as the default constructor. Default constructors play a special role: if we don’t define any constructors for a class, an empty default constructor is supplied for us. The default constructor is what gets called whenever you create an object by calling its con‐ structor with no arguments. Here we have implemented the default constructor so that it sets the instance variable time by calling a hypothetical method, currentTime(), which resembles the functionality of the real java.util.Date class. The second con‐ structor takes aString argument. Presumably, thisString contains a string represen‐ tation of the time that can be parsed to set thetime variable. Given the constructors in the previous example, we create aDate object in the following ways: Date now = new Date(); Date christmas = new Date("Dec 25, 2006"); In each case, Java chooses the appropriate constructor at compile time based on the rules for overloaded method selection. If we later remove all references to an allocated object, it’ll be garbage-collected, as we’ll discuss shortly: christmas = null; // fair game for the garbage collector Setting this reference tonull means it’s no longer pointing to the"Dec 25, 2006" string object. Setting the variable christmas to any other value would have the same effect. 150 Chapter 5: Objects in Java a

Advise: Why You Wasting Money in Costly SEO Tools, Use World's Best Free SEO Tool Ubersuggest.