2013年3月15日星期五

【转】Executor Framework源码分析 转贴

From Evernote:

【转】Executor Framework源码分析 转贴

Clipped from: http://hi.baidu.com/ethancheer/item/cfdc2eea475ed20e65db0024

【转】Executor Framework源码分析 转贴




Executor接口是整个framework的核心基础,只定义一个void execute(Runnable command)方法。
ExecutorService extends Executor,定义了一些LifeCycle方法。
AbstractExecutorService implements ExecutorService,提供ExecutorService的一个模板实现。

首先来看Executors类的能常用法:
1.调用Executors类中静态的newCachedThreadPool或newFixedThreadPool方法
2.调用submit来提交一个Runnable或Callable对象
3.如果希望能够取消任务或如果提交一个Callable对象,那就保存好返回的Future对象
4.当不想再提交任何任务时调用shutdown。
通用代码如下:
ExecutorService pool = Executors.newCachedThreadPool();
Callable<String> call0 = new FutureTest(0);
Future<String> future0 = pool.submit(call0);
System.out.println(future0.get());


下面重点分析Executors一些方法,首先看newCachedThreadPool():
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
可以看出是传入SynchronousQueue construct出ThreadPoolExecutor。其实也可以自己定义ThreadFactory:
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(),threadFactory);
}
通常采用默认的ThreadFactory,从上图中知道ThreadFactory中有一个newThread方法用来创建线程。,来看一下Executors中ThreadFactory的实现:
/**
* The default thread factory
*/
static class DefaultThreadFactory implements ThreadFactory {
static final AtomicInteger poolNumber = new AtomicInteger(1);
final ThreadGroup group;
final AtomicInteger threadNumber = new AtomicInteger(1);
final String namePrefix;

DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
}//constructor中实例化一些变量以变newThread方法调用

public Thread newThread(Runnable r) {
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;//创建出来的线程均非Daemon且优先级为NORM_PRIORITY级别
}
}
从上面的程序也可以知道,通过ThreadFactory创建出来的线程都属于同一个ThreadGroup。
下面继续分析ThreadPoolExecutor的constructor:
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
}
其中各参数意义如下:
  • corePoolSize: 线程池维护线程的最少数量,原始化时线程池中线程的数量
  • maximumPoolSize:线程池维护线程的最大数量
  • keepAliveTime: 线程池中线程所允许的空闲时间,如果超过这个时间还没有接收到任务就销毁
  • unit: 线程池维护线程所允许的空闲时间的单位
  • workQueue: 线程池所使用的缓冲队列
  • handler: 当线程池数目已经达到maximumPoolSize且workQueue已满,又有新的任务时,采用handler定义的策略处理
defaultHandler指定the handler to use when execution is blocked because the thread bounds and queue capacities are reached.
/**
* The default rejected execution handler
*/
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
默认采用AbortPolicy,也就是当队列中线程池已满而又有新任务达到时,会直接放弃新任务。ThreadPoolExecutor中定义了四种policy:

各种Policy均实现RejectedExecutionHandler interface,void rejectedExecution(Runnable r, ThreadPoolExecutor executor)方法定义策略。下面来看看各种Policy的源码:
AbortPolicy:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException();
}
直接抛出异常;
DiscardPolicy:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
什么也不做,直接忽略掉任务;
DiscardOldestPolicy:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
弹出队列中第一个任务,然后执行新加入的任务;
CallerRunsPolicy:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
执行caller的任务。四种Policy可以自行选择,默认采用AbortPolicy。

继续看其他方法的实现:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
可以发现这些方法内部全部是construct出ThreadPoolExecutor实例,只是传递的参数不同。
newCachedThreadPool因为采用SynchronousQueue来缓冲任务,所以保证只有队列里上一个任务被线程取出执行后下一个任务才能执行。其适用于execute many short-lived asynchronous tasks。
newFixedThreadPool保证线程池中永远有初始数目的线程,而且其采用LinkedBlockingQueue作为任务缓冲队列,保证任务能按FIFO的次序执行。

下面重点分析ThreadPoolExecutor的execute方法:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
if (runState == RUNNING && workQueue.offer(command)) {
if (runState != RUNNING || poolSize == 0)
ensureQueuedTaskHandled(command);
} else if (!addIfUnderMaximumPoolSize(command))
reject(command); // is shutdown or saturated
}
}
首先判断command,然后判断poolSize,其定义如下:
/**
* Current pool size, updated only while holding mainLock but volatile to
* allow concurrent readability even during updates.
*/
private volatile int poolSize;
从其注释知代表当前线程池中线程数目,接着执行addIfUnderCorePoolSize:
/**
* Creates and starts a new thread running firstTask as its first task, only
* if fewer than corePoolSize threads are running and the pool is not shut
* down.
*
* @param firstTask
*            the task the new thread should run first (or null if none)
* @return true if successful
*/
private boolean addIfUnderCorePoolSize(Runnable firstTask) {
Thread t = null;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (poolSize < corePoolSize && runState == RUNNING)
//当前线程池中线程数目小于最小数目,则向池中增加线程,运行并返回
t = addThread(firstTask);
} finally {
mainLock.unlock();
}
if (t == null)
return false;
t.start();
return true;
}
addThread方法如下:
/**
* Creates and returns a new thread running firstTask as its first task.
* Call only while holding mainLock.
* 
* @param firstTask
*            the task the new thread should run first (or null if none)
* @return the new thread, or null if threadFactory fails to create thread
*/
private Thread addThread(Runnable firstTask) {
Worker w = new Worker(firstTask);
Thread t = threadFactory.newThread(w);
if (t != null) {
w.thread = t;
workers.add(w);
int nt = ++poolSize;
if (nt > largestPoolSize)
largestPoolSize = nt;
}
return t;
}
Worker类主要是线程池的基本元素,其功能是将Runnable变量包装,其定义的变量如下:
/**
* The runLock is acquired and released surrounding each task execution.
* It mainly protects against interrupts that are intended to cancel the
* worker thread from instead interrupting the task being run.
*/
private final ReentrantLock runLock = new ReentrantLock();

/**
* Initial task to run before entering run loop. Possibly null.
*/
private Runnable firstTask;

/**
* Per thread completed task counter; accumulated into
* completedTaskCount upon termination.
*/
volatile long completedTasks;

/**
* Thread this worker is running in. Acts as a final field, but cannot
* be set until thread is created.
*/
Thread thread;
workers定义如下:
/**
* Set containing all worker threads in pool. Accessed only when holding
* mainLock.
*/
private final HashSet<Worker> workers = new HashSet<Worker>();

在addIfUnderCorePoolSize方法中启动Worker会执行其run方法:
/**
* Main run loop
*/
public void run() {
try {
Runnable task = firstTask;
firstTask = null;
while (task != null || (task = getTask()) != null) { //如果取不到task就中断循环
runTask(task);
task = null;
}
} finally {
workerDone(this);
}
}
可以看出worker会一直不停的尝试取任务,然后执行,先来看看线程池是如何进行任务分配的,getTask代码:
/**
* Gets the next task for a worker thread to run. The general approach is
* similar to execute() in that worker threads trying to get a task to run
* do so on the basis of prevailing state accessed outside of locks. This
* may cause them to choose the "wrong" action, such as trying to exit
* because no tasks appear to be available, or entering a take when the pool
* is in the process of being shut down. These potential problems are
* countered by (1) rechecking pool state (in workerCanExit) before giving
* up, and (2) interrupting other workers upon shutdown, so they can recheck
* state. All other user-based state changes (to allowCoreThreadTimeOut etc)
* are OK even when performed asynchronously wrt getTask.
* 
* @return the task
*/
Runnable getTask() {
for (;;) {
try {
int state = runState;
if (state > SHUTDOWN)
return null;//此时为shutdownNow状态,不分配任务
Runnable r;
if (state == SHUTDOWN) // Help drain queue
r = workQueue.poll();//此时为shutdown状态,需要把队列中的任务执行完毕
else if (poolSize > corePoolSize || allowCoreThreadTimeOut)
//池中线程数目大于最小数目,此时任务较多,poll方法能更快取出队列元素
r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
else
r = workQueue.take();
//此时池中线程数目较少,任务也较少,可以调用阻塞方法慢慢取任务
if (r != null)
return r;
if (workerCanExit()) {
if (runState >= SHUTDOWN) // Wake up others
interruptIdleWorkers();
return null;
}
// Else retry
} catch (InterruptedException ie) {
// On interruption, re-check runState
}
}
}
继续看runTask方法:
/**
* Runs a single task between before/after methods.
*/
private void runTask(Runnable task) {
final ReentrantLock runLock = this.runLock;
runLock.lock();
try {
/*
* Ensure that unless pool is stopping, this thread does not
* have its interrupt set. This requires a double-check of state
* in case the interrupt was cleared concurrently with a
* shutdownNow -- if so, the interrupt is re-enabled.
*/
if (runState < STOP && Thread.interrupted() && runState >= STOP)
thread.interrupt();
/*
* Track execution state to ensure that afterExecute is called
* only if task completed or threw exception. Otherwise, the
* caught runtime exception will have been thrown by
* afterExecute itself, in which case we don't want to call it
* again.
*/
boolean ran = false;
beforeExecute(thread, task);
try {
task.run();
ran = true;
afterExecute(task, null);
++completedTasks;
} catch (RuntimeException ex) {
if (!ran)
afterExecute(task, ex);
throw ex;
}
} finally {
runLock.unlock();
}
}
beforeExecute和afterExecute方法在ThreadPoolExecutor中没有具体实现,留给想实现自定义线程池的开发者自己实现,用于在线程执行任务之前和之后做一些其他的譬如清理等工作。
接着看ensureQueuedTaskHandled方法:
/**
* Rechecks state after queuing a task. Called from execute when pool state
* has been observed to change after queuing a task
. If the task was queued
* concurrently with a call to shutdownNow, and is still present in the
* queue, this task must be removed and rejected to preserve shutdownNow
* guarantees. Otherwise, this method ensures (unless addThread fails) that
* there is at least one live thread to handle this task
* 
* @param command
*            the task
*/
private void ensureQueuedTaskHandled(Runnable command) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
boolean reject = false;
Thread t = null;
try {
int state = runState;
if (state != RUNNING && workQueue.remove(command))
reject = true;
else if (state < STOP && poolSize < Math.max(corePoolSize, 1) && !workQueue.isEmpty())
t = addThread(null);
} finally {
mainLock.unlock();
}
if (reject)
reject(command);//调用handler来处理
else if (t != null)
t.start();
}
参考方法注释,这个方法主要用于当向缓冲队列中添加任务后检测队列,如果有任务提交shutdownNow,而此时队列中还有任务,那会reject掉该任务,否则保证至少有一个线程来执行该任务
最后来看看addIfUnderMaximumPoolSize方法:
/**
* Creates and starts a new thread running firstTask as its first task, only
* if fewer than maximumPoolSize threads are running and pool is not shut
* down.
* 
* @param firstTask
*            the task the new thread should run first (or null if none)
* @return true if successful
*/
private boolean addIfUnderMaximumPoolSize(Runnable firstTask) {
Thread t = null;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (poolSize < maximumPoolSize && runState == RUNNING)
t = addThread(firstTask);
} finally {
mainLock.unlock();
}
if (t == null)
return false;
t.start();
return true;
}

