Akira's Blog

神奈川在住のITエンジニアの雑記です。主にプログラミング(Perl, Java etc)やネットワーク技術について、ちょっとずつ書いていきます。

Javaの内部の文字コード

Javaの内部の文字コードUTF-16 である。

なので、Javaのプログラムが外部から読み込んだ SJISUTF-8などの文字列は、JavaのStringオブジェクトになる際に (内部コードである) UTF-16 に変換される。

例えば、以下のサンプルプログラムの通り、(外部文字列に見立てた)「あいう」という文字列に対して、Windows-31J としてバイト列を作成したもの(bytesWin31J)と、UTF-8 としてバイト列を作成したもの(bytesUTF8)は、当然であるが異なっている。
一方で、各々をもとに生成したStringオブジェクト(stringFromWin31J, stringFromUTF8)については、ともに中身の文字列が UTF-16 に変換されているため、同じものになっている。

import java.io.UnsupportedEncodingException;

public class testInternalCode {

    public static void main(String[] args) {
        String str = "あいう";
        try {
            byte[] bytesWin31J = str.getBytes("Windows-31J");
            byte[] bytesUTF8   = str.getBytes("UTF-8");
            
            dumpHexString(bytesWin31J);  // 82 a0 82 a2 82 a4 
            dumpHexString(bytesUTF8);    // e3 81 82 e3 81 84 e3 81 86 
            
            String stringFromWin31J = new String(bytesWin31J, "Windows-31J");
            String stringFromUTF8   = new String(bytesUTF8,   "UTF-8");
            
            if (stringFromWin31J.equals(stringFromUTF8)) { // True
                System.out.printf("stringFromWin31J is same as stringFromUTF8");
            }
            
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
    
    private static void dumpHexString(byte[] bytes) {
        for (byte b : bytes) {
            System.out.printf("%02x ", b);
        }
        System.out.println();
    }
}

Javaでスレッドを作成する時の注意点

まず、Javaでスレッドを作成する基本的な方法は、以下の2つである。

  • Threadクラスを継承したクラスのオブジェクトを生成して、そのオブジェクトのstart()メソッドを実行する。
  • Runnableインタフェースを実装したクラスのオブジェクトを生成し、それを引数としてThreadクラスのオブジェクトを生成し、そのThreadクラスのオブジェクトのstart()メソッドを実行する。

後者の方法では、Threadクラスのオブジェクトを生成して、それ経由で実施したい処理のスレッドを実行する必要がある。そうしないで、Runnableインタフェースを実装したクラスのオブジェクトのrun()メソッドを直接呼び出してしまうと、マルチスレッド(並行処理)にならない。

つまり、以下のように書いてしまうと、並行処理にならない。
# r1 の処理が全部終わってから、r2 が実行されてしまう。

public class ThreadTest {
  public static void main(String[] args) {
    System.out.println("Start!");
    
    Runnable r1 = new Runnable() {
      public void run() {
        try {
          for (int i=0; i<100; i++) {
            System.out.println("Runnable1" + ": " + i);
            Thread.sleep(100);
          }
        } catch (Exception ex){
          System.out.println(ex.getStackTrace());
        }
      }
    };
      
    Runnable r2 = new Runnable() {
      public void run() {
        try {
          for (int i=0; i<100; i++) {
            System.out.println("Runnable2" + ": " + i);
            Thread.sleep(100);
          }
        } catch (InterruptedException ex) {
          System.out.println(ex.getStackTrace());
        }
      }
    };

    //-----------------------------------------------
    // After r1 finished completely, r2 is executed.
    //-----------------------------------------------
    r1.run();
    r2.run();
  }
}


並行処理にしたいならば、

r1.run();
r2.run();

の箇所を、上述の方法に従って、

Thread thr1 = new Thread(r1);
Thread thr2 = new Thread(r2);
thr1.start();
thr2.start();

と書く必要がある。

AutoMDI/MDI-X

以前ネットワークを構築した時、ネットワーク機器同士をLANケーブルで接続する時はクロスケーブルで、ネットワーク機器と端末機器を接続する時はストレートケーブルで接続していた。

これは、LANポートに以下の2種類があり、

  • MDI (Medium Dependent Interface) 型:コンピューターなどの端末機器で使用される。
  • MDI-X (Medium Dependent Interface Crossover) 型:ネットワーク機器で使用される。

MDI 同士や MDI-X 同士を繋ぐときはクロスケーブルで、MDI と MDI-X を繋ぐときはストレートケーブルで接続する必要があるからである。

 

ただ最近では、「Auto MDI/MDI-X」という、通信相手のLANポートの状態を見て自動的に MDI と MDI-X を切り替える機能を搭載したネットワーク機器が一般的になっており、接続する際に LANケーブルの種類 (クロス/ストレート) を気にする必要はなくなってきている。便利な世の中になってきましたね。

Javaのスタックトレースを解析する時のコツ

フレームワークなどを多用した大規模なJavaプログラムにおいて、エラーが起きてログにスタックトレースが出ると、その量が膨大過ぎてどうやって解析すれば良いのか困ることがある。そんな時に自分が心掛けていることを書く。

まず、アプリケーション独自の部分を見る

通常、スタックトレースには、フレームワークのコードの部分とアプリケーション独自のコードの部分が入り混じる。問題を引き起こしているのは、フレームワークよりアプリケーション独自の部分であることの方が多いので、まずはアプリケーション独自の部分を見て、その部分のコードを解析するのが良いと思う。それで、アプリケーション独自の部分に問題がなければ、フレームワークの方を疑う。

一番下の「Caused by」ブロックの一番上のメソッドのコードを見る

スタックトレースが「Caused by ・・・」で多段になっていたら、一番下の「Caused by」ブロックの一番上のメソッドが大元の例外の発生源なので、そのソースコードを見ると何か分かるかも。

例えば、スタックトレース

ExceptionA
    at method3
    at method2
    at method1
Caused by: ExceptionB
    at method5
    at method4
    at method3
Caused by: ExceptionC
    at method8
    at method7
    at method6
    at method5

となっていたら、大元の例外の発生源は method8 なので、その処理内容を見れば何か分かるかもしれない。なお、直近の例外の発生源であるmethod3 を見ると有効な時もある。


ちなみに、上記についての簡単なサンプルプログラムと、その実行結果を以下に示す。

public class ExceptionChainSample {

    public static void main(String[] args) {
        new ExceptionChainSample().exec();
    }

    private void exec() {
        try {
            method1();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    private void method1()  throws Exception {
        method2();
    }
    private void method2()  throws Exception {
        method3();
    }
    private void method3() throws Exception {
        try {
            method4();
        } catch (Exception ex) {
            throw new TransformedException(ex);
        }
    }
    private void method4() throws Exception {
        method5();
    }
    private void method5() throws Exception {
        try {
            method6();
        } catch (Exception ex) {
            throw new TransformedException(ex);
        }
    }
    private void method6() throws Exception {
        method7();
    }
    private void method7() throws Exception {
        method8();
    }
    private void method8() throws Exception {
        throw new OrgException("test exception");
    }

    private class OrgException extends Exception {
        OrgException(String msg) {
            super(msg);
        }
    }
    private class TransformedException extends Exception {
        TransformedException(Throwable th) {
            super(th);
        }
    }
}


実行結果は以下。

ExceptionChainSample$TransformedException: ExceptionChainSample$TransformedException: ExceptionChainSample$OrgException: test exception
	at ExceptionChainSample.method3(ExceptionChainSample.java:24)
	at ExceptionChainSample.method2(ExceptionChainSample.java:18)
	at ExceptionChainSample.method1(ExceptionChainSample.java:15)
	at ExceptionChainSample.exec(ExceptionChainSample.java:9)
	at ExceptionChainSample.main(ExceptionChainSample.java:4)
Caused by: ExceptionChainSample$TransformedException: ExceptionChainSample$OrgException: test exception
	at ExceptionChainSample.method5(ExceptionChainSample.java:34)
	at ExceptionChainSample.method4(ExceptionChainSample.java:28)
	at ExceptionChainSample.method3(ExceptionChainSample.java:22)
	... 4 more
Caused by: ExceptionChainSample$OrgException: test exception
	at ExceptionChainSample.method8(ExceptionChainSample.java:44)
	at ExceptionChainSample.method7(ExceptionChainSample.java:41)
	at ExceptionChainSample.method6(ExceptionChainSample.java:38)
	at ExceptionChainSample.method5(ExceptionChainSample.java:32)
	... 6 more


エラーが起きて膨大なスタックトレースが出力されると途方に暮れそうになるが、ここに書いたようなテクニックを駆使しつつ、何とか対応していきたい。

ファイルの中身を16進ダンプするスクリプトをちょっとエンハンス

以前、ファイルの中身を16進ダンプで出力するperlスクリプト dumpHexStr.pl を紹介した。
akrad.hatenablog.com

ところが、これではファイル内に改行があった場合にそれが省かれてしまうので、改行も含めて16進ダンプするようにした。

use strict;
use warnings;

my $inFile = $ARGV[0];
if (!-f $inFile) {
    die "There is not $inFile file...";
}
my $outFile = "dumped_" . $inFile;

open(my $inFh,  "< $inFile")  or die("Error :$!");
open(my $outFh, "> $outFile") or die("Error :$!");
binmode($inFh);

while (my $line = <$inFh>) {
    my $hexDump = unpack("H*", $line);
    print($outFh $hexDump)
}

close($inFh);
close($outFh);

使い方は、以前のものと同じ。


なお、

binmode($inFh);

としているのは、ファイルから読み込んだ改行コードをそのまま扱うため。
# Windows上で実機検証したところ、binmode しない状態だと、読み込んだ CRLF(0d0a) が LF(0a) に変換されてしまった。

binmodeしないと、perl は ascii mode で改行を読む込むわけだが、perl は元々UNIX上で動くプログラムなので、UNIXのデフォルトの改行コードである LF(0a) に勝手に変換されるのではないか、と推測している。

メソッド間で変数をやり取りする方法

Javaでメソッド間で変数をやり取りする方法について書く。まずは一覧。 

  • 引数で渡す
  • メンバ変数に格納して渡す
  • static変数に格納して渡す
  • シングルトンなクラスのオブジェクトに格納して渡す

以下、それぞれの使いどころと注意点について、少し詳しく説明する。

 

引数で渡す

 メソッドが呼び出しの階層関係にある場合は、これがシンプルで使いやすい。ただ、何でもかんでも引数で渡すようにすると、メソッドの引数の数が多くなってしまうので注意。

 

メンバ変数に格納して渡す

クラス内の多数のメソッドから使用される変数の場合、いちいち引数で渡すのは面倒なので、メンバ変数に格納してそこから使うのが良いと思う。メンバ変数はクラス内のグローバル変数だと考えれば良い。なので、マルチスレッドプログラムで、そのメンバ変数を変更する処理が複数同時に走る可能性がある場合は、排他処理を実装しないといけない。

 

static変数に格納して渡す

static変数はオブジェクトではなくクラス側が持っている変数であり、プロセス内で一意のものである。つまり(C言語における)グローバル変数である。ただ、static変数はfinal指定子を付けて定数として扱われることがほとんどなので、メソッド間の変数のやり取りという観点では、使われることはまあないだろう。

 

シングルトンなクラスのオブジェクトに格納して渡す

staticな変数と同様、プロセス内で一意のもので、グローバル変数(オブジェクト)である。こちらもstaticな変数と同様、メソッド間の変数の受け渡しという観点ではあまり使われない。これはJavaでは基本的にはグローバル変数を使わない方針であることによるだろう。ただ、離れたクラスのメソッド間で変数をやり取りしたい場合は、これを使うことになるだろう。(本来はこのようなクラス設定をすべきではないが)

 

他にもあるかもしれないが、とりあえず自分が知っているものを挙げてみた。

Japan IT Week 春 2018

「Japan IT Week 春 2018」に行ってきた。あまり時間がなかったので、ざっくりとしか回れなかったが、以下に感想を書く。

  • AIのエリアはかなり盛況だった。理論が難しい分野なので、具体的な例を挙げた分かりやすい説明を心掛けている会社が多かった。社長自らがディープラーニングの説明を行っている会社もあった。その説明が分かりやすく、「この社長さん技術肌なんだな~」と思った。あと、自分もそうであるが、AIで何がどこまで出来るのか?ということがちゃんと分かっていないので、AI導入について顧客側は(提供側もか?)手探りしながら進めることになりそう。

 

  • IoTのエリアでは、各機器のセンサーから収集した情報を分析して、障害検知や今後の予測分析を行うサービスが多かった(特に工場系)。また、IoTでいろんなデバイスがネットワークに繋がることになり、不正な遠隔操作などセキュリティ的な懸念が出てくるため、IoTにおけるセキュリティ対策をアピールする企業も多く見られた。

 

  • AIもIoTもそうであるが、出来合いのパッケージ製品を提供するというわけではなく、導入企業の実力や実情に合わせて、オーダーメイドでサービスを提供するというかたちが主流になるだろう。出展していた企業(サービス提供側)の多くも、顧客と一緒に作り上げていきましょう、というスタンスだったと思う。なので、顧客要望をいかに正確に汲み取れるかが顧客満足のカギを握ることになる。

 

  • AIを用いた「働き方改革」を謳う企業も多かった。例えば、コールセンターにおけるAIの活用など。あと、今後のEXPOのテーマの一つは「働き方改革」だったはず。AIをうまく適用すれば、担当者の仕事量が減り、残業しなくてよくなる⇒働き方改革(長時間残業低減)、という理論だと思うのだが、そうなった場合、企業側としては経費削減のため担当者の数を減らすと思うので、結局、担当者は楽にならないんじゃないだろうか・・・とか思ってしまった。

 

  • ソフトウェアのテストのサービスを提供する企業が多く出展していた。昨今の複雑化したシステムのテストのすべてを自社(開発元)で行うのが困難になってきたので、こういったサービスへの要望が増えてきたのだろうか。

展博は最新の流行をキャッチするのに有効なので、今後も定期的に行きたい。