软件编程
位置:首页>> 软件编程>> java编程>> Java计时器StopWatch实现方法代码实例

Java计时器StopWatch实现方法代码实例

作者:BarryW  发布时间:2021-07-25 13:43:42 

标签:Java,计时器,Stop,Watch

下面提供三种计时器的写法供大家参考,大家可以自行选择自己钟爱的使用。

写法一(Spring 包提供的计时器):


import java.text.NumberFormat;
import java.util.LinkedList;
import java.util.List;

/**
* Simple stop watch, allowing for timing of a number of tasks,
* exposing total running time and running time for each named task.
*
* <p>Conceals use of {@code System.currentTimeMillis()}, improving the
* readability of application code and reducing the likelihood of calculation errors.
*
* <p>Note that this object is not designed to be thread-safe and does not
* use synchronization.
*
* <p>This class is normally used to verify performance during proof-of-concepts
* and in development, rather than as part of production applications.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sam Brannen
* @since May 2, 2001
*/
public class StopWatch {

/**
  * Identifier of this stop watch.
  * Handy when we have output from multiple stop watches
  * and need to distinguish between them in log or console output.
  */
 private final String id;

private boolean keepTaskList = true;

private final List<TaskInfo> taskList = new LinkedList<TaskInfo>();

/** Start time of the current task */
 private long startTimeMillis;

/** Is the stop watch currently running? */
 private boolean running;

/** Name of the current task */
 private String currentTaskName;

private TaskInfo lastTaskInfo;

private int taskCount;

/** Total running time */
 private long totalTimeMillis;

/**
  * Construct a new stop watch. Does not start any task.
  */
 public StopWatch() {
   this("");
 }

/**
  * Construct a new stop watch with the given id.
  * Does not start any task.
  * @param id identifier for this stop watch.
  * Handy when we have output from multiple stop watches
  * and need to distinguish between them.
  */
 public StopWatch(String id) {
   this.id = id;
 }

/**
  * Return the id of this stop watch, as specified on construction.
  * @return the id (empty String by default)
  * @since 4.2.2
  * @see #StopWatch(String)
  */
 public String getId() {
   return this.id;
 }

/**
  * Determine whether the TaskInfo array is built over time. Set this to
  * "false" when using a StopWatch for millions of intervals, or the task
  * info structure will consume excessive memory. Default is "true".
  */
 public void setKeepTaskList(boolean keepTaskList) {
   this.keepTaskList = keepTaskList;
 }

/**
  * Start an unnamed task. The results are undefined if {@link #stop()}
  * or timing methods are called without invoking this method.
  * @see #stop()
  */
 public void start() throws IllegalStateException {
   start("");
 }

/**
  * Start a named task. The results are undefined if {@link #stop()}
  * or timing methods are called without invoking this method.
  * @param taskName the name of the task to start
  * @see #stop()
  */
 public void start(String taskName) throws IllegalStateException {
   if (this.running) {
     throw new IllegalStateException("Can't start StopWatch: it's already running");
   }
   this.running = true;
   this.currentTaskName = taskName;
   this.startTimeMillis = System.currentTimeMillis();
 }

/**
  * Stop the current task. The results are undefined if timing
  * methods are called without invoking at least one pair
  * {@code start()} / {@code stop()} methods.
  * @see #start()
  */
 public void stop() throws IllegalStateException {
   if (!this.running) {
     throw new IllegalStateException("Can't stop StopWatch: it's not running");
   }
   long lastTime = System.currentTimeMillis() - this.startTimeMillis;
   this.totalTimeMillis += lastTime;
   this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
   if (this.keepTaskList) {
     this.taskList.add(lastTaskInfo);
   }
   ++this.taskCount;
   this.running = false;
   this.currentTaskName = null;
 }

/**
  * Return whether the stop watch is currently running.
  * @see #currentTaskName()
  */
 public boolean isRunning() {
   return this.running;
 }

/**
  * Return the name of the currently running task, if any.
  * @since 4.2.2
  * @see #isRunning()
  */
 public String currentTaskName() {
   return this.currentTaskName;
 }

/**
  * Return the time taken by the last task.
  */
 public long getLastTaskTimeMillis() throws IllegalStateException {
   if (this.lastTaskInfo == null) {
     throw new IllegalStateException("No tasks run: can't get last task interval");
   }
   return this.lastTaskInfo.getTimeMillis();
 }

/**
  * Return the name of the last task.
  */
 public String getLastTaskName() throws IllegalStateException {
   if (this.lastTaskInfo == null) {
     throw new IllegalStateException("No tasks run: can't get last task name");
   }
   return this.lastTaskInfo.getTaskName();
 }

/**
  * Return the last task as a TaskInfo object.
  */
 public TaskInfo getLastTaskInfo() throws IllegalStateException {
   if (this.lastTaskInfo == null) {
     throw new IllegalStateException("No tasks run: can't get last task info");
   }
   return this.lastTaskInfo;
 }

/**
  * Return the total time in milliseconds for all tasks.
  */
 public long getTotalTimeMillis() {
   return this.totalTimeMillis;
 }

/**
  * Return the total time in seconds for all tasks.
  */
 public double getTotalTimeSeconds() {
   return this.totalTimeMillis / 1000.0;
 }

/**
  * Return the number of tasks timed.
  */
 public int getTaskCount() {
   return this.taskCount;
 }

/**
  * Return an array of the data for tasks performed.
  */
 public TaskInfo[] getTaskInfo() {
   if (!this.keepTaskList) {
     throw new UnsupportedOperationException("Task info is not being kept!");
   }
   return this.taskList.toArray(new TaskInfo[this.taskList.size()]);
 }

/**
  * Return a short description of the total running time.
  */
 public String shortSummary() {
   return "StopWatch '" + getId() + "': running time (millis) = " + getTotalTimeMillis();
 }

/**
  * Return a string with a table describing all tasks performed.
  * For custom reporting, call getTaskInfo() and use the task info directly.
  */
 public String prettyPrint() {
   StringBuilder sb = new StringBuilder(shortSummary());
   sb.append('\n');
   if (!this.keepTaskList) {
     sb.append("No task info kept");
   }
   else {
     sb.append("-----------------------------------------\n");
     sb.append("ms   %   Task name\n");
     sb.append("-----------------------------------------\n");
     NumberFormat nf = NumberFormat.getNumberInstance();
     nf.setMinimumIntegerDigits(5);
     nf.setGroupingUsed(false);
     NumberFormat pf = NumberFormat.getPercentInstance();
     pf.setMinimumIntegerDigits(3);
     pf.setGroupingUsed(false);
     for (TaskInfo task : getTaskInfo()) {
       sb.append(nf.format(task.getTimeMillis())).append(" ");
       sb.append(pf.format(task.getTimeSeconds() / getTotalTimeSeconds())).append(" ");
       sb.append(task.getTaskName()).append("\n");
     }
   }
   return sb.toString();
 }

/**
  * Return an informative string describing all tasks performed
  * For custom reporting, call {@code getTaskInfo()} and use the task info directly.
  */
 @Override
 public String toString() {
   StringBuilder sb = new StringBuilder(shortSummary());
   if (this.keepTaskList) {
     for (TaskInfo task : getTaskInfo()) {
       sb.append("; [").append(task.getTaskName()).append("] took ").append(task.getTimeMillis());
       long percent = Math.round((100.0 * task.getTimeSeconds()) / getTotalTimeSeconds());
       sb.append(" = ").append(percent).append("%");
     }
   }
   else {
     sb.append("; no task info kept");
   }
   return sb.toString();
 }

/**
  * Inner class to hold data about one task executed within the stop watch.
  */
 public static final class TaskInfo {

private final String taskName;

private final long timeMillis;

TaskInfo(String taskName, long timeMillis) {
     this.taskName = taskName;
     this.timeMillis = timeMillis;
   }

/**
    * Return the name of this task.
    */
   public String getTaskName() {
     return this.taskName;
   }

/**
    * Return the time in milliseconds this task took.
    */
   public long getTimeMillis() {
     return this.timeMillis;
   }

/**
    * Return the time in seconds this task took.
    */
   public double getTimeSeconds() {
     return (this.timeMillis / 1000.0);
   }
 }

}