最后回顾一下execute方法所进行的操作:
1.检测任务
2.如果线程池中数目小于最小数,调用addIfUnderCorePoolSize方法增加线程,运行并返回
3.如果线程池处在RUNNING状态,并且线程池中数目大于最小数,任务缓冲队列未满:如果线程池处在非RUNNING状态,或者当前线程池中已经没有空闲线程,则执行ensureQueuedTaskHandled;
4.如果已满则执行addIfUnderMaximumPoolSize直到线程池中线程数目达至maximumPoolSize 。当线程数目达到maximumPoolSize 时,调用handler所定义的策略来处理新来的任务。

从上面可以看出线程池中处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

继续看看submit方法,这个方法在AbstractExecutorService中定义:
public <T> Future<T> submit(Callable<T> task) {
if (task == null)
throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);生成一个FutureTask
execute(ftask);
return ftask;
}
将其执行delegate给子类。

再来看一个较少用的方法getTaskCount:
public long getTaskCount() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
long n = completedTaskCount;
for (Worker w : workers) {
n += w.completedTasks;//累加池中每个线程已经完成的任务数
if (w.isActive())//如果线程是活跃的,表明其正在执行任务
++n;
}

return n + workQueue.size();//累加上缓冲队列里的任务
} finally {
mainLock.unlock();
}
}

继续看另外一个重要的方法shutdownNow:
public List<Runnable> shutdownNow() {
/*
* shutdownNow differs from shutdown only in that 1. runState is set to
* STOP, 2. all worker threads are interrupted, not just the idle ones,
* and 3. the queue is drained and returned.
*/
SecurityManager security = System.getSecurityManager();
if (security != null)
security.checkPermission(shutdownPerm);

final ReentrantLock mainLock = this.mainLock;
mainLock.lock(); //锁定
try {
if (security != null) { // Check if caller can modify our threads
for (Worker w : workers)
security.checkAccess(w.thread);
}

int state = runState;
if (state < STOP)
runState = STOP; //设置状态为STOP

try {
for (Worker w : workers) {
w.interruptNow();
}
} catch (SecurityException se) { // Try to back out
runState = state;
// tryTerminate() here would be a no-op
throw se;
}

List<Runnable> tasks = drainQueue();
tryTerminate(); // Terminate now if pool and queue empty
return tasks;
} finally {
mainLock.unlock();
}
}
可能看出,其主要是执行遍历workers,执行interruptNow方法:
/**
* Interrupts thread even if running a task.
*/
void interruptNow() {
thread.interrupt();//不管线程是否在执行任务,直接中断
}
然后调用drainQueue,返回workQueue中未执行的任务。

对比shutdown方法,其主要区别在于:
if (state < SHUTDOWN)
runState = SHUTDOWN;

try {
for (Worker w : workers) {
w.interruptIfIdle();
}
} catch (SecurityException se) { // Try to back out
runState = state;
// tryTerminate() here would be a no-op
throw se;
}

tryTerminate();
遍历workers时,执行interruptIfIdle方法:
/**
* Interrupts thread if not running a task.
*/
void interruptIfIdle() {
final ReentrantLock runLock = this.runLock;
if (runLock.tryLock()) {
try {
thread.interrupt();
} finally {
runLock.unlock();
}
}
}
shutdown和shutdownNow方法都是负责interrupt线程,主要逻辑如下:
1.设置runState并interrupt
2.getTask方法检测runState,如果为shutdownNow状态,返回null,如果take方法阻塞抛出InterruptedException,那么生吞这个异常并在下一轮中继续检测runState。
3.Worker的run方法中断循环,执行workDone方法。

最后需要注意的是:newCachedThreadPool允许线程池中线程最大数目为Inter.MAX_VALUE,事实上这是不现实的,JDK中默认一个线程1M内存。这点需要在使用中注意。

2013年3月8日星期五

volatile

From Evernote:

volatile

Clipped from: file:///

正确使用 volatile 变量的条件

您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:

  • 对变量的写操作不依赖于当前值。
  • 该变量没有包含在具有其他变量的不等式中。

对于应用场景而言, 

