java学习笔记——反射

时间:2022-09-20 08:43:41

初学者的见解,大家一起学习,反射在java的好多地方都有应用,感觉还蛮重要的。

什么是反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

上面的话大致可以这么理解,首先反射是在运行状态时动态的调用类的方法和属性,不用管这些方法或属性是否是私有的。这样我们可以很方便的实现访问、检测和修改描述java对象本身信息的功能。

利用反射获取类的三种方式

1.学过java的人都知道,所有的java类均继承Object类,那么反射的一个重要方法getClass(),就是在Object类中定义的。所以,所有的类都可以通过getClass()方法来获得自己的反射。getClass()方法返回Class类型。

Example_01 example_01 = new Example_01("abc",100);
Class example_01Class = example_01.getClass();

2.通过java.lang.Class类中的forName方法获取.

Class example_01Class = Class.forName("Example_01");

这里需要注意的是,通过这种方法获取反射类时,需要抛出ClassNotFoundException异常。
3.java每个类都有class属性,所以:

Class example_01Class = Example_01.class;

通过反射获取类中的信息

1.构造方法
构造方法用来初始化一个类中的信息,构造方法默认是不带参数的,当然我们也可以自定义带参的构造方法
超类:java.lang.reflect.Constructor

  • getConstructors()
    获得所有权限为public的构造方法,返回Constructor型的数组。

  • getConstructor(Class… parameterTypes)
    获取权限为public的指定构造方法,返回Constructor对象。

  • getDeclaredConstructors()
    获得所有的构造方法,返回Constructor型数组。

  • getDeclaredConstructor(Class… parameterTypes)
    获取指定的构造方法,返回Constructor对象,如:

Constructor declaredConstructor = example_01Class.getConstructor(String.class,int.class);

上述获取的构造方法,带有String和int两个类型的参数。
实例代码:
新建类Example_01,声明3个构造方法,并且设置不同的访问属性。

public class Example_01 {
String s;
int i,j,k;
private Example_01(){

}
protected Example_01(String s, int i){
this.s = s;
this.i = i;
}
public Example_01(String... strings) throws NumberFormatException{
if(0<strings.length)
i = Integer.valueOf(strings[0]);
if(1<strings.length)
j = Integer.valueOf(strings[1]);
if(2<strings.length)
k = Integer.valueOf(strings[2]);
}
public void print(){
System.out.println("s="+s);
System.out.println("i="+i);
System.out.println("j="+j);
System.out.println("k="+k);
}

}

再建测试类:Main_01,通过反射获取各方法,并观察信息。

public class Main_01 {

public static void main(String[] args) throws ClassNotFoundException{
// TODO Auto-generated method stub
//Example_01 example_01 = new Example_01("abc",100);
//Class example_01Class = example_01.getClass();
//Class example_01Class = Class.forName("Example_01");
Class example_01Class = Example_01.class;
Constructor[] declaredConstructors = example_01Class.getDeclaredConstructors();//获取所有的构造方法
//Constructor declaredConstructor = example_01Class.getConstructor(String.class,int.class);
for(int i=0; i<declaredConstructors.length; i++){
Constructor constructor = declaredConstructors[i];
System.out.println("是否允许带有可变数量的参数:"+constructor.isVarArgs());//判断是否有可变数量的参数,isVarArgs()方法,<String... string>就是可变数量的参数
System.out.println("该构造方法的入口参数类型依次为:");
Class[] parameterTypes = constructor.getParameterTypes();//将某个构造方法中的参数输出到class[]数组
for(int j=0; j<parameterTypes.length; j++){
System.out.println(" "+parameterTypes[j]);
}
System.out.println("该构造方法可能抛出的异常类型为:");
Class[] exceptionTypes = constructor.getExceptionTypes();//获取方法可能抛出的异常类型,用数组记录。
for(int k=0; k<exceptionTypes.length; k++){
System.out.println(" "+parameterTypes[k]);
}
Example_01 example_01 = null;//将Example_01对象实例化为null,以便循环重新实例化检验
while(example_01 == null){
try{//如果构造方法的访问权限为private,则不允许访问,会抛出异常
if(i == 2){
example_01 = (Example_01)constructor.newInstance();//通过构造方法的newInstance()方法实例化类对象。
}else if(i == 1){
example_01 = (Example_01)constructor.newInstance("7",5);//通过带有两个参数的构造方法创建对象
}else{
Object[] parameters = new Object[]{new String[]{"100","200","300"}};
example_01 = (Example_01)constructor.newInstance(parameters);
}
}catch(Exception e){
System.out.println("抛出异常,需要执行setAccessible()方法");
constructor.setAccessible(true);//此方法将允许我们访问private的构造方法
}
}
example_01.print();
System.out.println("-----------------------");
}

}
}

