Java言語と生産性について

Java言語は、Sun Microsystems社が開発した言語で、
現在は、吸収合併されて、Oracle社が管理している。
Java言語は、オブジェクト指向言語であり、開発当時先行して使われていた
C言語、C++言語を使った開発上の問題点を研究して作られている。
Java言語は、一般のコンピュータ言語で、使われる事は少なかったのだが、
組み込み機器用に使われ始めていたものが、携帯電話の普及で多く使われるようになったと思う。
Java言語は、コンパイル言語だが、仮想マシンのマシンコードにコンパイルされる。
これは、パスカル言語のPコードと似ていて、半分インタープリターの様な動作をする。
Java言語は、CPUのネイティブコードで動作する訳ではないので、C言語やC++言語と
比べるとその動作は、そんなに速くはならない。
しかし、実行時のエラーチェックが強力で、これによっても開発効率を上げられる。

構造化プログラミング

まず、アセンブリ言語やBASIC言語の時代に、生産効率の一大革命があった。
これは、パスカル言語やC言語が採用した「構造化プログラミング」によるものだ。
条件分岐とループ処理が一定のパターンで定義された言語仕様である。
当時、BASIC言語で条件判断を行うには、行番号とIF〜GOTO文が使われていたが、
GOTO文では、その条件の処理がどこから始まるかは分かっても、どこで終わるかは
分からなかった。GOTO文は、飛び先は自由であり、そのためにかなり分かりづらい
プログラムを平然と書いていたのだ。
自らのプログラミングコードを記号化して、すべて暗記掌握できる人にとっては、
GOTO文で書いてもさほど問題は起きないのだが、そういう書き方を
されたプログラムは、他人が読むには難解すぎるものとなりやすい。また、プログラムを
書く人も、そのプログラムを一生掌握していられる訳ではなくて、一時的な記憶によって
理解掌握しているので、プログラムが完成したら、細かい事は忘れてしまう。
つまり、自分で書いた過去のプログラムは、他人が書いたのとさほど変わらないのだ。
構造化プログラミングで書くと、GOTO文を一切使わないで、コードをブロック単位で
扱う事が出来る、これにより条件処理が把握しやすくなる。
GOTO文で書かれたプログラムも、分類してみると、条件分岐やループ処理をしている
だけで、基本的なデザイン・パターンは、そんなに多くないということだ。
これらが、if文、for文、switch〜case文といった形で定義されている。
もちろん、GOTO文で書いていたプログラムでは、関数の入り口が複数あって
途中で統合されるものや、合理的な書き方をしていたものがあるが、
そういう一部の書き方は構造化プログラミングでは許されなくなった。
しかし、若干の無駄などを容認すれば機能的に同じ機能は、構造化プログラミングで
実現できるのだ。

C言語、C++言語での問題点

Java言語も、構造化プログラミングが基本になっている。
しかし、構造化プログラミングでも色々な問題が出てきて、Java言語では、
それらを解決して開発効率を上げられるようにしている。
それでは、C言語、C++言語には、どのような問題点があったのか?

【当時の習慣など】
まず、グローバル変数の乱用。
プログラムの見通しを悪くする当時の悪癖として言われていた事で
グローバル変数を定義すれば、どこからでもアクセスできるので便利であり
ループ変数などもここに定義して使い回すケースもよくあった。
しかし、変数やワークメモリーは、どの機能のためのものが分からなくなり、
いつ初期化されてどう使われるか、プログラムの規模が大きくなると
管理できなくなるのだ。また、変数の使い回しは、不注意から厄介なBUGを
生むため、構造化プログラミングが流行った時期に、ループ変数は、関数の
ローカル変数(スタック)に定義して、同じ名前でも別の場所に定義する事が
一般的に推奨されるようになっていた。
C言語でも、変数などは、機能毎に構造体にまとめて定義するのが望ましい。
オブジェクト指向言語であれば、なおさらだ。
Java言語では、基本的にグローバル変数が使えないように設計されている。

【メモリーの初期化の問題】
プログラムが動作している場合、意図して動作している場合もあるが、
意図せず正常動作している場合もたまにある。
それは、変数やメモリーの値を初期化しないでプログラムを動かした場合だ。
多くの変数は、動作前に所定の値を書き込んでから動くのだが、
複雑な条件判断の中で初期化している様なプログラムは、ある条件が
重なると、初期化されないで変数の値を使用する事になる場合が起きたりする。
これは、一種のBUGなのだが、プログラムを変更していると、意図せず
初期化忘れが発生するのだ。
初期化忘れの恐いところは、動いていたプログラムが、ある日突然動かなくなる事だ。
プログラムを修正しているときなら、修正した辺りを調べれば不具合が見つかるが
初期化忘れで動かなくなったときは、まるで原因がわからず、原因究明に
かなりの時間がかかるのが普通だ。これは開発効率に大きく影響する。
そこでJavaでは、ヒープで確保したメモリー領域は、確保時に0クリアするようになっている。
また、メソッド(関数)内のローカル変数に関しては、クリアしないで、
コンパイル時に未初期化を警告するようになっている。
CPUの動作効率を考えると、確保したメモリーを一旦クリアーするというのは、
時間がかかる無駄な作業とも言えるが、Javaではこの効率を捨てて、
人為的ミスを防止することで開発効率を上げている。

