0%

单例模式指的是一个类永远都只有一个实例,如需要共享某对象,需要对资源统一管理,就可以用单例。 饿汉模式:

 public class Singleton {
    private final static Singleton INSTANCE = new Singleton();
 
    // Private constructor suppresses   
    private Singleton() {}
 
    // default public constructor
    public static Singleton getInstance() {
        return INSTANCE;
    }
  }

很简单,不解释。 懒汉模式:

package com.pocketdigi.patterns.singleton;

public class Singleton {
    private int a;
    /**
     * 需要用volatile修饰
     * 原因:
     *      new Singleton()非原子操作,不能一步完成。分三步:、
     *          1、给Singleton实例分配空间 
     *          2、初始化Singleton构造器
     *          3、将instance指定实例的引用
     *      但JVM可能乱序执行,不一定是1-2-3,也可能是1-3-2,当1-3-2时,执行完3,
     *      instance就不会==null了, 如果此时CPU刚好切换到另一个线程上,
     *      会导致这个线程得到的实例没有初始化
     */
    private volatile static Singleton instance;

    /**
     * private的构造方法,确保不会在外部被调用
     */
    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            //用同步块,保证多线程访问时new Singleton只会执行一次
            synchronized (Singleton.class) {
                //后面的线程,可能进了if,在synchronized前等待,所以这里还要再判断一次是否==null
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }
}

使用懒汉模式时,一定要注意。 调用:

        //单例
        Singleton singleton=Singleton.getInstance();
        singleton.setA(10);
        Singleton singleton2=Singleton.getInstance();
        System.out.println(singleton2.getA());

代理模式将对象使用者与对象用代理对象分开,代理类与具体执行的类实现同一个接口,调用者直接调用的是代理对象,代理对象再调用真正的实现。代理模式可以实现对目标对象的再次封装,添加其他操作(比如统计调用次数之类的),突破限制(调用者没权限访问目标对象,但可以访问代理对象). 接口

package com.pocketdigi.patterns.proxy;

public interface IPerson {
    public void say();
}



package com.pocketdigi.patterns.proxy;

public class PersonImp implements IPerson{
    String name;
    int age;
    public PersonImp(String name,int age)
    {
        this.name=name;
        this.age=age;
    }
    @Override
    public void say() {
        System.out.println("My name is "+name+", I'm "+age+" years old");
    }
}



package com.pocketdigi.patterns.proxy;

public class ProxyPerson implements IPerson {

    IPerson stu;
    public ProxyPerson(String name,int age)
    {
        stu=new PersonImp(name, age);
    }
    
    @Override
    public void say() {
        stu.say();
    }
}

调用:

        //代理模式
        ProxyPerson p=new ProxyPerson("zhang",20);
        p.say();

今天开始学习下设计模式,参考教材是电子书《Java设计模式》。 策略模式: 引用下维基百科上的图, 通过Context,对Strategy接口不同的实现操作。 代码: IStrategy.java:

package com.pocketdigi.patterns.strategy;
/**
 * 策略模式 接口
 * 每种策略实现该接口
 */
public interface IStrategy {
    public void operate();
}

StrategyA.java

package com.pocketdigi.patterns.strategy;
/**
 * 一个实现
 */
public class StrategyA implements IStrategy {

    @Override
    public void operate() {
        // TODO Auto-generated method stub
        System.out.println("策略A实现");
    }
}

StrategyB.java

package com.pocketdigi.patterns.strategy;

/**
 * 另一个实现
 */
public class StrategyB implements IStrategy {

    @Override
    public void operate() {
        // TODO Auto-generated method stub
        System.out.println("策略B实现");
    }
}

StrategyContext.java

package com.pocketdigi.patterns.strategy;
/**
 * 用于存放,操作策略
 */
public class StrategyContext {
    private IStrategy strategy;
    public StrategyContext(IStrategy strategy)
    {
        this.strategy=strategy;
    }
    public void operate()
    {
        strategy.operate();
    }
}

