![]() |
|||
![]() ![]() |
![]() |
|
![]() |
The Class ClassIntroductory texts and classes about object-oriented programming spend a lot of time trying to explain the difference between objects and classes. If there is a single thing that separates people who understand object-oriented programming from the people who merely know a few buzzwords, its the proper use of the words object and class. Its drilled into students that they are two different things, as different as the recipe for a cake and the cake itself. However, now that you are a more advanced student of object-oriented programming, I can tell you the truth. Classes really are objects, at least in Java. A Java .class file contains the byte code for a particular class. When the Java VM loads a class, a ClassLoader object reads the byte codes and uses them to instantiate a new object of type java.lang.Class in other words, a Class object. A Class object has methods that are useful for deducing information about the class at runtime. There are two primary ways that your program can bootstrap a reference to a Class object: a ClassLoader object can load the class from bytes, or your program can call the static method Class.forName(String s) to load the class given its name. For example: Class threadClass = Class.forName("java.lang.Thread"); The name of the class must be the fully qualified name, including the entire package. For example, you must write java.lang.Thread and not just Thread. This is true regardless of the import statements in the program or whether the class is in the java.lang package. Once you have a Class object, you can use the newInstance() method to create instances of the class. Its signature is public Object newInstance() throws InstantiationException, IllegalAccessException For example: try { Object o = threadClass.newInstance(); } catch (InstantiationException e) { } catch (IllegalAccessException e) { } The object is returned without any type information, though. In other words, its a raw java.lang.Object, not a java.lang.Thread. You can cast the created object to the appropriate type like this: Thread t = (Thread) o; You often see these three steps combined on one line like this: Thread t =(Thread) Class.forName("java.lang.Thread").newInstance(); You need to know what the object will be before you can cast it. Theres no convenient way to determine the most specific type of an object created at runtime. If you know roughly what sort of objects are likely to be created, then you can check the possibilities with the instanceof operator and respond accordingly. For example, if (o instanceof Thread) { Thread t = (Thread) o; // ... work with the Thread } else if (o instanceof Applet) { Applet a = (Applet) o; // work with the Applet } In general, though, its much easier if you know your objects types at compile time. Aside from the type change, the newInstance() method behaves exactly like using the new operator with the noargs constructor for the class. The following lines produce identical byte code: Thread t =(Thread) Class.getClass("java.lang.Thread"). newInstance(); Thread t = new Thread(); If a class doesnt have a noargs constructor, you cant instantiate it with the newInstance() method. For example java.lang.Integer has two constructors: public Integer(int value) public Integer(String s) It does not have a constructor with the signature public Integer() Therefore, you should not write Integer i =(Integer) Class.getClass("java.lang.Integer") .newInstance(); If you try this, a java.lang.NoSuchMethodError will be thrown at runtime. Integers and other classes that do not have noargs constructors must be instantiated with the new operator. Once you have a Class object, there are several other methods to help you determine runtime type information. The getName() method returns the full package and class name of the class of this object. Its signature is public String getName() This is primarily useful for debugging. For example, public void printname(Object o) { System.out.println("I got an object of class " + o.getname()); } The name returned is always the most specific type possible, never a superclass or an interface. The getSuperclass() method returns a Class object representing the class of the immediate superclass of this object. Its signature is public Class getSuperclass() You can use this to walk the class hierarchy of an object. For example, given a Class object c, while ((c = c.getSuperclass()) != null) { System.out.println("extends " + c.getName()); } However, java.lang.Object does not have a superclass, so if the Class object is of type java.lang.Object, then null is returned. Thus, the last name printed by this loop will always be java.lang.Object because thats the ultimate superclass of all Java objects. Also, null is returned if the object is an interface. Interfaces are also loaded into the VM as objects of type Class. You can test whether a Class object in fact represents an interface with the isInterface() method. It returns true if the Class object in question represents an interface and false if it doesnt. Its signature is public boolean isInterface() The getInterfaces() method returns an array containing Class objects that represent interfaces. Its signature is public Class[] getInterfaces() If the Class object represents a class, then the array contains Class objects representing all interfaces implemented by the class. However, if the Class object represents an interface, then the array contains objects representing all the interfaces extended by this interface. This array may be of length zero if the Class object neither implements nor extends any interfaces. The one remaining piece of information about a Class object is the ClassLoader that was used to load the class from bytes on disk or on the network. The getClassLoader() method returns the ClassLoader object which loaded this class. It returns null if the class was not created by a ClassLoader. Its signature is public ClassLoader getClassLoader() Finally, theres the usual toString() method for creating a string representation of the Class object. The string is the word class or interface followed by the full, package-qualified name of the class. Some examples are class java.lang.Integer and interface java.io.Serializable.
|
![]() |
|