黑马程序员-----程序员之路_____JDK1.5新特性之代理

----------android培训、Java培训、期待与您交流!----------

1)代理的概述

生活中的代理:

武汉人从武汉的代理商手中买联想电脑和直接跑到北京传智播客旁边来找联想总部买电脑,你觉得最终的主体业务目标有什么区别吗?基本上一样吧,都解决了核心问题,但是,一点区别都没有吗?从代理商那里买真的一点好处都没有吗?

程序中的代理:

要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?

编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。

2)代理的架构图如下:


客户端(Client)原来是直接调用的目标类(Target),现在不要让Client调用Target让Client调用代理(Proxy),代理和目标类实现了相同的接口(doSomeThing()),也就是说对外有相同的方法,在客户端程序编程的时候,不是直接引用目标类也不是引用代理而是用这个doSomeThing接口来进行引用,例如:Collection collections=(Collection)Class.forName(className).newInstance();到底要使用哪一个具体的类,是目标类还是代理类可以在配置文件中配。


但是一定要在代理的doSomeThing()中一定要调用目标类方法,但是在调用目标的前面或者后面要加上一点系统功能代码,

3)AOP(Aspect  Oriented  Program)面向方面的编程


4)a)动态代理技术



写个简单的代码辅助说明一下

Class Proxy{
void sayHello(){
	.......
	try{
		target.sayHello();
	}
	catch(Exception e)
	{
		.........
	}
...........
}
5)代理模式

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。照代理的创建时期,代理类可以分为两种。动态代理和静态代理

(2)分析JVM动态生成的类

创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数。

编码列出动态类中的所有构造方法和参数签名

编码列出动态类中的所有方法和参数签名

创建动态类的实例对象

用反射获得动态类的构造方法

编写一个最简单的InvocationHandler类

调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去

打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。

将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类。

总结思考:让jvm创建动态类及其实例对象,需要给它提供哪些信息?

三个方面:

生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知

产生的类字节码必须有一个关联的类加载器对象;

生成的类中的方法的代码是怎样的,也得由我们提供。

把我们的代码写在一个约定好了接口对象InvocationHandler的方法中,把对象传给它,它调用我的方法,即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用运行了。

用Proxy.newInstance方法直接一步就创建出代理对象。


b)动态代理的工作原理


