1.反射简介
Reflectionη(反射)是Java被视为动态语言的关键, 反射机制允许程序在执行期借助于 Reflection API取得任何类的内部信息,并能直接操作仼意对象的內部属性及方法。
Class c= Class.forName("java.lang.String")
加载完类之后在堆内存的方法区中就产生了一个C类型的对象(一个类只有一个Ca对象),
这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构。
这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为: 反射
反射机制主要提供以下功能:
①在运行时判断任意一个对象所属的类;
②在运行时构造任意一个类的对象;
③在运行时判断任意一个类所具有的成员变量和方法;
④在运行时调用任意一个对象的方法;
⑤生成动态代理。
反射的优势与弊端:
反射虽然很灵活,能够使得写的代码,变的大幅精简,所以在用的时候,一定要注意具体的应用场景,反射的优缺点如下:
优点:
(1)能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
(2)与Java动态编译相结合,可以实现无比强大的功能
缺点:
(1)使用反射的性能较低
(2)使用反射相对来说不安全
(3)破坏了类的封装性,可以通过反射获取这个类的私有方法和属性
任何事物,都有两面性,反射的优点,也同是就是它的缺点,所以,没有好与坏,只有最合适的场景。
2.反射的相关API
java.lang.Class代表一个类java.lang.reflect.Method代表类的方法java.lang.reflect.Field代表类的成员变量java.lang.reflect.Constructor代表类的构造方法对象照镜子后可以得到的信息: 某个类的属性、方法和构造器、某个类到底实现了哪些接口.
对于每个类而言,JRE都为其保留一个不变的cass类型的对象。
一个 Class对象包含了特定某个结构( class/interface/enum/annotation/primitive type/od)的有关信息。Class 本身也是一个类
- Class 对象只能由系统建立对象
- 一个加载的类在JVM中只会有一个Class实例
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 每个类的实例都会记得自己是由哪个Class实例所生成
- 通过Class可以完整地得到一个类中的所有被加载的结构
- Class类是 Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
2.1 获取Class类的实例
获取Class对象的三种方式
方式一: 通过对象获得 (Object类中的getClass()方法)
多用于对象的获取字节码的方式
Person p = new Person();Class c = p.getClass();
方式二: 类名.class 获取到字节码文件对象
(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)。
多用于参数的传递
Class c2 = Person.class;
方式三: Class.forName获得
(将类名作为字符串传递给Class类中的静态方法forName即可)。
多用于配置文件, 将类名定义在配置文件中. 读取文件, 加载类
Class c3 = Class.forName("com.jdxia.Person");
- 注意:第三种和前两种的区别
前两种你必须明确Person类型.后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了
2.2 获取Field属性
API接口
| 方法名 | 描述 |
|---|---|
| Field getDeclaredField(String name) | 返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 类对象。 |
| Field[] getDeclaredFields() | 返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。 |
| Field getField(String name) | 返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 类对象。 |
| Field[] getFields() | 返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 类对象 |
//忽略访问权限修饰符的安全检查
d.setAccessible(true);//暴力反射
Person.class
package reflect;/*** @program: practice* @description:* @author: yuanzi**/public class Person {String name;Integer age;public String a;protected String b;String c;private String d;public Person(String name, Integer age) {this.name = name;this.age = age;}Person(){}public String getName() {return name;}public Integer getAge() {return age;}public void setName(String name) {this.name = name;}public void setAge(Integer age) {this.age = age;}public void eat(){System.out.println("吃饭...");}public void eat(String str){System.out.println("吃饭..." + str);}}
测试代码
package reflect;import java.lang.reflect.Field;public class ReflectDemo2 {public static void main(String[] args) throws Exception {//0.获取Person的Class对象Class personClass = Person.class;/*1. 获取成员变量们* Field[] getFields()* Field getField(String name)* Field[] getDeclaredFields()* Field getDeclaredField(String name)*///1.Field[] getFields()获取所有public修饰的成员变量Field[] fields = personClass.getFields();for (Field field : fields) {System.out.println(field);}System.out.println("------------");//2.Field getField(String name)Field a = personClass.getField("a");//获取成员变量a 的值Person p = new Person();Object value = a.get(p);System.out.println(value);//设置a的值a.set(p,"张三");System.out.println(p);System.out.println("===================");//3. Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符Field[] declaredFields = personClass.getDeclaredFields();for (Field declaredField : declaredFields) {System.out.println(declaredField);}//4. Field getDeclaredField(String name)Field d = personClass.getDeclaredField("d");//忽略访问权限修饰符的安全检查d.setAccessible(true);//暴力反射Object value2 = d.get(p);System.out.println(value2);}}
执行结果
2.3 获取构造方法们
API接口
| 方法名 | 描述 |
|---|---|
| Constructor |
返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。 |
| Constructor<?>[] getConstructors() | 返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。 |
| Constructor |
返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 类函数。 |
| Constructor<?>[] | getDeclaredConstructors()返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的数组 类 。 |
测试代码
package reflect;import java.lang.reflect.Constructor;public class ReflectDemo3 {public static void main(String[] args) throws Exception {//0.获取Person的Class对象Class personClass = Person.class;/*2. 获取构造方法们* Constructor<?>[] getConstructors()* Constructor<T> getConstructor(类<?>... parameterTypes)* Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)* Constructor<?>[] getDeclaredConstructors()*///Constructor<T> getConstructor(类<?>... parameterTypes)Constructor constructor = personClass.getConstructor(String.class, Integer.class);System.out.println(constructor);//创建对象Object person = constructor.newInstance("张三", 23);System.out.println(person);System.out.println("----------");Constructor constructor1 = personClass.getConstructor();System.out.println(constructor1);//创建对象Object person1 = constructor1.newInstance();System.out.println(person1);Object o = personClass.newInstance();System.out.println(o);//constructor1.setAccessible(true);}}
执行结果
2.4 获取成员方法们
API接口
| 方法名 | 描述 |
|---|---|
| Method getMethod(String name, class <?>… parameterTypes) |
返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。 |
| Method[] getMethods() | 返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明。 |
| Method getDeclaredMethod(String name, class <?>… parameterTypes) |
返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。 |
| Method[] getDeclaredMethods() | 返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。 |
测试代码
package reflect;
import java.lang.reflect.Method;
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {
//0.获取Person的Class对象
Class personClass = Person.class;
/*
3. 获取成员方法们:
* Method[] getMethods()
* Method getMethod(String name, 类<?>... parameterTypes)
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name, 类<?>... parameterTypes)
*/
//获取指定名称的方法
Method eat_method = personClass.getMethod("eat");
Person p = new Person();
//执行方法
eat_method.invoke(p);
Method eat_method2 = personClass.getMethod("eat", String.class);
//执行方法
eat_method2.invoke(p,"str");
System.out.println("-----------------");
//获取所有public修饰的方法
Method[] methods = personClass.getMethods();
for (Method method : methods) {
System.out.println(method);
String name = method.getName();
System.out.println(name);
//method.setAccessible(true);
}
//获取类名
String className = personClass.getName();
System.out.println(className);//reflect.Person
}
}
执行结果
3.反射机制的应用实例
在泛型为Integer的ArrayList中存放一个String类型的对象
package net.xsoftlab.baike;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class TestReflect {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<Integer>();
Method method = list.getClass().getMethod("add", Object.class);
method.invoke(list, "Java反射机制实例。");
System.out.println(list.get(0));
}
}
通过反射取得并修改数组的信息
package net.xsoftlab.baike;
import java.lang.reflect.Array;
public class TestReflect {
public static void main(String[] args) throws Exception {
int[] temp = { 1, 2, 3, 4, 5 };
Class<?> demo = temp.getClass().getComponentType();
System.out.println("数组类型: " + demo.getName());
System.out.println("数组长度 " + Array.getLength(temp));
System.out.println("数组的第一个元素: " + Array.get(temp, 0));
Array.set(temp, 0, 100);
System.out.println("修改之后数组第一个元素为: " + Array.get(temp, 0));
}
}
通过反射机制修改数组的大小
package net.xsoftlab.baike;
import java.lang.reflect.Array;
public class TestReflect {
public static void main(String[] args) throws Exception {
int[] temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int[] newTemp = (int[]) arrayInc(temp, 15);
print(newTemp);
String[] atr = { "a", "b", "c" };
String[] str1 = (String[]) arrayInc(atr, 8);
print(str1);
}
// 修改数组大小
public static Object arrayInc(Object obj, int len) {
Class<?> arr = obj.getClass().getComponentType();
Object newArr = Array.newInstance(arr, len);
int co = Array.getLength(obj);
System.arraycopy(obj, 0, newArr, 0, co);
return newArr;
}
// 打印
public static void print(Object obj) {
Class<?> c = obj.getClass();
if (!c.isArray()) {
return;
}
System.out.println("数组长度为: " + Array.getLength(obj));
for (int i = 0; i < Array.getLength(obj); i++) {
System.out.print(Array.get(obj, i) + " ");
}
System.out.println();
}
}
将反射机制应用于工厂模式
package net.xsoftlab.baike;
interface fruit {
public abstract void eat();
}
class Apple implements fruit {
public void eat() {
System.out.println("Apple");
}
}
class Orange implements fruit {
public void eat() {
System.out.println("Orange");
}
}
class Factory {
public static fruit getInstance(String ClassName) {
fruit f = null;
try {
f = (fruit) Class.forName(ClassName).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
}
/**
* 对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。 当我们添加很多的子类的时候,会很麻烦。
* Java 工厂模式可以参考
* http://baike.xsoftlab.net/view/java-factory-pattern
*
* 现在我们利用反射机制实现工厂模式,可以在不修改工厂类的情况下添加任意多个子类。
*
* 但是有一点仍然很麻烦,就是需要知道完整的包名和类名,这里可以使用properties配置文件来完成。
*
* java 读取 properties 配置文件 的方法可以参考
* http://baike.xsoftlab.net/view/java-read-the-properties-configuration-file
*
* @author xsoftlab.net
*/
public class TestReflect {
public static void main(String[] args) throws Exception {
fruit f = Factory.getInstance("net.xsoftlab.baike.Apple");
if (f != null) {
f.eat();
}
}
}
可以创建任意类的对象,可以执行任意方法
package reflect;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* 框架类
*/
public class ReflectTest {
public static void main(String[] args) throws Exception {
//可以创建任意类的对象,可以执行任意方法
/*
前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
*/
//1.加载配置文件
//1.1创建Properties对象
Properties pro = new Properties();
//1.2加载配置文件,转换为一个集合
//1.2.1获取class目录下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
pro.load(is);
//2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.加载该类进内存
Class cls = Class.forName(className);
//4.创建对象
Object obj = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
//6.执行方法
method.invoke(obj);
}
}
测试结果
