ITと服と食と

洋服と食をこよなく愛するWebプログラマ。

Java標準ライブラリを学ぶ #1

Javaエンジニア養成読本を読んで気になったところをまとめていこうと思います。

今回は第4章。

文字列を扱う

文字列を比較するときに気をつけること

  • 「==」は使用できない

Stringクラスはnew演算子インスタンスを生成することが稀なため、インスタンスの識別が曖昧になりがち。
文字列リテラルは常にインスタンスを生成するわけでなく、生成済みの場合は同じインスタンスが共有される。Stringクラスのインスタンスをnew演算子で各々生成すれば、同じ文字列であっても、==演算子でfalseを返す。
文字列の一致を検証したい場合は、equals()メソッドを使用する。

文字列の連結で知っておくべきこと

+で文字列の連結が可能だが、連結されるたびに新たなインスタンスが生成される。そのため、連続して連結する場合は、StringBuilderクラスを使用するのが一般的。
Java SE8 にてStringクラスに文字列を区切り文字つきで連結するjoin()メソッドが追加された。

Stringクラスはイミュータブル

replace()メソッドなどの状態を変化させそうなメソッドも自身のインスタンスは変更せず、別のインスタンスを返すようになっている。

実際にいろんな文字列連結パターンの所要時間出して見ました。コードはこれ。
次のブログから知恵をお借りしました。
calms.hatenablog.com

public class TestString {
	public static void main(String[] args) {
		String val1 = "abcde";
		String val2 = "fghij";
		String val3 = "klmno";
		String val4 = "pqrst";
		String val5 = "uvwxyz";

		long startTime1 = System.currentTimeMillis();
		String message1 = null;
		for (int i = 0; i < 100000000; i++) {
			message1 = val1 + val2;
			message1 += val3;
			message1 += val4;
			message1 += val5;
		}
		long endTime1 = System.currentTimeMillis();
		System.out.println("+で連結の所要時間(ms) : " + (endTime1 - startTime1));

		long startTime2 = System.currentTimeMillis();
		String message2 = null;
		for (int i = 0; i < 100000000; i++) {
			StringBuilder buf2 = new StringBuilder();
			buf2.append(val1);
			buf2.append(val2);
			buf2.append(val3);
			buf2.append(val4);
			buf2.append(val5);
			message2 = buf2.toString();
		}
		long endTime2 = System.currentTimeMillis();
		System.out.println("StringBuilderで連結の所要時間(ms) : " + (endTime2 - startTime2));

		long startTime3 = System.currentTimeMillis();
		String message3 = null;
		StringBuilder buf3 = new StringBuilder();
		for (int i = 0; i < 100000000; i++) {
			buf3.setLength(0);
			buf3.append(val1);
			buf3.append(val2);
			buf3.append(val3);
			buf3.append(val4);
			buf3.append(val5);
			message3 = buf3.toString();
		}
		long endTime3 = System.currentTimeMillis();
		System.out.println("StringBuilder&setLength(0)で連結の所要時間(ms) : " + (endTime3 - startTime3));

		long startTime4 = System.currentTimeMillis();
		String message4 = null;
		for (int i = 0; i < 100000000; i++) {
			StringBuilder buf4 = new StringBuilder(val1);
			buf4.append(val2);
			buf4.append(val3);
			buf4.append(val4);
			buf4.append(val5);
			message4 = buf4.toString();
		}
		long endTime4 = System.currentTimeMillis();
		System.out.println("StringBuilder&new StringBuilder(val1)で連結の所要時間(ms) : " + (endTime4 - startTime4));

		long startTime5 = System.currentTimeMillis();
		String message5 = null;
		for (int i = 0; i < 100000000; i++) {
			message5 = val1 + val2 + val3 + val4 + val5;
		}
		long endTime5 = System.currentTimeMillis();
		System.out.println("+で一気に連結の所要時間(ms) : " + (endTime5 - startTime5));
	}
}

ちなみに結果は、こんな感じです。
=======
+で連結の所要時間(ms) : 6387
StringBuilderで連結の所要時間(ms) : 7995
StringBuilder&setLength(0)で連結の所要時間(ms) : 4869
StringBuilder&new StringBuilder(val1)で連結の所要時間(ms) : 8711
+で一気に連結の所要時間(ms) : 2991
=======

5番目の連結方法が圧倒的に早いです!

ユニットテストのナレッジ #1

1ヶ月ぶりに更新します。今年はもっとインプットを増やしてアウトプットも増やしていこうと思います!

まずテスト対象クラスはこちらのShopService.java

@Service
public class ShopService {

	@Autowired
	private ShopRepository shopRepository;

	public Shop getShop(Integer id) {
		return shopRepository.findOne(id);
	}

	public Shop create(ShopCreateRequest request) {
		LocalDateTime now = LocalDateTime.now();

		Shop shop = new Shop();
		shop.setGenre(request.getGenre());
		shop.setShopName(request.getShopName());
		shop.setStation(request.getStation());
		shop.setUrl(request.getUrl());
		shop.setMemo(request.getMemo());
		shop.setCreatedAt(now);

		return shopRepository.saveAndFlush(shop);
	}