package cn.itcast.day3;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
//分析JVM动态生成的类
public class ProxyTest {
	public static void main(String[] args) throws Exception{	
//创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数。	
//getProxyClass(loader,interfaces) 返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
//获得动态类Proxy类的字节码的实例对象clazzProxy1
Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);//提供类加载器和接口数组
//类加载器通常用与接口相同的类加载器
System.out.println(clazzProxy1.getName());		
//编码列出动态类中的所有构造方法和参数签名  接收InvocationHandler参数的构造方法。
	System.out.println("...............begin constructors list.........");
	Constructor[] constructors = clazzProxy1.getConstructors();
	for(Constructor constructor : constructors){
		String name = constructor.getName();
		StringBuilder sBuilder  = new StringBuilder(name);//往字符串中动态的添加内容
		sBuilder.append("(");
		Class[] clazzParams = constructor.getParameterTypes();
		for(Class clazzParam : clazzParams){
			sBuilder.append(clazzParam.getName()).append(',');
			}
			if(clazzParams != null && clazzParams.length !=0)
				sBuilder.deleteCharAt(sBuilder.length()-1);
			sBuilder.append("");
			System.out.println(sBuilder.toString());
		}	
		//编码列出动态类中的所有方法和参数签名
		System.out.println("...............begin methods list.........");
		Method[] methods = clazzProxy1.getMethods();
		for(Method method : methods){
			String name = method.getName();
			StringBuilder sBuilder  = new StringBuilder(name);
			sBuilder.append("(");
			Class[] clazzParams = method.getParameterTypes();
			for(Class clazzParam : clazzParams){
				sBuilder.append(clazzParam.getName()).append(',');
			}
			if(clazzParams != null && clazzParams.length !=0)
				sBuilder.deleteCharAt(sBuilder.length()-1);
			sBuilder.append("");
			System.out.println(sBuilder.toString());
		}		
		//创建动态类的实例对象作为代理对象
		System.out.println(".......begin create instance object......");		
		/*
		第一种方法 创建动态类的实例对象的代理
		用反射获得有参数的构造方法
		编写一个最简单的InvocationHandler类
		调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去
		打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。
		第二种方法 用匿名内部类来做
		第三种方法 二合一   
 		//第一种方法
		//用动态类Rroxy的字节码的实例对象来获取Rroxy有参数的构造方法
 	 	Constructor construncor = clazzProxy1.getConstructor(InvocationHandler.class);//获取构造方法的时候												传的是参数类型
 	 	//自己做个实现类,在invoke方法中加一点代码,就可以看到这些代码被调用运行了
		class MyInvocationHandler implements InvocationHandler {
		//实现接口中的方法
			public Object invoke(Object arg0, Method arg1, Object[] arg2)
				throws Throwable {
				// TODO Auto-generated method stub
				return null;
			}	
		}
//获得动态类Rroxy的构造方法后调用构造方法,创建出了动态类的实例对象proxy1,(new出来的肯定是个对象,是Collection的实现类的对象,类型就是Collection)
	Collection proxy1 =(Collection)construncor.newInstance(new MyInvocationHandler());//调用构造方法传的是真正											的参数
								//这个真正的参数是InvocationHandler类的实例对象
				//那么new一个InvocationHandler对象不就行了么,结果发现不行,因为InvocationHandler
				//是一个接口,只能new他的实现类MyInvocationHandler
		System.out.println(proxy1);//打印一下对象
		proxy1.clear();//没有制定目标呢,这里的clear是Collection接口方法
		proxy1.size();	
		//第二种方法 用匿名内部类来做 
		//创建了动态类的实例对象proxy2
		Collection proxy2 =(Collection)construncor.newInstance(new InvocationHandler(){

			public Object invoke(Object proxy, Method method, Object[] args)
				throws Throwable {
				return null;
			}
		});
		System.out.println(proxy2);
		proxy2.clear();
		proxy2.size();
		//第三种方法 合二为一       newProxyInstance直接一步到位
		//利用Proxy的静态方法创建了一个动态类的代理对象proxy3
		Collection proxy3 = (Collection)Proxy.newProxyInstance(	
			Collection.class.getClassLoader(),//提供类加载器,类加载器通常用与接口相同的类加载	
			new Class[] {Collection.class},//提供接口数组(不能用可变参数)
			new InvocationHandler(){   //指派方法调用的调用处理程序,这里用了匿名内部类
				ArrayList target = new ArrayList();//为这个动态类指定一个目标
				//实现InvocationHandler接口中唯一的方法invoke
	                	//Client程序调用proxy3.add("dad")方法时,涉及三要素:proxy3对象、add方法、"dad"参数
		public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
			//系统开始运行的时间
			long beginTime = System.currentTimeMillis();
			//实际调用的方法,并接受方法的返回值(抽取是想在返回之前和之后加一些系统功能)
			Object retVal = method.invoke(target, args);
			//结束的时间
			long endTime = System.currentTimeMillis();
			//打印出系统运行所消耗的时间
			System.out.println(method.getName()+"running of time "+(endTime-beginTime));
			//返回实际调用的方法的返回值回
				return retVal;
		}			
	});	
		proxy3.add("dad");//在proxy3这个代理对象上添加字符串 
		proxy3.add("huih");
		proxy3.add("faf");
		System.out.println(proxy3.size());//调用proxy3这个代理对象的size方法
		//System.out.println(proxy3.getClass().getName());		
		/*
		 猜想分析动态生成的类的内部代码 
		 1动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法
		   和一个如下接受InvocationHandler参数的构造方法。   
		 2构造方法接受一个InvocationHandler对象,接受对象了要干什么用呢?该方法内部的代码会是怎样的呢?
			$Proxy0 implements Collection
				{
					InvocationHandler handler;
					public $Proxy0(InvocationHandler handler)
					{
						this.handler = handler;
					}
				}		
	   3实现Collection接口的动态类中的各个方法的代码又是怎样的呢?
	   		$Proxy0 implements Collection
			{
				InvocationHandler handler;
				public $Proxy0(InvocationHandler handler)
				{
					this.handler = handler;
				}
				//生成的Collection接口中的方法的运行原理
				int size()
				{
					return handler.invoke(this,this.getClass().getMethod("size"),null);
				}
				void clear(){
					handler.invoke(this,this.getClass().getMethod("clear"),null);
				}
				boolean add(Object obj){
					handler.invoke(this,this.getClass().getMethod("add"),obj);
				}
			}
	   4InvocationHandler接口中定义的invoke方法接受的三个参数又是什么意思?图解说明如下:
		Client程序调用objProxy.add(“abc”)方法时,涉及三要素:objProxy对象、add方法、“abc”参数
	 	Class Proxy$ {
			add(Object object) {
		return handler.invoke(Object proxy, Method method, Object[] args);
			}
		}
		*/				
	}
	/*
	让动态生成的类成为目标类的代理
	怎样将目标类传进去?
	直接在InvocationHandler实现类中创建目标类的实例对象,可以看运行效果和加入日志代码,但没有实际意义。
	为InvocationHandler实现类注入目标类的实例对象,不能采用匿名内部类的形式了。
	让匿名的InvocationHandler实现类访问外面方法中的目标类实例对象的final类型的引用变量。
	将创建代理的过程改为一种更优雅的方式,eclipse重构出一个getProxy方法绑定接收目标同时返回代理对象,让调用者更	懒惰,更方便,调用者甚至不用接触任何代理的API。
	将系统功能代码模块化,即将切面代码也改为通过参数形式提供,怎样把要执行的系统功能代码以参数形式提供?
	要执行的代码装到一个对象的某个方法里,然后把这个对象作为参数传递,接收者只要调用这个对象的方法,即等于执行	了外界提供的代码!
	为bind方法增加一个Advice参数。	
	*/
	/*
	//把目标抽取成一个参数(用快捷键)//把系统功能抽取成为一个对象
	final ArrayList target = new ArrayList();
	Collection proxy3 = (Collection)getProxy(target,new MyAdvice());
	proxy3.add("dad");
	proxy3.add("huih");
	proxy3.add("faf");
	System.out.println(proxy3.size());
	System.out.println(proxy3.getClass().getName());
	private static Object getProxy(final Object target,final Advice advice) {
		Object proxy3 = Proxy.newProxyInstance(		
			target.getClass().getClassLoader(),
			target.getClass().getInterfaces(),	
			new InvocationHandler(){
				public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
						
			//long beginTime = System.currentTimeMillis();
			//Object retVal = method.invoke(target, args);
			//long endTime = System.currentTimeMillis();
			//System.out.println(method.getName() + " running time of " + (endTime - beginTime));
			//return retVal;	
				advice.beforeMethod(method);
				Object retVal = method.invoke(target, args);
				advice.afterMethod(method);
				return retVal;						
				}
			}
		);
		return proxy3;
	}
*/
}

