パークのソフトウエア開発者ブログ|ICT技術(Java・Android・iPhone・C・Ruby)なら株式会社パークにお任せください -33ページ目

パークのソフトウエア開発者ブログ|ICT技術(Java・Android・iPhone・C・Ruby)なら株式会社パークにお任せください

開発の解決方法や新しい手法の情報を、パークのエンジニアが提供します。パークのエンジニアが必要な場合は、ぜひお気軽にお問い合わせ下さい。 株式会社パーク:http://www.pa-rk.co.jp/

?

あまり聞きなれない方もいるかと思いますが、
今回はESXiについて簡単ではありますが、触れてみたいと思います。

そもそもESXiとは、VMWareが無料で提供するハイパーバイザ。
サーバ機に直接インストールし、その上でいろいろなOSを動かすことができるOSです。

なぜ、このテーマかと言うと、
昨今、仮想化の技術が進み、最近では本番運用を仮想上で稼働させるケースが
増えてきており、私自身、携わる事が非常に多くなってきたため、
是非、皆さんにも名前だけでもと思いこの度のテーマとさせていただきました。

さて、このESXiの簡単なイメージ図になります。
これは言わずと知れたイメージになりますね(汗
Windows Hyper-VやOracleのVirtualboxと同じです。

イメージ)
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ


◆サポートOSについて
Windowsは95から8の最新OSまで、なんとMS-DOSまでサポートされています。
恥ずかしながら聞いたことのないOSもサポートされており、
非常に多くのOSに対応しております。
詳しくは、以下の公式サイトをご参照ください。
http://partnerweb.vmware.com/GOSIG/home.html


◆インストールについて
では、早速インストールしてみましょう...といいたいところですが、
このESXiの動作条件(ハードウェア条件)は限定的なスペックを必要とするため、
この度は、身近にインストールできる環境がなかったため文面だけで失礼いたします。

①ESXiのインストール
 - インストール対象のサーバ機にESXiの入ったメディア(CD/DVD)を挿入し起動します。
これでESXiのインストールが開始されます。
 - インストール先のドライブを選択し、確認画面でEnterキーを押すことでインストールが完了します。
- 再起動後にユーザー名やパスワード、IPアドレスなどサーバー機を動作させるうえで必要な設定をします。

②vSphere Client
 - クライアントPCにvSphere Clientをインストールします。
  もちろん、サーバー機のIPを設定します。また、ここでホストのESXiを無償で使用するためのライセンスを
  登録します。

③ゲストOSのインストール
 - vSphere ClientからESXi上にゲストOSを作成します。

 非常に簡単です。


・ハードウェア条件等のセットアップについて
 http://www.vmware.com/jp/support/support-resources/pubs/vsphere-esxi-vcenter-server-pubs

今回は文面での説明となってしまいましたが、仮想化が主流となりつつあり、導入方法などは簡単に確認することができます。もし興味を持たれたら是非一度調べてみてはいかがでしょうか。
今回はAndroid端末でOpenGL ESを動かしてみたいと思います。
そもそもOpenGL ESとは何かについてですが、元々はOpenGLという
3Dグラフィックを表示するライブラリを携帯端末など向けの組込みシステムで動かす物です。

さて、実際にどうすれば動くのかを解説してきたいと思います。
まずは前回までの記事を参考にAndroidプロジェクトを作ります。
その後メニューの「ウィンドウ」→「Android Virtual Device Manager」を起動します。

$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-AVD起動

そうしたら「Device Definitions」タブをクリックし、真ん中の「Nexus One」を選択し、
「Create AVD...」ボタンを押します。

$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-Devece_definitions選択

そうすると以下の様な画面が出るので、Targetを「Android 4.2.2 - API Level 17」に変更し、
CPU/ABIに「ARM(armeabi-v7a)」を選択して下の「OK」を押します。


$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-Create_new_AVD

そうしたら「Android Virtual Devices」タブに移動し、右側の真ん中辺りにある「Start...」ボタンを押します。
その後出てくるLaunch Optionsはそのまま「Launch」ボタンを押します。

