那些繞不過去的 Java 知識點(一)

關(guān)于本文

雖然接觸 Java 已經(jīng) 8 年之久蜓肆,可惜學(xué)習(xí)之初的筆記文檔沒能很好地保存下來坦仍。本文是近幾年工作學(xué)習(xí)中遇到的一些零散的知識點,包括了 基礎(chǔ)概念蔚叨、實用的編程技巧、代碼可讀性辙培、設(shè)計模式蔑水、性能優(yōu)化(工具&編碼)、測試相關(guān)扬蕊、JVM 相關(guān)搀别、常用的工具和常見問題。本著好記性不如爛筆頭的初衷尾抑,在不斷地踩坑和爬坑的過程中歇父,慢慢地記錄成文。期待著本文能起到拋磚引玉的作用蛮穿,以看到大家的真知灼見。

基礎(chǔ)知識

注解

GuardedBy

@GuardedBy 注解可以作用于某一個屬性或者方法毁渗,約定在訪問這些被注解標(biāo)記的資源時践磅,能被同步代碼塊保護(hù)著。簡單的使用案例如下:

@GuardedBy("obj")
private ConcurrentMap<String, String> map = new ConcurrentHashMap<>();
private final Object obj = new Object();

public void put(String k, String v) {
    synchronized (obj) {
        map.put(k, v);
    }
}

/**
 * If you use `error prone` tool to check this, this annotation should be `@SuppressWarnings("GuardedBy")`
 * {@see https://errorprone.info/bugpattern/GuardedBy}
 * {@see https://github.com/apache/incubator-druid/pull/6868#discussion_r249639199}
 */
@SuppressWarnings("FieldAccessNotGuarded")
public void remove(String k) {
    map.remove(k);
}

@Override
public String toString() {
    synchronized (obj) {
        return "GuardedByExample{" +
                "map=" + map +
                '}';
    }
}

Tips: Code Example from Apache Druid灸异;另外府适,error-prone 工具支持對多種版本@GuardedBy 進(jìn)行檢查

InterfaceStability

  • @InterfaceStability.Stable
    主版本是穩(wěn)定的,不同主版本間肺樟,可能不兼容

  • @InterfaceStability.Evolving
    不斷變化中檐春,不同的次版本間,可能不兼容

  • @InterfaceStability.Unstable
    不對可靠性和健壯性做任何保證

InterfaceAudience

  • @InterfaceAudience.Public
    對所有工程可用

  • @InterfaceAudience.LimitedPrivate
    僅限特定的工程么伯,如 HBase疟暖、ZookeeperHDFS 等(以 Hadoop 為例)

  • @InterfaceAudience.Private
    僅限于工程內(nèi)部使用

序列化

transient

給實現(xiàn)了 Serializable 接口的類中的字段,增加 transient 修飾符俐巴,則可以讓該字段跳過序列化的過程

Tips: Full code is here and here.

類中包含沒有實現(xiàn) Serializable 接口的字段

需要自己實現(xiàn) serializedeserialize 方法

Tips: Full code is here and here.

實用技巧

Collection 內(nèi)元素類型轉(zhuǎn)換

// 對集合里面的元素類型進(jìn)行轉(zhuǎn)換(A -> B)
List<B> variable = (List<B>)(List<?>) collectionOfListA;

集合的差集骨望、交集、并集

// 使用 Guava 中封裝的 Sets 類
import com.google.common.collect.Sets;

Sets.difference(set1, set2)
Sets.intersection(set1, set2)
Sets.union(set1, set2)

數(shù)組轉(zhuǎn)為 Set

// JDK8
new HashSet<>(Arrays.asList(str.trim().split(",")))
// JDK9+
Set.of(str.trim().split(","));

TransmittableThreadLocal 解決跨父子線程和線程池緩存問題

編碼

String tlMsg = "tl";
String ttlMsg = "ttl";

final ThreadLocal<String> tl = new ThreadLocal<>();
tl.set(tlMsg);
final TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();
ttl.set(ttlMsg);

assertEquals(tl.get(), tlMsg);
assertEquals(ttl.get(), ttlMsg);

new Thread(() -> {
    assertNull(tl.get());
    assertEquals(ttl.get(), ttlMsg);
}).start();

Tips: Full code is here.

參考

可讀性

魔法數(shù)字