@NotThreadSafe   public class NumberRange {      private int lower, upper;        public int getLower() { return lower; }      public int getUpper() { return upper; }        public void setLower(int value) {           if (value > upper) //不等式              throw new IllegalArgumentException(...);          lower = value;      }        public void setUpper(int value) {           if (value < lower)               throw new IllegalArgumentException(...);          upper = value;      }  }  


作为独立标记使用:


一次性发布:
独立观察:
volatile bean 模式

无可奈何的写锁

2012年7月13日星期五

Java命令行运行参数说明大全

Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOME"bin"java –option 来启动,-option为虚拟机参数,JAVA_HOME为JDK安装路径,通过这些参数可对虚拟机的运行状态进行调整,掌握参数的含义可对虚拟机的运行模式有更深入理解。

一、         查看参数列表:
虚拟机参数分为基本和扩展两类,在命令行中输入JAVA_HOME"bin"java 就可得到基本参数列表,在命令行输入JAVA_HOME"bin"java –X 就可得到扩展参数列表。
二、         基本参数说明:
1.     -client,-server
这两个参数用于设置虚拟机使用何种运行模式,client模式启动比较快,但运行时性能和内存管理效率不如server模式,通常用于客户端应用程序。相反,server模式启动比client慢,但可获得更高的运行性能。
在 windows上,缺省的虚拟机类型为client模式,如果要使用 server模式,就需要在启动虚拟机时加-server参数,以获得更高性能,对服务器端应用,推荐采用server模式,尤其是多个CPU的系统。在 Linux,Solaris上缺省采用server模式。 

2.     -hotspot
含义与client相同,jdk1.4以前使用的参数,jdk1.4开始不再使用,代之以client。
3.     -classpath,-cp
虚拟机在运行一个类时,需要将其装入内存,虚拟机搜索类的方式和顺序如下:
Bootstrap classes,Extension classes,User classes。
Bootstrap 中的路径是虚拟机自带的jar或zip文件,虚拟机首先搜索这些包文件,用System.getProperty("sun.boot.class.path")可得到虚拟机搜索的包名。
Extension是位于jre"lib"ext目录下的jar文件,虚拟机在搜索完Bootstrap后就搜索该目录下的jar文件。用System. getProperty("java.ext.dirs")可得到虚拟机使用Extension搜索路径。
User classes搜索顺序为当前目录、环境变量 CLASSPATH、-classpath。
4.     -classpath
告知虚拟机搜索目录名、jar文档名、zip文档名,之间用分号;分隔。
例如当你自己开发了公共类并包装成一个common.jar包,在使用 common.jar中的类时,就需要用-classpath common.jar 告诉虚拟机从common.jar中查找该类,否则虚拟机就会抛出java.lang.NoClassDefFoundError异常,表明未找到类定义。 
在运行时可用System.getProperty("java.class.path")得到虚拟机查找类的路径。

使用-classpath后虚拟机将不再使用CLASSPATH中的类搜索路径,如果-classpath和CLASSPATH都没有设置,则虚拟机使用当前路径(.)作为类搜索路径。
推荐使用-classpath来定义虚拟机要搜索的类路径,而不要使用环境变量 CLASSPATH的搜索路径,以减少多个项目同时使用CLASSPATH时存在的潜在冲突。例如应用1要使用a1.0.jar中的类G,应用2要使用 a2.0.jar中的类G,a2.0.jar是a1.0.jar的升级包,当a1.0.jar,a2.0.jar都在CLASSPATH中,虚拟机搜索到第一个包中的类G时就停止搜索,如果应用1应用2的虚拟机都从CLASSPATH中搜索,就会有一个应用得不到正确版本的类G。
5.     -D<propertyName>=value
在虚拟机的系统属性中设置属性名/值对,运行在此虚拟机之上的应用程序可用System.getProperty("propertyName")得到value的值。
如果value中有空格,则需要用双引号将该值括起来,如-Dname="space string"。
该参数通常用于设置系统级全局变量值,如配置文件路径,应为该属性在程序中任何地方都可访问。

6.    -verbose[:class|gc|jni]
在输出设备上显示虚拟机运行信息。
verbose和verbose:class含义相同,输出虚拟机装入的类的信息,显示的信息格式如下:
[Loaded java.io.FilePermission$1 from shared objects file]
当虚拟机报告类找不到或类冲突时可用此参数来诊断来查看虚拟机从装入类的情况。
7.     -verbose:gc
在虚拟机发生内存回收时在输出设备显示信息,格式如下:
[Full GC 268K->168K(1984K), 0.0187390 secs]
该参数用来监视虚拟机内存回收的情况。
8.     -verbose:jni
在虚拟机调用native方法时输出设备显示信息,格式如下:
[Dynamic-linking native method HelloNative.sum ... JNI]
该参数用来监视虚拟机调用本地方法的情况,在发生jni错误时可为诊断提供便利。
9.     -version
显示可运行的虚拟机版本信息然后退出。一台机器上装有不同版本的JDK时
10.-showversion
显示版本信息以及帮助信息。 内容来自dedecms 
11.-ea[:<packagename>...|:<classname>]
12.-enableassertions[:<packagename>...|:<classname>]
从JDK1.4开始,java可支持断言机制,用于诊断运行时问题。通常在测试阶段使断言有效,在正式运行时不需要运行断言。断言后的表达式的值是一个逻辑值,为true时断言不运行,为false时断言运行,抛出java.lang.AssertionError错误。
上述参数就用来设置虚拟机是否启动断言机制,缺省时虚拟机关闭断言机制,用-ea 可打开断言机制,不加<packagename>和classname时运行所有包和类中的断言,如果希望只运行某些包或类中的断言,可将包名或类名加到-ea之后。例如要启动包com.foo.util中的断言,可用命令 –ea:com.foo.util 。
13.-da[:<packagename>...|:<classname>]
14.-disableassertions[:<packagename>...|:<classname>]
用来设置虚拟机关闭断言处理,packagename和classname的使用方法和-ea相同。
15.-esa | -enablesystemassertions
设置虚拟机显示系统类的断言。
16.-dsa | -disablesystemassertions
设置虚拟机关闭系统类的断言。 dedecms.com 
17.-agentlib:<libname>[=<options>]
该参数是JDK5新引入的,用于虚拟机装载本地代理库。
Libname 为本地代理库文件名,虚拟机的搜索路径为环境变量PATH中的路径,options为传给本地库启动时的参数,多个参数之间用逗号分隔。在Windows 平台上虚拟机搜索本地库名为libname.dll的文件,在Unix上虚拟机搜索本地库名为libname.so的文件,搜索路径环境变量在不同系统上有所不同,Linux、SunOS、IRIX上为LD_LIBRARY_PATH,AIX上为LIBPATH,HP-UX上为SHLIB_PATH。
例如可使用-agentlib:hprof来获取虚拟机的运行情况,包括CPU、内存、线程等的运行数据,并可输出到指定文件中,可用-agentlib:hprof=help来得到使用帮助列表。在jre"bin目录下可发现hprof.dll文件。
18. -agentpath:<pathname>[=<options>]
设置虚拟机按全路径装载本地库,不再搜索PATH中的路径。其他功能和agentlib相同。
19.-javaagent:<jarpath>[=<options>]
虚拟机启动时装入java语言设备代理。Jarpath文件中的mainfest 文件必须有Agent-Class属性。代理类要实现public static void premain(String agentArgs, Instrumentation inst)方法。当虚拟机初始化时,将按代理类的说明顺序调用premain方法。 copyright dedecms 
参见:java.lang.instrument
三、         扩展参数说明
1.     -Xmixed
设置-client模式虚拟机对使用频率高的方式进行Just-In-Time编译和执行,对其他方法使用解释方式执行。该方式是虚拟机缺省模式。
2.     -Xint
设置-client模式下运行的虚拟机以解释方式执行类的字节码,不将字节码编译为本机码。
3.     -Xbootclasspath:path
4.     -Xbootclasspath/a:path
5.     -Xbootclasspath/p:path
改变虚拟机装载缺省系统运行包rt.jar而从-Xbootclasspath中设定的搜索路径中装载系统运行类。除非你自己能写一个运行时,否则不会用到该参数。
/a:将在缺省搜索路径后加上path 中的搜索路径。
/p:在缺省搜索路径前先搜索path中的搜索路径。
6.     -Xnoclassgc
关闭虚拟机对class的垃圾回收功能。
7.     -Xincgc
启动增量垃圾收集器,缺省是关闭的。增量垃圾收集器能减少偶然发生的长时间的垃圾回收造成的暂停时间。但增量垃圾收集器和应用程序并发执行,因此会占用部分CPU在应用程序上的功能。 
8.     -Xloggc:<file>
将虚拟机每次垃圾回收的信息写到日志文件中,文件名由file指定,文件格式是平文件,内容和-verbose:gc输出内容相同。
9.     -Xbatch
虚拟机的缺省运行方式是在后台编译类代码,然后在前台执行代码,使用-Xbatch参数将关闭虚拟机后台编译,在前台编译完成后再执行。
10.-Xms<size>
设置虚拟机可用内存堆的初始大小,缺省单位为字节,该大小为1024的整数倍并且要大于1MB,可用k(K)或m(M)为单位来设置较大的内存数。初始堆大小为2MB。
例如:-Xms6400K,-Xms256M
11.-Xmx<size>
设置虚拟机内存堆的最大可用大小,缺省单位为字节。该值必须为1024整数倍,并且要大于2MB。可用k(K)或m(M)为单位来设置较大的内存数。缺省堆最大值为64MB。
例如:-Xmx81920K,-Xmx80M
当应用程序申请了大内存运行时虚拟机抛出java.lang.OutOfMemoryError: Java heap space错误,就需要使用-Xmx设媒洗蟮目捎媚诖娑选?BR>
12.-Xss<size>
设置线程栈的大小,缺省单位为字节。与-Xmx类似,也可用K或M来设置较大的值。通常操作系统分配给线程栈的缺省大小为1MB。 
另外也可在java中创建线程对象时设置栈的大小,构造函数原型为Thread(ThreadGroup group, Runnable target, String name, long stackSize)。
13.-Xprof
输出CPU运行时的诊断信息。
14.-Xfuture
对类文件进行严格格式检查,以保证类代码符合类代码规范。为保持向后兼容,虚拟机缺省不进行严格的格式检查。
15.-Xrs
减少虚拟机中操作系统的信号(singals)的使用。该参数通常用在虚拟机以后台服务方式运行时使用(如Servlet)。
16.-Xcheck:jni
一、运行class文件 
执行带main方法的class文件,命令行为:
java <CLASS文件名>
注意:CLASS文件名不要带文件后缀.class
例如:
java Test
如果执行的class文件是带包的,即在类文件中使用了:
package <包名>
那应该在包的基路径下执行,命令行为:
java <包名>.CLASS文件名
例如:
PackageTest.java中,其包名为:com.ee2ee.test,对应的语句为:
package com.ee2ee.test;
PackageTest.java及编译后的class文件PackageTest.class的存放目录如下:
classes
  |__com
      |__ee2ee
           |__test
                |__PackageTest.java
                |__PackageTest.class

要运行PackageTest.class,应在classes目录下执行:
java com.ee2ee.test.PackageTest 
二、运行jar文件中的class 
原理和运行class文件一样,只需加上参数-cp <jar文件名>即可。
例如:执行test.jar中的类com.ee2ee.test.PackageTest,命令行如下:
java -cp test.jar com.ee2ee.test.PackageTest 
三、显示jdk版本信息 
当一台机器上有多个jdk版本时,需要知道当前使用的是那个版本的jdk,使用参数-version即可知道其版本,命令行为:
java -version 
四、增加虚拟机可以使用的最大内存 
java虚拟机可使用的最大内存是有限制的,缺省值通常为64MB或128MB。
如果一个应用程序为了提高性能而把数据加载内存中而占用较大的内存,比如超过了默认的最大值128MB,需要加大java虚拟机可使用的最大内存,否则会出现Out of Memory(系统内存不足)的异常。启动java时,需要使用如下两个参数:
-Xms java虚拟机初始化时使用的内存大小
-Xmx java虚拟机可以使用的最大内存
以上两个参数中设置的size,可以带单位,例如:256m表示256MB
举例说明:
java -Xms128m -Xmx256m ...
表示java虚拟机初始化时使用的内存为128MB,可使用的最大内存为256MB。
对于tomcat,可以修改其脚本catalina.sh(unix平台)或catalina.bat(windows平台),设置变量JAVA_OPTS即可,例如:
JAVA_OPTS='-Xms128m -Xmx256m' 
在控制台输出信息中,有个-X(注意是大写)的命令,这个正是查看JVM配置参数的命令。
其次,用java -X 命令查看JVM的配置说明:
运行后如下结果,这些就是配置JVM参数的秘密武器,这些信息都是英文的,为了方便阅读,我根据自己的理解翻译成中文了(不准确的地方还请各位博友斧正)
-----------------------------------------------------------------------
D:\j2sdk15\bin>java -X
    -Xmixed           mixed mode execution (default)
    -Xint             interpreted mode execution only
    -Xbootclasspath:<directories and zip/jar files separated by ;>
                      set search path for bootstrap classes and resources
    -Xbootclasspath/a:<directories and zip/jar files separated by ;>
                      append to end of bootstrap class path
    -Xbootclasspath/p:<directories and zip/jar files separated by ;>
                      prepend in front of bootstrap class path
    -Xnoclassgc       disable class garbage collection
    -Xincgc           enable incremental garbage collection
    -Xloggc:<file>    log GC status to a file with time stamps
    -Xbatch           disable background compilation
    -Xms<size>        set initial Java heap size
    -Xmx<size>        set maximum Java heap size
    -Xss<size>        set java thread stack size
    -Xprof            output cpu profiling data
    -Xfuture          enable strictest checks, anticipating future default
    -Xrs              reduce use of OS signals by Java/VM (see documentation)
    -Xcheck:jni       perform. additional checks for JNI functions
    -Xshare:off       do not attempt to use shared class data
    -Xshare:auto      use shared class data if possible (default)
    -Xshare:on        require using shared class data, otherwise fail.
 
The -X options are non-standard and subject to change without notice.
-----------------------------------------------------------------------
 
JVM配置参数中文说明:
-----------------------------------------------------------------------
1、-Xmixed           mixed mode execution (default)
 混合模式执行
 
2、-Xint             interpreted mode execution only
 解释模式执行
 
3、-Xbootclasspath:<directories and zip/jar files separated by ;>
      set search path for bootstrap classes and resources
 设置zip/jar资源或者类(.class文件)存放目录路径
 
3、-Xbootclasspath/a:<directories and zip/jar files separated by ;>
      append to end of bootstrap class path
 追加zip/jar资源或者类(.class文件)存放目录路径
 
4、-Xbootclasspath/p:<directories and zip/jar files separated by ;>
      prepend in front of bootstrap class path
 预先加载zip/jar资源或者类(.class文件)存放目录路径
 
5、-Xnoclassgc       disable class garbage collection
 关闭类垃圾回收功能
 
6、-Xincgc           enable incremental garbage collection
 开启类的垃圾回收功能
 
7、-Xloggc:<file>    log GC status to a file with time stamps
 记录垃圾回日志到一个文件。
 
8、-Xbatch           disable background compilation
 关闭后台编译
 
9、-Xms<size>        set initial Java heap size
 设置JVM初始化堆内存大小
 
10、-Xmx<size>        set maximum Java heap size
 设置JVM最大的堆内存大小
 
11、-Xss<size>        set java thread stack size
 设置JVM栈内存大小
 
12、-Xprof            output cpu profiling data
 输入CPU概要表数据
 
13、-Xfuture          enable strictest checks, anticipating future default
 执行严格的代码检查,预测可能出现的情况
 
14、-Xrs              reduce use of OS signals by Java/VM (see documentation)
 通过JVM还原操作系统信号
 
15、-Xcheck:jni       perform. additional checks for JNI functions
 对JNI函数执行检查
 
16、-Xshare:off       do not attempt to use shared class data
 尽可能不去使用共享类的数据
 
17、-Xshare:auto      use shared class data if possible (default)
 尽可能的使用共享类的数据
 
18、-Xshare:on       require using shared class data, otherwise fail.
 尽可能的使用共享类的数据,否则运行失败
 
The -X options are non-standard and subject to change without notice.
-----------------------------------------------------------------------
 
怎么用这这些参数呢?其实所有的命令行都是这么一用,下面我就给出一个最简单的HelloWorl的例子来演示这个参数的用法,非常的简单。
 
HelloWorld.java
-----------------------------------------------
public class  HelloWorld
{
 public static void main(String[] args)
 {
  System.out.println("Hello World!");
 }
}
 
编译并运行:
D:\j2sdk15\bin>javac HelloWorld.java
 
D:\j2sdk15\bin>java -Xms256M -Xmx512M HelloWorld
Hello World!
 
呵呵,这下满足了吧!
 
实践:在大型系统或者应用中配置JVM参数
比如你配置IDE工具的参数,常见的有IDEA、Eclipse,这个是在一个配置文件中指定即可。
如果你要在J2EE环境中配置这些参数,那么你需要在J2EE应用服务器或者Servlet容器相关启动参数设置处指定,其启动文件中来配置,Tomcat是在catalina.bat中配置,weblogic和websphere是在其他地方,具体我就说了,相信玩过的这些大型服务器的人都知道,没玩过的看看这篇文章,玩玩就知道了,呵呵。
另外常常有人问到jdk的一些相关命令用法,其实,当你看到这里的时候,你应该知道如何获取这些命令的用法了。如果你还不会,那么,建议你去学学DOS,我是没辙了。如果你会这些,还是没有看明白,那么你赶紧学学英语吧,这样你就能看懂了。

--
言念君子 温其如玉

2012年2月4日星期六

过去一年里最离奇的7件事

金牌奖得主:

JAMESELLIOT—-作为加州长滩市的抢劫犯,当他把38左轮手枪对准被害人开枪时,枪卡弹了(子弹没有击发)!此时,他做了非常有建设性的举动:

—把眼睛对准枪口仔细瞄了一眼,同时扣下了扳机;

—这次枪没有卡弹。

银牌奖得主:

1.一个瑞士酒店的厨师被切肉机切断了一根手指,他向保险公司求偿。

保险公司怀疑他操作错误才发生意外,派了代表来检查机器。

这个代表操作切肉机之后,他也失去了一根手指,

保险公司终于核准了厨师的理赔要求。

2.一个男人在暴风雪的温哥华街头奋力铲雪一个小时,终于给自己的车清出了一个停车位。当他把车开过去时,发现一位女士已经抢了他的车位。

可以理解,他朝她开了一枪,把她干掉了。

3.一位巴士司机要负责将20名精神病患送到BULAWAYO精神病院,但是,他意然在一个非法酒吧停下车来,等喝完酒后发现:巴士上的病患全逃跑了!

他很怕由于粗心大意所酿的祸将被惩罚,于是司机把车开到了附近的公共汽车站,允诺乘客可以免费搭乘。接着,他把这些乘客送进了BULAWAYO精神病院,并告诉医院工作人员:「这些"病人"非常容易激动,胡言乱语、充满幻觉」

这个诡计,直到三天后才被识破。

铜牌奖得主:

4.一个美国少年头部被火车撞伤,伤势严重而被送进了医院。

pol.ice问他怎么受伤的?

他说,他只是想看看自己能把头伸到离行进的火车多近的地方。然后,就晕过去了。

5.一个男人走进路易斯安那州某一便利商店,他拿出20元纸钞要求找零;当职员一打开收款机时,他即亮出手枪,并要求职员把收款机里所有现金都给他。

职员照做了,他拿了钱迅速消失,但把自己的20元钞票留在柜台上,他一共拿走了15元。

(这起事件引起一场不小的法律争论:如果一个人拿枪威胁你、要给你钱,这算不算犯罪?)

6.阿肯色州一个小伙子想喝啤酒想得要命,于是他朝一酒铺的橱窗扔了一块空心砖,他原打算砸破玻璃后,抢他几瓶酒,然后逃之夭夭。

他不知道橱窗是强化玻璃,待空心砖反弹回来,当场把他砸得失去了知觉。

整个过程被录像下来。(你猜:这家伙会不会把商店告上法庭?)

7.ANNARBOR新闻报犯罪专栏报导,密西根州一名男子凌晨5点持枪走进快餐店打劫。

柜台人员表示,如果没有点餐,他无法打开收款机;这个男子只好点了一份炸洋葱圈,但柜台人员说,早餐时间不提供这玩意儿。该男子深感挫折,只好怅然离去。

年度特别大奖:

pol.ice在西雅图街道发现:有人蜷缩在一辆RV车旁,似乎身体极度不适;待上前查问,这人承认,他企图用虹吸管偷汽油,但他错误地将吸管另一头放到了旅行汽车的小型化粪桶里。 

--
言念君子 温其如玉

2011年12月27日星期二

迷失--剧评

世界只存在吾心,不在别处;所以无需怯弱,也不必沾沾自喜。
"再也不会有这样的一部剧,如此清晰地碰触到我的灵魂,让我泣不成声,让我豁然开朗,让我感动心碎,让我终于明了。 明了的,不仅仅是剧中的所谓谜题和答案;而是明了了这是一场怎样的人生。" 
注:先要告诉读者,我很感动,而且看懂了。此处用词一定要声情并茂、梨花带雨、欲罢不能。先把自己摆在一定的高度。 "——有些人会懂;有些人不会懂。 
不懂的人们,请不必着急。是因为你还没有遇到一件触碰你灵魂的事情。关于"灵性"的东西,一旦你懂了,你就全懂了。" 注:再告诉读者,你不懂是因为你条件不够,而且你一旦懂了与XX相关的东西,那么你就全懂了,所以,那些看不懂的,你们要加油了,而不是在那里废话。另注:XX可用很多词代替,比如"人性"啊,"缘分"啊,"宿命"啊,"灵魂"啊,反正越扯不清的越好。 
"LOST风靡美剧市场六年之久,其中扑朔迷离、细枝末节的无数"奇案",无数"问题",大家都期盼获得一个终极答案,于是对大结局抱有极高的期望。尽管我对结局有过种种猜测和幻想,甚至认为无论怎样结局也很难令人满意了。" 注:要勇于承认大家公认的问题。没有共同点,大家就没得谈了。 "但我万万没想到,大结局的震撼和感人远远出乎了我的意料,它不但解决了几乎全部遗留问题,给出了终极答案,而且还令我们对人生有了重新的思考:我们为什么会遇到一些人,为什么与他们发生了一些事情,为什么厮守,为什么错过,为什么有些事情有结果,而有些没有,我们的命运是注定的吗,这一切都是真实的吗?什么又是"真实"?我们来到这个世界上是有意义的吗,我们为何而来,又去往何方?…… " 注:注意段落结构,先强调剧本的确出人意料的给出了终极答案,但在要说明答案是什么的时候,笔锋急转,开始思考更多的问题。这些问题可以包括很多,比如为什么我们会出生,为什么我们会遇到我们的父母,为什么这是真的,为什么这是真的又变成了假的,为什么假的又不是真的,为什么…… "第六季开播以来,大家都以为Juliet的行动成功了,未来改变了,大家过上了和谐安宁的幸福生活。看到结局这集,再想起那句时刻回想耳边的"what happened,happended"不禁怅然。其实所谓"平行世界",更确切地说是已逝的亡者的灵魂空间。在这里每一个人都不自觉地牵挂着自己最在乎的东西,最爱的人,曾经在前世人生里最想得到的,都在这里实现。——那不是因为未来改变了,飞机从未坠毁;而是因为在这个人人创造的空间里,有最诚挚的梦想在闪耀。 " 注:"想到……(的时候),不禁怅然"此句万能,切记切记。
原来大家都以为未来改变了,其实,错,未来从未改变,这只是大家的集体幻觉。 "这就是为什么Jack有个"幻想出来"的儿子,还和他修好了"父子情";John有了爱他的父亲,而不是无情夺取他一颗肾的骗子;Daniel继续他最喜欢的音乐,成了钢琴家,而不是物理学家;James化身警察而不是罪犯;Ben兼具了善良宽容的美好秉性,服侍着生病的父亲,而不是亲手毒死他;这也就是为什么Boone对Hugo说,我都在这等了你们好久了……等等等等。
 每一个人在通往天堂的路途之前,都在自我反省和救赎。在无意地"修补"他们的前世人生,弥补着他们的终身遗憾。而且,他们会被无形中安排,与生命中曾经最重要的人再次相遇,再次发生联系。就如Jack的父亲Christian对他解释的那样"这是一个大家一起创造的世界,为的是找回所有人。没有谁是独自死去的。在你生命里那些重要的人,要和你,一起走完这段旅程。" Desmond最先醒悟,所以才会在真实世界里笑意盈盈地"乖乖"让Charles带路,去做"该做"的事情。也所以才会在灵魂世界里积极地召集众人,创造他们"回忆"的机会。所以,当Jack在前些集几次发现脖子上的血迹时,我们都和他一样不明白究竟怎么回事,直到最后他抚摸父亲的棺木,他的整个人生,他那些最珍贵的片段是那样地强烈地被激发出来。 " 注:然后开始解释各种各样的事情,因为这是幻觉,所以你怎么解释都可以。 "他终于懂了。 " 注:你们懂了没,没懂?他都懂了,你还不懂?! 
"作为观众,我曾经幻想过最终答案到底是什么呢?没想到,最终答案其实就是在天堂之前的一段温馨的对话。我想,我们每一个都在冥冥中追逐着,询问着,不解着……但许许多多的问题,等你走到终点才发现,它们并没有什么意义。真正有意义的是我们脚下走过的这段过程,我们遇到的人,我们的情感,我们的感知,我们潜在的人性的光辉。 " 注:给出总结性的答案,就是这一切都没有意义,所以,你还要什么答案?!
我们也可以这样理解,这个电视剧本身没有意义,看了也白看,真正有意义的是我们看美剧的过程。 "在教堂里的对话是这样的: Jack问,爸爸,我不明白,既然你死了,你怎么能在这? 父亲说,那你是怎么在这的? Jack顿悟,痛苦地说,我也死了。。 父亲抱住痛苦的儿子说,没事的孩子,没事的,没事的。我爱你,孩子。 Jack:我也爱你。爸爸,你……你是真实的吗? 父亲笑了:是啊,我是真实的,你也是真实的。所有在你生命发生的事情,每一件事情,都是真实的。所有你生命中遇到的人,都是真实的。 Jack:那我们为什么都聚到这里? 父亲:孩子,其实没有一个人是独自死去的。这是一个我们一起创造的世界,为的是寻找到彼此。 Jack:那又是为的什么? 父亲:为了铭记你的这一生,为了放下它,然后前行。 Jack:前行,去哪里呢? 父亲微笑着:来,我们一起去看看吧。 " 注:此处附上原文。 
"影片把两个世界的场景不断切换,回到真实世界里,看到Jack临死前,回到了故事发生的起点,看着最终得救的寥寥几人乘着飞机离开,他终于释然地闭上眼睛。回想第一季第一集,Jack在这里躺下,惊恐地睁开眼睛。这一段路途,可谓圆满。 如果Jack还是穿着第一集的西服死去,那一切就成了幻想,编剧就小儿科了;妙就秒在这不同,Jack已经不是原来那个Jack了,这里发生的一切全是真实的。他真的经历过的,如今,他也真的拯救了小岛,牺牲了自己。" 注:此处再告诉大家,这其实不是梦境,一切都是真实的。
读者: 呔!怎么前后矛盾? 靠,不矛盾你不就看懂了么,你看懂了,我还怎么装? "再追究那山洞里的"小岛心脏"究竟是什么科学原理,我认为已经意义不大。因为到了最终结局,你会发现,那其实已经不是你所关心的问题了。这听上去似乎有些讽刺,但是仔细想想,这不也就类似人生吗?那些我们奋力追逐,拼命要弄个所以然的"问题",等到结束的那天才发现:都是一场空。 " 注:这里告诉读者,你经过以上忽悠,你的问题已经意义不大了,既然意义不大,你还找什么答案呢? "剧中的主角和我们每一个凡人一样,有过梦想,有过挫败,有种种的遗憾和不完整,也都有死亡。我们以为许多事情都是随机发生的,但其实上帝既有他详细的预先安排,也允许每一个人的自由选择意识。LOST是带有宗教色彩的故事,更确切的理解,我认为是更为接近灵魂的东西。 " 注:继续上岗上线,凡事没有逻辑的,都扯上宗教,凡事搞不清楚的,都扯上灵魂。你能解释上帝是什么吗,你知道灵魂是什么吗? 
"这些人们,一上岛就追寻答案:这是哪啊?谁是Jacob啊,从来没见过。数字按钮就是吓唬人的吧,为什么非要按它?我们是被陷害到岛上的吧?…… 我们也曾经和主角们一样,恨不得一把抓住Jacob问无数问题,问个究竟,砸破砂锅问到底。但是有时候,那答案在你还不能理解的时候,你懂得了吗? Jacob就好似上帝的一个喻体,默默地从小关注每一个人,关注他们的成长,关注他们的喜怒哀愁。他不是不能给你答案,而是那些并不是重要的东西,也不是你能力所及可以改变的。 " 注:Jacob说:你们不要问我这么多问题,其实我也是打酱油的。 "现在看是悲剧,或许是好事;现在觉得完全不可能发生的,或许都会在将来发生。但是这是一个集体行动,每个人都不是孤独前行,或许冥冥中一切天定,或许冥冥中改变历史。我们影响自己的同时,也在影响着周围,影响着每一个和我们和联系的人。就好比Jacob选候选人一样,圣经里也谈到,上帝之所以给人赋予生命都是有目的,上帝有一个很宏大的计划,这涉及到每一个人。 那么,这一切的一切的终点在哪里呢? 我想,只有此生结束时,才会知晓了。 " 注:最后一句话亮点,也就是说,等到死,你才知道答案。那你还想知道答案吗? 
"我非常喜欢,尊敬,和赞叹这个LOST大结局,因为这是一个看似开放的不开放结局。它只给了一个答案。但是无数的人可以给出无数的理解,每一个人的角度不同,看到的世界就完全不同。 亦如,这场人生。" 注:说明其实是有答案的,而且理解不同答案不一样,那么你知道答案了吗?
--
言念君子 温其如玉

2011年12月22日星期四

Java初始化顺序

初始化(initialization)其实包含两部分:
1.类的初始化(initialization class & interface)
2.对象的创建(creation of new class instances)。
因为类的初始化其实是类加载(loading of classes)的最后一步,所以很多书中把它归结为"对象的创建"的第一步。其实只是看问题的角度不同而已。为了更清楚的理解,这里还是分开来。

顺序:
应为类的加载肯定是第一步的,所以类的初始化在前。大体的初始化顺序是:
类初始化 -> 子类构造函数 -> 父类构造函数 -> 实例化成员变量 -> 继续执行子类构造函数的语句

下面结合例子,具体解释一下。
1。类的初始化(Initialization classes and interfaces),其实很简单,具体来说有:
(a)初始化类(initialization of class),是指初始化static field 和执行static初始化块。
例如:
class Super {
        static String s = "initialization static field"; //初始化static field,其中"= "initialization static field" "又叫做static field initializer

