Java反射

1、Class对象

   Class类对象包含了一个类的各种信息,它的域、方法、接口等等。每一个类都有一个Class对象,即每当编写一个新类,就会产生一个Class对象(更恰当的说,是被保存在一个同名的.class文件中)。为了生成这个类的对象,运行这个程序的JVM将使用被称为"类加载器"的子系统。获取一个类对象有三种方式:

  1.1、Class.forName("类的全限定名")

   传给Class.forName()的参数必须是全限定名(包括包名),这个方法是Class类(所有Class对象都属于该类)的一个static成员。  该方法会先找到参数里的类,如果没有加载就加载它,然后进行初始化,如果没有找到该类,会抛出ClassNotFounfException的  异常,找到就会返回一个Class对象的引用。

  1.2、getClass()

   如果已经拥有了一个类的对象,那么可以通过调用该方法来获取Class的阴影,这个方法属于Object类的一部分,它表示返回该  对象的实际类型的Class引用。

  1.3、.class

   还可以通过类字面常量来生成对Class对象的引用,例如String.class。此方法创建对Class的引用时,不会自动地初始化该   Class对象,初始化被延迟到了对静态方法(构造器隐式地是静态的)或者非常数静态域进行首次引用时才执行。
   虚拟机为每个类型管理一个Class对象。因此可以使用==运算符来实现两个类对象的比较操作,例如:
   if(e.getClass == String.class) ....

2、泛化的Class引用

     可以通过泛型来对Class引用所指向的Class对象的类型进行限定。例如下面的两种语法都是对的:

public class GenericClassReferences {
  public static void main(String[] args) {
    Class intClass = int.class;
    Class<Integer> genericIntClass = int.class;
    genericIntClass = Integer.class; // Same thing
    intClass = double.class;
    // genericIntClass = double.class;会报错
  }
}

  可以看到,通过使用泛型语法,可以让编译器强制执行额外的类型检查。如果你操作有误,稍后立即就会发现,使用普通的Class引用只有运行期才会发现,就会显得很不方便。另外也可以使用通配符与extends关键字结合使用,创建一个范围。

public 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.
  }
}

        另外可以使用Class.newInstance()方法来生成一个类的实例,但是该类必须有无惨构造器,不然会抛出异常。例如:

public class Test {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException{
	String s = "java.util.Date";   
	Object m = Class.forName(s).newInstance(); //编译期间就已经确定,所以要进行异常处理
    }
}

  当然也可以与泛型语法结合,返回一个确切的类型,而不仅仅Object类型。

public class GenericToyTest {
  public static void main(String[] args) throws Exception {
    Class<FancyToy> ftClass = FancyToy.class;//确切的类型
    FancyToy fancyToy = ftClass.newInstance();
    Class<? super FancyToy> up = ftClass.getSuperclass();//获取父类
    // Class<Toy> up2 = ftClass.getSuperclass();不能用此种方法获取父类
    Object obj = up.newInstance();//只能生成Object实例,而不是具体类型
  }
} 

     另外还可以通过关键字instanceof来判断某个对象是不是某个特定类型的实例,并且它还保留了类型的概念,即"你是这个类吗,或者你是这个类的派生类么?"。使用时就像这样:

 if(x instanceof Dog)

   ((Dog)x).bark();

  而对于Class对象可以直接使用Class.isInstance方法来进行判断:"类对象".isInstance("实例")。

3、RTTI和反射

     在这个例子中,当把shape对象放入List<Shape>中时会发生向上转型。但在向上转型的时候也丢失了Shape的对象类型。对于集合而言,它们只是Object类的对象。

  当从集合中取出元素后,这种容器——实际上都当作Object持有——会自动转型回为Shape。这是RTTI最基本的使用形式,因为在Java中,所有的对象类型都是在运行时来进行正确性检查的。这也是RTTI名字的含义:在运行时,识别一个对象的类型。

  在这里,RTTI转型并不彻底:Object被转型为Shape,而不是Cirle、Square、Triangle。这是因为目前我们只知道这个List<Shape>保存的都是Shape。在编译时,由容器和泛型来强制确保这一点;而在运行时,由类型转换来确保这一点。

  接下来就是多态的事情了,Shape对象实际执行什么样的代码,是由引用所指向的具体对象Circle、Square或者Triangle来决定的。 

