动态代理模式,前提还是代理模式,只是优化了静态代理的一些不足。
比如,静态代理在一对一关系出现时,创建的代理对象较多,代码量也大,可维护性就会稍差,在一对多的代理关系出现是,可扩展性就更差了。
而动态代理,就是在使用时,才去创建代理类和实例,这样就可以通过一个动态代理类解决创建多个静态代理的问题,更灵活了。
当然动态代理的缺点也是有的,就是相比静态代理直接调用目标对象方法,动态代理效率会低,因为它是通过反射机制,间接调用目标方法的。
所以,在讨论动态代理前,需要先说说静态代理,及反射。
先说反射,通常对一个类对象执行操作时,都要先知道它是什么类,是做什么的,然后去实例化 对象进行操作。
但是,反射则是一开始不知道要初始化的类是什么,无法使用new关键字来创建对象。而是在运行时才知道要操作的类是什么,可以在运行时获取类的完整构造,并调用对应的方法,访问属性。
那反射跟运行时类信息(RTTI)有什么区别呢?
运行时类型识别(Run-Time Type Identification),使得可以在程序运行时发现和使用类型信息。
Java有两种方式可以让我们在运行时识别对象和类的信息,一种是RTTI,它假定在编译时已经知道了所有的类型;另一种就是反射,允许在运行时发现和使用类信息。
RTTI的基本使用
package rtti;
import java.util.Arrays;
import java.util.List;
public class ShapeRtti {
static abstract class Shape {
void draw() {
System.out.println(this + ".draw()");
}
abstract public String toString();
}
static class Circle extends Shape{
@Override
public String toString() {
return "Circle";
}
}
static class Square extends Shape{
@Override
public String toString() {
return "Square";
}
}
public static void main(String[] args) {
List<Shape> shapeList = Arrays.asList(new Circle(),new Square());
for (Shape shape : shapeList) {
shape.draw();
}
}
}
/*output*/
Circle.draw()
Square.draw()
在这个例子中,把Circle对象放入List<Shape>数组时会向上转型,在向上转型为Shape时,也丢失了Shape对象的具体类型,对于数组来说,它们只是Shape类的对象。
当从数组取出元素时,会自动转型会Shape(因为List实际上把所有事物都当做Object持有),这就是RTTI最基础的使用形式。在java中,多有的类型转换都是在运行时进行正确性检查的,也就是RTTI的含义,在运行时,识别一个对象的类型。
在说反射,Class类跟java.lang.reflect类库,一起对反射的概念进行了支持,类库包含了Field,Method,Constructor类,可以用Constructor创建新的对象,用get,set方法读取修改Field对象关联的字段,用invoke方法调用Method对象关联的方法,用getField,getMethods,getContructor返回表示字段,方法,构造器的对象的数组。当通过反射与一个未知类型的对象打交道时,JVM只是简单的检查这个对象,看他属于哪个特定的类,用他做事情前,必须先加载这个类的class对象,这个类的class文件对于jvm来说必须是可获取的。
RTTI跟反射最根本的区别在于,对于RTTI来说,编译器在编译时打开和检查.class文件,也就是说我们使用普通的方式调用对象的方法,但是对于反射来说,.class文件在编译时是不可获取的,而是要在运行时打开和检查.class文件。
Class是一个类,封装了当前对象所对应的类的信息
一个类中有属性,方法,构造器等,比如说有一个Person类,一个Order类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是Class,它应该有类名,属性,方法,构造器等。Class是用来描述类的类。
Class类是一个对象照镜子的结果,对象可以看到自己有哪些属性,方法,构造器,实现了哪些接口等等
对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
对象只能由系统建立对象,一个类(而不是一个对象)在 JVM 中只会有一个Class实例
获取Class对象的三种方式
1.通过类名获取 类名.class
2.通过对象获取 对象名.getClass()
3.通过全类名获取 Class.forName(全类名)
Class类的常用方法
然后说,静态代理模式:为其他对象提供一种代理,以控制对这个对象的访问。
其中有抽象主题Subject,真实主题RealSubject,代理Proxy,
Subject定义了RealSubject,Proxy共用接口,在任何使用RealSubject的地方都可以使用Proxy。
RealSubject定义了Proxy代表的真实主题。
Proxy保存一个真实主题的引用,使得代理可以访问真实主题,并提供了跟Subject相同的接口,这样就可以代替真实主题。
RealSubject,Proxy跟Subject是继承关系,而Proxy需要知道它代理的真实主题,所以跟RealSubject是关联关系。
代理模式的实现代码。
package simplePry;
public class SimpleProxyRun {
public SimpleProxyRun() {
}
public static void consumer(ProxyInterface pi) {
pi.doSomething();
pi.somethingElse("help me do.....");
}
public static void main(String[] args) {
consumer(new RealObject());
System.out.println("-----------------");
consumer(new SimpleProxy(new RealObject()));
}
}
package simplePry;
public class RealObject implements ProxyInterface {
public RealObject() {
System.out.println("realObject");
}
@Override
public void doSomething() {
System.out.println("realObject,do Something");
}
@Override
public void somethingElse(String args) {
System.out.println("realObject,somethingElse "+ args);
}
}
package simplePry;
public class SimpleProxy implements ProxyInterface {
private ProxyInterface mProxy;
public SimpleProxy(ProxyInterface proxy) {
mProxy = proxy;
System.out.println("SimpleProxy,mProxy="+mProxy);
}
@Override
public void doSomething() {
System.out.println("SimpleProxy,do Something");
mProxy.doSomething();
}
@Override
public void somethingElse(String args) {
System.out.println("SimpleProxy,somethingElse "+ args);
mProxy.somethingElse(args);
}
}
public class SimpleProxyRun {
public SimpleProxyRun() {
}
public static void consumer(ProxyInterface pi) {
pi.doSomething();
pi.somethingElse("help me do.....");
}
public static void main(String[] args) {
consumer(new RealObject());
System.out.println("-----------------");
consumer(new SimpleProxy(new RealObject()));
}
}
/*output*/
realObject
realObject,do Something
realObject,somethingElse help me do.....
-----------------
realObject
SimpleProxy,mProxy=simplePry.RealObject@15db9742
SimpleProxy,do Something
realObject,do Something
SimpleProxy,somethingElse help me do.....
realObject,somethingElse help me do.....
例子中,consumer接受的是ProxyInterface,它无法知道正在获得的到底是RealObject还是SimpleProxy。但是SimpleProxy被插入到了客户端和RealObject之间,所以他会执行操作,然后调用RealObject上相同的方法。
动态代理比静态代理的思想又向前了一步,因为他可以动态的创建代理并动态的处理对所代理方法的调用,在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的任务就是揭示调用的类型,然后确定响应的对策。
动态代理中所谓的动态,是针对使用java代码实际编写了代理类的静态代理而言的。它的优势不在于省去了编写代理类那一点工作量,而是实现了可以在原始类和接口还未知的时候,就确定代理类的代理行为,当代理类与原始类脱离直接关系后,就可以灵活重用与不同的应用场景中。
下面用动态代理重写上例。
package simpleDynamicPry;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public 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);
}
}
package simpleDynamicPry;
import java.lang.reflect.Proxy;
import simplePry.ProxyInterface;
import simplePry.RealObject;
public class SimpleDynamicProxy {
public SimpleDynamicProxy() {
}
public static void consumer(ProxyInterface pi) {
pi.doSomething();
pi.somethingElse(" dynamic proxy.");
}
public static void main(String[] args) {
RealObject real = new RealObject();
consumer(real);
//ProxyInterface.class.getClassLoader(),
//new Class[] {ProxyInterface.class},
ProxyInterface pi = (ProxyInterface)Proxy.newProxyInstance(
real.getClass().getClassLoader(),
real.getClass().getInterfaces(),
new DynamicProxyHandler(real));
consumer(pi);
}
}
/*output*/
realObject
realObject,do Something
realObject,somethingElse dynamic proxy.
**********************
-----proxy:class com.sun.proxy.$Proxy0,method:public abstract void simplePry.ProxyInterface.doSomething(),args :null
realObject,do Something
-----proxy:class com.sun.proxy.$Proxy0,method:public abstract void simplePry.ProxyInterface.somethingElse(java.lang.String),args :[Ljava.lang.Object;@6bc7c054
dynamic proxy.
realObject,somethingElse dynamic proxy.
通过静态方法Proxy.newProxyInstance创建动态代理,需要一个类加载器,一个希望该代理实现的接口列表(是接口,不是类、抽象类),一个InvocationHandler接口的实现。动态代理可以将所有调用重定向到调用处理器,因此通常会向调用处理器的构造器传递一个真实对象的引用,使得调用处理器在执行其中介任务时,可以将请求转发。
这个Proxy.newProxyInstance()方法,返回的是一个实现了ProxyInterface接口,并且代理了RealObject实例行为的对象。
最后,动态代理的实现原理。
在动态代理的例子中,真实的代理类,代理对象,并没有看到它的class对象,那么jdk内部是怎么实现的呢?
通常,在jdk中,一个类的使用这样的:
- 写一个java源文件,
- 编译成.class文件,
- Jvm通过类加载器生成一个Class对象,
- 通过Class对象,获取到实例对象,
对于动态代理,没有看到些java源文件的过程,也就是说动态代理类,没有.class文件。
查看例子生成的class文件,也只看到:
DynamicProxyHandler.class
SimpleDynamicProxy.class
单步debug,可以看到
ProxyInterface pi = (ProxyInterface)Proxy.newProxyInstance(
real.getClass().getClassLoader(),
real.getClass().getInterfaces(),
new DynamicProxyHandler(real));
这句代码创建的对象是:$Proxy0,
这个类我们没有写过,在例子的输出目录中也没有这个Proxy0这个类。
深入源码:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
@Proxy.java {
// 前面的常规检查,不去关注
// Class是对每个类的一个抽象,
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
//通过class拿到类的构造器,
final Constructor<?> cons = cl.getConstructor(constructorParams);
//返回类的一个实例。代理类实例的创建,就是由newInstance来负责的。
return cons.newInstance(new Object[]{h});
}
//Class<?> cl这个cl,很有可能就是我们没有写,由jdk内部帮我们创建的动态代理类。
//继续跟进getProxyClass0,
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) @ Proxy.java {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
//代理类,jdk是实现时,可能会做缓存
return proxyClassCache.get(loader, interfaces);
}
第一次用动态代理时,如果缓存里没有,怎么处理的呢?
继续看源码:
public V get(K key, P parameter) @ WeakCache.java{
//这里会从缓存获取。
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
//这里是真正创建代理类的地方,创建完成后,放入缓存。
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
}
分析:subKeyFactory.apply(key, parameter)
//这是一个泛型接口,
R apply(T t, U u)@BiFunction.java
在Proxy.java中有这个接口的具体实现。
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>> @ Proxy.java{
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
。。。
//不去关注别的,仅关注proxy类是怎么创建的。
/*
* Choose a name for the proxy class to generate.
*/
//这里可以看到,我们例子中创建的代理类$Proxy0,这个名字是怎么来的,
//前缀:private static final String proxyClassNamePrefix = "$Proxy";
//num,是通过CAS的原子操作产生,换句话说,如果我们创建了2个动态代理类,第//二个应该就是$Proxy1
//需要提醒的是,这里是android源码库下的实现,
//openjdk源码的实现不太一样,下面给出。
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}
修改测试代码,验证第二个动态代理类是不是$Proxy1.
public interface ProxyInterface2 {
void doSomething();
void somethingElse(String args);
}
public class RealObject2 implements ProxyInterface2 {
public RealObject2() {
System.out.println("realObject");
}
@Override
public void doSomething() {
System.out.println("realObject,do Something");
}
@Override
public void somethingElse(String args) {
System.out.println("realObject,somethingElse "+ args);
}
}
public class SimpleDynamicProxy {
public SimpleDynamicProxy() {
}
public static void consumer(ProxyInterface pi) {
pi.doSomething();
pi.somethingElse(" dynamic proxy.");
}
public static void main(String[] args) {
RealObject real = new RealObject();
RealObject2 real_2 = new RealObject2();
consumer(real);
System.out.println("**********************");
//ProxyInterface.class.getClassLoader(),
//new Class[] {ProxyInterface.class},
ProxyInterface pi = (ProxyInterface)Proxy.newProxyInstance(
real.getClass().getClassLoader(),
real.getClass().getInterfaces(),
new DynamicProxyHandler(real));
ProxyInterface2 pi_2 = (ProxyInterface2)Proxy.newProxyInstance(
real_2.getClass().getClassLoader(),
real_2.getClass().getInterfaces(),
new DynamicProxyHandler(real_2));
consumer(pi);
}
}
如上图,通过debug可以看到第二个动态代理类名字是$Proxy1。
在看下openjdk源码的实现。
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
@ Proxy.java {
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
}
Jdk源码中,代理类先是生成了一个字节数组,然后通过native方法创建了代理类的class,有字节数组,就可以通过流输出到文件中,看看这个代理类长什么样。
但是android源码中,没有看到这个字节数组的生成,而是通过class_linker.cc中方法一步步生成,要输出$proxy不是很容易。
generateProxy是native方法:
static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring name, jobjectArray interfaces,
jobject loader, jobjectArray methods, jobjectArray throws) @ java_lang_reflect_Proxy.cc {
ScopedFastNativeObjectAccess soa(env);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
return soa.AddLocalReference<jclass>(class_linker->CreateProxyClass(
soa, name, interfaces, loader, methods, throws));
}
下面用jdk的源码实现,把$Proxy0的内容输出到文件,看看这个类有哪些内容:
只要把源码实现中:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
这个字节数组保存到文件即可。
public class ProxyUtils {
public static void generateClassFile(Class clazz,String proxyName){
/*ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);*/
byte[] proxyClassFile =ProxyGenerator.generateProxyClass(
proxyName, new Class[]{clazz});
String paths = clazz.getResource(".").getPath();
System.out.println(paths);
FileOutputStream out = null;
try {
out = new FileOutputStream(paths+proxyName+".class");
out.write(proxyClassFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class SimpleDynamicProxy {
public static void main(String[] args) {
RealObject real = new RealObject();
RealObject2 real_2 = new RealObject2();
consumer(real);
System.out.println("**********************");
//ProxyInterface.class.getClassLoader(),
//new Class[] {ProxyInterface.class},
ProxyInterface pi = (ProxyInterface)Proxy.newProxyInstance(
real.getClass().getClassLoader(),
real.getClass().getInterfaces(),
new DynamicProxyHandler(real));
//添加生成$proxy0文件的调用
ProxyUtils.generateClassFile(real.getClass(),
pi.getClass().getSimpleName());
}
}
再次运行后,可以在项目的输出目录看到$Proxy0.class文件。
借助反编译工具JD-GUI,查看这个class文件
通过$Proxy0这个class文件,可以看出动态代理究竟是怎么实现的。
这个代理类$Proxy0继承自Proxy,实现了我们自定义的业务接口RealObject,所有的代理类都是继承自Proxy。从下面Proxy.java的注释也可以看出。
/**
* {@code Proxy} provides static methods for creating dynamic proxy
* classes and instances, and it is also the superclass of all
* dynamic proxy classes created by those methods.
*/
继续看$Proxy0,
public final void doSomething()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
这个doSomething(),就是我们自定义的接口中的方法。
this.h.invoke(this, m3, null);
这是一个标准的反射机制中的调用。其中的h并不在$Proxy0中,而是在其父类Proxy.java中,
/**
* the invocation handler for this proxy instance.
* @serial
*/
protected InvocationHandler h;
这个h就是在实现动态代理时,传入的最后一个参数:
ProxyInterface pi = (ProxyInterface)Proxy.newProxyInstance(
real.getClass().getClassLoader(),
real.getClass().getInterfaces(),
new DynamicProxyHandler(real));
所以$Proxy0中的invoke调用的就是DynamicProxyHandler.java中的invoke().。
其中的方法名m3,是谁呢?
Method m3就是通过反射拿到的业务类中定义的doSomething方法。所以invoke实际调用的就是业务类中实现的方法。
这个代理类的实现代码其实很简单,它为接口中的每一个方法,及从java.lang.Object继承下来的方法如equals(), hashcode()都生成了对应的实现,并且这些实现统一调用InvocationHandler的invoke方法去实现这些方法的内容,各个方法的区别只是传入的参数和Method对象不同,所以无论调用动态代理的那个方法,实际都是执行InvocationHandler中的invoke的代码逻辑。
要生成$Proxy0.class这个文件,还可以通过在main()中添加这个属性:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
然后会在项目的根目录下生成这个class文件。
附录:$Proxy0.class
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import simplePry.RealObject;
public final class $Proxy0 extends Proxy
implements RealObject
{
private static Method m1;
private static Method m3;
private static Method m9;
private static Method m2;
private static Method m4;
private static Method m7;
private static Method m6;
private static Method m8;
private static Method m10;
private static Method m0;
private static Method m5;
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void doSomething()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void notify()
throws
{
try
{
this.h.invoke(this, m9, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void somethingElse(String paramString)
throws
{
try
{
this.h.invoke(this, m4, new Object[] { paramString });
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void wait(long paramLong)
throws InterruptedException
{
try
{
this.h.invoke(this, m7, new Object[] { Long.valueOf(paramLong) });
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void wait(long paramLong, int paramInt)
throws InterruptedException
{
try
{
this.h.invoke(this, m6, new Object[] { Long.valueOf(paramLong), Integer.valueOf(paramInt) });
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final Class getClass()
throws
{
try
{
return (Class)this.h.invoke(this, m8, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void notifyAll()
throws
{
try
{
this.h.invoke(this, m10, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void wait()
throws InterruptedException
{
try
{
this.h.invoke(this, m5, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("simplePry.RealObject").getMethod("doSomething", new Class[0]);
m9 = Class.forName("simplePry.RealObject").getMethod("notify", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("simplePry.RealObject").getMethod("somethingElse", new Class[] { Class.forName("java.lang.String") });
m7 = Class.forName("simplePry.RealObject").getMethod("wait", new Class[] { Long.TYPE });
m6 = Class.forName("simplePry.RealObject").getMethod("wait", new Class[] { Long.TYPE, Integer.TYPE });
m8 = Class.forName("simplePry.RealObject").getMethod("getClass", new Class[0]);
m10 = Class.forName("simplePry.RealObject").getMethod("notifyAll", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m5 = Class.forName("simplePry.RealObject").getMethod("wait", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}