java反射说的是一套反射机制,通过这套机制我们可以访问已经装载到JVM(java虚拟机的简称)里面的java某个对象,还可以进行检测以及修改这个对象本身的信息。可以说反射机制的功能是非常强大的,在java.lang.reflect包下的类支持了这些功能。
说到反射我们绕不开的一个类Class,它到底是什么?
其实在面向对象的世界里万事万物都是对象,类也不例外;所有运行的java虚拟机中的类都是Class类的对象。
类也是对象,类是java.lang.Class类的实例对象。
知道了这个Class类的某个对象(注意这个对象是某个类,可以是java的jdk中自带的类,也可以是我们创建的类,),通过这个对象,我们就可以知道由它创建的某个实例对象的的基本信息。这个就是反射机制的直接表现形式。这样说可能有一点拗口,下面举个例子:
比如有一个类 public class A {},通过 A a=new A();我们创建了A类的一个对象 a;
Class clz=a.getClass();//这样就获得Class类的某个对象 clz
//下面通过反射机制操作这个clz,我们就可以获得实例对象a的一些基本信息:比如方法,构造方法,属性,方法参数党法修饰符等
上面只是谈了一下我对反射机制的的一般理解,不知道我说明白了?下面通过代码学习一下
1.首先如何获取Class类的对象?有如下三种方式;
package com.csdn;
class Person {}//Person类
public class T {
public static void main(String[] args) throws ClassNotFoundException {
Person mPerson = new Person();
// 第一种方式(任何类里面都有一个隐含的静态成员变量class)
Class p1 = Person.class;
// 第二种方式(已经知道了Person类的一个对象,比如上面mPerson)
Class p2 = mPerson.getClass();
// 第三种方式(Class.forName("类的全称"),会抛异常)
Class p3 = Class.forName("com.csdn.Person");
System.out.println(p1 == p2);// 结果为true
System.out.println(p2 == p3);// 结果为true
}
}
上面也打印了p1,p2和p3的堆内存比较结果都为true,结果证明他们都是同一个;
结果同样也说明:
Class类的某个对象(类)也是唯一的。
上面三种方式,不管是通过哪一种得到的Person类都是同一个,这也很好理解,因为在JVM中我们的类只可能是一个,不会存在两个一模一样的类;
2.介绍一下反射有关的几个类,当然有很多,这里只列举几个常见的;
Field类:用于操作类中的属性
Method类:用于操作类中的方法
Constructor类:提供单个构造方法的信息
Modifier类:用于解码访问修饰符(类和成员变量的访问修饰符),这个类里面全是整型常量。每个常量表示一个访问修饰符。
3.通过反射调用方法,这里要使用到Method类(方法对象),一个成员方法就是一个Method的实例对象。
为上面的Person类添加一个方法
class Person {
private int height;
private int addHeight(int a) {
height = a + 1;
return height;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
下面通过Method类来进行操作,还是接着使用p1
Class<?> p1 = Person.class;
// getDeclaredMethods()获取的是本类自己声明的所有方法信息
Method[] mds = p1.getDeclaredMethods();
System.out.println("得到全部方法信息: ");
for (int i = 0; i < mds.length; i++)
{
// 得到方法的访问修饰权限
int mModify = mds[i].getModifiers();
// Modifier类里面是所有修饰符的整形常量信息
// 比如此处 public static final int PRIVATE =0x2 代表 private
// 使用Modifier.toString()方法可以将整形转化为String类型的修饰符private
System.out.print(Modifier.toString(mModify) + " ");
// 得到方法的返回值类型
Class<?> mReturnType = mds[i].getReturnType();
System.out.print(mReturnType.getName());
// 得到方法的名称
String mMethodName = mds[i].getName();
System.out.print(" " + mMethodName);
// 获取方法的参数类型
Class<?>[] mParamType = mds[i].getParameterTypes();
System.out.print("(");
// 说明当前这个方法里面没有参数类型
if (mParamType.length == 0)
System.out.println(")");
for (Class<?> mClass : mParamType) {
// 获取参数类型的名称
System.out.print(mClass.getName());
System.out.println(")");
}
}
打印的结果如下:
得到全部方法信息:
public void setHeight(int)
public int getHeight()
private int addHeight(int)
这里可以看到正好就是上面我们声明的Person类里面的三个方法,通过Method类我们可以很好的获取方法信息。其实我们还可以通过Method类来调用这个方法,这里方法是private修饰的不可以使用对象对象直接调用,利用反射可以轻松做到;比如我给 private int addHeight(int a) 方法传一个实参值是2,操作如下:
Class<?> p1 = Person.class;
Person mPerson = new Person();
//获取到某一个特定的方法对象,第一个传进去的是方法的名称,后面传入形式参数的Class类型
Method md = p1.getDeclaredMethod("addHeight", int.class);
//首先要获得访问的权限,打破封装
md.setAccessible(true);
// 调用invoke方法来调用方法第一个是Person类的对象,第二个是传递的参数
int returnValue = (Integer) md.invoke(mPerson, 2);//retrunValue是addHeight(int a)方法返回的结果
System.out.println("返回值: " + returnValue);
System.out.println("通过mPerson得出的结果:"+mPerson.getHeight());
打印结果是
返回值: 3
通过mPerson得出的结果:3
4,使用Field类来操作属性 一个Field类实例代表一个成员变量
Class<?> p1 = Person.class;
System.out.println("得到的属性是 : ");
// 得到本类声明的所有属性
Field[] mFields = p1.getDeclaredFields();
for (int i = 0; i < mFields.length; i++)
{
// 得到变量的访问修饰权限
System.out.print(Modifier.toString(mFields[i].getModifiers()) + " ");
// 得到属性的类型
System.out.print(mFields[i].getType().getName() + " ");
// 得到属性的名称
System.out.println(mFields[i].getName());
}
打印的结果:
得到的属性是 :
private int height
当然同方法类似,通过Field也可以来直接为变量赋值的
Class<?> p1 = Person.class;
Person mPerson = new Person();
// 获取本类声明的特定的属性,参数传入变量名称
Field mField = p1.getDeclaredField("height");
// 还是要设置一下访问权限,这步是必不可少的
mField.setAccessible(true);
// 通过setInt为对象的height属性赋值为2,因为是int型所以使用setInt
mField.setInt(mPerson, 2);
System.out.println("mPerson的height属性值是:" + mPerson.getHeight());
打印结果:
mPerson的height属性值是:2
5,使用Constructor类来操作构造方法 一个Constructor类实例代表一个构造方法对象
再为Person类添加如下构造方法
public Person() {
super();
}
private Person(int height) {
super();
this.height = height;
}
下面使用Constructor类来操作构造方法 ,接着用p1
Class<?> p1 = Person.class;
System.out.println("得到的构造方法:");
// 获取本类声明的所有构造方法
Constructor<?>[] mCons = p1.getDeclaredConstructors();
for (int i = 0; i < mCons.length; i++)
{
// 获取构造方法的访问修修饰符
System.out.print(Modifier.toString(mCons[i].getModifiers()) + " ");
System.out.print(mCons[i].getName());
// 得到构造方法的参数类型
Class<?>[] mConsClass = mCons[i].getParameterTypes();
System.out.print("(");
for (Class<?> css : mConsClass) {
System.out.print(css.getName());
}
System.out.println(")");
}
打印的结果:
得到的构造方法:
public com.csdn.Person()
private com.csdn.Person(int)
当然了通过Constructor也可以创建Person类的对象
Class<?> p1 = Person.class;
Constructor<?>[] mConstructs = p1.getDeclaredConstructors();
System.out.println(mConstructs.length);
for (int i = 0; i < mConstructs.length; i++) { if (i == 0) {// 是无参的构造方法 Person person1 = (Person) mConstructs[i].newInstance(); System.out.println("打印无参的构造方法中的Height的值:"+person1.getHeight()); } else if (i == 1)
{// 是有参的构造方法 mConstructs[i].setAccessible(true); Person person2 = (Person) mConstructs[i].newInstance(2); System.out.println("打印无参的构造方法中的Height的值:"+person2.getHeight()); }
}
打印结果如下:
打印无参的构造方法中的Height的值:0
打印无参的构造方法中的Height的值:2
说明一下:p1.getDeclaredConstructors()得到的数组中构造方法的顺序是按照,构造方法在Person类中的声明顺序排列的,因为Person类中 先是无参,后是有参,
所以得到的数组中 第一个元素是表示无参构造方法,第二个元素是有参的构造方法
上面写法不科学,这里只是为了演示,我知道Person只有两个构造方法,才这样写的。
第二个因为不是public修饰的构造方法,所以要使用setAccessible来打破访问权限.
到这里反射机制基础知识也就介绍完了,希望对大家有所帮助,有些地方可能写的也不是很好,有错误地方请指正!