□技術メモ - Java ※管理人の個人的な技術メモです。サンプルソースを実行した結果について 管理人はいかなる責任も負いかねますのでご自身の責任でお試しください。 ----------------------------------------------------------- ■用語 ○Warファイル ・Web Application Archiveの略。 Webアプリケーションで使用されるファイルをコンテキストごとにまとめてZip圧縮したもの。 JarのコマンドでWarファイルを作成する。 ・Tomcatが起動している時にWarファイルを $CATALINA_HOME\webapps に配置すると 自動的にファイルが展開される。 ○Tile ・Apacheが提供するフレームワーク。Spring、Strutsで使用可能。 同じテンプレートファイルを何回も再利用可能できる。 例えば、ヘッダ、フッタ、メニュー、コンテンツで構成されているページの コンテンツだけ違うページを多数作成する場合は、 ヘッダ、フッタ、メニューをTileの機能で作成することができる。 ○XML parser ・XMLはテキスト形式で記述されているが、これをアプリケーションが使用しやすいように変換する。 同時にXMLの文法、フォーマット等が正しいかを検証する。 一般的にはXMLのテキストファイルを直接読むより、XMLパーサでデータに変換した方が効率がよい。 IBM社「XML Parser for Java」や、Microsoft社「MSXML」などがある。 ----------------------------------------------------------- ○フォントを指定した場合の文字幅の取得 BufferedImage bi=new BufferedImage(800,600,BufferedImage.TYPE_INT_ARGB); //適当なサイズで表示領域を定義する Graphics g=bi.getGraphics(); Font font=new Font("MS P明朝",0,18); FontMetrics fm=g.getFontMetrics(font); //FontMetricsが取れれば文字幅は取れる。 文字幅=fm.stringWidth(str); //文字幅の取得 ※文字を縦書きにするには文字列の中心線を決めて、文字幅の半分だけ左に動かせばよい。 ----------------------------------------------------------- ○ソケット通信 ・サーバ側を先に起動して待ち合わせに入る。 ・クライアントから接続に行って接続が確立するとサーバからメッセージ送信して、 クライアントはメッセージ表示すると終了する。 ・サーバ側は次の待ち合わせに入り、次の接続を待つ。 ・サーバ側はCtrl + Cで停止する。 ・下記の仕組みは、クライアントからリクエストがあったらサーバが要求された情報を返信する、という仕組みなので、 天気予報や、チケットの販売状況などの情報提供サービス等に応用することが考えられる。 ---- tstSvr.java import java.io.*; import java.net.*; public class tstSvr{ public static void main(String[] arg) { try{ int i=0; ServerSocket serverSocket=new ServerSocket(5000); //ポート番号5000 while(true){ System.out.println("\nサーバ側待ち合わせ開始"); Socket socket=serverSocket.accept(); System.out.println("Clientからの接続あり"); PrintWriter wt=new PrintWriter(socket.getOutputStream()); String msg="こんにちは、サーバです"; //メッセージ送信 wt.println(msg); i++; String scnt=i + "回目 "; System.out.println(scnt + "以下のメッセージを送信しました。:" + msg); wt.close(); } } catch(IOException e) { System.out.println(e); } } } ---- tstClient.java import java.io.*; import java.net.*; public class tstClient{ public static void main(String[] arg) { try{ Socket socket=new Socket("192.168.0.1", 5000); //Serverのアドレス InputStreamReader isr=new InputStreamReader(socket.getInputStream()); BufferedReader rd=new BufferedReader(isr); String msg=rd.readLine(); //受信内容を表示 System.out.println(msg); rd.close(); } catch(IOException e) { System.out.println(e); } } } ----------------------------------------------------------- ○GC(ガーベージコレクション)について ・Javaはメモリの解放はGC(ガーベージコレクション)で行われる。 ・Javaにおけるヒープ領域は、寿命の短いメモリから、new, old, permanentに分かれる。 ・通常のGC(Scavenge GC)ではnew のメモリが解放される。 これでもメモリの解放が追いつかない場合はfullGCによりold, permanentのメモリを解放する。 ・fullGCに掛かる時間は人間の感覚では短くても、 CPUにとってはエラーが発生するほど長いかもしれない。 fullGCが頻発するとメモリ関連のエラーが発生しやすくなる。 ・GC, fullGCがどの程度発生しているかはGCのログを出力すればわかる。 Javaの起動オプションでは以下を組み合わせることが多い。 -verbose:gc (一般的なGC情報) -Xloggc:filename (ファイル出力先を指定) -XX:+PrintGCDetails (new,oldの詳細情報を出力) ・new, old, permanentのサイズは起動オプションで設定可能。 (例) -Xms1024m -Xmx1024m -XX:NewSize=128m -XX:MaxNewSize=128m -XX:PermSize=256m -XX:MaxPermSize=256m 意味は左から以下の通り。 ヒープ全体 の初期値、同最大値 New領域 の初期値、同最大値 Permanent領域 の初期値、同最大値 ・最初のfullGCでエラーが発生することもあるし、メモリ不足ぎりぎりまで頻発することもある。 いずれにしてもfullGCは回避すべき。そのためにはできるだけnew領域を使用して、 安定してメモリ解放されるようにする。 ・fullGCを回避するコツとして、ローカル変数の使いまわしを避けるということがある。 loopカウンタなどは使うときのみ宣言するようにする。 loopカウンタをメソッドの最初に宣言してこれを使いまわすと、 new領域にいたメモリがoldに移されてしまうのでこれは避けたい。 ・データの処理の単位もデータ全件をまとめて流していくと、 件数が多い場合にメモリエラーになる。 できるだけ1件流しにする方が良い。 ---------------------------------------------------------- ○外部APPの実行 ・try catch でエラーハンドリングすること。 Process proc = Runtime.getRuntime().exec(command); proc.waitFor(); //終了待ち ----------------------------------------------------------- ○Javaコンパイラ エスケープ文字の処理 ・以下の3段階で処理される。 (1) Unicodeのエスケープ文字表記の展開(構文解析前に展開される) \u で始まる文字列はunicodeに展開しようとする。解釈できない場合は「Unicodeエスケープが不正です」エラーになる。 ※注意 : \uで始まるフォルダ名がコード(コメントを含む)に存在するとエラーになるので注意。 ちなみに管理人は「C:\uploadsフォルダに配置」等と書いたコメント行がエラーになって驚いたことがある。 さらにフォルダ名が \uFFFF などの名前だと、間違った解釈をしてそのまま進むのでさらに注意が必要。 (2) \n, \t, \b などのエスケープ文字の展開(構文解析時に実施) 文字列リテラル(直書きの定数など)に対してのみ行われる。 文字列を加工した結果が偶然エスケープ文字になった場合などは、置き換えは発生しない。 ・Javaで使用できるエスケープシーケンスは以下の通り。 \n \t \" \' \\ \r キャリッジリターン(復帰) \b バックスペース \f 改ページ ¥uFFFF ユニコード文字(16進数) ¥xxx 文字コード(8進数) (3) 関数内の展開 フォーマット付き関数(printfなど)でエスケープ文字を展開する。 展開するかどうかは関数別の仕様に従うことに注意。 つまり、printf()では %n は改行になるが、print()では改行にはならない。 ・環境によって改行コードは違うことがる。改行コードの取得は以下の通り。 System.getProperty("line.separator"); ----------------------------------------------------------- ○総称型 Generic TreeMap map = new TreeMap(); //コンパイル時に警告がでる TreeMap map = new TreeMap(); ----------------------------------------------------------- ○Objectから文字列への変換 str = String.ValueOf(object); ・LongやIntegerなら以下の通り。 str = Integer.toString(intWork); ※違いとしては、String.valueOfはオーバーロードが多いのに対して、 Integer.toStringは引数の型チェックがあるので安全ということらしい。 ※用語:オーバーロードは、同じメソッド名で違う型の引数を使用できること。 オーバーライドは、継承先で同じ名前のメソッドを定義すること。 ----------------------------------------------------------- ○3項演算子 条件式 ? 式1 : 式2 -------- if (dVal<1.5) { iVal=1; } else { iVal=2; } --------上記を書き換えるとこうなる iVal = (dVal<1.5) ? 1 : 2; ----------------------------------------------------------- ○文字列の数値判定 ・try catch を使う方法と、Apache.commonsを使用する方法がある。 ・正規表現でも判断できるかもしれない。 (1) try chach で判定を行う ・try catch の場合は同じ処理を2回行いがちなのが気になる。 気にしなければいいのかもしれないが、判定用メソッドと変換用メソッドが違う場合、 変換時にエラーがでることが全くないとは言えないわけで、個人的には1つのメソッドで判定と変換を行いたい。 ・引数に参照渡しをしても値を返せない場合があることに注意。 文字列の場合、Stringは値が変わった時点で別のインスタンスになってしまうので値を返せないが、StringBufferを使えば返せる。 Integerも同様に参照渡しで値を返せないので、この例では戻り値として返している。変換できなかった場合はnullを返すとしている。 Test.java -------- public class Test { public static void main (String[] args) { int iWork=0; System.out.println(iWork); Integer ICnv = cnvNumber("123"); if (ICnv != null) { iWork = ICnv.intValue(); System.out.println(iWork); } } // 変換できない場合はnullを返す public static Integer cnvNumber(String sOrg) { Integer iRet=null; try { iRet=Integer.parseInt(sOrg); return iRet; } catch (Exception e) { return iRet; } } } (2) Apache.commonsの使用 ・Apache.commons を使用するには以下のサイトから http://commons.apache.org/index.html → Lang → DownLoad → commons-lang3-3.3.2-bin.zip 解凍して commons-lang3-3.3.2.jar をTomcatのLibに入れておく。 ・以下をimport宣言する。(zip解凍してNumberUtilsに至るまでのフォルダ構成になるようにimportを記述すること) import org.apache.commons.lang3.math.NumberUtils; ・NumberUtilsで判定したならNumberUtilsで変換する。ライブラリが違うなら判定基準が違う可能性がある。 -------- import org.apache.commons.lang3.math.NumberUtils; if (NumberUtils.isNumber(val)) { inum=NumberUtils.toInt(val); //NumberUtilsで判断したなら、NumberUtilsで変換すること。 } -------- ○変数の格納先クラスを参照する ・thisが破棄される前に呼び元となる親クラスを参照する。 ・ダイアログで設定した値を、 ダイアログが破棄される前に呼び元のクラスに格納する場合など public boolean setData(クラス名 pa, String sOrg) { try { pa.sData=sOrg; //他のクラスにアクセスする return true; } catch (Exception e) { return false; //エラー } } } ----------------------------------------------------------- ◎各種変換 ○文字列(10進)→数値 int i = Integer.parseInt(s); //NumberFormatExceptionをthrow ○数値→文字列(10進) ---- String s = String.valueOf(i); //Stringクラスのメソッド ---- String s = Integer.toString(i); //Integerクラスのメソッド ---- Integer Iwk = new Integer(10); //Integerを多用する場合などはこれも良い。 String s = Iwk.toString(); ---- ○文字列(16進)→数値 int i = Integer.parseInt(s, radix); //NumberFormatExceptionをthrowする。第2引数は基数。16進なら16。 ○数値→文字列(16進) String hex=Integer.toHexString(iCode); ○String→char String str = "あいう"; char[] ca=str.toCharArray(); ○char→String char[] ca={'あ','い','う'}; str=String.valueOf(ca); ○日本語→unicode ・String→charに変換して各文字の値を取り出す。 ・JVMの内部コードはunicodeなので問題ないと思われる。 ・getNumericValue() は文字の Unicode数値を負でない整数で返す。 String str = "あいう"; char[] ca=str.toCharArray(); int d0=Character.getNumericValue(ca[0]); int d1=Character.getNumericValue(ca[1]); int d2=Character.getNumericValue(ca[2]); ・ちなみにint→charの変換は明示的なキャストが必要。 int ibuf=80; char cwrk=(char)ibuf; ○unicode→日本語 ・16進なら ￿ 形式の連続を作る。10進ならxは不要。 String strUni = "あいうえぐ"; 表示 : あいうえお ○日本語→文字参照 ・1文字単位で変換すれば文字単位でとれる。変換前と同じならURLエンコードの必要のない文字。 ・World Wide Web Consortium Recommendationでは UTF-8を使用すべきとされている。 http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars String str = URLEncoder.encode("JAVAで開発", "UTF-8"); 表示 : JAVA%E3%81%A7%E9%96%8B%E7%99%BA ○文字参照→日本語 ・URL表記をdecodeして元の日本語に戻せる。 ・World Wide Web Consortium Recommendationでは UTF-8を使用すべきとされている。 http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars String strde = URLEncoder.decode("JAVA%E3%81%A7%E9%96%8B%E7%99%BA", "UTF-8"); 表示 : JAVAで開発 ----------------------------------------------------------- ○interface 書式: (public) interface インターフェース名 extends スーパーインターフェース名, ... { type メソッド名(param, ...); type メンバ変数; } ・宣言時のpublicがない場合は、同じパッケージ内で定義されたクラスからのみアクセスできる。 ・メソッド名には暗黙的に、public, abstract修飾子が付く。 ・メソッド名、メンバ変数には、修飾子transient,volatile,synchronizeを付与できない。private,protectedも付与できない。 ○implements(実装) 書式: (修飾子) class クラス名 extends スーパークラス名 implements インターフェース名, ... { type メソッド名(param, ...); type メンバ変数; } ・継承は1つのクラスからしかできないが、インターフェイスの実装は複数のインターフェイスから可能。 実装は、全てのメソッドを実装する必要がある。abstructにしておいてもよい。 ○interfaceの利点 ・標準化 実装したクラスは振る舞いが統一される。 ・多重継承 クラスの場合は多重継承できない。 ・抽象化 機能を抽象化した状態にしたまま設計に専念できる。 ・疎結合 各コンポーネント間の結合が疎になり、保守、拡張の際に有利。 ・わかり易い例としては、引数の型をinterfaceにしておけばオーバーロードする複数のメソッドを定義しなくても済む、 ということがあるかもしれない。 ----------------------------------------------------------- ○文字コードを指定してテキストファイルを読み込む FileInputStream strm = new FileInputStream(path); InputStreamReader is = new InputStreamReader(strm,"UTF-8"); BufferedReader br = new BufferedReader(is); //1行で書いてもいいかも知れない while( (line=br.readLine()) != null) { System.out.println(line); } br.close(); ----------------------------------------------------------- ○文字コードを指定してテキストファイルを書き込む FileOutputStream strm = new FileOutputStream(path, true); //第2引数がtrueならappend OutputStreamWriter os = new OutputStreamWriter(strm,"windows-31j"); PrintWriter pw = new PrintWriter(os); //1行で書いてもいいかも知れない pw.println("Hello, Java!"); pw.close(); ----------------------------------------------------------- ○substring ・書式は以下の通り。文字の位置は左端を0とする。 substring(開始位置, 捨てる位置); ・文字数がNの場合は以下の記述で全て取得できる。エラーにはならない。 substring(0, N); ----------------------------------------------------------- ○ファイル一覧の取得 File file = new File("D:\\work"); File faList[] = file.listFiles(); List listFile = new ArrayList(); for(int i=0; i