目录
1、Class类:反射的核心类,可以获取类的属性,方法等信息。
2、Field类:Java.lang.reflec包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。
3、Method类:Java.lang.reflec包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。
4、Constructor类:Java.lang.reflec包中的类,表示类的构造方法。
一、概念:
“反射”机制:允许我们在运行时发现和使用类的信息。指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。
从反射角度说JAVA属于半动态语言,动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化。比如常见的JavaScript就是动态语言,除此之外Ruby,Python等也属于动态语言,而C、C++则不属于动态语言。
二、反射的作用
- 反射是为了解决在运行期,对某个实例一无所知的情况下,如何调用其方法。
- 编译时类型和运行时类型,在Java程序中许多对象在运行是都会出现两种类型:编译时类型和运行时类型。编译时的类型由声明对象时使用的类型来决定,运行时的类型由实际赋值给对象的类型决定。如:
Fruit fruit = new Apple();
其中编译时类型为Fruit,运行时类型为Apple;在编译时fruit无法获取apple具体方法(非继承)。
- 程序在运行时还可能接收到外部传入的对象,该对象的编译时类型为Object,但是程序有需要调用该对象的运行时类型的方法。为了解决这些问题,程序需要在运行时发现对象和类的真实信息。然而,如果编译时根本无法预知该对象和类属于哪些类,程序只能依
靠运行时信息来发现该对象和类的真实信息,此时就必须使用到反射了。
三、JAVA 反射API
1、Class类:反射的核心类,可以获取类的属性,方法等信息。
- Java的类型全部都是class(除了基本类型外),所以class(包括interface)的本质是数据类型(Type)。无继承关系的数据类型无法赋值:而class是由JVM在执行过程中动态加载的。
- JVM在第一次读取到一种class类型时,将其加载进内存,每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来。
注意:这里的Class类型是一个名叫Class的class。它长这样:
public final class Class {
private Class() {}
}
以String类为例,当JVM加载String类时,它首先读取String.class文件到内存,然后,为String类创建一个Class实例并关联起来:
Class cls = new Class(String);
这个Class实例是JVM内部创建的,如果我们查看JDK源码,可以发现Class类的构造方法是private,只有JVM能创建Class实例,我们自己的Java程序是无法创建Class实例的。所以,JVM持有的每个Class实例都指向一个数据类型(class或interface): 由于 JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。
- 获取Class对象的3种方法
1、调用某个对象的getClass()方法
Person p=new Person();
Class clazz=p.getClass();
2、调用某个类的class属性来获取该类对应的Class对象
Class clazz=Person.class;
3、使用Class类中的forName()静态方法(最安全/性能最好)
Class clazz=Class.forName("类的全路径"); (最常用)
当我们获得了想要操作的类的Class对象后,可以通过Class类中的方法获取并查看该类中的方法和属性。
- 动态加载应用
// Commons Logging优先使用Log4j:
LogFactory factory = null;
if (isClassPresent("org.apache.logging.log4j.Logger")) {
factory = createLog4j();
} else {
factory = createJdkLog();
}
boolean isClassPresent(String name) {
try {
Class.forName(name);
return true;
} catch (Exception e) {
return false;
}
}
这就是为什么我们只需要把Log4j的jar包放到classpath中,Commons Logging就会自动使用Log4j的原因。
2、Field类:Java.lang.reflec包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。
- 获取属性API
- Field getField(name):根据字段名获取某个public的field(包括父类)
- Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
- Field[] getFields():获取所有public的field(包括父类)
- Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
- 一个
Field
对象包含了一个字段的所有信息:
getName()
:返回字段名称,例如,"name"
;getType()
:返回字段类型,也是一个Class
实例,例如,String.class
;getModifiers()
:返回字段的修饰符,它是一个int
,不同的bit表示不同的含义。
-
Field.setAccessible(true)
的意思是,别管这个字段是不是public
,一律允许访问
public class Fruit {
private String name;
public Fruit() {
}
public Fruit(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
class Apple extends Fruit {
public int color;
private int shape ;
}
public class FruitTest {
@Test
public void test() throws NoSuchFieldException, IllegalAccessException {
Class appleClass = Apple.class;
// 获取public字段"color":
System.out.println(appleClass.getField("color"));
// 获取继承的public字段"name":
// System.out.println(appleClass.getField("name"));
// 获取private字段"shape":
System.out.println(appleClass.getDeclaredField("shape"));
// 获取所有public的field(包括父类)
List<Field> fields = Arrays.asList(appleClass.getFields());
for (Field field: fields) {
System.out.println(field);
}
// 获取当前类的所有field(不包括父类)
List<Field> declaredfields = Arrays.asList(appleClass.getDeclaredFields());
for (Field field: declaredfields) {
System.out.println(field);
}
// 获取public字段"color":
Field field = appleClass.getField("color");
// 返回字段名称
System.out.println(field.getName());
// 返回字段类型
System.out.println(field.getType());
// 返回字段的修饰符
System.out.println(field.getModifiers());
int m = field.getModifiers();
Modifier.isFinal(m); // false
Modifier.isPublic(m); // true
Modifier.isProtected(m); // false
Modifier.isPrivate(m); // true
Modifier.isStatic(m); // false
// 获取字段值
Object obj = new Fruit("ping guo");
Class objClass = obj.getClass();
Field name = objClass.getDeclaredField("name");
name.setAccessible(true);
Object value = name.get(obj);
System.out.println(value); // "ping guo"
// 设置字段值
Fruit fruit = new Fruit("ping guo");
System.out.println(fruit.getName());
Class c = fruit.getClass();
Field f = c.getDeclaredField("name");
f.setAccessible(true);
f.set(fruit, "Xiao jiao");
System.out.println(fruit.getName());
}
}
3、Method类:Java.lang.reflec包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。
- 获取Method API;
- getName():返回方法名称,例如:"getScore";
- getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
- getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
- getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
- 获取Method方法信息API
- getName():返回方法名称,例如:"getScore";
- getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
- getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
- getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
- 调用方法,:Object invoke(Object instance, Object... parameters);
- 调用静态方法,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null
- 调用非public方法,通过设置setAccessible(true)来访问非public方法;
- 使用反射调用方法时,仍然遵循多态原则
public class Person {
public String getName() {
return "xiaom";
}
}
class Student extends Person {
public int getScore(String type) {
return 100;
}
private int getGrade(int year) {
return 6;
}
}
public class PersonTest {
@Test
public void test() throws Exception {
Class stdClass = Student.class;
// 获取public方法getScore,参数为String:
System.out.println(stdClass.getMethod("getScore", String.class));
// 获取继承的public方法getName,无参数:
System.out.println(stdClass.getMethod("getName"));
// 获取private方法getGrade,参数为int:
System.out.println(stdClass.getDeclaredMethod("getGrade", int.class));
/**
* 调用方法
*/
// String对象:
String s = "Hello world";
// 获取String substring(int)方法,参数为int:
Method m = String.class.getMethod("substring", int.class);
// 在s对象上调用该方法并获取结果:
String r = (String) m.invoke(s, 6);
// 打印调用结果:
System.out.println(r);
/**
* 调用静态方法
*/
// 获取Integer.parseInt(String)方法,参数为String:
Method method = Integer.class.getMethod("parseInt", String.class);
// 调用该静态方法并获取结果:
Integer n = (Integer) method.invoke(null, "89898");
// 打印调用结果:
System.out.println(n);
/**
* 调用非public方法
*/
Car car = new Car();
Method staticMethod = car.getClass().getDeclaredMethod("setName", String.class);
staticMethod.setAccessible(true);
staticMethod.invoke(car, "宝马");
System.out.println(car.name);
}
}
4、Constructor类:Java.lang.reflec包中的类,表示类的构造方法。
- 获取构造方法API
- getConstructor(Class...):获取某个public的Constructor;
- getDeclaredConstructor(Class...):获取某个Constructor;
- getConstructors():获取所有public的Constructor;
- getDeclaredConstructors():获取所有Constructor。
- 创建对象
- 使用Class对象的newInstance()方法来创建该Class对象对应类的实例,但是这种方法要求该Class对象对应的类有默认的空构造器。
- 先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建Class对象对应类的实例,通过这种方法可以选定构造方法创建实例。
//获取Person类的Class对象
Class clazz=Class.forName("reflection.Person");
//使用.newInstane方法创建对象
Person p=(Person) clazz.newInstance();
//获取构造方法并创建对象
Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class);
//创建对象并设置属性
Person p1=(Person) c.newInstance("张三","女",18);
5、获取继承关系
通过Class
对象可以获取继承关系:
Class getSuperclass()
:获取父类类型;Class[] getInterfaces()
:获取当前类实现的所有接口。
通过Class
对象的isAssignableFrom()
方法可以判断一个向上转型是否可以实现。
四、反射应用(动态代理)
- 代理是基本的设计模式之一。一个对象封装真实对象,代替其提供其他或不同的操作---这些操作通常涉及到与“真实”对象的通信,因此代理通常充当中间对象。不仅动态创建代理对象而且动态处理对代理方法的调用。在动态代理上进行的所有调用都被重定向到单个调用处理程序,该处理程序负责发现调用的内容并决定如何处理。
- 比较Java的class和interface的区别,可以实例化class(非abstract);不能实例化interface,标准库提供了一种动态代理(Dynamic Proxy)的机制:可以在运行期动态创建某个interface的实例
- 在运行期动态创建一个
interface
实例的方法如下:
- 定义一个
InvocationHandler
实例,它负责实现接口的方法调用; - 通过
Proxy.newProxyInstance()
创建interface
实例,它需要3个参数:
使用的ClassLoader
,通常就是接口类的ClassLoader
;
需要实现的接口数组,至少需要传入一个接口进去;
用来处理接口方法调用的InvocationHandler
实例。
3. 将返回的Object
强制转型为接口。
import java.lang.reflect.*;
class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
@Override
public Object
invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println(
"**** proxy: " + proxy.getClass() +
", method: " + method + ", args: " + args);
if (args != null)
for (Object arg : args)
System.out.println(" " + arg);
return method.invoke(proxied, args);
}
}
class SimpleDynamicProxy {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
RealObject real = new RealObject();
consumer(real);
// Insert a proxy and call again:
Interface proxy = (Interface) Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{Interface.class},
new DynamicProxyHandler(real));
consumer(proxy);
}
}
五、Optional类(轻量级代理)
Optional 是 Java 8 为了支持流式编程才引入的,Optional 对象可以防止你的代码直接抛出 NullPointException。实际上,在所有地方都使用 Optional 是没有意义的,有时候检查一下是不是 null 也挺好的,或者有时我们可以合理地假设不会出现 null,甚至有时候检查 NullPointException 异常也是可以接受的。Optional 最有用武之地的是在那些“更接近数据”的地方,在问题空间中代表实体的对象上。举个简单的例子,很多系统中都有 Person 类型,代码中有些情况下你可能没有一个实际的 Person 对象(或者可能有,但是你还没用关于那个人的所有信息)。这时,在传统方法下,你会用到一个 null 引用,并且在使用的时候测试它是不是 null。而现在,我们可以使用 Optional:
class Person {
public final Optional<String> first;
public final Optional<String> last;
public final Optional<String> address;
// etc.
public final Boolean empty;
Person(String first, String last, String address) {
this.first = Optional.ofNullable(first);
this.last = Optional.ofNullable(last);
this.address = Optional.ofNullable(address);
empty = !this.first.isPresent()
&& !this.last.isPresent()
&& !this.address.isPresent();
}
Person(String first, String last) {
this(first, last, null);
}
Person(String last) {
this(null, last, null);
}
Person() {
this(null, null, null);
}
@Override
public String toString() {
if (empty)
return "<Empty>";
return (first.orElse("") +
" " + last.orElse("") +
" " + address.orElse("")).trim();
}
public static void main(String[] args) {
System.out.println(new Person());
System.out.println(new Person("Smith"));
System.out.println(new Person("Bob", "Smith"));
System.out.println(new Person("Bob", "Smith",
"11 Degree Lane, Frostbite Falls, MN"));
}
}