输出结果如下:

是否允许带有可变数量的参数:true
该构造方法的入口参数类型依次为:
class [Ljava.lang.String;
该构造方法可能抛出的异常类型为:
class [Ljava.lang.String;
s=null
i=100
j=200
k=300
-----------------------

是否允许带有可变数量的参数:false
该构造方法的入口参数类型依次为:
该构造方法可能抛出的异常类型为:
抛出异常,需要执行setAccessible()方法
s=null
i=0
j=0
k=0
-----------------------

是否允许带有可变数量的参数:false
该构造方法的入口参数类型依次为:
class java.lang.String
int
该构造方法可能抛出的异常类型为:
s=7
i=5
j=0
k=0
-----------------------

通过结果可知:
1)首先,Constructor数组存储方法的顺序,声明的最后一个方法是在数组的第一个位置,所以,数组排列与声明顺序相反。
2)我们可以通过构造方法来实例化类对象,constructor.newInstance();
3)当我们试图访问一个private的构造方法时,会抛出异常,如:

java.lang.IllegalAccessException: Class com.springinaction.springidol.Main_01 can not access a member of class com.springinaction.springidol.Example_01 with modifiers "private"

4)Constructor类常用的方法:

  • isVarArgs():查看构造方法是否与匈奴带有可变参数,若有,返回true
  • getParameterTypes():按照顺序返回个参数类型(String.class等),返回是一个Class类型的数组
  • getExceptionTypes():可能抛出的异常,返回Class数组
  • newInstance(Object… initargs):通过构造方法利用指定参数创建一个该类的对象.
  • setAccessible(boolean flag):若构造方法为private,不允许通过反射利用newInstance()方法创建对象,需要设置访问为true.
  • getModifiers():可以获得构造方法所采用的修饰符的类型,返回int类型。

2.访问成员变量
超类:java.lang.reflect.Field;
与访问构造方法类似,有如下方法:
- getFields():获取所有权限为public的成员变量,包含从超类中继承到的成员变量,返回Field数组。
- getField(String name):获得指定权限为public的成员变量,返回Field对象
- getDeclaredFields():获取所有成员变量。
- getDeclaredField(String name):获取指定成员变量。

Field类中提供的常用方法:
- getName():获得成员变量的名字
- getType():获得成员变量的类型,如(int.class)
- get(Object obj):获得指定对象中成员变量的值,返回Object型
- set(Object obj, Object value):设置obj对象的值为value
- getInt(Object obj):获得obj中int型变量的值
- setInt(Object obj, int value):为int型赋值
- getFloat(Object obj):获得float型
- setFloat(Object obj, float value):
- getBoolean(Object obj)
- setBoolean(Object obj, boolean value):
- setAccessible(boolean flag):被private修饰的成员变量需要获得权限去访问
- getModifiers():获得成员变量所采用的修饰符的整数,返回int。

3.访问方法
超类:java.lang.reflect.Method;
与构造方法类似,其中
getDeclaredMethods():返回Method型数组
getDeclaredMethod(String name, Class… parameterTypes):用来返回指定的Method型方法。

常用方法也类似构造方法,不过可以通过
getName():获取该方法的名称
getReturnType():以Class对象的形式获取该方法的返回值类型。
其他的就不一一列举了。

总结:理解反射的机制,通过类名的方式来访问已经装载到JVM中的java对象描述。一个类中可以说就三类描述:构造方法,成员变量以及方法,掌握好其获取的应用方法,就应该能熟练应用反射,当然,还得多多加强练习才行。(参考《java从入门到精通》)