Thinking in Java: Chapter 14th type information

Source: Internet
Author: User

The runtime type information allows you to discover and use the type information when the program is running.

14.1 why rtti?

In Java, all type conversions are checked for correctness at runtime. This is also the meaning of rtti: identifying the type of an object at runtime.

14.2 Class Object

Class Object is used to create all the "regular" objects of the class.

All classes are dynamically loaded to the JVM during the first use. When the program creates the first reference to the static member of the class, the class is loaded. This proves that the constructor is also a static method of the class, even if no static keyword is used before the constructor. Therefore, the new object created by the new operator will also be referenced as a static member of the class.

Java programs are not fully loaded before they start to run, and each part of them is loaded only when necessary.

//: typeinfo/SweetShop.java// Examination of the way the class loader works.import static net.mindview.util.Print.*;class Candy {  static { print("Loading Candy"); }}class Gum {  static { print("Loading Gum"); }}class Cookie {  static { print("Loading Cookie"); }}public class SweetShop {  public static void main(String[] args) {    print("inside main");    new Candy();    print("After creating Candy");    try {      Class.forName("Gum");    } catch(ClassNotFoundException e) {      print("Couldn't find Gum");    }    print("After Class.forName(\"Gum\")");    new Cookie();    print("After creating Cookie");  }} /* Output:inside mainLoading CandyAfter creating CandyLoading GumAfter Class.forName("Gum")Loading CookieAfter creating Cookie*///:~

Class

Class (in the Java. lang Package, instances of the class classrepresent classes and interfaces in a running javaapplication ):
In Java, each class has a corresponding class object. That is to say, when we compile a class, after compilation, a class object will be generated in the generated. Class file to indicate the type information of this class;
Three methods for getting a class instance:

  • Call the getclass () method to obtain the class instance of the object;
  • Use the static method forname () of the class to obtain a class instance (staticclass forname (string classname) with the class name) returns the classobject associated with the class or interface with the given stringname .);
  • The. Class method is used to obtain the class instance. For the encapsulation class of the basic data type, you can also use. type to obtain the corresponding class instance of the basic data type.

The default constructor objectnewinstance () in the newinstance () call class (you can refer to the instance of this class when you do not know the name of this class) creates a new instance of the class represented by this classobject.
During running, if we want to generate a class object, the Java Virtual Machine (JVM) checks whether the class object of this type has been loaded. If the file is not loaded, JVM will find the. Class file based on the class name and load it. Once a class object of a certain type has been loaded into the memory, it can be used to generate all objects of this type;

public class ClassTest {     public static void main(String [] args)throws Exception{         String str1="abc";         Class cls1=str1.getClass();         Class cls2=String.class;         Class cls3=Class.forName("java.lang.String");         System.out.println(cls1==cls2);         System.out.println(cls1==cls3);     } }

The returned results are true and true.
Explanation: a virtual machine generates only one bytecode, which can be used to generate multiple instance objects.

The newinstance () method of class is a way to implement the "virtual constructor". The virtual constructor allows you to declare: "I don't know your exact type, but in any case, you must create yourself correctly. The class must have a default constructor.

