Which common object best describes you


25.2 Metadata of the classes with the Class object

Suppose we want to write a class browser. This should display all classes belonging to the current program and further information such as variable allocation, declared methods, constructors and information about the inheritance hierarchy. For this we need the library class Class. Instances of the class Class are objects that represent either a Java class or a Java interface (the following does not make a detailed distinction between interfaces that are also represented by class objects).

This is where Java differs from many conventional programming languages, as the properties of classes can be queried by the currently running program using the class objects. The instances of Class are a restricted form of meta-objects [Real metaclasses would be classes, the only instance of which is the normal Java class. Then the normal class variables would in truth be object variables in the metaclass. ] - the description of a Java class that only reveals selected information. In addition to normal classes, interfaces are also represented by a class object.


25.2.1 Get to a class object

First we have to find out the associated class object for a particular class. Class objects themselves can only be created by the JVM. We can't (the objects are immutable and the constructor is private). [And the JavaDoc reads: »Constructor. Only the Java Virtual Machine creates Class objects. «] To get a reference to a Class object, you can:

  • If an instance of the class is available, we call the getClass () method of the object and get the Class instance of the associated class.
  • Each class contains a class variable named .class of type Class, which refers to the associated class instance.
  • The ending .class is also allowed on primitive data types. The same class object supplies the static variable TYPE of the wrapper classes. This means that int.class == Integer.TYPE.
  • The class method Class.forName (String) can query a class, and we get the corresponding class instance as the result. If the class has not yet been loaded, forName () searches for and integrates the class. Because the search can go wrong, a ClassNotFoundException is possible.
  • If we already have a class object, but we are not interested in it but in its ancestor, we can simply get a class object for the superclass with getSuperclass ().

The following example shows three ways to get a Class object for java.util.Date:

Listing 25.1com / tutego / island / meta / GetClassObject.java, main ()

Class c1 = java.util.Date.class; System.out.println (c1); // class java.util.Date Class c2 = new java.util.Date (). getClass (); // or Class c2 = ... System.out.println (c2); // class java.util.Date try {Class c3 = Class.forName ("java.util.Date"); System.out.println (c3); // class java.util.Date} catch (ClassNotFoundException e) {e.printStackTrace (); }

The variant with forName () makes sense if the class name was not yet fixed when the program was compiled. Otherwise the previous technique is more straightforward and the compiler can check to see if the type exists.


Example class objects for primitive elements does not return forName ()! The instructions Class.forName ("boolean"); and Class.forName (boolean.class.getName ()); lead to a java.lang.ClassNotFoundException.




  • final class getClass () Returns the class instance that represents the class of the object at runtime.

final class java.lang.Class implements Serializable, GenericDeclaration, Type, AnnotatedElement

  • static Class forName (String className) throws ClassNotFoundException Returns the class instance for the class or interface with the specified fully qualified name. If it has not yet been required by the program, the class loader searches for and loads the class. The method never returns null. If the class could not be loaded and integrated, there is a ClassNotFoundException. An alternative method forName () also enables loading with a desired class loader.

"ClassNotFoundException" and "NoClassDefFoundError" *

The methods forName () from Class and loadClass () or findSystemClass () from ClassLoader always trigger a ClassNotFoundException if the class loader cannot find the class by its class name.

In addition to the exception class, there is a NoClassDefFoundError - a hard LinkageError that the system triggers whenever the JVM cannot load a class referenced in the bytecode. For example, take a statement like new MyClass (). If the JVM executes this instruction, it tries to load the bytecode from MyClass. If the bytecode for MyClass has been removed after compiling, the JVM triggers the NoClassDefFoundError if the load attempt was unsuccessful. The error also occurs if the class MyClass was found when loading the bytecode, but MyClass has a static initialization block, which in turn references a class for which no class file is available.

While ClassNotFoundException is more common than NoClassDefFoundError, it is generally an indication that a Java archive is missing from the classpath.

Renaming of the class names by the obfuscator

The fact that the compiler automatically generates bytecode according to this changed source code only leads to unexpected problems if we run an obfuscator over the program text that subsequently modifies the bytecode and thus obscures the meaning of the program or the bytecode and renames classes. Obfuscator is obviously not allowed to rename classes whose class instances are queried; or the obfuscator would also have to correctly replace the corresponding strings (but of course not all strings that happen to match names of classes).


25.2.2 What the class object describes *