編碼過程中欣舵,應(yīng)該避免出現(xiàn)沒有聲明含義的純數(shù)字

反面示例

// org.apache.kafka.connect.runtime.distributed.DistributedHerder#stop
@Override
public void stop() {
  if (!forwardRequestExecutor.awaitTermination(10000L, TimeUnit.MILLISECONDS))
    forwardRequestExecutor.shutdownNow();
}

正面示例

// 這里除了需要將 10000L 抽象成 FORWARD_REQUEST_SHUTDOWN_TIMEOUT_MS 靜態(tài)變量
// 還可以進(jìn)一步使用 10_000L 方便閱讀
// 因為這里和時間有關(guān)擎鸠,更進(jìn)一步,可以使用 TimeUnit.SECONDS.toMillis(10) 來代替純數(shù)字
private static final long FORWARD_REQUEST_SHUTDOWN_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);

@Override
public void stop() {
  if (!forwardRequestExecutor.awaitTermination(FORWARD_REQUEST_SHUTDOWN_TIMEOUT_MS, TimeUnit.MILLISECONDS))
    forwardRequestExecutor.shutdownNow();
}

Tips: Full code is here.

字符串判空

反面示例

// org.apache.kafka.connect.runtime.distributed.DistributedHerder#reconfigureConnector
leaderUrl.equals("")

正面示例

// 一方面缘圈,需要考慮不能將對象放在 equals 方法之前劣光,避免空指針異常
// 另一方面,使用 `str.length() == 0` 的方式糟把,效率會高一些
// 進(jìn)一步绢涡,使用 isEmpty() 方法,則可以使得代碼更加可讀
leaderUrl == null || leaderUrl.trim().isEmpty()

Tips: Full code is here.

箭頭型代碼

反面示例

public void m(String s) {
    if (s != null) {
        if (s.trim().length() > 0) {
            if (s.contains("yuzhouwan")) {
                System.out.println("https://yuzhouwan.com");
            }
        }
    }
}

正面示例

public void m(String s) {
    if (s == null) {
        return;
    }
    if (s.trim().length() == 0) {
        return;
    }
    if (!s.contains("yuzhouwan")) {
        return;
    }
    System.out.println("https://yuzhouwan.com");
}

參考

函數(shù)式編程

Optional

反面示例
public Long deserialize(ByteArrayDataInput in) {
    return isNullByteSet(in) ? null : in.readLong();
}
正面示例
return Optional.ofNullable(in)
               .filter(InputRowSerde::isNotNullByteSet)
               .map(ByteArrayDataInput::readLong)
               .get();
參考
  • Write null byte when indexing numeric dimensions with Hadoop #7020

anyMatch

反面示例
private boolean isTaskPending(Task task) {
    for (TaskRunnerWorkItem workItem : taskRunner.getPendingTasks()) {
        if (workItem.getTaskId().equals(task.getId())) {
            return true;
        }
    }
    return false;
}
正面示例
final String taskId = task.getId();
return taskRunner.getPendingTasks()
                 .stream()
                 .anyMatch(t -> taskId.equals(t.getTaskId()));
參考
  • Run pending tasks when assigned a task that is already pending #6991

設(shè)計模式

里氏替換原則

描述

里氏替換原則Liskov substitution principle糊饱,LSP)強(qiáng)調(diào)的是 面向?qū)ο蟪绦蛟O(shè)計中的 可替代性垂寥,說明在計算機(jī)程序中,如果 S 是 T 的子類型另锋,那么類型 T 的對象可以用類型 S 的對象替換(即 T 類型的對象可以被任何子類型 S 的對象替換)滞项,而不改變程序的任何期望屬性(正確地執(zhí)行的任務(wù)等)

參考

性能優(yōu)化

工具層面

性能指標(biāo)監(jiān)控

<metrics.version>3.2.0</metrics.version>

<dependency>
  <groupId>io.dropwizard.metrics</groupId>
    <artifactId>metrics-core</artifactId>
  <version>${metrics.version}</version>
  <exclusions>
    <exclusion>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
    </exclusion>
  </exclusions>
</dependency>

JMH 基準(zhǔn)測試

增加 Maven 依賴
<jmh.version>1.19</jmh.version>

<!-- JMH -->
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>${jmh.version}</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>${jmh.version}</version>
</dependency>
編寫 JMH 測試案例
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
public class BenchmarkSimple {

