20200125のJavaに関する記事は10件です。

Spring Boot/SecurityでOAuthクライアントを実装してみた(LINE連携)

1. 認可サーバ設定(LINE)

LINE Developers:
https://developers.line.biz/ja

・Channel Type: LINE Login
・App Types: Web app
・Callback URL: http://localhost:8080/redirect (認証後のリダイレクト先URL)

2. クライアント実装(Spring Boot/Security)

2-1. 依存ライブラリの追加

pom.xml
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
        <dependency>
        <groupId>org.springframework.security.oauth.boot</groupId>
        <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    </dependency>
2-2. application.ymlの設定

以下内容をもとに、ユーザーをLINE認証画面に遷移させ、LINE API を呼び出してユーザー情報を取得

security.oauth2.client.clientId
 - Channnel ID
security.oauth2.client.clientSecret
 - Channnel Secret
security.oauth2.client.accessTokenUri
 - アクセストークン発行の際に呼び出すエンドポイント
security.oauth2.client.userAuthorizationUri
 - 認証先エンドポイント
security.oauth2.client.pre-established-redirect-uri
 - 認証後のリダイレクト先URL (※LINE側で設定したCallback URL)
security.oauth2.client.scope
 - トークンをリクエストする際にクライアントが使えるスコープのリスト
security.oauth2.client.clientAuthenticationScheme
 - OAuth2 Token Endpoint 呼び出しに使うフォーマット(※LINEはFORMに対応)
security.oauth2.resource.userInfoUri
 - ユーザー情報(ユーザーID・ユーザ名・プロフィール画像)取得のために提供されるエンドポイント

application.yml
spring:
  main:
    allow-bean-definition-overriding: true
security:
  oauth2:
    client:
      clientId: [CLIENT_ID]
      clientSecret: [CLIENT_SECRET]
      accessTokenUri: https://api.line.me/oauth2/v2.1/token
      userAuthorizationUri: https://access.line.me/oauth2/v2.1/authorize
      use-current-uri: false
      pre-established-redirect-uri: http://localhost:8080/redirect
      authorized-grant-types:
      - authorization_code
      - refresh_token
      clientAuthenticationScheme: form
      scope:
        - openid
        - email
        - profile
    resource:
      userInfoUri: https://api.line.me/oauth2/v2.1/userinfo
      preferTokenInfo: true
server:
 port: 8080
2-3. @Beanの定義

 - @Beanを記述したメソッドでインスタンス化したクラスを返却

ConfigBean.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ConfigBean {

  @Bean
  public DoHttpRequests dorequest(){
    return new DoHttpRequests();
  }
}
2-4. ユーザが指定パス(/login)にアクセスした際にOAuth認証を実行
SecurityConfig.java
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;