【ワーク破壊の問題】
未初期化より発生頻度が多く恐いと思うのが、配列変数の領域外アクセスによる
クラッシュや他のワーク破壊だ。これも、完璧なプログラムを書いていれば
起きる事のないミスなのだが、発生するトラブルの原因としては珍しくない。
全く関係ない変数が、動作中どこかで突然その値が書き換えられたりする
という不具合なので、その原因究明に時間がかかるのだ。
C言語やC++言語では、配列変数は定義されているものの
その長さまでいちいち管理しない言語仕様になっている。
また、これを動作時にいちいちチェックするのは、パフォーマンスの低下を招く。
Javaでは、配列変数を配列オブジェクトとして定義して長さを管理できるようにしている。
長さを超えた配列変数のアクセスをすると、Exceptionが発生して知らせてくれる。
【ポインターの問題】
ポインターは、便利なのだが、関数で渡して使う場合は、&aでアドレスを渡して、
ルーチン内は、*aで読み書きするといった風に、書式が分かりにくく、
これが理解できなくてプログラムが書けない人もある。
これは、オブジェクト指向言語では、ポインターに似た、参照(リファレンス)を
使えばスッキリするのだが、C++言語では、C言語との互換性からポインタを
使うのが主流になっている。
C++言語では、構造体やクラスのメンバー変数にアクセスする時、
"."でアクセスする方法と、"->"でアクセスする方法の2種類があり、
これらをむやみに混在させて書くと、後で使いづらいものになる。
Java言語には、参照はあってもポインタはないのでスッキリしている。

【メモリーアロケーションの問題】
ヒープメモリーを使うプログラムでは、その確保と開放に多くの労力が裂かれる。
C言語、C++言語でメモリーを確保すると、そのポインターを開放するまで
追跡管理しなければならない。
よくあるトラブルとしては、
・確保していない未初期化ポインターの参照
・あるポインタに、メモリー開放しないで新しいポインタを代入して
 古いメモリー領域を開放しないままポインタが失われる。(メモリーリークになる)
・開放した筈の領域に、間違ってアクセスする。
・既に開放したポインタを開放していない重い開放。(二重開放)
Javaでは、これらのトラブルを防ぐために、参照をチェックして、参照が切れた
メモリーを自動開放するという方法で、メモリーアロケーションに関わる
煩わしさと、ミスを回避している。
ただ、ガベージコレクション処理を定期的に呼ぶ必用があり、この処理で
一時的なパフォーマンス低下を起す事がある。

【型チェックの問題】
C言語では、if文の中に代入文が書けるが、これは間違いの元になっている。
代入した値を評価して、真偽を0かそれ以外かで判断するので便利ではあるが
= と == を見間違える事も多いのだ。
Java言語では、if文などがboolean型しか受け付けないため、この手のトラブルは、
簡単にエラーチェックできるようになっている。
この他、キャスト変換による精度落ちも厳しくチェックされる。
C言語では、unsigned char 型と signed char型の混同で、-1と比較したつもりが
255と比較されていたというようなことがある。そのためJavaでは、unsigned byte型が
存在しない。しかし、これはこれで不便でもある。

【ヘッダファイルの問題】
C言語、C++言語では、関数などのテンプレート宣言を行ってから、他で参照して
使うために、ヘッダファイルを作成して使用する。
#include 文で階層的に、読み込む事が出来るが、これが入れ子になっていたり、
部分的に重複定義されたり、複雑な依存関係など色々と厄介な問題を生み、
この解決のために多大な労力を要する事がある。
Java言語では、クラス宣言をすれば、テンプレート宣言は必要なく参照できるため
メソッド(関数)を書く順番も問われないし、ヘッダファイルの煩わしさからも開放されている。

【その他】
C言語や、C++言語では、あるルーチンをどのファイルに定義したか、自由なので
とんでもない場所に、書かれる事があるが、Javaでは、クラス単位での制約がある。
publicのクラスは、クラス名とファイル名を一致させなければならない。
このため、プログラムの規模が大きくなった時に、クラス単位で、コードの所在が
どこにあるか探しやすくなっている。


フューチャー・ホームページへ戻る

(C)2014 Future on netyou ALL RIGHTS RESERVED.