        // static初始化块,又叫做static initializer,或 static initialization block
        static {
        System.out.println("This is static initializer");
}
}
btw,有些书上提到static initializer 和 static field initializer 的概念,与之对应的还有 instance initializer 和 instance variable initializer。例子中的注释已经解释了其含义。

(b)初始化接口(initialization of interface),是指初始化定义在该interface中的field。

*注意*
--initialization classes 时,该class的superclass 将首先被初始化,但其实现的interface则不会。
--initialization classes 时,该class的superclass,以及superlcass的superclass 会首先被递归地初始化,一直到java.lang.Object为止。但initialiazation interface的时候,却不需如此,只会初始化该interface本身。
--对于由引用类变量(class field)所引发的初始化,只会初始化真正定义该field的class。
--如果一个static field是编译时常量(compile-time constant),则对它的引用不会引起定义它的类的初始化。

为了帮助理解最后两点,请试试看下面的例子:
public class Initialization {
        
        public static void main(String[] args) {
                
                System.out.println(Sub.x); // Won't cause initialization of Sub, because x is declared by S, not Sub.
                                         // 不会引起Sub类的初始化,因为x是定义在Super类中的
                System.out.println("-------------------------");
                System.out.println(Sub.y); // Won't cause initialization of Sub, because y is constant.
                                         // 不会引起Sub类的初始化,因为y是常量
                System.out.println("-------------------------");
                System.out.println(Sub.z = 2004); // Will cause initialization of Sub class
// 将会引起Sub的初始化
        }
}

class Super{
        static int x = 2006;
}

class Sub extends Super {
        
