Javaのmainメソッドを public static void にしなかったらどうなるかやってみた。

Javaのmainメソッドを記載するとき、public static void main( String[ ] args ) { . . . と記載しますが、mainメソッドにpublicやstaticがなかったり、戻り値がvoid型でなかった場合にどうなるか?をやってみました。

Javaの実行環境はLinuxで、バージョンはJava8です。

サンプルソースです。


package sample08;

public class MainSample {

	public static void main( String[] args ) {

		System.out.println("Hello.");

	}
}

これを実行すると、画面に「Hello.」と表示されます。

mainメソッドのpublicを外してみました。
public static void main ⇒ static void main にしました。


package sample08;

public class MainSample {

	static void main( String[] args ) {

		System.out.println("Hello.");

	}
}

結果は、コンパイルはできましたが実行はできませんでした。

実行時の出力です。


$ java sample08.MainSample 
エラー: メイン・メソッドがクラスsample08.MainSampleで見つかりません。次のようにメイン・メソッドを定義してください。
   public static void main(String[] args)
またはJavaFXアプリケーション・クラスはjavafx.application.Applicationを拡張する必要があります

mainメソッドのアクセス修飾子にはpublicが必要とのこと。

mainメソッドのstaticを外してみました。
public static void main ⇒ public void main にしました。


package sample08;

public class MainSample {

	public void main( String[] args ) {

		System.out.println("Hello.");

	}
}

結果は、コンパイルはできましたが実行はできませんでした。

実行時の出力です。


$ java sample08.MainSample 
エラー: メイン・メソッドがクラスsample08.MainSampleのstaticではありません。次のようにメイン・メソッドを定義してください。
   public static void main(String[] args)

mainメソッドにはstatic句の指定が必須のようです。

MainSampleクラス(mainメソッドがあるクラス)のインスタンスを生成するクラスがないから、インスタンスの生成なしで外部からmainメソッドを呼べるようにstaticである必要があるのでしょう。また外部から呼べるようにpublicとする必要があるのかと思いました。

クラスのアクセス修飾子であるpublicを外してみました。
public class MainSample ⇒ class MainSample にしました。


package sample08;

class MainSample {

	public static void main( String[] args ) {

		System.out.println("Hello.");

	}
}

結果は、コンパイルできて実行もできました。mainメソッドがあるクラスのアクセス修飾子は無指定でも良いようです。

クラスのアクセス修飾子が無指定でも実行できたということは、mainメソッドを呼ぶ人(おそらくJavaのVM)はどのパッケージにあるクラスでもアクセスできるということかと思います。

戻り値のvoidもintに変更してみました。
public static void main ⇒ public static int main にしました。


package sample08;

public class MainSample {

	public static int main( String[] args ) {

		System.out.println("Hello.");
		return 1;

	}
}

結果は、コンパイルはできましたが実行はできませんでした。

実行時の出力です。


$ java sample08.MainSample 
エラー: メイン・メソッドはクラスsample08.MainSampleのvoid型の値を返す必要があります。
次のようにメイン・メソッドを定義してください。
   public static void main(String[] args)

mainメソッドの戻り値はvoid型でないとダメなようです。なぜvoid以外の戻り値を許可していないのかは不明です。

でもそうするとJavaの結果は呼び出し元に返せないのかと思ってしまうのですが、戻り値を返すメソッドがありました。

System.exit()を記載すれば、呼び出し元にリターンコードを返せるようです。以下のようにします。


package sample08;

public class MainSample {

	public static void main( String[] args ) {

		System.out.println("Hello.");
		System.exit(255);
		
	}
}

このようにすると呼び出し元に255のリターンコードが返されます。

$ java sample08.MainSample
Hello.
$ echo $?
255

mainメソッドの引数ですが、String[] args となっています。これはmainメソッドの仕様でしょう。試しに String[] args ⇒ String args としたところ実行時にエラーとなりました。

ということで、Javaのmainメソッドは、

public static void main( String[ ] args ) { . . .

とするしかないようです。mainメソッドのあるクラスのアクセス修飾子はpublicがなくても大丈夫です。

Javaの修飾子(static、final、abstract)とインターフェイスについて

Javaの修飾子であるstaticとfinalについて、まずは見ていきます。

staticはフィールドとメソッドに指定し、指定されたフィールドとメソッドは静的フィールドと静的メソッドになります。静的フィールドと静的メソッドはインスタンスを生成しなくても使うことができます。

finalはクラス、フィールド、メソッドに指定します。finalが指定されると変更ができなくなります。

static、finalの用途を整理したものです。

次にabstractです。

abstractは抽象メソッドの宣言に指定します。抽象メソッドとは具体的な処理を書かずプロトタイプ(戻り値と引数)のみを定義したメソッドのことです。抽象メソッドが1つでもあるクラスを抽象クラスと呼びます。抽象クラスのインスタンスは生成することができません。

abstractの用途を整理したものです。

抽象メソッドを持つクラスにはabstract指定が必要です。無いとコンパイルエラーになります。抽象メソッドがない場合でもクラスにabstract指定は可能です。あえて継承して使ってもらいたい場合はクラス修飾子にabstractをつけます。

最後にインターフェイスについてです。

インターフェイスは定数フィールドと抽象メソッドのみで構成されたクラスです(Java8からはデフォルトメソッドとスタティックメソッドが追加されましたがこれについては後述します)。インターフェイスには次のような特徴があります。

  • インターフェイスのすべてのフィールドは public static final が指定されているとみなされる。
  • インターフェイスのすべてのメソッドは public abstract が指定されているとみなされる。

そのため以下のインターフェイスはまったく同じものです。

インターフェイスのフィールドとメソッドはpublicであるため、インターフェイスのアクセス制限はインターフェイス自身につけられたアクセス修飾子によって決まります。

public interface Keisan1 { ・・ } とした場合、interface句の前にあるpublicがインターフェイス自身につけられたアクセス修飾子となります。

ここからはJava8から追加されたインターフェイスの機能についてです。

インターフェイスの抽象メソッドでは処理の記載はしないのですが、デフォルト処理の記載ができるようになりました。このデフォルト処理を記載したメソッドをデフォルトメソットと呼びます。デフォルト処理というのは、インターフェイスを実装したクラスがインターフェイスに記載されたデフォルト処理をそのまま使う場合に限り、メソッドのオーバーライドが不要となるものです。デフォルトメソッドの宣言には、default修飾子をつけます。

デフォルトメソッドは実装するクラス側のコーディング量を減らすために導入されたものです。実装する側のクラスで、どのクラスも同じ処理となる場合に有効です。

またJava8からはインターフェイスでも静的メソッドが使えるようになりました。クラスと同じようにstatic句をつけることで静的メソッドになります。インターフェイス名.メソッド名 で他のクラスから静的メソッドを呼べます。

インターフェイスのデフォルトメソッドと静的メソッドを使用した例を示します。

Helloインターフェイスではデフォルトメソッドと静的メソッドを宣言します。HelloEnクラスではHelloインターフェイスを実装しますが、デフォルトメソッドをそのまま使います(オーバーライドしません)。HelloJpクラスではHelloインターフェイスを実装して、処理を記載します(オーバーライドします)。MainSampleクラスで、デフォルトメソッド(HelloEnインスタンスのメソッド)、オーバーライドしたメソッド(HelloJpインスタンスのメソッド)、Helloインターフェイスの静的メソッドの呼び出しを行います。

ソースコードは以下です。

Hello.java


public interface Hello {

	//デフォルトメソッド
	default void hello() {
		System.out.println("Hello.");
	}

	//静的メソッド
	static void goodBye() {
		System.out.println("Good bye.");
	}

}

HelloEn.java


public class HelloEn implements Hello {
	//何もしない
}

HelloJp.java


public class HelloJp implements Hello {

	//オーバーライドする
	public void hello() {
		System.out.println("こんにちは");
	}
	
}

MainSample.java


public class MainSample {

	public static void main( String[] args ) {

		Hello h1 = new HelloEn();
		Hello h2 = new HelloJp();

		//デフォルトメソッドを呼ぶ
		h1.hello();

		//オーバーライド下メソッドを呼ぶ
		h2.hello();

		//静的メソッドを呼ぶ
		Hello.goodBye();		
		
	}
}

実行結果は以下のようになります。

$ java MainSample
Hello.
こんにちは
Good bye.

なお、default修飾子とstatic修飾子を合わせての指定(static default void hello(). . .)はできませんでした。コンパイルエラー「修飾子staticとdefaultの組合せは不正です」になります。