$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-AVD

$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-Launch_options

そうするとAndroidのエミュレータが立ち上がります。
デフォルトのままだと言語が英語担っているので、お好みによって日本語に変えてください。
変え方は以下のとおりです

・settingを開く
・Language
・local languageを日本語に

$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-Language設定1

ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-Language設定2

ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-Language設定3

ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-Language設定4

ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-Language設定5

さて、事前準備が長くなりましたがここまでできたら実際にソースを書いていきましょう。
まずは必要なクラスを作ります。
以下の2つのクラスを作成してください。

・MyRenderer
 android.opengl.GLSurfaceView.Renderer(インタフェース)を継承
・MyGLView
 android.opengl.GLSurfaceViewを継承

そうしたら以下のソースを記述します。


package com.example.opengl_test;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView.Renderer;
import android.opengl.GLU;

public class MyRenderer implements Renderer {
MyCube cube = new MyCube();

@Override
public void onDrawFrame(GL10 arg0) {
// TODO 自動生成されたメソッド・スタブ
// 背景色を設定
arg0.glClearColor(0, 0, 1, 1.0f);
arg0.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

arg0.glMatrixMode(GL10.GL_MODELVIEW);
arg0.glLoadIdentity();
arg0.glTranslatef(0, 0, -3f);

arg0.glRotatef(30f, 0, 1, 0);
arg0.glRotatef(30f, 1, 0, 0);

cube.draw(arg0);
}

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO 自動生成されたメソッド・スタブ
gl.glViewport(0, 0, width, height);

gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl, 45f,(float) width / height, 1f, 50f);
}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glEnable(GL10.GL_LIGHTING);
gl.glEnable(GL10.GL_LIGHT0);
gl.glDepthFunc(GL10.GL_LEQUAL);
}

}



package com.example.opengl_test;

import android.content.Context;
import android.opengl.GLSurfaceView;

public class MyGLView extends GLSurfaceView {
private MyRenderer myRenderer;

public MyGLView(Context context) {
super(context);

myRenderer = new MyRenderer();
setRenderer(myRenderer);
}
}



package com.example.opengl_test;

import android.os.Bundle;
import android.app.Activity;

public class MainActivity extends Activity {
private MyGLView myGLView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myGLView = new MyGLView(this);
setContentView(myGLView);
}

@Override
protected void onResume(){
super.onResume();
myGLView.onResume();
}

@Override
protected void onPause(){
super.onPause();
myGLView.onPause();
}
}



package com.example.opengl_test;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

public class MyCube {
private final FloatBuffer mVertexBuffer;

public MyCube() {

float vertices[] = {
// 前
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,

// 後
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,

// 左
-0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,

// 右
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, -0.5f,

// 上
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,

// 底
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f };

ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
mVertexBuffer = vbb.asFloatBuffer();
mVertexBuffer.put(vertices);
mVertexBuffer.position(0);

}

public void draw(GL10 gl) {

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);

// Front
gl.glNormal3f(0, 0, 1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);

// Back
gl.glNormal3f(0, 0, -1.0f);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 4, 4);

// Left
gl.glNormal3f(-1.0f, 0, 0);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 8, 4);

// Right
gl.glNormal3f(1.0f, 0, 0);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 12, 4);

// Top
gl.glNormal3f(0, 1.0f, 0);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 16, 4);

// Right
gl.glNormal3f(0, -1.0f, 0);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 20, 4);
}
}


それではソースの記入が終わりビルドが終わったら早速実行します。

実行するには「実行」の「実行構成」を選びます。

$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-実行構成

そうしたら以下の様な画面が立ち上がりますので、「Android Application」をダブルクリックします。

$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-Android実行構成作成1

その後「新規構成」を選択し、右側の「Browse...」を押し今回作成したプロジェクトを選択します。
上記が済んだら下の「適用」を押し「実行」を押します。

$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-Android実行構成作成2

実行すると以下の様に四角が画面に表示されると思います。