        static final int y = 2005;
        
static int z;
        
static {
                System.out.println("Initialization Sub");
        }
}

2。对象的创建(creation of new class instances),稍微有点烦琐,具体的步骤如下
(a) 所有的成员变量—包括该类,及它的父类中的成员变量--被分配内存空间,并赋予默认值。(Btw,这里是第一次初始化成员变量)
(b) 为所调用的构造函数初始化其参数变量。(如果有参数)
(c) 如果在构造函数中用this 调用了同类中的其他构造函数,则按照步骤(b)~(f)去处理被调用到的构造函数。
(d) 如果在构造函数中用super调用了其父类的构造函数,则按照步骤(b)~(f)去处理被调用到的父类构造函数。
(e) 按照书写顺序,执行instance initializer 和 instance variable initializer来初始化成员变量。(Btw,这里是第二次初始化成员变量)
(f) 按照书写顺序,执行constructor的其余部分。

*注意*
成员变量其实都被初始化2次,第一次是赋予默认值,第二次才是你想要设定的值。

最后看一个例子:
public class InitializationOrder {

        public static void main(String[] args) {
                Subclass sb = new Subclass();
        }
}

class Super{
        
        static {
                System.out.println(1);
        }
        
        Super(int i){
                System.out.println(i);
        }
}

class Subclass extends Super implements Interface{
        
        static {
                System.out.println(2);
        }        
        
        Super su = new Super(4);
        