@EnableWebSecurity
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                            "/images/**",
                            "/css/**",
                            "/javascript/**",
                            "/webjars/**",
                            "/favicon.ico");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/redirect/**")
                    .permitAll()
                .and()
            .authorizeRequests()
                .antMatchers("/login/**")
                .authenticated();
    }   
}
2-5. @Controllerの定義

 - 認証完了後に、認可コードを取得し、アクセストークン発行/ユーザ情報取得リクエストを実行
 - @AutowiredでDoHttpRequestsクラスをInject
 - ModelAndViewクラスでThymeleafテンプレートへ取得したオブジェクトを渡す

DemoController.java
import java.io.IOException;

import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
@Component
public class DemoController {

    @Autowired
    DoHttpRequests dohttpRequests;

    @RequestMapping(value = "/redirect", method = RequestMethod.GET)
    public ModelAndView test(@RequestParam("code") String code, ModelAndView mv) throws IOException{
      String responseToken = dohttpRequests.getToken(code);
      JSONObject tokenjson = new JSONObject(responseToken);
      String infoResp = dohttpRequests.getUserInfo(tokenjson.getString("access_token"));
      JSONObject infojson = new JSONObject(infoResp);

      mv.addObject("access_token", tokenjson.getString("access_token"));
      mv.addObject("expires_in", tokenjson.getInt("expires_in"));
      mv.addObject("refresh_token", tokenjson.getString("refresh_token"));
      mv.addObject("scope", tokenjson.getString("scope"));
      mv.addObject("token_type", tokenjson.getString("token_type"));
      mv.addObject("id_token", tokenjson.getString("id_token"));
      mv.addObject("sub", infojson.getString("sub"));
      mv.addObject("name", infojson.getString("name"));
      mv.addObject("picture", infojson.getString("picture"));
      mv.setViewName("index");

      return mv;
    }  
}
2-6. アクセストークン発行/ユーザ情報取得をリクエスト実行クラス

 - getTokenメソッドで認可コードを引数にアクセストークン発行APIをリクエスト
 - getUserInfoメソッドにてアクセストークンをAuthorizationヘッダーに付与することでユーザ情報を取得

DoHttpRequests.java
package com.example.demo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Value;

public class DoHttpRequests {
    @Value("${security.oauth2.resource.userInfoUri}")
    private String userInfoUri;

    @Value("${security.oauth2.client.accessTokenUri}")
    private String accesstokenurl;

    @Value("${security.oauth2.client.clientId}")
    private String clientId;

    @Value("${security.oauth2.client.clientSecret}")
    private String clientSecret;

    @Value("${security.oauth2.client.pre-established-redirect-uri}")
    private String redirecturi;

    public String getToken(String code) throws IOException {
        URL url = new URL(accesstokenurl);
        HttpURLConnection  conn = null;
        conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        conn.setUseCaches(false);
        String params = new String("code=" + code + "&client_id=" + this.clientId + "&client_secret=" + this.clientSecret +
                "&redirect_uri=" + this.redirecturi + "&grant_type=authorization_code");    
        String resp = this.parseResp(conn, params);
        return resp;
    }

    public String getUserInfo(String accessToken) throws IOException {
        URL url = new URL(userInfoUri);
        HttpURLConnection  conn = null;
        conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Authorization", "Bearer " + accessToken);
        conn.setUseCaches(false);
        String resp = this.parseResp(conn, null);
        return resp;
    }


    public String parseResp(HttpURLConnection conn, String params) throws IOException {
        StringBuffer result = new StringBuffer();
        if(Objects.nonNull(params)) {
            OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());
            out.write(params);
            out.close();
        }
        conn.connect();
        final int status = conn.getResponseCode();
        if (status == 200) {
            final InputStream input = conn.getInputStream();
            String encoding = conn.getContentEncoding();
            if(null == encoding){
                encoding = "UTF-8";
            }
            final InputStreamReader inReader = new InputStreamReader(input, encoding);
            final BufferedReader bufReader = new BufferedReader(inReader);
            String line = null;
            while((line = bufReader.readLine()) != null) {
                result.append(line);
            }
            bufReader.close();
            inReader.close();
            input.close();
        } else {
            System.out.println(status);
        }

        conn.disconnect();

        return result.toString();
    }
}

3. 検証

3-1. http://localhost:8080/loginへアクセス
3-2. LINEログイン画面へリダイレクト

キャプチャ.PNG

3-3. ログイン実行

 - リフレッシュトークンにより、アクセストークンの有効期限満了後(30日後)も、当該トークンのリクエストによりアクセストークンの再発行が可能。
 - リフレッシュトークンはアクセストークン満了後、最長10日間有効。

キャプチャ.PNG

3-4. id_tokenをデコード

ID TokenがJWT形式で返却されているため、ヘッダー・ペイロード部分をBase64デコードし、中身を参照

ヘッダー:
{"typ":"JWT","alg":"HS256"}

ペイロード:
{"iss":"https://access.line.me","sub":"**********","aud":"1653797412","exp":1579957272,"iat":1579953672,"amr":["linesso"],"name":"Yu","picture":"https://profile.line-scdn.net/0ho4KGrFFFMBt2PhvCq9pPTEp7PnYBEDZTDg98LVpsanhTXn4aTFx5Lwdpbn9SWnEYHwgtfFs3Znhd"}

クレーム名 クレーム内容
iss トークンの発行者(ISSuer)
sub トークンの対象者(SUBubect)
aud トークンの受け手(AUDience)
exp トークンの有効期限(EXPiration time)
iat トークンの発行時タイムスタンプ(Issued-AT)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AtCoderでコード提出したら速攻つまずいた。

はじめに

今までエンジニアとして働いてきたのですが、主に上流工程ばかりで、実装が殆どできずやばいと思って、競技プリグラミングやり始めました。なんとか一問解きましたが、そこでつまずいたことを備忘録として載せます。

今回はデータ入力のScannerについて書きます。

挑戦した問題

Atcodeの過去の得点100の問題…つまり簡単な問題です。

https://atcoder.jp/contests/abc042/tasks/abc042_a

提出1回目

色々なサイトを見ながら四苦八苦しつつもなんとかコーディングし、vscodeのターミナルから実行して想定通りの結果を得ることができました。以下がコード内容(おそらく有識者からみたら、汚いコードですがご容赦を。。)

public class Main {
    public static void main(String args[]) {
        String answer = "YES";
        String[] haiku = args;
        int sum = 0;
        for(int i = 0; i < 3; i++){
          if(haiku[i].equals("7") || haiku[i].equals("5")){
              sum = sum + Integer.valueOf(haiku[i]);
              System.out.println(sum);
          }
          else{
              answer = "NO";
          }
          System.out.println(haiku[i]);
        } 
        if(sum != 17){
            answer = "NO";
        }
        System.out.println(answer);
    }
}

結果

実行時エラー…中身を確認すらしてもらえず…\(^o^)/

スクリーンショット 2020-01-25 19.45.10.png

何がだめだったのか

入力方法が単純にだめだったみたいですね。

ターミナルでJava Main X X Xと入力するとスペース区切りで配列argsに格納されるので、大丈夫と思っていましたが完全に見誤っていました。

どうすればよかったのか

入力内容を取得するときにScannerを使うとよいとのこと。next();の部分で入力を取得、もう一度使用すると次の入力値が取得できます。

Scanner sc = new Scanner(System.in);
int i = Integer.parseInt(sc.next());

提出5回目

コードを何度もリファクタリング(用語使いたがりのにわかですみません。。)してなんとかクリアしました笑

import java.util.Scanner;
public class Main {
    public static void main(String args[]) {
        String answer = "YES";
        Scanner s = new Scanner(System.in);
        int sum = 0;
        for(int i = 0; i < 3; i++){
            int haiku = Integer.parseInt(s.next());
          if(haiku == 7 || haiku == 5 ){
              sum = sum + haiku;
          }
          else{
              answer = "NO";
          }
        } 
        if(sum != 17){
            answer = "NO";
        }
        System.out.println(answer);
    }
}

参考サイト

https://qiita.com/p_shiki37/items/a0f6aac33bf60f5f65e4
https://uxmilk.jp/48686

何かご指摘等ありましたら、ご遠慮無くおっしゃってくださいーm(_ _)m

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

AtCoderで早速標準入力の方法でつまずいた。

はじめに

今までエンジニアとして働いてきたのですが、主に上流工程ばかりで、実装が殆どできずやばいと思って、競技プリグラミングやり始めました。なんとか一問解きましたが、そこでつまずいたことを備忘録として載せます。

今回はデータ入力のScannerについて書きます。

挑戦した問題

Atcodeの過去の得点100の問題…つまり簡単な問題です。

https://atcoder.jp/contests/abc042/tasks/abc042_a

提出1回目

色々なサイトを見ながら四苦八苦しつつもなんとかコーディングし、vscodeのターミナルから実行して想定通りの結果を得ることができました。以下がコード内容(おそらく有識者からみたら、汚いコードですがご容赦を。。)

public class Main {
    public static void main(String args[]) {
        String answer = "YES";
        String[] haiku = args;
        int sum = 0;
        for(int i = 0; i < 3; i++){
          if(haiku[i].equals("7") || haiku[i].equals("5")){
              sum = sum + Integer.valueOf(haiku[i]);
              System.out.println(sum);
          }
          else{
              answer = "NO";
          }
          System.out.println(haiku[i]);
        } 
        if(sum != 17){
            answer = "NO";
        }
        System.out.println(answer);
    }
}

結果

実行時エラー…中身を確認すらしてもらえず…\(^o^)/

スクリーンショット 2020-01-25 19.45.10.png

何がだめだったのか

入力方法が単純にだめだったみたいですね。

ターミナルでJava Main X X Xと入力するとスペース区切りで配列argsに格納されるので、大丈夫と思っていましたが完全に見誤っていました。

どうすればよかったのか

入力内容を取得するときにScannerを使うとよいとのこと。next();の部分で入力を取得、もう一度使用すると次の入力値が取得できます。

Scanner sc = new Scanner(System.in);
int i = Integer.parseInt(sc.next());

提出5回目

コードを何度もリファクタリング(用語使いたがりのにわかですみません。。)してなんとかクリアしました笑

import java.util.Scanner;
public class Main {
    public static void main(String args[]) {
        String answer = "YES";
        Scanner s = new Scanner(System.in);
        int sum = 0;
        for(int i = 0; i < 3; i++){
            int haiku = Integer.parseInt(s.next());
          if(haiku == 7 || haiku == 5 ){
              sum = sum + haiku;
          }
          else{
              answer = "NO";
          }
        } 
        if(sum != 17){
            answer = "NO";
        }
        System.out.println(answer);
    }
}

参考サイト

https://qiita.com/p_shiki37/items/a0f6aac33bf60f5f65e4
https://uxmilk.jp/48686

何かご指摘等ありましたら、ご遠慮無くおっしゃってくださいーm(_ _)m

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

ListとArrayListの違い

List

こんな感じで作成

List<Integer> list = Arrays.asList(array);

※:addメソッド等を使うと、実行時エラーとなる
arrayを参照しているだけなので、arrayを変更すると、反映される。

ArrayList

こんな感じで作成

ArrayList<Integer> arrayList = new ArrayList<Integer>(Arrays.asList(array));

addメソッドが使える
arrayの値を元に新しいインスタンスを作成しているため。独立したオブジェクトとなる。

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

Javaの<>ってなんだ?

導入

こんにちは。けちょんです。
皆さん、ジェネリクス使ってますか?
私は今日初めて学びました。
というか今まで見て見ぬ振りしてましたが、ついに学ぶ場に出会いました。

ジェネリクスと出会う場面

一番出会うのは、リストだと思います。
以下のような宣言したことあるかと思います。

ArrayList<String> list = new ArrayList<>();

可変長な配列ということしか私は知りませんでした。
この<>を使うクラスをジェネリッククラスと言います。

どういう役割なの?

ArrayList<String> list

まずこの部分。
ArrayListはそもそもどんなクラスかというと、複数の要素を配列のように保持するクラスです。
Stringに限らず、様々な型のオブジェクトを格納できます。(一種類だけだけど。)
で、今回Stringを格納すると定めているのが上の宣言です。

new ArrayList<>();

次にこの部分。<>の中が空になってますが、これは省略してるだけです。(左辺のStringから型推測できる)

一般的なジェネリクス

ではリストに限らず、より広い観点でジェネリクスに触れてみます。
簡単な定義は以下ですね。

public class Gene<T> {
    T t;

    public void setT(T t) {
        this.t = t;
    }

    public T getT() {
        return t;
    }
}

使い方は以下

Gene<String> g1 = new Gene<>(); // String型で固定
Gene<Integer> g2 = new Gene<>(); // Integer型で固定
g1.setT("test");
System.out.print(g1.getT()); // test
g2.setT(24);
System.out.print(g2.getT()); // 24

何が嬉しいのか

今回の例だと、同一のクラスに対して、複数のクラスを使用できることができました。

いやいや、これでも良くない?

public class GeneObj {
    Object t;

    public void setT(Object t) {
        this.t = t;
    }

    public Object getT() {
        return t;
    }
}

型Tで限定していた部分に対して、Objectクラスを使ってみました。
では使ってみましょう。

GeneObj g1 = new GeneObj(); 
GeneObj g2 = new GeneObj(); 
g1.setT("test");
System.out.print(g1.getT()); // test
g2.setT(24);
System.out.print(g2.getT()); // 24

同じようなコードで書けましたね。

まとめ

ジェネリクスは使わなくても良い???

いやいや

今回の例ではどちらも動きますが、Objectを使用すると問題があります。
簡単に言うと、このクラスは使いにくいです。

実行時エラーとなる使用例

GeneObj g1 = new GeneObj(); // String型で固定
g1.setT("1");
Integer res = (Integer) g1.getT() + 1; //ClassCastExceptionの発生
System.out.print(res);

上では、誤ってInteger型ではなくStringを詰め込んでしまっています。
そのためIntegerクラスにキャストする際ClassCastExceptionが発生してしまいました。
使い手からすれば、自分がsetTした型を把握しておかなければならず、バグの温床になってしまいます。
ジェネリクスを使えば、そもそもキャストを使用する必要がなく、コンパイルエラーにて気づくことができます。

まとめ

型の柔軟性とコードの保守性を両立させるためにジェネリクスは必要

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

Java13におけるJavaFXの環境構築

概要

MacBookProを購入したためJavaFXの環境構築をしました。

環境

  • java 13.0.2
  • zsh 5.7.1 (x86_64-apple-darwin19.0.0)
  • MacBook Pro (13-inch, 2017, Four Thunderbolt 3 Ports)

手順

  1. Javaのインストール
  2. JavaFXのインストール
  3. 使いやすくする
    1. PATHを通す
    2. ラップする

Javaのインストール

公式サイトからファイルをダウンロードし、その後インストールします。詳細な指示がサイトに乗っています。

JavaFX

公式サイトのダウンロードページからSDKの方をダウンロードします。そのファイルを、今回は/Library/Java/JavaVirtualMachines/の下で展開しました。

使いやすくする

この状態でも、例えばコンパイルなら

$ javac --module-path /Library/Java/JavaVirtualMachines/javafx-sdk-13.0.2/lib --add-modules=javafx.controls,javafx.fxml *.java

で、実行できます。
でも長すぎるので、もっと使いやすくしましょう。

PATHを通す

まずは途中の/Library/Java/JavaVirtualMachines/javafx-sdk-13.0.2/libにPATHを通します。

PATHを通すには設定ファイルを編集する必要があります。今回はzshなので.zshrcを編集します。

~/.zshrc
export PATH_TO_FX=/Library/Java/JavaVirtualMachines/javafx-sdk-13.0.2/lib

上の一行を一番下に加えました。その後ターミナルを再起動もしくは

$ source ~/.zshrc

をターミナル上で実行することで反映させることができます。PATHが通っているかどうかは

$ echo $PATH_TO_FX

の実行結果が、指定した場所になっているかどうかで確認できます。
ここまでやると、

$ javac --module-path $PATH_TO_FX --add-modules=javafx.controls,javafx.fxml,javafx.media *.java

で実行できるようになります。

ラップする

まだ長いのでラップします。そのためにまずは$HOME/binというディレクトリを用意し、ここにPATHを通すため~/.zshrc

~/.zshrc
export PATH="$PATH:$HOME/bin"

上の一行を加えます。
その後該当ディレクトリに、jfxcjfxの二つのファイルを作製します。

$HOME/bin/jfxc
#!/bin/sh
javac --module-path $PATH_TO_FX --add-modules=javafx.controls,javafx.fxml,javafx.media $@
$HOME/bin/jfxc
#!/bin/sh
java --module-path $PATH_TO_FX --add-modules=javafx.controls,javafx.fxml,javafx.media $@

それぞれ上記のように書き、権限をふっておきます。

$sudo chmod 755 jfxc
$sudo chmod 755 jfx

最後に反映させると実行できるようになります。

$ source ~/.zshrc

実行方法

コンパイル

$ jfxc *.java

実行

$ jfx (ファイル名)

追記

1月26日 Audioclipが使えなかったため一部編集しました

参考

以下の記事を参考にさせていただきました。
Java11でJavaFXを動かす
macでPATHを通す方法
コピペ1分でMacの自作コマンドを作る

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

研修でよく出てきそうな●とか■で図形出力10連発(Java)

(共通部分)

package test;                   

import java.util.LinkedList;                    
import java.util.List;                  
import java.util.Scanner;                   

public class Main {                                 
    public static void main(String[] args) {                
        Scanner scan = new Scanner(System.in);          
        int num2 = scan.nextInt();          
        String square = "■";          

        // コード

    }
}

スクショは、全て入力値が9の場合です。

パターン1

image.png

// 1から開始して、入力された数になるまでインクリメントする               
// ※lineは現在の行数であると同時に、■の描画数を表す                
for(int line = 1; line <= num2; line++ ){               
    // 1から開始して、この行の描画数以下の間は繰り返す           
    for(int idxOfMarks = 1; idxOfMarks <= line; idxOfMarks++){          
        System.out.print(square);       
    }           
    // 次の行に行くために改行する          
    System.out.println("");         
}               

簡潔に書くなら以下。

StringBuilder sb = new StringBuilder();             
for (int line = 0; line < num2; line++) {               
    System.out.println(sb.append(square));              
}               

パターン2

image.png

// 入力された数値から開始して、その数が1になるまでデクリメントする               
// ※idxは現在の行数であると同時に、■の描画数を表す             
for(int line = num2; line >= 1 ; line--){               
    for(int idxOfMarks = line; idxOfMarks >= 1; idxOfMarks--){          
        System.out.print(square);       
    }           
    System.out.println("");         
}               

パターン3

image.png

String batsu = "×";                
for(int line = 1; line <= num2; line++ ){               
    // 1から開始して、この行の描画数以下の間は繰り返す           
    for(int idxOfMarks = 1; idxOfMarks <= line; idxOfMarks++){          
        if(idxOfMarks % 2 !=0){     
            System.out.print(square);   
        }else{      
            System.out.print(batsu);    
        }       
    }           
    System.out.println("");         
}   

パターン4

image.png

for(int line = 1; line <= num2; line++){                
    for(int idxOfMarks = 1; idxOfMarks <= line; idxOfMarks++){          
        // 行が奇数である場合      
        if(line % 2 != 0){      
            // 奇数個目の印になる場合    
            if(idxOfMarks % 2 != 0){    
                System.out.print(square);
            // 偶数個目の印になる場合    
            }else{  
                System.out.print(batsu);
            }   
        // 行が偶数である場合      
        }else{      
            // 奇数個目の印になる場合    
            if(idxOfMarks % 2 == 0){    
                System.out.print(square);
            // 偶数個目の印になる場合    
            }else{  
                System.out.print(batsu);
            }   
        }       
    }           
    System.out.println("");         
}               

パターン5

image.png

for(int line = num2; line >=1; line--){             
    // 空白表示数は、入力数-現在の行で求める          
    int empty = num2 - line;            
    // まず空白分を出力する           
    for(int n=1 ;n <=empty; n++){           
        System.out.print(" ");        
    }           
    // その後、印を出力する           
    for(int idxOfMarks = line; idxOfMarks >= 1 ; idxOfMarks--){         
        System.out.print(square);       
    }           
    System.out.println("");         
}               

パターン6

image.png

for(int line = 1 ; line<= num2 ; line++){               
    System.out.print(square);           
}               
System.out.println("");             

int emptyLines = num2 - 2;              
for(int line2 = 1; line2 <= emptyLines; line2++){               
    for (int index2 = 1 ; index2 <= num2; index2++){            
        if(index2 == 1 || index2 == num2){      
            System.out.print(square);   
        }else{      
            System.out.print(" ");    
        }       
    }           
    System.out.println("");         
}               
for(int line = 1 ; line<= num2 ; line++){               
    System.out.print(square);           
}               

パターン7

image.png

for(int idx = 1; idx <= num2; idx++){               
    for(int i = 1; i <= num2-idx; i++){         
        System.out.print(" ");        
    }           

    for(int idxOfMarks = 1; idxOfMarks <= idx*2-1; idxOfMarks++){           
        System.out.print(square);       
    }           

    for(int i = 1; i <= num2-idx; i++){         
        System.out.print(" ");        
    }           
    System.out.println();           
}               

パターン8

image.png

StringBuilder sb3 = new StringBuilder();                
for (int line = 0; line < num2; line++) {               
    System.out.println(sb3.append(square));             
}               
for (int line = 0; line < num2; ++line){                
    System.out.println(sb3.deleteCharAt(0));                
}               

パターン9

image.png

    for(int line = 1 ; line <= num2; line++){                   
        for(int idxOfNum=1; idxOfNum<=line; idxOfNum++){                
            if(line == 1 || line == 2 || line == num2){         
                System.out.print("●");        
            }else{          
                if(idxOfNum == 1 || idxOfNum == line){      
                    System.out.print("●");    
                }else{      
                    System.out.print("○");    
                }       
            }           
        }               
        System.out.println();               
    }   

パターン10

image.png

    // 行が1増えると、左の●の位置が+1、右の●の位置が-1となる。                  
    int idxOfBlack1 = 1;                    
    int idxOfBlack2 = num2;                 
    for(int line = 1 ; line <= num2; line++){                   
        for(int idxOfNum = 1; idxOfNum<=num2; idxOfNum++){              
            if(idxOfNum == idxOfBlack1 | idxOfNum == idxOfBlack2){          
                System.out.print("●");        
            }else{          
                System.out.print("○");        
            }           
        }               
        idxOfBlack1++;              
        idxOfBlack2--;              
        System.out.println();               
    }                   

(きれいな書き方がありましたらご教授頂けると幸いです。)

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

研修でよく出てきそうな●とか■で図形描画10連発(Java)

(共通部分)

package test;                   

import java.util.LinkedList;                    
import java.util.List;                  
import java.util.Scanner;                   

public class Main {                                 
    public static void main(String[] args) {                
        Scanner scan = new Scanner(System.in);          
        int num2 = scan.nextInt();          
        String square = "■";          

        // コード

    }
}

スクショは、全て入力値が9の場合です。

パターン1

image.png

// 1から開始して、入力された数になるまでインクリメントする               
// ※lineは現在の行数であると同時に、■の描画数を表す                
for(int line = 1; line <= num2; line++ ){               
    // 1から開始して、この行の描画数以下の間は繰り返す           
    for(int idxOfMarks = 1; idxOfMarks <= line; idxOfMarks++){          
        System.out.print(square);       
    }           
    // 次の行に行くために改行する          
    System.out.println("");         
}               

簡潔に書くなら以下。

StringBuilder sb = new StringBuilder();             
for (int line = 0; line < num2; line++) {               
    System.out.println(sb.append(square));              
}               

パターン2

image.png

// 入力された数値から開始して、その数が1になるまでデクリメントする               
// ※idxは現在の行数であると同時に、■の描画数を表す             
for(int line = num2; line >= 1 ; line--){               
    for(int idxOfMarks = line; idxOfMarks >= 1; idxOfMarks--){          
        System.out.print(square);       
    }           
    System.out.println("");         
}               

パターン3

image.png

String batsu = "×";                
for(int line = 1; line <= num2; line++ ){               
    // 1から開始して、この行の描画数以下の間は繰り返す           
    for(int idxOfMarks = 1; idxOfMarks <= line; idxOfMarks++){          
        if(idxOfMarks % 2 !=0){     
            System.out.print(square);   
        }else{      
            System.out.print(batsu);    
        }       
    }           
    System.out.println("");         
}   

パターン4

image.png

for(int line = 1; line <= num2; line++){                
    for(int idxOfMarks = 1; idxOfMarks <= line; idxOfMarks++){          
        // 行が奇数である場合      
        if(line % 2 != 0){      
            // 奇数個目の印になる場合    
            if(idxOfMarks % 2 != 0){    
                System.out.print(square);
            // 偶数個目の印になる場合    
            }else{  
                System.out.print(batsu);
            }   
        // 行が偶数である場合      
        }else{      
            // 奇数個目の印になる場合    
            if(idxOfMarks % 2 == 0){    
                System.out.print(square);
            // 偶数個目の印になる場合    
            }else{  
                System.out.print(batsu);
            }   
        }       
    }           
    System.out.println("");         
}               

パターン5

image.png

for(int line = num2; line >=1; line--){             
    // 空白表示数は、入力数-現在の行で求める          
    int empty = num2 - line;            
    // まず空白分を出力する           
    for(int n=1 ;n <=empty; n++){           
        System.out.print(" ");        
    }           
    // その後、印を出力する           
    for(int idxOfMarks = line; idxOfMarks >= 1 ; idxOfMarks--){         
        System.out.print(square);       
    }           
    System.out.println("");         
}               

パターン6

image.png

for(int line = 1 ; line<= num2 ; line++){               
    System.out.print(square);           
}               
System.out.println("");             

int emptyLines = num2 - 2;              
for(int line2 = 1; line2 <= emptyLines; line2++){               
    for (int index2 = 1 ; index2 <= num2; index2++){            
        if(index2 == 1 || index2 == num2){      
            System.out.print(square);   
        }else{      
            System.out.print(" ");    
        }       
    }           
    System.out.println("");         
}               
for(int line = 1 ; line<= num2 ; line++){               
    System.out.print(square);           
}               

パターン7

image.png

for(int idx = 1; idx <= num2; idx++){               
    for(int i = 1; i <= num2-idx; i++){         
        System.out.print(" ");        
    }           

    for(int idxOfMarks = 1; idxOfMarks <= idx*2-1; idxOfMarks++){           
        System.out.print(square);       
    }           

    for(int i = 1; i <= num2-idx; i++){         
        System.out.print(" ");        
    }           
    System.out.println();           
}               

パターン8

image.png

StringBuilder sb3 = new StringBuilder();                
for (int line = 0; line < num2; line++) {               
    System.out.println(sb3.append(square));             
}               
for (int line = 0; line < num2; ++line){                
    System.out.println(sb3.deleteCharAt(0));                
}               

パターン9

image.png

    for(int line = 1 ; line <= num2; line++){                   
        for(int idxOfNum=1; idxOfNum<=line; idxOfNum++){                
            if(line == 1 || line == 2 || line == num2){         
                System.out.print("●");        
            }else{          
                if(idxOfNum == 1 || idxOfNum == line){      
                    System.out.print("●");    
                }else{      
                    System.out.print("○");    
                }       
            }           
        }               
        System.out.println();               
    }   

パターン10

image.png

    // 行が1増えると、左の●の位置が+1、右の●の位置が-1となる。                  
    int idxOfBlack1 = 1;                    
    int idxOfBlack2 = num2;                 
    for(int line = 1 ; line <= num2; line++){                   
        for(int idxOfNum = 1; idxOfNum<=num2; idxOfNum++){              
            if(idxOfNum == idxOfBlack1 | idxOfNum == idxOfBlack2){          
                System.out.print("●");        
            }else{          
                System.out.print("○");        
            }           
        }               
        idxOfBlack1++;              
        idxOfBlack2--;              
        System.out.println();               
    }                   

(きれいな書き方がありましたらご教授頂けると幸いです。)

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

macOS に jenvをインストール、設定する

2020/01/25時点で他のQiita記事が古くてちゃんと動作しない&公式ドキュメントと違うので記述

前提

環境

macOS Mojave 10.14.6で確認

~/.jenv を作成する

mkdir ~/.jenv

jenvのインストール

1. homebrewを利用する場合

brew install jenv

2. git からcloneする場合

git clone https://github.com/jenv/jenv.git ~/.jenv

2-1. bash

echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(jenv init -)"' >> ~/.bash_profile

2-2. zsh

echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(jenv init -)"' >> ~/.zshrc

シェルの再起動

やり方

  • ターミナルを再起動する or
  • exec $SHELL -l

jenvの動作確認

jenv doctor

ちゃんと動く場合、以下のアウトプットが出る

[OK]    No JAVA_HOME set
[ERROR] Java binary in path is not in the jenv shims.
[ERROR] Please check your path, or try using /path/to/java/home is not a valid path to java installation.
    PATH : /Users/user/.jenv/libexec:/Users/user/.jenv/shims:/Users/user/.jenv/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
[OK]    Jenv is correctly loaded

JAVA_HOMEの設定確認

echo ${JAVA_HOME}
jenv enable-plugin export
exec $SHELL -l

Java環境のインストール

Java環境をbrew caskでインストールする場合

brew cask install java

Java8の場合

brew cask install java8

Java環境を追加

jenv addを使う

jenv add $(/usr/libexec/java_home)

もちろん、以下のように直接ディレクトリを指定しても良い

jenv add /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home

Java環境のバージョン一覧を見る


$ jenv versions
* system (set by /Users/user/.jenv/version)
  11.0
  11.0.2
  openjdk64-11.0.2

デフォルトでは、system JavaがJavaの最新バージョンになる。

Java環境の選択

$ jenv local 11.0.2
$ exec $SHELL -l
$ cat .java-version
11.0.2

設定されたかの確認

echo ${JAVA_HOME}
/Users/hogehoge/.jenv/versions/11.0.2

これで設定OK。

いらないので .java-versionを削除する

rm .java-version

グローバルJavaバージョンの設定

グローバルに設定したい時のみ必要

jenv global 11.0.2

シェルJavaバージョンの設定

jenv shell 11.0.2

共通ワークフロー

macOSで2つのJVMを使う

Java11.0.2がインストールされているところに、Java 8をインストールする

brew cask install adoptopenjdk8
brew cask install caskroom/versions/adoptopenjdk8

上記によって、Java 8の最新バージョンをmacOSの特別なディレクトリにインストールする

$ ls -1 /Library/Java/JavaVirtualMachines 
adoptopenjdk-8.jdk
openjdk-11.0.2.jdk

adoptopenjdk-8.jdkディレクトリというのが見える。(ユーザによってこのディレクトリは変わる)
/usr/libexec/java_home からは取得できないので、 jenv addで追加する

$ jenv add /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/
openjdk64-1.8.0.222 added
1.8.0.222 added
1.8 added
$ jenv versions
* system
  1.8
  1.8.0.222
  openjdk64-1.8.0.222
  11.0
  11.0.2
  openjdk64-11.0.2
  oracle64-1.8.0.202-ea

参考

以下を参考に、冗長なところを削除、足りないところを追加しました。

GitHub - jenv/jenv: Manage your Java environment
https://github.com/jenv/jenv

以上。

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

Java勉強始めたてによる二次元配列

自分用メモ

参考にはなりませんよぉ~

Javaに触れつつ持ち合わせてる知識だけで二次元配列を考えてみる

今回は5行5列の二次元配列に1~5のランダムな数字をぶち込んで表示させた

やったこと
二次元配列の宣言
    ↓
1~5のランダムな要素を代入  
    ↓
改行しつつ表示させるのを5回繰り返す
みたいな

package sample;

public class sample {

    public static void main(String[] args) {

        String sep = System.lineSeparator();

        int x = 6;
        int y = 6;

        int[][] array = new int [x][y];

        for(int i = 1; i < x; i++){
            for(int j = 1; j < y; j++){
                array[i][j] = new java.util.Random().nextInt(5)+1;
                System.out.print(array[i][j]);
            }
            System.out.print(sep);
        }

    }

}


出力はこんな感じ

53232
31333
24344
34225
12133

感想

なんだこの稚拙なコードは!!って感じですわね。調べてみた感じだとリストやランダムなど便利なコレクションがあるみたいですね~~。classの外側にimport java.util.~;とか書いてあってこれマジで何ってなったりしてるので追々勉強していこうと思いましたまる。(Qiitaの書き方も勉強…)
次回は探索アルゴリズムなども考慮しつつ隣どうし重複しないランダムな要素を含んだ二次元配列書く^

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