$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ-実行

今回は登場しませんでしたが3Dプログラムは行列やベクトルなど、結構複雑な数学の知識が必要になります。
今後そういうものも解説していきたいと思います。

それでは、今回はここまで!
今回はHerokuにGitを使用してPHPをプッシュする手順を記載します。
「Git」は、Heroku上のFBアプリの更新等を行うために使用します。
そのため、まずは「Git」の説明と「インストール」について記載します。

◆「Git」とは
- オープンソースの分散型バージョン管理システムです。
   バージョン管理システムには主に集中型と分散型に分けられます。

◆ バージョン管理に関わる用語
- バージョン管理
・ファイルの変更を管理するもの。過去のファイルの編集履歴などを記録するためのツール。
- リポジトリ
・サーバにプログラム(変更履歴やコードなど)を溜めておく場所
- 集中型
・集中型はリポジトリが一箇所にのみ存在し、通常は開発者の手元ではないリモートサーバーにある。
有名なのはsubversionなど
- 分散型
分散型では開発者各自が自分専用の作業リポジトリを持ってそこにコミットをします。
(さらに、リモートサーバーにもリポジトリがある場合が多い)。
分散型のメリットとしては、リポジトリをインターネット上で公開することで複数の開発者で、
そのリポジトリを複製して開発に参加できるという点です。
- ブランチ
履歴の流れを分岐して記録していくためのものです。
分岐したブランチは他のブランチの影響を受けないため、同じリポジトリ中で複数の変更を同時に進めていくことができます。

- Gitを使って複数人で開発するユースケース。
・自分のある時点までのソース内容をGitコマンドを使用しpush(アップロード)できる
・他の人のソースとの差分を取得(pull)できる
・差分を取得すると自動的にマージされ、自分+他人の最新コードになる

- その他の分散型のバージョン管理システム
Git の他にも Bazzar、Mercurial、monotone、darcsなどがあります。

◆Gitのインストール
GitはHerokuにPHPファイルをプッシュをするために使用します。
●ダウンロード
- 以下のURLから「Git-1.8.3-preview20130601.exe 」(2013/08/26時点)をダウンロードします。
 http://code.google.com/p/msysgit/downloads/list
 ※「Full installer for official Git for Windows」との説明が記載されています。
●インストール
- ダウンロードした「Git-1.8.3-preview20130601.exe」を実行します。
- 基本的にはデフォルト設定で問題ありません。
  -- 一部インストール画面の説明 --
→「Adjusting your PATH environment How would you like to use Git from the command line?」
上記のインストール設定画面は、インストール時にCUIの「Bash」が一緒にインストールされますが、
Gitコマンドを「Bash」のみで使うか、Windowsのコマンドプロンプトでも使用可能とするかどうかの設定画面です。(どちらの設定でも構いません)

→「Configuring the line ending conversions How shold Git treat line endings in text files?」
上記のインストール設定画面は、改行コードの設定画面です。
基本的にはデフォルトの設定で問題ないと思います。


次回はインストールしたGitを使用して、Herokuへのプッシュ方法を記載します。
こんにちは。ゆんぼう です。

今回は、1つのiOS Developer Programアカウントで
複数のMacintoshアカウントにて実機インストールする手順を記載します。

iOS Developer Program に登録したiOS アプリ を複数人で開発する場合、
1つのiOS Developer Programアカウントを共有する必要があります。

実機インストールする場合、Macintoshアカウントに開発用のプロビジョニングプロフィールを登録します。
しかし、登録できるMacintoshアカウントは一つとなります。
そこで、登録済みのMacintoshアカウントから証明書ファイルを作成して、
他のアカウントでiOSアプリを実機インストールしてみます。

こちらで実施した環境は下記の通りです。
・MacBook Air
・OSX version 10.8.4
・Xcode version 4.6.3

証明書ファイルの作成
登録済みのアカウントで、証明書ファイルを作成します。

デスクトップで「Finder」のアイコンをクリックします。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

「Finder」が起動します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

