反射
什么是反射?
Java反射机制是在运行状态中,对任意一个类(class文件),都能够知道这个类的属性和方法,对于任意一个对象,都能够调用它的方法和属性。
这种动态获取的信息以及动态调用对象的方法的功能称为Java反射机制。可以理解为对类的剖析。
(一)反射的功能
①在运行时分析类的能力
②在运行时查看对象
③实现通用的数组操作代码
④利用method对象
反射是一种功能强大且复杂的机制,使用它的主要人员是构造工具者,而不是应用程序,但是它也可以用来扩展应用程序。
(二)Class类
在程序运行时,Java始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。
我们知道类就是对某一类型数据的描述,例如学生类,可以描述姓名、年龄,所以我们可以对这些不同的学生创建一个student类,那么我们对每一个类也可以进行抽象,对每一个类的描述就是Class类。如下图:
对于反射用于扩展应用程序,当我们一个应用程序已经在运行时,我们想要对它进行扩展就变得不那么容易了,可能需要修改源代码,但是这是非常危险的事情。接口也是扩展应用程序一个重要的工具,但是我们实现接口还是需要创建对象,也需要修改源代码,那么如何解决这个问题呢?这时我们可以引入配置文件,告诉我们的程序在这个时候要去读取哪个类的字节码文件,通过Class类就可以获取这个类有哪些属性和方法。
假设电脑运行是一个应用程序,运行都是通过主板,通过主板上的接口来开关其他硬件,比如网卡、声卡。我们试着用代码来展现这个实例:
package reflect;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Properties;
/**
* Java反射机制是在运行状态中,对任意一个类(class文件),都能够知道这个类的所有属性和方法
* 对于任意一个对象,都能够调用它的任意一个方法和属性
* 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
*
* 动态获取类中信息,就是java反射。
* 可以理解为对类的解剖。
*
*/
public class ReflectDemo {
public static void main(String[] args) throws Exception {
MainBoard mb = new MainBoard();
mb.run();
//假设我们需要添加一个声卡,需要创建声卡对象,然后调用声卡类的方法
// PCI pci = new SoundCard();
// pci.open();
// pci.close();
//在这种情况下,如果还需要添加网卡,鼠标等硬件,我们还是需要修改源码才能实现,但是现在可以利用class类来实现
//能不能不修改代码就可以完成添加硬件的动作呢?不用new来完成,而只是获取其class文件,在内部实现创建对象的动作。
File configFile = new File("pci.properties");
Properties prop = new Properties();
FileInputStream fis = new FileInputStream(configFile);
prop.load(fis);
//假设有很多个硬件,我们读取配置文件获取多个类,利用多态来创建对象并调用方法。
for(int i=1;i<=prop.size();i++) {
String pciName = prop.getProperty("pci"+i);//通过键值对获取类名
Class clazz = Class.forName(pciName);//使用forName方法获得类名对应的对象
PCI p = (PCI) clazz.newInstance();
p.open();
p.close();
}
fis.close();//关闭资源
}
}
package reflect;
//主板
public class MainBoard {
public void run() {
System.out.println("main board run...");
}
}
package reflect;
/**
* 这个类表示主板上的接口,用于提供硬件开、关的方法
*
*/
public interface PCI {
public void open();
public void close();
}
package reflect;
//声卡
public class SoundCard implements PCI{
@Override
public void open() {
System.out.println("SoundCard open");
}
@Override
public void close() {
System.out.println("SoundCard close");
}
}
package reflect;
//网卡
public class NetCard implements PCI{
@Override
public void open() {
System.out.println("NetCard open");
}
@Override
public void close() {
System.out.println("NetCard close");
}
}
//pci.priperties文件
pci1=reflect.SoundCard
pci2=reflect.NetCard
获取Class对象的三种方式
①通过Object类的getClass方法
②通过类的静态属性.class
③通过Class类的forName方法
package reflect;
/**
* 要想对字节码文件进行剖析,必须要要字节码文件对象
* 如何获取字节码文件对象呢?有三种方式
*
*/
public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException{
getClassObject_1();
getClassObject_2();
getClassObject_3();
}
/*
* 方式3:
* 只要通过给定的字符串名称就可以获取该类。
* 可以用Class类中的方法完成。就是Class类的forName的方法
*/
public static void getClassObject_3() throws ClassNotFoundException {
String className = "reflect.Person";
Class clazz = Class.forName(className);
System.out.println(clazz.toString());
}
/*
* 方式2:
* 任何数据类型都具备一个静态的属性.class来获取其对应的Class对象
*/
public static void getClassObject_2() {
Class clazz = Person.class;
Class clazz1 = Person.class;
System.out.println(clazz==clazz1);
}
/*
* 方式1:
* Object类中的getClass方法
* 想要用这种方式,必须要明确具体的类,并创建对象。但是麻烦
*/
public static void getClassObject_1() {
Person p = new Person();
Class clazz = p.getClass();
Person p1 = new Person();
Class clazz1 = p.getClass();
System.out.println(p==p1);
}
}
package reflect;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
System.out.println("person param run.."+this.name +"..."+this.age);
}
public Person() {
super();
System.out.println("person run");
}
public void show() {
System.out.println(name+"...show run..."+age);
}
public void method() {
System.out.println("method run");
}
public void paramMethod(String str,int num) {
System.out.println("paramMethod run...."+str+":"+num);
}
public static void staticMethod() {
System.out.println("static method run...");
}
}
获取Class中的构造函数
当我们使用空参数的构造函数时,我们可以使用下面的方法来创建该类对象:
String name = "reflect.Person";
Class clazz = Class.forName(name);
Object obj = clazz.newInstance();
但是,如果当我们需要指定构造函数(非空参数)时,我们就需要通过获取某个类的构造函数。如何获取某个类的构造函数?看看下面的例子:
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo3 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
createNewObject_1();
createNewObject_2();
}
//使用默认构造函数创建某个类的实例
public static void createNewObject_1() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String name = "reflect.Person";
Class clazz = Class.forName(name);
Object obj = clazz.newInstance();
}
//通过获取指定的构造函数创建某个类的实例
public static void createNewObject_2() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
String name = "reflect.Person";
Class clazz = Class.forName(name);
Constructor cons = clazz.getConstructor(String.class,int.class);
Object obj = cons.newInstance("小强",24);
}
}
获取Class中的字段
通过反射获取某个类的实例域,有个类叫Field类,这个类里有方法可以操作实例域,看看下面的例子:
package reflect;
import java.lang.reflect.Field;
public class ReflectDemo4 {
public static void main(String[] args) throws Exception{
getFieldDemo();
}
/*
* 获取字节码文件中的字段
*/
public static void getFieldDemo() throws Exception{
Class clazz = Class.forName("reflect.Person");
// Field field = clazz.getField("age");//getField方法只能访问公有的属性,而不能访问私有的
Field field = clazz.getDeclaredField("age");//只获取本类的字段,可以访问私有的。
field.setAccessible(true);//对私有字段的访问取消权限检查,暴力访问
//创建字节码文件对应类的实例
Object obj = clazz.newInstance();
field.set(obj, 89);
Object o = field.get(obj);
System.out.println(o);
}
}
获取Class中的方法
同上面获取字段一样,我们也可以通过反射来获取方法或指定的方法:
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo5 {
/**
*
* @param args
*/
public static void main(String[] args) throws Exception{
getMethods_1();
getMethods_2();
getMethods_3();
}
/*
* 获取指定Class中的所有公共函数
*/
public static void getMethods_1() throws Exception{
Class clazz = Class.forName("reflect.Person");
// Method[] methods = clazz.getMethods();//获取的都是公有的方法
Method[] methods = clazz.getDeclaredMethods();//获取的只是本类中的方法,包含私有
for(Method m :methods) {
System.out.println(m);
}
}
/*
* 获取指定方法,是带参数列表的
*/
public static void getMethods_3() throws Exception{
Class clazz = Class.forName("reflect.Person");
Method method =clazz.getMethod("paramMethod", String.class,int.class);
Object obj = clazz.newInstance();
method.invoke(obj, "小强",77);
}
/*
* 获取指定Class中的指定方法(空参数的)
*/
public static void getMethods_2() throws Exception{
Class clazz = Class.forName("reflect.Person");
Method method = clazz.getMethod("show", null);
Constructor cons = clazz.getConstructor(String.class,int.class);
Object obj = cons.newInstance("小王",33);
method.invoke(obj, null);
}
}