    @Benchmark
    public void bench() {
        add(1, 1);
    }

    private static int add(int a, int b) {
        return a + b;
    }

    /*
    Benchmark               Mode    Cnt     Score        Error     Units
    BenchmarkSimple.bench   thrpt    5  13352311.603 ± 767137.272  ops/ms
     */
    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(BenchmarkSimple.class.getSimpleName())
                .forks(1)
                .warmupIterations(5)
                .measurementIterations(5)
                .threads(10)
                .build();
        new Runner(opt).run();
    }
}

Tips: Full code is here.

優(yōu)勢
  • 不和會 JUnit 沖突,不用擔(dān)心 Jenkins 會自動跑 Benchmark 測試而影響效率(否則需要添加 @Ignore 讓 CI 系統(tǒng)忽略掉性能相關(guān)的 JUnit 測試用例)
  • 支持 warm up夭坪,可以解決 JIT 預(yù)熱問題

BTrace

介紹

BTrace is a safe, dynamic tracing tool for the Java platform. BTrace can be used to dynamically trace a running Java program (similar to DTrace for OpenSolaris applications and OS). BTrace dynamically instruments the classes of the target application to inject tracing code ("bytecode tracing").

參考

LooseJar

介紹

分析沒有被加載任何 class 的 jar 包戈二,幫助刪除工程中不必要的 jar 包立莉。不過,需要注意的是,有些類是動態(tài)加載的(比如數(shù)據(jù)類型轉(zhuǎn)換類的诽里,只有加載數(shù)據(jù)時才會用到),需要盡可能地多測試丑勤,才能保證 LooseJar 分析準(zhǔn)確

使用步驟
  • 下載
    在 LooseJar 的 release 頁面稳强,下載 loosejar-1.1.0.jar

  • 拷貝
    將 loosejar.jar 放到應(yīng)用的 WEB-INF 下的 lib 目錄中,比如說路徑是 /yuzhouwan/yuzhouwan-site/yuzhouwan-site-web/src/main/webapp/WEB-INF/lib/loosejar.jar

  • 配置
    在 IDE 中的 installed JRES 里面的 JDK 處配置 -Dfile.encoding=uft8 -javaagent:/yuzhouwan/yuzhouwan-site/yuzhouwan-site-web/src/main/webapp/WEB-INF/lib/loosejar.jar

  • 啟動
    啟動應(yīng)用间涵,盡可能做到路徑全覆蓋地測試應(yīng)用仁热,讓每段代碼都被執(zhí)行到

  • 查看結(jié)果
    運(yùn)行 JDK 自帶的 Jconsole 工具,選擇 BootStrap 的那個端口勾哩,然后選擇 MBean 下的 com.googlecode.loosejar抗蠢,并點擊 summary,即可看到分析結(jié)果

編碼層面

并發(fā)相關(guān)

synchronized

詳見思劳,《如何運(yùn)用 JVM 知識提高編程水平 - synchronized 的性能之爭

StampedLock
特性

該類是一個讀寫鎖的改進(jìn)迅矛,它的思想是讀寫鎖中讀不僅不阻塞讀,同時也不應(yīng)該阻塞寫

參考

集合優(yōu)化

HashMap
為什么 Java 8 版本中引入紅黑樹
  • 原因

    JDK8 以前 HashMap 的實現(xiàn)是 數(shù)組+鏈表潜叛,即使哈希函數(shù)取得再好秽褒,也很難達(dá)到元素百分百均勻分布

    當(dāng) HashMap 中有大量的元素都存放到同一個桶中時壶硅,這個桶下有一條長長的鏈表,這個時候 HashMap 就相當(dāng)于一個單鏈表震嫉,假如單鏈表有 n 個元素森瘪,遍歷的時間復(fù)雜度就是 O(n),完全失去了它的優(yōu)勢

    針對這種情況票堵,JDK8 中引入了 紅黑樹(查找時間復(fù)雜度為 O(\log n))來優(yōu)化這個問題

  • 流程

    添加時扼睬,當(dāng)桶中鏈表個數(shù)超過 8 時會轉(zhuǎn)換成紅黑樹

    刪除、擴(kuò)容時悴势,如果桶中結(jié)構(gòu)為紅黑樹窗宇,并且樹中元素個數(shù)太少的話,會進(jìn)行修剪或者直接還原成鏈表結(jié)構(gòu)

    查找時即使哈希函數(shù)設(shè)計不合理特纤,大量元素集中在一個桶中军俊,由于有紅黑樹結(jié)構(gòu),性能也不會差