「よく使う項目」から「アプリケーション」を選択します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

「ユーティリティ」フォルダの「キーチェーンアクセス」を選択します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

「キーチェーンアクセス」が起動します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

「キーチェーン」欄から「ログイン」を選択します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

「分類」欄から「証明書」を選択します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

「iPhone Developer:【iOS Developer Program登録者名】」の証明書を選択して、
証明書の三角アイコンをクリックします。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

証明書の秘密鍵が表示されます。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

秘密鍵を副ボタンクリックして、コンテキストメニューを表示します。
「"【秘密鍵名】"を書き出す...」を選択します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

証明書ファイルの設定を行います。
・名前:証明書ファイルの名前になります。
・場所:証明書ファイルを保存する場所となります。
・フォーマット:証明書ファイルのフォーマットになります。「個人情報交換(.p12)」を選択してください。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

証明書ファイル用のパスワードを設定します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

Macintoshアカウントのパスワードを入力します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

Finderで出力した証明書ファイルを確認します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ


証明書ファイルの登録
実機インストールを行いたいアカウントに移り、証明書ファイルをダブルクリックします。
「キーチェーンアクセス」に以下のリストが表示されたら、正しく登録されたことになります。
・「Apple Worldwide Developer Relations Certification Authority」
・「iPhone Developer: 【iOS Developer Program登録者名】」


プロビジョニングプロフィールの読み込み
実機インストールを行いたいアカウントでプロビジョニングプロフィールを読み込みます。
Finderから「Xcode」を選択します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

「Xcode」が起動します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

Xcodeの「Organizer」を選択します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

「Organizer」が起動します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

Organizerで「Devices」を選択します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

Devicesで「Provisioning Profiles」を選択します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

Provisioning Profilesの右下の「Refresh」を選択します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

iOS Developer Program のIDとパスワードを求められるので入力します。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

プロビジョニングプロフィールを正しく取得できれば、
リストに「Valid profile」と表示されます。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ


実機インストール
実機をMacintoshに接続します。
「Organizer」で接続した実機が正しく認識されていることを確認します。
正しく認識されている場合は、右側に緑色のアイコンが表示されます。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

「Xcode」のスキームで、実機を選択して「Run」で実機インストールが開始されます。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

なお、「/usr/bin/codesign failed with exit code 1」と表示された場合は、
証明書が正しく設定できていませんので、最初からやり直してください。
$ソフトウエア開発(android・iphone・windows・java等)の事なら株式会社パークにお任せください_パク太郎のソフトウエア開発者ブログ

 すべての get メソッドに対して set メソッドを書きたい衝動に抵抗してください。可変にすべきかなり正当な理由がない限り、クラスは不変であるべきです。

 不変性が実用的でないクラスもあります。もし、クラスを不変にできないのであれば、その可変性をできるだけ制限すべきです。オブジェクトが存在できる状態の数を減らすことで、オブジェクトについて明確に論理的に考えるのが容易となり、エラーの可能性も減ります。したがって、final にできないやむを得ない理由がない限り、すべてのフィールドを final にしてください。

Effective Java 第2版
項目15 可変性を最小限にする

こんにちわ。ちかです
Scalaのimmutableなコレクションについてという記事を読んで
改めて可変性/不変性について考えています。

不変オブジェクトと再代入 (再束縛) 不可能な変数

不変と言われてもピンとこないかもしれません。
注意してほしいのは、不変オブジェクト再代入不可能な変数の違いです。

(変数への再代入も最小限にすべきであることに違いはありませんが。)

Java で言うと、、、

  • String不変クラスです。
    String のインスタンスは不変オブジェクトです。
  • StringBuilder可変クラスです。
    StringBuilder のインスタンスは可変オブジェクトです。
  • final 修飾子つきの変数は再代入不可能です。
    再代入不可能な変数可変オブジェクトを参照することもあれば、
    再代入可能な変数不変オブジェクトを参照することもあります。
  • あるオブジェクトのフィールド (変数) への再代入は、そのオブジェクトの変更です。
    あるオブジェクトのフィールド (オブジェクト) の変更は、そのオブジェクトの変更です。
    初期化済みのオブジェクトに対して変更する手段がないとき、そのオブジェクトは不変と言えます。
