- 投稿日:2020-01-25T21:17:18+09:00
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.ymlspring: 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: 80802-3. @Beanの定義
- @Beanを記述したメソッドでインスタンス化したクラスを返却
ConfigBean.javaimport 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.javaimport 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.javaimport 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.javapackage 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ログイン画面へリダイレクト
3-3. ログイン実行
- リフレッシュトークンにより、アクセストークンの有効期限満了後(30日後)も、当該トークンのリクエストによりアクセストークンの再発行が可能。
- リフレッシュトークンはアクセストークン満了後、最長10日間有効。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)
- 投稿日:2020-01-25T20:06:11+09:00
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^)/
何がだめだったのか
入力方法が単純にだめだったみたいですね。
ターミナルで
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
- 投稿日:2020-01-25T20:06:11+09:00
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^)/
何がだめだったのか
入力方法が単純にだめだったみたいですね。
ターミナルで
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
- 投稿日:2020-01-25T18:50:16+09:00
ListとArrayListの違い
- 投稿日:2020-01-25T18:16:03+09:00
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した型を把握しておかなければならず、バグの温床になってしまいます。
ジェネリクスを使えば、そもそもキャストを使用する必要がなく、コンパイルエラーにて気づくことができます。まとめ
型の柔軟性とコードの保守性を両立させるためにジェネリクスは必要
- 投稿日:2020-01-25T16:34:35+09:00
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)
手順
- Javaのインストール
- JavaFXのインストール
- 使いやすくする
- PATHを通す
- ラップする
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
を編集します。~/.zshrcexport 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
に~/.zshrcexport PATH="$PATH:$HOME/bin"上の一行を加えます。
その後該当ディレクトリに、jfxc
とjfx
の二つのファイルを作製します。$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の自作コマンドを作る
- 投稿日:2020-01-25T13:18:43+09:00
研修でよく出てきそうな●とか■で図形出力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
// 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
// 入力された数値から開始して、その数が1になるまでデクリメントする // ※idxは現在の行数であると同時に、■の描画数を表す for(int line = num2; line >= 1 ; line--){ for(int idxOfMarks = line; idxOfMarks >= 1; idxOfMarks--){ System.out.print(square); } System.out.println(""); }パターン3
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
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
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
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
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
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
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
// 行が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(); }(きれいな書き方がありましたらご教授頂けると幸いです。)
- 投稿日:2020-01-25T13:18:43+09:00
研修でよく出てきそうな●とか■で図形描画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
// 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
// 入力された数値から開始して、その数が1になるまでデクリメントする // ※idxは現在の行数であると同時に、■の描画数を表す for(int line = num2; line >= 1 ; line--){ for(int idxOfMarks = line; idxOfMarks >= 1; idxOfMarks--){ System.out.print(square); } System.out.println(""); }パターン3
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
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
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
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
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
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
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
// 行が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(); }(きれいな書き方がありましたらご教授頂けると幸いです。)
- 投稿日:2020-01-25T09:53:50+09:00
macOS に jenvをインストール、設定する
2020/01/25時点で他のQiita記事が古くてちゃんと動作しない&公式ドキュメントと違うので記述
前提
環境
macOS Mojave 10.14.6で確認
~/.jenv を作成する
mkdir ~/.jenvjenvのインストール
1. homebrewを利用する場合
brew install jenv2. git からcloneする場合
git clone https://github.com/jenv/jenv.git ~/.jenv2-1. bash
echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.bash_profile echo 'eval "$(jenv init -)"' >> ~/.bash_profile2-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 loadedJAVA_HOMEの設定確認
echo ${JAVA_HOME}jenv enable-plugin export exec $SHELL -lJava環境のインストール
Java環境をbrew caskでインストールする場合
brew cask install javaJava8の場合
brew cask install java8Java環境を追加
jenv addを使う
jenv add $(/usr/libexec/java_home)もちろん、以下のように直接ディレクトリを指定しても良い
jenv add /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/HomeJava環境のバージョン一覧を見る
$ 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.jdkadoptopenjdk-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以上。
- 投稿日:2020-01-25T01:59:24+09:00
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の書き方も勉強…)
次回は探索アルゴリズムなども考慮しつつ隣どうし重複しないランダムな要素を含んだ二次元配列書く^