Main.java:

package com.pocketdigi.patterns;

import com.pocketdigi.patterns.strategy.StrategyA;
import com.pocketdigi.patterns.strategy.StrategyB;
import com.pocketdigi.patterns.strategy.StrategyContext;

public class Main {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        StrategyContext context=new StrategyContext(new StrategyA());
        context.operate();
        
        context=new StrategyContext(new StrategyB());
        context.operate();
    }

}

优点:体现高内聚低耦合的特性,方便扩展,以后有更多的其实只需要添加相应类

前面介绍过另一款事件总线,叫EventBus,从官网说明来看,比今天的这个otto要强大很多,比如支持异步,以及主线程,后台线程分发事件等。但因为项目里同事用了otto,所以做下了解。 与BventBus基于事件类型和方法名来分发事件不同,Otto使用注解和事件类型。 主要实现代码:

Bus bus=new Bus();
//注册
bus.register(this);
//发送事件
bus.post(new TestEvent("asdfa"));
//接收事件,加Subscribe注解,方法名可以任意,但参数要与post的一致
@Subscribe 
public void onEvent(TestEvent event) {
     textview.setText(event.getText());
}
//解除注册
bus.unregister(this);

另外,Bus类最好封装一下实现单例,因为只有同一个实例注册、发送,才能正常接收。

EventBus是一款针对Android优化的发布/订阅事件总线。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。 源码:https://github.com/greenrobot/EventBus 使用方法: MainActivity:

package com.test.robodemo;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import de.greenrobot.event.EventBus;

public class MainActivity extends Activity implements View.OnClickListener{
    Button button1,button2,button3,button4;
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button1 = (Button) findViewById(R.id.button1);
        button2 = (Button) findViewById(R.id.button2);
        button3 = (Button) findViewById(R.id.button3);
        button4 = (Button) findViewById(R.id.button4);
        textView = (TextView) findViewById(R.id.textView);
        //注册:三个参数分别是,消息订阅者(接收者),接收方法名,事件类
        EventBus.getDefault().register(this,"setTextA",SetTextAEvent.class);
        EventBus.getDefault().register(this,"setTextB",SetTextBEvent.class);
        EventBus.getDefault().register(this,"messageFromSecondActivity",SecondActivityEvent.class);
        EventBus.getDefault().register(this,"countDown",CountDownEvent.class);
        button1.setOnClickListener(this);
        button2.setOnClickListener(this);
        button3.setOnClickListener(this);
        button4.setOnClickListener(this);
    }
    /**
     * 与注册对应的方法名和参数,没有后缀,默认使用PostThread模式,即跟发送事件在同一线程执行
     * @param event
     */
    public void setTextA(SetTextAEvent event)
    {
        textView.setText("A");
    }
    public void setTextB(SetTextBEvent event)
    {
        textView.setText("B");
    }
    public void messageFromSecondActivity(SecondActivityEvent event)
    {
        textView.setText(event.getText());
    }
    
    /**
     * 加Async后缀,异步执行。还有MainThread和BackgroundThread,分别是在主线程(UI)执行和后台线程(单一)执行
     * @param event
     */
    public void countDownAsync(CountDownEvent event)
    {
        for(int i=event.getMax();i>0;i--)
        {
            Log.v("CountDown", String.valueOf(i));
            SystemClock.sleep(1000);
        }
    }

    @Override
    public void onClick(View v) {
       //发送事件
        switch(v.getId())
        {
            case R.id.button1:
                EventBus.getDefault().post(new SetTextAEvent());
                break;
            case R.id.button2:
                EventBus.getDefault().post(new SetTextBEvent());
                break;
            case R.id.button3:
                //测试SecondActivity中发送事件,MainActivity接收
                Intent intent=new Intent(getApplicationContext(), SecondActivity.class);
                startActivity(intent);
                break;
            case R.id.button4:
                EventBus.getDefault().post(new CountDownEvent(99));
                break;
        }
    }
    protected void onDestroy() {
        super.onDestroy();
        //解注册
        EventBus.getDefault().unregister(this);
    }
}

