Going my way

いいなと思ったことをメモしていきます。

マルチスレッドことはじめ

スレッドとは
実行制御の流れのこと。
マルチスレッドとは、実行制御を複数並行で持つこと。
実際は同時に行なっているというよりは、CPUが短い時間で処理を切り替えて実行を切り替えている。


スレッドクラスの実装は2パターンある。
java.lang.Threadクラスの拡張
java.lang.Runnableインターフェースの実装

Threadクラスを使用したスレッドのサンプル

package thread;

public class ThreadSample {
	public static void main(String[] args){
		Count1 c1 = new Count1();
		Count2 c2 = new Count2();
		Count3 c3 = new Count3();
		c1.start();
		c2.start();
		c3.start();
	}
}

class Count1 extends Thread {
	public void run(){
		for(int i=1; i<20; i++){
			System.out.println("Count1:"+i);
		}
		System.out.println("Count1 end");
	}
}

class Count2 extends Thread {
	public void run(){
		for(int i=1; i<20; i++){
			System.out.println("Count2:"+i);
		}
		System.out.println("Count2 end");
	}
}

class Count3 extends Thread {
	public void run(){
		for(int i=1; i<20; i++){
			System.out.println("Count3:"+i);
		}
		System.out.println("Count3 end");
	}
}

Count1,Count2,Count3のクラスがマルチスレッドで実行される。
ただし、startで呼び出された順に実行されるわけではない。
スレッドの処理をスタートさせるのはOSで、プログラマには制御不可能である。
事実、このサンプルの実行結果は、以下の順で処理が終了した。

The thread has been started
Count2 end
Count3 end
Count1 end

Runnableインターフェースを使用したスレッドの作成

package thread;

public class ThreadSample2 {
	public static void main(String[] args){
		RunCount rc = new RunCount();
		RunCount2 rc2 = new RunCount2();
		RunCount3 rc3 = new RunCount3();
		
		//スレッド作りまくる
		Thread th = new Thread(rc);
		th.start();
		Thread th2 = new Thread(rc);
		th2.start();
		Thread th3 = new Thread(rc2);
		th3.start();
		Thread th4 = new Thread(rc3);
		th4.start();
		Thread th5 = new Thread(rc2);
		th5.start();
		Thread th6 = new Thread(rc3);
		th6.start();

	}
}

class RunCount implements Runnable{
	public void run(){
		for(int i=1; i<100; i++){
			//System.out.println("Count1:"+i);
		}
		System.out.println("Count1 end");
	}
}

class RunCount2 implements Runnable{
	public void run(){
		for(int i=1; i<100; i++){
			//System.out.println("Count2:"+i);
		}
		System.out.println("Count2 end");
	}
}

class RunCount3 implements Runnable{
	public void run(){
		for(int i=1; i<100; i++){
			//System.out.println("Count3:"+i);
		}
		System.out.println("Count3 end");
	}
}

実行結果は以下の通り

Count1 end
Count2 end
Count1 end
Count3 end
Count3 end
Count2 end

さっそくテンプレートメソッドパターン使ってみたよ!

テンプレートメソッド(TempleteMethod)とは、読んで字のごとく、
テンプレート(型)を作って、テンプレに沿って実装するパターンである。
f:id:go_my_own_way:20050923143836j:plain

interfaceで型を決めて、実装はサブクラスに任せる。
クラスを使う側は、型だけ知っていれば良い。

せっかくテンプレートメソッドパターンを学んだから、実装してみた。

要件は以下の通り。

hoge	fuga	foo
hoge2	fuga2	foo2

タブ区切りのファイルを読み込んで、データの粒を取り出して標準出力に出力する。

同じようにカンマ区切りのファイルを読み込み、データを取り出して標準出力する。

hoge,fuga,foo
hoge2,fuga2,foo

これらには共通の処理があるので、それをテンプレートにする。
共通の処理はこのようなものがある。

・ファイルを開く(open)
・ファイルを読む(read)
・ファイルのデータを出力する(print)
・ファイルを閉じる(close)

クラス図にすると、こんな感じ。
f:id:go_my_own_way:20120811002823p:plain

では、ソースコードを見てみよう。

package templeteMethod;

import java.io.BufferedReader;

public interface ReadFile {
	public BufferedReader open(String fileName);
	public void read(BufferedReader inputFile);
	public void print(String line);
	public void close(BufferedReader inputFile);
	
}

これはテンプレートとなるインターフェースだ。
この型を使って、CSVファイルの読み込みとタブの読み込みを実装する。

ReadTabFile.java

