Class 类和 Class 对象
Java 虚拟机在运行时,知道一块内存到底指向的是什么对象。比如:
java">class A{public A(){}public void method(){System.out.println("A's method has been called.");}
}
// 定义了 B 是 A 的子类
class B extends A{public B(){}public void method(){System.out.println("B's method has been called.");}
}
// 某个代码段:
A obj = new B();
obj.method();
虚拟机知道,obj
指向的内存是一个B
对象(运行时类型),而不是A
对象(引用类型)。这是通过获得obj
的class
成员得知的。所以,根据多态性,实际运行时obj.method()
会调用B
类的method
方法,从而在控制台打印出B's method has been called.
虚拟机是怎么知道obj
指向的内存是一个B
对象的?
每个类都有一个Class
类型的class
成员,比如A.class
就包含了类A
的类型信息。A.class
是在A
类编译时产生的。
Class
类中提供了很多方法。其中,getName
返回该类的完全限定类名,getSimpleName
返回该类的简单类名。
以下面的代码为例:
java">package hust.cs.javacourse.ch13;public class ClassTest {public static void main(String[] args){System.out.println(ClassTest.class.getName());System.out.println(ClassTest.class.getSimpleName());}
}
它的运行结果是:
hust.cs.javacourse.ch13.ClassTest
ClassTest
如何获取 Class 对象
通过类
上文中已经提到,通过类名.class
可以直接获得该类的class
对象。
此外,Class
类提供了静态方法forName(String className)
,可以通过类的完全限定类名className
获得这个类的class
对象;同时,还提供了实例方法getSuperClass()
获取其直接父类的class
对象。
当
forName(String className)
无法找到完全限定类名为className
的对象时,方法会抛出必检异常ClassNotFoundException
。所以通过类名.class
获取class
对象是一种比较安全的方法。
java">package hust.cs.javacourse.ch13;class Person{}class Employee extends Person{}class Manager extends Employee{}public class ClassDemo {public static void main(String[] args){try {Class clz = Class.forName("hust.cs.javacourse.ch13.Manager"); //参数是类完全限定名字符串System.out.println(clz.getName()); //产生完全限定名hust.cs.javacourse.ch13.ManagerSystem.out.println(clz.getSimpleName()); //产生简单名ManagerClass superClz = clz.getSuperclass(); //获得直接父类型信息System.out.println(superClz.getName()); //产生完全限定名hust.cs.javacourse.ch13.EmployeeSystem.out.println(superClz.getSimpleName()); //产生简单名Employee} catch (ClassNotFoundException e) {e.printStackTrace();}}}
运行上面的代码,得到以下结果:
hust.cs.javacourse.ch13.Manager
Manager
hust.cs.javacourse.ch13.Employee
Employee
通过对象
任何一个对象在运行时还能够调用getClass()
方法获取该对象的运行时类型。getClass
方法是Object
类中的final
方法。
反射机制
看完上面的内容,你可能觉得class
对象用处不大。但是反射机制中恰恰能够发挥class
对象的用处。
实例化对象
考虑实现下面的功能:从System.in
中获取类的完全限定类名,构建一个相应的类的实例对象。有人可能会这样写:
java">// 从System.in中获取输入的字符串input
Object o = null;
if( input.equals("java.lang.String") ){o = new String("");
}
else if( input.equals("ch13.Student") ){o = new Student();
}
// ……更多的else if语句
这种一个个枚举的方法显然不行,因为事先不知道会输入一个什么类的完全限定名字符串,if
语句不可能列出所有可能的类型。这时,我们可以利用反射机制,编写以下的代码:
java">// 从System.in中获取输入的字符串input
try{Class clz = Class.forName();Object o = clz.newInstance();// 调用该类的无参构造函数
} catch (Exception e) {e.printStackTrace();
}
即可成功创建实例对象o
。
获取类的构造器或方法
获取所有构造器或方法
Class
类中提供了实例方法getConstructors()
获得某个类的所有构造器,提供了实例方法getMethods()
获得某个类的所有方法(包括从父类继承的方法),提供了实例方法getDeclaredMethods()
获得某个类中定义的所有方法。比如下面的代码:
java">package hust.cs.javacourse.ch13;import java.lang.reflect.Constructor;
import java.lang.reflect.Method;class Student{private String name;public Student(){this.name = "unknown";}public Student(String name){this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String toString() {return "Name:" + name;}
}public class ReflectDemo {public static void main(String[] args) {System.out.println("获取构造器:");try {Class clz = Class.forName("hust.cs.javacourse.ch13.Student");//获取所有的Constructor对象Constructor[] ctors = clz.getConstructors();for(Constructor c : ctors){System.out.println(c.toString());}} catch (Exception e) {e.printStackTrace();}System.out.println("获取方法:");try {Class clz = Class.forName("hust.cs.javacourse.ch13.Student");//获取所有的Method//Method[] methods = clz.getMethods(); //会显示所有方法,包括继承的Method[] methods = clz.getDeclaredMethods(); //本类定义的方法for(Method m: methods){System.out.println(m.toString());}} catch (Exception e) {e.printStackTrace();}}
}
运行结果为:
获取构造器:
public hust.cs.javacourse.ch13.Student()
public hust.cs.javacourse.ch13.Student(java.lang.String)
获取方法:
public java.lang.String hust.cs.javacourse.ch13.Student.getName()
public java.lang.String hust.cs.javacourse.ch13.Student.toString()
public void hust.cs.javacourse.ch13.Student.setName(java.lang.String)
获取某个构造器或方法
获取某个构造器
前面我们已经知道,可以用一个Class
对象的newInstance()
方法,调用它的无参构造函数创建一个实例对象。那如果要调用有参构造函数呢?
Class
类还提供了实例方法getConstructor(Class<?>... parameterTypes)
,按照构造器的参数类型列表parameterTypes
获取对应的构造器,返回一个Constructor
对象。其中参数类型列表是Class
类对象的列表。而Constructor
类又提供了实例方法newInstance(Object ... initargs)
,按照构造器的参数列表initargs
传入实参,从而创建一个实例类型。
例如,按照上面hust.cs.javacourse.ch13.Student
的定义,我们可以使用如下代码创建一个名为John
的学生:
java">try {Class clz = Class.forName("hust.cs.javacourse.ch13.Student");Student s = (Student)clz.getConstructor(String.class).newInstance("John");
} catch (Exception e) {e.printStackTrace();
}
其中,clz.getConstructor(String.class)
获取了Student
类的参数类型为String.class
的构造器,也就是下面这个构造器:
java">public Student(String name){this.name = name;
}
然后对于这个构造器,又调用了newInstance("John")
方法,从而将"John"
传入形式参数name
,构造出了一个Student
实例对象。
对于一个
Class
类对象clz
,可以直接使用clz.newInstance()
调用该类的无参构造函数,此时newInstance
是Class
类提供的实例方法;也也可以使用clz.getConstructor().newInstance()
调用该类的无参构造函数,此时newInstance
是Constructor
类提供的实例方法。
获取某个方法
Class
类提供的实例方法getMethod(String name, Class<?>... parameterTypes)
,按照方法名name
以及方法的参数类型列表parameterTypes
获取对应的方法,返回一个Method
对象。而Method
类又提供了实例方法invoke(Object obj, Object... args)
,调用对象obj
的相应方法,并给方法的形参列表传入args
实参列表。
例如,按照上面hust.cs.javacourse.ch13.Student
的定义,我们可以使用如下代码创建一个名为Marry
的学生:
java">try {Class clz = Class.forName("hust.cs.javacourse.ch13.Student");Student s = (Student)clz.getConstructor().newInstance();clz.getMethod("setName", String.class).invoke(s, "Marry"); //调用s1对象的setName方法,实参"Marry"
} catch (Exception e) {e.printStackTrace();
}
其中,clz.getMethod("setName", String.class)
获取了Student
类的参数类型为String.class
的、方法名为setName
的方法,也就是下面这个方法:
java">public void setName(String name) {this.name = name;
}
然后对于这个方法,又调用了invoke(s,"Marry")
方法,从而将"Marry"
传入形式参数name
,并调用s
对象的该方法。
代码实例
上面的解释可以通过一段代码的运行来体现:
java">package hust.cs.javacourse.ch13;import java.lang.reflect.Constructor;
import java.lang.reflect.Method;class Student{private String name;public Student(){this.name = "unknown";}public Student(String name){this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String toString() {return "Name:" + name;}
}public class ReflectDemo {public static void main(String[] args) {try {Class clz = Class.forName("hust.cs.javacourse.ch13.Student");//实例化对象//1:如有缺省构造函数,调用Class对象的newInstance方法Student s1 = (Student)clz.getConstructor().newInstance();//2. 调用带参数的构造函数Student s2 = (Student)clz.getConstructor(String.class).newInstance("John");//invoke methodclz.getMethod("setName", String.class).invoke(s1, "Marry"); //调用s1对象的setName方法,实参"Marry"System.out.println(s1.toString());System.out.println(s2.toString());} catch (Exception e) {e.printStackTrace();}}
运行的结果为:
Name:Marry
Name:John