リファクタリング

●リファクタリングとは
外部からみたときにプログラムの振る舞いを変えずに、プログラム内部の構造を改善すること

●リファクタリング箇所

1.マジックナンバーをシンボリック定数に置き換える
・public staic finalのシンボリック定数に置き換える(クラス内での使用はprivate)
・定数そのものの値を使用する(出力、比較)のではなく、種類等であればenumの方がすっきりする。

2.制御フラグの削除
・制御フラグはどんな制御フラグかわかるように名前をかんがえること
例)
・初期化が済んだ事がわかるフラグ→initialized
・デバッグフラグ→debug
・処理が完了した事を表すフラグ→done
・スレッドへの処理のキャンセル指令→interrupted
・メソッドの引数に度々使用されるフラグで再起的にメソッドを適用するかどうかを
制御するフラグ
(Compositeパターンが使用されている再起的な構造「OO-Recursiveな構造」があるとき)
→recurse

・条件に制御フラグにfalse/tureを入れて最後にフラグをreturnするより
そのまま「return true;」のように記載した方が、変数を作らなくてすむ。
・一度、trueになったら、breakをいれるなど。

3.アサーションの導入
・実行時に確かにその条件がなりたっていること、プログラマの意図をはっきりさせるために
アサーションを導入する事を考える
例)valuesは必ず正である
→assert value > 0;
※上記の記述で、もしvalues > 0が成り立たないなら、Java実行処理系は
java.lang.AssertionErrorという例外をthrowする。
この例外は致命的なエラーを表すjava.lang.Errorのサブクラス。
VirtualMachineErrorやThreadDeathなどと同様にcatchするコードは書かない。
あたりまえだが、java.lang.AssertionErrorが投げられるという事は意図と反しているため
修正が必ず必要であるからだ。

・javaコマンドで実行しますが、デフォルトでassertは無効になっている
→java -ea Main
上記のように「-ea」オプションをつけて実行する必要がある。
ちなみに「-ea」はenable assertionsの略(覚えやすい)

・アサーションの記法
a) assert expr;
b) assert expr: option;

aについては、前述していますが、bのoptionですがこれはアサーションが
成り立たなかった時に詳細な情報optionとして与えています。
→assert value > 0 : “value = ” + value;

optionの内容はコンソールに出力されます。

・Javaのアサーションはクラスライブラリではない
assertという語は、Java言語のキーワードとして扱われる。
すなわち、Javaのクラスライブラリとして組み込まれたものではなく、
Java言語仕様として組み込まれている。

・アサーションの完全削除
アサーションは無効で実行されていても、バイトコードの中にに残っている。
ただ、Java言語仕様の14.20「到達不能文」を利用するとassert文を完全にクラスから
削除できる。(厳密にはコンパイラの実装に依存)

private static final boolean ASSERT = true;
…….
if (ASSERT) {
assert value > 0;
}

“ASSERT”をfalseにした時点でif文の中身は実行されない事がコンパイル時にわかるので、
関連コードは含まれなくなります。これは、Java言語仕様14.20「到達不能文」に記述
されているif文を用いた「条件コンパイル」という機能を使っている。

 

JVMのメモリ構造

Javaのメモリ領域の使い方を纏めたいと思います。
JavaはJVM上で動作します。Javaのメモリの使い方を考えるときには
JVMのメモリ構造を理解する必要があります。
JVMのメモリ構造はヒープとスタックの2つに大別されます。

1.Javaスタック

スタックとは積み上げていくデータ構造のことです。
Javaスタックでは、フレームを積み上げるときにこの仕組みをとっています。
フレームとはメソッドを呼び出すごとにひとつずつ確保されるもので、
フレームの中身はメソッド内のローカル変数の領域とオペランドスタック
(計算中の値を積んでおくところ)です。

Javaスタックはスレッド毎に割り当てられ、後に入れたものを先に出す(LIFO)の
待ち行列の仕組みで、固定長の線形データ構造です。
なお、メソッドを抜けたときにローカル変数の領域は解放されます。

スタックが使用されるケースとして例外発生時があります。
Javaのプログラム実行時に例外が発生したときにcatchブロックを検索しますが、
実行したメソッド内にcatchブロックが見つからない場合、さらにスタックを
さかのぼって呼び出し元メソッドのcatchグロックが検索されます。

2.ヒープ