Collections 類
空集合

Collections.emptyList() 重用一個對象而不是創(chuàng)建一個新對象捧存,就像 Arrays.asList() 一樣粪躬。不同的是,Collections.singletonList(something) 是不可變的昔穴,而 Arrays.asList(something) 是一個固定大小的 List镰官,其中 List 和 Array 在 Heap 中已經(jīng)連接。

參考
LinkedList vs. ArrayList
LinkedList
  • get(int index) is O(\frac{n}4) average
  • add(E element) is O(1)
  • add(int index, E element) is O(\frac{n}4) average, but O(1) when index = 0
  • remove(int index) is O(\frac{n}4) average
  • Iterator.remove() is O(1)
  • ListIterator.add(E element) is O(1)

Note: O(\frac{n}4) is average, O(1) best case (e.g. index = 0), O(\frac{n}2) worst case (middle of list)

ArrayList
  • get(int index) is O(1)
  • add(E element) is O(1) amortized, but O(n) worst-case since the array must be resized and copied
  • add(int index, E element) is O(\frac{n}2) average
  • remove(int index) is O(\frac{n}2) average
  • Iterator.remove() is O(\frac{n}2) average
  • ListIterator.add(E element) is O(\frac{n}2) average
參考
contains 方法
HashSet

時間復(fù)雜度 和 內(nèi)存使用率 角度看吗货,HashSet 為最佳之選

參考
toArray 方法
說明

There are two styles to convert a collection to an array: either using a pre-sized array (like c.toArray(new String[c.size()])) or using an empty array (like c.toArray(new String[0]).

In older Java versions using pre-sized array was recommended, as the reflection call which is necessary to create an array of proper size was quite slow. However since late updates of OpenJDK 6 this call was intrinsified, making the performance of the empty array version the same and sometimes even better, compared to the pre-sized version. Also passing pre-sized array is dangerous for a concurrent or synchronized collection as a data race is possible between the size and toArray call which may result in extra nulls at the end of the array, if the collection was concurrently shrunk during the operation.

This inspection allows to follow the uniform style: either using an empty array (which is recommended in modern Java) or using a pre-sized array (which might be faster in older Java versions or non-HotSpot based JVMs).

壓測
@State(Scope.Thread)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@BenchmarkMode(Mode.AverageTime)
public class ToArrayBenchmark {

    @Param({"1", "100", "1000", "5000", "10000", "100000"})
    private int n;

    private final List<Object> list = new ArrayList<>();

    @Setup
    public void populateList() {
        for (int i = 0; i < n; i++) {
            list.add(0);
        }
    }

    @Benchmark
    public Object[] preSize() {
        return list.toArray(new Object[n]);
    }

    @Benchmark
    public Object[] resize() {
        return list.toArray(new Object[0]);
    }

    /*
    Integer List:
    Benchmark                    (n)  Mode  Cnt       Score        Error  Units
    ToArrayBenchmark.preSize       1  avgt    3      41.552 ±    108.030  ns/op
    ToArrayBenchmark.preSize     100  avgt    3     216.449 ±    799.501  ns/op
    ToArrayBenchmark.preSize    1000  avgt    3    2087.965 ±   6027.778  ns/op
    ToArrayBenchmark.preSize    5000  avgt    3    9098.358 ±  14603.493  ns/op
    ToArrayBenchmark.preSize   10000  avgt    3   24204.199 ± 121468.232  ns/op
    ToArrayBenchmark.preSize  100000  avgt    3  188183.618 ± 369455.090  ns/op
    ToArrayBenchmark.resize        1  avgt    3      18.987 ±     36.449  ns/op
    ToArrayBenchmark.resize      100  avgt    3     265.549 ±   1125.008  ns/op
    ToArrayBenchmark.resize     1000  avgt    3    1560.713 ±   2922.186  ns/op
    ToArrayBenchmark.resize     5000  avgt    3    7804.810 ±   8333.390  ns/op
    ToArrayBenchmark.resize    10000  avgt    3   24791.026 ±  78459.936  ns/op
    ToArrayBenchmark.resize   100000  avgt    3  158891.642 ±  56055.895  ns/op

    Object List:
    Benchmark                    (n)  Mode  Cnt      Score       Error  Units
    ToArrayBenchmark.preSize       1  avgt    3     36.306 ±    96.612  ns/op
    ToArrayBenchmark.preSize     100  avgt    3     52.372 ±    84.159  ns/op
    ToArrayBenchmark.preSize    1000  avgt    3    449.807 ±   215.692  ns/op
    ToArrayBenchmark.preSize    5000  avgt    3   2080.172 ±  2003.726  ns/op
    ToArrayBenchmark.preSize   10000  avgt    3   4657.937 ±  8432.624  ns/op
    ToArrayBenchmark.preSize  100000  avgt    3  51980.829 ± 46920.314  ns/op
    ToArrayBenchmark.resize        1  avgt    3     16.747 ±    85.131  ns/op
    ToArrayBenchmark.resize      100  avgt    3     43.803 ±    28.704  ns/op
    ToArrayBenchmark.resize     1000  avgt    3    404.681 ±   132.986  ns/op
    ToArrayBenchmark.resize     5000  avgt    3   1972.649 ±   174.691  ns/op
    ToArrayBenchmark.resize    10000  avgt    3   4021.440 ±  1114.212  ns/op
    ToArrayBenchmark.resize   100000  avgt    3  44204.167 ± 76714.850  ns/op
     */
    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder()
                .include(ToArrayBenchmark.class.getSimpleName())
                .forks(1)
                .warmupIterations(1)
                .measurementIterations(3)
                .threads(1)
                .build();
        new Runner(opt).run();
    }
}

