通过这种能力可以彻底的了解自身的情况为下一步的动作做准备。
Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。
在JDK在主要实现反射机制类都位于java.lang.reflect包中:
1.Class类:代表一个类
2.Field类:代表类的成员变量(成员变量也称为类的属性)
3.Method类:代表类的方法。
4.Constructor类:代表类的构造方法。
5.Array类
后面四个看看API就明白了
这里主要说下Class类
Class是Reflection故事起源。针对任何您想探勘的class,唯有先为它产生一个Class object。
Class对象怎样产生的?
根据API解释
Class
没有公共构造方法。Class
对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass
方法自动构造的。
当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class object。注意Class并没有public constructor。
怎么得到Class对象?
在我上一篇博客上反射(1)也有提到。
这里再说一下:
一般有三种方式得到Class对象:
1、利用每个类都有的getClass()
方法(java.lang.Object),
如String str = "xx";
Class c1 = str.getClass();
c1.getName();得到java.lang.String
2、利用static method------Class.forName()(最常被使用)
如Class c2 = Class.forName ("java.lang.String");
c2.getName();得到java.lang.String
3、类名.class方法
Class c3 = String.class;
c3.getName();得到java.lang.String
1)对于一些基本类型数据还可以通过.TYPE的方式,且看下面代码
package com.testclass;
import java.lang.reflect.Method;
public class Reflection {
public static void main(String args[])
{
try {
/**
* 我们的程序中的每个类都有一个相应的Class对象.每当新的类被编译
* 完成,就会产生一个Class对象存储与相同的.class文件内.执行期间
* 当你想要产生该class的对象是,JVM便会检查该型别的Class对象是
* 否被加载.如果没被加载,JVM会根据名称找到.class文件并加载它
*
* java中每个class都有一个相应的Class对象,当编写好一个类,编译完成后,
* 在生成的.class文件中,就产生一个Class对象,用来表示
* 这个类的类型信息。获得Class实例的三种方式
*/
//1、使用Class的静态方法forName(),用类的名字获取一个Class实例
Class c = Class.forName("com.testclass.TestOne");
System.out.println(c.getName());//输出com.md5.TestOne
TestOne test = (TestOne)c.newInstance();//产生这个class类对象的一个实例,调用该类的无参的构造方法,作用等同于new TestOne();
/**
* 有异常处理
* newInstance创建对象实例的时候会调用无参的构造函数,
* 所以必需确保类中有无参数的构造函数,否则将会抛出java.lang.InstantiationException异常。
*/
System.out.println("=====================================================");
//2、利用对象调用getClass()方法获取该对象的的Class实例,对象实现存在
TestOne test2 = new TestOne();
Class c2 = test2.getClass();
System.out.println(c2.getName());//输出com.md5.TestOne
Method[] m = c2.getDeclaredMethods();//得到TestOne中的所有类型的自定义的方法,
// Method[] m = c2.getMethods();//得到TestOne中的public类型的方法,不仅仅是自定义的,还有继承于Object类的
System.out.println("TestOne中的所有方法");
for(Method m2 : m)
{
System.out.println(m2);
}
System.out.println("======================================================");
//3-1、运用.class方式获取Class实例(类)
Class c3 = TestOne.class;
System.out.println(c3.getName());//输出com.md5.TestOne
//3-2 运用.class的方式获取Class实例(基本类型)
Class c4 = int.class;
System.out.println("基本类型Class实例:"+c4.getName());//输出int
//3-3运用.class的方式获取Class实例(封装类型类型)
Class c5 = Integer.TYPE;//获取的是这个Integer类型
System.out.println(c5.getName());//输出int
Class c6 = Integer.class;//获取的是这个Integer的Class对象
System.out.println(c6.getName());//输出java.lang.Integer
} catch (ClassNotFoundException e) {//如果包下不存在相应的com.md5.TestOne的.class文件,会抛出异常
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (InstantiationException e) {
e.printStackTrace();
}
catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//使用一般方法构造实例
TestOne two = new TestOne();//静态块只会被加载一次,第二次创建对象,不会再加载静态块,只执行构造方法
}
}
class TestOne{
static {
System.out.println("静态块执行");
}
public TestOne(){
System.out.println("构造方法执行");
}
void getTest()
{
}
public void getTest2()
{
}
}
2)对于method方法还可以输出方法的参数等信息、
package com.testclass;
import java.lang.reflect.Method;
public class Reflection2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
Class c = Class.forName("com.testclass.Test2");
Method[] m = c.getDeclaredMethods();
for(Method method : m)
{
Class[] pre = method.getParameterTypes();
/**
* 按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。
* 如果底层方法不带参数,则返回长度为 0 的数组
*/
for(Class cc : pre)
{
// System.out.println(cc);//输出所有参数类型
System.out.println(cc.getName());//输出所有参数类型,这样输出更好
}
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Class c = Integer.class;
// System.out.println(c);//输出class java.lang.Integer
// Class c = int.class;
// System.out.println(c);//输出int
}
}
class Test2
{
public void getTest(Integer a,int b)
{
}
}
3)利用反射可以验证一个对象是否属于这个类
package com.testclass;
public class Reflection3 {
public static void main(String[] args) {
try {
Class c = Class.forName("com.testclass.Test2");//!!!
String s = "";
System.out.println(c.isInstance(s));//输出false
System.out.println(c.isInstance(new Test2()));//输出true
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4)反射在框架中用的很多,工厂模式,当你不断的new 就不断的分配内存空间,当你new到一定程度没有空间的时候,不就出问题了。一般都不考虑用new来构造实例,除非特殊情况。
new是不在内存中有了空间的分配,当知道类型的时候可以new,当不知道类型的时候,不能new
还要明白在java里面任何class都要装载在虚拟机上才能运行。Class.forName就是装载类用的(和new 不一样,要分清楚)。
Class.forName("oracle.jdbc.driver.OracleDriver");只有加载了数据库驱动(就是加载这个.class文件)之后,才能用DriverManager。
DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:orcl","xxx","xxx");
5)利用反射,动态加载类
要动态加载类,必须有个接口或者是抽象类(A),反射所加载的类是A的实现类或者是子类,否则动态加载的类毫无意义可言。
AA a = (AA)(Class.forName("xxx.xxx.xxx.AAImplClass").newInstance());//其中的forName中的类路径可以动态的传进来
7)数组对象的反射(这个还在研究中)
int[] a = new int[2];
int[]b = new int[5];
System.out.println(a.getClass().getName());//输出结果 [I
System.out.println(b.getClass().getName());输出结果 [I
6)此外,我们还可以在servlet中发现发射的影子
有时我们会奇怪,当我们把一个form表单提交给一个servlet处理时,我们并没有创建这个servlet对象,但是怎么呢调用其中的方法的呢?
当一个request请求传递给web服务器后,web服务器利用发射机制创建相应servlet对象,这个对象调用init方法将这个对象实例加载内存中(init方法只调用一次),然后就可以用这个实例调用servlet中的各种方法