概念:
java反射机制就是在运行状态中,对于任意一个类,都能够指定这个类的所有属性和方法。对于任意一个类,都能够调用它的任意一个方法和属性。这种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制。动态获取类中信息,就是java反射(可以理解为对类的解剖)。按毕老师说的,反射大大加强了程序的扩展性。
动态获取类中的信息。在动态时,我们并不能够new一个对象,那怎样才能获取类的信息呢?
所有的java对象都有class属性的Class类,java提供了三种获取class的办法:
1、通过一个对象获取;
Person p = new Person();
Class clazz = p.getClass();
这种方式需要明确具体的类,并且创建对象,没有扩展性。
2、任何数据类型都具备一个静态的属性 .class 来获取其对应的Class对象;
Class clazz = Person.class;
这种方式要用到类中的静态成员,不够扩展。
3、通过类名获取(Class的forName()方法);
String className = "IODemo.Person";(这是我创建的一个地址,需要在同一个project下)
Class clazz = Class.forName(className);
这种 只要通过给定的类的字符串名称就可以获取该类,更为扩展。
因而在反射时,第三种方式更多使用。
反射机制创建对象(以我创建的有age(int)和name(String)属性的Person类为例):
找寻该名称类文件,并加载进内存,产生Class对象:
String className = "IODemo.Person";(需要注意这里,名字需要完整的包名或者绝对路径,路径找不到下面的代码会发生ClassNotFoundException异常)
Class clazz = Class.forName(className);
创建无参构造函数:
Object obj = clazz.newInstance();
通过指定构造函数创建对象:getConstructor(参数类型...)
Constructor constructor = clazz.getConstructor(String.class,int.class);
Object obj = constructor.newInstance("ad",34);
获取类中的字段,也就是通常我们所说的属性:
getField(String):Field; 此方法只能获取公有的(public修饰的);
getFields():Field[];获取字段数组;
获取私有字段:
getDeclaredField(String):Field;
getDeclaredFields():Field[];
但当我们想要访问私有变量或是私有函数时,由于权限,并不能简单的访问到,于是反射也给我们提供了方法:
setAccessible(boolean); 当boolean值设置为true时,则可以调用的字段对象进行权限的取消,暴力访问。
获取函数:
getMethod(传入函数名和参数列表,空参数传null(String.class,int.class)):获取公有函数
getMethods():Method[];
getDeclaredMethod():Method;获取本类中的方法
getDeclaredMethods():Method[];获取本类中所有方法,不包括父类
invoke(Object,函数的参数(空就为null)); 函数运行(如果invoke(null,参数),则是静态方法)
当我们在进行一个程序的功能扩展时,如果频繁地去修改源程序,不但容易出现些错误,扩展性也极差。因而,若源程序提供了一个借口,我们只需要能够实现这个接口,便能将新功能添加进去,而不必要去修改程序,这样就显得简便安全。但我们的功能需要去让源程序知道,因此,新的功能和源程序的连接,就由配置文件来搭建。例,
我创建了一个USB接口,添加的新功能能够实现此接口以便扩展原功能:
public interface USB {
public void open();
public void close();
}
并创建源代码Computer类去运行USB功能:
public class Computer {
Computer(){
System.out.println("computer......................run");
}
public void UseUSB(USB u){
u.open();
u.close();
}
}
public class ReflectTest {
public static void main(String[] args) throws Exception{
//创建Computer类
Computer com = new Computer();
//创建流对象,读取文件内容
File file = new File("USB.properties");
Properties pro = new Properties();
FileInputStream fis = new FileInputStream(file);
//加载文件中的内容
pro.load(fis);
//将文件中添加的新类加载并运行
for (int i = 0; i < pro.size(); i++) {
String className = pro.getProperty("pro" +(i+1));
Class clazz = Class.forName(className);
USB usb = (USB)clazz.newInstance();
com.UseUSB(usb);
}
fis.close();
}
}
只要实现USB接口的子类复写了其中的open和close方法,并将子类的名字写进配置文件,在运行Computer时,流会读取文件并加载其中的内容,动态地获取到了添加的类中的信息,而无需我们去修改代码,或是new一个对象。