Tips: Full code is here.

instanceof
Operation Runtime in nanoseconds per operation Relative to instanceof
INSTANCEOF 39,598 ± 0,022 ns/op 100,00 %
GETCLASS 39,687 ± 0,021 ns/op 100,22 %
TYPE 46,295 ± 0,026 ns/op 116,91 %
OO 48,078 ± 0,026 ns/op 121,42 %
參考

字符串相關(guān)

repeat

借鑒 JDK11 中新增的 String#repeat 特性泳唠,實現(xiàn)高效的 repeat 工具方法

import java.nio.charset.StandardCharsets;

/**
 * Returns a string whose value is the concatenation of the
 * string {@code s} repeated {@code count} times.
 * <p>
 * If count or length is zero then the empty string is returned.
 * <p>
 * This method may be used to create space padding for
 * formatting text or zero padding for formatting numbers.
 *
 * @param count number of times to repeat
 * @return A string composed of this string repeated
 * {@code count} times or the empty string if count
 * or length is zero.
 * @throws IllegalArgumentException if the {@code count} is negative.
 * @link https://bugs.openjdk.java.net/browse/JDK-8197594
 */
public static String repeat(String s, int count) {
    if (count < 0) {
        throw new IllegalArgumentException("count is negative, " + count);
    }
    if (count == 1) {
        return s;
    }
    byte[] value = s.getBytes(StandardCharsets.UTF_8);
    final int len = value.length;
    if (len == 0 || count == 0) {
        return "";
    }
    if (len == 1) {
        final byte[] single = new byte[count];
        Arrays.fill(single, value[0]);
        return new String(single, StandardCharsets.UTF_8);
    }
    if (Integer.MAX_VALUE / count < len) {
        throw new OutOfMemoryError();
    }
    final int limit = len * count;
    final byte[] multiple = new byte[limit];
    System.arraycopy(value, 0, multiple, 0, len);
    int copied = len;
    for (; copied < limit - copied; copied <<= 1) {
        System.arraycopy(multiple, 0, multiple, copied, copied);
    }
    System.arraycopy(multiple, 0, multiple, copied, limit - copied);
    return new String(multiple, StandardCharsets.UTF_8);
}
參考

Fork / Join 思想

這里以查找最小數(shù)為例,具體實現(xiàn)如下:

import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

/**
 * Copyright @ 2019 yuzhouwan.com
 * All right reserved.
 * Function:Minimum Finder
 *
 * @author Benedict Jin
 * @since 2019/4/13
 */
public class MinimumFinder extends RecursiveTask<Integer> {

    private static final int JOIN_THRESHOLD = 5;