package templeteMethod;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class ReadTabFile implements ReadFile{
	private static String fileName;
	File inputFile = null;
	BufferedReader in = null;
	
	public void setFileName(String fileName){
		fileName = this.fileName;
	}
	
	
	public BufferedReader open(String fileName){
		try{
			inputFile = new File(fileName);
			in = new BufferedReader(new FileReader(inputFile));
			return in;
		} catch(FileNotFoundException e){
			e.printStackTrace();
		}
		return null;
	}
	
	public void read(BufferedReader inputFile){
		try {
			String line;
			while((line = inputFile.readLine()) != null){
				print(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void print(String line){
		Pattern p = Pattern.compile("\t");
		String pieces[] = p.split(line);
		List<String> csvList = new ArrayList<String> ();
		
		for(int i=0; i<pieces.length;i++){
			csvList.add(pieces[i]);
		}
		
		for(String word : csvList){
			System.out.println(word);
		}
		
	}
	
	public void close(BufferedReader inputFile){
		try{
			inputFile.close();
		}catch(IOException e){
			e.printStackTrace();
		}
	}

}

タブの区切りは1行を正規表現「\t」で区切って配列にして格納した。

次はCSVファイルの処理。

package templeteMethod;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public class ReadCsvFile implements ReadFile {
	private static String fileName;
	File inputFile = null;
	BufferedReader in = null;
	
	public void setFileName(String fileName){
		fileName = this.fileName;
	}
	
	
	public BufferedReader open(String fileName){
		try{
			inputFile = new File(fileName);
			in = new BufferedReader(new FileReader(inputFile));
			return in;
		} catch(FileNotFoundException e){
			e.printStackTrace();
		}
		return null;
	}
	
	public void read(BufferedReader inputFile){
		try {
			String line;
			while((line = inputFile.readLine()) != null){
				print(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public void print(String line){
		Pattern p = Pattern.compile(",");
		String pieces[] = p.split(line);
		List<String> csvList = new ArrayList<String> ();
		
		for(int i=0; i<pieces.length;i++){
			csvList.add(pieces[i]);
		}
		
		for(String word : csvList){
			System.out.println(word);
		}
		
	}
	
	public void close(BufferedReader inputFile){
		try{
			inputFile.close();
		}catch(IOException e){
			e.printStackTrace();
		}
	}


}

CSVファイルもPatternクラスを使って「,」で区切った。
見ればわかるけど、タブ区切りとCSVファイルを読み込むクラスは正規表現の部分しか違わない。
あれ?テンプレートメソッドパターンなんて使わないで、1つのクラスに正規表現を渡せば済んだのでは?
というツッコミはスルーしよう。
なぜならこれはテンプレートメソッドの練習だから。

でお待ちかね、Mainメソッドはこちら。

package templeteMethod;

import java.io.BufferedReader;

public class Main {
	private static String CSVFile = "C:\\temp\\CSV.txt";
	private static String TabFile = "C:\\temp\\Tab.txt";
	private static BufferedReader in = null;
	
	public static void main(String[] args){
		
		//オブジェクトの参照を代入する型がReadFileであることに注目!!
		ReadFile readCsv = new ReadCsvFile();
		ReadFile readTab = new ReadTabFile();
		
		
		System.out.println("----------------------");
		System.out.println("カンマ区切りのデータを取り出す");
		//CSVファイルを読み込んで出力する。
		//Mainクラスを使う人は、ReadFileのインターフェースだけ知ってればいい。
		in = readCsv.open(CSVFile);	
		readCsv.read(in);
		readCsv.close(in);
		
		System.out.println("----------------------");
		System.out.println("タブ区切りのデータを取り出す");
		//Tab区切りファイルを読み込んで出力する
		in = readTab.open(TabFile);
		readTab.read(in);
		readTab.close(in);
		
		
	}
}

注目すべきはここ。

		//オブジェクトの参照を代入する型がReadFileであることに注目!!
		ReadFile readCsv = new ReadCsvFile();
		ReadFile readTab = new ReadTabFile();

型がReadFileなんですよね。
つまり、クラスを使うMain側から見ると、インターフェースのメソッドだけ知ってればいいってこと。
この観点はけっこう重要で、

・インターフェースは変わりづらい
・実装クラスは変わりやすい

ので、インターフェース型でメソッドを使うのは、変更に強いコードを書くために大切なことなのである。

実行した結果は以下のとおり

----------------------
カンマ区切りのデータを取り出す
hoge
fuga
foo
hoge2
fuga2
foo
----------------------
タブ区切りのデータを取り出す
hoge
fuga
foo
hoge2
fuga2
foo2

ちゃんとデータ取れてますね!

Javaの実行コマンドとクラスパスの指定、jarにパスを通して実行する方法など

Javaアプリケーションの実行

外部jarの参照など、eclipseで設定するのは簡単だ。
しかし、linuxバッチ処理として動かしたいときなどはjavaコマンドを使い、
その際は-classpathの設定をしなければならない。

Javaアプリケーションの実行の際は、依存関係のあるすべてのクラスは実行環境(JVM)から検索可能でなければならないからだ。

先日作成したTweetTestのソースを実行してみる。
これを実行するにはtwitter4Jにクラスパスが通っている必要がある。

■javacコマンド
javacコマンドはjavaプログラムのソースをコンパイルするためのコマンドである。

以下のようなオプションがある。
[-d]
クラスファイルを出力するディレクトリを指定する。
デフォルトではカレントディレクトリに出力される。

[-classpath][-cp]
コンパイル時に参照されるクラスファイルを見つけるために使う。
デフォルトはカレントディレクトリ(.)

では、例を見てみよう。

C:\temp\twitter4j
というプロジェクトを用意した。

C:\temp>dir
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 9061-B91A です

 C:\temp のディレクトリ

2012/08/07  08:25    <DIR>          .
2012/08/07  08:25    <DIR>          ..
2012/05/21  22:32    <DIR>          app
2012/07/14  18:11    <DIR>          java
2012/08/07  08:25    <DIR>          twitter4j
               0 個のファイル                   0 バイト
               5 個のディレクトリ  140,101,054,464 バイトの空き領域

ここにある
C:\temp\twitter4j\src\test\TweetTest.java

コンパイルして
C:\temp\twitter4j\classes
以下にクラスファイルを出力したい。

【誤】クラスパスを通さない場合

C:\temp>javac -d twitter4j/classes twitter4j/src/test/TweetTest.java
twitter4j\src\test\TweetTest.java:3: パッケージ twitter4j は存在しません。
import twitter4j.Twitter;

上記のようなエラーが出る

コンパイル時に
C:\temp\twitter4j\lib\twitter4j-core-2.2.6.jar
を参照するようにオプションで指定してみよう。

この場合のコマンドは以下の通り(カレントディレクトリはC:\temp)

C:\temp>javac -classpath twitter4j/lib/twitter4j-core-2.2.6.jar -d twitter4j/classes twitter4j/src/test/TweetTest.java

C:\temp\twitter4j\classes\test以下に
TweetTest.class
が作成された。


javaコマンド
javaコマンドで指定するクラスは必ず、メインメソッドを持つクラスでなければならない。

C:\tempディレクトリから、先ほど作成したclassファイルを実行してみる。

C:\temp>java twitter4j.classes.test.TweetTest

すると、以下のエラーが出る。

Exception in thread "main" java.lang.NoClassDefFoundError: twitter4j/classes/tes
t/TweetTest (wrong name: test/TweetTest)

なぜかというと、必要なクラスパスが通っていないからだ、

では、C:\temp\twitter4j\lib\twitter4j-core-2.2.6.jarにクラスパスを通して実行してみる。

C:\Users\temp>java -classpath C:\temp\twitter4j\classes;C:\temp\twitter4j\
lib\twitter4j-core-2.2.6.jar test.TweetTest
ツイートしたよw

うまく実行できたようだ。

ここで注意すべき点がいくつかある
① -classpathは上書きされる

  • classpathを指定すると、デフォルトが.(カレントディレクトリ)なのが上書きされる。

なので、クラスファイルを見つけるためのパスも通さなければならない。

windowsは「;(セミコロン)」で区切り、linuxは「:(コロン)」で区切る。
環境変数を設定するときもwindowsは「;」で区切るよね。それと同じ。

②パッケージからFQDN
Javaファイルの先頭が

package test;

のようになっているときは、-classpath をtestを格納しているフォルダに通そう。
そして、java test.TweetTest
というように、.(ドット)で区切って実行するんだ。
そうしないと、パッケージ構成のところでエラーが出る。

■自動的に検索されるクラス
\Java\jre1.6.0_31\lib\ext
のように、%JAVA_HOME%/jre/lib/ext以下は「拡張ディレクトリと呼ばれ、
ここに置いたJARファイルやクラスファイルは自動的にCLASSPATHに追加される。

Javaでファイル操作まとめ(FileInputStream編)

JavaではFileクラスを使ってローカルシステムにあるファイルをオブジェクトとして表現できる。
では、そのファイルを読み書きするにはどうしたらいいだろうか。

そこで使うのは、「ストリーム」である。

ストリームとは、データを読み書きできる性質のみを取り出した機能のこと。

Javaではファイルの読み書きをストリームに抽象化して扱う。

"あえて"データを先頭から順に読み出すことと、順に書き込むこと"だけ"しかできないようにしている。

なぜか。

それは、機能を「ストリーム」という抽象的な概念で扱うことで、
対象がファイルだろうとネットワークを経由したデータであろうと、メモリに対する書き込みだろうと、
全てストリームで扱えるからである。

ストリームでファイルを読み書きするのは次の3つの手順で実行される。
1.ストリームを開く
2.データが存在する限り読み取りor書き込みを継続する
3.ストリームを閉じる

package forBlog;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileReadAndWrite {
	public static void main(String[] args){
		File inputFile = null;
		File outputFile = null;
		FileInputStream in = null;
		FileOutputStream out = null;
		
		try{
			inputFile = new File("C:\\temp\\Javaでツイートまとめ.txt");
			outputFile = new File("C:\\temp\\Javaでツイートまとめのコピー.txt");
			in = new FileInputStream(inputFile);
			out = new FileOutputStream(outputFile);
			int c;
			
			//ファイル末尾まで読み込み。最後は-1を返す
			while((c = in.read()) != -1){
				//inputファイルの内容を書き出す。
				out.write(c);
			}
			
		}catch(FileNotFoundException e){
			System.out.println("File not found!");
		}catch(IOException e){
			System.out.println("IO Exception!");
		}finally{
			try{
				if(in != null){
					in.close();
				}
				if(out != null){
					out.close();
				}
			} catch(IOException e){
				System.out.println("Close fail");
				e.printStackTrace();
			}
		}
	}
}

この例を実行すると、
Javaでツイートまとめ.txt
ファイルと全く同じ内容の
Javaでツイートまとめのコピーテキスト.txt
が作成される。

Javaでファイル操作まとめ(Fileクラス編)

Fileクラスを理解することが最も基本となる。
Fileクラスのオブジェクトは「不変」なので、一度パス名を指定してFileオブジェクトを作成すると、
そのオブジェクトによって表される抽象パス名は決して変わらない。

システム依存の抽象パス名を取得する。

package forBlog;

import java.io.File;
import java.io.IOException;

public class CanonicalPath {
	public static void main(String[] args){
		try{
			//コンストラクタ File(String pathname)
			File file = new File("C:\\temp\\Javaでツイートまとめ.txt");
			System.out.println("パス名:" +file.getCanonicalPath());
			
			//システム依存のファイルセパレータを取得
			String fs = System.getProperty("file.separator");
			System.out.println("システム依存のセパレータ:"+fs);
		}catch(IOException e){
			e.printStackTrace();
		}
	}
}

結果は次の通り。

パス名:C:\temp\Javaでツイートまとめ.txt
システム依存のセパレータ:\

windowsだったら「\」
linuxだったら[/」が表示される。


Fileクラスでもう少し遊んでみる。

Fileクラスが示すのがディレクトリなのか、ファイルなのか調べるとき

package forBlog;

import java.io.File;
import java.io.IOException;

public class CanonicalPath {
	public static void main(String[] args){
		//コンストラクタ File(String pathname)
		File file = new File("C:\\temp\\Javaでツイートまとめ.txt");
		
		//存在するか確かめる
		if(file.exists()){
			System.out.println("Fileオブジェクトは存在します");
		}
		
		//ディレクトリなのか確かめる
		if(file.isDirectory()){
			System.out.println("Fileオブジェクトはディレクトリを参照しています。");
		}
		
		//ファイルなのか確かめる
		if(file.isFile()){
			System.out.println("Fileオブジェクトはファイルを参照しています");
		}
		
		//ファイル名をhogehoge.txt変更する
		File hogeFile = new File("C:\\temp\\hogehoge.txt");
		file.renameTo(hogeFile);
		//file.getName()メソッドでファイル名を取得する。
		//最初に指定したオブジェクトの名前が表示されるので注意
		System.out.println("ファイル名は "+file.getName()+" です");
		
	}
}

繰り返すが、Fileクラスのオブジェクトは「不変」である。
実際PCのフォルダにあるファイル名は変更されるが、file.getName()で取得できるファイル名は変わらない。

結果

Fileオブジェクトは存在します
Fileオブジェクトはファイルを参照しています
ファイル名は Javaでツイートまとめ.txt です

超簡単!Javaでツイートbotを作る手順まとめ

Javabotを作成しようと思い色々と調べてみたのだが、思ったよりずっと簡単であった。
しかし、やってみると簡単でも外から見ると難しそうに見えるかもしれない。
そんな誤解を解くために、手順を解説する。

あなたがツイートするまで1時間もかからないだろう。


【準備編】
■Developper登録
Javaでツイートするためには準備が必要だ。
まずTwitterにDevelopper登録する必要がある。
そして、
・Consumer key
・Consumer secret
Access token
Access token secret
という4つのパスワード的なものを手に入れる必要があるんだ。

https://dev.twitter.com/

「Create an app」をクリック

ページの内容はこんな感じ。
[Name]
君が作るアプリケーションの名前を教えてくれよ。
32文字以内でね。

[Description]
君のアプリの説明をしてくれないか?
10文字から200文字以内でね。

[Website]
ネットにつないでれば見れるような君のホームページを教えてくれないか?
そのホームページを見ればもっと君のアプリがわかるようなホームページのURLを頼む。
省略しないで書いてくれよ。もし君がホームページを持ってなかったら、とりあえずyahooやgoogleのURLを書いてね。
でも、あとで変更するのを忘れないように。

[callback URL]
認証に成功したらどこに結果を返せばいい?
とりあえずyahooやgoogleでもいいよ。

こんな感じだ。
f:id:go_my_own_way:20120805151817p:plain

上記を入力したら、

次に
・Consumer key
・Consumer secret

というのが出てくるから、それをしっかりメモしておこう。

その後、同じページの下の方にある
「Your access token」を見て欲しい。
ツイッターアプリを作るにはアクセストークンが必要だ。

Create my access tokenをクリック!

すると、
Access token
Access token secret
という情報が出てくると思う。
これをメモすれば準備は完了だ!

もし君が「Read-only」でいいならね。

自動でツイートしたいなら、まだやることはある。

Access level

というのがあるけど、これがRead-Onlyだと、読んで字のごとく読むことしかできないんだ。
君が僕のブログを読むだけなようにね。


書き込みたいと、思わないか?

Ok,書き込もう。

上にある「setting」をクリックしよう。
そこに

「Application Type」があるから、

このAccess

「Read, Write and Access direct messages」

にしよう。

で、一番下にある
Update this Twitter application's settingsをクリックするんだ。

次に、もう一度「Details」タブを選択。

一番下の、Recreate my access tokenをクリックしよう。

すると、Access tokenとAccess token secretがもう一度表示されるはずだ。

これをメモしておこう。

繰り返すけど、必要なのは4つ。
・Consumer key
・Consumer secret
Access token
Access token secret

これらを必ずメモしておくこと。



【Twitter4J】
次にTwitter4JというツイートするためのAPIを手に入れよう。
以下のサイトで手に入れることができる。
http://twitter4j.org/ja/index.html

ダウンロードして、中にあるjarのパスを通すんだ。

何?パスの通し方がわからない?

君は新人の時の俺みたいなやつだな。

eclipseでいいかな?

まず、プロジェクトを選んで、右クリックしてくれ。

次にプロパティーを選ぶ。

Javaのビルド・パスを選ぶ。

ライブラリータブを選択して、「外部Jarの追加」だ。

そこにさっきダウンロードしたzipの中にあるtwitter4j-code-***を選んでくれればいい。

【ツイート編】

あとは、下記のコードを書いて、実行するだけである。
ワイルドなほど簡単だ。ほぼコピペだけでいい。

package test;

import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;

public class TweetTest {
	private static final String consumerKey = "ここにconsumer keyをコピペ!";
	private static final String consumerSecret = "ここにconsumer secretをコピペ!";
	private static final String accessToken = "ここにaccess tokenをコピペ!";
	private static final String accessTokenSecret = "ここにaccess token secretをコピペ!";
	//【重要】まずはこの内容でツイートしてみよう!
	private static final String tweet = "このブログ超面白いww→http://hogefuga.com/";
	
	public static void main(String[] args){
		//Twitterオブジェクトを参照する。Factoryパターンだ!
		Twitter twitter = new TwitterFactory().getInstance();
		twitter.setOAuthConsumer(consumerKey, consumerSecret);
		twitter.setOAuthAccessToken(new AccessToken(accessToken,accessTokenSecret));
		try{
			twitter.updateStatus(tweet);
			System.out.println("ツイートしたよw");
		} catch(TwitterException e){
			System.err.println("ツイート失敗"+e.getMessage());
		}
		
	}
}

twitterを確認してみよう。

「このブログ超面白いww→http://hoge.hoge.fuga.com/」

というツイートができているはずだ。



あとは、
・フォローしてくれた人に対してまとめてフォロー返しをしたり、
・タイムラインを取得したり
・キーワード検索したり、
そんな操作をちゃんと勉強したいならこの本を読みましょう。

Javaコレクションのまとめ

package forBlog;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class SampleList {
	public static void main(String[] args){
		
		//コレクションの要素の型には参照型を指定する。基本型は指定できない!
		List<Integer> list = new ArrayList<Integer>();
		
		for(int i=10; i>0;i--){
			list.add(i);
		}
		
		//リストの要素を取り出す
		for(int i=0; i<list.size();i++){
			System.out.println("list.size():"+ list.get(i));
		}
		
		//リストの要素を取り出す
		for(int i: list){
			System.out.println("拡張型for文:"+i);
		}
		
		//イテレーション
		for(Iterator<Integer> it = list.iterator();it.hasNext();){
			Integer n = it.next();
			System.out.println("Iterator:"+n);
		}
		
		
		//リストの要素の検索
		//ソートしてからじゃないと検索できない
		Collections.sort(list);
		for(int i: list){
			System.out.println(i);
		}
		
		
		int found_index = Collections.binarySearch(list,5,null);
		System.out.println("該当のインデックスは:"+found_index);
		
		//見つからない場合
		int not_found_index = Collections.binarySearch(list,20,null);
		System.out.println("見つからない:"+not_found_index);
		
		
		
	}
}


イテレータとは
繰り返し処理を抽象化したオブジェクトのこと。

Iteratorインターフェースがもつ3つのメソッド
boolean hasNext(); 次に取り出す要素があるか調べる
E next();  まだ取り出していない次の要素を取り出す
void remove();  nextで取り出した要素をコレクションから削除する

以下の例を見てみる

		//イテレーション
		for(Iterator<Integer> it = list.iterator();it.hasNext();){
			Integer n = it.next();
			System.out.println("Iterator:"+n);
		}

it.hasNext()で次の要素があればtrueとなって、for文を継続する。
it.nextで、次の要素を取り出す



拡張型for文と従来型for文

		//リストの要素を取り出す
		for(int i=0; i<list.size();i++){
			System.out.println("list.size():"+ list.get(i));
		}
		
		//リストの要素を取り出す
		for(int i: list){
			System.out.println("拡張型for文:"+i);
		}

どっちも同じことである。
順番に要素を取り出している。


要素をソートする

		Collections.sort(list);
		for(int i: list){
			System.out.println(i);
		}

検索前には必ずソートが必要なので注意。
こうすると辞書順、数字順に並べ替えられる。

		int found_index = Collections.binarySearch(list,5,null);
		System.out.println("該当のインデックスは:"+found_index);
		
		//見つからない場合
		int not_found_index = Collections.binarySearch(list,20,null);
		System.out.println("見つからない:"+not_found_index);

イナリサーチを使えば、探している要素のインデックスを取得できる。


一番上のソースを実行した結果は以下の通り

list.size():10
list.size():9
list.size():8
list.size():7
list.size():6
list.size():5
list.size():4
list.size():3
list.size():2
list.size():1
拡張型for文:10
拡張型for文:9
拡張型for文:8
拡張型for文:7
拡張型for文:6
拡張型for文:5
拡張型for文:4
拡張型for文:3
拡張型for文:2
拡張型for文:1
Iterator:10
Iterator:9
Iterator:8
Iterator:7
Iterator:6
Iterator:5
Iterator:4
Iterator:3
Iterator:2
Iterator:1
1
2
3
4
5
6
7
8
9
10
該当のインデックスは:4
見つからない:-11

ブログにツイートボタンを表示する方法

ツイートボタンを表示する方法
下記のJavaScriptをブログの記事に書くだけ。

<a href="http://twitter.com/share" class="twitter-share-button">Tweet</a>
<script type="text/javascript" src="http://platform.twitter.com/widgets.js">
</script>

こうなる↓

ツイートボタンを英語で表示した場合は

<a href="http://twitter.com/share" class="twitter-share-button" data-lang="en">Tweet</a>
<script type="text/javascript" src="http://platform.twitter.com/widgets.js">
</script>

こうなる↓

PHPでMySQLにSELECTめも。 

<form>タグの意味
<form action="データの送信先" method="送信方法">

	<input>タグなど記述する

</form>

属性の意味
action データを送信する先のプログラムを指定
method データを送信する方法を指定。postかget

<input>タグの意味
<input type="ボタンの種類" name="データを識別する名前" size="サイズ" 
value="表示する文字" "デフォルト値">

属性の意味
type  部品の種類を指定する文字列
name  データを識別する名前を設定。受信する側はこのnameを見てデータを判断する
size  テキストボックスの幅
value ボタンなどで文字列が表示される場合、その文字列を設定する


PHPの基礎

HTTPでデータを受信する
method="post"で送信されてきたデータが入る変数
$_POST["データを識別する名前"]

method="get"なら
$_GET["データを識別する名前"]


POSTとGETの違い

POST
データはURLにつけない
テキストとバイナリ両方送信可能

GET
URLにデータをつけて送る
送信できるデータはテキストのみ

MySQLに接続する

mysql_connect(サーバ名,ユーザ名,パスワード);

接続に成功するとデータベースの接続値を返す。
失敗するとFALSEを返す

データベースを指定する

mysql_select_db(データベース名,データベースの接続の値)

SQLを発行する関数

mysql_query(SQL文);

PHPMySQLにつないでデータを送信する例

<?php

$s=mysql_connect("localhost","test1","test1") or die("失敗");

print "接続に成功しました";

mysql_select_db("db1",$s);

mysql_query("INSERT INTO test1(title,author) VALUES('なると','岸本')");

mysql_close($s);

?>

SELECTした結果を表示する。

以下のデータを表示する

mysql> SELECT * FROM test1;
+----+----------------+------------+------------------------+------+
| id | title          | author     | sentence               | date |
+----+----------------+------------+------------------------+------+
|  1 | ONE PIECE      | 尾田栄一郎 | 海賊王に おれはなる! | NULL |
|  2 | HUNTER HUNTER  | 冨樫義博   | カイトは生きてる!     | NULL |
|  3 | ドラゴンボール | 鳥山明     | オラわくわくすっぞ!   | NULL |
|  4 | ダイの大冒険   | 忘れた     | ポップ~!!            | NULL |
|  5 | Is             | NULL       | NULL                   | NULL |
|  6 | NULL           | 名無しさん | 奇遇ですね             | NULL |
|  7 | なると         | 岸本       | NULL                   | NULL |
+----+----------------+------------+------------------------+------+
7 rows in set (0.00 sec)

PHPでSELECTした結果を表示する。

<?php

$s=mysql_connect("localhost","test1","test1") or die("失敗");

print "接続成功<br>";

mysql_select_db("db1",$s);

$re=mysql_query("SELECT * FROM test1");


while($kekka=mysql_fetch_array($re)){
	print $kekka[0];
	print ":";
	print $kekka[1];
	print ":";
	print $kekka[2];
	print ":";
	print $kekka[3];
	print ":";
	print $kekka[4];
	print "<br>";
}


mysql_close($s);

?>

プラウザの表示結果

接続成功
1:ONE PIECE:尾田栄一郎:海賊王に おれはなる!:
2:HUNTER HUNTER:冨樫義博:カイトは生きてる!:
3:ドラゴンボール:鳥山明:オラわくわくすっぞ!:
4:ダイの大冒険:忘れた:ポップ~!!:
5:Is:::
6::名無しさん:奇遇ですね:
7:なると:岸本::

ALTER TABLEいろいろ。

以下のテーブルを色々いじってみる。

mysql> desc test1;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| title    | varchar(100) | YES  |     | NULL    |                |
| author   | varchar(100) | YES  |     | NULL    |                |
| sentence | text         | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
4 rows in set (0.03 sec)


カラムのデータ型を変更する

構文
ALTER TABLE テーブル名 MODIFY カラム名 データ型;


ALTER TABLE test1 MODIFY author VARCHAR(200);

mysql> desc test1;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| title    | varchar(100) | YES  |     | NULL    |                |
| author   | varchar(200) | YES  |     | NULL    |                |
| sentence | text         | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
4 rows in set (0.03 sec)

このように変更が確認できる。

mysql> desc test1;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| title    | varchar(100) | YES  |     | NULL    |                |
| author   | varchar(100) | YES  |     | NULL    |                |
| sentence | text         | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
4 rows in set (0.03 sec)

このテーブルに日付カラムを追加してみる。

構文
ALTER TABLE テーブル名 ADD カラム名 データ型;

mysql> ALTER TABLE test1 ADD hizuke DATE;
mysql> desc test1;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| title    | varchar(100) | YES  |     | NULL    |                |
| author   | varchar(200) | YES  |     | NULL    |                |
| sentence | text         | YES  |     | NULL    |                |
| hizuke   | date         | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
5 rows in set (0.03 sec)

カラム名を変更する
ALTER TABLE テーブル名 CHANGE 変更前カラム名 変更後カラム名 変更後データ型;

先ほど作った「hizuke」カラムを「date」に変更してみる。

mysql> ALTER TABLE test1 CHANGE hizuke date DATE;

mysql> desc test1;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| title    | varchar(100) | YES  |     | NULL    |                |
| author   | varchar(200) | YES  |     | NULL    |                |
| sentence | text         | YES  |     | NULL    |                |
| date     | date         | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
5 rows in set (0.02 sec)

カラムを削除する

まずはいらないカラムを追加してみる。
mysql> ALTER TABLE test1 ADD hoge VARCHAR(100);

mysql> desc test1;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| title    | varchar(100) | YES  |     | NULL    |                |
| author   | varchar(200) | YES  |     | NULL    |                |
| sentence | text         | YES  |     | NULL    |                |
| date     | date         | YES  |     | NULL    |                |
| hoge     | varchar(100) | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
6 rows in set (0.03 sec)

次にhogeカラムを削除する。
mysql> ALTER TABLE test1 DROP hoge;

mysql> desc test1;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment |
| title    | varchar(100) | YES  |     | NULL    |                |
| author   | varchar(200) | YES  |     | NULL    |                |
| sentence | text         | YES  |     | NULL    |                |
| date     | date         | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
5 rows in set (0.01 sec)

見事にhogeが削除された。

PreparedStatementの使い方の例

SQLに指定する値を動的に設定したい場合などに使えるPreparedStatementの例。
WHERE文の指定に変数を使うときなどに使おう。

package jdbc;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import com.mysql.jdbc.Connection;


public class MySQLCount {
	private static String jdbc = null;
	private static String db = null;
	private static String host = null;
	private static String user = null;
	private static String pass = null;
	private static String driver = null;
	private static String sql = null;
	
	public static void main(String[] args){
		Properties prop = new Properties();
		FileInputStream fis = null;
		try {
			fis = new FileInputStream("properties.txt");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			System.err.println("プロパティファイルが見つかりません");
		}
		
		try {
			prop.load(fis);
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("プロパティファイルを開けません");
		}
		
		//プロパティの読み込み
		jdbc = prop.getProperty("jdbc");
		db = prop.getProperty("db");
		host = prop.getProperty("host");
		user = prop.getProperty("user");
		pass = prop.getProperty("pass");
		//sql = prop.getProperty("sql");
		
		//ドライバの作成
		driver = "jdbc:"+jdbc+"://"+host+"/"+db+"?user="+user+"&"+"password="+pass;
		Connection con = null;
		try{
			con = (Connection) DriverManager.getConnection(driver);
			//Statement stmt = (Statement) con.createStatement();
			/**
			ResultSet rs = stmt.executeQuery("select COUNT(*) cnt FROM test1");
			rs.next();
			System.out.println(rs.getInt("cnt"));
			*/
			
			/**
			//insert文を発行
			String sql = "INSERT INTO test1(id,title,author,sentence) VALUES(4,\'ダイの大冒険\',\'忘れた\',\'ポップ~!!\')";
			//executeQuery()メソッドを使うとエラーが発生する
			stmt.execute(sql);
			//stmt.executeQuery(sql);
			*/
			
			//PreparedStatementの使い方
			String sql = "SELECT * FROM test1 WHERE id = ?";
			PreparedStatement stmt = con.prepareStatement(sql);
			stmt.setInt(1,2);
			ResultSet rs = stmt.executeQuery();
			rs.next();
			System.out.println(rs.getString("title"));
			
		}catch(SQLException ex){
			System.out.println("SQLException: " + ex.getMessage());
			System.out.println("SQLState: " + ex.getSQLState());
			System.out.println("VendorError: " + ex.getErrorCode());
		}finally{
			if(con != null){
				try{
					con.close();
				}catch(SQLException e){
					e.printStackTrace();
				}
			}
		}
		
	}
}


この「?」をバインドパラメータという。
この「?」を指定した値で置き換えてSQLを実行する。

例えば
stmt.setInt(1,2)は

1番めの[?]を「2」に置き換えるという意味である。

int id = 3;
stmt.setInt(1,id)なら、1番めの[?]は「3」に置き換えられる。

javaからMySQLにINSERTする。

MySQLにINSERTのSQLを発行するjavaのコードの例

package jdbc;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import com.mysql.jdbc.Connection;
import com.mysql.jdbc.Statement;

public class MySQLCount {
	private static String jdbc = null;
	private static String db = null;
	private static String host = null;
	private static String user = null;
	private static String pass = null;
	private static String driver = null;
	private static String sql = null;
	
	public static void main(String[] args){
		Properties prop = new Properties();
		FileInputStream fis = null;
		try {
			fis = new FileInputStream("properties.txt");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			System.err.println("プロパティファイルが見つかりません");
		}
		
		try {
			prop.load(fis);
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("プロパティファイルを開けません");
		}
		
		//プロパティの読み込み
		jdbc = prop.getProperty("jdbc");
		db = prop.getProperty("db");
		host = prop.getProperty("host");
		user = prop.getProperty("user");
		pass = prop.getProperty("pass");
		sql = prop.getProperty("sql");
		
		//ドライバの作成
		driver = "jdbc:"+jdbc+"://"+host+"/"+db+"?user="+user+"&"+"password="+pass;
		Connection con = null;
		try{
			con = (Connection) DriverManager.getConnection(driver);
			Statement stmt = (Statement) con.createStatement();
			
			ResultSet rs = stmt.executeQuery("select COUNT(*) cnt FROM test1");
			rs.next();
			System.out.println(rs.getInt("cnt"));
			
			
			
			//insert文を発行
			String sql = "INSERT INTO test1(id,title,author,sentence) VALUES(4,\'ダイの大冒険\',\'忘れた\',\'ポップ~!!\')";
			
			//executeQuery()メソッドを使うとエラーが発生する
			stmt.execute(sql);
			//stmt.executeQuery(sql);
			
		}catch(SQLException ex){
			System.out.println("SQLException: " + ex.getMessage());
			System.out.println("SQLState: " + ex.getSQLState());
			System.out.println("VendorError: " + ex.getErrorCode());
		}finally{
			if(con != null){
				try{
					con.close();
				}catch(SQLException e){
					e.printStackTrace();
				}
			}
		}
		
	}
}

ポイントはここ

String sql = "INSERT INTO test1(id,title,author,sentence) VALUES(4,\'ダイの大冒険\',\'忘れた\',\'ポップ~!!\')";
stmt.execute(sql);
//stmt.executeQuery(sql);

ちゃんと「'(シングルクオテーション)」をエスケープすること。
executeQueryではなく、executeでSQLを発行すること。

executeQueryだと以下のようなエラーが出る

SQLException: Can not issue data manipulation statements with executeQuery().
SQLState: S1009
VendorError: 0

JavaからMySQLにクエリを投げて、データ数をカウントする。

結果行の行数を数えるコードの例

package jdbc;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import com.mysql.jdbc.Connection;
import com.mysql.jdbc.Statement;

public class MySQLCount {
	private static String jdbc = null;
	private static String db = null;
	private static String host = null;
	private static String user = null;
	private static String pass = null;
	private static String driver = null;
	private static String sql = null;
	
	public static void main(String[] args){
		Properties prop = new Properties();
		FileInputStream fis = null;
		try {
			fis = new FileInputStream("properties.txt");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			System.err.println("プロパティファイルが見つかりません");
		}
		
		try {
			prop.load(fis);
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("プロパティファイルを開けません");
		}
		
		//プロパティの読み込み
		jdbc = prop.getProperty("jdbc");
		db = prop.getProperty("db");
		host = prop.getProperty("host");
		user = prop.getProperty("user");
		pass = prop.getProperty("pass");
		sql = prop.getProperty("sql");
		
		//ドライバの作成
		driver = "jdbc:"+jdbc+"://"+host+"/"+db+"?user="+user+"&"+"password="+pass;
		Connection con = null;
		try{
			con = (Connection) DriverManager.getConnection(driver);
			Statement stmt = (Statement) con.createStatement();
			
			ResultSet rs = stmt.executeQuery("select COUNT(*) cnt FROM test1");
			rs.next();
			System.out.println(rs.getInt("cnt"));
			
		}catch(SQLException ex){
			System.out.println("SQLException: " + ex.getMessage());
			System.out.println("SQLState: " + ex.getSQLState());
			System.out.println("VendorError: " + ex.getErrorCode());
		}finally{
			if(con != null){
				try{
					con.close();
				}catch(SQLException e){
					e.printStackTrace();
				}
			}
		}
		
	}
}

このコードを実行すると結果の行数がコンソールに表示される。

なお、プロパティは別のファイルに外出しにしている。

なぜArrayListの参照変数はList型で宣言しなければならないのか

掲題の疑問について考えてみよう。
ついでに一度リストについてまとめる。

package sample2;

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

public class ListSample {
	public static void main(String[] args){
		
		//①
		ArrayList<String> list = new ArrayList<String>();
		List<Integer> list2 = new ArrayList<Integer>();
		
		//②コンパイルエラー
		//List<int> list3 = new ArrayList<int>();
		
		for(int i = 0; i < 10; i++){
			list2.add(i);
		}
		//list2:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
		System.out.println("list2:"+list2);
		
		//size():10 サイズ
		System.out.println("size():"+list2.size());
		
		//get():3 要素取得
		System.out.println("get():"+list2.get(3));
		
		//contains():true 含まれているか
		System.out.println("contains():"+list2.contains(5));
	}
}

②コレクションの要素は参照型しか指定できない。基本型だとコンパイルエラーになる


①について

ArrayList<String> list = new ArrayList<String>
List<String> list = new ArrayList<String>

この2つの参照変数の宣言の違いは何か。

それは、Listがインタフェース型でArrayListが具象クラスである。

一般的に下の
List list = new ArrayList
が望ましいとされる。

理由は、インターフェースは変更されにくいものだからだ。
具象クラスは実装を持つため変化しやすい。
ソースコードは変更の影響を受けにくいものが望ましい。

もっとわかりやすい例で見てみよう。

package sample2;

public interface Oya {
	void sayHello();
	void sayGoodBye();
}

このインターフェースを継承する子クラス

package sample2;

public class Ko implements Oya{

	private Ko(){}
	@Override
	public void sayHello() {
		// TODO 自動生成されたメソッド・スタブ
		System.out.println("Hello!");
	}

	@Override
	public void sayGoodBye() {
		// TODO 自動生成されたメソッド・スタブ
		System.out.println("GoodBye!");
	}
	
	//ファクトリメソッド風に
	static Ko getInstance(){
		Ko ko = new Ko();
		return ko;
	}

}


さて、以下のコードを実行した結果はどうなるだろうか?

package sample2;

public class Main2 {
	public static void main(String[] args){
		//インターフェース型として宣言している。
		Oya ko = Ko.getInstance();
		ko.sayHello();
		ko.sayGoodBye();
	}
}


結果

Hello!
GoodBye!

インターフェース型として宣言すれば、具象クラスの振る舞いを利用しつつも、
変更に影響を受けにくいコードを書くことができるのである。

ファクトリパターン超基礎

ファクトリパターンとは
オブジェクト生成を1つの役割とみなし、その機能を分離することである。

具体的には、ファクトリパターンでは、newによるオブジェクト生成をメソッド内に隠蔽する。

package sample2;

public class Main {
	public static void main(String[] args){
		
		//①
		Book book = Book.getInstance();
		
		//②
		//Book book = new Book();
		
		
		book.setName("吾輩は猫である");
		book.sayName();
	}
}

class Book {
	private String name;
	private String author;
	
	//③
	private Book(){}
	
	//④
	static Book getInstance() {
		Book book = new Book();
		return book;
	}
	
	public void setName(String name){
		this.name = name;
	}
	
	public void setAuthor(String author){
		this.author = author;
	}
	
	public void sayName(){
		System.out.println("book name is " + name);
	}
}

①getInstanceはstaticメソッドであるため、インスタンス化しなくても使用可能

コンストラクタの可視性が「private」であるため、Bookクラスの外ではインスタンス化できない

コンストラクタの可視性をprivateにすることで、クラス外からインスタンス化できなくする

④staticメソッドにすることで、利用する側がインスタンス化しなくても使用できるようにする

実行結果

book name is 吾輩は猫である