A class instance can describe an interface, a class, a primitive data type or an array type. This can be found out using the three methods isInterface (), isPrimitive () and isArray (). If none of the three methods return true for a class instance, the object represents an ordinary class.

It is initially astonishing that there are also class instances that describe the primitive data types of Java. However, this makes it possible to uniformly describe the parameter and result types of any Java methods using class instances. Each of the eight wrapper classes, which belong to the data types boolean, byte, char, short, int, long, float and double, and the special class for the type void, encode a constant TYPE. If we need a class object for the primitive type int, we access it with Integer.TYPE (or alternatively with int.class). All class instances for primitive data types are automatically generated by the JVM. The isPrimitive () method returns true for exactly these nine special class instances so that they can be distinguished from representatives for real classes.


Note Although void is not a type, isPrimitive () reports this:

System.out.println (void.class.isPrimitive ()); // true

The following section of the program systematically tests the attributes of class objects. We use the getName () method to output the name of the Class object. More on this in the next subsection. The class object for fields consists of the base type and pairs of square brackets, such as double [] []. Class.

Listing 25.2com / tutego / island / meta / CheckClassType.java, CheckClassType

class CheckClassType {public static void main (String [] args) {checkClassType (Observer.class); checkClassType (Observable.class); checkClassType ((new int [2] [3] [4]). getClass ()); checkClassType (Integer.TYPE); } static void checkClassType (Class c) ​​{if (c.isArray ()) System.out.println (c.getName () + "is a field."); else if (c.isPrimitive ()) System.out.println (c + "is a primitive type."); else if (c.isInterface ()) System.out.println (c.getName () + "is an interface."); else System.out.println (c.getName () + "is a class."); }}

The output of the program is now:

