Java反射之java.lang.reflect.Constructor

我们知道每个类都至少有一个构造器,因为一个类如果没有显示定义一个构造器,编译器自动会自动生成一个默认无参的构造器,构造器作为一个类的入口方法,在使用类的成员变量和方法之前,类的构造器必须被调用,生成一个实例,另外构造器不能被继承,如果子类的构造器没有显示的调用父类的构造器,执行器会默认的调用父类的构造器。
说了这么多,我们该进入正题了,本文讲解一个反射中怎么获取一个构造器的声明、参数等信息,怎么调用一个构造器。

获取构造器的方法

获取类的方法一样,在反射的包里,获取类的构造器也是有两个方法:
1. Class.getDeclaredConstructors(): 获取非public的构造器。
2. Class.getConstructors(): 获取public的构造器。
这两个方法的返回值都是java.lang.reflect.Constructor

public class ConstructorSift {
    public static void main(String... args) {
    try {
        Class<?> cArg = Class.forName(args[1]);

        Class<?> c = Class.forName(args[0]);
        Constructor[] allConstructors = c.getDeclaredConstructors();
        for (Constructor ctor : allConstructors) {
        Class<?>[] pType  = ctor.getParameterTypes();
        for (int i = 0; i < pType.length; i++) {
            if (pType[i].equals(cArg)) {
            out.format("%s%n", ctor.toGenericString());

            Type[] gpType = ctor.getGenericParameterTypes();
            for (int j = 0; j < gpType.length; j++) {
                char ch = (pType[j].equals(cArg) ? '*' : ' ');
                out.format("%7c%s[%d]: %s%n", ch,
                       "GenericParameterType", j, gpType[j]);
            }
            break;
            }
        }
        }

        // production code should handle this exception more gracefully
    } catch (ClassNotFoundException x) {
        x.printStackTrace();
    }
    }
}

这个例子的功能是查找第一个输入参数的类具有第二个输入参数的构造方法的信息。
执行几个例子:

查找普通类作为参数的类构造器

查找有Locale参数的java.util.Formatter的构造方法的信息:

$ java ConstructorSift java.util.Formatter java.util.Locale
public
java.util.Formatter(java.io.OutputStream,java.lang.String,java.util.Locale)
throws java.io.UnsupportedEncodingException
       GenericParameterType[0]: class java.io.OutputStream
       GenericParameterType[1]: class java.lang.String
      *GenericParameterType[2]: class java.util.Locale
public java.util.Formatter(java.lang.String,java.lang.String,java.util.Locale)
throws java.io.FileNotFoundException,java.io.UnsupportedEncodingException
       GenericParameterType[0]: class java.lang.String
       GenericParameterType[1]: class java.lang.String
      *GenericParameterType[2]: class java.util.Locale
public java.util.Formatter(java.lang.Appendable,java.util.Locale)
       GenericParameterType[0]: interface java.lang.Appendable
      *GenericParameterType[1]: class java.util.Locale
public java.util.Formatter(java.util.Locale)
      *GenericParameterType[0]: class java.util.Locale
public java.util.Formatter(java.io.File,java.lang.String,java.util.Locale)
throws java.io.FileNotFoundException,java.io.UnsupportedEncodingException
       GenericParameterType[0]: class java.io.File
       GenericParameterType[1]: class java.lang.String
      *GenericParameterType[2]: class java.util.Locale

查找数组类作为参数的构造器

查找String的char[]构造方法。