/*** 2012-2-6 * administrator * // *** @ Author: Liang huanyue * file name: testclass. java * Time: 10:01:52 */public class testclass {public static void main (string [] ARGs) {try {// test class. forname () Class testtypeforname = Class. forname ("testclasstype"); system. out. println ("testforname ---" + testtypeforname); // name of the test class. class class testtypeclass = testclasstype. class; system. out. println ("testtypeclass ---" + testtypeclass); // test the object. getclass () testclasstype testgetclass = new testclasstype (); system. out. println ("testgetclass ---" + testgetclass. getclass ();} catch (classnotfoundexception e) {// todo auto-generated Catch Block E. printstacktrace () ;}} class testclasstype {// constructor public testclasstype () {system. out. println ("---- constructor ---");} // initialize static {system. out. println ("--- static parameter initialization ---");} // non-static parameter initialization {system. out. println ("---- non-static parameter initialization ---");}}

The test results are as follows:

--- Static parameter initialization ---

Testforname --- class testclasstype

Testtypeclass --- class testclasstype

---- Non-static parameter initialization ---

---- Constructor ---

Testgetclass --- class testclasstype

Based on the results, we can find that the three generated class objects are the same. In addition, only one static parameter initialization is printed for the three class objects generated ".
We know that static method property Initialization is initiated when classes are loaded. Instead of static method property initialization, it is loaded when the new class instance object.
Therefore, this program shows that only one class object is generated in three methods. When generating the class object, first determine whether the memory has been loaded. Therefore, the process of generating class objects is actually as follows:
When we compile a new Java class, JVM will compile it into a class object and store it in a. Class file with the same name. During runtime, when an object of this class needs to be generated, JVM will check whether the class has been loaded into memory. If it is not loaded, load the. Class file into the memory. If it is loaded, the instance object is generated based on the class file.

14.2.1 class literal Constants

Literal constants can be used not only for common classes, but also for interfaces, arrays, and basic data types.

It is interesting, but when I use ". Class" to create a reference to a class object, this class object will not be automatically initialized. The preparation for using classes actually involves three steps:

  1. Load, which is executed by the class loader. This step searches for bytecode (usually in the path specified by classpath, but this is not required) and creates a class object from these bytecode.
  2. Link. In the link phase, the bytecode of the class is verified to allocate storage space for the static domain, and if necessary, all references to other classes created by the class will be parsed.
  3. Initialization. If this class has a superclass, initialize it and execute the static initialization tool and the static initialization module.
Initialization is delayed until the static method (the constructor is implicitly static) or the very few static fields are referenced for the first time. If a static domain is not final, you always need to link it before it is read (allocate storage space for this domain) and initialize the bucket ). 14.2.2 generalized class referenceA common class reference can be re-assigned to point to any other class object. By using the generic syntax, the compiler can force an additional type check. Wildcard "? "Represents" everything ".
//: typeinfo/WildcardClassReferences.javapublic class WildcardClassReferences {  public static void main(String[] args) {    Class<?> intClass = int.class;    intClass = double.class;  }} ///:~

//: typeinfo/BoundedClassReferences.javapublic class BoundedClassReferences {  public static void main(String[] args) {    Class<? extends Number> bounded = int.class;    bounded = double.class;    bounded = Number.class;    // Or anything else derived from Number.  }} ///:~

Newinstance () returns the exact type of the object. The getsuperclass () method returns a base class (not an interface), and the compiler knows what type it is during compilation. The cast () method accepts parameter objects and converts them to class-referenced types.14.3 check before type conversionThe rtti format includes:

  • Traditional type conversion;
  • Class Object of the object type. You can query the class object to obtain the information required for running.
  • Keyword instanceof. It returns a Boolean value indicating whether the object is of a specific type.
If explicit type conversion is not used, the compiler will not allow you to perform downward transformation assignment to inform the compiler that you have additional information that makes you know that the type is a specific type. Instanceof has strict restrictions: You can only compare it with the name type, but not the class object.
//: typeinfo/PetCount3.java// Using isInstance()import typeinfo.pets.*;import java.util.*;import net.mindview.util.*;import static net.mindview.util.Print.*;public class PetCount3 {  static class PetCounter  extends LinkedHashMap<Class<? extends Pet>,Integer> {    public PetCounter() {      super(MapData.map(LiteralPetCreator.allTypes, 0));    }    public void count(Pet pet) {      // Class.isInstance() eliminates instanceofs:      for(Map.Entry<Class<? extends Pet>,Integer> pair          : entrySet())        if(pair.getKey().isInstance(pet))          put(pair.getKey(), pair.getValue() + 1);    }    public String toString() {      StringBuilder result = new StringBuilder("{");      for(Map.Entry<Class<? extends Pet>,Integer> pair          : entrySet()) {        result.append(pair.getKey().getSimpleName());        result.append("=");        result.append(pair.getValue());        result.append(", ");      }      result.delete(result.length()-2, result.length());      result.append("}");      return result.toString();    }  }  public static void main(String[] args) {    PetCounter petCount = new PetCounter();    for(Pet pet : Pets.createArray(20)) {      printnb(pet.getClass().getSimpleName() + " ");      petCount.count(pet);    }    print();    print(petCount);  }} /* Output:Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat EgyptianMau Hamster EgyptianMau Mutt Mutt Cymric Mouse Pug Mouse Cymric{Pet=20, Dog=6, Cat=9, Rodent=5, Mutt=3, Pug=3, EgyptianMau=2, Manx=7, Cymric=5, Rat=2, Mouse=2, Hamster=1}*///:~

In addition, you can use isassignablefrom () to perform a runtime check to verify that the objects you pass belong to the inheritance structure we are interested in.

14.4 register a factory
//: typeinfo/RegisteredFactories.java// Registering Class Factories in the base class.import typeinfo.factory.*;import java.util.*;class Part {  public String toString() {    return getClass().getSimpleName();  }  static List<Factory<? extends Part>> partFactories =    new ArrayList<Factory<? extends Part>>();  static {    // Collections.addAll() gives an "unchecked generic    // array creation ... for varargs parameter" warning.    partFactories.add(new FuelFilter.Factory());    partFactories.add(new AirFilter.Factory());    partFactories.add(new CabinAirFilter.Factory());    partFactories.add(new OilFilter.Factory());    partFactories.add(new FanBelt.Factory());    partFactories.add(new PowerSteeringBelt.Factory());    partFactories.add(new GeneratorBelt.Factory());  }  private static Random rand = new Random(47);  public static Part createRandom() {    int n = rand.nextInt(partFactories.size());    return partFactories.get(n).create();  }}class Filter extends Part {}

14.5 equivalence between instanceof and classWhen querying type information, they are in the form of instanceof (that is, in the form of instanceof or isinstance (), and they produce the same result) it is very important to directly compare class objects. Instanceof maintains the concept of type. It refers to "Are you a class, or are you a class derived class? "And if we use a = actual class object, we will not consider inheritance-it is either the exact type, or not. 14.6 The reflection class and the java. Lang. Reflect class library support the concept of reflection. This class library contains the field, method, and constrctor classes (each class implements the member interface ). The real difference between rtti and reflection is that for rtti, the compiler opens or checks the. Class file during compilation. For reflection, the. Class file cannot be obtained during compilation, so it is opened and checked during runtime. Class file. 14.7 dynamic proxy

//: typeinfo/SimpleDynamicProxy.javaimport java.lang.reflect.*;class DynamicProxyHandler implements InvocationHandler {  private Object proxied;  public DynamicProxyHandler(Object proxied) {    this.proxied = proxied;  }  public Object  invoke(Object proxy, Method method, Object[] args)  throws Throwable {    System.out.println("**** proxy: " + proxy.getClass() +      ", method: " + method + ", args: " + args);    if(args != null)      for(Object arg : args)        System.out.println("  " + arg);    return method.invoke(proxied, args);  }}class SimpleDynamicProxy {  public static void consumer(Interface iface) {    iface.doSomething();    iface.somethingElse("bonobo");  }  public static void main(String[] args) {    RealObject real = new RealObject();    consumer(real);    // Insert a proxy and call again:    Interface proxy = (Interface)Proxy.newProxyInstance(      Interface.class.getClassLoader(),      new Class[]{ Interface.class },      new DynamicProxyHandler(real));    consumer(proxy);  }} /* Output: (95% match)doSomethingsomethingElse bonobo**** proxy: class $Proxy0, method: public abstract void Interface.doSomething(), args: nulldoSomething**** proxy: class $Proxy0, method: public abstract void Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@42e816  bonobosomethingElse bonobo*///:~
By calling the static method proxy. newproxyinstance () can create a dynamic proxy. This method requires a Class Loader (you can usually get its Class Loader from the loaded object and pass it to it ), an interface list (not a class or abstract class) that you want the proxy to implement and an implementation of the invocationhandler interface. 14.8 empty object
//: typeinfo/Person.java// A class with a Null Object.import net.mindview.util.*;class Person {  public final String first;  public final String last;  public final String address;  // etc.  public Person(String first, String last, String address){    this.first = first;    this.last = last;    this.address = address;  }  public String toString() {    return "Person: " + first + " " + last + " " + address;  }  public static class NullPerson  extends Person implements Null {    private NullPerson() { super("None", "None", "None"); }    public String toString() { return "NullPerson"; }  }  public static final Person NULL = new NullPerson();} ///:~
//: typeinfo/Position.javaclass Position {  private String title;  private Person person;  public Position(String jobTitle, Person employee) {    title = jobTitle;    person = employee;    if(person == null)      person = Person.NULL;  }  public Position(String jobTitle) {    title = jobTitle;    person = Person.NULL;  }  public String getTitle() { return title; }  public void setTitle(String newTitle) {    title = newTitle;  }  public Person getPerson() { return person; }  public void setPerson(Person newPerson) {    person = newPerson;    if(person == null)      person = Person.NULL;  }  public String toString() {    return "Position: " + title + " " + person;  }} ///:~

14.8.1 simulated object and Pile

The difference between the simulated object and the pile lies in the degree of difference. Simulated objects are usually lightweight and self-tested. Many simulated objects are usually created to handle various test cases. The pile only returns the pile data, which is usually lightweight and often reused between tests.

14.9 interface and type information

Through the type information, the coupling of the interface will still be transmitted-the interface is not an impeccable guarantee for decoupling.

Example 1,

//: typeinfo/InterfaceViolation.java// Sneaking around an interface.import typeinfo.interfacea.*;class B implements A {  public void f() {}  public void g() {}}public class InterfaceViolation {  public static void main(String[] args) {    A a = new B();    a.f();    // a.g(); // Compile error    System.out.println(a.getClass().getName());    if(a instanceof B) {      B b = (B)a;      b.g();    }  }} /* Output:B*///:~

In Example 2, you can use the package permission so that the client cannot use the following Transformation:

//: typeinfo/HiddenImplementation.java// Sneaking around package access.import typeinfo.interfacea.*;import typeinfo.packageaccess.*;import java.lang.reflect.*;public class HiddenImplementation {  public static void main(String[] args) throws Exception {    A a = HiddenC.makeA();    a.f();    System.out.println(a.getClass().getName());    // Compile error: cannot find symbol 'C':    /* if(a instanceof C) {      C c = (C)a;      c.g();    } */    // Oops! Reflection still allows us to call g():    callHiddenMethod(a, "g");    // And even methods that are less accessible!    callHiddenMethod(a, "u");    callHiddenMethod(a, "v");    callHiddenMethod(a, "w");  }  static void callHiddenMethod(Object a, String methodName)  throws Exception {    Method g = a.getClass().getDeclaredMethod(methodName);    g.setAccessible(true);    g.invoke(a);  }} /* Output:public C.f()typeinfo.packageaccess.Cpublic C.g()package C.u()protected C.v()private C.w()*///:~

The problem cannot be solved even if only compiled code is released, because the javap-Private C command can be used to obtain the member information. The-Private flag indicates that all members are displayed for this reason.

class typeinfo.packageacess.C extends java.lang.Object implements typeinfo.interfaca.A{      typeinfo.packageaccess.C();      public void f();      public void g();      void u();      protected void v();      private void w();
}

Example 3: Private internal class

//: typeinfo/InnerImplementation.java// Private inner classes can't hide from reflection.import typeinfo.interfacea.*;import static net.mindview.util.Print.*;class InnerA {  private static class C implements A {    public void f() { print("public C.f()"); }    public void g() { print("public C.g()"); }    void u() { print("package C.u()"); }    protected void v() { print("protected C.v()"); }    private void w() { print("private C.w()"); }  }  public static A makeA() { return new C(); }}public class InnerImplementation {  public static void main(String[] args) throws Exception {    A a = InnerA.makeA();    a.f();    System.out.println(a.getClass().getName());    // Reflection still gets into the private class:    HiddenImplementation.callHiddenMethod(a, "g");    HiddenImplementation.callHiddenMethod(a, "u");    HiddenImplementation.callHiddenMethod(a, "v");    HiddenImplementation.callHiddenMethod(a, "w");  }} /* Output:public C.f()InnerA$Cpublic C.g()package C.u()protected C.v()private C.w()*///:~

Example 4: anonymous internal class

//: typeinfo/AnonymousImplementation.java// Anonymous inner classes can't hide from reflection.import typeinfo.interfacea.*;import static net.mindview.util.Print.*;class AnonymousA {  public static A makeA() {    return new A() {      public void f() { print("public C.f()"); }      public void g() { print("public C.g()"); }      void u() { print("package C.u()"); }      protected void v() { print("protected C.v()"); }      private void w() { print("private C.w()"); }    };  }}public class AnonymousImplementation {  public static void main(String[] args) throws Exception {    A a = AnonymousA.makeA();    a.f();    System.out.println(a.getClass().getName());    // Reflection still gets into the anonymous class:    HiddenImplementation.callHiddenMethod(a, "g");    HiddenImplementation.callHiddenMethod(a, "u");    HiddenImplementation.callHiddenMethod(a, "v");    HiddenImplementation.callHiddenMethod(a, "w");  }} /* Output:public C.f()AnonymousA$1public C.g()package C.u()protected C.v()private C.w()*///:~

Example 5: You can call methods with non-public access permissions through reflection, or use private domains:

//: typeinfo/ModifyingPrivateFields.javaimport java.lang.reflect.*;class WithPrivateFinalField {  private int i = 1;  private final String s = "I'm totally safe";  private String s2 = "Am I safe?";  public String toString() {    return "i = " + i + ", " + s + ", " + s2;  }}public class ModifyingPrivateFields {  public static void main(String[] args) throws Exception {    WithPrivateFinalField pf = new WithPrivateFinalField();    System.out.println(pf);    Field f = pf.getClass().getDeclaredField("i");    f.setAccessible(true);    System.out.println("f.getInt(pf): " + f.getInt(pf));    f.setInt(pf, 47);    System.out.println(pf);    f = pf.getClass().getDeclaredField("s");    f.setAccessible(true);    System.out.println("f.get(pf): " + f.get(pf));    f.set(pf, "No, you're not!");    System.out.println(pf);    f = pf.getClass().getDeclaredField("s2");    f.setAccessible(true);    System.out.println("f.get(pf): " + f.get(pf));    f.set(pf, "No, you're not!");    System.out.println(pf);  }} /* Output:i = 1, I'm totally safe, Am I safe?f.getInt(pf): 1i = 47, I'm totally safe, Am I safe?f.get(pf): I'm totally safei = 47, I'm totally safe, Am I safe?f.get(pf): Am I safe?i = 47, I'm totally safe, No, you're not!*///:~

The final domain is actually safe when it is modified. During runtime, the system will accept any modification attempts without throwing an exception, but no modification will actually occur.

References:

Explanation of class objects in Java
Java class

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.