java.util.Observer is an interface. java.util.Observable is a class. [[[I is a field. int is a primitive type.

final class java.lang.Class implements Serializable, GenericDeclaration, Type, AnnotatedElement

  • boolean isInterface () Returns true if the class object describes an interface.
  • boolean isArray () Returns true if the Class object describes an array type.
  • boolean isPrimitive () Tests whether the class object describes a primitive data type.

Component type for fields

The getComponentType () method returns the type of the elements as a class object for fields. If the Class object does not represent a field, the method return is null.

System.out.println (double []. Class.getComponentType ()); // double System.out.println (double [] []. class.getComponentType ()); // class [D System.out.println (double.class.getComponentType ()); // zero

25.2.3 The name of the class

If the Class object is available for a class, we can output its fully qualified name at runtime using the getName () method. Since every type has a name, this method leads to the goal every time:

Listing 25.3SampleName.java

String n1 = new java.util.Date (). GetClass (). GetName (); System.out.println (n1); // java.util.Date String n2 = java.util.RandomAccess.class.getName (); System.out.println (n2); // java.util.RandomAccess String n3 = Deprecated.class.getName (); System.out.println (n3); // java.lang.Deprecated String n4 = Thread.State.class.getName (); System.out.println (n4); // java.lang.Thread $ State

Coding of fields *

The coding is more difficult with array types, which are a special form of classes. getName () encodes it with a leading "[". Each bracket stands for a dimension of the array type. The type of the array elements follows in a coded form after the brackets. So delivers

System.out.println (int [] [] []. Class.getName ()); // [[[I System.out.println ((new int [2] [3] [4]). GetClass (). GetName ()); // [[[I

the string »[[[I«, ie a three-dimensional array type with array elements of the primitive type int. The element type is coded as follows:


AbbreviationData type

B.

byte

C.

Char

D.

Double

F.

Float

I.

Int

J

Long

Element type;

Class or interface, such as [Ljava.lang.String; or [Ljava.awt.Point;

S.

Short

Z

Boolean


If the array accepts object references, their type is encoded in the form »Lclassname;«. (New Object [3]). GetClass (). GetName () returns the string [Ljava.lang.Object ;. As usual, the class or interface name is fully qualified.

The string is also important for Class.forName (). In the case of arrays, the method returns a class object for the element type. The first attempts to get a Class object for fields fail because of a ClassNotFoundException:

Class.forName ("String []"); Class.forName ("java.lang.String []");

The class name is not fully qualified in the first statement, and the string is also incorrectly constructed in the second statement;

out.println (Class.forName ("[Ljava.lang.String;")); // class [Ljava.lang.String;

If the question arises as to whether a class object stands for a field of objects or for a primitive field, the result of getName () can be evaluated:

public static boolean isObjectArray (Class clazz) {if (clazz! = null && clazz.isArray ()) return clazz.getName (). startsWith ("[L"); return false; }

So delivers:

System.out.println (isObjectArray (Object []. Class)); // true System.out.println (isObjectArray (int []. class)); // false System.out.println (isObjectArray (Object.class)); // false

toString ()

We are also aware of a second method for outputting class instances in a human-readable manner: the toString () method. It is essentially based on getName (), but also inserts the type of class represented (normal class, interface or primitive data type):

public String toString () {return (isInterface ()? "interface": (isPrimitive ()? "": "class")) + getName (); }

final class java.lang.Class implements Serializable, GenericDeclaration, Type, AnnotatedElement

  • String getName () Returns the fully qualified name of the represented class or interface or the represented array type or the primitive data type for a class instance as a string.
  • String toString () Returns a human-readable string representation of the Class object.

25.2.4 "instanceof" with class objects *

The binary operator instanceof tests whether an object is an instance of a class or the superclass. If the result is true, the object can be addressed under the given type and is therefore assignment-compatible. The right operator for instanceof, the type name, must always be known at compilation time and cannot be specified dynamically, for example by a string.

If the type name is perhaps unknown at compile time, the Class object can help. The isInstance (Object) method is, so to speak, a dynamic instanceof. Applies with the operator

object instanceof ReferenceType

that's the name of the method

ReferenceType-Class-Objekt.isInstance (object)

The fact that with the isInstance () method both operands are reversed is certainly something that needs getting used to. Here are a few examples:

Listing 25.4IsAssignableFrom.java, main ()

Component b = new JLabel (); out.println (b instanceof JLabel); // true out.println (JLabel.class.isInstance (b)); // true out.println (Object.class.isInstance (b)); // true out.println (Class.forName ("java.awt.Component"). isInstance (b)); // true out.println (String.class.isInstance (b)); // false

The isInstance (object) method is of course somewhat limited by the fact that there must always be a test object. IsInstance (object) cannot answer the question of whether the class object of the PublicKey interface is an »is-a-kind-of-serializable«, because then there would have to be an object beforehand. In this case, the Class object offers a second method: isAssignableFrom (Class):

Class Clazz = Serializable.class; out.println (clazz.isAssignableFrom (String.class)); // true out.println (clazz.isAssignableFrom (Thread.class)); // false out.println (clazz.isAssignableFrom (PublicKey.class)); // true

As long as the type name is known at compile time, instanceof is still the best solution. But if the class is only given by a Class object, isAssignableFrom () still remains. The clazz.isInstance (obj) method is, so to speak, a short form of clazz.isAssignableFrom (obj.getClass ()).


25.2.5 Find upper classes *

The class instance for a class gives access to the superclass, the visibility level and other information. GetSuperclass () determines the superclass. The method returns zero if the class object represents an interface or if we are already at the top of the hierarchy, i.e. with the class object for the root class Object. The following program finds all superclasses of a class by repeatedly calling the getSuperclass () method:

Listing 25.5com / tutego / island / meta / ShowSuperclasses.java

Class Subclass = javax.swing.JButton.class; Class Superclass = subclass.getSuperclass (); while (superclass! = null) {String className = superclass.getName (); System.out.println (className); subclass = superclass; superclass = subclass.getSuperclass (); }

A recursive variant would probably be more elegant, but that doesn't matter now.

javax.swing.AbstractButton javax.swing.JComponent java.awt.Container java.awt.Component java.lang.Object

final class java.lang.Class implements Serializable, GenericDeclaration, Type, AnnotatedElement

  • Class getSuperclass () Returns a class instance for the superclass of the class that is represented by the calling class object. If we are already at the top of the inheritance hierarchy for Object or ask about the superclass of an interface, the method returns null.

25.2.6 Implemented interfaces of a class or an interface *

On the one hand, classes have an inheritance relationship to a superclass and, on the other hand, they can implement several interfaces. In turn, interfaces can expand other interfaces. In the case of a class declaration, the implements keyword is followed by a list of the implemented interfaces. This is how the RandomAccessFile class implements the DataOutput, DataInput and Closeable interfaces:

public class RandomAccessFile implements DataOutput, DataInput, Closeable

To list the interfaces for an existing class object, we call getInterfaces (), which returns an array of class objects. From here we know the way to the name: Calling getName () returns the string for the name of the interface.


Example Output the implemented interfaces of RandomAccessFile:

Listing 25.6com / tutego / island / meta / ShowInterfaces.java, main ()

for (Class theInterface: java.io.RandomAccessFile.class.getInterfaces ()) System.out.println (theInterface.getName ());

The output is:

java.io.DataOutput java.io.DataInput java.io.Closeable


25.2.7 Modifiers and the »Modifier« class *

A class declaration can contain modifiers, i.e. keywords that determine visibility, for example. Among other things, these are public, protected, private and final. They appear in front of the keyword class in the class declaration or in front of methods. The modifiers can also be combined: The class Class itself is public final. The getModifiers () method returns the modifiers encrypted as an integer in the return value:

System.out.println (Modifier.class.getModifiers ()); // 1 System.out.println (Modifier.toString (Modifier.class.getModifiers ())); // public

final class java.lang.Class implements Serializable, GenericDeclaration, Type, AnnotatedElement

  • int getModifiers () Returns the modifiers for a class or an interface.

So that we don't have to deal with magic number values ​​of the JVM during decryption, there are some static methods in the java.lang.reflect.Modifier class that test this integer. In addition, constants are declared (such as Modifier.PUBLIC) with which this integer value can be compared. Since the integer potentially encodes a combination of several modifiers, the targeted query is easier with the static isXXX () methods. Although a class cannot be transient, synchronized, or native, we list all static methods here, as we will later use these modifiers to examine methods and object or class variables via reflection. Each of these test methods returns true if the requested modifier is included in the encoded integer value. All methods are static and return a Boolean result, with the exception of toString ().


class java.lang.reflect.Modifier

  • static boolean isAbstract (int mod)
  • static boolean isFinal (int mod)
  • static boolean isInterface (int mod)
  • static boolean isNative (int mod)
  • static boolean isPrivate (int mod)
  • static boolean isProtected (int mod)
  • static boolean isPublic (int mod)
  • static boolean isStatic (int mod)
  • static boolean isSynchronized (int mod)
  • static boolean isTransient (int mod)
  • static boolean isVolatile (int mod)

Let's consider the toString () method of the Modifier class. There we find a list of all possible modifiers with the constants:

public static String toString (int mod) {StringBuffer sb = new StringBuffer (); int len; if ((mod & PUBLIC)! = 0) sb.append ("public"); if ((mod & PRIVATE)! = 0) sb.append ("private"); if ((mod & PROTECTED)! = 0) sb.append ("protected"); / * Canonical order * / if ((mod & ABSTRACT)! = 0) sb.append ("abstract"); if ((mod & STATIC)! = 0) sb.append ("static"); if ((mod & FINAL)! = 0) sb.append ("final"); if ((mod & TRANSIENT)! = 0) sb.append ("transient"); if ((mod & VOLATILE)! = 0) sb.append ("volatile"); if ((mod & NATIVE)! = 0) sb.append ("native"); if ((mod & SYNCHRONIZED)! = 0) sb.append ("synchronized"); if ((mod & INTERFACE)! = 0) sb.append ("interface"); if ((len = sb.length ())> 0) / * trim trailing space * / return sb.toString (). substring (0, len-1); return ""; }

Note Interfaces such as java.io.Serializable have the "abstract" modifier.

int modifier = Serializable.class.getModifiers (); out.println (modifier); // 1537 out.println (Modifier.toString (modifier)); // public abstract interface


25.2.8 Work in the field *

The utility class java.lang.reflect.Array provides static methods for working generically on array objects. Elements can be queried and set and arrays can also be created with a desired type:

Object array = Array.newInstance (int.class, 20); System.out.println ( Array.getLength (array)); // 20 Array.setInt (array, 0, –1); System.out.println (Array.getInt (array, 0)); // -1

With newInstance () the type is int.class and not int []. Class!

A general static array method set () and get () works for objects, although wrappers can also be used for primitive fields:

Array.set (array, 0, Integer.valueOf (-1)); System.out.println (Array.get (array, 0)); // -1

For multi-dimensional fields, a field of sizes can be specified with newInstance ():

Object array = Array.newInstance (int.class, new int [] {2, 2}); ((int [] []) array) [0] [0] = 1; ((int [] []) array) [1] [1] = 1; System.out.println (Arrays.deepToString ((int [] []) array)); // [[1, 0], [0, 1]]


your comment

How did you like the ? We always look forward to your friendly and critical feedback. >> To the feedback form