        Subclass() {
                super(3);
                new Super(5);
        }
}

interface Interface{
        static Super su = new Super(0);
}

稍微解释一下:
首先,Java虚拟机要执行InitializationOrder类中的static 方法main(),这引起了类的初始化。开始初始化InitializationOrder类。具体的步骤略去不说。
接着,InitializationOrder类初始化完毕后,开始执行main()方法。语句Subclass sb = new Subclass()将创建一个Subclass对象。加载类Subclass后对其进行类初始化,但因为Subclass有一个父类Super,所以先初始化Super类,初始化块static {System.out.println(1);}被执行,打印输出1;
第三,Super初始化完毕后,开始初始化Subclass类。static {System.out.println(2);}被执行,打印输出2;
第四,至此,类的加载工作全部完成。开始进入创建Subclass的对象过程。先为Subclass类和其父类Super类分配内存空间,这时Super su 被附值为null;
第五,执行构造函数Subclass()时,super(3)被执行。如前面(d)所说,Super类的构造函数Super(int i){….}被调用,并按照步骤(b)~(f)来处理。因此,递归调用Super类的父类Object类的构造函数,并按照步骤(b)~(f)来初始化Object类,不过没有任何输入结果。最后打印输出3;
第六,如前面(e)所说,初始化成员变量su,其结果是打印输出4;
第七,如前面(f)所说,执行new Super(5),并打印输出5;
最后,Subclass虽然实现了接口Interface,但是初始化它的时候并不会引起接口的初始化,所以接口Interface中的static Super su = new Super(0)自始至终都没有被执行到。

① 类属性 (静态变量) 定义时的初始化,如上例的 static String a = "string-a";
    ② static 块中的初始化代码,如上例 static {} 中的 b = "string-b";
    ③ 对象属性 (非静态变量) 定义时的初始化,如上例的 String c = "stirng-c";
    ④ 构造方法 (函数) 中的初始化代码,如上例构造方法中的 d = "string-d";


--
言念君子 温其如玉

2011年12月20日星期二

GPL VS LGPL

GPL 是 GNU General Public License (GNU 通用公共许可证)的缩写形式;LGPL 是 GNU Lesser General Public License (GNU 宽通用公共许可证)的缩写形式,旧称 GNU Library General Public License (GNU 库通用公共许可证);GFDL 是 GNU Free Documentation License (GNU 自由文档许可证)的缩写形式。它们是自由软件(Free Software)的通用版权认证协议,由自由软件基金会(FSF)制定和发布。

  • 基于 GPL 的软件允许商业化销售,但不允许封闭源代码。
  • 如果您对遵循 GPL 的软件进行任何改动和/或再次开发并予以发布,则您的产品必须继承 GPL 协议,不允许封闭源代码。
  • 基于 LGPL 的软件也允许商业化销售,但不允许封闭源代码。
  • 如果您对遵循 LGPL 的软件进行任何改动和/或再次开发并予以发布,则您的产品必须继承 LGPL 协议,不允许封闭源代码。但是如果您的程序对遵循 LGPL 的软件进行任何连接、调用而不是包含,则允许封闭源代码。

GPL(General Public License)和LGPL( Lesser General Public License)是GNU的两种License。越来越多的自由软件(Free Software)使用GPL作为其授权声明,如果对GPL一点都不了解,有可能在使用自由软件时违反了GPL的授权。如果是个人或不正规的公司倒也无所 谓,但如果是有规模的公司,恐怕会有被起诉的风险。

在使用Log4cpp时我想到了授权的事情,于是有了兴趣对GPL做一下了解。这是必要的,因为公司也维护了一个验证过的自由软件库,里面包含的自由软件除了功能上是可靠的,另外就是一定可以被私有使用的,否则后果很严重(黎叔很生气!)。

Log4cpp最初的版本使用GPL作为授权声明的,在0.2.1版本以后改用更为宽松的 LGPL。LGPL最初是Library GPL的缩写,后来改称作Lesser GPL,即为更宽松的GPL。当一个自由软件使用GPL声明时,该软件的使用者有权重新发布、修改该软件,并得到该软件的源代码;但只要使用者在其程序中 使用了该自由软件,或者是使用修改后的软件,那么使用者的程序也必须公布其源代码,同时允许别人发布、修改。也就是说,使用GPL声明下的的自由软件开发 出来的新软件也一定是自由软件。

LGPL是GPL的变种,也是GNU为了得到更多的甚至是商用软件开发商的支持而提出的。与 GPL的最大不同是,可以私有使用LGPL授权的自由软件,开发出来的新软件可以是私有的而不需要是自由软件。所以任何公司在使用自由软件之前应该保证在 LGPL或其它GPL变种的授权下。

以下是Richard Stallman关于GPL和LGPL的论述

为什么你不应该使用LGPL发布你的下一个库

GNU计划在使用库时有两个首要的许可证.一个是GNU LGPL(库GPL);另一个是普通的GNU GPL.选择不同的许可有很大的不同:选择LGPL允许在私有程序中使用该库;选择普通的GPL则只允许在自由软件中使用它.

关于哪一种许可证对指定的库是最好的这一问题实际上是一个策略问题,它取决于实际情况.当前,大多数的GNU库被采用LGPL,这意味着我们只使用着其中的一个策略,而忽略了另一个. 所以现在我们在寻求更多以普通的GPL许可证形式发布的库.

私有软件开发者有金钱上的优势;自由软件开发者需要相互之间利用各自的优势.对一个库采用普通的GPL对自由软件开发者的优势要大于对私有软件开发者: 他们可以使用的库对于私有软件开发者是不可利用的.

使用普通的GPL并不是对于所有的库都有好处.在某些情况下更有理由来使用LGPL.最常见的情况就是当一个自由库的特性可以很容易地被私有软件以其他可替代库来实现.在这种情况下,库不能给与自由软件任何特别的优势,因而最好还是为LGPL发布该库.

这也就是为什么我们为GNU C 库选择LGPL.总之,有很多的其他C库;我们使用GPL发布该库,将迫使私有软件开发者不得不使用其它的库--对他们来说这不成问题,而我们则有了麻烦.

然而,当一个库所提供的功能是非常独特的时候,如GNU Readline, 情况就大不一样了. Readline库可实现输入编辑和记录交互式程序操作,这在别处通常是不可多得. 在GPL下发布它并限制它只能在自由程序中使用, 这我们的社团是一个重要的促进.至少今天某个应用程序之所以是自由软件,只是因为它必需要用到Readline.

如果我们收集一些强大的、私有软件中没有相类似东西的、采用GPL的库,它们将提供一系列有用 的模块用于新的自由软件的构造. 这对于将来的自由软件开发将是一个显著的优势, 一些项目将为了使用这些库而考虑使软件自由化. 大学的项目是易于被影响的;而且今天,随着某些公司开始考虑使软件自由化, 甚至一些商业项目也会由此受到影响.

私有软件开发者试图否认自由竞争的重要性, 他们会拼命说服作者不要将库使用GPL来发布. 例如,他们会呼吁利己主义,信誓旦旦地说如果我们让他们在私有软件产品中使用代码,将有"更多的用户"用到该库. 流行是一种诱惑,它使一个库开发者倾向于相信这种观点:社会首先需要的是促进一种库的流行;.

但是我们不应该听从这种诱惑,因为如果我们联合起来,我们可以做得更好.我们这些自由软件开发 者应该相互支持. 通过发布只能为自由软件使用的库,我们可以互相帮助,使彼此的自由软件包优于其它的私有替代品. 整个自由软件运动将会有更多的机会,因为自由软件作为一个整体将会在竞争中表现更佳.

因为"LGPL(Library GPL)"的称呼传达了关于这一问题的错误观点,我们计划将称呼改为"次级GPL(Lesser GPL)".事实上要更换名称要花一定的时间,但你不必再等--你可以现在就发布应用GPL许可证的库.


-- 
言念君子 温其如玉

--
言念君子 温其如玉

2011年12月16日星期五

面包配方

面包机食谱
—、带盖白土司带盖白土司(750g)
配料:高筋面粉1/2杯(385g)温开水1杯(235g)盐3/4小勺(3g)糖1/2大勺(20g)脱脂奶粉2大勺(14.5g)白油1/2大勺(17.5g)酵母3/4小勺(2.1g)做法: 1.按菜单键选择全麦档,选择重量750g,选择烧色(可根据您的不同喜好选择) 2.按下开始键,开始自动搅拌、发酵、烘烤 3.约3个半小时左右,面包机发出"嘀嘀嘀"的提示音,说明烘烤结束 4.最后切片完工
面包机的方子:一、普通咸味面包配料 750克 1000克面包 水 1杯+1大勺 1+2/5杯 植物油 3大勺 4大勺盐1+1/2小勺 2小勺 糖 2大勺 2大勺 面包粉 3+1/4杯 4+1/3杯 干酵母 1+5/6小勺 2小勺添加顺序应与配料表中一致,建议使用精致菜油或调和油适用程序:普通面包
二、全麦面包
配料 750克 水 1杯+3大勺全麦粉 1/4杯植物油 2大勺 盐 1小勺 红糖 4大勺奶粉 3大勺 面包粉 3杯干酵母 1+1/2小勺适用程序:全麦面包
三、快速全麦面包
配料 750克 水 1杯+3大勺全麦粉 1/4杯植物油 2大勺 盐 1小勺 红糖 4大勺奶粉 3大勺 面包粉 3杯干酵母 1+1/2小勺适用程序:快速面包
四、健康面包
配料 750克 水 1杯+3大勺全麦粉 2/3杯植物油 2大勺 盐 1小勺 红糖 1大勺面包粉 2+1/2杯 干酵母 1+1/2小勺适用程序:全麦面包
五、玉米面包
配料 750克 玉米面包预拌浓缩粉 1+2/5杯 面包粉 7/8杯 干酵母 2小勺玉米面包预拌浓缩粉为丹麦KMC公司生产的预拌面粉,适用程序:法式面包
六、菠菜包
配料 750克 鸡蛋 1个 盐 1小勺糖 1/4杯奶粉 2大勺 面包粉 1+1/4杯 干酵母 1+1/2小勺 黄油 3大勺面粉改良剂 2小勺菠菜糊 2/3杯菠菜湖制作方法:取250克洗净、空干水份的嫩菠菜,切成厚约2厘米的段,用搅拌机打成细腻糊状,用量杯称取2/3杯。与其他配料一起放入面包桶,其他操作方法一样。适用程序:普通面包
七、咖啡包
配料 750克 水加鸡蛋(一个) 2/3杯 蜂蜜 1大勺 奶粉 1/2杯盐 3/4小勺 糖 1/3杯 面包粉 2+2/3杯 干酵母 2小勺 黄油 2+1/2大勺速溶咖啡粉 1/5杯适用程序:甜面包
八、提子面包
配料 750克 水加鸡蛋(一个) 2/3杯 蜂蜜 1大勺 奶粉 1/2杯盐 3/4小勺 糖 1/3杯 面包粉 2+2/3杯 干酵母 2小勺 香蕉 1/3根 黄油 2+1/2大勺 葡萄干 1/3杯再面粉搅拌过程中蜂鸣器响时将葡萄干加入到料桶中适用程序:甜面包
九、香蕉面包
配料 750克 水加鸡蛋(一个) 2/3杯 蜂蜜 1大勺 奶粉 1/2杯盐 3/4小勺 糖 1/3杯 面包粉 2+2/3杯 干酵母 2小勺 香蕉 1/3根 黄油 2+1/2大勺香蕉切片后,将所有原料加完以后加到料桶中,然后选择"甜面包"程序,开始操作。最好在开始和面10分钟后加入黄油,味道更佳。
十、波萝吐司
配料 750克 水加鸡蛋(一个) 2/3杯 蜂蜜 1大勺 奶粉 1/2杯盐 3/4小勺 糖 1/3杯 面包粉 2+2/3杯 干酵母 2小勺 黄油 2+1/2大勺波萝糊 1/5杯波萝切片后,放入搅拌机中打成糊状。适合程序:甜面包。
十一、椰丝吐司
配料 750克 水加鸡蛋(一个) 2/3杯 蜂蜜 1大勺 奶粉 1/2杯盐 3/4小勺 糖 1/3杯 面包粉 2+2/3杯 干酵母 2小勺 黄油 2+1/2大勺椰丝 1/5杯适合程序:甜面包。
十二、杂粮健康面包
配料 750克 水 1杯全麦面包预拌粉(KMC) 7/8杯 面包粉 1+2/5杯 蜂蜜 2大勺 盐 1+1/2小勺 干酵母 2小勺 适合程序:全麦面包。十三、燕麦苹果面包 配料 750克 水加鸡蛋(一个) 2/3杯蜂蜜 1大勺 奶粉 1/2杯 盐 3/4小勺糖 1/3杯 面包粉 2+2/3杯 干酵母 2小勺 苹果(糊状) 1个黄油 2+1/2大勺 麦片 1/4杯 适合程序:甜面包。
十四、香橙面包
配料 750克 橙汁加鸡蛋(一个) 2/3杯 蜂蜜 1大勺 奶粉 1/2杯盐 3/4小勺 糖 1/3杯 面包粉 2+2/3杯 干酵母 2小勺 香蕉 1/3根 黄油 2+1/2大勺适合程序:甜面包。
十五、高蛋白面包
配料 750克水加鸡蛋清(约1/4杯) 2/3杯 蜂蜜 1大勺奶粉 1/2杯 盐 3/4小勺糖 1/3杯 面包粉 2+2/3杯 干酵母 2小勺黄油 2+1/2大勺 适合程序:甜面包。

--
言念君子 温其如玉

2011年12月2日星期五

[转贴]左小雨:2012,真正的崩溃在滞胀中开场

