Javaプログラミング・メモ
by K.I
2014/09/xx〜
Index
- Javaのプログラミングの勉強メモ
- いままでは、ほとんどCでプログラムを組んでいたので、Cとの比較になるかも
- Androidで実行することが多いので、Android独自の記述があるかもしれない
- JavaのプログラムはAndroidが初めてなので、取り留めの無いメモになると思う。
- 自分で解ったつもりのことを書いてるけど、間違って理解しているものもあるかも。。
型 | 意味 | 値の範囲 |
boolean | 論理値 | true, false |
| | |
char | 2byteUnicode文字 | \u0000〜\uFFFF |
| | |
byte | 1byte整数 | -128〜127 |
short | 2byte整数 | -32768〜32767 |
int | 4byte整数 | -2147483648〜2147483647 |
long | 8byte整数 | -9223372036854775808〜9223372036854775807 |
| | |
float | 4byte浮動小数点数 | ±(3.4028235E+38〜1.401298E-45) |
double | 8byte浮動小数点数 | ±(4.94065645841246544E-324〜1.79769313486231570E+308) |
- charは、1byteじゃないのか。。
- アーキテクチャに関係なく、型でbyte数が決まっているのは、良い事だと思う
- でも、unsignedが無いのはビックリした。。
- データファイルのフォーマットでは、unsignedのものも多いので、ちょっと気持ち悪いなぁ。
- 同様の機能のクラスを複数作るときは、共通機能を親クラスとして作成して、
- 継承のメリット
- 共通部分を、親クラスとして纏めることができる
- 親クラスと同じ名前のメソッドを定義すると、上書きされて子クラスのメソッドが実行されるので、機能変更が可能
- 親クラス型の変数に、子クラスのインスタンスを代入できる
- その場合、インスタンスの型によって実行時の動作が決まるので、後から子クラスを追加することによる拡張が容易
- 注意点
- 親クラスと同じ名前のフィールドを定義しても、上書きはされない。両方存在することになる。
- Staticなメソッドは上書きされない。
- superを使うと、継承した親クラスのコンストラクタやメソッドの呼出し
- メソッドをoverrideした時に、overrideする前のメソッドを呼ぶことも出来る
- というか、そのような時にsuperを使うことが多い
- それに対して、thisは自分のクラスのメソッドやメンバ変数にアクセスする場合に使われる
- ローカル変数とメンバ変数が同じ変数名の時に、区別するために使う
- finalを定義した時に最終的な値が決まり、以降、変更できない。
- 変数の場合は、代入不可となり、定数的に使える
- メソッドの場合、オーバーライド不可
- クラスの場合は継承不可、当然、メソッドのオーバーライドも出来なくなる
- 拡張できなくなるので、無闇にクラスにfinalを付けない方が良い。と思う。。
- staticメソッドの場合も、インスタンスを生成しなくても使用可能で、
- static final は、クラスで1つだけの定数となる
- staticじゃない変数やメソッドは、インスタンス毎に生成され、
- インスタンス変数、インスタンスメソッドと呼ばれる
- これらは、インスタンスを作らないと存在しない
- クラス名と同じ名称のメソッドで、クラスの初期化処理を行う
- 引数のないコンストラクタは、1行目に引数なしのsuper()が自動的に挿入される
- 引数のあるコンストラクタの場合は、superを記述する必要がある。
- ちなみにsuperは、必ずコンストラクタの最初に記述すること!
- デフォルトコンストラクタ
- コンストラクタを記述しないと、引数のない空のコンストラクタが自動的に作成される。
- 基本的には何もしないが、1行目でスーバークラスの呼出しがされる意味がある
- コンストラクタを1つでも記述すると、デフォルトコンストラクタは作成されない。
- 引数のあるコンストラクタを記述すると、引数のないnewで、nullポインタ例外になるので、引数のないコンストラクタも記述した方が良いかもしれない
- コンストラクタは、自分自身の再帰的な呼出し1は出来ない(たぶん)
- コンストラクタは、例外を投げない限り中断することは出来ない →コンストラクタが実行された時点でインスタンスは作成済み
- クラスのロード時に、1回だけ実行されるので、クラス変数の初期化等を行う
static {
// クラス変数の初期化
}
- インスタンスイニシャライザというのもあるらしい。
- これは、インスタンスの生成の度、コンストラクタの直前に実行される
{
// { }で括るだけ
}
- これは、コンストラクタに記述しても同じなので、あまり使わないかも
- コンストラクタが複数あった時に、共通処理とか記述したら良いのかな?
public class ptxy {
int x, y;
public ptxy(int X, int Y) {
x = X;
y = Y;
}
}
xylist
private LinkedList<ptxy> xylist = new LinkedList<ptxy>();
- LinkedListのアクセスは、iteratorを使う
xylist XY;
Iterator<xylist> i = XY.iterator();
while(i.hasNext()) {
next = i.next();
}
- うーん、この記述方法は、なんか気持ち悪い
- Cのポインタみたいに、Classを直接メンバに記述する方が良いかも。
- 0からシーケンシャルに番号付けされたデータに名前を付けたいという、
enum HJUST {LEFT, CENTER, RIGHT};
enum VJUST {TOP, MIDDLE, BOTTOM};
public enum HJUST {
LEFT(0), CENTER(1), RIGHT(2);
private final int _val;
HJUST(int n) {
this._val = n;
}
public int value() {
return _val;
}
}
public enum VJUST {
TOP(0), MIDDLE(1), BOTTOM(2);
private final int _val;
VJUST(int n) {
this._val = n;
}
public int value() {
return _val;
}
}
使い方は、
HJUST hjust = HJUST.CENTER;
VJUST vjust = VJUST.MIDDLE;
int xxx = vjust.value()*4 + hjust.value();
- 多分、これは例が悪いというかJavaを知らない所為もあると思うんだけど、
- Cは全くチェックされないとは言え、Javaは冗長すぎる気がする
- Cの列挙型は、単に数値定数を定義するだけのものだが、
- 個人的には、enumはこういう単純な使い方が一番多いので。
- Javaでもそういうものがあれば良いのに。。
- enumはコンストラクタを定義できるので、ちょっとしたオブジェクトを定数的に使える
public enum MARK {
dot (0,1, 1,0, 0,-1, -1,0, 0,1),
box (5,5, 5,-5, -5,-5, -5,5, 5,5),
tri (0,5, 5,-5, -5,-5, 0,5);
public xylist xy;
// コンストラクタ
MARK(int... zz) {
this.xy = new xylist(zz);
}
// xyの参照
public xylist xy() {
return this.xy;
}
}
この例では、MARK.xxx.xy()で別々の値を定数的に参照できる
- Javaの可変長引数は、実体として配列が使われる』
- 呼ぶ側では、引数を並べて記述すれば、引数の個数分の配列が渡される
- 配列なので、配列にしてから渡しても構わない
method(1,2,3);
method(4,5,6,7,8);
int[] idata = {10,20,30};
method(idata);
- 注意点
- nullを渡すと、配列なのかデータなのか曖昧なので、明示的にキャストする等が必要になる
- オーバーロードが同じ形になる定義があった場合は、可変長引数じゃない方が優先される
- 可変長引数は、メソッドの最後の引数にしか使えない
- 可変長引数は、C言語のstdarg.hのマクロを使うより、Javaの配列の方が使い易いと思う
1コンストラクタを再帰的に記述しようとして、あれ?でもどうやって中断するんだろと思って調べたら出来ないことがわかった。。
[top]
- Javaの場合は、Class変数の代入は参照のコピーとなる
// Javaの場合
xylist xy1, xy2, xy3, xy4;
:
xy1 = xy2; // 参照のコピー
xy3 = xy4.clone(); // 全てコピー(cloneの記述方法によって異なる)
- 自分で定義したClassの場合、superClassのcloneをpublicでオーバーライドすれば良い
- デフォルトのcloneは、単純にオブジェクトを全部コピーするだけなので、コンストラクタは実行されない。
- 基本的なオブジェクトであれば、デフォルトのcloneでちゃんとコピーされる
- intとか基本プリミティブだけでなく、Integerのようなラッパークラス、それにStringもコピーの対象となる
public class xylist implements Cloneable {
public int x;
public int y;
public xylist next;
public xylist(int X, int Y, xylist NEXT) {
x = X;
y = Y;
next = NEXT;
}
public xylist clone() {
try {
return (xylist) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e.toString());
}
}
}
- デフォルトのcloneはオブジェクトが丸ごと単純にコピーされるが、これをシャローコピー(浅いコピー)という。
- これは、Cの構造体の代入と同じようなコピーとなる。
- でもCの感覚からすると、Stringもコピーされるのはちょっと不思議な感じだ。
- まぁ、JavaのStringは書換えできない(新規に作成して入れ替えるしかない)ので、良いのか。。
- でも、この例のようにクラスの参照(next)のようなメンバ変数がある場合は、単にコピーするだけでは同じものが参照されてしまう。
- これで、参照しているデータも全てコピーされる。これをディープコピー(深いコピー)という。
- Clone可能なように記述されたクラスを継承したクラスも、cloneが使える
public class mylist extends xylist {
public int a;
public String s;
public xylist xy;
public mylist() {
this.a = 0;
this.s = null;
this.xy = null;
}
public mylist(int a, String s, xylist xy) {
this.a = a;
this.s = s;
this.xy = xy;
}
public void set(int a, String s, xylist xy) {
this.a = a;
this.s = s;
this.xy = xy;
}
}
- 例えばこのmylistは、cloneを使用可能で、
- mylistはCloneableを記述していないし、cloneメソッドも無いのに不思議な気がするが、
- cloneを呼び出したクラスの大きさで、丸ごとオブジェクトがコピーされるということなんだろうと思う。
- 継承したクラスが、別の参照を含む場合は、cloneを書けば良い。
- この場合、Croneableは不要で、try〜catchも必要ない
public class mylist extends xylist {
public int a;
public String s;
public xylist xy;
public mylist link;
public mylist() {
this.a = 0;
this.s = null;
this.xy = null;
this.link = null;
}
public mylist(int a, String s, xylist xy, mylist link) {
this.a = a;
this.s = s;
this.xy = xy;
this.link = link;
}
public void set(int a, String s, xylist xy, mylist link) {
this.a = a;
this.s = s;
this.xy = xy;
this.link = link;
}
public mylist clone() {
mylist result = (mylist) super.clone();
if (this.link != null) result.link = this.link.clone();
return result;
}
}
- でも、ディープコピーでも必ずしも全部コピーするだけではなく、一部だけコピーするようにした方が良い場合もあると思う。
public mylist clone(mylist link) {
mylist result = (mylist) super.clone();
result.link = link ;
return result;
}
- cloneは、プログラムのデータ構造に関わる部分なので、いろいろ工夫する余地2があって面白い
// スーバークラス
public class stream implements Cloneable {
:
public parent clone(parent link) {
try {
parent result = (parent) super.clone();
// スーバークラスのcloneの処理は実行される
return result;
} catch (CloneNotSupportedException e) {
throw new InternalError(e.toString());
}
}
}
// サブクラス
public class child extends parent {
:
public child clone(child link) {
child result = (child) super.clone(link);
// サブクラスのcloneの処理は実行されない
return result;
}
}
- これで、サブクラスのサブクラスのインスタンスを作って、cloneすると、
- スーバークラスのcloneは実行されるが、サブクラスのサブクラスのcloneは実行されない
public child clone(child para) { ⇒これが原因
- 実際は、サブクラスのサブクラスの処理になっていて、実行されない箇所の特定に手間取ったのと、
- cloneの記述に自信が無かったので、cloneの処理記述の方を疑っていたため、2〜3日悩んでしまった。。
- @Overrideを付けたら、すぐに分かった。。
- ここに書くべきことじゃないけど、Cloneの記述方法を疑って、すごく時間を無駄にしたので、備忘録として。。
- Cならば、データ構造を合わせておけば、型チェック甘いから
- 違うデータ型を処理する関数を書くのは、それほど難しくない。
- Javaでも、同じクラスを継承している、異なるオブジェクトを処理する関数を書くことは出来るんだけど、
- でも継承してないクラスを処理したい場合、ちょっと困る
class A extends ABC {}
class B extends ABC {}
class C extends ABC {}
class D extends DEF {}
void func(ABC data) {} // 同じABCを継承しているA,B,Cは処理できるが、Dは処理できない
- interfaceを使うと、継承関係が異なる複数のクラスを処理する関数が記述できる。
interface XYZ {}
class A extends ABC implements XYZ {}
class B extends ABC implements XYZ {}
class C extends ABC implements XYZ {}
class D extends DEF implements XYZ {}
void func(XYZ data) {} // 同じインターフェースXYZを持つクラスを全て対象とするメソッドを記述できる
- interfaceは、定数と抽象メソッドしか定義できない。
- あくまで、インターフェースの定義ということなんだろう。
- 抽象メソッド、抽象クラスを定義する
- このabstractなクラスを継承して、抽象メソッドをOverrideして使用する
- クラスの骨組みだけを定義して、実体のクラスの基底クラスとして
- interfaceに似ているけど、
- 継承して使うので、継承関係のあるクラスにしか使えない
- 抽象メソッドでない実体のあるメソッドも定義出来る
2ちゃんと考えないと、わけ分からない動作になるけど。。
3だから、ちゃんとクラス設計されていないと、使う意味が無いと思う。
[top]
メソッド | 説明 |
cail | 切り上げ |
floor | 切捨て |
round | 四捨五入 |
rint | 四捨五入(偶数側に) |
- CやJavaのroundは、普通の四捨五入だが、C#のroundは偶数側に丸められるらしい。
- これは四捨五入を繰り返した時の誤差を小さくするためで、銀行丸めとかJIS丸めとも言うらしい。
- JavaではBigDecimalというクラスがあるらしい。
[top]
String line = "ABCDEFG";
int len = line.length();
String line = "ABCDEFG";
char c = line.charAt(n);
String line = "ABCDEFG";
line.indexOf('C');
String line = "ABCDEFG";
if (line.startsWith("ABC")) [}
String line = "ABCDEFG";
if (line.endsWith("EFG")) [}
String line = "ABCDEFG";
if (line.matches(".*DEF.*")) {}
String line = "ABCDEFG";
String def = line.substring(3,6);
String efg = line.substring(4);
line.equals(line2)
int i = Integer.parseInt("123");
double d = Double.parseDouble("123");
でも変換出来ないときは、実行時にNumberFormatExceptionになる4
int i = Integer.parseInt("1.23"); // NumberFormatException
String s = String.valueOf(123);
String s2 = String.valueOf(1.23);
String s3 = Integer.toString(123);
String s = String.format("%02X",123);
char c = 'A';
char[] cc = new char[]{'A','B','C'};
String.valueOf(c);
String.valueOf(cc);
public static String collapse( String line ) {
StringBuffer buff = new StringBuffer(line.length());
for (int i=0; i<line.length(); i++) {
if ( line.charAt(i) == ' ' ||
line.charAt(i) == '\t' ||
line.charAt(i) == '\n' ) continue;
buff.append(line.charAt(i));
}
return buf.toString();
}
- replaceAllを使えば簡単だけど、こちらの方が処理は重いはず。多分。。
public static String collapse( String line) {
return line.replaceAll(" |\t|\n","");
}
public static String compress( String line ) {
return line.trim().replaceAll("\t|\n"," ").replaceAll(" {2,}"," ");
}
- 正規表現で、簡単に抽出するメソッドは無いみたいだ。
import java.util.regex.*;
4変換できないぐらいは普通のことなので、例外にするのは止めて欲しい気がするなぁ。
[top]
- StringTokenizerを使って、カンマ区切りの文字列から、アイテムを取り出す例
import java.util.StringTokenizer;
String line = "ABC,DEF,GHI,JKL,MNO";
String item;
StringTokenizer st = new StringTokenizer(line,",");
int n = st.countTokens();
while (st.hasMoreTokens())
item = nextToken();
- Tokenizerは大げさな気がする。こちらの方が良いかな。
String line = "ABC,DEF,GHI,JKL,MNO";
String item;
String[] items = line.split(",", 0);
int n = items.length;
for (int i=0; i<items.length ;i++)
item = items[i];
//// TEXTファイルを1単語毎に読込む(ファイルの最後にnullを返す)
//// 但し、ファイルの最後は改行或いは';'で終わっている必要がある
public static String read_word(BufferedInputStream bin) throws IOException {
StringBuffer sbuf = new StringBuffer();
int d;
//単語の頭を探す
while((d = bin.read()) != -1) {
if(d==' ' || d=='\t' || d=='\n') continue;
break;
}
//""で括られた文字列
if (d=='"') {
sbuf.append((char)d);
while((d = bin.read()) != -1) {
sbuf.append((char)d);
if(d=='"') break;
}
}
//単語が終わるまで読込み
else {
do {
if(d==' ' || d=='\t' || d=='\n') break;
sbuf.append((char)d);
if(d==';') break;
} while ((d = bin.read()) != -1);
}
if (d == -1) return null;
return sbuf.toString();
}
- ファイルの最後でnullを返す実装は、ちょっとアレなんだけど、良い方法が思いつかなかったので。。
[top]
- Cの配列はポインタなので、配列の途中から渡すのは簡単
- でもJavaではどうするんだろう
- とりあえず、途中からコピーする方法はあるようだ
int data[] = {0,1,2,3,4,5,6,7,8,9};
int[] buff = new int[256];
System.arraycopy(data,5,buff,0,data.length-5);
String[] array = { "ABC", "DEF", "GHI" };
String line = String.join(",", array);
String line = "ABC,DEF,GHI";
String[] items = line.split(",");
- Link構造の配列みたいなもの。大きさの決まっていない配列を扱うのに便利
ArrayList<String> array = new ArrayList<String>();
array.add("ABC");
array.add("DEF");
array.add("GHI");
String data = array.get(0);
String data = array.remove(1);
array.clear();
String[] items = {"ABC","DEF","GHI"};
// 配列をListに変換(でもこの場合、固定長のListになるので注意!)
List list = Arrays.asList(items);
// 配列をArrayListに変換
ArrayList alist = new ArrayList<String>(Arrays.asList(items));
- ちなみにListはinterfaceで、ArrayListはListのデフォルトの実装クラスなので、
- ArrayListをCharSequenceに変換
CharSequence[] cs = list.toArray(new CharSequence[list.size()]);
- ArrayListに、要素が含まれるかどうかはcontainsで
boolean res = array.contains("ABC");
- 普通の配列は、containsメソッドがないので、
- forを、要素を1個づつ取り出して処理するforeachみたいな使い方が出来るらしい
String[] items = {"ABC","DEF","GHI"};
for (String item : items) {
// 配列itemsから1個づつ取り出してitemに入る
}
- 論理値の配列は2値なので、これをビット演算で行う5BitSetクラスがある
public static BitSet table = new BitSet(256);
table.set(0,100+1,true); // 指定範囲(0〜100)をtrueにする
table.set(5,false); // インデックス5をfalseに
table.clear(5); // インデックス5をfalseに
table.clear(); // 全部の値をfalseに
boolean x = table.get(5); // インデックス5の値を取得
BitSet newtable = table.get(m,n); // インデックスm〜n-1の値のBitSetを生成
for (int i=table.nextSetBit(0); i>-1 ;i=table.nextSetBit(i+1)) {
// インデックスiを処理する
}
int count = table.cardinaly(); // セットされている数
boolean result = table.isEmpty(); // セットされているビットがあるかどうか
- 指定した別のBitSetと共通にセットされているビットがあるかどうか
boolean result = table.intersects(checktable);
String list = table.toString();
- これ以外に、BitSet同士の and, or, andnot, xor 等の論理演算も可能
Map<String, Integer> cmdtable = new HashMap<String, Integer>();
cmdtable.put("ABC",1);
cmdtable.put("DEF",2);
cdmtable.put("GHI",3);
Integer cmd_n = cmdtable.get("ABC");
final Map<String, Integer> cmdtable = new HashMap<String, Integer>() {{
put("ABC",1);
put("DEF",2);
put("GHI",3);
}};
このやり方は、匿名クラスとインスタンス初期化子を使っているらしい
- 以前、大きな変換テーブルを使っている時に、自前のハッシュテーブルを使っていたけど、
- ハッシュテーブルは、テーブルの大きさ以上のデータを入れることは出来ない。
- また、テーブルがある程度一杯になると、ハッシュコードが重複するので、時間が掛かるようになる
- そのため、データが50%ぐらいになるように、1年おきにテーブルをに大きくする作業を手動でしていた。。
- HashMapは、テーブルのデータが容量x負荷係数を超えるとテーブルを自動的に再生成するらしい
- とても便利だけど、テーブル再生成は重い処理なので、データの大きさに合わせて予め容量を決めた方が良い。
- HashMapのデフォルトの初期容量は16で、負荷係数は0.75になっているらしい
- 予想されるデータの大きさの、4/3の大きさで初期容量を指定するのが望ましい6
- 例えば、データが10000個ならば、初期容量は13333個で指定する
Map<String, Integer> cmdtable = new HashMap<String, Integer>(13333);
- switch文で、caseにStringが使えるらしい。
String key = "ABC";
Integer cmd = 0;
switch(key) {
case "ABC": cmd = 1; break;
case "DEF": cmd = 2; break;
case "GHI": cmd = 3; break;
default: cmd = 0;
}
- 自分は、Cでは変換テーブルでコード化してから、switchすることがあるけど、
- それだけのことなら、単純にSwitch文で書いた方が見やすくて良いかもしれない。
- 結局のところ、コンパイラは、おそらくハッシュでswitchしてくれそうな気がするし。。
5boolean配列よりはメモリを節約できると思う。
6あまり大きくしすぎると、テーブルの参照時間が長くなる。
[top]
- Javaに関して、ちょっと分かりにくかったことを自分なりに纏めたもの。
- ByteBufferは、Javaでメモリ上のデータにbyte単位で直接的にアクセスする仕組みだと思う。
- 通常、ByteBufferはJavaヒープ上に存在するが、allocateDirectすることで
- ネイティブのライブラリ関数等のI/O操作で、ネイティブのメモリ上のバッファを直接参照することが出来る
- allocateDirectされたByteBufferの実体は、ネイティブのメモリ上に存在し、
- Javaヒープ上のオブジェクトには、ネイティブのバッファへの参照しか含まれない
public void draw(){
GLES20.glUseProgram(shaderProg); // シェーダプログラム使用開始
int vposx = GLES20.glGetAttribLocation(shaderProg, "vpos"); // attribute変数のindex取得
GLES20.glEnableVertexAttribArray(vposx); // attribute変数のアクセスを有効に
float vlist[] = { // 三角形の頂点データ
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
ByteBuffer bb = ByteBuffer.allocateDirect(vlist.length * 4); // ネイティブのメモリ上にバッファを確保
bb.order(ByteOrder.nativeOrder()); // ネイティブのByteOderに合わせる
FloatBuffer vBuffer = bb.asFloatBuffer(); // float用のバッファとする
vBuffer.put(vlist); // floatのデータをコピー
vBuffer.position(0); // ポインタを最初のデータにする
GLES20.glVertexAttribPointer
(vposx, vlist.length/3, GLES20.GL_FLOAT, false, 0, vBuffer); // attribute変数に頂点情報の場所と書式を設定
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vlist.length/3); // 描画する
GLES20.glDisableVertexAttribArray(vposx); // attribute変数と頂点情報の対応付けを無効に
GLES20.glUseProgram(0); // シェーダプログラム使用終了
}
- 但し、allocateDirectによるメモリの確保と開放は、ちょっと重い処理らしいので、使いまわす様にするべきなのかも
[top]
- Javaの実行をAndroidでやってたんだけど、実機でデバッグするのは、ちょっと面倒なので、
- コマンドラインで実行7できるように、PCにOpenJDKをインストールしてみる。
- といっても、現状、自宅にUnixマシンが無いので、
- さくらインターネットのスタンダードで借りてるサーバにインストールする。
- ここは、LinuxじゃなくてFreeBSDなので、ちょっと面倒なんだけど、
- Java自体、Androidが初めてなので、
- Unixの環境で、改めて基礎的なところから復習してみようと思う。
- FreeBSD9だけど、7用しかないのでコレでやってみる
- diablo-caffe-freebsd7-i386-1.6.0_07-b02.tar.bz2
% tar zxvf diablo-caffe-freebsd7-i386-1.6.0_07-b02.tar.bz2
- あっ、これバイナリなんだ。。バージョンあってないけど、大丈夫かな。。。
setenv JAVA_HOME $HOME/opt/jdk
setenv JRE_HOME $JAVA_HOME/jre
set path = (/sbin /bin ... $HOME/local/bin $JAVA_HOME/bin)
% java -version
java version "1.6.0_07"
Diablo Java(TM) SE Runtime Environment (build 1.6.0_07-b02)
Diablo Java HotSpot(TM) Server VM (build 10.0-b23, mixed mode)
- 以下のソースを記述(HelloWorld.java)
public class HelloWorld {
public static void main (String[] args) {
System.out.println("Hello World !!");
}
}
% javac HelloWorld.java
% java HelloWorld
Hello World!!
- でも、やっぱりコマンドライン環境は慣れてるから、やり易いな〜。
public class Test {
public static void main (String[] args) {
System.out.println(args[0]);
}
}
% javac Test.java
% java Test ABC
ABC
% java Test
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at Test.main(Test.java:3)
public class Test {
public static void main (String[] args) {
try {
System.out.println(args[0]);
}
catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
}
}
% java Test
java.lang.ArrayIndexOutOfBoundsException: 0
at Test.main(Test.java:5)
- 例外処理って、慣れないので、ちょっと面倒だな〜
- まぁ、finallyとか書けるから便利なんだけどね。
- TEXTファイル読込み
- 単純に1行づつ読込んで、表示するだけのプログラム
import java.io.File;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.FileNotFoundException;
public class FileTest {
public static void main (String[] args) {
fileInput("Test.java","print.lis");
}
//// TEXTファイルを1行づつ読込み
public static void fileInput(String infile, String outfile) {
InputStream in=null;
BufferedReader br=null;
try {
in = new FileInputStream(infile);
br = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
finally {
try {
if (br != null) br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- ついでに、ファイルの書き出しもやってみる
- 1行づつ読込みプログラムに、ファイル出力を追加しただけ。
import java.io.File;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
public class FileTest {
public static void main (String[] args) {
fileInput("Test.java","print.lis");
}
//// TEXTファイルを1行づつ読込み、同時にTEXTファイルに出力
public static void fileInput(String infile, String outfile) {
InputStream in=null;
BufferedReader br=null;
OutputStream out=null;
PrintWriter pw=null;
try {
in = new FileInputStream(infile);
br = new BufferedReader(new InputStreamReader(in));
out = new FileOutputStream(outfile);
pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(out)));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
pw.write(line+"\n");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
finally {
try {
if (br != null) br.close();
if (pw != null) pw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- FreeBSDのバージョンが合ってなかったので、心配だったんだけど、
- この程度のファイル入出力が出来るなら、Android用のファイル処理プログラムのデバッグ用に十分使えそうだ。
- といっても、クロスコンパイル環境というわけじゃないので、Android関連の処理は出来ない
- 自分は、純粋なファイル処理、文字列処理のデバッグが出来れば十分なので、まぁ良し8とする。
- コマンドラインの環境で、パッケージをインポートしてコンパイルする
- 例えば、ディレクトリ構成が以下のようになっている9とする
- work/
- userprog.java
- userprog.class
- hm/
- orz/
- bluefish/
- userclasspath/
- userclass.java
- userclass.class
- インポートするパッケージのクラスファイルuserclass.javaの先頭に、
- 呼び出す側のjavaファイル(userprog.java)は、クラスファイルをインポートする
import hm.orz.bluefish.userclasspath.userclass;
- コンパイルは、パッケージのパス指定のルートディレクトリを指定する
javac -classpath ~/work/ userprog.java
- 実行は、実行するクラスファイルのあるディレクトリを指定する
java -classpath ~/work/ userprog
- このように実行するクラスファイルのあるディレクトリと、インポートするパッケージのルートディレクトリが一致していれば、特に問題ない。
- 異なる場合は、実行時にNoClassDefFoundErrorになってしまう。
- Cで、指数部と仮数部を求めるfrexpと同じ関数は、Javaでは無いっぽい
- Cのfrexpの場合、仮数部は0.5以上1未満となる
long e;
double f = -1.23e56;
double x = frexp(f,&e);
printf("f=%f, x=%f, e=%d\n",f,x,e);
//結果
f=-123000000000000010650706570528153382993683139181297008640.000000, x=-0.627041, e=187
- Javaでは、指数部を求めるgetExponentがあるが、
double f = -1.23e56;
long e = Math.getExponent(f)+1;
double x = f/Math.pow(2,e);
System.out.println("f="+f+" x="+x+" e="+e);
//結果
f=-1.23E56 x=-0.6270409762217333 e=187
- デバッグ時に、条件に合わない場合にAssertionErrorとなる
- コンパイル時は、
$ javac -source 1.4 test.java
実行時は、
$ java -enableassertions test.class
或いは
$ java -ea test.class
- 最終的なコードでは機能しないので、あくまでデバッグ用で、
- 人様のコードで見かけることがある。ちょっと面白い機能だなぁ
7もちろん、GUIのデバッグは出来ないんだけど。
8ホントは全体のコンパイルチェックぐらいしたいんだけど、これ以上の環境を作るのは自分にはよく分からんので。
9classファイルはコンパイル時に生成される。
[top]
- 例えば、クラス名が見つからない場合に出力される
- 単純にクラス名のスペルが間違っていないか確認
- classpathの指定、packageの指定、クラスファイルのあるディレクトリ構造が、全部一致しているかどうか確認する
- interfaceをパッケージ外から参照しようとした時に出た。
- eclipseでは、publicにしなくても出来たんだけど、interfaceの前にpublicを付けないとダメみたいだ。
- 型が違うものを代入しようとすると、このエラーになる。
int i = 1.23;
- 明示的にキャストしなければならない
int i = (int)1.23;
ちょっと分かりにくい例としては、shortは演算時にintとして扱われるので、このエラーになる。
short a=1, b=2, c;
c = a+b;
これも明示的にキャストすれば良い
c = (short)(a+b);
- java実行時のエラー
- インポートするパッケージのルートディレクトリが、実行するクラスのパスと一致しない場合にエラーになった。
- 一致させれば、実行可能になるが、異なる場合の指定方法はよく分からない。
- 文字通り、配列の範囲外にアクセスしたってことなんだけど、
- 明示的に作成した配列じゃなくて、何かの結果として生成された配列で出たので、
- どういう場合に出たか、ちゃんと記録するの忘れた。。
- いろいろ修正していたら、さっきまで動いていたProjectがエラーで動かない。
- 良く見ると、android.R が勝手にインポートされている。
import android.R;
- この行を削除することで、問題なく動くようになった。
- どういう操作でインポートされたか分からないが、結構悩んだ。。。
[top]
[top]
[プログラムの部屋に戻る]