public final class Immutability {
    public static void main(String[] args) {
        {
            StringBuilder stringBuilder = new StringBuilder("mutable");
            final StringBuilder finalStringBuilder = stringBuilder;
 
            // ※ StringBuilder の一部のインスタンスメソッドはオブジェクトを変更する
            finalStringBuilder.setLength(0);                   // オブジェクトの変更
            finalStringBuilder.append("muted");                // オブジェクトの変更
            System.out.println(stringBuilder);                 // "muted"
            System.out.println(finalStringBuilder);            // "muted"
 
            // ※ 変数への再代入はオブジェクトを変更しない
            stringBuilder = new StringBuilder("reassigned");   // 変数への再代入
            System.out.println(stringBuilder);                 // "reassigned"
            System.out.println(finalStringBuilder);            // "muted"
 
            // ※ final 修飾子つきの変数への再代入はコンパイルエラーを起こす
            finalStringBuilder = new StringBuilder();          // コンパイルエラー
        }
        {
            String string = "immutable";
            final String finalString = string;
 
            // ※ 変数への再代入はオブジェクトを変更しない
            string = "reassigned";                             // 変数への再代入
            System.out.println(string);                        // "reassigned"
            System.out.println(finalString);                   // "immutable"
 
            // ※ String のインスタンスメソッドはいずれもオブジェクトを変更しない
            //    (substring() などは新たな String オブジェクトを生成する)
            string += finalString.substring(5);                // 変数への再代入
            System.out.println(string);                        // "reassignedable"
            System.out.println(finalString);                   // "immutable"
 
            // ※ final 修飾子つきの変数への再代入はコンパイルエラーを起こす
            finalString = "reassigned";                        // コンパイルエラー
        }
    }
}

不変性がもたらすメリット (可変性の問題点)

IBM の Javaの理論と実践: 可変性か、不変性か?というアーティクルに、不変性のメリットがいくつも紹介されています。(ぶっちゃけると、私の今回の記事は上記アーティクルの劣化コピーと言えます(苦笑))
紹介されているメリットのうち、コード例があって直感的に分かりやすい部分を以下に引用します。
(スレッドセーフ性など他のメリットは上記リンク先を参照してください。)

オブジェクトが可変の場合は、そのオブジェクトへの参照を保存するときに、いくつか気をつけなければならない点があります。リスト1のコードを見てください。ここではスケジューラーで実行する2つのタスクをキューに入れています。このコードは、最初のタスクを今実行し、2番目のタスクを次の日に実行することを目的としています。

リスト1. 可変であるDateオブジェクトの潜在的な問題点

Date d = new Date();
scheduler.scheduleTask(task1, d);
 
d.setTime(d.getTime() + ONE_DAY);
scheduler.scheduleTask(task2, d);

Date は可変であるため、scheduleTask メソッドでは、十分な注意を払って (clone() などを使用して) 防御的に日付パラメーターを内部データ構造にコピーする必要があります。そうでないと、task1task2 は両方とも明日実行される可能性があり、それでは予定の動作と異なります。ひどい場合には、タスク・スケジューラーが使用する内部データ構造が破損する可能性もあります。scheduleTask() のようなメソッドを記述する場合には、日付パラメーターを防御的にコピーすることを忘れがちです。これを忘れると、しばらくの間は表面化しないものの、いざ起こってみると追跡に長い時間のかかるわかりにくいバグを作ることになります。Date クラスが不変であれば、この種のバグが発生することはないでしょう。

防御的コピー

上に引用したとおり、可変オブジェクトを扱う場合には注意が必要です。
可変オブジェクトを外部から変更されないよう安全に保持するには、防御的コピーというイディオムを使います。
(上の引用にも「防御的にコピーする」的な言い回しが出てきますが、このことです。)