    一直有朋友来问我,将来会怎么样?我这几年来一直是这么安慰他们说:没事的,没事的。2010年,我告诉大家没事的。2011年,我还是告诉大家没事的。很多人在论坛上说,中国要崩溃了,人民币要变成冥币,一文不值。对这些话,我都是当作笑话,笑笑而过。

  中国这个社会哦,现在很浮躁,不但希望发财的人很浮躁,希望崩溃的人也很浮躁。希望崩溃的那些人他们现在就希望中国一下子完蛋了,然后房价一下子掉下来了,他们可以结婚了。那么今天我就要对这些朋友说,你们想得实在是太好了。如果事情真的是这样发展,出现了美国1929年那样的大萧条的话,那就不叫崩溃了。你们想一想吧,1929年底美国股市崩盘,1945年美国打败法西斯联盟,成为世界上最强大的国家。这当中只有16年的时间。16年就发展成为世界第一强权,如果这样的"崩溃"落到中国的头上,那大概大家晚上做梦都要笑醒了。
  
  所以在这里我很郑重地告诉各位朋友,中国绝对不可能出现苏联式的崩溃,也绝对不可能出现美国式的崩溃,中国的崩溃将是有中国特色的真正的崩溃。
  
  首先,为什么中国不可能出现苏联式的崩溃?大家都知道,苏联崩溃源于轻工业的产能不足,物资紧张,人民生活极其困顿。我还记得苏联崩溃的时候我父亲去莫斯科,带去的牛仔裤子,小电池什么的都成为当地人抢购的紧俏物资。马路上老太太摆个小摊卖东西,卖什么呢?四根胡萝卜。各处的商店空空如也,除了一些刚刚从中国倒卖来的日用品,其他几乎什么都没有。当时苏联就是这么一副惨象。那今天中国会出现这种情况么?不可能,今天你要牛仔裤?你说好了,十万条还是二十万条?随便要。中国现在是什么情况?是严重的产能过剩。根本不是没有东西,而是东西生产出来卖不出去。所以苏联式的崩溃,也就是人民币变冥币,是不可能在中国出现的。
  
  那么美国式的呢?29年大萧条源于贫富差距过大和产能过剩,这和今天中国的情况极其相似,那为什么中国不会出现美国式的崩溃呢?大家要知道,不管是29年的崩溃,80年代的崩盘,还是最近的次贷,都伴随着一个现象出现,那就是大量银行的破产。银行的破产阻碍了金融系统的运作,让整个社会陷入突然性的失血,大量企业纷纷破产。然后人们才看到大崩溃。那么现在大家看看,这在中国可能吗?中国的大银行都是国有的,是国家信用支持的产物。中国的银行破产,几乎等同于国家信用的破产,而国家信用的破产,直接威胁到执政党的地位,这在中国是绝对不会被允许的。因此,中国也不可能出现美国式的大崩溃。
  
  那么中国会是什么样的情况呢?我大概概括一下,那就是小规模危机持续不断,整个社会不断失血,以相当慢的速度,逐渐进入僵尸社会,最后进入真正的崩溃。
  
  以温州为例,温州大家都知道,最近很多老板跳楼跑路。这是什么?这是美国式的崩溃,地下银行崩盘,流动性消失,企业倒闭,老板跳楼,这是经典的1929式的崩溃。但是这个时候哦,你忽然发现,有中国特色的救星到了。跑路的老板,被请回来处理问题。怎么处理啊?他是因为做不下去没有办法了才跑路了,到国外呆了一周就能脱胎换骨解决问题呢?不可能。无非就是国家支持,给你一笔钱,让你把问题解决掉,把厂子维持下去。那么大家想,这些企业,它们是因为经营不下去了倒闭的,你给了它们钱,维持自然可以了,可是利润呢?没有利润,或者利润不足以支付正常的利息的话,那么这些企业就开始变成吸血的僵尸。它们需要你源源不断,小规模地给于支持,同时却不能提供任何值得一提的回报。而更可怕的是,随着国际和国内环境的变化,这些企业只能越来越糟糕。那么民营企业变成僵尸,国企呢?最近的报道说,铁道部获得了2000亿。铁道部自从上次出轨事件后,所有的工程陷入停顿。欠了多少钱呢?据说有两万亿之多。利润呢?1-2亿。大家可以想像,1-2亿的利润如何去支撑2万亿的利息?那给它2000亿能不能解决其债务问题?显然杯水车薪,根本不可能起作用。那这2000亿是干啥的呢?维持。还是维持。
  
  那么这家给2亿,那家给2000亿,这七七八八的维持金到底是从什么地方来的呢?很显然,要么从税收来,要么印。那么税收的情况怎么样呢?从四月份开始,中国各地方政府的城投债,纷纷违约,地方债务的二级市场,几乎死掉。地方政府根本拿不出钱来支付其债务的利息。更不要说,拿出钱来支持那些僵尸企业了。上海等地,已经开始借新债,还旧债。中央政府还算好点,但是要救助这些僵尸企业,那也是心有余而力不足。那么接下来就只有一条路,那就是印。印的结果是什么?大家都知道,那就是通胀。中国的通胀高不高?你们每一个人身受之,比我清楚。所以为什么前面说了半天,都是说维持两个字?那就是因为当局也知道,不能多印啊,多印了这个物价涨起来要死人的。所以今天印一个亿,明天再印一个亿,就只能这么慢慢熬。中国经济就陷入了一放松就通胀,一收缩就破产的死循环,这就是滞胀。
  
  最后会出现什么呢?两个现象,第一个是加税。前几天看谢国忠的谈话。谢先生是我一直很钦佩的一个学者,分析问题非常切中要点,但是这老兄大概是因为美国绿卡没拿到,对中国总是抱着一些不切实际的幻想。他说中国要减税。这话一说出来,恐怕礼堂里要哄堂大笑了。地方政府已经穷到连利息都支付不了了,你还要减税?请问你减税的基础何在?你减税了你的军队还要不要养?稳定还要不要维持?公务员还要不要加薪?地方公债还要不要还?中国绝对不可能减税,即便有,也是名义上的,这里刚减了营业税,那里马上房产税又搞起来了。不管是什么税种,其意义只有一个,那就是从家庭消费部门,和企业生产部门抽钱。钱都叫你们抽去了,那消费还能起来么?内需还能发展么?你们能够干的,不过就是拆东墙补西墙,寅吃卯粮而已。
  
