20200817のJavaに関する記事は13件です。

Factory Methodパターン

Factory Methodパターン

Template Methodパターンではスーパークラス側で処理の骨組みを作り、サブクラス側で具体的な処理の肉付けを行う。
このパターンをインスタンス生成の場面に適用したものがFactory Methodパターン。

Product (製品)の役

このパターンで生成されるインスタンスが持つべきインタフェースを定める抽象クラス。
具体的な内容はConcreteProduct役が定める。

package factoryMethod;

public abstract class Product {
    public abstract void use();
}

Creator(作成者)の役

Product役を生成する抽象クラス。
具体的な内容はContreteCreatorが定める。
Creator役は実際に生成するConcreteProduct役については何も知らない。
Creator役が知っているのは、Product役とインスタンス生成のメソッドを呼び出せば、Productが生成されるということだけ。
newによる実際のインスタンス生成をインスタンス生成のためのメソッド呼び出しに代えることで、具体的なクラス名による束縛からスーパークラスを解放していることになる。

package factoryMethod;

public abstract class Factory {
    public final Product create(String owner) {
        Product p = createProduct(owner);
        registerProduct(p);
        return p;
    }

    protected abstract Product createProduct(String owner);
    protected abstract void registerProduct(Product product);

}

ConcreteProduct(具体的製品)の役

具体的な製品を定める。

package factoryMethod;

public class IdCard extends Product {
    private String owner;
     public IdCard(String owner) {
         System.out.println(owner + "のカードを作ります");
         this.owner = owner;
    }

     @Override
     public void use() {
         System.out.println(owner + "のカードを使います");
     }

     public String getOwner() {
         return owner;
     }
}

ConcreteCreator(具体的作成者)の役

具体的な製品を作るクラスを定める。

package factoryMethod;

import java.util.ArrayList;
import java.util.List;

public class IdCardFactory extends Factory {
    private List<String> owners = new ArrayList<>();

    @Override
    protected Product createProduct(String owner) {
        return new IdCard(owner);
    }

    @Override
    protected void registerProduct(Product product) {
        owners.add(((IdCard)product).getOwner());
    }

    public List<String> getOwners() {
        return owners;
    }
}

呼び出し元

package factoryMethod;

public class Main {

    public static void main(String[] args) {
        Factory factory = new IdCardFactory();
        Product card1 = factory.create("田中");
        Product card2 = factory.create("鈴木");
        Product card3 = factory.create("佐藤");
        card1.use();
        card2.use();
        card3.use();

    }
}

//実行結果
//田中のカードを作ります
//鈴木のカードを作ります
//佐藤のカードを作ります
//田中のカードを使います
//鈴木のカードを使います
//佐藤のカードを使います

上記IdCardクラスに、通し番号を追加し、IdCardFactoryクラスが通し番号と所有者の対応表を持つよう修正を行った。

package factoryMethod;

public class IdCard extends Product {
    private String owner;
    private int serial;
     public IdCard(String owner, int serial) {
         System.out.println(owner + "(" + serial + ")のカードを作ります");
         this.owner = owner;
         this.serial = serial;
    }

     @Override
     public void use() {
         System.out.println(owner + "(" + serial + ")のカードを使います");
     }

     public String getOwner() {
         return owner;
     }

     public int getSerial() {
         return serial;
     }
}
package factoryMethod;

import java.util.HashMap;

public class IdCardFactory extends Factory {
    private HashMap<Integer, String> database = new HashMap<>();
    private int serial = 1;


    @Override
    protected synchronized Product createProduct(String owner) {
        return new IdCard(owner, serial++);
    }

    @Override
    protected void registerProduct(Product product) {
        IdCard card = (IdCard)product;
        database.put(card.getSerial(), card.getOwner());
    }

    public HashMap<Integer, String> getDatabase() {
        return database;
    }
}
package factoryMethod;

public class Main {

    public static void main(String[] args) {
        Factory factory = new IdCardFactory();
        Product card1 = factory.create("田中");
        Product card2 = factory.create("鈴木");
        Product card3 = factory.create("佐藤");
        card1.use();
        card2.use();
        card3.use();

    }

}
// 実行結果
//田中(1)のカードを作ります
//鈴木(2)のカードを作ります
//佐藤(3)のカードを作ります
//田中(1)のカードを使います
//鈴木(2)のカードを使います
//佐藤(3)のカードを使います


Productクラス、Factoryクラス、Mainクラスを変更することなく修正を行うことができた。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者向け】「return」がイマイチ分からない2年前の自分に向けての記事(if-elseの話も少し)

この記事を読んで習得できること

・プログラミングを初めてまだ「return」の使い方が分からない方々
・3年前の私

経緯

大学院でプログラムを書いていた3年前のボク。
大学の研究で実験の解析で必要だったため、必死で書いていたのをなんとなく覚えている。

ひとまず、自分の行いたい解析プログラムは動くようになり、
喜びながらそのプログラムを使って解析を行っていた。

ひと段落してから、プログラムを見てみると、
「結構ソース汚いな…」と思い、修正しようと試みた。

しかし、ソースのスパゲッティ感*が半端なく、
なかなか修正が出来ずに、前回と同じようなソースなのに、
一から書き直したのを覚えている。

3年前のソースで何が起こっていたのか

おおよそのイメージだが、こんな感じ。