练习代码如下:

package com.itheima;
/**
 * 第四题:写一个ArrayList类的代理,实现和ArrayList中完全相同的功能,并可以计算每个方法运行的时间。
 * @author wangzhi
 */
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
/*思路:1,创建一个动态类Proxy实现List<Integer>接口,
 * 	2,利用Proxy的静态方法newProxyInstance创建一个动态类的代理对象arrayListProxy
 *  	3,newProxyInstance接受三个参数,分别是与接口相同的类加载器、接口列表、指派方法调用的调用处理程序
 *     4,指派方法调用的调用处理程序用匿名内部类实现InvocationHandler接口,
 *     5,在这个内部类中创建目标ArrayList类,并且覆盖InvocationHandler对象的invoke方法,
 *     6,在invoke方法中传入三个参数,并写入可以计算每个方法运行的时间一些代码,
 *     7,给代理对象arrayListProxy添加一些数据,每添加一次就要调用invoke方法一次,然后执行invoke里面的代码,得出每个方法运行的时间
 *     8,将arrayListProxy这个代理对象添加的数据进行迭代,并打印
 * */
	public class Test4 {
	 @SuppressWarnings("unchecked")//注释
	public static void main(String[] args){    
	       	//利用动态类Proxy的静态方法newProxyInstance创建了一个动态类的代理对象arrayListProxy,
		 	//这个静态方法newProxyInstance接受三个参数 
	        List<Integer> arrayListProxy = (List<Integer>)Proxy.newProxyInstance( 
	        	//定义代理类的类加载器,用于创建代理对象,不一定必须是ArrayList,也可以是其他的类加载器	
	        	//类加载器通常用与接口相同的类加载
	            ArrayList.class.getClassLoader(), 
	            //代理类要实现的接口列表								
	            ArrayList.class.getInterfaces(),  
	            //指派方法调用的调用处理程序,这里用了匿名内部类
	            new InvocationHandler() {   
	            	//定义一个目标对象target(真正操作的对象)
	                private final ArrayList<Integer> target = new ArrayList<Integer>(); 
	                //实现InvocationHandler接口中唯一的方法invoke
	                //Client程序调用arrayListProxy.add(2)方法时,涉及三要素:arrayListProxy对象、add方法、2参数
	                public Object invoke(final Object proxy, final Method method, final Object[] args)    
	                	throws Throwable {  
	                	//系统开始运行的时间	 
		             final long beginTime = System.currentTimeMillis(); 
		          //实际调用的方法,并接受方法的返回值(抽取是想在返回之前和之后加一些系统功能)
		          final Object obj = method.invoke(target, args); 
		           //系统运行结束的时间									
		           final long endTime = System.currentTimeMillis();
		           //打印出系统运行所消耗的时间
		           System.out.println("[" + method.getName() + "] spend " + (endTime - beginTime) + " ms");
		            //返回实际调用的方法的返回值
		           return obj;
	                    
	                }
	                
	            }
	        );
	        arrayListProxy.add(2);//在arrayListProxy这个代理对象上添加字符串 
	        arrayListProxy.add(4);
	        arrayListProxy.add(7);
	        arrayListProxy.add(9);
	        arrayListProxy.add(3);
	        System.out.println("arrayListProxy代理对象添加的数据为");
	        //将arrayListProxy这个代理对象添加的数据进行迭代,并打印
	        for(int i : arrayListProxy){
	            System.out.print(i + "\t");
	        }
	 }
}

