反射的概念
反射的概念是由 Smith 在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。
换句话说,就是能够得到代码自身的特征。
换句话说,就是把类本身也看成是对象,包括类中的变量名、方法名、内部类、超类、包、修饰符等等,都可以通过代码来得到并被看成是对象。
java为此设计了一些类来方便我们使用反射。这些类并不多,它们分别是:Field、Constructor、Method、Class、Object,下面对这些类做一个简单的说明。
摘抄于其它资料,仅供阅读
Field 类:提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。
Constructor 类:提供关于类的单个构造方法的信息以及对它的访问权限。这个类和 Field 类不同,Field 类封装了反射类的属性,而 Constructor 类则封装了反射类的构造方法。
Method 类:提供关于类或接口上某个单独方法的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。 这个类不难理解,它是用来封装反射类方法的一个类。
Class 类:类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
Object 类:每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。
获取Class类
有一个类,类名是Class,(首字母大写,不同于关键字class)。任何一个java类都是这个Class类的对象,即“类本身也是对象”的感觉。
一旦我们获取到了一个类的Class实例,那么在此基础上要获取Field、Constructor、Method等等的话也就很容易了(因此java的所有代码都在类中的嘛)。所以首要步骤是获取Class实例。
获取类自身有三种方式:
(1)利用 对象.getClass() 的方式获取该对象的Class实例;
(2)利用 对象.class 的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例;
(3)使用 Class类的静态方法forName(“全路径名”),用类的名字获取一个Class实例。
示例
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);
}
}
运行结果为
true
true
解释
1、运行结果为true说明虚拟机为某个类只会产生一份字节码,将来用这份字节码可以产生多个实例对象。
2、也即是说,在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象。
利用Class实例创建对象
以前我们创建对象都是用“new 类名()”的方式,现在我们先得到构造方法,并用构造方法来创建。现在我们要使用Consturctor(构造器)类:它代表某个类中的一个构造方法。
得到某个类所有的构造方法
Constructor [] constructors = Class.forName("java.lang.String").getConstructors();
得到某一个构造方法
Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
注:参数是一个Class实例,即去拿匹配这样参数的构造方法。
创建实例对象,用Constructor的newInstance方法
传统方式:String str=new String(new StringBuffer("abc"));
反射方式:String str=(String) constructor.newInstance(new StringBuffer("abc"));
注:newInstance()方法参数可变长,请尝试放多个参数。不合适时,报异常IllegalArgumentException。
上述原理可以下面示例来演练
class Test {
public static void main(String[] args) throws Exception {
Class c = Class.forName("java.lang.String");
Constructor constructor = c.getConstructor(StringBuffer.class);
String str = (String) constructor.newInstance(new StringBuffer("abc"));
System.out.println(str);
}
}
利用Constructor来创建实例与利用Class类来创建实例
class类也有创建实例的方法,下面的例子进行了展示。
此例来源于//seahb.iteye.com/blog/855107。
import java.lang.reflect.Constructor;
class A {
private A() { // 将private改为public试试
System.out.println("A's constructor A() is called.");
}
private A(int a, int b) {
System.out.println("A's constructor A(a,b) is called.");
System.out.println("a:" + a + " b:" + b);
}
}
class B {
public static void main(String[] args) {
B b = new B();
System.out.println("通过Class.NewInstance()调用私有构造函数:");
b.byClassNewInstance();
System.out.println("通过Constructor.newInstance()调用私有构造函数:");
b.byConstructorNewInstance();
}
/* 法一:通过Class.NewInstance()创建新的类示例 */
private void byClassNewInstance() {
try {
Class c = Class.forName("A");
A a = (A) c.newInstance();//调用无参构造方法。如果方法是私有的,则运行时会异常IllegalAccessException
} catch (Exception e) {
e.printStackTrace();
System.out.println("通过Class.NewInstance()调用构造方法【失败】");
}
}
/*法二:通过Constructor.newInstance()创建新的类示例 */
private void byConstructorNewInstance() {
try {
Class c = Class.forName("A");
Constructor c0 = c.getDeclaredConstructor();/* 调用无参构造方法 */
c0.setAccessible(true); //必须设置一下可见性后就可调用了
A a0 = (A) c0.newInstance();//调用构造方法
System.out.println("成功1");
Constructor c1 = c.getDeclaredConstructor(new Class[] { int.class, int.class });/* 调用带参构造方法 */
c1.setAccessible(true);
//A a1 = (A) c1.newInstance(new Object[] { 5, 6 });//参数是对象数组
A a1 = (A) c1.newInstance(5, 6);//参数可连写,因为newInstance()支持可变参数
//A a1 = (A) c1.newInstance(5, 6,7);//参数若不合适,则就报异常IllegalArgumentException
System.out.println("成功2");
} catch (Exception e) {
e.printStackTrace();
}
}
}
结论
class.newInstance和constructor.newInstance 区别
通过反射创建新的类示例,有两种方式:
Class.newInstance()
Constructor.newInstance()
Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;
Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。
Class.newInstance() 要求被调用的构造函数是可见的,也即必须是public类型的;
Constructor.newInstance() 在特定的情况下,可以调用私有的构造函数。
如果被调用的类的构造函数为默认的构造函数,采用Class.newInstance()则是比较好的选择,一句代码就OK;如果是调用带参构造函数、私有构造函数,就需要采用Constractor.newInstance(),两种情况视使用情况而定。不过Java Totorial中推荐采用Constractor.newInstance()。