下面写一个调用:


public static void main(String[] args) throws InterruptedException {
//    StopWatchTest.test0();
  StopWatchTest.test1();
}

public static void test1() throws InterruptedException {
  StopWatch sw = new StopWatch("test");
  sw.start("task1");
  // do something
 Thread.sleep(100);
 sw.stop();
 sw.start("task2");
 // do something
 Thread.sleep(200);
 sw.stop();
 System.out.println("sw.prettyPrint()~~~~~~~~~~~~~~~~~");
 System.out.println(sw.prettyPrint());
}

运行结果:

sw.prettyPrint()~~~~~~~~~~~~~~~~~
StopWatch 'test': running time (millis) = 308
-----------------------------------------
ms % Task name
-----------------------------------------
00104 034% task1
00204 066% task2
---------------------

start开始记录,stop停止记录,然后通过StopWatch的prettyPrint方法,可直观的输出代码执行耗时,以及执行时间百分比,瞬间感觉比之前的方式高大上了一个档次。

 除此之外,还有以下两个方法shortSummary,getTotalTimeMillis,查看程序执行时间。

写法二(apache.commons实现的计时器):


import java.util.concurrent.TimeUnit;

/**
* <p>
* <code>StopWatch</code> provides a convenient API for timings.
* </p>
*
* <p>
* To start the watch, call {@link #start()} or {@link StopWatch#createStarted()}. At this point you can:
* </p>
* <ul>
* <li>{@link #split()} the watch to get the time whilst the watch continues in the background. {@link #unsplit()} will
* remove the effect of the split. At this point, these three options are available again.</li>
* <li>{@link #suspend()} the watch to pause it. {@link #resume()} allows the watch to continue. Any time between the
* suspend and resume will not be counted in the total. At this point, these three options are available again.</li>
* <li>{@link #stop()} the watch to complete the timing session.</li>
* </ul>
*
* <p>
* It is intended that the output methods {@link #toString()} and {@link #getTime()} should only be called after stop,
* split or suspend, however a suitable result will be returned at other points.
* </p>
*
* <p>
* NOTE: As from v2.1, the methods protect against inappropriate calls. Thus you cannot now call stop before start,
* resume before suspend or unsplit before split.
* </p>
*
* <p>
* 1. split(), suspend(), or stop() cannot be invoked twice<br>
* 2. unsplit() may only be called if the watch has been split()<br>
* 3. resume() may only be called if the watch has been suspend()<br>
* 4. start() cannot be called twice without calling reset()
* </p>
*
* <p>This class is not thread-safe</p>
*
* @since 2.0
*/
public class StopWatch {

private static final long NANO_2_MILLIS = 1000000L;

/**
  * Provides a started stopwatch for convenience.
  *
  * @return StopWatch a stopwatch that's already been started.
  *
  * @since 3.5
  */
 public static StopWatch createStarted() {
   final StopWatch sw = new StopWatch();
   sw.start();
   return sw;
 }

/**
  * Enumeration type which indicates the status of stopwatch.
  */
 private enum State {

UNSTARTED {
     @Override
     boolean isStarted() {
       return false;
     }
     @Override
     boolean isStopped() {
       return true;
     }
     @Override
     boolean isSuspended() {
       return false;
     }
   },
   RUNNING {
     @Override
     boolean isStarted() {
       return true;
     }
     @Override
     boolean isStopped() {
       return false;
     }
     @Override
     boolean isSuspended() {
       return false;
     }
   },
   STOPPED {
     @Override
     boolean isStarted() {
       return false;
     }
     @Override
     boolean isStopped() {
       return true;
     }
     @Override
     boolean isSuspended() {
       return false;
     }
   },
   SUSPENDED {
     @Override
     boolean isStarted() {
       return true;
     }
     @Override
     boolean isStopped() {
       return false;
     }
     @Override
     boolean isSuspended() {
       return true;
     }
   };

/**
    * <p>
    * The method is used to find out if the StopWatch is started. A suspended
    * StopWatch is also started watch.
    * </p>

* @return boolean
    *       If the StopWatch is started.
    */
   abstract boolean isStarted();

/**
    * <p>
    * This method is used to find out whether the StopWatch is stopped. The
    * stopwatch which's not yet started and explicitly stopped stopwatch is
    * considered as stopped.
    * </p>
    *
    * @return boolean
    *       If the StopWatch is stopped.
    */
   abstract boolean isStopped();

/**
    * <p>
    * This method is used to find out whether the StopWatch is suspended.
    * </p>
    *
    * @return boolean
    *       If the StopWatch is suspended.
    */
   abstract boolean isSuspended();
 }

/**
  * Enumeration type which indicates the split status of stopwatch.
  */
 private enum SplitState {
   SPLIT,
   UNSPLIT
 }
 /**
  * The current running state of the StopWatch.
  */
 private State runningState = State.UNSTARTED;

/**
  * Whether the stopwatch has a split time recorded.
  */
 private SplitState splitState = SplitState.UNSPLIT;

/**
  * The start time.
  */
 private long startTime;

/**
  * The start time in Millis - nanoTime is only for elapsed time so we
  * need to also store the currentTimeMillis to maintain the old
  * getStartTime API.
  */
 private long startTimeMillis;

/**
  * The stop time.
  */
 private long stopTime;

/**
  * <p>
  * Constructor.
  * </p>
  */
 public StopWatch() {
   super();
 }

/**
  * <p>
  * Start the stopwatch.
  * </p>
  *
  * <p>
  * This method starts a new timing session, clearing any previous values.
  * </p>
  *
  * @throws IllegalStateException
  *       if the StopWatch is already running.
  */
 public void start() {
   if (this.runningState == State.STOPPED) {
     throw new IllegalStateException("Stopwatch must be reset before being restarted. ");
   }
   if (this.runningState != State.UNSTARTED) {
     throw new IllegalStateException("Stopwatch already started. ");
   }
   this.startTime = System.nanoTime();
   this.startTimeMillis = System.currentTimeMillis();
   this.runningState = State.RUNNING;
 }

/**
  * <p>
  * Stop the stopwatch.
  * </p>
  *
  * <p>
  * This method ends a new timing session, allowing the time to be retrieved.
  * </p>
  *
  * @throws IllegalStateException
  *       if the StopWatch is not running.
  */
 public void stop() {
   if (this.runningState != State.RUNNING && this.runningState != State.SUSPENDED) {
     throw new IllegalStateException("Stopwatch is not running. ");
   }
   if (this.runningState == State.RUNNING) {
     this.stopTime = System.nanoTime();
   }
   this.runningState = State.STOPPED;
 }

/**
  * <p>
  * Resets the stopwatch. Stops it if need be.
  * </p>
  *
  * <p>
  * This method clears the internal values to allow the object to be reused.
  * </p>
  */
 public void reset() {
   this.runningState = State.UNSTARTED;
   this.splitState = SplitState.UNSPLIT;
 }

/**
  * <p>
  * Split the time.
  * </p>
  *
  * <p>
  * This method sets the stop time of the watch to allow a time to be extracted. The start time is unaffected,
  * enabling {@link #unsplit()} to continue the timing from the original start point.
  * </p>
  *
  * @throws IllegalStateException
  *       if the StopWatch is not running.
  */
 public void split() {
   if (this.runningState != State.RUNNING) {
     throw new IllegalStateException("Stopwatch is not running. ");
   }
   this.stopTime = System.nanoTime();
   this.splitState = SplitState.SPLIT;
 }

/**
  * <p>
  * Remove a split.
  * </p>
  *
  * <p>
  * This method clears the stop time. The start time is unaffected, enabling timing from the original start point to
  * continue.
  * </p>
  *
  * @throws IllegalStateException
  *       if the StopWatch has not been split.
  */
 public void unsplit() {
   if (this.splitState != SplitState.SPLIT) {
     throw new IllegalStateException("Stopwatch has not been split. ");
   }
   this.splitState = SplitState.UNSPLIT;
 }

/**
  * <p>
  * Suspend the stopwatch for later resumption.
  * </p>
  *
  * <p>
  * This method suspends the watch until it is resumed. The watch will not include time between the suspend and
  * resume calls in the total time.
  * </p>
  *
  * @throws IllegalStateException
  *       if the StopWatch is not currently running.
  */
 public void suspend() {
   if (this.runningState != State.RUNNING) {
     throw new IllegalStateException("Stopwatch must be running to suspend. ");
   }
   this.stopTime = System.nanoTime();
   this.runningState = State.SUSPENDED;
 }

/**
  * <p>
  * Resume the stopwatch after a suspend.
  * </p>
  *
  * <p>
  * This method resumes the watch after it was suspended. The watch will not include time between the suspend and
  * resume calls in the total time.
  * </p>
  *
  * @throws IllegalStateException
  *       if the StopWatch has not been suspended.
  */
 public void resume() {
   if (this.runningState != State.SUSPENDED) {
     throw new IllegalStateException("Stopwatch must be suspended to resume. ");
   }
   this.startTime += System.nanoTime() - this.stopTime;
   this.runningState = State.RUNNING;
 }

/**
  * <p>
  * Get the time on the stopwatch.
  * </p>
  *
  * <p>
  * This is either the time between the start and the moment this method is called, or the amount of time between
  * start and stop.
  * </p>
  *
  * @return the time in milliseconds
  */
 public long getTime() {
   return getNanoTime() / NANO_2_MILLIS;
 }

/**
  * <p>
  * Get the time on the stopwatch in the specified TimeUnit.
  * </p>
  *
  * <p>
  * This is either the time between the start and the moment this method is called, or the amount of time between
  * start and stop. The resulting time will be expressed in the desired TimeUnit with any remainder rounded down.
  * For example, if the specified unit is {@code TimeUnit.HOURS} and the stopwatch time is 59 minutes, then the
  * result returned will be {@code 0}.
  * </p>
  *
  * @param timeUnit the unit of time, not null
  * @return the time in the specified TimeUnit, rounded down
  * @since 3.5
  */
 public long getTime(final TimeUnit timeUnit) {
   return timeUnit.convert(getNanoTime(), TimeUnit.NANOSECONDS);
 }

/**
  * <p>
  * Get the time on the stopwatch in nanoseconds.
  * </p>
  *
  * <p>
  * This is either the time between the start and the moment this method is called, or the amount of time between
  * start and stop.
  * </p>
  *
  * @return the time in nanoseconds
  * @since 3.0
  */
 public long getNanoTime() {
   if (this.runningState == State.STOPPED || this.runningState == State.SUSPENDED) {
     return this.stopTime - this.startTime;
   } else if (this.runningState == State.UNSTARTED) {
     return 0;
   } else if (this.runningState == State.RUNNING) {
     return System.nanoTime() - this.startTime;
   }
   throw new RuntimeException("Illegal running state has occurred.");
 }

/**
  * <p>
  * Get the split time on the stopwatch.
  * </p>
  *
  * <p>
  * This is the time between start and latest split.
  * </p>
  *
  * @return the split time in milliseconds
  *
  * @throws IllegalStateException
  *       if the StopWatch has not yet been split.
  * @since 2.1
  */
 public long getSplitTime() {
   return getSplitNanoTime() / NANO_2_MILLIS;
 }
 /**
  * <p>
  * Get the split time on the stopwatch in nanoseconds.
  * </p>
  *
  * <p>
  * This is the time between start and latest split.
  * </p>
  *
  * @return the split time in nanoseconds
  *
  * @throws IllegalStateException
  *       if the StopWatch has not yet been split.
  * @since 3.0
  */
 public long getSplitNanoTime() {
   if (this.splitState != SplitState.SPLIT) {
     throw new IllegalStateException("Stopwatch must be split to get the split time. ");
   }
   return this.stopTime - this.startTime;
 }

/**
  * Returns the time this stopwatch was started.
  *
  * @return the time this stopwatch was started
  * @throws IllegalStateException
  *       if this StopWatch has not been started
  * @since 2.4
  */
 public long getStartTime() {
   if (this.runningState == State.UNSTARTED) {
     throw new IllegalStateException("Stopwatch has not been started");
   }
   // System.nanoTime is for elapsed time
   return this.startTimeMillis;
 }

/**
  * <p>
  * Gets a summary of the time that the stopwatch recorded as a string.
  * </p>
  *
  * <p>
  * The format used is ISO 8601-like, <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.
  * </p>
  *
  * @return the time as a String
  */
 @Override
 public String toString() {
   return DurationFormatUtils.formatDurationHMS(getTime());
 }

/**
  * <p>
  * Gets a summary of the split time that the stopwatch recorded as a string.
  * </p>
  *
  * <p>
  * The format used is ISO 8601-like, <i>hours</i>:<i>minutes</i>:<i>seconds</i>.<i>milliseconds</i>.
  * </p>
  *
  * @return the split time as a String
  * @since 2.1
  */
 public String toSplitString() {
   return DurationFormatUtils.formatDurationHMS(getSplitTime());
 }

/**
  * <p>
  * The method is used to find out if the StopWatch is started. A suspended
  * StopWatch is also started watch.
  * </p>
  *
  * @return boolean
  *       If the StopWatch is started.
  * @since 3.2
  */
 public boolean isStarted() {
   return runningState.isStarted();
 }

/**
  * <p>
  * This method is used to find out whether the StopWatch is suspended.
  * </p>
  *
  * @return boolean
  *       If the StopWatch is suspended.
  * @since 3.2
  */
 public boolean isSuspended() {
   return runningState.isSuspended();
 }

/**
  * <p>
  * This method is used to find out whether the StopWatch is stopped. The
  * stopwatch which's not yet started and explicitly stopped stopwatch is
  * considered as stopped.
  * </p>
  *
  * @return boolean
  *       If the StopWatch is stopped.
  * @since 3.2
  */
 public boolean isStopped() {
   return runningState.isStopped();
 }

}

