- 投稿日:2020-07-24T17:38:19+09:00
QuarkusでMultipart Requestの文字化け対策
ちょっと嵌ったのでメモ。
TL;DR
これで文字化けが直る
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-undertow</artifactId> </dependency>@Provider public class ContentTypeSetterPreProcessorInterceptor implements ContainerRequestFilter { private @Context HttpServletRequest request; @Override public void filter(ContainerRequestContext requestContext) { request.setAttribute(InputPart.DEFAULT_CHARSET_PROPERTY, StandardCharsets.UTF_8.name()); } }背景
Vue.jsとQuarkusでファイルのアップロード機能を作ったのですが、同じフォームで連携しているタイトルが文字化けする現象が発生いしました。記載してたコードは以下の通り。
クライアント側。
multipart/form-data
を使うためにnew FormData()
を使ってます。let data = new FormData(); data.append("title", this.item.title); data.append("myfile", this.item.files[0]); this.axios.post(uri, data).then(() => { this.$swal({ icon: "success", text: "Upload Success!" }); })サーバ側。JAX-RSの
@MultipartForm
を使います。タイトルもファイルも自動でBeanにマッピングされます。public class MyFileFormBean { @FormParam("title") @PartType(MediaType.TEXT_PLAIN) private String title = null; @FormParam("myfile") @PartType(MediaType.APPLICATION_OCTET_STREAM) private byte[] myfile = null; ....JAX-RS側。
@POST @Consumes({MediaType.MULTIPART_FORM_DATA}) @Produces(MediaType.APPLICATION_JSON) public Response upload(@MultipartForm MyFileFormBean form) throws IOException { System.out.println("title: " + form.getTitle()); ... }と、これで上手くいく予定だったのですが、なぜだか日本語が文字化け。。。
title: ????????????原因
調べてみるとRESTEasyでは文字コードが含まれてない時はデフォルトではASCII(ISO-8859-1)に変換されるようです。
Formにaccept-charset
を指定すれば良いっぽいですがJSでやるのはちょっとめんどそうなので、サーバで強制変換するのがよさそうです。ググったらJBoss Communityにサンプル実装がありました。
@Provider @ServerInterceptor public class ContentTypeSetterPreProcessorInterceptor implements PreProcessInterceptor { public ServerResponse preProcess(HttpRequest request, ResourceMethod method) throws Failure, WebApplicationException { request.setAttribute(InputPart.DEFAULT_CHARSET_PROPERTY, "UTF-8"); return null; } }なんですが、Quarkusに使われているRestEasyはJAX-RS 2.0準拠なので上記の書き方は出来ません。なので
PreProcessInterceptor
の代わりにContainerRequestFilter
を使う必要があります。対策
サンプルを参考にしつつJAX-RS 2.0準拠で書き直します。
QuarkusはデフォルトではServlet compatibilityに記載があるようにVert.x HTTP serverを使うのでHttpServletRequestが使えないです。なのでquarkus-undertow
を依存に追加します。<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-undertow</artifactId> </dependency>JAX-RS 2.0ベースでリクエストをフックして
DEFAULT_CHARSET_PROPERTY
をUTF-8
に強制変換するのは以下のコードになります。@Provider public class ContentTypeSetterPreProcessorInterceptor implements ContainerRequestFilter { private @Context HttpServletRequest request; @Override public void filter(ContainerRequestContext requestContext) { request.setAttribute(InputPart.DEFAULT_CHARSET_PROPERTY, StandardCharsets.UTF_8.name()); } }
@Provider
を設定しておけば特に設定ファイルの追加等はいらないです。これで文字化けは解決しました。まとめ
JavaServletで文字化けするのでフィルタを書くのは昔はあるあるだったのですが、まさか現代でもやるとは思わず少し嵌りました。ファイルのアップロードは相変わらず罠がいっぱいですね。
RFCとは違うとはいえUTF-8はASCII領域は同じコードだし、UTF-8がデフォルトでも良い気はします。GitHubのIssueでも"UTF-8 encoding problem with MultiPart/RestEasy"として話題に上がってるので、設定ファイルで簡単に切り替えれるように将来的にはなるかもしれないです。
それではHappy Hacking!
- 投稿日:2020-07-24T17:06:06+09:00
JavaはPDF文書に形状を描画します
PDFドキュメントの編集プロセスでは、ドキュメントにポリゴン、長方形、楕円などのグラフィックスを追加する必要がある場合があります。Free Spire.PDF for Javaは、Javaコードを使用してPDFドキュメントに図形を描画するのに役立ちます、形状の縁の色と塗りつぶしの色を設定します。
JARパッケージのインポート
方法1:Free Spire.PDF for Javaをダウンロードして解凍し、jarパッケージをlibフォルダーに依存関係としてJavaアプリケーションに直接インポートします。方法2:Mavenリポジトリからjarパッケージをインストールし、pom.xmlファイルのコードを次のように構成します。
<repositories> <repository> <id>com.e-iceblue</id> <name>e-iceblue</name> <url>http://repo.e-iceblue.com/nexus/content/groups/public/</url> </repository> </repositories> <dependencies> <dependency> <groupId>e-iceblue</groupId> <artifactId>spire.pdf.free</artifactId> <version>2.6.3</version> </dependency> </dependencies>Javaコード
import java.awt.*; import java.awt.geom.Rectangle2D; import com.spire.pdf.FileFormat; import com.spire.pdf.PdfDocument; import com.spire.pdf.PdfPageBase; import com.spire.pdf.graphics.*; public class DrawShapes { public static void main(String[] args) { //PDFドキュメントを作成してページを追加する PdfDocument doc = new PdfDocument(); PdfPageBase page = doc.getPages().add(); //長方形を描く PdfPen pen =new PdfPen(new PdfRGBColor(Color.black),0.1); Rectangle2D.Float rect1 = new Rectangle2D.Float(0, 20, 120, 50); PdfLinearGradientBrush linearGradientBrush = new PdfLinearGradientBrush(rect1,new PdfRGBColor(Color.pink),new PdfRGBColor(Color.yellow),PdfLinearGradientMode.Horizontal); page.getCanvas().drawRectangle(pen, linearGradientBrush, rect1); //楕円を描く Point centerStart= new Point(205,45); Point centerEnd= new Point(205,45); PdfRadialGradientBrush radialGradientBrush = new PdfRadialGradientBrush(centerStart,0,centerEnd,25,new PdfRGBColor(Color.white),new PdfRGBColor(Color.cyan)); Rectangle2D.Float rect2= new Rectangle2D.Float(180, 20, 50, 50); page.getCanvas().drawEllipse(pen,radialGradientBrush,rect2); //多角形を描く Point p1=new Point(290,70); Point p2=new Point(310,45); Point p3=new Point(325,60); Point p4=new Point(340,20); Point p5=new Point(370,70); Point[] points = {p1, p2, p3, p4, p5}; PdfBrush brush= PdfBrushes.getGreenYellow(); page.getCanvas().drawPolygon(pen,brush, points); //弧を描く float startAngle = 0; float sweepAngle = 270; Rectangle2D.Float rect3= new Rectangle2D.Float(0, 110, 50, 50); page.getCanvas().drawArc(pen, rect3, startAngle, sweepAngle); //円グラフを描く Rectangle2D.Float rect4= new Rectangle2D.Float(70, 110, 50, 50); page.getCanvas().drawPie(pen, rect4, startAngle, sweepAngle); //線を引く Point pStart=new Point(205,110); Point pEnd=new Point(205,160); page.getCanvas().drawLine(pen, pStart, pEnd); //ベジェ曲線を描く Point startPoint = new Point(290, 135); Point firstControlPoint = new Point(330, 70); Point secondControlPoint = new Point(330, 200); Point endPoint = new Point(370, 135); page.getCanvas().drawBezier(pen, startPoint, firstControlPoint, secondControlPoint, endPoint); //ファイルに保存 doc.saveToFile("DrawShapes.pdf", FileFormat.PDF); } }
- 投稿日:2020-07-24T13:38:34+09:00
簡単な学習 Java
初心者がJavaをより速く、より速く学ぶために一歩一歩
Javaは強力なプログラミング言語です。 習得が簡単で楽しいJava。 この本はJavaを生き生きとさせ、風変わりでフルカラーのイラストが物事をより明るい面に保ちます。 オブジェクト指向プログラミングを整理し、クラスとメソッドでコードを再利用する方法、ループや条件ステートメントなどの制御構造を使用する方法、Javaでシェイプとパターンを描画する方法、キャンバスでゲーム、アニメーション、グラフィックを作成する方法を学びます。
ほんの少しの時間で、Javaを使用して設計および開発する方法を学ぶことができます。 この本の各レッスンは、わかりやすいステップバイステップのアプローチを使用して、前のレッスンに基づいており、基本を基礎から学ぶことができます。 明確な指示と実践的で実践的な例は、Javaと対話する方法を示しています。
この本は、Javaの主なスキルとコーディングを理解するための段階的なガイダンスを教えています。 本の終わりまでに、独自のアプリケーションとゲームを作成できます。 あなたはそれを簡単かつ迅速に学びます。
- 投稿日:2020-07-24T13:05:11+09:00
【Java】Google Custom Search APIで画像を取得する
必要なもの
・APIキー
・検索エンジンID
どちらもGoogle Custom Search APIを有効化する過程で取得できます。参考サイト
APIキーと検索エンジンID取得はこちらの記事が参考になりました。
Custom Search APIを使ってGoogle検索結果を取得する画像検索についてはこちら。
Google Custom Search APIを使って画像収集ソースコード
検索結果から画像を10枚ダウンロードします。
保存先はダウンロードフォルダに指定しました。CustomSearch.javaimport java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; public class CustomSearch { public static void main(String[] args) throws IOException { String key = "APIキー"; String cx = "検索エンジン ID"; String qry = "検索したい文字"; String link = null; List<String> linkList = new ArrayList<String>(); //Google Custom Search APIにリクエストを投げる URL url = new URL( "https://www.googleapis.com/customsearch/v1?key=" + key + "&cx=" + cx + "&q=" + qry + "&searchType=image"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", "application/json"); BufferedReader br = new BufferedReader(new InputStreamReader( (conn.getInputStream()))); //レスポンスはjson //jsonからURLを探し出す処理 String output; System.out.println("Output from Server .... \n"); while ((output = br.readLine()) != null) { if (output.contains("\"link\": \"")) { link = output.substring(output.indexOf("\"link\": \"") + ("\"link\": \"").length(), output.indexOf("\",")); System.out.println(link); linkList.add(link); } } conn.disconnect(); //URLから画像をダウンロードする処理。 URL imageURL = null; HttpURLConnection urlConnection = null; for (int i = 0; i < 10; i++) { try { imageURL = new URL(linkList.get(i)); urlConnection = (HttpURLConnection) imageURL.openConnection(); // trueならリダイレクトを認める urlConnection.setInstanceFollowRedirects(true); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // InputStreamはバイト入力ストリームを表現するすべてのクラスのスーパー・クラスです。 InputStream in = null; try { in = urlConnection.getInputStream(); } catch (IOException e) { e.printStackTrace(); } byte[] buf = new byte[4096]; int readSize; int total = 0; try { FileOutputStream fos = new FileOutputStream("C:\\Users\\user\\Downloads\\" + "test" + i + ".jpg"); //readメソッドはストリームがファイルの終わりに達したために読み込むバイトがない場合は値-1が返されます。 // 最初に読み込まれたバイトは要素b[0]に格納され、次のバイトはb[1]に格納され、それ以降も同様に続きます。 //読み込まれるバイト数の上限はbの長さと同じです。 while (((readSize = in.read(buf)) != -1)) { total = total + readSize; //指定されたバイト配列のオフセット位置0から始まるreadSizeバイトをこのファイル出力ストリームに書き込みます。 fos.write(buf, 0, readSize); } fos.flush(); fos.close(); in.close(); } catch (FileNotFoundException e) { System.out.println("ファイルエラー"); } catch (IOException e) { e.printStackTrace(); } System.out.println("Size:" + total); } } }どんな仕組みなの
検索文字をリクエストとしてAPIに投げる。
jsonデータでレスポンスが返ってくる。
そのjsonデータから画像のURLを探す。
そしてURLに接続してダウンロード!
これだけ!
- 投稿日:2020-07-24T12:14:45+09:00
JavaのSerializableを実際にアプリケーションを動かしながら理解する
Serializableの意味わかりますか?
Java でWebアプリケーションを作ると、以下のような JavaBeans クラスを作ることがあると思います。
JavaBeans.javaimport java.io.Serializable; public class JavaBeans implements Serializable { private static final long serialVersionUID = 1L; private String aaa = null; public String getAaa() { return aaa; } public void setAaa(String aaa) { this.aaa = aaa; } }この
implements Serializable
とserialVersionUID = 1L;
の意味わかりますか?
「わかりません!」っていう人向けに書いた記事です。「わかるよ!」って人はこの記事を読む必要ないです。まずは概念から
Javaのインスタンスをバイト配列として出力することをSerialize(シリアライズ)と言い、その逆をDeserializeと言います。下の図がわかりやすいのですが、ファイルやメモリ、データベースなどにインスタンスを保存することをSerializeと言っています。
図はこの記事から抜粋なので
implements Serializable
と宣言するということは、「このインスタンスはdiskなどに保存することができます!」って宣言していることになります。ちょっと個人的に作ったWebアプリケーションがあるので、それで実験してみます。
TestServlet.javapublic class TestServlet extends HttpServlet { private static final long serialVersionUID = 1L; // ・・・ }上記
TestServlet
はHttpServlet
を extends しており、HttpServlet
はGenericServlet
を extends、GenericServlet
はSerializable
を implements しているので、TestServlet
はSerializable
です。このWebアプリケーションのサーバ(Tomcat)を起動します。デプロイ先のlocalhostフォルダ直下にはまだ何もありません。
この後、
TestServlet
が動くようにブラウザを開くか何かして、サーバーを停止します。すると、デプロイ先のlocalhostフォルダ直下にSESSIONS.ser
という謎のファイルが出来上がります。再度サーバーを起動します。すると、
SESSIONS.ser
は消えます。
SESSIONS.ser
はTomcatがセッション情報をSerializeしたものです。これのおかげで、サーバが再起動されてもセッション情報は失われず、再起動後にアクセスすることで再起動前の情報を復元(Deserialize)することができます。逆に言えば、この仕様のために、セッションに格納するオブジェクトはSerializableをimplementsしている必要があります。
一見便利そうなんですけど、これ、再起動前後でプログラムが変更された場合にどうなるかわかりますか?実際にSerialize/Deserializeさせてみる
下図のような構成のアプリケーションを作り、実際にSerialize/Deserializeさせてみます。
まずSerializeする方です。任意のフォルダに
SampleBean.java
と、それをSerializeするWriteObject.java
を作ります。WriteObject.javaはSampleBeanインスタンスを生成し、2つのプロパティに100を設定してそれをSerializeしています。WriteObjectを実行することでsample.ser
を作ります。sample.serはSampleBeanインスタンスをSerializeしたものです。SampleBean.javaimport java.io.Serializable; public class SampleBean implements Serializable { private static final long serialVersionUID = 1L; private double propertyD; private int propertyE; public double getPropertyD() { return propertyD; } public void setPropertyD(double propertyD) { this.propertyD = propertyD; } public int getPropertyE() { return propertyE; } public void setPropertyE(int propertyE) { this.propertyE = propertyE; } }WriteObject.javaimport java.io.FileOutputStream; import java.io.ObjectOutputStream; public class WriteObject { public static void main (String args[]) { SampleBean sample = new SampleBean(); sample.setPropertyD(100); sample.setPropertyE(100); try { FileOutputStream fout = new FileOutputStream("sample.ser"); ObjectOutputStream oos = new ObjectOutputStream(fout); oos.writeObject(sample); oos.close(); System.out.println("Done"); } catch(Exception ex) { ex.printStackTrace(); } } }次にDeserializeする方です。
上記で生成したsample.ser
を読み込むクラス(JspTest.java)を作ります。
JspTest.javaで使用しているSampleBeanクラスは、先ほどのものと中身は同じものを別のフォルダにコピーして使用しています。JspTest.javaimport java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.util.ArrayList; import java.util.List; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/JspTest") public class JspTest extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { SampleBean sample = null; List<SampleBean> list = new ArrayList<>(); try { FileInputStream fin = new FileInputStream("c:\\sample.ser"); ObjectInputStream ois = new ObjectInputStream(fin); sample = (SampleBean) ois.readObject(); ois.close(); list.add(sample); } catch(Exception ex) { ex.printStackTrace(); } request.setAttribute("samplelist", list); String path = "/WEB-INF/jsp/jsptest.jsp"; RequestDispatcher disp = request.getRequestDispatcher(path); disp.forward(request, response); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }それを表示するjsp
jsptest.jsp<%@ page language="java" contentType="text/html;charset=Windows-31J"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ page isELIgnored="false" %> <html><body> <table border="1"> <thead> <tr> <th>プロパティD</th> <th>プロパティE</th> </tr> </thead> <tbody> <c:forEach items="${samplelist}" var="item"> <tr> <td><c:out value="${item.propertyD}" /></td> <td><c:out value="${item.propertyE}" /></td> </tr> </c:forEach> <tbody> </table> </body></html>SerializeされたインスタンスをDeserializeしてそれをブラウザに表示することができました。
これがSerialize/Deserializeです。serialVersionUIDとは?
次は
serialVersionUID
です。クラスに implements Serializable と書き、serialVersionUID を宣言しなかった場合、The serializable class XXXX does not declare a static final serialVersionUID field of type long
というワーニングが出てきます。
serialVersionUID
とは、インスタンスのバージョンです。と言われてもあまりピンとこないと思うので、もっと具体的に理解するために、少し実験をしてみます。
先ほどSerializeした時に使用したSampleBean.javaと、それをDeserializeした時に使用したSampleBean.javaのserialVersionUIDを不一致にさせてみます。
JspTest.javaが使用するSampleBean.java(Deserializeする方)をserialVersionUID = 2L;
に変更します。SerializeされたSampleBean.javaはserialVersionUID = 1L;
のままです。
変更した後、ブラウザをF5で更新すると以下のようなエラーが出力されます。エラー内容java.io.InvalidClassException: SampleBean; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2 at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:715) at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1997) at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1866) at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2159) at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1685) at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:499) at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:457) at JspTest.doGet(JspTest.java:27) at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.base/java.lang.Thread.run(Thread.java:832)
InvalidClassException
というエラーが発生し、その原因はSampleBeanのserialVersionUID が不一致だということがわかります。
逆にserialVersionUID は同じ値だけど中身が違う場合はどうなるでしょうか?
Serializeする方のSampleBeanは変更せず、Deserializeする方だけ以下のように、getterの中で×100してみます(getPropertyE()の中)。SampleBean.java(Deserialize側のみ変更)import java.io.Serializable; public class SampleBean implements Serializable { private static final long serialVersionUID = 1L; private double propertyD; private int propertyE; public double getPropertyD() { return propertyD; } public void setPropertyD(double propertyD) { this.propertyD = propertyD; } public int getPropertyE() { return propertyE * 100; // getterの中で×100してみる } public void setPropertyE(int propertyE) { this.propertyE = propertyE; } }エラーは発生せず、×100した値が表示されていることがわかります。正常終了はしたものの、この挙動は本当にあなたが想定した挙動ですか?Serializeした時とDeserializeした時でプログラムの中身が変更されていた場合の挙動を考慮したことがありますか?
Deserializeで復元しているのは、インスタンス変数の値です。Serializeした時に、propertyDにはdouble型の100、propertyEにはint型の100を設定しているので、Deserializeではその値を復元しています。setter/getterの中で何をしているかなんて気にしていません。なので、例えばsetterの中に、引数の値が10以上の場合はエラーとするようなチェックを入れたとしても、それを無視した値が復元されます。
SampleBean.javaimport java.io.Serializable; public class SampleBean implements Serializable { private static final long serialVersionUID = 1L; private double propertyD; private int propertyE; public double getPropertyD() { return propertyD; } public void setPropertyD(double propertyD) { // 引数の値が10以上の場合はエラー if (propertyD > 10) { throw new IllegalArgumentException(); } this.propertyD = propertyD; } public int getPropertyE() { return propertyE; } public void setPropertyE(int propertyE) { this.propertyE = propertyE; } }setterで10以上ならエラーというチェックを入れたにも関わらず、特にエラーもなく表示できてしまいます。
この記事の最初の方で、セッション情報がSerialize/Deserializeされているという話をしました。Webサーバがセッション情報をSerializeするのは以下のような場合です。
- サーバを停止した時
- セッションを格納する領域(メモリ)が満杯になった時
Deserializeするのはこの逆のタイミングです。SerializeされてからDeserializeされるまでの間にプログラムが変更されていたとしても、その変更内容を無視してDeserializeされてしまいます。それが困るようであればserialVersionUIDを変更する必要があります。
Serialize/DeserializeのJavaの仕様は以下にまとめられています。
5.6 直列化に影響する型変更
特に意識してほしいのは「5.6.2 互換性のある変更」の方で、Javaの仕様として互換性がある(=Deserialize可能)だとしても、実際に使用しているそのシステムにおいて互換性があるかどうかはまた別の話です。Javaの仕様として互換性がある場合に、それは本当にDeserializeさせて大丈夫なのか?改めて画面を操作させないと変なことが起こるようなのであれば、serialVersionUIDを変更してエラーを発生させた方が安全な場合もあると思います。serialVersionUIDの変更方法
自分の手で変更することも可能ですが、IDEに生成させることも可能です。Eclipseであれば以下のような手順になります。
(IntelliJ IDEAを使用している方はこちら)
クラスからprivate static final long serialVersionUID = 1L;
をいったん消します。するとクラスでワーニングが出るのでそこにカーソルを合わせる(下図)。
private static final long serialVersionUID = 2779728212248637579L;
というserialVersionUIDが生成されました。この値はクラスの構造が変わると生成される値も変更されます。クラスの構造とは、インスタンス変数の追加や変数名の変更、メソッドの追加、・・・などです。transientでSerialize/Deserializeの対象外とする
インスタンス変数にtransient修飾子を付与することで、Serialize/Deserializeの対象外とすることができます。試しに以下のようにpropertyEにtransient修飾子を付与してみます。
SampleBean.javaimport java.io.Serializable; public class SampleBean implements Serializable { private static final long serialVersionUID = 4798282072280430232L; private double propertyD; private transient int propertyE; // transientを付与 public double getPropertyD() { return propertyD; } public void setPropertyD(double propertyD) { this.propertyD = propertyD; } public int getPropertyE() { return propertyE; } public void setPropertyE(int propertyE) { this.propertyE = propertyE; } }Serializeする際には100を設定します。
WriteObject.javaimport java.io.FileOutputStream; import java.io.ObjectOutputStream; public class WriteObject { public static void main (String args[]) { SampleBean sample = new SampleBean(); sample.setPropertyD(100); sample.setPropertyE(100); try { FileOutputStream fout = new FileOutputStream("sample.ser"); ObjectOutputStream oos = new ObjectOutputStream(fout); oos.writeObject(sample); oos.close(); System.out.println("Done"); } catch(Exception ex) { ex.printStackTrace(); } } }ここで生成したsample.serを読み込ませてブラウザ表示した結果が以下です。
propertyEにtransientを付けたことで、Serializeの際に設定した100という値がDeserializeした時には設定されていないことがわかります。0が表示されているのはintのデフォルト値が0だからです。
ちなみにtransientを付けるとIDEが生成するserialVersionUID の値も変わります。おわりに
ざっとこんな感じです。私もよくわかっていない部分がありますが、私の周囲でこのあたりのことをちゃんと理解した上で
implements Serializable
とかserialVersionUID = 1L
を書いている人があまりいないような気がしたので書いてみました。この記事を読んで少しでも多くの方が、Serializeの意味をちゃんと理解した上でimplements Serializable
とかserialVersionUID = 1L
と書いてくれようになってくれたらいいなと思います。以上。
参考
公式
Serialize:シリアライズ(直列化)とDeserialize:デシリアライズ
- 直列化(serialize:シリアライズ)
- Serialization and Deserialization in Java
- Serializable について
- Javaのシリアライズ(直列化)メモ
- 難解なSerializableという仕様について俺が知っていること、というか俺の理解
- Javaとシリアライズと互換性
Serializeとセッション
- 3.1.14 セッションリカバリ機能を利用する場合のWebアプリケーション作成方法
- セッションに保存されるオブジェクトがシリアライズ可能でない
- sessionに登録するObjectはSerializableしなければならない
- [JAVA] フェイルオーバー時や再起動時に NullPointerException や Cannot get property ‘xxxxxx’ on null object
serialVersionUID
Tomcatのセッション管理
- 投稿日:2020-07-24T07:54:06+09:00
javaメゾットを作る 【メモ】【java11】
classとmainメゾット
class
classの書き方。
setumeiclass.javapublic class setumeiclass/*⇐ファイル名*/{ //この中にmainメゾットを書く。 }mainメゾット
これがないと始まらない。
この中にコードを書く。(基本的には。)Test.javapublic class Test{ public static void main(String args[]){ /*Write the code here!!*/ } }例
rei.javapublic class rei{ public static void main(String args[]){ System.out.println("Hello,World!!"); } }実行結果:
Hello,World!!自作メゾット
zisakupublic class zisaku{ public static 型名 メゾット名(引数){ このメゾットが実行するコード } public static void main(String args[]){ ここで自作メゾットを使用する } }myclass.javapublic class myclass{ public static void main(String args[]){ myclass(1,2); } public static void myclass(int a,int b){ System.out.println(a+b); } }実行結果:
3
myclass
の引数a
とb
の値が1と2なのでa
+b
(1+2)で3。
- 投稿日:2020-07-24T07:54:06+09:00
javaメソッドを作る 【メモ】【java11】
classとmainメソッド
class
classの書き方。
setumeiclass.javapublic class setumeiclass/*⇐ファイル名*/{ //この中にmainメソッドを書く。 }mainメソッド
これがないと始まらない。
この中にコードを書く。(基本的には。)Test.javapublic class Test{ public static void main(String args[]){ /*Write the code here!!*/ } }例
rei.javapublic class rei{ public static void main(String args[]){ System.out.println("Hello,World!!"); } }実行結果:
Hello,World!!自作メソッド
zisakupublic class zisaku{ public static 型名 メソッド(引数){ このメソッドが実行するコード } public static void main(String args[]){ ここで自作メソッドを使用する } }myclass.javapublic class myclass{ public static void main(String args[]){ myclass(1,2); } public static void myclass(int a,int b){ System.out.println(a+b); } }実行結果:
3
myclass
の引数a
とb
の値が1と2なのでa
+b
(1+2)で3。
- 投稿日:2020-07-24T07:54:06+09:00
javaメソットを作る 【メモ】【java11】
classとmainメソット
class
classの書き方。
setumeiclass.javapublic class setumeiclass/*⇐ファイル名*/{ //この中にmainメソットを書く。 }mainメソット
これがないと始まらない。
この中にコードを書く。(基本的には。)Test.javapublic class Test{ public static void main(String args[]){ /*Write the code here!!*/ } }例
rei.javapublic class rei{ public static void main(String args[]){ System.out.println("Hello,World!!"); } }実行結果:
Hello,World!!自作メソット
zisakupublic class zisaku{ public static 型名 メソット名(引数){ このメソットが実行するコード } public static void main(String args[]){ ここで自作メソットを使用する } }myclass.javapublic class myclass{ public static void main(String args[]){ myclass(1,2); } public static void myclass(int a,int b){ System.out.println(a+b); } }実行結果:
3
myclass
の引数a
とb
の値が1と2なのでa
+b
(1+2)で3。
- 投稿日:2020-07-24T03:30:07+09:00
javaでアルゴリズム入門 - 累積和
記事の概要
自分の勉強兼メモでまとめている記事シリーズです。第五弾。
今までの記事はこちら。
# 記事 4 javaでアルゴリズム入門 - 探索編(bit全探索) 3 javaでアルゴリズム入門 - 探索編(幅優先探索) 2 javaでアルゴリズム入門 - 探索編(深さ優先探索) 1 javaでアルゴリズム入門 - 探索編(全探索、二分探索) 今回の記事では
- 累積和
について勉強します。
探索編はいったん終了し、別のアルゴリズム(毎週のコンテストで気になったものを)を勉強していきましょう。累積和
計算のテクニック的なものらしいです。
配列のある区間の総和を求めるのに便利だそう。一応説明してみますね。
配列aを、{1,3,1,4,7,6,1,0,9}
だとしましょうか。
例えばこの配列の3番目から5番目までの総和を求めなさい、と言われたときを考えます。通常の求め方
a:{1,3,1,4,7,6,1,0,9} の3~5番目を単純に足し算する。
答えは、1+4+7 = 12累積和を使用
aの他に、aのi番目までの和を保存しておく配列bを作成する。
b:{0,1,4,5,9,16,22,23,23,32}
a_1をaの1個目の要素みたいな書き方をすると、
b_1 = 0(ここは固定。以降添字は一つずれる)
b_2 = a_1 = 1
b_3 = a_1 + a_2 = 1 + 3 = 4
b_4 = a_1 + a_2 + a_3 = 1 + 3 + 1 = 5
b_5 = a_1 + ... + a_4 = 1 + ... + 4 = 9
b_6 = a_1 + ... + a_5 = 1 + ... + 7 = 16
...と言った感じです。
このとき、aの3~5番目の総和は次のように求められます。
b_6 - b_3 = 16 - 4 = 12
詳細は以下のとおりです。
b_6 = a_1 + a_2 + a_3 + a_4 + a_5 -)b_3 = a_1 + a_2 -------------------------------------- = a_3 + a_4 + a_5なんとなくわかりますかね?
前から順番に総和を保存しておくことで、わざわざ3番目 + 4番目 + 5番目、みたいな足算をしなくてもよくなりました!何が嬉しいのか?
例えば●番目から▲番目までの総和を何度も(●や▲のバリエーションがたくさんある問題を想定)求めなければならないとき、通常の求め方だと、何度も何度もloopを回す必要がありますが、累積和を用いると一発で(O(1)で)総和が求まります。
では問題をみてみましょう。
例題
問題文・入力例などはここをクリックして表示
※できるだけ問題リンクを参照してください
(セクション開始)
【問題文】
長さ N の数列 {ai} と1 以上 N 以下の整数 K が与えられます。 この数列には長さ K の連続する部分列が N−K+1 個あります。これらのそれぞれ部分列に含まれる値の合計の総和を求めてください。【制約】
入力はすべて整数
1≤K≤N≤10^5
0≤ai≤10^8【入力】
入力は以下の形式で標準入力から与えられる。N K a1 .. aN【出力】
部分列に含まれる値の合計 N−K+1 個の総和を出力せよ。(セクション終了)
こんな問題です。
まさに累積和の問題ですね。
K個の足し算をN−K+1回やらなくてはなりません。計算量はO(K * (N-K+1))です。さらに、選び方が悪いと(K = N/2 など)、O(N^2)かかってしまうことがわかります。これでは間に合いません。ということで累積和の出番です。
「K個の足し算をN-K+1回」ですが、累積和を用いれば「K個の足し算」はすべてO(1)で可能となりますので、計算量は大体O(N)で済みます。方針は以下のとおりです。
- 累積和を保持しておく配列を準備する
- 1~K,2~K+1,3~K+2,...,N-K+1~N のそれぞれの総和を累積和の配列によって求め、最後にその総和を答えとする。
では、以下回答例です。
Main.javaimport java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int k = sc.nextInt(); long a[] = new long[n]; // 入力を受け取る配列 long b[] = new long[n + 1]; // 累積和を格納 for (int i = 0; i < n; i++) { // 入力受け取り a[i] = sc.nextLong(); } for (int i = 1; i <= n; i++) { // 累積和計算 b[i] = b[i - 1] + a[i - 1]; } long ans = 0; // 答え格納 for (int i = 0; i < n - k + 1; i++) { ans += b[i + k] - b[i]; } System.out.println(ans); } }・・・う〜ん、添字のところ難しかった。。
特に累積和の一番最初は0にしないといけないのむずいな・・・。ちょっと練習あるのみですかね。概念とか考え方自体は知れたのでよかったです。
とりあえずここまでにします。それでは〜。
- 投稿日:2020-07-24T01:39:55+09:00
[Processing×Java] 配列の使いかた
この記事はプログラムの構造をProcessingを通じて理解していくための記事です。
今回は配列について書いていきます。目次
0.配列とは
1.簡単な配列のつくりかた
2.クラスをつかった配列をつくりかた
3.要素が多い配列のつくりかた
4.配列をつかった例0.配列とは
0-0. 配列は簡単にいうと
データを一列に並べたものです。
並べ方(順番)には意味があります。
よく目にする、座標(3,10)のようなものも配列の一種です。0-1. なぜ配列を使うのか
主な理由は、
大量のデータをまとめてあつかうことができるから。
です。0-2. 配列のイメージ
順位 チーム 1 巨人 2 DeNA 3 広島 4 阪神 5 中日 6 ヤクルト この表はプロ野球セリーグの順位表です。
順番と要素(チーム)が結びついて一列に並んでいます。
こんなイメージです。1.簡単な配列のつくりかた
①宣言
②初期設定
③使用
という手順で、配列をつくって使っていきます。簡単な例で見てみましょう。
string.java//配列の名前と長さの宣言(名前はnumbersで長さは7) int[] numbers = new int[5]; //配列の要素の定義 numbers[0] = 2; numbers[1] = 4; numbers[2] = 6; numbers[3] = 8; numbers[4] = 10; //配列の使用 int a = numbers[0] + numbers[4]; println(a);12このプログラムは、配列の要素(中身)を足し算して、その結果を表示するプログラムです。
手順ごとに詳しくみてみます。①宣言
//配列の名前と長さの宣言(名前はnumbersで長さは7) int[] numbers = new int[5];Point:宣言の方法
データ型 [ ] 配列の名前 = new データ型 [ 配列の長さ ]今回の例で説明すると
「今から配列をつくるんだけれども、その配列の中のデータは整数だよ。
配列の名前はnumbersで、配列の中のデータの個数は5個にするよ!」
って感じです。②初期設定
//配列の要素の定義 numbers[0] = 2; numbers[1] = 4; numbers[2] = 6; numbers[3] = 8; numbers[4] = 10;配列に中身を入れます。
今回は、配列の要素の中身を個別に決める方法です。
※要素の個数が多いと大変です。③使用
//配列の使用 int a = numbers[0] + numbers[4]; println(a);配列の要素は、配列の名前[ インデックス ]という形で指定します。
インデックスは配列の要素の順番です。
インデックスは、0,1,2...というふうに0からはじまります。2.クラスをつかった配列をつくりかた
下のプログラムは、跳ねまわるボールを2つ描くプログラムです。
今回は、このプログラムを改造していきたいと思います。//オブジェクトの宣言 Ball b1; Ball b2; void setup(){ size(600,400); //オブジェクトを定義する b1 = new Ball(50,50,7,5); b2 = new Ball(400,50,-7,-5); } void draw(){ background(255); //オブジェクトb1をつかう b1.display(); b1.move(); b1.edges(); //オブジェクトb2をつかう b2.display(); b2.move(); b2.edges(); } //クラスを定義する class Ball{ //使う変数(フィールド)を宣言する int x; int y; float xspeed; float yspeed; //Ballクラスのコンストラクタ Ball(int xpos,int ypos,float xvelocity,float yvelocity){ x = xpos; y = ypos; xspeed = xvelocity; yspeed = yvelocity; } //ボールを表示する関数(メソッド) void display(){ fill(234,159,217); noStroke(); ellipse(x,y,30,30); } //ボールを動かす関数(メソッド) void move(){ x += xspeed; y += yspeed; } //ボールを跳ね返らせる関数(メソッド) void edges(){ if(x > width-15 || x < 15){ xspeed *= -1; } if(y > height-15 || y < 15){ yspeed *= -1; } } }2-0.クラスから配列をつくる
Ballクラスから、配列ballsをつくります。
配列の要素(中身)は2個です。//配列(の名前と長さ)の宣言 Ball[] balls = new Ball[2]; void setup(){ size(600,400); //配列の要素を定義する balls[0] = new Ball(50,50,7,5); balls[1] = new Ball(400,50,-7,-5); } void draw(){ background(255); //配列ballsの1つ目の要素を扱う balls[0].display(); balls[0].move(); balls[0].edges(); ////配列ballsの2つ目の要素を扱う balls[1].display(); balls[1].move(); balls[1].edges(); } //クラスを定義する class Ball{ //使う変数(フィールド)を宣言する int x; int y; float xspeed; float yspeed; //Ballクラスのコンストラクタ Ball(int xpos,int ypos,float xvelocity,float yvelocity){ x = xpos; y = ypos; xspeed = xvelocity; yspeed = yvelocity; } //ボールを表示する関数(メソッド) void display(){ fill(234,159,217); noStroke(); ellipse(x,y,30,30); } //ボールを動かす関数(メソッド) void move(){ x += xspeed; y += yspeed; } //ボールを跳ね返らせる関数(メソッド) void edges(){ if(x > width-15 || x < 15){ xspeed *= -1; } if(y > height-15 || y < 15){ yspeed *= -1; } } }クラスから配列をつくる方法を詳しく見ていきます。
配列をつくる流れは、変わりません。
つまり
①宣言
②初期設定
③使用
という流れです。①宣言
//配列の名前と長さの宣言(名前はballsで長さは2) Ball[] balls = new Ball[2];Point:宣言の方法
クラス [ ] 配列の名前 = new クラス [ 配列の長さ ]今回の例で説明すると
「今から配列をつくるんだけれども、その配列はBallクラスをもとにしてつくるよ。
配列の名前はballsで、配列の中のデータの個数は2個にするよ!」
って感じです。②初期設定
//配列の要素を定義する balls[0] = new Ball(50,50,7,5); balls[1] = new Ball(400,50,-7,-5);配列の要素を定義します。
今回は、配列の要素の中身を個別に決める方法です。
※要素の個数が多いと大変です。。③使用
//配列ballsの1つ目の要素を扱う balls[0].display(); balls[0].move(); balls[0].edges(); ////配列ballsの2つ目の要素を扱う balls[1].display(); balls[1].move(); balls[1].edges();配列の要素は、配列の名前[ インデックス ]という形で指定します。
ところで配列の要素はオブジェクトです。
オブジェクトはクラスのフィールド(変数)やメソッド(関数)を持っています。
オブジェクトが、自身のもつフィールドやメソッドにアクセスするためには、ドット(.)を使います。
今回は、オブジェクト.メソッド();というかたちで使っています。3.要素が多い配列のつくりかた
つぎは上の配列のプログラムを改造していきたいと思います。
//配列(の名前と長さ)の宣言 Ball[] balls = new Ball[20]; void setup(){ size(600,400); //配列の要素を定義する //forループでシンプルな形でまとめます。 for(int i = 0;i < balls.length;i++){ balls[i] = new Ball(random(width),random(height),random(-i,i),random(-i,i)); } } void draw(){ background(255); //配列を使う //forループでシンプルな形でまとめます。 for(int i = 0;i < balls.length;i++){ balls[i].display(); balls[i].move(); balls[i].edges(); } } //クラスを定義する class Ball{ //使う変数(フィールド)を宣言する int x; int y; float xspeed; float yspeed; //Ballクラスのコンストラクタ Ball(int xpos,int ypos,float xvelocity,float yvelocity){ x = xpos; y = ypos; xspeed = xvelocity; yspeed = yvelocity; } //ボールを表示する関数(メソッド) void display(){ fill(234,159,217); noStroke(); ellipse(x,y,30,30); } //ボールを動かす関数(メソッド) void move(){ x += xspeed; y += yspeed; } //ボールを跳ね返らせる関数(メソッド) void edges(){ if(x > width-15 || x < 15){ xspeed *= -1; } if(y > height-15 || y < 15){ yspeed *= -1; } } }クラスから要素が多い配列をつくる方法を詳しく見ていきます。
配列をつくる流れは、変わりません。
つまり
①宣言
②初期設定
③使用
という流れです。①宣言
//配列の名前と長さの宣言(名前はballsで長さは20) Ball[] balls = new Ball[20];Point:宣言の方法
クラス [ ] 配列の名前 = new クラス [ 配列の長さ ]②初期設定
//配列の要素を定義する //forループでシンプルな形でまとめます。 for(int i = 0;i < balls.length;i++){ balls[i] = new Ball(random(width),random(height),random(-i,i),random(-i,i)); }個別に要素を定義するのではなくを、ループをつかって効率よく定義していきます。
③使用
//配列を使う //forループでシンプルな形でまとめます。 for(int i = 0;i < balls.length;i++){ balls[i].display(); balls[i].move(); balls[i].edges(); }配列の要素は、配列の名前[ インデックス ]という形で指定します。
個別に要素を使うのではなく、ループをつかって効率よく使っていきます。ところで配列の要素はオブジェクトです。
オブジェクトはクラスのフィールド(変数)やメソッド(関数)を持っています。
オブジェクトが、自身のもつフィールドやメソッドにアクセスするためには、ドット(.)を使います。
今回は、オブジェクト.メソッド();というかたちで使っています。4.配列をつかった例
マウスをクリックするたびに、ボールが増えていくプログラムです。
//配列の宣言 Ball[] balls = new Ball[20]; //ボールの総数を変化させたいのでそのために変数をつくる int total = 0; void setup(){ size(600,400); //配列の要素を定義する //forループでシンプルな形でまとめます for(int i = 0;i < balls.length;i++){ balls[i] = new Ball(random(width),random(height),random(-i,i),random(-i,i)); } } //マウスが押されるたびにに、ボールの数が増えていきます void mousePressed(){ total += 1; } void draw(){ background(255); //配列を使う //forループでシンプルな形でまとめます //totalの数のボールが描かれます for(int i = 0;i < total;i++){ balls[i].display(); balls[i].move(); balls[i].edges(); } } //クラスを定義する class Ball{ //使う変数(フィールド)を宣言する int x; int y; float xspeed; float yspeed; //Ballクラスのコンストラクタ Ball(int xpos,int ypos,float xvelocity,float yvelocity){ x = xpos; y = ypos; xspeed = xvelocity; yspeed = yvelocity; } //ボールを表示する関数(メソッド) void display(){ fill(234,159,217); noStroke(); ellipse(x,y,30,30); } //ボールを動かす関数(メソッド) void move(){ x += xspeed; y += yspeed; } //ボールを跳ね返らせる関数(メソッド) void edges(){ if(x > width-15 || x < 15){ xspeed *= -1; } if(y > height-15 || y < 15){ yspeed *= -1; } } }最後に
読んでいただきありがとうございました。
より良い記事にしていくために御意見、ご指摘のほどよろしくお願いいたします。※この記事は、ダニエルシェフマン氏のyoutube"Coding Train"を参考にしています。
- 投稿日:2020-07-24T00:15:10+09:00
【初学者向け】技術とキャリアに関して参考になったものを並べてみた。
概要・経緯
後輩がこれからエンジニアとしてのキャリアをスタートさせるとのことで、自分がこれまで大変お世話になってきたコンテンツ・やってきたことなんかを記事にまとめておこうと思い執筆。
また、技術の養成と同時にキャリアについて考えるのも重要だと思ったので、今回は技術(業務)・キャリアの両方に焦点を当てながらまとめていく。上から目線で恐縮ではあるが、どのコンテンツも非常に良質というか、説明が丁寧で分かりやすいことこの上ない。前提知識があまりない状態でもストレスなく理解していけるのが何より素晴らしいので、是非参考にしてもらえたらと思う。ちなみに私がつくったコンテンツは特に含めておらず、特に宣伝的な意はないのでご安心を。
※内容は2020年7月時点のものです。技術/業務編
主に用語を押さえる目的で利用。抽象的な解説がほぼ見当たらず、納得感を持って用語を理解できる。
特に1年目とかで開発現場入りたての時期は、技術的に期待されることはあまりないため振られるタスク自体は重くないのだが、如何せん飛び交う用語が一々分からなくて手が止まりやすい。
「DNS」や「テーブル」といった日常的に使われる用語もひとつずつ押さえていけばなんてことないのだが、知的負荷が高い環境下での作業だと固有名詞ひとつとってもバカにはできない(意味が分からないとその分無駄な質問や調査に時間を取られ、先に進めなくてコストがかかる)。すぐ調べる習慣を身に付ける上でも是非利用してほしい。
筆者(私)は入社当時、社内研修にてJavaが採用されていたのだが、まー意味が分からなかった。
特に理解に苦しみやすいとされる「オブジェクト指向」では自分も例に漏れず詰んでいたし、というかその前段階の「メソッド」「クラス」あたりから思考停止しかけていた。「関数だよ。鯛焼きの型みたいなもんだよ。」みたく図示しながら説明されていたらあんなにツラい思いはしなかったであろう。
そういう、一回説明を受けたときに生じるような疑問をきれいに解消してくれるのがこのサイト。今より結構前に書かれている記事が多いが、考え方としては今後も相違なく使っていけるものであるし、実際Javaに関してかなりイメージがしやすくなると思う。何かしらのコンテンツで学習しながら並行して使うと◎。こういう丁寧で分かりやすいサイトがあるよという意を込め紹介。
Qiita記事。新卒入りたての頃、全項目を印刷していつでも見れるよう鞄に忍ばせていた。お陰で現場での立ち回りや質問の仕方、今後考えるべき指針などに関して迷いを減らしてくれたように感じる。かなり情報量が詰まっているので、一気見するよりこまめに見て確認するのがいいかも。読み物的に流してもいいけれど、あんまり先輩や現場が教えてくれないようなエッセンスが1本に凝縮されているので本当にオススメ。ぶっちゃけ最初の最初はプログラミングとかのハードスキルよりも対人のソフトスキルを訓練した方がいいような気さえするからなおさら。伝え方は本当に重要っす、、、()
所謂「競技プログラミング」と呼ばれるもので、問題が与えられ規定時間内にプログラムを作成。解決に要した時間や完成度の高さなんかを競うオンラインコンテスト。
結構難易度が高かったり実務には直結しなかったりなど、賛否両論あるにはあるが、一つの問題に対して色々な人の解答を参考にできるため刺激にはなる。漫然とGitHubのソースを眺めたりするよりは実用的というか身になる活動になると思われる。
以下、動画コンテンツをいくつか。
こちらではプログラムを書くにあたって必要な考え方、手の動かし方を丁寧に解説してくれている。とりあえず基本的な文法とかは学習したけど、「書いて」と言われると何から手を付けていいのか分からない。総じてプログラムを考えて書けない。この初心者特有の書けないスパイラルを見事に解決してくれる良コンテンツ(と感じるほど筆者には有益な情報だった)。有料・無料関係なく、この考え方を実演込みで教えてくれるところはあまりないと思うので、是非参考にしてほしい。個人的にはお金を払ってでも教えてもらう価値ある動画だったと思う。
まっつんぱっつん氏による言語習得プロセス解説動画。ご自身ではこれまでスキル習得に相当時間を要してきたと別動画でも語られており、なかなか身に付いてる実感がないという人が次に何をするべきか丁寧に明示してくれている。新しく言語を学んでみようとするときはある程度このプロセスに従ってみると習得が早まるかもしれない。彼はフロントエンド(主に画面側)専門だそうだが、サーバーサイド(主に内部処理側)を専門にしたい人も大いに参考になると思う。
Web系・業務系に関係なく知っていて損しない情報であると思う。「エンジニアたる者、何か革新的コードとか書いて頑張らないといけないのでは」と身構えていた当時の自分にも教えてあげたい本動画(動画内に出てくる「ITエンジニアの仕事は99.9%既存の誰かの仕事の組み合わせ」というのがまさにそれ)。
前の会社の上司に「こういうのはそこまで覚える必要はない」と言われたが、個人的には知っていて損するものでもないと思うし、仮にコーディング規約等が決められていても自分の中で工夫の余地を持てるかどうかで色々選択肢が増えるからいいじゃないか、と考えている。
有益な情報であることには変わりなかろう、ということで紹介することにした。あと、業務においても結構大事なことを言っているので参考にしてほしい。
株式会社div代表取締役の真子社長が運営するチャンネルの動画。啓発系やライフハック的コンテンツが多いが、個人的に一番ためになったのはこの動画。Webがどういう仕組みで動いているのかを短時間で体系的に知れるという意味で非常に有益。Excelしか使いませんみたいな人ならともかく、Webに1ミリでも関わる可能性があるなら必須の教養なので是非とも押さえておいてほしい。
真子社長といえばこっちの動画も業務の上では重宝するから知っておくと色々楽。時短テクはなんぼ知っていてもいい。
こちらはYouTubeチャンネル。業務未経験の方や業界をあまり知らない方向けにも非常にオススメしたい。
技術・業務風景・キャリアに関してバランスよく取り上げてくれており、「そうそうそれが知りたかった」みたいな痒い所に手が届く情報満載で見ていて勉強になるし楽しい。キャリア初期にはもちろん、この動画のようにデスク環境なんかも紹介してくれていたりして参考になる(家でも長時間の作業をする際にはなるべく快適な開発環境をつくるべきだと自戒を込めて書いておく())。
動画学習プラットフォーム。エンジニアが学習するにはうってつけのサイトで、各コース金額設定が高めに設定されてあるが、しょっちゅうセールで¥1,500とかになってるから安い時を狙って買うべし。また、購入後も30日間の返金保証もあるので、合わないと思ったら返金手続きも可能。ちなみに無料コースでもかなりクオリティが高いコースもあるので、気になったら積極的に視聴するべし。また、日本語に限らず英語のコースが非常に秀逸なのでそちらも是非。
キャリア編
界隈では言わずと知れた重鎮、勝又健太氏の動画。基本的にはキャリアに関する情報発信が中心だが、あえて情報収集の手段について触れてあるこちらの動画をとりあげた。技術に関することで最新のトレンドを追う場合は、この動画に紹介されているような手法を試すと吉。また、筆者個人の意見としては必ずしも最新のトレンドを追う必要はないと思っているものの、あまり情報感度が高くないというのは技術者としてあんまりイケてないという印象もあるので、こちらも知っておいて損はないというスタンス。
ちなみにこの方のチャンネルはがっつり専門的なところに触れているので、あまり知識がないうちに見ると「???」となる動画も多々ある。勉強する傍ら参考にみるとちょうどいいかも。
スマホアプリ開発黎明期からエンジニア活動を続けているあんまさ氏による発信動画。ご本人は既に個人開発で生じた収益のみで不労所得が得れる状況下にあるそうだが、そのような立ち場も踏まえて個人開発やフリーランス活動に対し、比較的中立かつ現実的な見解を示してくれている。個人的にはバイアスが少ないと感じる発信が特に好きなので、今回取り上げた次第である。
こちらはiOS開発の発信をはじめ、YouTubeで活動しているKBOY氏の動画。本動画では質問する際の注意点や、質問された側の心境等も合わせて解説をしてくれている。そしておそらくほとんどの現場でこうなんだろうなと筆者も感じるため、参考までに。
また、彼のチャンネルでは会社員時代の小話がおもしろく、そういう世界があるのかと思いながら見ることができるため息抜きとしても楽しめると思う(これは完全に個人の感想)。
現役でIT企業の人事を務める毛呂淳一朗氏による発信動画。どうしてもエンジニアからの情報となると市場の状況や技術以外のキャリア情報に関して偏りが出がちになってしまうが、この方の場合は人事の観点から最新の市場の動向なんかも発信してくれているため大変参考になる。本動画で取り上げている話題に関しても、
筆者がエンジニアであるというバイアスが当然あるが個人的には大いに賛成である。
一部インフルエンサーに噛みついていることを批判されていたりするが、総じて有益な情報を発信してくれていると思う。とはいえ、自分の目と耳で市場の動向を確認することも怠らず参考にしてほしいと思う次第である。
ともすた合同会社代表、株式会社エイチツーオー・スペース代表取締役のたにぐちまこと氏による発信動画。
業界が隆盛して以来騒がれている「エンジニア35歳定年説」に対する見解を示してくれている。どの発信者も一度は話題にする内容であるが、ご本人の経験談も交えながら分かりやすく解説されているのが好印象だったためここで紹介することにした。
また、先に取り上げた動画学習プラットフォームのUdemyでも講師を務めており、大変評価が高いことで有名。筆者もPHP学習においては大変お世話になった。質問に対する応対も大変丁寧で早いので、彼のコースを受講するのは大いにオススメする。
外資系IT企業で営業を務め業界トップの成績を残したことで知られる宇都宮隆二氏によるWebエンジニアに対するキャリア解説。ご本人もObjective-CやPHP等の開発スキルがあり、ITやその他あらゆる業界に精通する経歴や知見を持つ。
総じてプログラマーやテスター等の下流工程よりも要件定義や基本設計といった上流工程に身を置くべきだと主張されている。これまでのエンジニアたちとは意見を違える内容も多いが、将来的に選択肢を持ってキャリアを見つめるという意味ではこのような見解も視野に入れるべきであろう。なにより事実ベースかつ経験に裏打ちされた確かな発信内容であるため、発信の信頼度はこの上なく高い。と思っている。
ちなみに本動画で我々駆け出しエンジニアに有益な情報は動画の14:33~あたりからなので、そのあたりから重点的に見てほしい。
まとめ
まだまだ紹介したいコンテンツは山のようにあるが、今回は筆者が特に参考にしていた(一部現在も参考にしている)情報をまとめてみた。
これから業務に携わる方は特に、今後のエンジニア人生を幸せに働いていくため、今の自分には何が不足していてこれから何が必要なのかを、技術面とキャリア面の双方で考える視野の広さを大切にしてほしいと思う。これはおそらく意識しているかしていないか、そのような機会に触れるのが早いか遅いかの違いでしかないと感じている。どうせなら早いうちに色々なものを吸収して考えて力をつけてステップアップしていった方が企業にとってもあなたにとっても有意義であると感じる。
大そうな解説と所感でつづってきたが、私自身もどんどん調べて試して失敗してという渦中にいる。
どんどん前に出て挑戦する技術者が出る、そんなエネルギーに満ちた界隈があってもいいと思っている。
これが我が後輩に幾ばくかでも伝わってほしいと願うばかりである。