abstract class Shape {
  void draw() { System.out.println(this + ".draw()"); }
  abstract public String toString();
}

class Circle extends Shape {
  public String toString() { return "Circle"; }
}

class Square extends Shape {
  public String toString() { return "Square"; }
}

class Triangle extends Shape {
  public String toString() { return "Triangle"; }
}	

public class Shapes {
  public static void main(String[] args) {
    List<Shape> shapeList = Arrays.asList(
      new Circle(), new Square(), new Triangle()
    );
    System.out.println(shapeList.get(0).getClass());
    for(Shape shape : shapeList){
    	 System.out.println(shape.getClass());
    	 shape.draw();
    }
   
     
  }
} /* 输出
Circle.draw()
Square.draw()
Triangle.draw()

      如果不知道某个对象的确切类型,RTTI可以告诉你。但是有一个限制:这个类型在便宜期间必须已知,这样才能使用RTTI识别它。换句话说,在编译时,编译器必须知道所有要通过RTTI来出来里的类。而对于反射来说,可以在创建一个在编译期间完全未知的对象,并调用它的方法。

  因此RTTI与反射的区别只在于:对RTTI来说,编译器在编译时打开和检查.class文件,即我们可以使用"普通"的方式来调用对象的所有方法。而对于反射机制,.class文件在编译时是不可取的,所以在运行期间打开和检查.class文件。

  通过java ShowMethods ShowMethods命令来调用。

public class ShowMethods {
  private static String usage =
    "usage:\n" +
    "ShowMethods qualified.class.name\n" +
    "To show all methods in class or:\n" +
    "ShowMethods qualified.class.name word\n" +
    "To search for methods involving 'word'";
  private static Pattern p = Pattern.compile("\\w+\\.");
  public static void main(String[] args) {
    if(args.length < 1) {
      print(usage);
      System.exit(0);
    }
    int lines = 0;
    try {
      Class<?> c = Class.forName(args[0]);
      Method[] methods = c.getMethods();
      Constructor[] ctors = c.getConstructors();
      if(args.length == 1) {
        for(Method method : methods)
          print(
            p.matcher(method.toString()).replaceAll(""));
        for(Constructor ctor : ctors)
          print(p.matcher(ctor.toString()).replaceAll(""));
        lines = methods.length + ctors.length;
      } else {
        for(Method method : methods)
          if(method.toString().indexOf(args[1]) != -1) {
            print(
              p.matcher(method.toString()).replaceAll(""));
            lines++;
          }
        for(Constructor ctor : ctors)
          if(ctor.toString().indexOf(args[1]) != -1) {
            print(p.matcher(
              ctor.toString()).replaceAll(""));
            lines++;
          }
      }
    } catch(ClassNotFoundException e) {
      print("No such class: " + e);
    }
  }
} /* Output:
public static void main(String[])
public native int hashCode()
public final native Class getClass()
public final void wait(long,int) throws InterruptedException
public final void wait() throws InterruptedException
public final native void wait(long) throws InterruptedException
public boolean equals(Object)
public String toString()
public final native void notify()
public final native void notifyAll()
public ShowMethods()
*///:~

  Class类与java.lang.reflect包一起对反射的概念进行了支持,该包包括了Field、Method以及Constructor类。这些类型的对象是由JVM在运行时创建的,用以表示未知类里对应的成员。这样就可以使用Constructor创建新的对象,用get()和set()方法读取和修改与Field对象关联的字段,用invoke()方法(动态代理模式里写)调用与Method对象关联的方法。另外还能使用getFields(),getMethod(),getConstructor()等方法返回表示字段、方法、构造器的对象数组。这样,匿名对象的类信息就在运行时能完全确定下来,而不需要在编译期知道任何信息。

  Employee harry = new Employee("Harry Hacker", 3500);//获取该类的实例

  Class cl = harry.getClass  //获取类对象

  Field f = cl.getDeclaredField("name");

  Object v = f.get(harry);//获取f的当前值

  反射机制默认行为受限于Java的访问控制。然而可以调用Field、Method或者Constructor对象的setAccessible方法,例如:

f.setAccessible(true),即可访问私有域的数据。当然也可以调用f.set(obj, value)进行设置新值。而获得构造器后可以通过newInstance()或者newInstance("参数")来进行实例化。获得方法后需调用invoke来进行方法调用。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值