  因为是不断地维持,所以中国不会立马崩溃,但是会缓慢地慢慢地下滑。这个过程会很长,估计超过十年。然后十年以后你们会看到什么?那就是民营经济基本全部死亡。都是一个个靠国家支持的僵尸企业,而生产能力也会逐渐下滑。大量产能闲置,十年后,生锈的生锈,发霉的发霉。这个时候可能会出现一定程度的消费物资短缺。然后还会出现什么?那就是严重的人口老龄化。也就是说,中国改革的时间窗口只有十年,十年之后,天皇老子也救不了,但是这最宝贵的十年,注定会在全社会的稳定中逐渐地失去。所以这才是最严重的危机,这才是真正的崩溃。

--
言念君子 温其如玉

2011年11月23日星期三

亲门耳番

1.今天是____年__月__日,农历_______,距离______还有_____天。今天节目的主要内容有:_____、_____、______。下面请听详细内容。

2.____在钓鱼台国宾馆亲切会见了____,双方进行了亲切友好的会谈。____高度赞赏了____,并对____一贯坚持"一个中国"的原则表示感谢。

3.____出访____,会见了____,_______高度赞扬中__两国关系,对____表示欢迎,并强烈谴责了___国家____的做法。

4.______会议在北京隆重召开。_____致开幕词。____以____票支持、0票反对、1票弃权通过一项____决议。

5.外交部发言人____就____发表声明,对____表示遗憾,提出**,并将继续关注。

6.______就____向______致慰问电,对_____表达诚挚哀悼

7."__五"期间,我国____重点工程,突破____课题,创造效益____,实现利税____。

8.______在____的陪同下,不远万里,来到_____家中,为_____带来了节日的祝福和良好的祝愿,并饶有兴致的观看了_____。____握着___的手激动的说_______。

9.____省____市____县____村加强学习"____"的重要精神,切实为农民办好事,办实事。情为民所系,利为民所谋。一年内共解决_____农民的实际问题,受到农民的好评。

10.____海关加大打击contraband力度,破获一起特大contraband案件,查获____共____件,价值人民币____万元。

11.今天是____日,各地群众、学生纷纷走上街头,宣传普及____知识,加强____教育。

12.____事丶件的原因已经查明,有关责任人已被刑事拘留。

13.今天是____诞辰____周年,____举行座谈会,深入探讨____,缅怀这位____家。

14.本台短评:中国________受到各国称赞,________行为不得民心

15.____国群众不满____,举行**示丶威活动。骚丶乱已持续_____天。

16._____国发生_______级地震,目前已造成______人伤亡。我国已派出______人救援队前往救援

17._____国发生_______特大飓风,_____人受灾,直接经济损失达_________美元。

18.____,____,请看今晚19点38分播出的《焦点访谈》节目。

19.中国__的优秀_员,久经考验的忠诚的__战士,无产阶级__家、__家、__家,我国杰出的___,原___,___,___,______**,于___年__月__日__时__分在北京逝世,享年__岁。

20.联播节目播送完了,谢谢您的收看


--
言念君子 温其如玉

2011年11月15日星期二

GWT中的list

From Evernote:

GWT中的list

对于GWT中的LIST,翻译js的时候估计有缺陷
当做for遍历时候,中间有remove操作
如:
for(PokerPanel panel : selectPanelList) {
   if(xxxxx) { 
        selectPanelList.remove(panel);
    }
}
当该list全部remove,再次遍历selectPanelList 会有空指针异常

解决办法:
for (int i = selectPanelList.size(); i > 0; i--) {
     PokerPanel panel = selectPanelList.get(i - 1);
     panel.onRevert();
     selectPanelList.remove(panel);
}

test

From Evernote:

test

测试

2011年8月25日星期四

低俗小说:人人都是老鼠会 ----李承鹏

低俗小说:人人都是老鼠会


拜登走了,可城里的交通还是堵,这让老子有些失望。前两天堵在熊猫基地时,我还发过一条微博:龟儿的欠我们那么多外债,还来扰民。后来知道,当时拜登还没落地,也没有到熊猫基地的日程安排。是咯,熊猫基地在北门,机场在南门,老子属菜鸽子的,把方向搞反了……

 

这些不管了,反正烦这个总统,总统,饭桶,就知道来吃债主。在姚记只吃了79元炸酱面,都明白是想用亲民秀颠覆我国。至于当真颠覆得了不,这个我倒也不太相信,一碗炸酱面就颠覆了国家,这国家就也太面了。红会万元工作餐都颠覆不了,炸酱面怎可能,炸弹面也不可能。何况我们还有航母。咦,这航母烧啥子油,0号还是99号?抬头一看才意识到,我正进加油站。

 

加油站小妹的脸色还是那么难看。小妹,我是来加油的,不是来揩油的。揩油的是你们中石化。小妹听出我的内心独白,粗暴地嚓地把油枪插进车体。油泵表往上直飙,20、130、240、360……内心凄怆得很,感觉不是在加油,这是在抽血。

 

抽完血连个酒精棉也不给,过去都免费送包餐巾纸,现在这个都免了。我正争辩,后面的车喇叭大作,玛莎拉蒂对我直吼:餐巾纸都要贪,卫生巾要不要。贪?有你干爹贪?老子走下去教育这90后妹妹,车上有个小伙对我冷笑,挥手就把我打出鼻血。我很痛,假装打了个喷嚏,走了。我只是不想跟暴发户一般见识,真的,每当看到玛莎拉蒂,不知为何就想起玛勒戈壁。等老子有钱了,一定买辆玛勒戈壁,不对,有钱了还呆在这里干毛线,直接移民米国,把女人、娃儿全带上。

 

至于为什么我这么恨米国,还想移民米国,这个问题一直没想明白。

 

这个夏天特别闷热,湿度大,连蝉都飞不起来了。老家的气候真是变了。当然不是建大坝造成的,这显然是米国人消耗太多能源造成的温室效应。他们把自己国内的油价定得那么低,迫使我们把油价弄得那么高,他们到处指手划脚,却欠了我们上万亿的债。这着实可恨,一时间恨不得组织菜刀队砍翻了这帮鸟人,可联想到菜刀实名制后一时准备不得这么多,最好就由外星UFO直接收了他们。昨天新闻才说上海万米上空有巨大的光柱疑似外星飞船。别留在这里,飞吧,飞到对岸去,平了他们。忽想起那个紧要问题,要是平了那里,我又怎么移民?可这辈子移民遥遥无期,还不如平了那里,感官上爽些。可平了就完全没希望……平,还是不平,一时间我脸色阴晴不定,内心纠结。

 

吱……猛踩刹车,差点撞上一横穿的老太婆。确定没撞上,我驾屁遁飞奔了。这时千万不能下车去搀扶,一扶,就得扶一辈子。

 

真是世风日下,这些老赖……让人真没安全感。其实悄悄也觉得自己很可耻。可没办法,世风不管我事,我要是管了世风,只能喝西北风。公司倒闭后,我每天跑来跑去,有天跑到一个公园累了,刚躺在长椅上准备思考一下再创业,见草坪上人越聚越多。有个大姐就附耳跟我说:放心,我们上面有人,只要发展几个下线,就可以赚钱。

 

别用老鼠会这么难听的名字好吧。老鼠会怎么了,其实我压根看不起李大眼说的"我是这个国家十三亿分之一股东",你有股权吗。你能做空还是做满。你其实连散户都算不上,最多是级别不算太低的一个下线。这个国家几千年从来就不是股份制,从来就是一老鼠会,皇上带着四辅臣的下线,四辅臣带着十六总督的下线,带着108个巡府道台的下线,带着三千二百个知县亭长的下线……总之是少数人控制着多数人的传销制,不是多数人选出少数人的民主制。只要不在大雨天碰到陈胜吴广,就其乐融融,娱乐无极到下线。到今朝的跨越式发展,就是老鼠会发展。世上最勤奋的一个老鼠会,人人都在发展下线,人人都争取成为上线。你看,从幼稚园开始的教育就是传销口号,学雷锋其实是为了忽悠更多的下线,是思想上的老鼠会。考学也是进老鼠会,你学不到什么,从中学到大学到硕博士,不过是下线到上线的一通修炼。每年招公务员,简直就是由国家统一发展下线,科长有七八个下线,局长有百十来号下线,厅长有千八百人下线……一级一级往上爬,等爬到铁道部部长那一层,全国坐火车的全是你的下线。

 

想到这一点,微风中的我忽然张嘴笑了,我不是最恐惧的,这里官员更恐惧。不说在动车追尾之前就有个部长先追了尾,就说前些时候有个教育局官员在微博上跟大波妹聊到,白,嫩,柔滑,喜欢……就下课了。我倒觉得这句从文本意义上也很有文学性,色香味俱全。可惜还是官太小,足够大就不用微博聊奶,就直接养奶。所以官员们拼命往上爬,就是想把这份恐惧感转加给下线。可是又能爬到哪一层,铁道部长还不如王勇平,王勇平还不如凤姐,这下解脱了,在世上最大的一个老鼠会,凤姐能早早清仓,这是一个奇迹。

 

思量间,车已到学校,我抹干鼻血摸出红包,悄悄塞进校长手里,我儿子要读重点中学……校长收下,面无表情远行。我看见她走进一家医院,她儿子喝牛奶后,肾里长了一些舍利子……医生面无表情收下红包。远行。然后走进一处楼盘,把首付交给开发商。开发商收下这些钱,走进一处豪包,把更多的钱交给一个领导。领导满意地点点头,转身把一把车钥匙交给身后一美妞。美妞亲一口领导后走出豪包,开着玛莎拉蒂,转角处悄悄接上一小伙。俩人开进加油站,前面是一个猥琐男,就是我,挥手打我出鼻血。我屁遁经过一老太婆,并不停下。老太婆缠上了后面的玛莎拉蒂……

 

我终于搞懂,这里是世上最勤奋一个老鼠会,人人那么拼命,是想做到最上线,清仓,移民米国。可我搞不懂的是,大家都这么努力,一定都可到了米国,抬头一看全是鼠兄鼠弟,那里又成了世上最大一个老鼠会。等公元20某0年,有个外国元首访


--
言念君子 温其如玉