    private final int[] data;
    private final int start;
    private final int end;

    private MinimumFinder(int[] data, int start, int end) {
        this.data = data;
        this.start = start;
        this.end = end;
    }

    private MinimumFinder(int[] data) {
        this(data, 0, data.length);
    }

    @Override
    protected Integer compute() {
        final int len = end - start;
        if (len < JOIN_THRESHOLD) {
            return internal();
        }
        final int split = len / 2;
        final MinimumFinder left = new MinimumFinder(data, start, start + split);
        left.fork();
        final MinimumFinder right = new MinimumFinder(data, start + split, end);
        return Math.min(right.compute(), left.join());
    }

    private Integer internal() {
        System.out.println(Thread.currentThread() + " computing: " + start + " to " + end);
        int min = Integer.MAX_VALUE;
        for (int i = start; i < end; i++) {
            if (data[i] < min) {
                min = data[i];
            }
        }
        return min;
    }

    public static void main(String[] args) {
        final int[] data = new int[1000];
        final Random random = new Random(System.nanoTime());
        for (int i = 0; i < data.length; i++) {
            data[i] = random.nextInt(100);
        }
        final ForkJoinPool pool = new ForkJoinPool(Runtime.getRuntime().availableProcessors());
        final MinimumFinder finder = new MinimumFinder(data);
        System.out.println(pool.invoke(finder));
    }
}
參考

堆棧優(yōu)化

ByteBuffer

通過 allocateDirect(int capacity) 方法可以避開堆棧宙搬,直接通過操作系統(tǒng)創(chuàng)建內(nèi)存塊作為緩沖區(qū)笨腥。該方式與操作系統(tǒng)能更好地耦合,因而能進(jìn)一步提高 I/O 操作的速度勇垛。缺點是脖母,分配直接緩沖區(qū)的系統(tǒng)開銷很大。因此闲孤,只有在緩沖區(qū)較大并會長期存在谆级,或者需要經(jīng)常重用時,才使用這種緩沖區(qū)

位運(yùn)算

奇偶數(shù)
public static boolean isEven(int i) {
    return (i & 1) == 0;
}
毫秒
public static final long SECOND_MASK = 0xFFFFFFFF00000000L;

public static boolean isMillis(long timestamp) {
    return (timestamp & SECOND_MASK) != 0;
}
參考

日志相關(guān)

log4j 開啟 BufferedIO

參考

測試相關(guān)

參數(shù)驅(qū)動

利用 @Parameterized.Parameters 注解可以指定多個可能的傳值,使得當(dāng)前測試類下的所有測試用例可以被多次復(fù)用玻侥。但是該注解并不能讓參數(shù)之間自行組合决摧,所以嚴(yán)格來說,并不是參數(shù)驅(qū)動(后續(xù)介紹的 HttpRunner 框架則是嚴(yán)格意義上的參數(shù)驅(qū)動)

import com.yuzhouwan.compression.CompressionType;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.junit.runners.Parameterized;

import java.util.Arrays;

@FixMethodOrder(MethodSorters.JVM)
@RunWith(Parameterized.class)
public class CompressionTest {

    @Parameterized.Parameter()
    public CompressionType compressionType4ts;
    @Parameterized.Parameter(1)
    public CompressionType compressionType4longValue;
    @Parameterized.Parameter(2)
    public CompressionType compressionType4doubleValue;

    @Parameterized.Parameters
    public static Iterable<Object[]> getParameters() {
        return Arrays.asList(new Object[][]{
                {CompressionType.NONE, CompressionType.NONE, CompressionType.NONE},
                {CompressionType.SIMPLE8B, CompressionType.NONE, CompressionType.GORILLA},
                {CompressionType.SIMPLE8B_WITH_RLE, CompressionType.ZIGZAG_WITH_SIMPLE8B, CompressionType.NONE},
        });
    }

    /**
     * NONE - NONE - NONE
     * SIMPLE8B - NONE - GORILLA
     * SIMPLE8B_WITH_RLE - ZIGZAG_WITH_SIMPLE8B - NONE
     */
    @Test
    public void test() {
        System.out.println(compressionType4ts + " - " + compressionType4longValue + " - " + compressionType4doubleValue);
    }
}

測試先行

參考

自動生成 Test Case

參考

自動化測試

HttpRunner

介紹

