`
leonzhx
  • 浏览: 766538 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Chapter 5. Inheritance -- Core Java Ninth Edition

阅读更多

1.    The prefixes super and sub come from the language of sets used in theoretical computer science and mathematics. The set of all employees contains the set of all managers, and this is said to be a superset of the set of managers. Or, to put it another way, the set of all managers is a subset of the set of all employees.

2.    When defining a subclass by extending its superclass, you only need to indicate the differences between the subclass and the superclass. When designing classes, you place the most general methods into the superclass and more specialized methods in its subclasses. Factoring out common functionality by moving it to a superclass is common in object-oriented programming.

3.    Some people think of super as being analogous to the this reference. However, that analogy is not quite accurate: super is not a reference to an object. For example, you cannot assign the value super to another object variable. Instead, super is a special keyword that directs the compiler to invoke the superclass method.

4.    Inheritance can never take away any fields or methods.

5.    To initialize the fields of superclass, the constructor is invoked with the special super syntax. The call using super must be the first statement in the constructor for the subclass. If the subclass constructor does not call a superclass constructor explicitly, the no-argument constructor of the superclass is invoked. If the superclass does not have a no-argument constructor and the subclass constructor does not call another superclass constructor explicitly, the Java compiler reports an error.

6.    The fact that an object variable can refer to multiple actual types is called polymorphism. Automatically selecting the appropriate method at runtime is called dynamic binding. Dynamic binding is the default behavior for Java. If you do not want a method to be dynamic binding, you tag it as final.

7.    The “is–a” rule states that every object of the subclass is an object of the superclass. It’s a simple rule can help you decide whether or not inheritance is the right design for your data. Another way of formulating the “is–a” rule is the substitution principle. That principle states that you can use a subclass object whenever the program expects a superclass object.

8.    In the Java programming language, object variables are polymorphic.

9.    In Java, arrays of subclass references can be converted to arrays of superclass references without a cast. However, to make sure no corruption can occur, all arrays remember the element type with which they were created, and they monitor that only compatible references are stored into them. For example, the array created as new Manager[10] remembers that it is an array of managers. Attempting to store an Employee reference causes an ArrayStoreException.

10.    The return type is not part of the signature. However, when you override a method, you need to keep the return type compatible. A subclass may change the return type to a subtype of the original type. The subclass method must be at least as visible as the superclass method.

11.    When a method call is applied to an object:
    a)  The compiler looks at the declared type of the object and the method name. Let’s say we call x.f(param), and the implicit parameter x is declared to be an object of class C. Note that there may be multiple methods, all with the same name, f, but with different parameter types. The compiler enumerates all methods called f in the class C and all accessible methods called f in the superclasses of C.

    b)  Next, the compiler determines the types of the parameters that are supplied in the method call. If among all the methods called f there is a unique method whose parameter types are a best match for the supplied parameters, then that method is chosen to be called. This process is called overloading resolution. If the compiler cannot find any method with matching parameter types or if multiple methods all match after applying conversions, the compiler reports an error.

    c)  If the method is private, static, final, or a constructor, then the compiler knows exactly which method to call. This is called static binding. Otherwise, the method to be called depends on the actual type of the implicit parameter, and dynamic binding must be used at runtime.

    d)  When the program runs and uses dynamic binding to call a method, then the virtual machine must call the version of the method that is appropriate for the actual type of the object to which x refers. Let’s say the actual type is D, a subclass of C. If the class D defines a method f(String), that method is called. If not, D’s superclass is searched for a method f(String), and so on.

It would be time-consuming to carry out this search every time a method is called. Therefore, the virtual machine precomputes for each class a method table that lists all method signatures and the actual methods to be called. When a method is actually called, the virtual machine simply makes a table lookup. In our example, the virtual machine consults the method table for the class D and looks up the method to call for f(String). That method may be D.f(String) or X.f(String), where X is some superclass of D. There is one twist to this scenario. If the call is super.f(param), then the compiler consults the method table of the superclass of the implicit parameter.

12.  Classes that cannot be extended are called final classes, and you use the final modifier in the definition of the class to indicate this. You can also make a specific method in a class final. If you do this, then no subclass can override that method. There is only one good reason to make a method or class final: to make sure that its semantics cannot be changed in a subclass.

13.  Some programmers used the final keyword hoping to avoid the overhead of dynamic binding. If a method is not overridden, and it is short, then a compiler can optimize the method call away—a process called inlining. Fortunately, the just-in-time compiler in the virtual machine can do a better job than a traditional compiler. It knows exactly which classes extend a given class, and it can check whether any class actually overrides a given method. If a method is short, frequently called, and not actually overridden, the just-in-time compiler can inline the method. What happens if the virtual machine loads another subclass that overrides an inlined method? Then the optimizer must undo the inlining. That’s slow, but it happens rarely.

14.  To actually make a cast of an object reference, use a syntax similar to what you use for casting a numeric expression. Surround the target class name with parentheses and place it before the object reference you want to cast.

15.  There is only one reason why you would want to make a cast—to use an object in its full capacity after its actual type has been temporarily forgotten.

16.  The compiler checks that you do not promise too much when you store a value in a variable. If you assign a subclass reference to a superclass variable, you are promising less, and the compiler will simply let you do it. If you assign a super-class reference to a subclass variable, you are promising more. Then you must use a cast so that your promise can be checked at runtime. When the program runs, the Java runtime system notices the broken promise and generates a ClassCastException.

17.  You can cast only within an inheritance hierarchy. The compiler will not let you make a cast if there is no chance for the cast to succeed. Use instanceof to check before casting from a superclass to a subclass.

18.  If you use the abstract keyword, you do not need to implement the method at all. A class with one or more abstract methods must itself be declared abstract. When you extend an abstract class, you have two choices. You can leave some or all of the abstract methods undefined; then you must tag the subclass as abstract as well. Or you can define all methods, and the subclass is no longer abstract.

19.  Abstract classes cannot be instantiated. A class can even be declared as abstract even though it has no abstract methods.

20.  Methods of a specific subclass can peek inside the protected field or method of its instances only, not of other subclass (even the superclass) instances. (Except it’s in the same package of the superclass) This restriction is made so that you can’t abuse the protected mechanism by forming subclasses just to gain access to the protected fields.

21.  Here is a summary of the four access modifiers in Java that control visibility:
    a)  Visible to the class only (private).
    b)  Visible to the world (public).
    c)  Visible to the package and all subclasses (protected).
    d)  Visible to the package—the (unfortunate) default. No modifiers are needed.

22.  In Java, only the primitive types (numbers, characters, and boolean values) are not objects. All array types, no matter whether they are arrays of objects or arrays of primitive types, are class types.

23.  The equals method, as implemented in the Object class, determines whether two object references are identical.

24.  The call Objects.equals(a, b) returns true if both arguments are null, false if only one is null, and calls a.equals(b) otherwise.

25.  When you define the equals method for a subclass, first call equals on the superclass. If that test doesn’t pass, then the objects can’t be equal. If the super-class fields are equal, then you are ready to compare the instance fields of the subclass.

26.  The Java Language Specification requires that the equals method has the following properties:
    a)  It is reflexive: For any non-null reference x, x.equals(x) should return true.
    b)  It is symmetric: For any references x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
    c)  It is transitive: For any references x, y, and z, if x.equals(y) and y.equals(z) return true, then x.equals(z) should return true.
    d)  It is consistent: If the objects to which x and y refer haven’t changed, then repeated calls to x.equals(y) return the same value.
    e)  For any non-null reference x, x.equals(null) should return false.

27.  Using instanceof instead of getClass() to check the type of parameter in equals() may break the symmetric rule. The way we see it, there are two distinct scenarios:
    a)  If subclasses can have their own notion of equality, then the symmetry requirement forces you to use the getClass test.
    b)  If the notion of equality is fixed in the superclass, then you can use the instanceof test and allow objects of different subclasses to be equal to one another.   

 

28.  Check out the API documentation of the java.sql.Timestamp class, where the implementers note with some embarrassment that they have painted themselves in a corner. The Timestamp class inherits from java.util.Date, whose equals method uses an instanceof test, and it is impossible to override equals to be both symmetric and accurate.

29.  Here is a recipe for writing the perfect equals method:
    a)  Name the explicit parameter otherObject—later, you will need to cast it to another variable that you should call other.
    b)  Test whether this happens to be identical to otherObject:
         if (this == otherObject) return true;
    c)  Test whether otherObject is null and return false if it is:
         if (otherObject == null) return false;
    d)  Compare the classes of this and otherObject. If the semantics of equals can change in subclasses, use the getClass test:
         if (getClass() != otherObject.getClass()) return false;
         If the same semantics holds for all subclasses, you can use an instanceof test:
         if (!(otherObject instanceof ClassName)) return false;
    e)  Cast otherObject to a variable of your class type:
         ClassName other = (ClassName) otherObject;
    f)  Now compare the fields, as required by your notion of equality. Use == for primitive type fields, Objects.equals for object fields. Return true if all fields match, false otherwise.
    If you redefine equals in a subclass, include a call to super.equals(other).

30.  If you have fields of array type, you can use the static Arrays.equals method to check that the corresponding array elements are equal.

31.  Hash codes should be scrambled—if x and y are two distinct objects, there should be a high probability that x.hashCode() and y.hashCode() are different.

32.  The hashCode method is defined in the Object class. Therefore, every object has a default hash code. That hash code is derived from the object’s memory address. No hashCode method has been defined for the StringBuilder class (neither has equals method).

33.  As of Java 7, it is better to use the null-safe method Objects.hashCode. It returns 0 if its argument is null and the result of calling hashCode on the argument otherwise. Even better, when you need to combine multiple hash values, call Objects.hash with all of them. It actually invokes Arrays.hashCode.

34.  Your definitions of equals and hashCode must be compatible: If x.equals(y) is true, then x.hashCode() must return the same value as y.hashCode().

35.  Most (but not all) toString methods follow this format: the name of the class, followed by the field values enclosed in square brackets.The Object class defines the toString method to print the class name plus @ plus the hash code of the object.

36.  Whenever an object is concatenated with a string by the “+” operator, the Java compiler automatically invokes the toString method to obtain a string representation of the object.

37.  The ArrayList class is similar to an array, but it automatically adjusts its capacity as you add and remove elements, without any additional code. It is a generic class with a type parameter to specify the type of the element objects that the array list holds.

38.  As of Java 7, you can omit the type parameter on the right-hand side:
ArrayList<Employee> staff = new ArrayList<>();
The compiler checks what happens to the new value. If it is assigned to a variable, passed into a method, or returned from a method, then the compiler checks the generic type of the variable, parameter, or method. It then places that type into the <>.

Commented by Sean: “passed into a method” is not allowed, Compiler will not infer the actual type parameter.

39.  If you call ArrayList.add and the internal array is full, the array list automatically creates a bigger array and copies all the objects from the smaller to the bigger array. If you already know, or have a good guess, how many elements you want to store, call the ArrayList.ensureCapacity method before filling the array list. You can also pass an initial capacity to the ArrayList constructor.

40.  Once you are reasonably sure that the array list is at its permanent size, you can call the ArrayList.trimToSize method. This method adjusts the size of the memory block to use exactly as much storage space as is required to hold the current number of elements. The garbage collector will reclaim any excess memory.

41.  Do not call list.set(i, x) until the size of the array list is larger than i. Use the add method instead of set to fill up an array, and use set only to replace a previously added element.

42.  For compatibility, the compiler translates all typed array lists into raw ArrayList objects after checking that the type rules were not violated. In a running program, all array lists are the same—there are no type parameters in the virtual machine. You can assign a typed ArrayList to a raw one without any error or warning. Conversely, when you assign a raw ArrayList to a typed one, you get a warning.

43.  The wrapper classes have obvious names: Integer, Long, Float, Double, Short, Byte, Character, Void, and Boolean. (The first six inherit from the common superclass Number.) The wrapper classes are immutable and final.

44.  The == operator, applied to wrapper objects, only tests whether the objects have identical memory locations.

45.  The autoboxing specification requires that boolean, byte, char127, short, and int between -128 and 127 are wrapped into fixed objects.

46.  Finally, let us emphasize that boxing and unboxing is a courtesy of the compiler, not the virtual machine. The compiler inserts the necessary calls when it generates the bytecodes of a class. The virtual machine simply executes those bytecodes.

47.  The Object... parameter type is exactly the same as Object[]. It is legal to pass an array as the last parameter of a method with variable parameters. It is legal to pass no values for varargs parameter but illegal for array parameter.

Commented by Sean: You can pass objects separated by comma to methods with variable parameter, but you can't do that for methods with array parameter.

48.  You can add constructors, methods, and fields to an enumerated type. Of course, the constructors are only invoked when the enumerated constants are constructed.

49.  All enumerated types are subclasses of the class Enum.

50.  Each enumerated type has a static values method that returns an array of all values of the enumeration. The ordinal method yields the position of an enumerated constant in the enum declaration, counting from zero.

51.  You can use reflection to:
    a)  Analyze the capabilities of classes at runtime;
    b)  Inspect objects at runtime—for example, to write a single toString method that works for all classes;
    c)  Implement generic array manipulation code;
    d)  Take advantage of Method objects that work just like function pointers in languages such as C++.

52.  The Java runtime system always maintains what is called runtime type identification on all objects. This information keeps track of the class to which each object belongs. Runtime type information is used by the virtual machine to select the correct methods to execute. You can also access this information by working with a special Java class. The class that holds this information is called Class. The getClass() method in the Object class returns an instance of Class type.

53.  Class.getName() returns the name of the class. The package name is part of the class name.

54.  You can obtain a Class object corresponding to a class name by using the static forName method.

55.  At startup, the class containing your main method is loaded. It loads all classes that it needs. Each of those loaded classes loads the classes that it needs, and so on. You can give users of your program the illusion of a faster start with the following trick: Make sure that the class containing the main method does not explicitly refer to other classes. Then manually force the loading of other classes by calling Class.forName.

56.  If T is any Java type, then T.class is the matching class object.

57.  Note that a Class object really describes a type, which may or may not be a class. For example, int is not a class, but int.class is nevertheless an object of type Class.

58.  The virtual machine manages a unique Class object for each type. Therefore, you can use the == operator to compare class objects.

59.  newInstance() lets you create an instance of a class on the fly. It calls the no-argument constructor to initialize the newly created object. An exception is thrown if the class does not have a no-argument constructor. If you need to provide parameters for the constructor of a class, you must use the newInstance method in the Constructor class.

60.  There are two kinds of exceptions: unchecked exceptions and checked exceptions. With checked exceptions, the compiler checks that you provide a handler.

61.  The three classes Field, Method, and Constructor in the java.lang.reflect package describe the fields, methods, and constructors of a class, respectively. All three classes have a method called getName that returns the name of the item. The Field class has a method getType that returns an object, again of type Class, which describes the field type. The Method and Constructor classes have methods to report the types of the parameters, and the Method class also reports the return type. All three of these classes also have a method called getModifiers that returns an integer, with various bits turned on and off, that describes the modifiers used, such as public and static. You can then use the static methods in the Modifier class in the java.lang.reflect package to analyze the integer that getModifiers returns. Use methods like isPublic, isPrivate, or isFinal in the Modifier class to tell whether a method or constructor was public, private, or final. You can also use the Modifier.toString method to print the modifiers. The getFields, getMethods, and getConstructors methods of the Class class return arrays of the public fields, methods, and constructors that the class supports. This includes public members of superclasses. The getDeclaredFields, getDeclaredMethods, and getDeclaredConstructors methods of the Class class return arrays consisting of all fields, methods, and constructors that are declared in the class. This includes private, package, and protected members, but not members of superclasses.

62.  getDeclaringClass() returns the Class object for the class that defines the constructor, method, or field.

63.  If f is an object of type Field (for example, one obtained from getDeclaredFields) and obj is an object of the class of which f is a field, then f.get(obj) returns an object whose value is the current value of the field of obj. If the field to be got is not accessible (i.e. private) , the get method will throw an IllegalAccessException. You can override access control by invoke the setAccessible method on a Field, Method, or Constructor object. AccessibleObject.setAccessible can help to set access for array of AccessibleObject objects. The call f.set(obj, value) sets the field represented by f of the object obj to the new value.

64.  The Array class in the java.lang.reflect package allows you to create arrays dynamically. This is used in the implementation of the copyOf method in the Arrays class.

65.  A Java array remembers the type of its entries—that is, the element type used in the new expression that created it. It is legal to cast an Employee[] temporarily to an Object[] array (no need for explicit cast) and then cast it back, but an array that started its life as an Object[] array can never be cast into an Employee[] array.

66.  The static newInstance method of the Array class constructs a new array. You must supply the type for the entries and the desired length as parameters to this method. The static getLength method of the Array class returns the length of an array. The isArray method of the Class class test whether the Class objects represent arrays. The getComponentType method of the Class class (which is defined only for class objects that represent arrays) to find the right type for the array.

67.  The integer array type int[] can be converted to an Object, but not to Object[].

68.   The Method class has an invoke method that lets you call the method that is wrapped in the current Method object. The first parameter is the implicit parameter, and the remaining objects provide the explicit parameters. For a static method, the first parameter is ignored—you can set it to null.

69.  Code that uses reflection to invoke methods is significantly slower than code that simply calls methods directly. Using interfaces and inner classes for the callbacks leads to code that runs faster and is a lot more maintainable.

70.  Design Hints for Inheritance:
    a)  Place common operations and fields in the superclass.
    b)  Don’t use protected fields.
    c)  Use inheritance to model the “is–a” relationship.
    d)  Don’t use inheritance unless all inherited methods make sense.
    e)  Don’t change the expected behavior when you override a method.
    f)  Use polymorphism, not type information.
    g)  Don’t overuse reflection.

 

71.  The following codes print all features of a class:

public class ReflectionTest
 {
    public static void main(String[] args)
    {
       // read class name from command line args or user input
       String name;
       if (args.length > 0) name = args[0];
       else
       {
          Scanner in = new Scanner(System.in);
          System.out.println("Enter class name (e.g. java.util.Date): ");
          name = in.next();
       }

       try
       {
          // print class name and superclass name (if != Object)
          Class cl = Class.forName(name);
          Class supercl = cl.getSuperclass();
          String modifiers = Modifier.toString(cl.getModifiers());
          if (modifiers.length() > 0) System.out.print(modifiers + " ");
          System.out.print("class " + name);
          if (supercl != null && supercl != Object.class) System.out.print(" extends "
                + supercl.getName());

          System.out.print("\n{\n");
          printConstructors(cl);
          System.out.println();
          printMethods(cl);
          System.out.println();
          printFields(cl);
          System.out.println("}");
       }
       catch (ClassNotFoundException e)
       {
          e.printStackTrace();
       }
       System.exit(0);
    }

    /**
     * Prints all constructors of a class
     * @param cl a class
     */
    public static void printConstructors(Class cl)
    {
       Constructor[] constructors = cl.getDeclaredConstructors();

       for (Constructor c : constructors)
       {
          String name = c.getName();
          System.out.print("  ");
          String modifiers = Modifier.toString(c.getModifiers());
          if (modifiers.length() > 0) System.out.print(modifiers + " ");
          System.out.print(name + "(");

          // print parameter types
          Class[] paramTypes = c.getParameterTypes();
          for (int j = 0; j < paramTypes.length; j++)
          {
             if (j > 0) System.out.print(", ");
             System.out.print(paramTypes[j].getName());
          }
          System.out.println(");");
       }
    }

    /**
     * Prints all methods of a class
     * @param cl a class
     */
    public static void printMethods(Class cl)
    {
       Method[] methods = cl.getDeclaredMethods();
       for (Method m : methods)
       {
          Class retType = m.getReturnType();
          String name = m.getName();

          System.out.print("  ");
          // print modifiers, return type and method name
          String modifiers = Modifier.toString(m.getModifiers());
          if (modifiers.length() > 0) System.out.print(modifiers + " ");
          System.out.print(retType.getName() + " " + name + "(");

          // print parameter types
          Class[] paramTypes = m.getParameterTypes();
          for (int j = 0; j < paramTypes.length; j++)
          {
             if (j > 0) System.out.print(", ");
             System.out.print(paramTypes[j].getName());
          }
          System.out.println(");");
        }
    }

    /**
     * Prints all fields of a class
     * @param cl a class
     */
    public static void printFields(Class cl)
    {
       Field[] fields = cl.getDeclaredFields();

       for (Field f : fields)
       {
          Class type = f.getType();
          String name = f.getName();
          System.out.print("  ");
          String modifiers = Modifier.toString(f.getModifiers());
          if (modifiers.length() > 0) System.out.print(modifiers + " ");
          System.out.println(type.getName() + " " + name + ";");
       }
    }
}

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics