反射
java 反射机制是在运行状态中,对于任何一个类(class文件),都能够知道这个类的所有属性和方法对于任意一个对象,都能够调用它的任意一个方法和属性。简单理解:动态获取类中的信息,就是反射机制。
反射(Class)类解刨
要想对字节码文件进行解刨,就必须有字节码文件对象。
如何获取字节码文件对象??(三种方式)
常用方法:
获取类的字节码
static Class<?> | forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。 |
static Class<?> | forName(String name, boolean initialize, ClassLoader loader) 使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class 对象。 |
获取构造器
无参构造
T | newInstance() 创建此 Class 对象所表示的类的一个新实例。 |
有参构造
Constructor<T> | getConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。 |
Constructor<?>[] | getConstructors() 返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。 |
Constructor类的方法
T | newInstance(Object... initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。 |
获取字段
Field | getField(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。 |
Field[] | getFields() 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。 |
Field | getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 |
Field[] | getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段 |
Field
类的方法
Object | get(Object obj) 返回指定对象上此 Field 表示的字段的值。 |
void | set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。 |
获取方法
Method | getMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 |
Method[] | getMethods() 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。 |
Method | getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。 |
Method[] | getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 |
Method类的方法
Object | invoke(Object obj, Object... args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。 |
package 反射;
public class Person {
private String name;
private int age;
public Person(){
System.out.println("无参数构造方法!");
}
public Person(String name,int age){
this.name = name;
this.age = age;
System.out.println("有参数构造方法!"+this.name+"--"+this.age);
}
public void show(){
System.out.println("show..."+this.name+":"+this.age);
}
public void parameMethod(String addr){
System.out.println("parameMethod..."+this.name+"--"+addr);
}
public static void staticMethod(){
System.out.println("static method ...");
}
}
package 反射;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
getClassObject_3();
}
/*
* 3. 只要通过给定的字符串名称就可以获取该类。
* 可以用Class 类中的方法完成。
* 该方法是 forName(String name)
* */
public static void getClassObject_3() throws ClassNotFoundException {
String classname = "反射.Person";
Class clazz = Class.forName(classname);
System.out.println(clazz);
}
/*
* 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<? extends Person> clazz = p.getClass();
Person p1 = new Person();
Class<? extends Person> clazz1 = p1.getClass();
System.out.println(clazz == clazz1);
}
}
利用反射初始化对象(获取构造方法)
package 反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo2 {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
// createNewObject();
createNewObject2();
}
public static void createNewObject2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 早期:
// com.long.bean.Person p = new com.long.bean.Person("lisi",20);
/*
* 通过指定的构造器获取对象?
* 应该先获取该构造函数。通过字节码文件对象即可完成
* 该方法是: Constructor<T> getConstructor(Class<?>... parameterTypes)
*
*
* */
// 现在
String name = "反射.Person";
//寻找该文件名称,并加载进内存,产生Class对象。
Class clazz = Class.forName(name);
// 获取指定构造方法
Constructor constructor = clazz.getConstructor(String.class, int.class);
// 通过该构造器对象的 T newInstance(Object... initargs) 方法进行对象的初始化
Object obj = constructor.newInstance("lisi", 20); //有参数构造方法!lisi
}
public static void createNewObject() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
// 早期:new的时候,先根据被new 的类的名称寻找该类的字节码文件,并加载进内存,
// 创建该字节码对象,接着创建字节码文件对应的Person对象
// com.long.bean.Person p = new com.long.bean.Person();
// 现在
String name = "反射.Person";
//寻找该文件名称,并加载进内存,产生Class对象。
Class clazz = Class.forName(name);
// 如何获取该类对象?
Object bbj = clazz.newInstance(); // 实例化 无参数构造方法
}
}
利用反射操作字段
package 反射;
import java.lang.reflect.Field;
public class ReflectDemo3 {
public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, InstantiationException, IllegalAccessException {
getFieldDemo();
}
/*获取字节码文件的字段*/
public static void getFieldDemo() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
Class clazz = Class.forName("反射.Person");
// Field getField(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
// Field field = clazz.getField("age"); // 公共成员字段,只能访问共有
// Field getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
Field field = clazz.getDeclaredField("age"); // 不管是什么权限都可访问
System.out.println(field); // private int 反射.Person.age
// 对私有字段的权限进行设置,暴力破击
field.setAccessible(true);
// 获取实例对象
Object obj = clazz.newInstance();
// void set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
field.set(obj,20);
// Object get(Object obj) 返回指定对象上此 Field 表示的字段的值。
Object age = field.get(obj);
System.out.println(age); //20
}
}
利用反射操作方法
package 反射;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*获取class 的方法*/
public class ReflectDemo4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
getMethodDemo2();
}
private static void getMethodDemo2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Class.forName("反射.Person");
// 获取有参构造,创建实例
Constructor constructor = clazz.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("lisi", 20);
Method method = clazz.getMethod("parameMethod", String.class); // 有参方法
// Object invoke(Object obj, Object... args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
// 执行方法
method.invoke(obj,"beijing"); // parameMethod...lisi--beijing
}
private static void getMethodDemo1() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Class.forName("反射.Person");
// 获取有参构造,创建实例
Constructor constructor = clazz.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("lisi", 20);
Method method = clazz.getMethod("show", null); // 无参方法
// Object invoke(Object obj, Object... args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
method.invoke(obj,null); // show...lisi:20
}
public static void getMethodDemo() throws ClassNotFoundException {
// Method[] getMethods() 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
Class clazz = Class.forName("反射.Person");
Method[] methods = clazz.getMethods(); // 获取所有的共有方法,以及父类和接口中的
for(Method method:methods){
System.out.println(method);
}
}
}
练习:
Mainboard.java
package Reflect.test;
public class Mainboard {
public void run(){
System.out.println("main board run ...");
}
public void usePCI(PCI p){
if(p!=null){
p.open();
p.close();
}
}
}
PCI.java(接口)
package Reflect.test;
// 定义PCI 接口
interface PCI {
public abstract void open();
public abstract void close();
}
SoundCard.java
package Reflect.test;
// 声卡实现PCI 接口
public class SoundCard implements PCI{
public void open(){
System.out.println("soundcard open..");
}
public void close(){
System.out.println("soundcard close..");
}
}
pci.properties
pci1=Reflect.test.SoundCard
pci2=Reflect.test.NetCard
ReflectTest.java
package Reflect.test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
/*
* 电脑运行
* */
public class ReflectTest {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
Mainboard mb = new Mainboard();
mb.run();
// 每次添加一个设备都需要修改代码传递一个新创建的对象
// mb.usePCI(new SoundCard());
// 不修改代码完成这个动作
// 获取其class文件,在内部实现创建对象的动作
File configFile = new File("C:\\Users\\hecha\\IdeaProjects\\Hello\\src\\Reflect\\test\\pci.properties");
Properties prop = new Properties();
FileInputStream fis = new FileInputStream(configFile);
prop.load(fis);
for(int x=0;x<prop.size();x++){
String pciName = prop.getProperty("pci"+(x+1));
// System.out.println(pciName);
Class clazz = Class.forName(pciName);
PCI p = (PCI)clazz.newInstance();
mb.usePCI(p);
}
fis.close();
}
}
NetCard.java (只需实现PCI 接口,在配置文件中添加配置,就完成对象创建)
package Reflect.test;
public class NetCard implements PCI {
@Override
public void open() {
System.out.println("网卡开启!");
}
@Override
public void close() {
System.out.println("网卡关闭!");
}
}