public final class DateOwner {
    private final Date date;
 
    public DateOwner(Date date) {
        // 防御的コピー:
        //   引数として渡された可変オブジェクトはそのまま保持せず、コピーを保持する
        //   (渡された後で変更されても this.date に影響しないように)
        this.date = date.clone();
    }
 
    public Date getDate() {
        // 防御的コピー:
        //   保持している可変オブジェクトはそのまま返さず、コピーを返す
        //   (dateOwner.getDate().setXX() のような変更が this.date に影響しないように)
        return date.clone();
    }
}

保持するオブジェクトのデータ型によって防御的コピーの生成方法は変わってきます。
(clone(), コピーコンストラクタ, etc)

可変オブジェクトカプセル化する際には、防御的コピーは必須といえるでしょう。
ですが、人間は、間違いなく、忘れます。

不変オブジェクトを扱うときは、引数をそのまま保持しても、保持しているオブジェクトをそのまま返しても、内部状態を変更・破壊される心配はありません。

不変クラスのレシピ

Java で不変クラスを作成するには、次の5つの原則に従います。(『Effective Java 第2版』項目15より)

  1. オブジェクトの状態を変更するためのいかなるメソッドも提供しない
  2. クラスが拡張できないことを保証する
    (クラスを final にする。もしくはコンストラクタを private にして static ファクトリメソッドを提供する。)
  3. すべてのフィールドを final にする
  4. すべてのフィールドを private にする
  5. 可変コンポーネントに対する独占的アクセスを保証する
    (可変オブジェクトを外部からそのまま受け取って保持したり、外部へそのまま渡したりしない。必要なら防御的コピーを行う。)
例として OpenJDK の
String クラスの実装
AbstractStringBuilder クラス(StringBuilder クラス の実装補助基底クラス) を
見比べてみるとおもしろいかもしれません。

さいごに

C/C++ では、変数 (関数の引数や戻り値も含む) やメンバー関数に const をつけることで、
オブジェクトの不変性コンパイラにチェックさせることができます。

Java には C/C++ の const に相当する機能がない (final では不変性をチェックできない) ため、
人間がオブジェクトの変更注意してコーディングする必要がある、ということです。

(C/C++ では人間が注意して const をつけていくわけですが、「const により、他のプログラマにもコンパイラにも、指定したオブジェクトの値を変更しないよう知らせることができる」のは大きな大きな利点なのです。
Effective C++ 第3版』3項「可能ならいつでもconstを使おう」参照。)


ということで
この言葉を贈って終わりにします。

人は「変わらざる中心」がなければ、変化に耐えることができない。

スティーブン・R・コヴィー

ついでに

Scala の不変リスト scala.collection.immutable.List のインスタンスは、初期化後に要素の追加・削除・再代入 (再束縛) はできませんが、要素を取得して変更することはできます。

scala> val list = List(java.util.Calendar.getInstance())
list: List[java.util.Calendar] = List(java.util.GregorianCalendar[time=1371644777333,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Tokyo",offset=32400000,dstSavings=0,useDaylight=false,transitions=10,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2013,MONTH=5,WEEK_OF_YEAR=25,WEEK_OF_MONTH=4,DAY_OF_MONTH=19,DAY_OF_YEAR=170,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=3,AM_PM=1,HOUR=9,HOUR_OF_DAY=21,MINUTE=26,SECOND=17,MILLISECOND=333,ZONE_OFFSET=32400000,DST_OFFSET=0])
 
scala> list(0).getTime()
res0: java.util.Date = Wed Jun 19 21:26:17 JST 2013
 
scala> list(0).add(java.util.Calendar.YEAR, 10)
 
scala> list(0).getTime()
res2: java.util.Date = Mon Jun 19 21:26:17 JST 2023

したがって、要素のデータ型が不変であれば不変に、要素のデータ型が可変であれば可変になります。
(防御的コピーをするためにはコピーの生成方法を知る必要があるので、制約のないジェネリッククラスのインスタンスは防御的コピーができないのかもしれませんね。。)