	public Shop edit(Integer id, ShopEditRequest request) {
		Shop shop = getShop(id);
		shop.setGenre(request.getGenre());
		shop.setShopName(request.getShopName());
		shop.setStation(request.getStation());
		shop.setUrl(request.getUrl());
		shop.setMemo(request.getMemo());

		return shopRepository.saveAndFlush(shop);
	}
}

続いてテストクラスは、ShopService.java

import com.uchitate.core.entity.Shop;
import com.uchitate.core.repository.ShopRepository;
import com.uchitate.web.support.Genre;
import com.uchitate.web.support.ShopCreateRequest;
import com.uchitate.web.support.ShopEditRequest;
import de.bechte.junit.runners.context.HierarchicalContextRunner;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;

@RunWith(HierarchicalContextRunner.class)
public class ShopServiceTest {

	@InjectMocks
	private ShopService shopService;

	@Mock
	private ShopRepository shopRepository;

	@Captor
	private ArgumentCaptor<Shop> captor;

	@Before
	public void setup() {
		MockitoAnnotations.initMocks(this);
	}

	public class Create {
		private ShopCreateRequest generateShopCreateRequest(
				Genre genre,
				String shopName,
				String station,
				String url,
				String memo) {
			ShopCreateRequest request = new ShopCreateRequest(genre, shopName, station, url, memo);
			return request;
		}

		@Test
		public void success() {
			ShopCreateRequest request = generateShopCreateRequest(
					Genre.JAPANESE,
					"定食屋1",
					"渋谷",
					"http://teisyoku-1.co.jp",
					"トンカツが有名");

			shopService.create(request);
			Mockito.verify(shopRepository).saveAndFlush(captor.capture());
			Shop savedShop = captor.getValue();
			Assertions.assertThat(savedShop)
					.extracting(Shop::getGenre, Shop::getShopName, Shop::getStation, Shop::getUrl, Shop::getMemo)
					.contains(Genre.JAPANESE, "定食屋1", "渋谷", "http://teisyoku-1.co.jp", "トンカツが有名");
		}
	}

	public class Edit {
		private ShopEditRequest generateShopEditRequest(
				Genre genre,
				String shopName,
				String station,
				String url,
				String memo) {
			ShopEditRequest request = new ShopEditRequest(genre, shopName, station, url, memo);
			return request;
		}

		private Shop generateShop(int id, Genre genre, String shopName, String station, String url, String memo) {
			Shop shop = new Shop();
			shop.setId(id);
			shop.setGenre(genre);
			shop.setShopName(shopName);
			shop.setStation(station);
			shop.setUrl(url);
			shop.setMemo(memo);
			return shop;
		}

		@Test
		public void success() {
			ShopEditRequest request = generateShopEditRequest(
					Genre.JAPANESE,
					"定食屋1",
					"渋谷",
					"http://teisyoku-1.co.jp",
					"トンカツが有名");

			Shop shop = generateShop(1, Genre.ITALIAN, "イタリアン1", "代官山", "http://italian-1.co.jp", "ピザ");

			Mockito.when(shopRepository.findOne(Mockito.anyInt())).thenReturn(shop);

			shopService.edit(1, request);
			Mockito.verify(shopRepository).saveAndFlush(captor.capture());
			Shop savedShop = captor.getValue();
			Assertions.assertThat(savedShop)
					.extracting(Shop::getGenre, Shop::getShopName, Shop::getStation, Shop::getUrl, Shop::getMemo)
					.contains(Genre.JAPANESE, "定食屋1", "渋谷", "http://teisyoku-1.co.jp", "トンカツが有名");
		}
	}
}

では、解説。

@RunWith(HierarchicalContextRunner.class)

ランナーは、「HierarchicalContextRunner.class」を指定します。
github.com
このクラスは、テストクラスの階層構造を可能にします。ただそれだけといえばそれだけです。
見た目が美しくなります。私は、テスト対象クラスのメソッド単位でテストクラスを作ってます。


まず、pom.xmlに以下を追加します。

<dependency>
    <groupId>de.bechte.junit</groupId>
    <artifactId>junit-hierarchicalcontextrunner</artifactId>
    <version>4.12.1</version>
</dependency>

あとは、

@RunWith(HierarchicalContextRunner.class)

と書くだけです。

@InjectMocks

このクラスにモックを注入しますよアノテーション

@Mock

モックにするよアノテーション

@Captor

好きなところの状態のオブジェクトを取得したいよアノテーション

@Captor
private ArgumentCaptor<Shop> captor;

Shop.javaのオブジェクトを取得することを指定します。
次をテスト対象クラスのメソッドを呼んだあとに書きます。

Mockito.verify(shopRepository).saveAndFlush(captor.capture());
Shop savedShop = captor.getValue();

これで、「shopRepository.saveAndFlush(shop);」を呼ぶ時に渡す引数のオブジェクトを取得します。

editは、ほぼ同じなので省略します。
以上、ユニットテストのナレッジ #1でした〜

福岡へ行ってきた話

福岡に行ってきました〜
以前住んでいたので思い出の地&好きな食べ物を主に巡りました!