HttpRunner? 是一款面向 HTTP(S) 協(xié)議的通用測試框架,只需編寫維護(hù)一份 YAML/JSON 腳本掌桩,即可實現(xiàn)自動化測試边锁、性能測試、線上監(jiān)控波岛、持續(xù)集成等多種測試需求茅坛。這里將以測試 OpenTSDB 為例,更加具象地介紹 HttpRunner

QuickStart
安裝
$ pip install httprunner==1.5.15

$ hrun -V
  1.5.15

$ har2case -V
  0.2.0
啟動 Flask
$ pip install flask

$ mkdir docs/data/
$ wget https://cn.httprunner.org/data/api_server.py -P docs/data/
$ export FLASK_APP=docs/data/api_server.py
$ export FLASK_ENV=development
$ flask run

$ curl localhost:5000                                          
  Hello World!
測試
$ wget https://cn.httprunner.org/data/demo-quickstart.har -P docs/data/

# 將 demo-quickstart.har 轉(zhuǎn)換為 HttpRunner 的測試用例文件
# 默認(rèn)輸出 JSON 文件则拷,加 `-2y` 參數(shù)贡蓖,可以轉(zhuǎn)化為 YAML
$ har2case docs/data/demo-quickstart.har
$ hrun docs/data/demo-quickstart.json
新建測試項目
# 新建目錄
$ httprunner --startproject yuzhouwan

$ ls -sail
  12891420763 4 -rw-r--r--  1 benedictjin wheel   44 Feb  2 11:37 .env
  12891384628 0 drwxr-xr-x  2 benedictjin wheel   64 Feb  1 16:44 api/
  12891454305 4 -rw-r--r--  1 benedictjin wheel 2389 Feb  2 14:34 debugtalk.py
  12891454901 4 -rw-r--r--  1 benedictjin wheel 1452 Feb  2 15:21 locustfile.py
  12891454386 0 drwxr-xr-x  8 benedictjin wheel  256 Feb  2 15:30 reports/
  12891384629 0 drwxr-xr-x  7 benedictjin wheel  224 Feb  2 14:47 testcases/
  12891384630 0 drwxr-xr-x  2 benedictjin wheel   64 Feb  1 16:44 testsuites/
# .env         存放環(huán)境變量的 properties 文件
# testcases    存放所有 httprunner 的 json 測試實例
# debugtalk.py 存放所有 httprunner 測試實例中,需要用到自定義函數(shù)
# reports      生成的 html 結(jié)果頁面
  • 本文作者: Benedict Jin
  • 本文鏈接: https://yuzhouwan.com/posts/190413/
  • 版權(quán)聲明: 本博客所有文章除特別聲明外煌茬,均采用 BY-NC-ND 許可協(xié)議斥铺。轉(zhuǎn)載請注明出處!
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末坛善,一起剝皮案震驚了整個濱河市晾蜘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌眠屎,老刑警劉巖剔交,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異组力,居然都是意外死亡省容,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門燎字,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腥椒,“玉大人,你說我怎么就攤上這事候衍×耄” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵蛉鹿,是天一觀的道長滨砍。 經(jīng)常有香客問我,道長妖异,這世上最難降的妖魔是什么惋戏? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮他膳,結(jié)果婚禮上响逢,老公的妹妹穿的比我還像新娘。我一直安慰自己棕孙,他們只是感情好舔亭,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布些膨。 她就那樣靜靜地躺著,像睡著了一般钦铺。 火紅的嫁衣襯著肌膚如雪订雾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天矛洞,我揣著相機(jī)與錄音洼哎,去河邊找鬼。 笑死沼本,一個胖子當(dāng)著我的面吹牛谱净,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播擅威,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼壕探,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了郊丛?” 一聲冷哼從身側(cè)響起李请,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厉熟,沒想到半個月后导盅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡揍瑟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年白翻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绢片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡滤馍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出底循,到底是詐尸還是另有隱情巢株,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布熙涤,位于F島的核電站阁苞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏祠挫。R本人自食惡果不足惜那槽,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望等舔。 院中可真熱鬧骚灸,春花似錦、人聲如沸软瞎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涤浇。三九已至鳖藕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間只锭,已是汗流浹背著恩。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留蜻展,地道東北人喉誊。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像纵顾,于是被迫代替她去往敵國和親伍茄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容