SecondActivity:

package com.test.robodemo;

import de.greenrobot.event.EventBus;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class SecondActivity extends Activity implements View.OnClickListener {
    Button button1,button2;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        button1 = (Button) findViewById(R.id.button1);
        button2 = (Button) findViewById(R.id.button2);
        button1.setOnClickListener(this);
        button2.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch(v.getId())
        {
            case R.id.button1:
                finish();
                break;
            case R.id.button2:
                //发送事件,因为MainActivity已经注册过SecondActivityEvent,且没有关闭,所以会接收到
                EventBus.getDefault().post(new SecondActivityEvent("Message From SecondActivity"));
                break;
        }
    }
}

源码: http://pan.baidu.com/s/1iW5gM

PHP中有一个hash_hmac函数,hash_hmac(‘sha512’, $data, $key),可以用sha512算法给data通过key生成一个摘要,但Java中没有直接可用的方法。

    /**
     * 与php中的hash_hmac('sha512', $data, $key)功能相同
     * @param data
     * @param key
     * @return
     */
    private String hmacSHA512(String data,String key) {
        String result = "";
        byte[] bytesKey = key.getBytes();
        final SecretKeySpec secretKey = new SecretKeySpec(bytesKey, "HmacSHA512");
        try {
            Mac mac = Mac.getInstance("HmacSHA512");
            mac.init(secretKey);
            final byte[] macData = mac.doFinal(data.getBytes());
            byte[] hex = new Hex().encode(macData);
            result = new String(hex, "ISO-8859-1");
        } catch (NoSuchAlgorithmException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        return result;
    }

需要apache的commons-codec包.

MVC架构的优点就不介绍了不过说实话,在小项目里用MVC,相对而言,确实复杂了。状态模式是指让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。下面的一个例子是模拟下载文件,不同的状态,点击按钮有不同的动作,使用Handler在Controller和Activity之间传递消息,以执行不同的操作,改变UI。范例独立出了Controller,并没有把Activity当Controller。 先看图: android_mvc 就是点击Download按钮,改变进度条。 先写Model.java:

package models;

import java.util.ArrayList;
import java.util.List;

import android.os.SystemClock;

public class Model {
    private String url;
    private long totalSize, downloadedSize;
    //监听器,可以添加多个
    private final List listeners = new ArrayList();
    //标记是否被取消
    private boolean cancelFlag = false;

    public Model(String url) {
        this.url = url;
    }

    public interface Listener {
        void onProgressUpdated(Model model);
        void onCompleted(Model model);
    }

    /**
     * 下载,耗时操作
     */
    public void download() {
        cancelFlag = false;
        totalSize = 100;
        while (downloadedSize < 100 && !cancelFlag) {
            SystemClock.sleep(1000);
            downloadedSize++;
            // 加上同步块,避免迭代时add或remove
            synchronized (listeners) {
                for (Listener listener : listeners) {
                    listener.onProgressUpdated(this);
                }
                if (downloadedSize == totalSize) {
                    for (Listener listener : listeners) {
                        listener.onCompleted(this);
                    }
                }
            }

        }
        // 取消后重置下载计数
        if (cancelFlag) {
            downloadedSize = 0;
        }

    }

    /**
     * 取消下载
     */
    public void cancel() {
        cancelFlag = true;
    }

    public void addListener(Listener listener) {
        synchronized (listeners) {
            listeners.add(listener);
        }

    }

    public void removeListener(Listener listener) {
        synchronized (listeners) {
            listeners.remove(listener);
        }

    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public long getTotalSize() {
        return totalSize;
    }

    public void setTotalSize(long totalSize) {
        this.totalSize = totalSize;
    }

    public long getDownloadedSize() {
        return downloadedSize;
    }

    public void setDownloadedSize(long downloadedSize) {
        this.downloadedSize = downloadedSize;
    }

}

接口ControllerProtocol,用于保存常量,这样,只要实现了该接口的类都会有相同的常量,这里主要是保存Message的what值。

package controllers;

/**
 * 用于共享常量,所有实现该接口的类都可以使用这些常量
 * @author fhp
 *
 */
public interface ControllerProtocol {
    /**Message的what值**/
    public final int W_REQUEST_DOWNLOAD=1,W_REQUEST_CANCEL=2, W_TOAST=3,W_UPDATE=4,W_COMPLETE=5;
}

ControllerState.java,三种不同状态:未下载,正在下载,下载完成

package controllers;

import android.os.Message;
/**
 * 不同状态
 * @author fhp
 *
 */
public abstract class ControllerState {
    protected Controller controller;
    public ControllerState(Controller controller)
    {
        this.controller=controller;
    }
    public abstract boolean handleMessage(Message msg);
    
}
/**
 * 未开始下载状态,默认值
 * @author fhp
 *
 */
final class NotStartState extends ControllerState implements ControllerProtocol{

    public NotStartState(Controller controller) {
        super(controller);
        // TODO 自动生成的构造函数存根
    }

    @Override
    public boolean handleMessage(Message msg) {
        // TODO 自动生成的方法存根
        switch(msg.what)
        {
        case W_REQUEST_DOWNLOAD:
            controller.notifyOutboxHandlers(W_TOAST, 0, 0, "开始下载!");
            controller.changeState(new DownloadingState(controller));
            break;
        case W_REQUEST_CANCEL:
            controller.notifyOutboxHandlers(W_TOAST, 0, 0, "下载未开始,不用取消!");
            break;
        }
        return false;
    }
    
}
/**
 * 正在下载的状态
 * @author fhp
 *
 */
final class DownloadingState extends ControllerState implements ControllerProtocol{

    public DownloadingState(final Controller controller) {
        super(controller);
        // 切换到下载状态时,开始下载。不能用Controller里的HandlerThread下载,虽然不会阻塞UI,但会阻塞消息传递
        new Thread(){
            public void run()
            {
                controller.getModel().download();
            }
        }.start();
    }

    @Override
    public boolean handleMessage(Message msg) {
        // TODO 自动生成的方法存根
        switch(msg.what)
        {
        case W_REQUEST_DOWNLOAD:
            controller.notifyOutboxHandlers(W_TOAST, 0, 0, "下载已开始!");
            break;
        case W_REQUEST_CANCEL:
            controller.getModel().cancel();
            controller.changeState(new NotStartState(controller));
            controller.notifyOutboxHandlers(W_TOAST, 0, 0, "下载取消!");
            break;
        }
        return false;
    }
    
}
/**
 * 下载完成状态
 * @author fhp
 *
 */
final class CompletedState extends ControllerState implements ControllerProtocol{

    public CompletedState(Controller controller) {
        super(controller);
    }

    @Override
    public boolean handleMessage(Message msg) {
        switch(msg.what)
        {
        case W_REQUEST_DOWNLOAD:
            controller.notifyOutboxHandlers(W_TOAST, 0, 0, "下载已完成!");
            break;
        case W_REQUEST_CANCEL:
            controller.notifyOutboxHandlers(W_TOAST, 0, 0, "下载完成,不能取消!");
            break;
        }
        return false;
    }
    
}

Controller.java:

package controllers;

import java.util.ArrayList;
import java.util.List;

import models.Model;
import models.Model.Listener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;


public class Controller implements Handler.Callback,ControllerProtocol {
    //收信Handler
    private Handler inBoxHandler;
    //发信Handler
    private List outBoxHandlers = new ArrayList();

    private Model model;
    //状态模式,状态改变,改变state
    private ControllerState state;

    public Controller(Model model) {
        this.model = model;
        // 默认没开始状态
        this.state = new NotStartState(this);

        inBoxHandler = new Handler(this);
        //增加监听器
        this.model.addListener(new Listener() {
            
            @Override
            public void onProgressUpdated(Model model) {
                // TODO 自动生成的方法存根
                notifyOutboxHandlers(W_UPDATE,0,0,model);
            }
            
            @Override
            public void onCompleted(Model model) {
                // TODO 自动生成的方法存根
                notifyOutboxHandlers(W_COMPLETE,0,0,model);
                changeState(new CompletedState(Controller.this));
            }
        });
        
    }

    /**
     * 改变状态
     * 
     * @param state
     */
    public void changeState(ControllerState state) {
        this.state = state;
    }

    public void addOutBoxHandler(Handler handler) {
        outBoxHandlers.add(handler);
    }

    public Handler getInBoxHandler() {
        return inBoxHandler;
    }

    public Model getModel() {
        return model;
    }

    @Override
    public boolean handleMessage(Message msg) {
        // 收到消息后(这里就是点击事件),转发给ControllerState处理,不同的State处理方法不同
        return state.handleMessage(msg);
    }
    /**
     * 消息发给所有的Handler,例子里只有一个
     * @param what
     * @param arg1
     * @param arg2
     * @param obj
     */
    public void notifyOutboxHandlers(int what, int arg1, int arg2, Object obj) {
        for (Handler handler : outBoxHandlers) {
            Message msg = Message.obtain(handler, what, arg1, arg2, obj);
            msg.sendToTarget();
        }

    }

}

最后是activity:

package com.pocketdigi.mvcdemo;

import models.Model;
import controllers.Controller;
import controllers.ControllerProtocol;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity implements Handler.Callback,OnClickListener,ControllerProtocol{
    
    private TextView tv_count;
    private Button btn_down,btn_cancel;
    private ProgressBar progressBar;
    //独立的Controller
    private Controller controller;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViews();
        
        controller=new Controller(new Model("http://www.pocketdigi.com"));
        controller.addOutBoxHandler(new Handler(this));
        
    }
    
    private void findViews()
    {
        tv_count=(TextView)findViewById(R.id.tv_count);
        btn_down=(Button)findViewById(R.id.btn_down);
        btn_cancel=(Button)findViewById(R.id.btn_cancel);
        progressBar=(ProgressBar)findViewById(R.id.progressBar);
        btn_down.setOnClickListener(this);
        btn_cancel.setOnClickListener(this);
    }

    @Override
    public boolean handleMessage(Message msg) {
        // TODO 自动生成的方法存根
        switch(msg.what)
        {
        case W_TOAST:
            String string=(String)msg.obj;
            showToast(string);
            break;
        case W_UPDATE:
            Model model=(Model)msg.obj;
            double percent=model.getDownloadedSize()/(double)model.getTotalSize()*100;
            progressBar.setProgress((int)percent);
            tv_count.setText(String.format("%1$.2f%%", percent));
            break;
        case W_COMPLETE:
            showToast("下载完成");
            break;
        }
        return false;
    }
    private void showToast(String string)
    {
        Toast.makeText(this, string, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onClick(View v) {
        switch(v.getId())
        {
        case R.id.btn_down:
            controller.getInBoxHandler().sendEmptyMessage(W_REQUEST_DOWNLOAD);
            break;
        case R.id.btn_cancel:
            controller.getInBoxHandler().sendEmptyMessage(W_REQUEST_CANCEL);
            break;
        }
    }
       
}

源码下载链接: http://pan.baidu.com/s/1o6rv0BS

通过查看HandlerThread,可以知道,本质上,它就是一个Thread,只不过多了一个Looper,可以跟UI线程一样,通过Handler,发送消息在后台线程中执行任务。示例代码:

package com.pocketdigi.handlerthread;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.widget.TextView;

public class MainActivity extends Activity {
    TextView textView;
    Handler mBackgroundHandler, mUiHandler;
    //destory标记
    boolean mDestoryed=false;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textView);
        //实例化一个HandlerThread,参数为name,可随意
        HandlerThread thread = new HandlerThread("handlerThread");
        //必须马上start();
        thread.start();
        //通过HandlerThread的looper,实例化一个handler,这个handler发送的消息都由HandlerThread处理
        mBackgroundHandler = new Handler(thread.getLooper());
        //无参数,默认是当前线程的handler,即UiThread
        mUiHandler = new Handler();

        mBackgroundHandler.post(new Runnable() {

            @Override
            public void run() {
                // 后台线程执行
                for (int i = 0; i < 100&&!mDestoryed; i++) {
                    SystemClock.sleep(1000);
                    System.out.println(i);
                    final int finali = i;
                    mUiHandler.post(new Runnable() {
                        //UI线程执行
                        @Override
                        public void run() {
                            // TODO 自动生成的方法存根
                            textView.setText(String.valueOf(finali));
                        }
                    });
                }

            }
        });

    }

    @Override
    protected void onDestroy() {
        // TODO 自动生成的方法存根
        super.onDestroy();
        //标记activity已关闭
        mDestoryed=true;
         //最后让looper执行quit,否则一直在循环,HandlerThread就不会终止
        mBackgroundHandler.getLooper().quit();
    }
}

默认情况下,不能捕获线程中逃逸的异常,一但异常逃出run方法,就会向外传播到控制台,J2SE 5以后,可以使用Executor来解决这个问题。 Thread类有一个setUncaughtExceptionHandler方法,可以设置未捕获异常的处理方法。利用ThreadFactory,我们可以给所有使用线程池管理的线程添加处理方法。

package com.test;

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.concurrent.ThreadFactory;

public class TFactory implements ThreadFactory {

    @Override
    public Thread newThread(Runnable r) {
        // TODO 自动生成的方法存根
        Thread t=new Thread(r);
        t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                // TODO 自动生成的方法存根
                System.out.println("caught "+e);
            }
        });
        return t;
    }

}