$ java ConstructorSift java.lang.String "[C"
java.lang.String(int,int,char[])
       GenericParameterType[0]: int
       GenericParameterType[1]: int
      *GenericParameterType[2]: class [C
public java.lang.String(char[],int,int)
      *GenericParameterType[0]: class [C
       GenericParameterType[1]: int
       GenericParameterType[2]: int
public java.lang.String(char[])
      *GenericParameterType[0]: class [C

三、查找变长参数的构造器
ProcessBuilder 有一个构造器: public ProcessBuilder(String... command)

java ConstructorSift java.lang.ProcessBuilder "[Ljava.lang.String;"
public java.lang.ProcessBuilder(java.lang.String[])
      *GenericParameterType[0]: class [Ljava.lang.String;

变成参数是一个语法糖,编译成字节码之后参数就是一个数组而已,关于语法糖的一些知识看可以一下我的另外一篇文章
四、查找泛型参数的构造器

java ConstructorSift java.util.HashMap java.util.Map
public java.util.HashMap(java.util.Map<? extends K, ? extends V>)
      *GenericParameterType[0]: java.util.Map<? extends K, ? extends V>

获取构造器的标识符

构造器和类的其他方法不一样,构造器只有以下几种标识符:
1. 访问权限描述符: public, protected, and private。
2. 注解。

public class ConstructorAccess {
    public static void main(String... args) {
    try {
        Class<?> c = Class.forName(args[0]);
        Constructor[] allConstructors = c.getDeclaredConstructors();
        for (Constructor ctor : allConstructors) {
        int searchMod = modifierFromString(args[1]);
        int mods = accessModifiers(ctor.getModifiers());
        if (searchMod == mods) {
            out.format("%s%n", ctor.toGenericString());
            out.format("  [ synthetic=%-5b var_args=%-5b ]%n",
                   ctor.isSynthetic(), ctor.isVarArgs());
        }
        }

        // production code should handle this exception more gracefully
    } catch (ClassNotFoundException x) {
        x.printStackTrace();
    }
    }

    private static int accessModifiers(int m) {
    return m & (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED);
    }

    private static int modifierFromString(String s) {
    if ("public".equals(s))               return Modifier.PUBLIC;
    else if ("protected".equals(s))       return Modifier.PROTECTED;
    else if ("private".equals(s))         return Modifier.PRIVATE;
    else if ("package-private".equals(s)) return 0;
    else return -1;
    }
}

这个例子是获取第一个参数名的类具有第二参数类型的构造器。
比如获取File类有private访问权限的构造器:

$ java ConstructorAccess java.io.File private
private java.io.File(java.lang.String,int)
  [ synthetic=false var_args=false ]
private java.io.File(java.lang.String,java.io.File)
  [ synthetic=false var_args=false ]

和获取类的注解以下,获取构造器的注解也是通过Constructor.getAnnotations获取。
具体可以看我的另外一篇文章

反射调用类构造器

我们知道Class上有一个反射实例化一个类的方法: Class.newInstance(),现在又有了另外一种方式: java.lang.reflect.Constructor.newInstance(),两者的调用是不同的:
1. Class.newInstance() 只能调用无参构造器,有参构造器只能通过java.lang.reflect.Constructor.newInstance()来调用。
2. Class.newInstance() 会抛出很多种异常,java.lang.reflect.Constructor.newInstance()只会抛出InvocationTargetException。
3. Class.newInstance() 只能调用当前调用者可见的构造器,java.lang.reflect.Constructor.newInstance()可以调用private等当前调用者不可见的构造器。

class EmailAliases {
    private Set<String> aliases;
    private EmailAliases(HashMap<String, String> h) {
    aliases = h.keySet();
    }

    public void printKeys() {
    out.format("Mail keys:%n");
    for (String k : aliases)
        out.format("  %s%n", k);
    }
}

public class RestoreAliases {

    private static Map<String, String> defaultAliases = new HashMap<String, String>();
    static {
    defaultAliases.put("Duke", "duke@i-love-java");
    defaultAliases.put("Fang", "fang@evil-jealous-twin");
    }

    public static void main(String... args) {
    try {
        Constructor ctor = EmailAliases.class.getDeclaredConstructor(HashMap.class);
        ctor.setAccessible(true);
        EmailAliases email = (EmailAliases)ctor.newInstance(defaultAliases);
        email.printKeys();

        // production code should handle these exceptions more gracefully
    } catch (InstantiationException x) {
        x.printStackTrace();
    } catch (IllegalAccessException x) {
        x.printStackTrace();
    } catch (InvocationTargetException x) {
        x.printStackTrace();
    } catch (NoSuchMethodException x) {
        x.printStackTrace();
    }
    }
}

本例通过反射调用一个Hash参数的类构造器。

$ java RestoreAliases
Mail keys:
  Duke
  Fang

构造器经常遇到的异常

无默认构造器异常

上面我们讲了Class.newInstance()只能调用无参构造器,如果没有这个构造器就会抛出InstantiationException。

public class ConstructorTrouble {
    private ConstructorTrouble(int i) {}

    public static void main(String... args){
    try {
        Class<?> c = Class.forName("ConstructorTrouble");
        Object o = c.newInstance();  // InstantiationException

        // production code should handle these exceptions more gracefully
    } catch (ClassNotFoundException x) {
        x.printStackTrace();
    } catch (InstantiationException x) {
        x.printStackTrace();
    } catch (IllegalAccessException x) {
        x.printStackTrace();
    }
    }
}

测试结果:

$ java ConstructorTrouble
java.lang.InstantiationException: ConstructorTrouble
        at java.lang.Class.newInstance0(Class.java:340)
        at java.lang.Class.newInstance(Class.java:308)
        at ConstructorTrouble.main(ConstructorTrouble.java:7)

构造器抛出异常

如果调用构造器时,构造器本身抛出异常,则我们的反射调用也会抛出java.lang.RuntimeException。

public class ConstructorTroubleToo {
    public ConstructorTroubleToo() {
    throw new RuntimeException("exception in constructor");
    }

    public static void main(String... args) {
    try {
        Class<?> c = Class.forName("ConstructorTroubleToo");
        // Method propagetes any exception thrown by the constructor
        // (including checked exceptions).
        if (args.length > 0 && args[0].equals("class")) {
        Object o = c.newInstance();
        } else {
        Object o = c.getConstructor().newInstance();
        }

        // production code should handle these exceptions more gracefully
    } catch (ClassNotFoundException x) {
        x.printStackTrace();
    } catch (InstantiationException x) {
        x.printStackTrace();
    } catch (IllegalAccessException x) {
        x.printStackTrace();
    } catch (NoSuchMethodException x) {
        x.printStackTrace();
    } catch (InvocationTargetException x) {
        x.printStackTrace();
        err.format("%n%nCaught exception: %s%n", x.getCause());
    }
    }
}

测试结果:

$ java ConstructorTroubleToo class
Exception in thread "main" java.lang.RuntimeException: exception in constructor
        at ConstructorTroubleToo.<init>(ConstructorTroubleToo.java:6)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance
          (NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance
          (DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at java.lang.Class.newInstance0(Class.java:355)
        at java.lang.Class.newInstance(Class.java:308)
        at ConstructorTroubleToo.main(ConstructorTroubleToo.java:15)

查找或者调用错误的构造器

查找或者调用错误的构造器会抛出NoSuchMethodException和IllegalArgumentException,这里就不举例子的,读者可以自己实现这个例子。

调用不可访问的构造器

构造器存在,但是对当前访问者不可见时会抛出: IllegalAccessException异常。

class Deny {
    private Deny() {
    System.out.format("Deny constructor%n");
    }
}

public class ConstructorTroubleAccess {
    public static void main(String... args) {
    try {
        Constructor c = Deny.class.getDeclaredConstructor();
//          c.setAccessible(true);   // solution
        c.newInstance();

        // production code should handle these exceptions more gracefully
    } catch (InvocationTargetException x) {
        x.printStackTrace();
    } catch (NoSuchMethodException x) {
        x.printStackTrace();
    } catch (InstantiationException x) {
        x.printStackTrace();
    } catch (IllegalAccessException x) {
        x.printStackTrace();
    }
    }
}

测试结果:

java.lang.IllegalAccessException: Class ConstructorTroubleAccess can not access
  a member of class Deny with modifiers "private"
        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:505)
        at ConstructorTroubleAccess.main(ConstructorTroubleAccess.java:15)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。   Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。   有时候我们说某个语言具有很强的动态性,有时候我们会区分动态和静态的不同技术与作法。我们朗朗上口动态绑定(dynamic binding)、动态链接(dynamic linking)、动态加载(dynamic loading)等。然而“动态”一词其实没有绝对而普遍适用的严格定义,有时候甚至像对象导向当初被导入编程领域一样,一人一把号,各吹各的调。   一般而言,开发者社群说到动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。   尽管在这样的定义与分类下Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。   Java如何能够做出上述的动态特性呢?这是一个深远话题,本文对此只简单介绍一些概念。整个篇幅最主要还是介绍Reflection APIs,也就是让读者知道如何探索class的结构、如何对某个“运行时才获知名称的class”生成一份实体、为其fields设值、调用其methods。本文将谈到java.lang.Class,以及java.lang.reflect中的Method、Field、Constructor等等classes。
JAVA高级特性 1.静态导入:先举个离例子 。 import java.lang.Integer.parseInt; public class StaticImport { int x = parseInt("123"); System.out.println(x); } 这样的程序如果不在IDE 工具中输入,是很难看出这个程序代码会出现问题,可它偏偏就出问题了,这是为什么呢?因为程序里面有个静态方法,如果导入import static java.lang.Integer.parseInt;这样的程序就可以运行了。 2.可变参数与for循环增强 这个是一般的用法: public static void loop(String[] args){ for(int i=0;i<args.length;i++) { System.out.println(args[i]); } } 这个是JDK 增加的新特性的用法! public static void loop(int x,int... args ) { //这里的参数一定要以这样的形式输入 for(int i:args) { System.out.println(i); } } 3.枚举 写枚举技巧: 1. enum Gender{MALE,FEMALE,BOTH} 2. enum Gender{MALE,FEMALE,BOTH;public abstract getTitle();} 3.enum Gender{MALE{},FEMALE{},BOTH{};public abstract getTitle();} 4.填充各个{}中的getTitle方法。 下面是个红绿黄灯的例子: public enum TrafficLamp { RED(30){ public TrafficLamp next() { return GREEN; } }, GREEN(50){ public TrafficLamp next() { return YELLOW; } }, YELLOW(5){ public TrafficLamp next() { return RED; } }; public abstract TrafficLamp next(); private int time; private TrafficLamp(int time) { this.time = time; } 4.反射. 这个知识点,真是费了我好大劲才理解。当真正理解了,其实也就不难了。先举例子来理解什么是反射。 先建这样的一个类,带会下面有个类里面有反射成员变量的方法的! public class ReflectPoint { private int x; public int y; public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } } public class Test{ String obj = (String)Class.forName("java.lang.String").newInstance(); 这是制造另一个章:Class.forName("java.lang.Integer") --------------------- 讲Constructor://构造方法 Constructor constructors[] = Class.forName("java.lang.String").getConstructors(); Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class); String str = (String)constructor.newInstance(new StringBuffer("abc")); System.out.println(str); class.newInstance()内部其实就是在用默认的构造方法 ----------------------- 讲Method //方法 Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class); System.out.println(charAt.invoke(str, 1)); ------------------------- 讲Field //成员变量 ReflectPoint point = new ReflectPoint(1,7); Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("y"); y.setAccessible(true); System.out.println(y.get(point)); Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x"); x.setAccessible(true); System.out.println(x.get(point)); } 在这里我们运行的时候可以看出,ReflectPoint类里面的x和y 都可以打印出来了! 学习心得:先从这四个知识点来看,张老师的确很让人敬佩!以上的一些程序代码均为张老师课堂即兴发挥所写,也可以看出,张老师对JAVA特性的深刻理解能力!现在说说我对这些程序代码的理解,说实话,才开始听张老师讲的时候,我感觉很模糊的,但是当我真正理解了之后,觉得这些程序写的真是太好了。象以后我门在学习JAVA 的时候,一定要对每个知识点要慢慢的消化吃透,切不能走马观花。一个知识点必须要反复的动手练习,不然很难理解其中的奥秘所在的! 5.在JAVA的程序中,我经常性的看到字符前面有@这种标志的符号.这个就叫做注解! 下面是使用 @SuppressWarnings 来取消 deprecation 警告的一个例子: public class Test { @Deprecated //在eclipse下运行的时候,方法名上会加一横线 public static void sayHello() { } } public class Test2 { @SuppressWarnings("deprecation") //在mian方法内调用一个没有定义的方法时,运行的时候将会出现这一注解! public static void main(String [] args) { Test.sayHello(); } } @SuppressWarnings 批注允许您选择性地取消特定代码段(即,类或方法)中的警告。其中的想法是当您看到警告时,您将调查它,如果您确定它不是问题,您就可以添加一个 @SuppressWarnings 批注,以使您不会再看到警告。虽然它听起来似乎会屏蔽潜在的错误,但实际上它将提高代码安全性,因为它将防止您对警告无动于衷 — 您看到的每一个警告都将值得注意。 由如下代码引出@Override的讲解: User类中的方法: public boolean equals(User other) { return name.equals(other.name); } 下面的代码执行时将有问题: User user1 = new User(); User user2 = new User(); user1.setName("abc"); user2.setName("abc"); System.out.println(user1.equals(user2)); HashSet set = new HashSet(); set.add(user1); set.add(user2); System.out.println(set.size());//期望结果为1,但实际为2 这时候在User类的equals方法上加上@Override,发现了问题。 再看看这个代码:一看就知道有问题, 这里 就有个很好的解决办法,在写public @interface MyAnnotation {}这样的类的时候,下面的代码上的错误提示就是结束的! public class dsds { public static void main(String[] args) throws Exception{ // TODO Auto-generated method stub System.out.println(User.class.isAnnotationPresent(MyAnnotation.class) ); System.out.println( User.class.getAnnotation(MyAnnotation.class) ); } } 运行的结果为:false null 下面演示了一下@Target和@Retention import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE}) //用于构造方法 @Retention(RetentionPolicy.RUNTIME) //在运行是加载Annotation到JVM中 public @interface MyAnnotation { public String value() default "yellow"; public int[] array() default {1,2}; public MetaAnnotation annotation() ; } 注解最主要的就是这么多,其实注解真正的,我们都很少留心的,但是有的注解会给你在写程序和查找错误的时,会有很大的帮助的! 6.泛型: 1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。 2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。 3、泛型的类型参数可以有多个。 4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上成为“有界类型”。 5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName(java.lang.String); 例子: a..使用?通配符可以引用其他各种参数化的类型,但不能调用与参数化有关的方法; Collection<?> c = new Vector<String>(); c.add("abc");//报错 c.size();//正确 所以,?通配符定义的变量主要用作引用,调用与参数化无关的方法,如果要调用与参数化相关的方法,那么必须在使用?通配符引用之前调用,否则就与java 5提供泛型的目的背道而驰了。 b..向下限定通配符: 正确:Vector<? extends Number> x = new Vector<Integer>(); 错误:Vector<? extends Number> x = new Vector<String>(); 向上限定通配符: 正确:Vector<? super Integer> x = new Vector<Number>(); 错误:Vector<? super Integer> x = new Vector<Byte>(); 7.代理 这里就直接先应用张老师写的代码然后再讲清其原理吧! package cn.itcast.corejava; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Collection; import java.util.Vector; public class ProxyTest { public static void main(String[] args) { System.out.println(Integer.class.getClassLoader()); //System.out.println(ProxyTest.class.getClassLoader().getParent().getClass().getName()); System.out.println(ProxyTest.class.getClassLoader().getClass().getName()); //test1(); //test2(); } private static void test3() { Vector v = new Vector(); class MyInvocationHandler implements InvocationHandler { Collection target = null; public Collection bind(Collection target) { this.target = target; Collection proxy1 = (Collection)Proxy.newProxyInstance( ProxyTest.class.getClassLoader(), new Class[]{Collection.class} , this); return proxy1; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("begin " + method.getName()); Object retval = method.invoke(target, args); System.out.println("end" + method.getName()); return retval; } } MyInvocationHandler handler = new MyInvocationHandler(); Collection proxy1 = handler.bind(v); System.out.println(proxy1.getClass().getName()); proxy1.add("abc"); proxy1.add("xyz"); System.out.println(proxy1.size()); } private static void test2() { Vector v = new Vector(); class MyInvocationHandler implements InvocationHandler { Collection target = null; public MyInvocationHandler(){} public MyInvocationHandler(Collection target){this.target = target;} public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("begin " + method.getName()); Object retval = method.invoke(target, args); System.out.println("end" + method.getName()); return retval; } } Collection proxy1 = (Collection)Proxy.newProxyInstance( ProxyTest.class.getClassLoader(), new Class[]{Collection.class} , new MyInvocationHandler(v)); System.out.println(proxy1.getClass().getName()); proxy1.add("abc"); proxy1.add("xyz"); System.out.println(proxy1.size()); } private static void test1() { Collection proxy = (Collection)Proxy.newProxyInstance( ProxyTest.class.getClassLoader(),//first parameter new Class[]{Collection.class} , //second parameter new InvocationHandler(){ //third parameter Vector target = new Vector(); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub System.out.println("begin " + method.getName()); Object retval = method.invoke(target, args); System.out.println("end" + method.getName()); return retval; } } ); System.out.println(proxy.getClass().getName()); proxy.add("abc"); proxy.add("xyz"); System.out.println(proxy.size()); } } 以下是Proxy的API文档: public class Proxyextends extends Object implements Serializable 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. To create a proxy for some interface Foo: InvocationHandler handler = new MyInvocationHandler(...); Class proxyClass = Proxy.getProxyClass( Foo.class.getClassLoader(), new Class[] { Foo.class }); Foo f = (Foo) proxyClass. getConstructor(new Class[] { InvocationHandler.class }). newInstance(new Object[] { handler }); or more simply: Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);从以上的程序中在方法中定义参数的时候总是三个参数,可这三个参数有分别代表什么意思呢!?现在以我自己个人的理解,比如我是买家,我要买本书,可是现在我有事,是不是需要别人代我买呢?那带我买的人是不是一定要到卖书的地方买呢?所以着中间产生了三个实体.说白了就是一种代理机制.需要三方一起运行! 代理其实很好理解的,而且会用固定的语法格式,很快会掌握这一原理的!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值