まず、福岡から〜
飛行機で到着してまず空港内のクロワッサン専門店の三日月へ!
開店から3時間くらいにも関わらず結構売り切れてた。。。
残りの中からゴマをチョイス!
冷えてるのにサクサク感を残してる上に美味(^^)ゴマの風味を生かすためにバターは抑えめでした。
f:id:b1a9id:20170104174432j:plain

お腹を軽く満たしたので空港を後にして天神へ!
ホテルに荷物を預けいざ出発。
前日のプランを忘れてて全く違うところにいってしまったため行きたかったパン屋さんを2件断念。。。

気をとりなおして櫛田神社へ。
中には結構いろんな神様がいらっしゃいました。
f:id:b1a9id:20170104174700j:plain

次に大好きな場所、ヤフオクドーム裏の百道浜へ!
f:id:b1a9id:20170104174743j:plain
駅から遠いのが残念ですけどやはりいい景色。海の音は心を落ち着かせてくれる〜

日が明けて次の日の朝ごはん。
とても好きな鶏めし。このおにぎり216円ってびびる。
おいしすぎるためぜひ買ってもらいたい。
f:id:b1a9id:20170104175020j:plain

そして、お昼。一心不乱へ。前に1組で余裕で入れた。
ここの黒とんこつの超こってりはとてもオススメ。なんと背脂入り。笑
f:id:b1a9id:20170104175143j:plain
口の中が臭くなったところで福岡を後にしました。

nodebrewによるNode.jsインストール手順

仕事にてnodeが必要になりまして、、、
nodeのインストールにちょいと苦戦したので参考までに!
Node.jsをMacにインストールしてnpmを使えるようにする - Hirooooo’s Laboを参考にさせていただきました〜

Node.jsがインストールされているバージョンを確認

$ node -v
v6.2.1

Node.jsのアンインストール方法

すでにNode.jsがインストールされている場合は、次の方法でアンインストールしてください。

$ brew uninstall node
Uninstalling /usr/local/Cellar/node/6.2.1... (3,818 files, 45.3M)

しかし、macのOS的問題でこんな感じで怒られてしまい、すんなりアンインストールはできませんでした。

Error: Permission denied - /usr/local/bin/node

こんな場合は、次のコマンドで/usr/localのパーミッション変えちゃいましょう

$ sudo chown -R `whoami` /usr/local

これ以降の手順

先に書きましたリンクの記事を参考にしていただけるとよいかと思いますが、
Node.jsのインストールのところでつまづいたのでそこの解決方法だけ補足します。

$ nodebrew install-binary latest
Fetching: https://nodejs.org/dist/v7.2.1/node-v7.2.1-darwin-x64.tar.gz
Warning: Failed to create the file
Warning: /[Home_Directory]/.nodebrew/src/v7.2.1/node-v7.2.1-darwin-x64.tar.gz:
Warning: No such file or directory
                                                                           0.0%
curl: (23) Failed writing body (0 != 941)

なんだこのエラーは!!!と思い、次のようにディレクトリを作ってあげたらできました。

$ mkdir -p ~/.nodebrew/src

mysqlで月+2の15日を入れたい

今回も備忘録レベルです。
仕事で作成日の月に2を足した15日を値を入れるというSQL書かないといけなくて。。。
作成日が6/2のとき、8/15をいれたいって時です。

update table1 t set t.delete_date=date_format(date_add(t.created_at, interval 2 month),'%Y-%m-15');

UPDATE文でのINNER JOINの書き方

よく忘れちゃうので。

update table1 inner join table2 on table1.column1 = table2.column2
set table1.column2 = table2.column2;

PrivateメソッドとPrivateフィールドのユニットテスト

表題の通り、備忘録として。

Target.java(テスト対象クラス)

public class Target {
 
    private String str = "test";
 
    public String getStr() {
        return this.str;
    }
 
    private String thisIsPrivateMethod(Integer number) {
        return number == 1 ? "One" : "Other";
    }
     
}

TargetTest.java(テストクラス)

puclic class TargetTest {
 
    Field field;
 
    Method thisIsPrivateMethod;
     
    @Before
    public void setup() {
         
        // Targetクラスのstrフィールドにアクセス可能
        field = Target.class.getDeclaredField("str");
        field.setAccessible(true); 
 
        // TargetクラスのthisIsPrivateMethodメソッドにアクセス可能(getDeclareMethodの第1引数はメソッド名、第2引数以降は対象メソッドのパラメータの型)
        thisIsPrivateMethod = Target.class.getDeclareMethod("thisIsPrivateMethod",  Integer.class);
        thisIsPrivateMethod.setAccessible(true);
    }
 
    @Test
    public void privateField() {
        Target target = new Target();
        // Targetインスタンスのstrフィールドに"tagbengers"をセット
        field.set(target, "tagbangers");
 
        Assert.assertEquals("tagbangers", target.getStr());
    }
 
    @Test
    public void privateMethod() {
        Target target = new Target();
        Assert.assertEquals("One", thisIsPrivateMethod.invoke(target, 1));
    }
}

こんな感じでユニットテストかけます。