三  实现AOP功能的封装与配置

***工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,  如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。

***BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:

#xxx=java.util.ArrayList

xxx=cn.itcast.ProxyFactoryBean

xxx.target=java.util.ArrayList

xxx.advice=cn.itcast.MyAdvice

***ProxyFacotryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?

目标

通知

***编写客户端应用:

编写实现Advice接口的类和在配置文件中进行配置

调用BeanFactory获取对象

package cn.itcast.day3.aopframework;  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
import java.lang.reflect.Proxy;    
import cn.itcast.day3.Advice;   
public class ProxyFactoryBean {   
    private Advice advice;  
    private Object target;      
    public Advice getAdvice() {  
        return advice;  
    }   
    public void setAdvice(Advice advice) {  
        this.advice = advice;  
    }  
    public Object getTarget() {  
        return target;  
    }  
    public void setTarget(Object target) {  
        this.target = target;  
    }   
    public Object getProxy() {  
        // TODO Auto-generated method stub  
        Object proxy3 = Proxy.newProxyInstance(  
                target.getClass().getClassLoader(),  
                /*new Class[]{Collection.class},*/  
                target.getClass().getInterfaces(),  
                new InvocationHandler(){  
             public Object invoke(Object proxy, Method method, Object[] args)  
               throws Throwable {   
            /*long beginTime = System.currentTimeMillis(); 
              Object retVal = method.invoke(target, args); 
             long endTime = System.currentTimeMillis(); 
              System.out.println(method.getName() + " running time of " + (endTime - beginTime)); 
             return retVal;*/       
             advice.beforeMethod(method);  
             Object retVal = method.invoke(target, args);  
             advice.afterMethod(method);  
             return retVal;                                     
                  	 }  
                }   );  
        return proxy3;  
    }   
}  