sample.swift
func yabaiCode() {
    if 条件1 {
        処理1(15行以上
        処理A(10行以上)
        処理4(10行以上)
    } else if 条件2 {
        処理2(15行以上
        処理A`(10行以上)
        処理5(10行以上)
    }  if 条件3 {
        処理3(15行以上)
        処理A``(10行以上)
        処理6(10行以上)
    } else {
        処理7(20行以上)
    }
}

何が言いたいかって、条件分岐の中の処理がとんでもなく長いのだ。
しかし、処理を分けたいのだが、分け方がイマイチ分からない。
どうやら、メソッドで分割していけば、もっと簡単に処理ができるとのことだ。

でも、メソッドで分けることが出来なかった。
メソッド間での、値の受け渡しが分からなかったのだ…

んで、結局分からないまま、このままのコードを残して卒業してしまったわけだが(後輩ちゃんごめん)、
今になって(流石に)return文がある程度分かった自分が、過去の自分にreturnを教えるために、この記事を書いたってわけです。

returnの主な使い方は2つ

1.メソッドを終了させる

sample.swift
func return1() {

    num = 5 // 好きな数値を入れる

    if num == 4 {
        print("4です")
        return
    } else if num == 5 {
        print("5です")
        return 
        // このメソッドはここで終了するはず
        // これ以降は処理されない
    }  if num == 6 {
        print("6です")
        return
    } else {
        print("分からない")
        return
    }
}

このreturnは、無駄な処理を省くことができる。
途中で答えが出たら、その時点で処理を終了させればいいし、
また、returnがあるおかげで、if-elseを使わなくてよくなる。

if-elseは、処理が多くなる上に、ifとelseが同時でいることが前提になるため、
1メソッドあたりの処理が多くなることがある。
読みにくくなったりもする。
職場によっては、「if-else禁止!」なんてところもあるだろう。
(自分の部署はそうでした)

どんだけif-elseがないとコードが読みやすくなるか。
FizzBuzz文を参考にしてみたいと思う。

まずは、if-else文を使用したもの。

FizzBuzz.swift
func fizzBuzz() {
    for i in 1...30 {
        judgeFizzBuzz(num: i)
    }
}

func judgeFizzBuzz(num : Int) {
    if num % 15 == 0 {
        print("FizzBuzz")
    } else if num % 3 == 0 {
        print("Fizz")
    } else if num % 5 == 0 {
        print("Buzz")
    } else {
        print(num)
    }
}

めっちゃ悪いわけではないが、もし仮に、7の倍数の時の処理を入れるなんて時は、
気をつけないと、全ての処理がぶっ壊れてしまう。
(return使うときももちろん気をつける必要があるが)

では、return文を使ってみる。

FizzBuzz.swift
func fizzBuzz() {
    for i in 1...30 {
        judgeFizzBuzz(num: i)
    }
}

func judgeFizzBuzz(num : Int) {
    if num % 15 == 0 {
        print("FizzBuzz")
        return
    } 

    if num % 3 == 0 {
        print("Fizz")
        return
    } 

    if num % 5 == 0 {
        print("Buzz")
        return
    }

    print(num)
}

if文がパーツ化されるので、実に見やすい。本当に素晴らしい。
何もなければnumがprintされるってことも一目瞭然だ。

2.値を返してくれる

ここでは、数値を返してくれるメソッドを使用する

sample.swift
func say() {
    num = 5
    doubleNum = doubleNum(num) // 10が返ってくる
}

// 2倍した数値を返してくれる関数
func doubleNum(num :Int) -> Int {
    return num * 2 //数値を返す
}

別のメソッドで計算して、処理結果を返してもらうってことも容易になる。

FizzBuzz使うとさらにお分りいただけるかもしれない。

FizzBuzz.swift
func fizzBuzz() {
    for i in 1...30 {
        print(judgeFizzBuzz(num: i))
    }
}

func judgeFizzBuzz(num : Int) -> String {
    if num % 15 == 0 {
        return "FizzBuzz"
    }

    if num % 3 == 0 {
        return "Fizz"
    }

    if num % 5 == 0 {
        return "Buzz"
    }

    return String(num)

}

1で書いたものよりも、さらに見やすくなったと思う。
judgeFizzBuzzがStringを返すようになったということで、全体としてのコード量も減った(print)

まとめ

returnをうまく使わないとメソッドが盛り盛りになってしまうので、もし使っていない方がいたら是非使って欲しい。
これを知らないで、よく大学卒業できたな、俺…

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

kotlinとjavaでリストを作りたい!

はじめに…

この記事は2個前の記事で書いた、

7日間毎日記事投稿の3日目

になってます

使うコードは下に貼りますが、このアプリの詳しい機能はその2個前の記事をご覧ください!

↓ここからが今回の記事の本題です↓

それぞれの言語でリストを扱うには…

-- 目次 --

  1. Arraylistの作り方

  2. 要素を追加・削除する方法

  3. 要素数を取得する方法

  4. リストの中身を逆順・シャッフルする方法

  5. 中身を違うリストにコピーする方法

ArrayListの作り方

  • javaの場合
ArrayList<要素の型>変数名 = new ArrayList<>();

例)

WhoActivity.java
ArrayList<String>memberL = new ArrayList<>();  // 20行目

変数定義と大きく異なっている

  • kotlinの場合
val/var 変数名:ArrayList<要素の型> = arrayListOf()

例)

WhoActivity.kt
var memberL:ArrayList<String> = arrayListOf()  // 13行目

リストの中身の指定方法以外は変数定義とよく似ている

要素の型を指定する方法は両者ともよく似ている

要素を追加・削除する方法

  • javaの場合
// 追加
リスト名.add(要素);
// 削除
リスト名.remove(要素の場所);

例)

WhoActivity.java
//追加
memberL.add(memberET.getText().toString());  // 82行目

.getText()は後日説明予定

ResultActivity.java
// 削除
memberL.remove(0);  // 46行目
  • kotlinの場合
// 追加
リスト名.add(要素)
// 削除
リスト名.remove(要素)

例)

WhoActivity.kt
// 追加
memberL.add(member_et.text.toString())  // 54行目

.textについては後日説明予定

ResultActivity.kt
// 削除
memberL.remove(memberL[0])  // 37行目

追加の仕方は同じだが、削除するときは対象の要素を指定するために使用する物が違う

要素数を取得する方法

  • javaの場合
リスト名.size();

例)

resultActivity.java
int memberNum = memberL.size();  // 33行目
  • kotlinの場合
リスト名.size

例)

ResultActivity.kt
val memberNum = memberL.size  // 24行目

javaは.size()、kotlinは.sizeとよく似ている

リストの中身を逆順・シャッフルする方法

  • javaの場合
// 逆順
Collections.reverse(リスト名);
// シャッフル
Collections.shuffle(リスト名);

例)

WhoActivity.java
// 逆順
ArrayList<String> memberLR = (ArrayList<String>) memberL.clone();  //84行目
Collections.reverse(memberLR);

※ 1行目の処理内容は「中身を違うリストにコピーする方法」参照

resultActivity.java
// シャッフル
Collections.shuffle(memberL);  // 34行目
  • kotlinの場合
// 逆順
リスト名.reverse()
// シャッフル
リスト名.shuffle(Random())

例)

WhoActivity.kt
// 逆順
var memverLR = ArrayList<String>(memberL)  // 56行目
memverLR.reverse()

※ 1行目の処理内容は「中身を違うリストにコピーする方法」参照

ResultActivity.kt
// シャッフル
memberL.shuffle(Random())  // 25行目

書き方は大きく違うがどちらも
逆順の場合は「reverse」、シャッフルの場合は「shuffle」というのがキーワードとなっている

中身を違うリストにコピーする方法

  • javaの場合
ArrayList<要素の型> 変数名 = (ArrayList<要素の型>) コピーするリスト名.clone();

例)

WhoActivity.java
ArrayList<String> memberLR = (ArrayList<String>) memberL.clone();  // 84行目
  • kotlinの場合
val/var 変数名 = ArrayList<要素の型>(コピするリスト名)

例)

WhoActivity.kt
var memverLR = ArrayList<String>(memberL)  // 56行目

javaは.clone()を使用し、kotlinはリスト定義の方法と少し書き方が変わる

最後に…

今回はjavaとkotlinでリストの定義をしました。
まだまだたくさんの機能があると思うので、またほかのコードで出てきた際に記事にできたらなと思います。

また、1日目に記載した予定から今日以降の予定を少し変更しました。
その記事の予定も変更しておくので、あまり関係ないですが
一応自分のメモ用としてもここに記載しておきます。

明日も記事を投稿するので、引き続き温かく見守ってください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【忘備録】Java : 文字列調査

はじめに

今回は今まで逃げ続けてきたJavaの今までの復習と勉強ををしていきます。初めてプログラミングでJavaを触った結果、Javaに対して苦手意識が生まれたのですが、JavascriptやRubyを学んでから、学んだことがJava現場でも少しずつ活かされ始めて少しずつJavaも書けるようになり、Javaを今度こそ、しっかり学んでいきたいと思うようになりました。

今回はJavaにもともと、備わっているStringクラスのメソッドの復習をしていきます。使っている参考書は有名なこちらです。

String操作

1, equals

文字列比較の定番。
比較対象の文字列が等しいか見てくれる。小文字、大文字も見てくれる。

sample.java
public class string {
    public static void main(String[] args){
        boolean result;
        int length;
        boolean empty;

        // 文字列の内容が正しいか調べる
        String s1 = "this is Java";
        String s2 = "This is Java";
        String s3 = "これはJavaです";

        result = s1.equals(s2);
        System.out.println("s1 : s2 => "+result);
        result = s1.equals(s3);
        System.out.println("s1 : s3 => "+result);
        result = s2.equals(s3);
        System.out.println("s2 : s3 => "+result);
        result = "this is Java".equals(s1);
        System.out.println("this is Java : s1 => "+result);
    }
}
結果.java
s1 : s2 => false
s1 : s3 => false
s2 : s3 => false
this is Java : s1 => true

2, equalsIgnoreCase

大文字、小文字関係なく同等の文字列か判定してくれる。

sample.java
result = s1.equalsIgnoreCase(s2);
System.out.println("s1 : s2 => "+result);
result = s1.equalsIgnoreCase(s3);
System.out.println("s1 : s3 => "+result);
result = s2.equalsIgnoreCase(s3);
System.out.println("s2 : s3 => "+result);
result = "this is Java".equalsIgnoreCase(s1);
System.out.println("this is Java : s1 => "+result);
結果.java
s1 : s2 => true
s1 : s3 => false
s2 : s3 => false
this is Java : s1 => true

3, length

文字列長を見てくれる。スペースも一文字として見ることに注意。

sample.java
length = s1.length();
System.out.println("\"this is Java\"の文字列長は"+length+"です。");
length = s2.length();
System.out.println("\"This is Java\"の文字列長は"+length+"です。");
length = s3.length();
System.out.println("\"これはJavaです\"の文字列長は"+length+"です。");
結果.java
"this is Java"の文字列長は12です
"This is Java"の文字列長は12です
"これはJavaです"の文字列長は9です

4, isEmpty

文字列が空か判定。isEmptyの場合、空白が入っていると空扱いにならない。
isBlankの場合、空白が含まれていても空扱いになってしまう。

sample.java
empty = s1.isEmpty();
        System.out.println("s1.isEmpty : " + empty);
        empty = s2.isEmpty();
        System.out.println("s2.isEmpty : " + empty);
        empty = s3.isEmpty();
        System.out.println("s3.isEmpty : " + empty);
        empty = "".isEmpty();
        System.out.println("\"\".isEmpty : " + empty);
        empty = "  ".isEmpty();
        System.out.println("\"  \".isEmpty : " + empty);
        empty = "".isBlank();
        System.out.println("\"\".isBlank : " + empty);
        empty = "  ".isBlank();
        System.out.println("\"  \".isBlank : " + empty);
結果.java
s1.isEmpty : false
s2.isEmpty : false
s3.isEmpty : false
"".isEmpty : true
"  ".isEmpty : false
"".isBlank : true
"  ".isBlank : true

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Java】ArrayListの最長文字列をstreamを使って簡単に取得する方法

ArrayList<String>に格納されている文字列のうち、最大長であるものを簡単に取り出す方法です。
普通に考えたら、forで回してlenghtがMaxになるものを取得するというものですが、
ここではstreamを使って1行で取得します。

注)streamはJava8以降で利用可能です。

ソースコード

maxString()が最大文字列長を取得するメソッドです。

StringList.java
package samples.stream.main;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class StringList {
    private List<String> _strList = new ArrayList<String>();

    public void add(String str) {
        _strList.add(str);
    }

    public String maxString() {
        return _strList.stream().max(Comparator.comparing(String::length)).get();
    }
}

 

解説

  • max(

指定されたComparator(全体順序付けを行う比較関数)の最大要素を返します。
これにより、Comparatorで比較した最大値が返ってきます。

  • Comparator.comparing(String::length)

比較するためのキーとして、String型のlengthを指定します。
これにより、文字列の長さで比較します。

  • .get()

値を返します。

よって、String型のlengthで比較して最大となる値を返すことができます。
 

テストコード(おまけ)

検証したテストコードです。
今後は、public static void main(String...strings) で例示するのではなく、
TDDの学習も兼ねて、テストコードで例示していきたいと思っています。

StreamMaxTest.java
package samples.stream.test;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.jupiter.api.Test;

import samples.stream.main.StringList;

class StreamMaxTest {
    @Test
    void ArrayList_String_内で最大長となる文字列() {
        StringList stringList = new StringList();
        stringList = new StringList();
        stringList.add("");
        stringList.add("12345");
        stringList.add("1234567890");
        String str = stringList.maxString();
        assertThat("1234567890", is(str));
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HV000030: No validator could be found for constraint 'javax.validation.constraints.Pattern' validating type 'java.math.BigDecimal'. Check configuration for 'price'

【環境】
  • ProductName: Mac OS X
  • ProductVersion: 10.14.6
  • BuildVersion: 18G2022
【遭遇したタイミング】
  • spring-bootでアプリを開発中

結論

  • 使うべきアノテーションを間違えていた。

原因

@Pattern では使えない方法でバリデーションを設定していた。


【エラー内容】
There was an unexpected error (type=Internal Server Error, status=500).
HV000030: No validator could be found for constraint 'javax.validation.constraints.Pattern' validating type 'java.math.BigDecimal'. Check configuration for 'price'

訳:

HV000030: 型 'java.math.BigDecimal' を検証する制約 
'javax.validation.constraints.Pattern' のバリデータが見つかりませんでした。price' の構成を確認してください。

// 必須入力、1000円以上、数値へ変換
@NotNull
@Min(1000)
@Pattern(regexp = "#,###") // 指定パターンの文字列を、数値に変換する
private BigDecimal price;

// 必須入力、1000円以上、数値へ変換
@NotNull
@Min(1000)
@NumberFormat(pattern = "#,###") // 指定パターンの文字列を、数値に変換する
private BigDecimal price;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【エラー解決】HV000030: No validator could be found for constraint 〜〜

【環境】
  • ProductName: Mac OS X
  • ProductVersion: 10.14.6
  • BuildVersion: 18G2022
【遭遇したタイミング】
  • spring-bootでアプリを開発中

結論

  • 使うべきアノテーションを間違えていた。

原因

@Pattern では使えない方法でバリデーションを設定していた。


【エラー内容】
There was an unexpected error (type=Internal Server Error, status=500).
HV000030: No validator could be found for constraint 'javax.validation.constraints.Pattern' validating type 'java.math.BigDecimal'. Check configuration for 'price'

訳:

HV000030: 型 'java.math.BigDecimal' を検証する制約 
'javax.validation.constraints.Pattern' のバリデータが見つかりませんでした。price' の構成を確認してください。

// 必須入力、1000円以上、数値へ変換
@NotNull
@Min(1000)
@Pattern(regexp = "#,###") // 指定パターンの文字列を、数値に変換する
private BigDecimal price;

// 必須入力、1000円以上、数値へ変換
@NotNull
@Min(1000)
@NumberFormat(pattern = "#,###") // 指定パターンの文字列を、数値に変換する
private BigDecimal price;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プログラミング言語を学ぶ時には、はじめにプログラミング言語の家系図を見ておくとよいよ、という話

プログラミミング言語の家系図ってなに

あるプログラミングがいつ、どのような言語に影響を受けて誕生したかを表す系譜のことです。

プログラミミング言語の家系図はここにある

プログラミング家系図を載せているサイトはいくつかあるのですが、今回はこちらのサイトを紹介します。

diagram & history of programming languages (プログラミング言語の系譜)

program_diagram.PNG

主要言語のみものと、150以上の言語を載せているバージョンの2種類が記載されています。
2020年8月時点で、2018年の分まで記載されています。

いくつかの言語をピックアップ

主要言語の図から、いくつかの言語をピックアップしてみましょう。

C言語(K&R)

  • 誕生: 1978年
  • 影響を受けた言語: Algol 60
  • 影響を与えた言語: C++, Python (他、子孫多数)

注)
C言語の誕生は1972年であるが、リンク先の図では、『プログラミング言語C』(原題:The C Programming Language、通称 K&R)が出版された年を採用している。(参考: C言語 - Wikipedia, プログラミング言語C - Wikipedia)

Python

  • 誕生: 1991年
  • 影響を受けた言語: C, C++, Pascal
  • 影響を与えた言語: Ruby

Ruby

  • 誕生: 1995年
  • 影響を受けた言語: Perl, Eiffel, Python
  • 影響を与えた言語: Swift

Java

  • 誕生: 1995年
  • 影響を受けた言語: C++
  • 影響を与えた言語: JavaScript, C#, Kotlin

JavaScript

  • 誕生: 1995年
  • 影響を受けた言語: Java
  • 影響を与えた言語: Kotlin

プログラミング言語(2言語目以降)を学ぶ時には、はじめにプログラミング言語の家系図を見ておくとよいよ

さて表題の件ですが、2言語目以降のプログラミング言語を学ぶときにはこの家系図を最初に確認することをおすすめします。

理由は

  • 自分がすでに知っているプログラミング言語と、新しく学ぶ言語がどのくらい離れているかによって習得難易度を把握することができるから
  • 自分がすでに知っているプログラミング言語より新しい場合、新しくて便利な構文があることを期待しながら学習を進められるから
  • 自分がすでに知っているプログラミング言語より古い場合、便利な機能がないことを覚悟しながら学習を進めることができるから

です。

自分の場合は、Java歴5年、Perlはバッチ程度ならで少し書ける程度のときに、Rubyを習得しようとしました。
そのときに、RubyはPerlから影響を受けていることを知ったので構文の理解に役立ちました。
一方、関数型言語の知識はほぼ無かったのでその部分は苦労しましたが、そのことを事前に予測できたのはよかったと思います。

あとがき

はじめてのRuby』(著: Yugui) にRubyに関係する部分のプログラミング言語家系図が記載されていました。この図はRubyの理解に大変役に立ちました。

ほかの言語の図も探していたところリンク先のものが見つかりました。

プログラミング言語はここ数十年で大幅に進化しているのだなあ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java 練習問題「初級」

基礎編に引き続き初級編です。

初級編

package com.company;


public class HundredKnocksBeginner {
    public static void main(String arg[]) {
        Main main = new Main();
//        question21(10,3);
//        question22(10);
//        question23(0);
//        question24(5);
//        question25(-10);
//        question26(0);
//        question27(6);
//        question28(10);
//        question29(11, 22, 33, 44, 55);
//        question30(0);
//        question31(17);
//        question32();
//        question33(7);
//        question34(7);
//        question35(3);
//        question36(3,6);
//        question37(8);
//        question38();
//        question39();
    }

整数値を2つ入力させ、1つめの値を2つめの値で割った結果を表示し、続けてその結果に2つめの値を掛けた結果を表示するプログラムを作成せよ。計算はすべて整数型で行うこと(割り切れない場合は自動的に小数点以下が切り捨てられる)。

    public static void question21(int num1, int num2) {
        System.out.println("問21");
        int num3 = num1 / num2;
        //1つめの値を2つめの値で割った結果を表示
        System.out.println(num3);
        //その結果に2つめの値を掛けた結果を表示する
        System.out.println(num3 * num2);
    }

整数値を入力させ、その値が5よりも大きく、かつ、20よりも小さければOKと表示するプログラムを作成せよ。

 *
 * @param num1
 */
public static void question22(int num1) {
    System.out.println("問22");
    // 値が5よりも大きく、かつ、20よりも小さい場合
    if (num1 > 5 && num1 < 20) {
        // 値が5よりも大きく、かつ、20よりも小さければ
        System.out.println("1つ目の値はOKです");
    }
}

整数値を入力させ、その値が-10以下、または、10以上であればOKと表示するプログラムを作成せよ。

 *
 * @param num1
 */
public static void question23(int num1) {
    System.out.println("問23");
    if (num1 <= -10 || num1 >= 10) {
        // 値が-10以下、または、10以上の場合
        System.out.println("1つ目の値はOKです");
    }
}

数値を入力させ、その値が-10以上0未満、または、10以上であればOK、そうでなければNGと表示するプログラムを作成せよ。

 *
 * @param num1
 */
public static void question24(int num1) {
    System.out.println("問24");
    // 値が-10以上0未満の場合
    if ((num1 >= -10 && num1 < 0) || (num1 >= 10)) {
        System.out.println("1つ目の値はOKです");
    } else {
        System.out.println("NGです");
    }
}

整数値を入力させ、その値が-10未満ならrange 1、-10以上0未満であればrange 2、0以上であればrange 3、と表示するプログラムを作成せよ。

public static void question25(int num1) {
    System.out.println("問25");
    // 値が-10未満の場合
    if (num1 < -10) {
        System.out.println("range 1");
        // -10以上0未満
    } else if (num1 < 0) {
        System.out.println("range 2");
        // 0以上
    } else {
        System.out.println("range 3");
    }
}

整数値を入力させ、その値が1ならone、2ならtwo、3ならthree、それ以外ならothersと表示するプログラムをswicth-case文を使って作成せよ。

 *
 * @param num1
 */
public static void question26(int num1) {
    System.out.println("問26");
    // 記入例num1(1,2,3,0)
    switch (num1) {
        // 式の値と値num1が一致したときの処理
        case 1:
            System.out.println("one");
            break;
        case 2:
            System.out.println("two");
            break;
        case 3:
            System.out.println("three");
            break;
        // 式の値がどのcaseの値とも一致しなかったときの処理
        default:
            System.out.println("others");
    }
}

整数値を入力させ、1からその値までの総和を計算して表示するプログラムを作成せよ。ただし、0以下の値を入力した場合は0と表示する。

 *
 * @param num1
 */

public static void question27(int num1) {
    System.out.println("問27");
    for (int i = 1; i < num1; i++) {
        System.out.println(i);
    }
    if (num1 == 0) {
        System.out.println(0);
    }
}

整数値を入力させ、その値の階乗を表示するプログラムを作成せよ。ただし、0以下の値を入力した場合は1と表示する。(階乗メソッド)

 *
 * @param num1
 */
public static void question28(int num1) {
    for (int i = 1; i <= 10; i++) {
        num1 = num1 * i;
    }
    System.out.println(num1);
}

整数値を5回入力させ、それらの値の合計を表示するプログラムを繰り返しを使って作成せよ。

 *
 * @param num1
 * @param num2
 * @param num3
 * @param num4
 * @param num5
 */

public static void question29(int num1, int num2, int num3, int num4, int num5) {
    System.out.println("問29");
    // 値の合計を表示
    System.out.println(num1 + num2 + num3 + num4 + num5);
}

整数値を入力させ、その個数だけ*を表示するプログラムを作成せよ。入力値が0以下の値の場合は何も書かなくてよい。

 *
 * @param num1
 */
public static void question30(int num1) {
    System.out.println("問30");
    for (int i = 0; i < num1; i++) {
        System.out.print("*");
    }
}

整数値を入力させ、その個数だけ*を、5個おきに空白(スペース)を入れて表示するプログラムを作成せよ。

 * 入力値が0以下の値の場合は何も書かなくてよい。
 *
 * @param num1
 */
public static void question31(int num1) {
    System.out.println("問31");
    for (int i = 1; i < num1; i++) {
        System.out.print("*");
        // *を、5個おきに空白にする
        if (i % 5 == 0) {
            System.out.print("\t");
        }
    }
}

1から20まで順に表示するが、5の倍数の場合は数字の代わりにbarと表示するプログラムを作成せよ。

 */
public static void question32() {
    System.out.println("問32");
    for (int i = 1; i < 20; i++) {
        // 5の倍数の場合
        if (i % 5 == 0) {
            System.out.println("bar");
        } else {
            // 5の倍数ではないとき
            System.out.println(i);
        }
    }
}

整数値を入力させ、1から9まで、入力値以外を表示するプログラムを作成せよ。

 *
 * @param num1
 */
public static void question33(int num1) {
    System.out.println("問33");
    for (int i = 1; i < 10; i++) {
        // iが入力した値じゃなかったら
        if (!(i == num1)) {
            System.out.println(i);
        }
    }
}

整数値を入力させ、1から9まで、入力値と入力値+1以外を表示するプログラムを作成せよ。入力値が9の場合は9のみ表示しない。

 *
 * @param num1
 */
public static void question34(int num1) {
    System.out.println("問34");
    // 入力値と入力値+1ではなかったら
    for (int i = 1; i < 10; i++) {
        if ((!(i == num1)) && (!(i == (num1 + 1)))) {
            System.out.println(i);
        }
    }
}

{3, 7, 0, 8, 4, 1, 9, 6, 5, 2}で初期化される大きさ10の整数型配列を宣言し、整数値を入力させ、

 * 要素番号が入力値である配列要素の値を表示するプログラムを作成せよ。入力値が配列の要素の範囲外であるかどうかのチェックは省略してよい。
 */
public static void question35(int num1) {
    System.out.println("問35");
    // {3, 7, 0, 8, 4, 1, 9, 6, 5, 2}で初期化される大きさ10の整数型配列を宣言
    int[] array = {3, 7, 0, 8, 4, 1, 9, 6, 5, 2};
    System.out.println(array[num1]);
}

{3, 7, 0, 8, 4, 1, 9, 6, 5, 2}で初期化される大きさ10の整数型配列を宣言し、整数値を2つ入力させ、

 * 要素番号が入力値である2つの配列要素の値の積を計算して表示するプログラムを作成せよ。入力値が配列の要素の範囲外であるかどうかのチェックは省略してよい。
 *
 * @param num1
 */
public static void question36(int num1, int num2) {
    System.out.println("問36");
    // {3, 7, 0, 8, 4, 1, 9, 6, 5, 2}で初期化される大きさ10の整数型配列を宣言
    int[] array = {3, 7, 0, 8, 4, 1, 9, 6, 5, 2};
    System.out.println(array[num1] * array[num2]);
}

{3, 7, 0, 8, 4, 1, 9, 6, 5, 2}で初期化される大きさ10の整数型配列を宣言し、整数値を入力させ、要素番号が入力値の配列要素の値を参照し、

 * さらにその参照した値を要素番号とする配列要素の値を参照して表示するプログラムを作成せよ。入力値が配列の要素の範囲外であるかどうかのチェックは省略してよい。
 */
public static void question37(int num1) {
    System.out.println("問37");
    // {3, 7, 0, 8, 4, 1, 9, 6, 5, 2}で初期化される大きさ10の整数型配列を宣言
    int[] array = {3, 7, 0, 8, 4, 1, 9, 6, 5, 2};
    int num2;
    num2 = array[num1];
    System.out.println(array[num2]);
}

{3, 7, 0, 8, 4, 1, 9, 6, 5, 2}で初期化される大きさ10の整数型配列を宣言し、最初は参照する要素番号を0とし、この参照する要素番号の配列要素の値を表示し、次にその配列要素の値を次の参照する要素番号とし、この次の参照する要素番号の配列要素の値を表示し、さらにその配列要素の値を次の参照する要素番号とし、……を10回繰り返すプログラムを作成せよ。

public static void question38() {
    System.out.println("問38");
    // {3, 7, 0, 8, 4, 1, 9, 6, 5, 2}で初期化される大きさ10の整数型配列を宣言
    int[] array = new int[]{3, 7, 0, 8, 4, 1, 9, 6, 5, 2};
    // 参照する要素番号0の配列要素の値を表示する
    int index = 0;
    // 10回繰り返す
    for (int i = 0; i < 10; i++) {
        // indexの値を要素番号indexに代入
        index = array[index];
        // 要素番号aのときの配列要素の値を出力する
        System.out.println(index);
    }
}

{3, 7, 0, 8, 4, 1, 9, 6, 5, 2}で初期化される大きさ10の整数型配列を宣言し、最初は参照する要素番号を0とする。この参照する要素番号の配列要素の値から次の要素番号の配列要素の値を引いた値を表示し、参照する要素番号を1増やす。この手順を9回繰り返すプログラムを作成せよ。)

public static void question39() {
    System.out.println("問39");
    // {3, 7, 0, 8, 4, 1, 9, 6, 5, 2}で初期化される大きさ10の整数型配列を宣言
    int[] array = new int[]{3, 7, 0, 8, 4, 1, 9, 6, 5, 2};
    // 9回繰り返す
    // 現在の要素番号 index
    for( int index = 0; index < 9; index++){
        /**
         * index=0のとき currentValue=3,nextIndex=1,nextValue=7
         * index=1のとき currentValue=7,nextIndex=2,nextValue=0
         * index=2のとき currentValue=0,nextIndex=3,nextValue=8
         * */
        // 参照する要素番号の配列要素の値 currentValue
        int currentValue = array[index];
        // 今ある要素番号(index)の次の要素番号をnextIndex
        int nextIndex = index + 1;
        // 次の要素番号の配列要素の値をnextValue
        int nextValue = array[nextIndex];
        // 要素番号の配列要素の値から次の要素番号の配列要素の値を引いた値
        System.out.println(currentValue - nextValue);
    }
}

}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaのnull合体演算子

Javaにnull合体演算子が無いのでイケてないなあと思いつつ
Util関数を用意する次第

名称をcoalesceとして大体SQLの関数と同じ感じで使えるかなと

public final class Utils {
    @SafeVarargs
    public static <T> T coalesce(T... value) {
        for (T v : value) {
            if (v != null) {
                return v;
            }
        }
        return null;
    }
}
    public static void main() {
        String foo = null;
        String bar = null;
        String hoge = Utils.coalesce(foo, bar, "huga");
        System.out.println(hoge);
    }

でも正直なところC#のような??演算子が使いたいのです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java で gRPC サーバーを書くことによって Java 知識をアップデートする (1)

Java を再びやることになったので、Java の知識をアップデートするために、gRPC サーバーを書いてみることにした。

こちらの記事が素晴らしいので、これを流しているだけなのだが、大量のヤクの毛刈りが発生しているので、それを含めて書いていきたい。

maven でコンソールアプリケーションを書く時のテンプレート

最初に詰まったのが、maven でとても単純な、Hello World レベルのコンソールアプリケーションを作成すること。単純に、maven-archetype-quickstart を使うと良い。

mvn archetype:generate 
  -DgroupId=com.simplearchitect \
  -DartifactId=helloGRPC \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DinteractiveMode=false

proto file

まさに Hello World レベルの protobuf. syntaxproto3 にしておかないと、古いバージョンが使われてしまう。java_package で、自動生成されるクラスの、パッケージが決まるので、もうちょっと真面目な名前にしておいてもよかったかもしれない。outer_classnameはプロトコルの概念を表す自動生成クラスの名前になる。

下記のプロトコルバッファは、SayHello というメソッドを持ち、HelloRequest を送信すると、HelloReply が返ってくるシンプルなサービスを定義している。

main/proto/helloworld.proto

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

こちらを読むと、proto ファイルをよりよく理解できる。

クラスの自動生成

proto ファイルを元にクラスを自動生成する必要がある。これは、mavendependencyplugin を設定する必要がある。
gRPC-Java - An RPC library and framework のトップページを見て、maven の設定を行う。

io.grpc 系のライブラリの追加と、protobuf-maven-plugin および os-maven-plugin を入れる必要がある。ちなみに、os-maven-plugin

os-maven-plugin is a Maven extension/plugin that generates various useful platform-dependent project properties normalized from ${os.name} and ${os.arch}.

とのこと。os-maven-plugin.

<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-netty-shaded</artifactId>
  <version>1.31.1</version>
</dependency>
<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-protobuf</artifactId>
  <version>1.31.1</version>
</dependency>
<dependency>
  <groupId>io.grpc</groupId>
  <artifactId>grpc-stub</artifactId>
  <version>1.31.1</version>
</dependency>
<dependency> <!-- necessary for Java 9+ -->
  <groupId>org.apache.tomcat</groupId>
  <artifactId>annotations-api</artifactId>
  <version>6.0.53</version>
  <scope>provided</scope>
</dependency>
  :
<build>
  <extensions>
    <extension>
      <groupId>kr.motd.maven</groupId>
      <artifactId>os-maven-plugin</artifactId>
      <version>1.6.2</version>
    </extension>
  </extensions>
  <plugins>
    <plugin>
      <groupId>org.xolstice.maven.plugins</groupId>
      <artifactId>protobuf-maven-plugin</artifactId>
      <version>0.6.1</version>
      <configuration>
        <protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
        <pluginId>grpc-java</pluginId>
        <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.31.1:exe:${os.detected.classifier}</pluginArtifact>
      </configuration>
      <executions>
        <execution>
          <goals>
            <goal>compile</goal>
            <goal>compile-custom</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>

この設定により、mvn clean package で、自動でクラスが生成されるようになる。
image.png

ちなみに IntelliJ にプロジェクトを作成する場合は、一旦 target を生成したのちに実施するほうがよさそうだ。デフォルトのままだと、target ディレクトリは表示されなくなってしまう。target を作った後に、プロジェクトをintelliJ に読み込むと、自動で設定してくれる。そうでない場合は下記の設定 File > Project Structure Module を設定する必要がある。

image.png

尚、IntelliJ にロードするときは、プロジェクトを右クリックして、Mavenプロジェクトとして追加的なものを選んでおかないと、maven ではエラーは出なくとも、画面上 dependency が解決できず Error になってしまう。

gRPC サーバーを書く

生成されたクラスを参照してサーバーを書く。これを Main のプログラムから実行できるようにする。単純に、サーバーを起動して、設定して、継承したクラスをセットするだけになっている。

package com.simplearchitect;

import java.io.IOException;
import java.util.logging.Logger;

import io.grpc.BindableService;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.GreeterGrpc.GreeterImplBase;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.stub.CallStreamObserver;

import static io.grpc.ServerBuilder.*;

public class SimpleServer {
    Logger logger = Logger.getLogger(getClass().getName());

    Server server;

    public void start() throws IOException {
        server = ServerBuilder
                .forPort(8080)
                .addService((BindableService) new SimpleHelloServiceImpl())
                .build()
                .start();
        logger.info("start gRPC server.");
    }

    public void stop() {
        if (server != null) {
            server.shutdown();
            logger.info("Shutting down gRPC server.");
        }
    }

    public void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }

    public static class SimpleHelloServiceImpl extends GreeterImplBase {
        Logger logger = Logger.getLogger(getClass().getName());

        public void sayHello(HelloRequest request, CallStreamObserver<HelloReply> responseObserver) {
            logger.info(String.format("request: name = %s", request.getName()));
            HelloReply reply = HelloReply.newBuilder().setMessage("Hello, " + request.getName()).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }
    }
}

abstract static class

不思議なことに、static クラスを new している。これはどういうことだろうか? 下記のクラスが継承元のクラスだが、static abstract class になっている。これは、実際は、static クラスではなく、このクラスは、インナークラスになっており、インナークラスの親のクラスをインスタンス化していなくても、インスタンス化できることを示すため、static がついているので、実態はインスタンス化できるクラスである。

public static abstract class GreeterImplBase implements io.grpc.BindableService {

    /**
     * <pre>
     * Sends a greeting
     * </pre>
     */
    public void sayHello(io.grpc.examples.helloworld.HelloRequest request,
        io.grpc.stub.StreamObserver<io.grpc.examples.helloworld.HelloReply> responseObserver) {
      asyncUnimplementedUnaryCall(getSayHelloMethod(), responseObserver);
    }

    @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
      return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
          .addMethod(
            getSayHelloMethod(),
            asyncUnaryCall(
              new MethodHandlers<
                io.grpc.examples.helloworld.HelloRequest,
                io.grpc.examples.helloworld.HelloReply>(
                  this, METHODID_SAY_HELLO)))
          .build();
    }
  }

Java への Dependency の追加

mvn clean package で jar ファイルを作って実行するとエラーになる。Jar にライブラリが含まれていないためだ。maven-assembly-plugin を追加しよう。これで、mainClass も指定できて、依存関係にある jar を含んだライブラリを作ってくれる。
`

      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
          <archive>
            <manifest>
              <mainClass>com.simplearchitect.App</mainClass>
            </manifest>
          </archive>
        </configuration>
        <executions>
          <execution>
            <id>sample</id>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

The Assembly Plugin for Maven enables developers to combine project output into a single distributable archive that also contains dependencies, modules, site documentation, and other files.

うむ。まさに求めていたもの。

実行

PS > > java -jar .\target\helloGRPC-1.0-SNAPSHOT-jar-with-dependencies.jar
Aug 16, 2020 11:17:33 PM com.simplearchitect.SimpleServer start
INFO: start gRPC server.
> Enter stop.

ちゃんと動くかしらんけど、まずは、コンパイルされて、ライブラリ付きの jar ができて、動くところまでは出来た。次回はクライアントを作ってみて、動かしてみる。CallStreamObserver の挙動を明確に理解したい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JConsoleを使ってアプリケーション情報をリアルタイム監視する方法

概要

アプリケーション情報をリアルタイム監視できるようにJConsoleというツールが用意されています。
このJConsoleの使い方について備忘録を残しておきます。

検証環境

  • Eclipse Oxygen.3a Release (4.7.3a)
  • Java8
  • JConsole 1.8.0_162-b12

JConsoleの場所

image.png

JConsoleはJDKのbinフォルダ内にexeとして格納されており「jconsole.exe」を直でクリックして起動させます。

JConsoleの使い方

image.png
JConsoleを起動すると上記画面が表示されるので、たとえば「ローカル・プロセス」の監視したいプロセスを選択して「接続」ボタンを押下します。

image.png
上記画面が出た場合は、特に問題なければ「保護されていない接続」ボタンを押下します。(今回はローカル接続ですので特に問題ない)

image.png
上記画面の通り
1.ヒープメモリー使用率
2.スレッド
3.クラス
4.CPU使用率
を確認することができます。

より詳細な情報を見たい場合はそれぞれのタブを開いて確認しましょう。

メモリータブ

image.png

スレッドタブ

image.png

クラスタブ

image.png

VMサマリー

image.png

MBeans

image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

文字列比較 パイザのスキルチェック問題で引っかかったこと

パイザで公開されているスキルチェック問題「文字列比較」(※コード公開OKの問題)で引っかかったのでまとめ。

問題内容は、
「入力した2つの文字列を比較する」というもの
 

引っかかった理由

文字列比較関数があることは知っていたが、参照した動画内でそのやり方が書かれていなかったので、「まさか使うとは思ってなかった」という先入観。

不正解コード
import java.util.*;

public class Main {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        String line1 = sc.nextLine();
        String line2 = sc.nextLine();

        if( line1 == line2 ){
           System.out.println("OK"); 
        }
        else{
           System.out.println("NG"); 
        }

    }
}

出力結果は同じ文字列でも「NG」

以下のコードを確認のため、入力

デバックしてみる
import java.util.*;


public class Main {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        String line1 = sc.nextLine();
        String line2 = sc.nextLine();

        // if文の確認
        String line1 = "moji";
        String line2 = "moji";

        // 入力の確認
        System.out.println(line1);
        System.out.println(line2);

        if( line1 == line2 ){
           System.out.println("OK"); 
        }
        else{
           System.out.println("NG"); 
        }

    }
}
出力結果;
moji
moji
ok

if文も文字入力の取得も問題なし。
やっぱり文字列比較が悪い気がする(if文の条件)と思い確認し、以下のサイトにたどり着く。
【Java入門】文字列(String)を比較する方法(「==」と「equals」)
JavaにおけるString型の等価比較に関して

正解コード
import java.util.*;


public class Main {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        String line1 = sc.nextLine();
        String line2 = sc.nextLine();

        if( line1.equals(line2)){
           System.out.println("OK"); 
        }
        else{
           System.out.println("NG"); 
        }

    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む