线程池中使用:

package com.test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) {
        ExecutorService exec=Executors.newCachedThreadPool(new TFactory());

        exec.execute(new Runnable() {
            @Override
            public void run() {
                // TODO 自动生成的方法存根
                throw new IllegalArgumentException();
            }
        });
        exec.shutdown();
    }

}

如果使用Runnable接口,run方法是没有返回值的,若需要返回值,可以使用Callable接口。不过在获取返回值时,如果线程没执行会,会阻塞。 代码来自Think in Java. TaskWithResult.java

package com.test;

import java.util.concurrent.Callable;

public class TaskWithResult implements Callable {
    private int id;
    public TaskWithResult(int id) {
        // TODO 自动生成的构造函数存根
        this.id=id;
    }

    @Override
    public String call() throws Exception {
        // TODO 自动生成的方法存根
        for(int i=0;i<5;i++)
        {
            System.out.println("#"+id+" "+i);
            Thread.sleep(1000);
        }
        return "result of TaskWithResult "+id;
    }

}



public class Main {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        //必须调用exec.submit方法开启线程,exec.submit会产生一个Feture对象
        ArrayList> results = new ArrayList>();
        for (int i = 0; i < 10; i++) {
            results.add(exec.submit(new TaskWithResult(i)));
        }
        for (Future fs : results) {
            try {
                //fs,get()方法获取call方法的返回值,但如果调用时,call()方法没执行完,会阻塞
                //可以用fs.isDone()判断是否执行完
                System.out.println(fs.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            } finally {
                exec.shutdown();
            }
        }

    }

}

说实话,不太理解为什么在finally里shutdown,而不是在第一个或者第二个for循环完。