写法三(Scala函数写法):


import org.slf4j.LoggerFactory

/**
* 类功能描述:Debug日志追踪
*
* @author barry create at 18-8-29 下午3:41
* @version 1.0.0
*/
object Debug {
val LOGGER = LoggerFactory.getLogger(getClass)
val counter = collection.mutable.Map[String, Int]() // label -> count
val times = collection.mutable.Map[String, Long]() // label - time(ns)

/**
 *追踪代码块
 * @param label 标签名
 * @param codeBlock 代码块
 * @tparam T 返回结果类型
 * @return
 */
def trace[T](label: String)(codeBlock: => T) = {
 val t0 = System.nanoTime()
 val result = codeBlock
 val t1 = System.nanoTime()
 counter.get(label).map(_counter => counter.put(label, _counter + 1)).orElse(counter.put(label, 1))
 times.get(label).map(cost => times.put(label, cost + (t1 - t0))).orElse(times.put(label, t1 - t0))
 result
}

/**
 * 打印日志
 */
def info(): Unit = {
 LOGGER.warn("FinallyDone...")
 LOGGER.warn(s"counter:${counter}")
 LOGGER.warn(s"times:${times.map { case (label, cost) => (label, cost / 1000000)}}ms")
}

/**
 * 重新计数
 */
def reset(): Unit = {
 counter.clear()
 times.clear()
}
}