package cn.itcast.day3.aopframework;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import cn.itcast.day3.Advice;
public class BeanFactory {
	Properties props = new Properties();
	public BeanFactory(InputStream ips){
		try {
			props.load(ips);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public Object getBean(String name){
		String className = props.getProperty(name);//拿到类名
		Object bean = null;
		try {
		Class clazz = Class.forName(className);
		bean = clazz.newInstance();//创建一个对象,调用不带参数的构造方法//对于javabean来说必须要是有一个不带参数的构造方法
		} catch (Exception e) {
			e.printStackTrace();
		} 
		if(bean instanceof ProxyFactoryBean){
			Object proxy = null;
			ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean)bean;
			try {
				Advice advice = (Advice)Class.forName(props.getProperty(name + ".advice")).newInstance();
				Object target = Class.forName(props.getProperty(name + ".target")).newInstance();
				proxyFactoryBean.setAdvice(advice);
				proxyFactoryBean.setTarget(target);
				proxy = proxyFactoryBean.getProxy();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return proxy;
		}
		return bean;
	}
}
package cn.itcast.day3.aopframework;   
import java.io.InputStream;  
import java.util.Collection;   
public class AopframeworkTest {   
    /** 
     * @param args 
     */  
    public static void main(String[] args) throws Exception {  
        // TODO Auto-generated method stub  
        InputStream ips = AopframeworkTest.class.getResourceAsStream("config.properties");  
        Object bean = new BeanFactory(ips).getBean("xxx");  
        System.out.println(bean.getClass().getName());  
        ((Collection)bean).clear();  
    }   
}  


配置信息如下:

#xxx=java.util.ArrayList  
xxx=cn.itcast.day3.aopframework.ProxyFactoryBean  
xxx.advice=cn.itcast.day3.MyAdvice  
xxx.target=java.util.ArrayList  

最好是为这个案例建立一个子包例如叫:aopframework,然后将该案例涉及到的java源文件都放到这个子包下面。
对于设计思想,刚开始要用在记事本中敲如下代码和配置文件内容的方式进行比划,这样大家更容易理解需求和设计思想。
Object obj = BeanFactory.getBean("xxx")
-------------------
config.properties
//xxx=java.util.ArrayList
xxx=cn.itcast.ProxyFactoryBean
xxx.target=java.util.ArrayList
xxx.advice=cn.itcast.MyAdvice
----------------------------------------------
package cn.itcast.javaehance3.day2;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class BeanFactory {
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		InputStream ips = BeanFactory.class.getResourceAsStream("config.properties");
		BeanFactory beanFactory = new BeanFactory(ips);
		Object obj = beanFactory.getBean("xxx");
		System.out.println(obj.getClass().getName());
		System.out.println(obj.toString());		
	}
	private InputStream ips = null;
	private Properties props = new Properties();
	public BeanFactory(InputStream ips){
		this.ips = ips;
		try {
			props.load(ips);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	Object getBean(String beanName){
		Object obj = null;
		try {
			String className = props.getProperty(beanName);
			Class clazz = Class.forName(className);
			obj = clazz.newInstance();
			if(obj instanceof ProxyFactoryBean){
				ProxyFactoryBean factoryBean = (ProxyFactoryBean)obj;
				String adviceName = props.getProperty(beanName + ".advice");
				String targetName = props.getProperty(beanName + ".target");
				factoryBean.setAdvice((Advice)Class.forName(adviceName).newInstance());
				factoryBean.setTarget(Class.forName(targetName).newInstance());
				obj = factoryBean.getProxy();
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return obj;
	}
}
-----------------------------------
package cn.itcast.javaehance3.day2;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
public class ProxyFactoryBean {
	Advice advice = null;
	Object target = null;
	public Advice getAdvice() {
		return advice;
	}
	public void setAdvice(Advice advice) {
		this.advice = advice;
	}
	public Object getTarget() {
		return target;
	}
	public void setTarget(Object target) {
		this.target = target;
	}
	public Object getProxy(){
		Class clazz1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);		
		class MyInvocationHander implements InvocationHandler {
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {

				advice.beforeMethod();
				Object retVal = method.invoke(target, args);
				advice.afterMethod();
				return retVal;
			}
		}
		Object proxy = null;
		//Collection proxy = (Collection)clazz.newInstance();
		try {
			Constructor constructor = clazz1.getConstructor(InvocationHandler.class);
			proxy = constructor.newInstance(new MyInvocationHander());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 	
		return proxy;		
	}
}
-----------------------------------
	public class MyAdvice implements Advice
	{

		public void afterMethod() {
			System.out.println("从传智毕业习啦");
		}

		public void beforeMethod() {
			// TODO Auto-generated method stub
			System.out.println("来传智学习啦");
		}	
	}



-------android培训、Java培训、期待与您交流! ----------



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值