Spring徹底入門のチュートリアルアプリを「とりあえず」動かす

www.shoeisha.co.jp

前置き

Springの開発案件、いずれ関わりたいと思いつつ ひとまず業務で触れる機会なく、興味だけ持ったので勉強として徹底入門を読んだ。 執筆時点よりSpringのバージョン上がってたりで本の通りにチュートリアルアプリ作ってもエラーが出てしまう。 (もしかしたら自分が購入した本の版が古かったり?は確認していない) 現在時点で、「とりあえず」動かすために修正した内容をメモとして載せる。

修正した内容は3点

とりあえず動くアプリを作るために、下記3点を本の内容から修正した

1. schema.sqlのカラムのデータ型変更

reservable_roomとreservationテーブルに「reserved_date」ってカラムがある。
本書通りDATE型だと下記のように怒られる。
DATE型→TIMESTAMP型に変更したら解決した。

Schema-validation: wrong column type encountered in column [reserved_date] in table [reservable_room];
found [date (Types#DATE)], but expecting [timestamp (Types#TIMESTAMP)]
2. UserエンティティクラスにProxyアノテーションを追加する

アノテーションない場合に下記のようなエラー。

org.hibernate.LazyInitializationException: could not initialize proxy [mrs.domain.model.User#aaaa] - no Session

ちゃんと調べてないけど遅延ロードエラー?が関係してる?
暫定対策だろうけど、下記のようにアノテーション追加でひとまず解決した。

package mrs.domain.model;

import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.Proxy;

@Entity
@Proxy(lazy = false) // これを追加
@Table(name = "usr")
public class User {
    @Id
    private String userId;
    private String password;
    private String firstName;
    private String lastName;
    @Enumerated(EnumType.STRING)
    private RoleName roleName;

        // 以下省略(本書のサンプルコード通り)
}
3. JpaRepositoryインタフェースのfindOne()→getOne()に変更

本書通りfindOne()メソッド使うと、IDE(自分はeclipse)でエラー教えてくれる。
getOne()メソッドに変換して解決。

まだまだ物足りない

そもそも一読しただけでまだ全然自分は理解できてないし「とりあえず」動かすだけのことしかできてない。 Springに詳しい方の記事なんか見ながら、モチベーションあるときにまた修正を加えたい。
令和時代に「Spring入門」「Spring徹底入門」を読むとき気をつけるべきN個のこと - Qiita

非推奨で怒られたりしてる箇所も結構ある。 いまは業務でSpring使わないこと確定してモチベーションがないのでここまで。 いずれSpringに関わる案件に行きたいとは思ってるので、 コミュニティに参加したりして勉強を継続していこうとおもいました。

Android Studio + Kotlinで簡単なじゃんけんゲームを作る

前置き

自社待機期間もあと2日。 1カ月くらい前に作成してGooglePlayにアップして満足してた アプリを復習として作り方書いておく。アップしたアプリは下記。

5回先取じゃんけん - Apps on Google Play

GitHub - n-yata/RockPaperScissors

まずはプロジェクト作成

New ProjectからEmpty Activityを選択し『Next』ボダン
f:id:n-yata:20201228131408p:plain

アプリ名を適当に入力、Minimum SDKはAPI16にした。 (ここの設定でAndroid OSのバージョンどこまで対応するかを指定する)
f:id:n-yata:20201228131402p:plain

『Finish』ボタン押してビルド終わるまで少し待つ。 下記の画面が出たらプロジェクト作成完了。
f:id:n-yata:20201228132059p:plain

build.gradleの編集

下記の画像の通り、build.gradle(:app)の修正する。 pluginsの中に『id 'kotlin-android-extensions'』を追記し 『Sync Now』を押す
f:id:n-yata:20201228132528p:plain

画像をダウンロードして配置

じゃんけんグー・チョキ・パーの画像をフリー画像から好きなのダウンロード。 自分は下記URLから使用させてもらった。
じゃんけんのイラスト「グー・チョキ・パー」 | かわいいフリー素材集 いらすとや

画像の配置先は下記の通り。
f:id:n-yata:20201228133239p:plain

画面のレイアウトを作る

string.xmlで画面上で使用する文字を定義しとく
f:id:n-yata:20201228134456p:plain

<resources>
    <string name="app_name">5回先取じゃんけん</string>
    <string name="win_text">勝ち</string>
    <string name="lose_text">負け</string>
    <string name="gu_text">グー</string>
    <string name="choki_text">チョキ</string>
    <string name="pa_text">パー</string>
    <string name="reset_text">最初からやりなおす</string>
    <string name="yourHand_text">あなた</string>
    <string name="androidHand_text">相手</string>
</resources>

画面レイアウトはactivity_main.xmlを編集する。 f:id:n-yata:20201228133806p:plain

記載したXMLは下記のとおり。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/winText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="68dp"
        android:layout_marginLeft="68dp"
        android:layout_marginTop="16dp"
        android:text="@string/win_text"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/loseText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="64dp"
        android:layout_marginRight="64dp"
        android:text="@string/lose_text"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/guBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginLeft="32dp"
        android:layout_marginBottom="100dp"
        android:text="@string/gu_text"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/chokiBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginLeft="10dp"
        android:layout_marginEnd="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="30dp"
        android:text="@string/choki_text"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        app:layout_constraintBottom_toTopOf="@+id/resetBtn"
        app:layout_constraintEnd_toStartOf="@+id/paBtn"
        app:layout_constraintStart_toEndOf="@+id/guBtn" />

    <Button
        android:id="@+id/paBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="32dp"
        android:layout_marginRight="32dp"
        android:layout_marginBottom="100dp"
        android:text="@string/pa_text"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <Button
        android:id="@+id/resetBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="20dp"
        android:text="@string/reset_text"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        app:backgroundTint="@android:color/holo_blue_dark"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <ImageView
        android:id="@+id/yourHandImage"
        android:layout_width="140dp"
        android:layout_height="180dp"
        android:layout_marginStart="32dp"
        android:layout_marginLeft="32dp"
        android:layout_marginTop="150dp"
        app:layout_constraintBottom_toTopOf="@+id/guBtn"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/winText"
        app:srcCompat="@drawable/janken_gu" />

    <ImageView
        android:id="@+id/droidHandImage"
        android:layout_width="140dp"
        android:layout_height="180dp"
        android:layout_marginStart="32dp"
        android:layout_marginLeft="32dp"
        android:layout_marginTop="150dp"
        app:layout_constraintBottom_toTopOf="@+id/paBtn"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/yourHandImage"
        app:layout_constraintTop_toBottomOf="@+id/loseText"
        app:srcCompat="@drawable/janken_gu" />

    <TextView
        android:id="@+id/resultText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="76dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:gravity="center_horizontal"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/yourHandText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="80dp"
        android:layout_marginLeft="80dp"
        android:text="@string/yourHand_text"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        app:layout_constraintBottom_toTopOf="@+id/yourHandImage"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/androidHandText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="80dp"
        android:layout_marginRight="80dp"
        android:text="@string/androidHand_text"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        app:layout_constraintBottom_toTopOf="@+id/droidHandImage"
        app:layout_constraintEnd_toEndOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

本来はデザインビューでいろいろ調整しながらだけど割愛。
公式のチュートリアルがすごくわかりやすかったです。
初めてのアプリを作成する  |  Android デベロッパー  |  Android Developers

Kotlinで処理を書いてく

1画面だけの簡単なアプリなので編集するのはMainActivityのみ。
f:id:n-yata:20201228134852p:plain

package com.nyata.rockpaperscissors

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    private var yourHand = 0
    private var droidHand = 0
    private var winCount = 0
    private var loseCount = 0
    private var gameStart = false

    /**
     * アクティビティ生成時の処理
     */
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        guBtn.setOnClickListener{
            if(gameStart) {
                yourHand = 0
                compareHand()
            }
        }

        chokiBtn.setOnClickListener{
            if(gameStart) {
                yourHand = 1
                compareHand()
            }
        }

        paBtn.setOnClickListener{
            if(gameStart) {
                yourHand = 2
                compareHand()
            }
        }

        resetBtn.setOnClickListener{
            onResume()
        }
    }

    /**
     * ユーザーの操作受付処理
     */
    override fun onResume() {
        super.onResume()
        winCount = 0
        loseCount = 0
        winText.text = getString(R.string.win_text)
        loseText.text = getString(R.string.lose_text)
        resultText.text = "じゃんけん"

        // ゲーム開始状態にする
        gameStart = true
        // リセットボタンを非表示
        resetBtn.visibility = View.INVISIBLE

    }

    /**
     * 自分とCPUの手の比較をする
     */
    private fun compareHand(){
        resultText.text = "ぽん!"
        // CPUの手をランダムで決定
        droidHand = (0..2).random()
        // 画面にイメージを表示する
        showHand()

        when(yourHand){
            // グー
            0 -> {
                if(droidHand == 0){
                    // do nothing
                }else if(droidHand == 1){
                    winCount++
                    winText.text = getString(R.string.win_text) + winCount
                }else if(droidHand == 2){
                    loseCount++
                    loseText.text = getString(R.string.lose_text) + loseCount
                }
            }
            // チョキ
            1 -> {
                if(droidHand == 0){
                    loseCount++
                    loseText.text = getString(R.string.lose_text) + loseCount
                }else if(droidHand == 1){
                    // do nothing
                }else if(droidHand == 2){
                    winCount++
                    winText.text = getString(R.string.win_text) + winCount
                }
            }
            // パー
            2 -> {
                if(droidHand == 0){
                    winCount++
                    winText.text = getString(R.string.win_text) + winCount
                }else if(droidHand == 1){
                    loseCount++
                    loseText.text = getString(R.string.lose_text) + loseCount
                }else if(droidHand == 2){
                    // do nothing
                }
            }
        }

        if(winCount == 5){
            resultText.text = "あなたの勝ちです!"
            gameStart = false
            resetBtn.visibility = View.VISIBLE
        }else if(loseCount == 5){
            resultText.text = "あなたの負けです・・・"
            gameStart = false
            resetBtn.visibility = View.VISIBLE
        }else{
            // do nothing
        }
    }

    private fun showHand(){
        when(yourHand){
            0 -> yourHandImage.setImageResource(R.drawable.janken_gu)
            1 -> yourHandImage.setImageResource(R.drawable.janken_choki)
            2 -> yourHandImage.setImageResource(R.drawable.janken_pa)
        }

        when(droidHand){
            0 -> droidHandImage.setImageResource(R.drawable.janken_gu)
            1 -> droidHandImage.setImageResource(R.drawable.janken_choki)
            2 -> droidHandImage.setImageResource(R.drawable.janken_pa)
        }
    }
}

参考にした本は下記。

www.shoeisha.co.jp

Kotlin勉強してはまったこと

前置き

Webアプリもほんの少し作れるようになったので、
Androidアプリに手を出してみた。はじめてのKotlin。
落とし穴がいっぱい。これからも学習してみて、
嵌まってきたら定期的にこちらにアップしようと思う。
あと用語も間違えてるの気づいたらたまに修正する。

継承可能なクラスの作成

Kotlinで作成するクラスは、デフォルトで継承不可とのこと。
継承可能にするためにはopenモディファイアなるものをつける必要がある。

継承可能なクラスを作成する (open, abstract, override) | まくまくKotlinノート

下記のような感じ

// classの前にopenつけてる
open class TodoItem: RealmObject(){
    @PrimaryKey
    var id: Long = 0
    var dateTime: Date = Date()
    var todo: String = ""
}

Realm Databaseと一緒に使ったときに嵌まった。
上記コードはRealmのモデルクラスになるのだけれど
openつけないでビルドしたらなんか失敗した。
↓こんなエラー

Execution failed for task ':app:kaptDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution
   > java.lang.reflect.InvocationTargetException (no error message)

エラーコードでググってもよくわかんなかったけど、
参考書のソースコードと比べてみてやっとopenつけなきゃいけないことに気づいた。

ビルド中にRealmモデルクラスを継承したJavaクラスを作ってる模様。
appname_TodoItemRealmProxy.javaみたいなクラスができてた。
自分で継承してなくて気づかなかった。

画面パーツにidつけ忘れた

たぶん写経くらいでしか嵌まることないんだけど。
下記のようなソースではまった。

class MainActivity : AppCompatActivity() {
    private lateinit var realm: Realm
    private lateinit var adapter: TodoRecyclerViewAdapter
    private lateinit var layoutManager: RecyclerView.LayoutManager

    override fun onStart() {
        super.onStart()
        realm = Realm.getDefaultInstance()
        val realmResults = realm.where(TodoItem::class.java)
            .findAll()
            .sort("id", Sort.DESCENDING)
        layoutManager = LinearLayoutManager(this)
        // このrecyclerViewではまった
        recyclerView.layoutManager = layoutManager

        adapter = TodoRecyclerViewAdapter(realmResults)
        recyclerView.adapter = this.adapter
    }
}

変数使うときKotlinではvarとかvalで宣言してるけど、
recyclerViewっていう変数は特に宣言してない。参考書の写経してて、
なんで宣言してない変数名があるんだ誤字脱字か?
と勝手に怒って小一時間くらい悩んだ。

結果、原因はactivity_main.xmlのidつけわすれ。
画面に表示するボタンとかレイアウトとかパーツごとに
idをつけといて、そのidでパーツ特定してKotlinで操作するのだけど、
そもそも「recyclerView」というidを該当のパーツにつけ忘れてた。

変数の宣言は全然関係なかった。参考書はすごく良書です。
ちゃんとidつけててbuild.gradle(:app)に

plugins {
    id 'kotlin-android-extensions'
}

って書いとけばAndroid Studioのコード補完に出てきてくれる。

eclipseで作成した動的Webプロジェクト(Java 1.8,Tomcat 8.5)をHerokuにデプロイしてみる

前置き

暇を持て余していてせっかくなので、
無料で利用できるHerokuにeclipseで作成したWebアプリをデプロイしてみる。

下準備

まず公式のチュートリアルはやっとくべきだと思う devcenter.heroku.com

あとはいろんな方が始め方をまとめてるのでそちらを参照。
自分は下記の記事を参考にした。
Heroku用開発ツールのセットアップまでは同じようにやる。
web.plus-idea.net

ひな型アプリの入手

Tomcatベースのアプリを動かすためのひな型を下記から入手する。

GitHub - heroku/devcenter-embedded-tomcat

$ git clone git@github.com:heroku/devcenter-embedded-tomcat.git

必要に応じて追記・修正をする

pom.xml

ひな型落としてくるとJavaのバージョン1.5でビルドになってるので
Java1.8を明記したり文字コードをセットしたり。
dependenciesに使用するjarファイルを追記する。

  <properties>
    <tomcat.version>8.5.38</tomcat.version>
    <!-- ここから追記 -->
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <!-- ここまで -->
  </properties>
  <dependencies>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
        <version>${tomcat.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
        <version>${tomcat.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-jasper</artifactId>
        <version>${tomcat.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-jasper-el</artifactId>
        <version>${tomcat.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-jsp-api</artifactId>
        <version>${tomcat.version}</version>
    </dependency>

    <!-- ここから追記 -->
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>42.2.12</version>
    </dependency>
    <!-- ここまで -->
  </dependencies>
WebContentフォルダの中身

動的WebプロジェクトのWebContentフォルダの中身、
HTML,CSS,JSP,JSなんかのファイルはsrc/main/webappフォルダに移す。
Javaソースの配置は動的Webプロジェクトとほぼ変わらず。
ディレクトリの階層はちょっと違うけど迷うことはない)
参考として自分のクソコードを晒す。

Procfile.windows

必須でない、かつOSがWindowsのひと限定。
ローカルで動作確認するときに便利なので作っといてもよいかと。
ファイルの中身は下記のとおり。配置場所はプロジェクト直下でOK。

web: target\bin\webapp.bat
Database.java

こちらも必須でない、
けどPostgreSQL使うとき便利なので載せとく。
PostgreSQLのURL直書きしなくても
System.getenv("DATABASE_URL")で取得できる。
ソース元は他の方の記事から。URLは失念してしまいました。

package util;

import java.net.URI;
import java.net.URISyntaxException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * DB接続のクラス
 * @author
 *
 */
public class Database {

    public static Connection getConnection() throws SQLException {
        String databaseUrl = System.getenv("DATABASE_URL");

        try {
            Class.forName("org.postgresql.Driver");
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e);
        }
        try {
            URI uri = new URI(databaseUrl);
            String host = uri.getHost();
            int port = uri.getPort();
            if (port > 0) {
                host += ":" + port;
            }
            String db = uri.getPath();
            String username = uri.getUserInfo();
            String password = null;
            int idx = username.indexOf(":");
            if (idx != -1) {
                password = username.substring(idx + 1);
                username = username.substring(0, idx);
            }
            Connection con = DriverManager.getConnection("jdbc:postgresql://" + host + db, username, password);
            return con;
        } catch (URISyntaxException e) {
            throw new IllegalStateException(e);
        }

    }
}

Mavenでビルドする

必要なリソースを全部配置したら、
コンソールでプロジェクト直下に移動して、Mavenビルドを実行

$ mvn clean install

gitコミットする

warファイルが作成できたらgitコミットする

# 初期化
$ git init
# コミットするソースの追加
$ git add .
# コミット
$ git commit -m "コメント入力"

# コミットしてないファイルないか確認
$ git status

herokuにプッシュする

herokuコマンドの実行にはチュートリアルとか参考記事で出てきた
Heroku Toolbeltが必要です。

herokuにプッシュしてアプリ起動したら、下記のURLで確認できる
https://自分で設定したアプリ名.herokuapp.com/

# herokuにプッシュ先のリポジトリを作成
# アプリ名は一意なURLとなる必要ある、ほかの人がすでに使ってたらエラー
# アプリ名省略した場合(heroku createで実行)、ランダムなURLが振られる
$ heroku create (アプリ名)

# herokuへプッシュ
$ git push heroku master

# プッシュ後、アプリ起動してるか確認
$ heroku ps

 # ログの確認
$ heroku log --tail

# アプリ名の変更
$ heroku apps:rename hogehoge

# リモートリポジトリの一覧を表示
$ git remote -v

# リモートリポジトリのURLを変更
$ git remote set-url heroku {new url}

PostgreSQLのアドオン追加

PostgreSQL使用する場合、heroku上にPostgresSQLのアドオン追加が必要。
アドオン追加は簡単。

# アドオン(PostgreSQL)の追加
$ heroku addons:add heroku-postgresql

# DB接続先の確認
$ heroku config

#DB接続
$ heroku pg:psql

ローカルで動かすとき

herokuにプッシュする前にローカルで確認する場合は
下記のコマンドを実行
アプリのデフォルトのポートは下記
http://localhost:5000/

# macの場合
$ heroku local web
# windowsの場合(設定によって文字化けするけどちゃんと起動してる)
$ heroku local web -f Procfile.windows

応用情報技術者試験 合格したので勉強メモ

受験内容の概要

  • 午前のでき具合
    確実にできた:50%(過去問のまま出た)
    多分できた:20%(消去法でできる問題や過去問の類似が出た)
    あてずっぽう:30%(最新の用語わからん、計算問題わからん)

試験開始して1.5hくらいで回答終わり退出
結果71点取れてた。

  • 午後のでき具合
    選択分野:
    情報セキュリティ、経営戦略、プロジェクトマネジメント、
    サービスマネジメント、システム監査

かなりできた。iTECの参考書やっといてよかった。
1問30分くらいかけて試験時間10分前くらいに回答終わった。
上記は結果でるまでの印象。
かなりできたと思ったのは幻想だった。
結果は68点。午前より低かった。

勉強内容の概要

  • 学習期間:4カ月
  • 総学習時間:300時間弱
  • 毎日の学習時間:平日1-2h、休日3-5hくらい
  • 使った教科書など

おそらく下記2つで合格点は取れる
応用情報技術者過去問道場|応用情報技術者試験.com
2020 応用情報技術者 午後問題の重点対策 | IT資格試験の取得、IT人材育成は株式会社アイテック(iTEC)

心配になって参考書も買った(ほとんど役に立たなかった)
令和02年【春期】【秋期】応用情報技術者 合格教本:書籍案内|技術評論社
心配になって過去問集も買った(ほとんどやってない)
令和02年【春期】応用情報技術者 パーフェクトラーニング過去問題集:書籍案内|技術評論社

なにはともあれ情報収集

まず重要なことは試験勉強を始める前の情報収集。
いままで受験した方の合格/不合格体験を集めて、
自分に合った学習方法を探す。

自分の場合、参考になったのは下記。
YouTubeやブログ記事を参考にしています。

自分の学習方針

合格しやすくて楽な学習方法にする
試験勉強にあまり時間使いたくない、という方針でとにかく楽な勉強法を前提とした。
(結局300時間くらい勉強しちゃったけど)
学習開始時の方針は下記2つ

1.午前は基本過去問だけ
2.午後は1冊問題集(iTECのやつ)を買って学習

学習内容

1か月目:とにかく午前の過去問を回す
過去問ドットコムを利用して、とにかく過去問を解いた。
質より量で、まずは答えを暗記する気持ちで取り組む。
自分は結構学習時間とれたので、過去問20回分をほぼ暗記したけど、
そんなやる必要なく実際は過去10回分くらいをまわせればよいと思う。
20回はやりすぎた、と個人的には感じてる。

平日に毎日1回分×2回(通勤時間の電車でやった)
休日は、5回分(平日5日分)を土日に分けて復習した。
20回分やったので、1カ月かけてやっと1周といった感じ。
この時点で過去問20回分の平均点数は70点くらい。

2カ月目:ひたすら午前の過去問を回す
過去問ドットコムのみ。
平日に毎日2回分(通勤時間の電車)
休日は、10回分を土日に分けて復習をした。
20回分やったので、1カ月かけて2周した感じ。
この時点で過去問20回分の平均点数は80点くらい

3か月目:午後の問題集を買う(いちおう参考書も)
ここから学習時間を増やした。過去問ドットコム+午後問題集って感じ。
過去問ドットコムは
平日に毎日4回分(通勤時間の電車)
休日は、20回分を土日に分けて復習をした。
20回分やったので、1カ月かけて4周した感じ。
この時点で過去20回分の平均点数は90点くらい

午後はiTECの問題集を購入して分野を絞って取り組む。
自分は完全に文系問題の5分野のみ選択してそれだけ取り組んだ。
平日に1問、休日に5問を土日に分けてやって2週間で30問解く感じ。
(問題集の構成が1分野6問程度なので5分野で計30問)
1カ月かけて2周した。

それと、こんな勉強でいいのか不安になって、
いまさらながら参考書を買った。が、正直読んでも頭に入らず。
あとでしらべてキタミ式のほうがよかったと後悔した。
(受験後の感想としては、そもそも参考書不要と思う)

4か月目:勉強に飽きた
毎日のルーティンとして
平日:午前過去問4回分 + 午後問題集3問分
休日:午後問題集15問分を土日に分けて

を実施したけど、1週間回して飽きた。
結果、2週間目からは上記のルーティンやりつつ、
応用情報の勉強してて自分に不足してると感じた
ネットワークとセキュリティの勉強をはじめた。
久々に資格以外の勉強をして楽しくなってしまい、試験勉強がおっくうになった。
なんとかルーティンは最後まで続けた。

学習内容の反省

午前の過去問をやりすぎた
10回分で十分だと思ったし、途中からは量より質の勉強に切り替えるべきだった。
自分の場合、「答えの暗記」しかほとんどしなかったので、
本番で計算問題の応用ができず、丸暗記した答えしか回答できなかった。
ちゃんと過去問ドットコムの回答解説を読んで計算が応用できるようにしとけばよかった。

最新の情報を仕入れとくべきだった
最近のIT用語はまったくわからずあてずっぽうだった。
試験終わった後だけど、反省してIT系の雑誌を購読しようと思う。
スキルある人ならネット漁って最新技術どんどんキャッチアップできるのだろうけど。
自分のような底辺プログラマは、まずは雑誌なんかで
まとめられた情報を入手するのがよいのでは、とおもいました。
ひとまずSoftware Design読んだけどよさげ。

JavaScriptの値比較でハマった話

前置き

プログラマ転職して1年経ったけど、
SESで働いてて入社2日目からずっと現場勤務(同じ会社の人いない現場)、
9月末でコロナの影響で予算縮小、切られて10月から自社待機してる。
現場には1年1カ月勤務した。たくさんお世話になった人いるし、
すごく感謝してる。逆にいま自社の人と接点ないし毎日つらい。

そんな状況で時間を持て余してるので
いままで書いたコードのリファクタリングしたり
応用情報の勉強したりしてる。(応用情報は先日受験した)

なんとなくやりたくなって、Javaで書いたWebアプリを
Servlet+JSPからJSP廃止してServlet+HTML(JS,JQueryでデータ処理)に
修正するのにはまってる。

そして修正したアプリはPaaS(heroku)にデプロイして遊んでみる。

前置きが長くなったけど、JSP廃止してJavaScriptを使用したときに
ハマった内容を2点、下記にメモとして置いておく。

その1.String型のまま数値比較はダメ

はまったこと

すごく当たり前の話なのですが。
数値比較するときはString型だったらちゃんとNumber型に変換しないとダメ。
そんな当たり前のことで嵌まってしまったのが私です。
いままでJavaばっかり書いてきて、事前に型を決めてるから
値比較は何も考えずに下記のように書いてた。

// int型に文字列を格納しようとしてコンパイルエラー
// int num1 = "6";
// int num2 = "55";
  
// これが正しい
int num1 = 6;  
int num2 = 55;  

boolean result = false;

if(num2 > num1){
    result = true;
}
// resultはtrueになる

で、JavaScriptで何も考えず書いた間違った値比較が下記。

var num1 = "6";
var num2 = "55";

var result = false;

if(num2 > num1){
    result = true;
}
// resultはfalseになる 正しくない!!

理由は下記の記事参考
javascript 文字列のまま数字を比較すると危険が危ない。 - かもメモ

実際にはvar num1やnum2にはJavaからJSONでデータ渡してます。
そもそもJava側で数値型の状態("1"じゃなく1)で渡せって話なんですが、
Jacksonライブラリ使ってMapにして面倒だったので全部Stringで渡してしまいました。
調べればInt型で送る手段あるのでしょうがそこまで手をつけず。

解決策

値比較する前に文字列型→数値型に変換する

var num1 = Number("6"); // 数値型に変換
var num2 = Number("55"); // 数値型に変換

var result = false;

if(num2 > num1){
    result = true;
}
// resultはtrueになる  

その2.なんでもかんでも明示的に型変換すればよいわけでもない?

はまったこと

その1を解決して私は思いました。
「値比較する前には、明示的に型変換してあげるのがきっと正しい!」

そして書いたのが下記。

var bool = Boolean("false"); // Boolean型に変換?  
  
var result = false;

// boolがfalseなのだからresultがtrueになるはずない・・・
if(bool == true){
    result = true;
}

// resultはtrueになる 正しくない!!

boolにfalseを入れたはず、と思ってたのが、
なぜかBoolean("false") → trueに。

調べてみるとBoolean()での値変換は
上記の場合trueになるのが仕様らしい。
JavaScript難しい・・・

解決策

下記の記事を参考にした。
javascript で string 型と boolean 型を相互に変換 – II

var bool = "false";
  
var result = false;

if(bool == "true"){
    result = true;
}

// resultはfalseになる

つまりはそのまま何も考えずに文字列のまま使った。
JavaScript難しい・・・

Java Gold(SE8) 取得できたのでメモ

JavaGold(SE8)無事に資格取得できたのでメモ

  • 正答率88%
  • 勉強開始前の情報収集は大事
  • 良質の参考書1冊あれば最短で合格できる

受験前の状況

  • 実務経験6ヶ月(JavaSQLを書くのがメイン、レガシーコード)
  • 前職はITと全く関係ないメーカー営業
  • 転職前にJavaSilverは取得済み
    (ちょうどGold受験の1年前くらい、93%で合格)

勉強開始前の情報収集は大事

Goldの前にOracleMasterBronzeを取得しましたが、
そのときは1発合格できず2回目で合格。(SQL/DBAどっちも2回受けた)
遠回りした理由は事前の情報収集不足。
ネットでもっと合格した方の記録を漁るべきだった。

参考になった記事
https://ito-u-oti.com/post-565/
つまづいた点とかまとめられた方には本当に感謝しかない。
ありがとうございました!!

学習に使ったもの

  • 黒本
    これだけやっとけば合格できる

  • ラクル公式サンプル問題
    再受験無料キャンペーン中だけ?
    か知らないけど公式HPにサンプル問題が掲載されてる
    https://education.oracle.com/ja/

  • Java SE API
    ラムダ式とStreamのAPIは通勤時間とかよく見てた

学習期間・方法

  • 学習期間 : 2ヶ月
  • 学習時間 : 平日1時間、休日2-4時間
    受験1ヶ月前までは休日2時間、残り1ヶ月は4時間くらい勉強

  • 学習方法

    • 1ヶ月目 : 黒本1周
    • 2ヶ月目 : 黒本5周くらい(+ サンプル問題やったりAPI見たり)

試験自体は黒本全部暗記すれば合格できる
(問題の答えじゃなく問題の構造を暗記)
問題のソースを見た瞬間に「このコードはここらへんを見れば答えがわかる」
って目星がつくようになれば、余計なソース読む必要なくなる。
本番のソースはクラス名とか変えただけで黒本と構造は一緒。
8割くらい見覚えのある問題だった。
本番でもソースを見た瞬間に「ここみれば答えわかる」
って感じでテンポよく回答できた。

資格を取得して

資格はあくまで自己満足。実務に役立つかって言ったら役立たない。
実際現場に出れば、資格なんてなくてもスーパーエンジニアはたくさんいる。
資格はファッションとして外見はよくできる。
あと身についた知識をとっかかりとしてググることができる。
結局は勉強し続けることが大事。
普段から毎日勉強しているけれど、
どうしてもモチベーションが下がってくることがある。
そういうときに資格の勉強をする。
目標があると勉強を頑張れる。モチベーションが回復して
資格以外の勉強もはかどる。
これからも好きなことを勉強していく。