参考下面测试代码:

java版本:


/**
* @author WangXueXing create at 19-7-31 下午1:46
* @version 1.0.0
*/
public class StopWatchDemo {
 public static void main(String[] args){
   Debug.trace("方法1调用次数及用时", ()->{
     try {
       Thread.sleep(2000);
     } catch (InterruptedException e) {
       e.printStackTrace();
     }
     return "";
   });

Debug.trace("方法1调用次数及用时", ()->{
     try {
       Thread.sleep(2000);
     } catch (InterruptedException e) {
       e.printStackTrace();
     }
     return "";
   });

Debug.trace("方法2调用次数及用时", ()->{
     try {
       Thread.sleep(2000);
     } catch (InterruptedException e) {
       e.printStackTrace();
     }
     return 10;
   });

Debug.info();
 }
}

输出结果:

15:29:32.228 [main] WARN test.Debug$ - FinallyDone...
15:29:32.361 [main] WARN test.Debug$ - counter:Map(方法2调用次数及用时 -> 1, 方法1调用次数及用时 -> 2)
15:29:32.364 [main] WARN test.Debug$ - times:Map(方法2调用次数及用时 -> 2000, 方法1调用次数及用时 -> 4000)ms

scala版本:


/**
* @author WangXueXing create at 19-8-1 下午3:40
* @version 1.0.0
*/
object StopWatchTest {
def main(args: Array[String]): Unit = {
 Debug.trace("方法1调用次数及用时")( Thread.sleep(200))
 Debug.trace("方法1调用次数及用时")( Thread.sleep(200))
 Debug.trace("方法2调用次数及用时")( Thread.sleep(200))
 Debug.info()
}
}

输出结果:

15:43:58.601 [main] WARN test.stopwatch.Debug$ - FinallyDone...
15:43:58.735 [main] WARN test.stopwatch.Debug$ - counter:Map(方法2调用次数及用时 -> 1, 方法1调用次数及用时 -> 2)
15:43:58.739 [main] WARN test.stopwatch.Debug$ - times:Map(方法2调用次数及用时 -> 200, 方法1调用次数及用时 -> 400)ms

对比java版本与scala版本,是不是看到scala的简洁及强大了吧!

来源:https://www.cnblogs.com/barrywxx/p/11282215.html

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com