ヒープ(Heap)とはJVM起動時に割り当てられる広大な領域です。
クラスのインスタンス(オブジェクト)を格納する領域です。
newする毎に必要な領域がヒープに確保されています。
newすると、そのオブジェクトを変数に格納すると思います。
ですので、変数がnewしたオプジェクトが存在するヒープ領域を参照しています。
よって、1で説明したJavaスタックとの関連を考えると、ローカル変数が
ヒープ領域へのポインタを保持していることになります。

また、ヒープはGCの対象になります。
(※ただし、各クラスのstaticフィールドについてはこのヒープ領域に
確保されていますが、GC対象とはなりません。
また、そのクラスが必要になったときに初めてメモリに確保されるので、
起動時間が短縮できます。=起動時に確保されていない)

参考URL:
http://msugai.fc2web.com/java/perform/storage.html

ここで、GCについても説明したいと思います。
GC(Garbage Collector)は不要になったオブジェクトのメモリの解放を自動的に行ってくれます。
不要なオブジェクトの定義ですが、以下になります。

・Javaスタック、staticフィールドからポインタを追いかけてたどることができるかどうか。

なぜ、このような定義になるかというとスタックからもstaticフィールドからも
たどれないものは絶対に参照は不可能ということになるからです。
考えてみると当たり前なのですが・・・。

ヒープにあるオブジェクトをGCの対象にしたいということであれば、
その変数にnullを代入して、参照をきってあげればOKです。

Javaコンパイルオプション

Javaは様々な言語のキャラクタセットに対応できるように、16ビットのUnicodeキャラクタにしています。
UnicodeにはASCIIで定義されている英数字や記号に割り当てられた文字コードをそのまま引き継いでいるため、英数字と記号を使っている限りは問題ないのですが、日本語を使う場合は、その文字エンコーディングをUnicode(UTF-8)に変換する必要があります。

javacでコンパイルオプション「-encoding」を指定する場合がありますが、これはOS標準のエンコーディング(WindowsはShift JIS、UNIXでは日本語EUC)と同じであれば自動的に標準の文字エンコーディングから変換を行うのでこのオプションを意識する必要はありませんが、標準と異なるエンコーディングを使用している場合は、javacに「-encoding」のオプションを指定する必要があります。

※ただしコンパイル前にJavaのソースコードをUnicodeに変換する必要がある。

 

AARRGGBBを10進数に変換後、再度AARRGGBBに戻す

●TestAppActivity3.java

public class TestAppActivity3 extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view3);

final TextView txtvResult = (TextView) findViewById(R.id.txtvResult);
final EditText edtInputHex = (EditText) findViewById(R.id.edtInputHex);
final Button btnConvert = (Button) findViewById(R.id.btnConvert);

btnConvert.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {

String hex = edtInputHex.getText().toString();
if (hex == null || hex.length() <= 5) {
Toast.makeText(TestAppActivity3.this, “16進数を入力してください”, Toast.LENGTH_SHORT).show();
}

// 変換
try {
long num = Color.parseColor(“#” + hex);
String returnHex = Long.toHexString(num);
if (returnHex.length() < 8) {
int padLen = 8 – returnHex.length();
String addText = String.format(“%1$0” + String.valueOf(padLen) + “d”, 0);
returnHex = addText + returnHex;
} else if (returnHex.length() > 8) {
returnHex = returnHex.substring(8);
}
// 大文字に変換
returnHex = returnHex.toUpperCase();

// 画面への反映
String text = “”;
text = “「” + hex + “」を10進数に変換すると、「 “;
text += String.valueOf(num);
text += “」。「”;
text += String.valueOf(num);
text += “」を16進数に戻すと、 「”;
text += returnHex + “」。”;
txtvResult.setText(text);
} catch (Exception e) {
txtvResult.setText(“”);
Toast.makeText(TestAppActivity3.this, “エラーが発生しました”, Toast.LENGTH_SHORT).show();
}

}
});

}

}

●view3.xml

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android&#8221;
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:orientation=”vertical” >

<LinearLayout
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:orientation=”horizontal” >

<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:text=”16進数:” />

<EditText
android:id=”@+id/edtInputHex”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content” />
</LinearLayout>

<Button
android:id=”@+id/btnConvert”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:layout_margin=”5dp”
android:text=”16進数->10進数変換->16進数変換” />

<TextView
android:id=”@+id/txtvResult”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content” />

</LinearLayout>