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

【実践‼】Spring Boot で Hello World を表示する

1.事前知識

事前知識として、上記リンクの内容が必要です。

2.事前準備

01.png
1. Eclipseを起動する。
02.png
2. [ウィンドウ(W)]→[パースペクティブを開く]→[その他] を選択する。
03.png
3. JavaEE を選択して、 開く ボタンをクリックする。
04.png
4. [ヘルプ(H)]→[Eclipse マーケットプレース(M)...] を選択する。   
05.png
5. Spring Tools 4 (aka Spring Tool Suite 4) 4.7.0.RELEASEインストール する。

3.Spring Boot プロジェクトの作成

フォルダ構成
Hello
└─ src
     └─ main
          ├─ java
          │   └─ com
          │        └─ example
          │             └─ demo
          │                  ├─ HeloController.java
          │                  └─ HelloApplication.java
          └─ resources
               ├─ application.properties
               │  
               ├─ static
               └─ templates
                    └─ index.html
HeloController.java
package com.example.demo;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HeloController {
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String index(Model model) {
        model.addAttribute("message", "Hello Springboot");
        return "index";
    }
}
index.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
  <head>
    <title>Hello</title>
    <meta charset="utf-8" />
  </head>
  <body>
    <h1>Hello World</h1>
    <p>
      <span th:text="${message}"></span>!!!
    </p>
  </body>
</html>

06.png
1. [ファイル(F)]→[新規(N)]→[Spring スターター・プロジェクト] を選択する。
07.png
2. 名前に Hello と入力し、 次へ(N) > ボタンをクリックする。
08.png
3. [テンプレート・エンジン]→[Tymeleaf][Web]→[Spring Web] を選択し、完了 ボタンをクリックする。   
09.png
4. com.example.demo を右クリックして [新規(N)]→[クラス] 選択する。
10.png
5. 名前に HelloController と入力し、 完了(F) ボタンをクリックする。
11.png
6. templates を右クリックして [新規(N)]→[その他] 選択する。
12.png
7. [Web]→[HTML ファイル] を選択し、次へ ボタンをクリックする。 
13.png
8. ファイル名(M)index.html と入力し、 完了(F) ボタンをクリックする。 
14.png
9. Hello [boot] を右クリックして [実行(R)]→[5 Maven install] 選択する。
HelloController.javaindex.html上記に記載
15.png

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  4.125 s
[INFO] Finished at: 2020-07-05T21:50:22+09:00
[INFO] ------------------------------------------------------------------------

10.コンソール に 上記の文が表示されれば成功。 
16.png
11.Hello [boot] を右クリックして [実行(R)]→[9 Spring Boot アプリケーション] 選択する。
17.png
12. localhost:8080 にアクセスし、画像のように表示されれば成功。

4.関連

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

Java Gold対策:ローカライズ

はじめに

本稿は、Java Goldの学習のまとめとして以下項目についてまとめる。
(同じくJava Goldを学習中やローカライズについて知りたい方の参考になればと。)

  • ロケール
  • リソースバンドル

ロケールとは

java.util.Locale

言語や国ごとに異なるものの表記規則を示す。
# 例:単位・記号・日付・通貨など

アプリケーションを使用した国毎に、表示を変えたい場合などにロケールを利用する。
(アプリを使用する国によって文字を日本語・英語と分けたり等)

インスタンス生成方法は主に以下3種類ある。

  • new
  • Localクラスの定数
  • getDefault()メソッド

■ 主なコンストラクタ・メソッド

コンストラクタ 内容
Local(String language, String country) 引数の言語/国コードからオブジェクトを生成
// newによるインスタンス生成
// ja・en:言語名コード | JP・US:国名コード
Locale localeJp = new Locale("ja","JP");  // 日本のロケール
Locale localeUs = new Locale("en","US");  // アメリカのロケール

// Localeクラスの定数によるインスタンス生成
Locale localeJp = Locale.JAPAN;
Locale localeUs = Locale.US;

// getDefault()メソッドによるインスタンス生成
// 日本で実行すると日本のLocaleインスタンスが生成される
Locale localeJp = Locale.getDefault();
メソッド 内容
static getDefault() デフォルトロケールの現在の値を取得
final String getDisplayCountry() ロケールの国名を返却
final String getDisplayLanguage() ロケールの言語名を返却
String getCountry() ロケールの国名コードを返却
String getLanguage() ロケールの言語名コードを返却
// ja_JP が格納される(OSが日本の場合)
Locale locale = Locale.getDefault();

// 日本 が格納される
String country = locale.getDisplayCountry();

// 日本語 が格納される
String language = locale.getDisplayLanguage();

// JP が格納される
String country_code = locale.getCountry();

// ja が格納される
String language_code = locale.getLanguage();

// 備考:他のインスタンス生成方法
Locale locale = new Locale.Builder().setLanguage("ja")
                                    .setScript("Jpan")
                                    .setRegion("JP")
                                    .build();
// setScript():引数はISO 15924 alpha-4スクリプトコード(Javadoc参照)
// build():Locale.Builderのメソッドで、Localオブジェクトを生成

リソースバンドル

java.util.ResourceBundle

言語や国ごとに異なる表記規則をロケールに応じて対応させる。
これによりロケールに応じて日本表記や英語表記を切り替えることが出来る。

主に使用するメソッドは以下の通り。

メソッド 内容
boolean containsKey(String key) 引数で指定されたキーがリソースにある場合はtrueを返却
final Object getObject 引数で指定されたキーに紐づくオブジェトを返却
final String getString(String key) 引数で指定されたキーに紐づく文字列を返却
final String[] getStringArray(String key) 引数で指定されたキーに紐づく文字列の配列を返却
Set keySet() バンドルに含まれるすべてのキーを返却

また、サブクラスとして以下がある。

サブクラス 内容
ListResourceBundle ロケール用のリソースを便利かつ使いやすいリストで管理
PropertyResourceBuilder プロパティファイルからの一連のstatic文字列を使用してロケール用のリソースを管理

■ ListResourceBundleを使用する場合

こちらを使用する場合の定義ルールは以下の通り。

  • 定義方法
    • ListResourceBundleを継承したpublicクラスを作成する
    • getContents()メソッドをオーバーライドして配列でリソースのリストを作成する
    • リソースは、キーと値を要素とする配列として作成

引用

▼ 実装例

Resource.java
public class Resource extends ListResourceBundle {

    public Object[][] getContents() {
        Object[][] contents = {
                {"apple", "リンゴ"},
                {"orange", "オレンジ"}
        };
        return contents;
    }
}
Resource_en_US.java
public class Resource_en extends ListResourceBundle {

    protected Object[][] getContents() {
        Object[][] contents = {
                {"apple", "apple"},
                {"orange", "orange"}
        };
        return contents;
    }
}

基底名言語コード国コード.javaとすることでロケール
のオブジェクトに対応した言語のファイルが参照される。

デフォルトのロケールインスタンスの場合は基底名のリソースが参照される。
また、ロケールのインスタンスに言語コードのみ指定した場合は、基底名の後に言語コードのみで良い。

Main.java
// 出力結果は以下の通り
// リンゴ:オレンジ
// apple:orange

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

        Locale localeJa = Locale.JAPAN;
        Locale localeUs = Locale.US;

        // 第二引数の指定がない場合デフォルトロケートを指定する
        List<Locale> locales =
                new ArrayList<Locale>(Arrays.asList(localeJa, localeUs));

        for(Locale locale : locales) {
            // パッケージ名.ファイル名を指定
            ResourceBundle rb =
                ResourceBundle.getBundle("resource.Resource", locale);

            // String以外を取得する場合はgetObjectメソッドを使用してキャストする
            System.out.println(rb.getString("apple") + ":" + rb.getString("orange"));
        }
    }
}

■ PropertyResourceBuilderを使用する場合

こちらを使用する場合の定義ルールは以下の通り。

  • プロパティファイル名は、基底名言語コード国コード.properties
  • リソースのキーと値はプロパティファイル内にキー = 値の形の形で記載

▼ 実装例

image.png

Source.properties
apple=J_apple
orange=J_orange
Source_en_US.properties
apple=U_apple
orange=U_orange
Main.java
// 出力結果
// J_appleJ_orange
// U_appleU_orange

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

        File dicDir = Paths.get(".\\resource").toFile();

        URLClassLoader urlLoader;
        urlLoader = new URLClassLoader(new URL[]{dicDir.toURI().toURL()});

        Locale localeJp = Locale.JAPAN;
        Locale localeUs = Locale.US;

        List<Locale> locales =
                new ArrayList<Locale>(Arrays.asList(localeJp, localeUs));

        for (Locale locale : locales) {
            // ファイル名のみ指定
            ResourceBundle rb =
                    ResourceBundle.getBundle("Source" ,locale ,urlLoader);

            System.out.println(rb.getString("apple") + rb.getString("orange"));

        }
    }
}

参考文献

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

MapStructを使用するBeanコピー

1. なぜMapStruct

Javaを使用して、Beanコピーする際、よく使われるライブラリはいくつかある。

  • org.apache.commons.beanutils.BeanUtils.copyProperties
  • org.apache.commons.beanutils.PropertyUtils.copyProperties
  • org.springframework.beans.BeanUtils.copyProperties
  • org.springframework.cglib.beans.BeanCopier.copy
  • org.mapstruct

この中で、私が一番推奨するのはmapstructである。その原因は、mapstruct一番速いのだ。以下のソースでは、上記の5つのライブラリを使用してBeanコピーする際に、それぞれ掛かった時間を統計している。

@Slf4j
public class CopyDemoTest {

    public UserMainBO bo;

    public static int count = 1000000;

    @Before
    public void init(){
        bo = new UserMainBO();
        bo.setId(1L);
    }

    @Test
    public void mapstruct() {
        UserMainVOMapping INSTANCE = Mappers.getMapper( UserMainVOMapping.class );
        log.info("star------------");
        for (int i = 1; i <=count; i++) {
            UserMainVO vo = INSTANCE.toVO(bo);
        }
        log.info("end------------");
    }

    @Test
    public void beanCopier() {
        log.info("star------------");
        BeanCopier copier = BeanCopier.create(UserMainBO.class, UserMainVO.class, false);
        for (int i = 1; i <=count; i++) {
            UserMainVO vo = new UserMainVO();
            copier.copy(bo, vo, null);
        }
        log.info("end------------");
    }

    @Test
    public void springBeanUtils(){
        log.info("star------------");
        for (int i = 1; i <=count; i++) {
            UserMainVO vo = new UserMainVO();
            BeanUtils.copyProperties(bo, vo);
        }
        log.info("end------------");
    }

    @Test
    public void apacheBeanUtils() throws InvocationTargetException, IllegalAccessException {
        for (int i = 1; i <=count; i++) {
            UserMainVO vo = new UserMainVO();
            org.apache.commons.beanutils.BeanUtils.copyProperties(bo, vo);
        }
        log.info("end------------");
    }

    @Test
    public void apachePropertyUtils() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        log.info("star------------");
        for (int i = 1; i <=count; i++) {
            UserMainVO vo = new UserMainVO();
            PropertyUtils.copyProperties(bo, vo);
        }
        log.info("end------------");
    }

}

検証結果:

1,000回 10,000回 100,000回 1,000,000回
apache.BeanUtils 550ms 1085ms 4287ms 32088ms
apache.PropertyUtils 232ms 330ms 2080ms 20681ms
cglib.BeanCopier 73ms 106ms 102ms 99ms
mapstruct 91ms 5ms 7ms 12ms
spring.BeanUtils 5ms 188ms 336ms 844ms

以上の検証結果から、mapstructは他のライブラリよりパフォマンスが遥かに良いことが確認できる。

2. MapStructを使用するには

MapStructを使用するには、MapStructのライブラリをプロジェクトにインポートする必要がある。
以下は、mavenを使用するときの例である。

pom.xml
<properties>
    <org.mapstruct.version>1.3.1.Final</org.mapstruct.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${org.projectlombok.version}</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source> <!-- depending on your project -->
                <target>1.8</target> <!-- depending on your project -->
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

2.1 lombokとの併用

1.2.0.Finalバージョン以前のMapStructとlombokを併用する際、以下のように設定必要:

pom.xml
<properties>
    <org.mapstruct.version>1.1.0.Final</org.mapstruct.version>
    <org.projectlombok.version>1.18.12</org.projectlombok.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${org.projectlombok.version}</version>
    </dependency>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>${org.mapstruct.version}</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <source>1.8</source> <!-- depending on your project -->
                <target>1.8</target> <!-- depending on your project -->
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>${org.projectlombok.version}</version>
                    </path>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

2.2 swagger-uiとの併用

swagger-ui内部は古いバージョンのMapSructを使用しているため、swagger-uiと併用する際、swagger-uiのライブラリからMapSructへの依存を除外する必要がある。

pom.xml
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>${springfox-swagger.version}</version>
    <exclusions>
        <exclusion>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>${springfox-swagger.version}</version>
    <exclusions>
        <exclusion>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
        </exclusion>
    </exclusions>
</dependency>

3. MapStructの使い方

3.1 簡単な使い方

下記、2つのBeanクラスがある。

Student.java
import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class Student {
    private String name;
    private String address;
    private String phone;
}
Person.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Person {
    private String name;
    private String address;
    private String telephone;
}

これから、PersonオブジェクトからStudentオブジェクトへBeanコピーを行う。Beanコピーする前に、Mapperクラスを事前に用意する必要。

StudentMapper.java
import model.Person;
import model.Student;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

@Mapper
public interface StudentMapper {
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    Student personToStudent(Person person); // メソッド名は任意
}

では、Beanコピーして見よう。

main_method
Person person = new Person("lisa", "Tokyo", "12345");
Student student = StudentMapper.INSTANCE.personToStudent(person);
System.out.println(student);

// 出力
// Student(name=Lisa, address=Tokyo, phone=null)

デフォルトでは、名前不一致のフィールドがあれば自動的に無視されるため、telephoneからphoneへコピーできない。

3.2 名前不一致のフィールドのコピー

sourceとtargetのフィールド名不一致の場合、コピーしようとすると、@Mappingを指定することが必要。

StudentMapper.java
import model.Person;
import model.Student;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface StudentMapper {
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "telephone", target = "phone")
    Student personToStudent(Person person);
}

実行結果:

Student(name=Lisa, address=Tokyo, phone=12345)

3.3 フィールドを除外にする

コピーしたくないフィールドがあれば、@Mappingignore属性を通してコピー対象から除外することができる。

StudentMapper.java
import model.Person;
import model.Student;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface StudentMapper {
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "telephone", target = "phone")
    @Mapping(target = "address", ignore = true)
    Student personToStudent(Person person);
}

実行結果:

Student(name=Lisa, address=null, phone=12345)

3.4 複数のBeanから1つのBeanにコピー

MapStructは、複数のBeanから1つのBeanにフィールドをコピーできる。

LoginInfo.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginInfo {
    private String id;
    private String password;
}
UserProfile.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserProfile {
    private String email;
    private String address;
}
UserInfo.java
@ToString
@Data
public class UserInfo {
    private String id;
    private String password;
    private String email;
    private String address;
}

Mapperを準備する。

UserInfoMapper.java
import model.LoginInfo;
import model.UserInfo;
import model.UserProfile;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface UserInfoMapper {
    UserInfoMapper INSTANCE = Mappers.getMapper(UserInfoMapper.class);

    @Mapping(target = "id", source = "loginInfo.id")
    @Mapping(target = "password", source = "loginInfo.password")
    @Mapping(target = "email", source = "userProfile.email")
    @Mapping(target = "address", source = "userProfile.address")
    UserInfo fromLoginInfoAndUserProfile(LoginInfo loginInfo, UserProfile userProfile);
}

コピーする。

main_method
LoginInfo loginInfo = new LoginInfo("12345", "54321");
UserProfile userProfile = new UserProfile("123@gmail.com", "Tokyo");

UserInfo userInfo = UserInfoMapper.INSTANCE.fromLoginInfoAndUserProfile(loginInfo, userProfile);
System.out.println(userInfo);

// 出力
// UserInfo(id=12345, password=54321, email=123@gmail.com, address=Tokyo)

3.5 作成済のBeanを更新する

MapStructは、Beanをコピーして作成だけではなく、作成済のBeanを更新することも可能。

UserInfoMapper.javaに、下記のソースを追加する:

UserInfoMapper.java
@Mapping(target = "email", source = "email")
@Mapping(target = "address", source = "address")
void updateUserProfile(UserProfile userProfile, @MappingTarget UserInfo userInfo);

更新してみよう:

main_method
LoginInfo loginInfo = new LoginInfo("12345", "54321");
UserProfile userProfile = new UserProfile("123@gmail.com", "Tokyo");

UserInfo userInfo = UserInfoMapper.INSTANCE.fromLoginInfoAndUserProfile(loginInfo, userProfile);
System.out.println(userInfo);
// 出力
// UserInfo(id=12345, password=54321, email=123@gmail.com, address=Tokyo)

userProfile = new UserProfile("456@gmail.com", "Fukuoka");
UserInfoMapper.INSTANCE.updateUserProfile(userProfile, userInfo);
System.out.println(userInfo);
// 出力
// UserInfo(id=12345, password=54321, email=456@gmail.com, address=Fukuoka)

3.6 フォーマット変換

MapStructでは、Date/LocalDateからStringに変換する際、intからStringに変換する際、フォーマットを指定して変換できる。

Person.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDate;

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private String name;
    private LocalDate birthday;
    private int salary;
}
Employee.java
import lombok.Data;
import lombok.ToString;

@Data
@ToString
public class Employee {
    private String name;
    private String birthday;
    private String salary;
}
PersonMapper.java
import model.Employee;
import model.Person;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper
public interface PersonMapper {
    PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);

    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy/MM/dd")
    @Mapping(source = "salary", target = "salary", numberFormat = "#,###")
    Employee personToEmployee(Person person);
}
main_method
Person person = new Person("lisa", LocalDate.of(1990, 1, 20), 1234567);
Employee employee = PersonMapper.INSTANCE.personToEmployee(person);
System.out.println(employee);
// 出力
// Employee(name=lisa, birthday=1990/01/20, salary=1,234,567)

3.7 expression

Beanコピーする際、もし複雑は変換が必要な場合、expressionで簡単に実現できる。

例えば、PersonオブジェクトからEmployeeオブジェクトにコピーする際、nameフィールドを大文字にする。

PersonMapper.java
import model.Employee;
import model.Person;
import org.apache.commons.lang3.StringUtils;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper(imports = {StringUtils.class}) // StringUtilsをインポートする
public interface PersonMapper {
    PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);

    @Mapping(target = "name", expression = "java(StringUtils.upperCase(person.getName()))")
    Employee personToEmployee(Person person);
}

3.8 defaultメソッド

Java8から、interfaceはdefaultメソッドをサポートできるようになった。MapStructもdefaultメソッドを利用して、複雑なコピーロジックを作成できる。

PersonMapper.java
import model.Employee;
import model.Person;
import org.apache.commons.lang3.StringUtils;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

import java.time.format.DateTimeFormatter;

@Mapper(imports = {StringUtils.class})
public interface PersonMapper {
    PersonMapper INSTANCE = Mappers.getMapper(PersonMapper.class);

    default Employee personToEmployee(Person person, String dateFormat) {
        Employee employee = new Employee();
        // nameを大文字にする
        employee.setName(StringUtils.upperCase(person.getName()));
        // birthdayを指定されたフォーマットに変換する
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat);
        employee.setBirthday(person.getBirthday().format(dateTimeFormatter));

        return employee;
    }
}

defaultメソッドを実行する。

main_method
Person person = new Person("lisa", LocalDate.of(1990, 1, 20), 1234567);
Employee employee = PersonMapper.INSTANCE.personToEmployee(person, "yyyy年MM月dd日");
System.out.println(employee);
// 出力
// Employee(name=LISA, birthday=1990年01月20日, salary=null)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaのHashMapの中身を覗いてみた

JavaのHashMapのソースコードを読んで、hashのキーの比較がequals()で行われる`ことを確認した。

    /**
     * Implements Map.get and related methods.
     *
     * @param hash hash for key
     * @param key the key
     * @return the node, or null if none
     */
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;                            // 内部配列
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {                                // 内部配列の(hashCode/配列の長さの剰余)番目に収納されている要素(LinkedListの最初のNode)
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {                                                                 // LinkedListを順々に探索していく
                    if (e.hash == hash &&                                            // hashCodeが異なったら次のNodeへ(optimization目的)      
                        ((k = e.key) == key || (key != null && key.equals(k))))      // ここでequals()で比較!!!!!!
                        return e;
                } while ((e = e.next) != null);     
            }
        }
        return null;
    } 

探索中のオブジェクトと探索中のLinkedListの各Nodeの持つkeyのオブジェクトを、それぞれのequals()を用いて同意性比較していることが確認できた。

参考

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

[Java]Eclipseでstruts1.3を開発するための環境構築手順

2020年4月から現場が変わることになりましたが、そこの案件がStruts 1.3(2.xでもない:innocent:)ということで、勉強を兼ねて環境を構築をした手順をまとめました。

世間では、JavaといえばSpring bootが主流になっていますが、Strutsもまだまだ現役です。
レガシー環境に流れ着いてしまった不運なあなた、むしろそんな現場を求めているあなたにとって、手助けになるかもしれません。

前提条件

  • 使用PCはWindows
  • Java1.5以上がインストール済み(Oracle JDKでもOpen JDKでも何でも良いので、インストールしてください。)
  • Git for Windowsがインストール済み

事前準備

Tomcatのインストール

ダウンロード

Tomcatを以下からダウンロードします。(※2020年3月現在、最新の安定版は9.0.33)
http://tomcat.apache.org

任意のフォルダに解凍

ダウンロードしたファイルを解凍します。
今回は以下に配置しました。
C:\public\tool\apache-tomcat-9.0.33

■ フォルダ構成(第一階層のみ)

C:\public\tool\apache-tomcat-9.0.33
├─bin
├─conf
├─lib
├─logs
├─temp
├─webapps
└─work

mavenのインストール

ダウンロード

mavenを以下からダウンロードします。(※2020年3月現在、最新の安定版は3.6.3)

Maven – Download Apache Maven
https://maven.apache.org/download.cgi

「apache-maven-3.6.3-bin.zip」を選択。

任意のフォルダに解凍

ダウンロードしたファイルを解凍します。今回は以下に配置しました。
C:\public\tool\apache-maven-3.6.3

■ フォルダ構成(第一階層のみ)

C:\public\tool\apache-maven-3.6.3&amp;amp;gt;tree
├─bin
├─boot
├─conf
└─lib

PATHを追加設定

今回はターミナルにGit Bashを使用するので、Git BashにPATHを設定します。

  • C:\Program Files\Git\etc\bash.bashrc
# System-wide bashrc file
### add start
if [ -f ~/.bashrc_profile ]; then
. ~/.bashrc_profile
fi
### add end
  • C:\Users[user].bashrc_profile
export PATH=$PATH:/c/public/tool/apache-maven-3.6.3/bin 

Eclipseのインストール

ダウンロード

Eclipseを以下からダウンロードしてください。
https://www.eclipse.org/downloads/

Subclipseのインストール

Eclipseマーケットプレースより以下をインストール

  • Subclipse 4.3.0

image.png

プロジェクトの作成

struts-archetype-blankのインストール

struts-archetype-blankのチェックアウト

ファイル→新規→その他→SVNからプロジェクトをチェックアウト
image.png

新規リポジトリー・ロケーションを生成
image.png

リポジトリ:http://svn.apache.org/repos/asf/struts/maven/trunk/struts-archetype-blank

image.png

mvnにてインストールする

cd C:\Users\msg_h\git\struts-sample\struts-archetype-blank
mvn install

新規プロジェクトの作成

新規にプロジェクトを作成する。

  • ファイル→新規→Java image.png

アーキタイプの追加
image.png

以下を入力する。

項目
アーキタイプ・グループID org.apache.struts
アーキタイプ・アーティファクトID struts-archetype-blank
アーキタイプ・バージョン 1.3.5-SNAPSHOT
リポジトリーurl http://svn.apache.org/repos/asf/struts/maven/trunk/struts-archetype-blank/

image.png

追加された「struts-archetype-blank」を選択する。

image.png

以下を入力する。

項目
グループID my.struts
アーティファクトID struts13-app
バージョン 0.0.1-SNAPSHOT
パッケージ my.struts.struts13_app (自動入力)

image.png

これでベースを作成することができました。

gitリポジトリ

今回作成したソースを以下のリポジトリにコミットしています。
他のソースもいろいろ混じっているかもしれません。。。
https://github.com/hrk-okd/struts13-app

参考

下記参考にさせていただきました。
https://qiita.com/morozumi_h/items/e36faee2c2bebb2fb15d

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

プログラミング言語の比較(PythonとJavaとRubyと)

面接で聞かれてしゅって答えられず痛い目にあったので自分用に整理としてみる。

概要

自分の経験期間は以下(2020年7月現在)

言語 使用期間 使用用途
Python 2018.5 ~ 2020.3 (1)JupyterNotebook上でのWebスクレイピングや機械学習を含むデータ解析 (2)MLOpsにおけるAWS Lambdaの実行スクリプトやAWSBatchの実行ファイル
Java 2018.8 ~ 2019.5 Javaによるスタンドアロンアプリ開発
Ruby 2019.11 ~ RailsでのWebアプリ開発
  • オブジェクト指向やシステム開発の基礎を身に付けたのはJava、Webアプリを実際に作ったのはRuby on Railsである。
  • PythonのDjangoはチュートリアルだけやった
  • PYthonはデータ解析メインで使ったため、システム開発の経験があるJavaとRubyを比較する。

Java

  • kafka, Hadoopなどデータ処理系には結構よく使われているイメージ。
  • コンパイル型言語で処理速度速め
    • バイトコードコンパイル型言語
    • つまり、javacコマンドでコンパイルした時点ではマシン語ではなくバイトコード(中間コード)になっている。
      • ソフトウェアはこの状態で配布されていて、JVMが入っていればどこでも動く。
    • JVM上でこのバイトコードをJustInTimeコンパイル(実行時コンパイル)してマシンコードにしながらCPUに実行される。
      • JITコンパイルとは、ソフトウェアを構成するモジュールやクラス、関数などの、ある単位のコードがまさに実行されるその時に、コンパイルすること。
  • メソッド毎で必ず例外処理をする(かthrowする)必要がある。
  • intはプリミティブとIntegerというラッパクラスがあり、それらを使い分けている。
  • マルチスレッドできるって話にはよく聞く。デザインパターン入門のマルチスレッド編出てるし。

Ruby

  • webフレームワーク(Rails)以外の用途で使われているのを見たことがない。
  • インタープリタ型言語で処理速度遅い(らしい)
  • 書いてて楽。以下が要因としてあげられる。
    • メソッドの()が省略可能であること。個人的には気持ち悪いが...
    • クラスのメソッドにaliasが貼られていることが多いこと。()
    • ブロックの導入。
      • これに関しては、読みやすさに全振りするために導入したもではないか?と感じる。メソッドの引数にブロックの代わりにラムダ関数を入れて渡すのでも問題ないように思えるし。
      • ブロックの導入、さらにブロックを{|x|...}のみならずdo |x| ... endという書き方を許容したことで、以下のようにeach/map/filterと言った関数型プログラミングと合わせた時の書きやすさ/読みやすさが抜群に高まった。(自分が初めて以下を見た時どうしたらこのような書き方が許容される言語仕様になるのか本当に不思議だった)
[1, 2, 3].map do |each_num|
    each_num + 1
end
  • 定数が書き換え可能
    • 定数はVarのように、大文字始まりで命名された変数
    • 定数への再代入も、定数の破壊的メソッドの実行も(警告が発生するが)できてしまう
      • freezeメソッドで破壊的メソッドは防止できる
  • メタプログラミング(黒魔術)ができる。
    • コードを書く人は楽だが、コードを読む人は結構追跡が大変になるのでそんなに好きじゃない。
    • これは以下のメソッドによって実現されている。
      • eval "@#{key}=value"でStringオブジェクトをコードとして解釈できる
      • define_method str {...}でクラス内で動的にメソッドを追加できる
      • send(:method_name, arguments)で動的なメソッド呼び出しができる
  • マルチスレッドプログラミングをしているのをあまり見たことがない。

Java vs Ruby

  • Javaのinterfaceを実装 <=> RubyのmoduleをMixIn

  • エラーハンドリングが、Javaではメソッド毎に必須 <=> Rubyだと必須でない

    • Javaでは、メソッド毎で必ず例外処理をする(か例外を明示的にthrowする)必要がある。
      • RuntimeExceptionとIllegalArgumentExceptionはその必要はない
    • Rubyでは、メソッド内でハンドリングされないエラーが生じた場合、その時点で現在のメソッドを終了して呼び出し元にエラーが引き継がれる。
      • ちなみにRailsだと、アクティブレコードで生じたActiveRecord::RecordNotFoundがコントローラーのアクションで明示的にハンドリングされなかった場合、404エラーのHTTPレスポンスが返されるという仕様があることを知った時には、本当によくできているものだと感動した。
    • ちなみにGolangだと、エラーオブジェクトは返り値として返す。
  • Javaではprimitive型が存在する <=> Rubyは全ての値はオブジェクト

    • Javaでは、boolean, byte, char, short, int, float long, doubleがprimitive型。
      • これらにはラッパークラスが存在している。例えばリストの要素としてintを含める場合には、List<int>ではなく、ラッパー型のjava.lang.Integerを用いてList<Integer>にしてやる。
      • primitive型を使うメリットはここに詳しい。知らなかった...
    • Rubyだとintegerもクラスであるため、3.each{...}のように数値からメソッドを生やすことができる。
      • これまたRubyは書いてて楽しい、との評判に貢献していそうではある。
  • equalsと==の意味が逆なのがややこしくてストレシング。

    • Javaだと== は同一性.equals()は同等性
      • hashのキーの比較は.equalsを用いる。
      • equals()をoverrideしたらhashCode()もoverrideする必要がある。
      • A.equals(B)がfalseなのにAとBのhashCode()が異なることは許されない。
      • A.equals(B)がfalseなのにAとBのhashCode()が同じになることは許される。ただしこの場合ハッシュの衝突が生じる。
    • Rubyだと==は同等性equal?()は同一性
      • hashのキーの比較はeql?を用いる。これはequal()? + 比較する二つのオブジェクトのクラスが同一か?の和集合である。
      • eql?()をoverrideしたらhash()もoverrideする必要がある。
      • A.eql?(B)がfalseなのにAとBのhash()が異なることは許されない。
      • A.eql?(B)がfalseなのにAとBのhash()が同じになることは許される。ただしこの場合ハッシュの衝突が生じる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaとRubyを個人的に比較してみた

面接で聞かれてしゅって答えられず痛い目にあったので自分用に整理としてみる。

概要

自分の経験期間は以下(2020年7月現在)

言語 使用期間 使用用途
Python 2018.5 ~ 2020.3 (1)JupyterNotebook上でのWebスクレイピングや機械学習を含むデータ解析 (2)MLOpsにおけるAWS Lambdaの実行スクリプトやAWSBatchの実行ファイル
Java 2018.8 ~ 2019.5 Javaによるスタンドアロンアプリ開発
Ruby 2019.11 ~ RailsでのWebアプリ開発
  • オブジェクト指向やシステム開発の基礎を身に付けたのはJava、Webアプリを実際に作ったのはRuby on Railsである。
  • PythonのDjangoはチュートリアルだけやった
  • PYthonはデータ解析メインで使ったため、システム開発の経験があるJavaとRubyを比較する。

Java

  • kafka, Hadoopなどデータ処理系には結構よく使われているイメージ。
  • コンパイル型言語で処理速度速め
    • バイトコードコンパイル型言語
    • つまり、javacコマンドでコンパイルした時点ではマシン語ではなくバイトコード(中間コード)になっている。
      • ソフトウェアはこの状態で配布されていて、JVMが入っていればどこでも動く。
    • JVM上でこのバイトコードをJustInTimeコンパイル(実行時コンパイル)してマシンコードにしながらCPUに実行される。
      • JITコンパイルとは、ソフトウェアを構成するモジュールやクラス、関数などの、ある単位のコードがまさに実行されるその時に、コンパイルすること。
  • マルチスレッドできるって話にはよく聞く。デザインパターン入門のマルチスレッド編出てるし。

Ruby

  • webフレームワーク(Rails)以外の用途で使われているのを見たことがない。
  • インタープリタ型言語で処理速度遅い(らしい)
  • 書いてて楽。以下が要因としてあげられる。
    • メソッドの()が省略可能であること。個人的には気持ち悪いが...
    • クラスのメソッドにaliasが貼られていることが多いこと。()
    • ブロックの導入。
      • これに関しては、読みやすさに全振りするために導入したもではないか?と感じる。メソッドの引数にブロックの代わりにラムダ関数を入れて渡すのでも問題ないように思えるし。
      • ブロックの導入、さらにブロックを{|x|...}のみならずdo |x| ... endという書き方を許容したことで、以下のようにeach/map/filterと言った関数型プログラミングと合わせた時の書きやすさ/読みやすさが抜群に高まった。(自分が初めて以下を見た時どうしたらこのような書き方が許容される言語仕様になるのか本当に不思議だった)
[1, 2, 3].map do |each_num|
    each_num + 1
end
  • 定数が書き換え可能
    • 定数はVarのように、大文字始まりで命名された変数
    • 定数への再代入も、定数の破壊的メソッドの実行も(警告が発生するが)できてしまう
      • freezeメソッドで破壊的メソッドは防止できる
  • メタプログラミング(黒魔術)ができる。
    • コードを書く人は楽だが、コードを読む人は結構追跡が大変になるのでそんなに好きじゃない。
    • これは以下のメソッドによって実現されている。
      • eval "@#{key}=value"でStringオブジェクトをコードとして解釈できる
      • define_method str {...}でクラス内で動的にメソッドを追加できる
      • send(:method_name, arguments)で動的なメソッド呼び出しができる
  • マルチスレッドプログラミングをしているのをあまり見たことがない。

Java vs Ruby

  • Javaのinterfaceを実装 <=> RubyのmoduleをMixIn

  • エラーハンドリングが、Javaではメソッド毎に必須 <=> Rubyだと必須でない

    • Javaでは、メソッド毎で必ず例外処理をする(か例外を明示的にthrowする)必要がある。
      • RuntimeExceptionとIllegalArgumentExceptionはその必要はない
    • Rubyでは、メソッド内でハンドリングされないエラーが生じた場合、その時点で現在のメソッドを終了して呼び出し元にエラーが引き継がれる。
      • ちなみにRailsだと、アクティブレコードで生じたActiveRecord::RecordNotFoundがコントローラーのアクションで明示的にハンドリングされなかった場合、404エラーのHTTPレスポンスが返されるという仕様があることを知った時には、本当によくできているものだと感動した。
    • ちなみにGolangだと、エラーオブジェクトは返り値として返す。
  • Javaではprimitive型が存在する <=> Rubyは全ての値はオブジェクト

    • Javaでは、boolean, byte, char, short, int, float long, doubleがprimitive型。
      • これらにはラッパークラスが存在している。例えばリストの要素としてintを含める場合には、List<int>ではなく、ラッパー型のjava.lang.Integerを用いてList<Integer>にしてやる。
      • primitive型を使うメリットはここに詳しい。知らなかった...
    • Rubyだとintegerもクラスであるため、3.each{...}のように数値からメソッドを生やすことができる。
      • これまたRubyは書いてて楽しい、との評判に貢献していそうではある。
  • equalsと==の意味が逆なのがややこしくてストレシング。

    • Javaだと== は同一性.equals()は同等性
      • hashのキーの比較は.equals()を用いる。(ここで確認済)
      • equals()をoverrideしたらhashCode()もoverrideする必要がある。
      • A.equals(B)がfalseなのにAとBのhashCode()が異なることは許されない。
      • A.equals(B)がfalseなのにAとBのhashCode()が同じになることは許される。ただしこの場合連鎖法で実装されたハッシュでは衝突が生じる。
    • Rubyだと==は同等性equal?()は同一性
      • hashのキーの比較はeql?を用いる。これはequal()? + 比較する二つのオブジェクトのクラスが同一か?の和集合である。
      • eql?()をoverrideしたらhash()もoverrideする必要がある。
      • A.eql?(B)がfalseなのにAとBのhash()が異なることは許されない。
      • A.eql?(B)がfalseなのにAとBのhash()が同じになることは許される。ただしこの場合連鎖法で実装されたハッシュでは衝突が生じる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

10年日语营业转行IT从深圳到日本东京圈工作生活2019

10年日语营业转行IT从深圳到日本东京圈工作生活2019

主旨:成人学历可以来日本,拖家带口可以来日本,不通过中介可以来日本,只会日语不会IT可以来日本。

序章

日本对于我来说确实曾经像一个童话,

日本对于我来说确实曾经是高不可攀的目标,

日本对于我来说确实曾经是一个做醒了就扔到一边的梦。

2011年,日语语言学校毕业取得日语一级,刚毕业就想过去日本,当时说是要10万元保证金,全日制本科学历,还有留学学费,这种事情咱想想就蒜了吧。由于贫穷限制想象,一直在东莞的各大人才市场大显身手,进了各种坑货台湾工厂。

2014年,真正进了一家传统日企,真正做上了业务,真正锻炼到了终身难忘的日语,真正成家,真正成人,这家日企经历了两三年。

2018年,深圳地王大厦之上,加班到半夜,深圳夜景万家灯火,曾经的日语同学一通电话问哥们你还想去日本试一下吗?这通电话点燃了多年的梦想,星星之火迅速燎原。

到日本可以留学,也可以工作,留学的话前期费用和打工的辛苦程度很高,选择工作到日本挺稳妥的。有钱人可以投资移民到日本,不过终究不是穷人考虑的问题,其实调查下来,开公司的最低注册金500万日元有了这个钱就挺好办的。

有啥条件

2009年-2011年,国内日语培训学校毕业,无学历。取得日语二级,日语一级证书,J.TEST准B级证书。

2012年-2014年,自考外语系的大学,取得自考大专学历。

2017年-2019年,取得成人本科学历,专业国际经济与贸易。

2019年6月-9月在大连,开始学习Java相关前后端知识。HTML , CSS , JS , Vue.js , Node.js , Java , PHP , Laravel 。后面陆续学了Linux,SSH框架,SSM框架,微服务,Jenkins自动化等,基本自学。

应聘过程

【2018年5月:】

  1. 这时候完全没有想到以IT工作去日本这个点子,只想着要去日本,不管什么手段。
  2. 开始着手制作日文简历,再做一个中午简历副本,更新前程无忧上的中文简历。
  3. 在百度找了一些日企招聘网站,也联系了一些专业面向日企的猎头。
  4. 在日本的DODA和リクナビNEXT投简历,收到skype面试唐吉坷德的店员机会,面试失败。
  5. 最后找来找去还是觉得去不了日本,就在去IBM的日语电话客服或者日企上市公司的商社做决定,还是继续在国内又工作。

【2019年4月:】

  1. 边工作,还是继续寻找赴日工作的机会。
  2. 利用日本的DODA和リクナビNEXT,以及各种日本的猎头,PASONA,Recruit Agent等制作日式的 履歴書職務経歴書
  3. 尝试利用日本求职网站寻找合适的公司,通过DODA找到了一家日本仙台的公司做日语营业经常出差日本的工作,面试失败。
  4. 想尝试通过各种中介去日本的温泉酒店,电信店的工作等,中介费我还是介意了,放弃。
  5. 4月17日晚上,进行第一家IT公司的视频面试,这家公司就是2018年5月份找工作的时候前程无忧上联系到的,因为找赴日工作各种碰壁,所以当他晚上坐在肯德基不知道怎么突发奇想翻到微信里的这个联系人,一联系结果妥了。现在想来还是微信联系人管理的细致,才找到了这么个机会。
  6. 因为上次聊天就问了会日语不会编程怎么办,她说可以去她大连分社学习,所以入行也有了着落。
  7. 面试的是在日本那边公司的法人、社长,永驻资格的中国人。自我介绍以及一些针对简历上个人经历背景的普通问答,日语交流,基本没有涉及到技术问题,后面聊薪酬待遇、日本工作生活的情况等等,面试过程很轻松顺畅,没有很形式的感觉。因为本来就是感觉要么会日语,要么会编程都可以进的一个公司。
  8. 因为我是自考大专学历,她说需要确认这个学历能不能办工作签证,后面确认到这个学历办理企业内转勤签证比较好。
  9. 开始调查这家日本有总社,大连有分社的公司,看有无猫腻
  10. 4月22号坐火车去大连考察这家公司真假,反正去了一天也没调查出多少东西来。结果最靠谱的回答是我有个朋友在香港上班,因为他在日本做过IT行业,所以懂;说是这种公司也不存在坑不坑的,就是中间商赚差价。大连有很多这样的,所以也就放心了。
  11. 犹豫自己能不能学会编程

【2019年5月~~:】

  1. 5月8日--5月14日,毕业证、日语等级证、淘宝买的印章,二寸照片等其它材料打包国际EMS邮寄到日本公司,由日本公司方面申请在留资格。
  • 各位,这里注意了,办签证这些不需要毕业证、学位证、日语等级证什么的原件、全部复印件就搞定了。
  • 但是公司要想押你的,你又想去,那就答应吧。
  • 因为我后面,学历和日语等级证都去相关机构申请补办回来了,也算是见招拆招了,他不还给我也无所谓了。
  • 这里批评一下不知哪些人在网上留言说日语等级证书不能重新补办,我真是信了他的话了,还真准备不申请补办去直接再考一次日语一级。
  • 可最后还好尝试了一下在日本补办结果补回来了,美美哒。不过学位证咋补办就不知道了。
  1. 6月1号坐长途火车去大连学编程。

  2. 6月3号到大连入住大连弘基书香园小区,宿舍二三十个人,上下铺铁板床,基本都是各个公司搞IT的程序猿,房租420一个月水电网都包了,刚开始住进去的稀稀拉拉几个人,住到后面不知道是毕业季的原因还是啥,一下子宿舍住满了人,而且还有个哥们天天早上洗澡半天不出来,哈哈 。二三十个人的宿舍就一个厕所,其他都还好,其中有段时间我一直就在附近的星巴克蹭空调,从来没有买过一杯星巴克,罪过,罪过!想起这几个月的居住经历,我到了大连的夏天连小风扇都没舍得买过,真是节约到底了,确实像是艰苦奋斗的感觉,每天学习也是拼了老命想要把编程学会,最开始2进制也不知道是什么,我就是一个10万个为什么这样学过来的,其中的心酸和激动就真是难以言表了,,里面的程序猿很多都喜欢打游戏,我住了四个月也没交到什么朋友,可能是因为我不是很喜欢打游戏吧。最后走的时候,房东还是如数退还了押金,这算是比较良心了。

  3. 过了一个星期左右和大连分公司这边签订了劳动合同,这里是中国法律管辖内的合同,基本就是制约你在分公司呆了3个月左右一定要去日本的,不去要赔偿培训费、签证办理费这样的条款。

  4. 6月26号---7月26号办理大连的居住证,因为住的便宜的房子,办居住证花了功夫,这里感谢公司的同事!

  5. 8月28日,收到日本总社人事通知,在留资格认定证明书下来了。

  6. 9月5日, 在留資格認定証明書 通过EMS邮寄到手,在留资格类型 企业内转勤 1 year

  7. 看到在留资格认定书在8月2号都已经许可了,感觉公司给我押了半个月,谁曾想当时那段时间真的好郁闷,好颓废,好焦急;当一个东西等不到的时候,我就去查平均下在留的时间,在留认定不通过的理由原因等,我真的是费尽心思想为啥要那么久才下在留,因为,我是5月8号就寄了资料,6月11号日本总社人事问我工作经历,提交在留申请,估计最晚提交在留申请也就是6月15号左右,可是到了8月15号,等了2个月,我还没等到在留下发的消息,心里真是焦急啊。

  8. 9月5日,前往大连国际友好服务中心申请代办 签证,需要材料:

①,签证申请表、

②,户口本原件和复印件、

③,在留资格原件和复印件、

④,大连暂住证原件和复印件、

⑤,护照原件的复印件、

⑥,白色的照片身份证及复印件,

具体咨询各辖区代办点(个人出境签证只能通过指定的代办机构办理,签证费200,代办费300不等,我一共花了400元左右)

  1. 9月16日,拿到签证。

  2. 9月24号,回家团建。

  3. 在深圳的中国银行深圳支行,取日元挺麻烦的,最好去大的中国银行取钱,大的支行。取钱之前最好预约一下。取出约25W日元现金作为头两个月生活费带过去(最多可携带约60W,5k美元等值)银行间汇率有差异,一般情况下取40W日元,最大相差600日元左右,在日本相当于一顿午饭钱,当时汇率,0.066632,汇率可参考:人民币专栏-外汇频道-和讯网

  4. 10月10日,出发去日本!!我买的南航直飞羽田机场的机票,提前一个月,机票便宜得很才958元,又是白天的飞机,坐起来很爽,但是第一次坐了4个多小时飞机,还确实感觉有些长。关于入境卡,填的资料零零碎碎的,建议提前做好准备,网上搜索样板,自己准备好要填的内容,在飞机上就不用手抓脚抓的。

  5. 来日之前需要准备的事情:
    ❶.购买手机上网wifi卡,推荐淘宝购买链接:http://zmnxbc.com/s/71iSV?tm=452a56(临时Wi-Fi卡,下飞机后保证微信能联系上),或者是买一张CMlink的电话卡,也很便宜的。
    ❷.如果来日后要给家人如妻子等办理签证,需要带上公证书,结婚证,妻子或其他家人的护照复印件,证件照来日。这样来日后方便申请
    ❸.行李问题,公司宿舍一人一个房间,但是被子行李需要自己提供,可以从国内带过来也可以来日后再自行购买
    ❹.西服套装、衬衫、皮鞋,这个至少准备一套
    ❺.登机前需要准备一支笔,飞机上填写入境卡需要
    ❻.手头换一些日元好方便生活使用

公司及工作形式介绍

1.应聘的公司是在神奈川县由中国人开办8年左右的小型IT外包企业,这公司还有关联的房地产公司,大连有分公司,约80名开发人员(技術者)。

2.入职公司,是IT方面毕业的,或者是有经验者,或者是懂点日语的基本都可以进。而这些东西即使你不会也是可以马上学习,马上报个培训班什么的,马上就可以进这种公司的,所以我的观点是只要是个人都可以进。

3.获得技术签证需要有相关资格,如大学专业学位类型、软考资格证、工作经验年限等;最好N3-N2水平以及2年以上工作经验等。

简单来说只要想去日本,这些都不是问题,即使你一无所有。

​ 我刚开始对于签证办理真是花费了大量调查研究工作:

  • 日语,日语好就是件好事,可是很遗憾,日语和办理签证没啥关系,除非高度人才签证可以加点分。

  • 公司规模,你赴日的这家公司,公司规模越大越好,当然最不济的注册金500万日元也不是不可以。

  • 工作经验,假如IT,最好是在国内也有相关IT工作经验,当然越长越好。

  • 学历,你要到日本来做什么事,最好学历上就是这个专业的。成人大专,自考学历,大专,本科学历等这总是最低要求了吧。

  • 如果来日本做IT,办理技术签证。国内的软考证明(软件考试,计算机,网络,通信方面的考试)特别有帮助,程序员考试以及网络工程师等等,在日本也是得到认可的,而且有了这个还可以免除学历审查的要件,简单说有软考证就可以不看学历了,这方面有不懂的可以问我。具体哪些证书日本承认可以了解一下:

1 (1).png

4.日本IT行业状况:

1 (2).png

  • 如图所示,SE是工资少,工作又不开心的一群人。要想钱多,工作又开心,你就要想办法进入日本真正的互联网行业,有些人也称WEB系的行业,因为和SIer相对应的就是WEB系。
  • SIer(Systems Integrator er),也就说系统整合商、er是拟人化表现手法。可以理解为软件外包系统承包商。
    • 上流的有野村,伊藤忠,NEC,富士通,NTT DATA,KDDI等等;他们是一级承包商。
    • 下面的二级承包商都是些独立系的软件承包商、又叫soft屋,这种一般都是日本人开的公司去承接这些大的SIer的活。
    • 再下面很多就是三级承包,一般都是公司实力小,但是有几个人能派出去的,很多中国人老板开的公司什么的。
    • 这种小派遣公司也会有很多人脉,交叉派遣什么的,假如我们都是小派遣,哪天我缺人了就找你问;你哪天缺人了就问我。
    • 很多IT赴日的小伙伴就是进的这种公司,就等于一包、二包、三包、N包的公司了,肯定项目预算被吃了一截又一截,留下残羹剩渣。
    • 这么底层的承包,交期非常紧迫,这就滋生了超级加班的土壤。
    • 所以想要在SIer行业混得好就要朝IT consultant也就是IT系统、软件顾问什么这方向发展才能赚大钱;接触最上流,工作内容都是客户咨询对应,要件定义,基本设计,技术选型这些内容。详细设计,编码,测试都是下流,所以苦逼又穷。所以,要做这些,那么也就意味着你的文档要做得很漂亮,Excel、Word、PowerPoint什么的溜溜溜。
    • SIer行业一般都不是敏捷开发,一般都是瀑布流式开发。但是我不知道其他SIer什么情况,我去的那个SIer现场就是敏捷开发了,而且用AWS这类的一些云服务,Jenkins自动化等,还是在技术上挺跟得上时代的。
    • SIer行业一般都是大项目,动辄几百上千人的项目。而且都是些大的国家基础设施已经银行,保险,政府业务居多。
  • 但是,大部分日本IT小公司都是以派遣企业形式生存;特别是中国人老板在日本开的公司,从中国、印度、越南、缅甸、日本等地招募大量IT开发人员,派驻到各个客户公司(称为现场)驻点上班,开发代码。
  • 到现场上班的机会需要自己另行跟现场负责人面试争取(日语为主),上班时间及每月基准工作时间依现场而定,午休1小时,请假由现场领导批准即可。
  • 项目结束后需要找下一家现场面试进驻新项目,一般这种SES的项目合同都是签3个月,现场的人觉得你做得好,你的合同可以无限期延长;如果合同不续签了,那么会提前一个月通知你的,以便于你去找下个现场;当然,找现场是你的公司回去给你安排的。
  • 自己所属的派遣公司仅负责招募员工、联系现场安排面试、发放工资等工作。

5.刚到日本一般住公司宿舍,有条件自己去找个UR团地住更好,开办银行账户、购买手机、日常用品等,西装我建议不要在淘宝去买,其 实在日本买西装一套很方便而且也不是很贵的,而且出门少带点东西不香吗,而且消费也是一种美德哈。

合同以及劳动条件

工作条件

1.来日本前在国内先和分公司那边签订劳动合同,3年的,如果国内培训期间(其实不培训多少知识,基本上自学或者免费给公司干活) 离职,或者在日本没有干满3年公司可以让你赔付培训费签证办理费用什么的,大概1万元左右。这个合同可以无视,因为个人感觉中国 和日本的法律不同,你只要到了日本,这个合同就不用去考虑它的约束力了。

2.在国内说好的是正社员,结果到了日本公司一来就签的是业务委托合同,也就是个人事业主。这时感觉特别无助,但是也只有忍了。日 本的一般的劳动合同形式分类:

​ 2.1.正社员

​ 2.2.契约社员

​ 2.3.派遣社员

​ 2.4.个人事业主

​ 一般来说,因为这种从国内招人到日本的公司都是小规模的SES公司,所以一般还拿不到派遣资质;然而,正社员招聘的各种成本高 昂,比如税务、保险等方面的花费很高;所以一般的中国人的老板从国内招聘IT技术员过来的劳动合同形式都是契约社员。

​ 殊不知,我这家公司连契约社员都省了,直接签个人事业主的合同,也就是业务委托合同,就等于说公司除了给你这点钱,其他啥都不 管了,简直是狗血至极。要知道,在日本不管是外国人还是日本人做个人事业主的,月收入基本都是50万日元左右打底的,我连人家一 半收入都不到,这不就是狗血至极吗?而且,最终去的现场也都是被派出去的形式,实质上是以派遣社员的方式在工作。

3.个人自费购买国民健康保险、国民年金、自己报税。

4.月薪约25-30+万,视工作经验而定,如工作满5年左右大概可以达到50W月薪。

5.年薪按12个月标准,次月月底发出。

6.全额报销交通费(仅限电车定期票,每月2万日元上限)。

7.无年终奖、无年假,有一点点旅游等福利。

8.不包食宿,大概一个月的花费10万日元左右。

9.没有被派驻到现场正式工作之前60%工资,每周双休+法定假日。

10.每天工作8小时,中午一般休息1小时,带加班费(基本不要指望)

11.每月基本工作时间,各公司各自规定一个属于正常的月工作总时长,例如160-200个小时,每月记录本人工作总时长低于下限值 则扣减相应工资,超出上限值部分则算入加班时间,工作时长在区间内按约定月薪给付,不多不少。反正,拿正常工资不多不少的时候占大多数。

12.每年有一次涨薪机会,最高也就涨个月薪多5万日元左右。

13.自己后面研究了一下,我这家公司抽成率(margin),也就是抽水,中间商赚差价。抽成率差不多40%--50%左右。

14.其实很多游戏规则都是中国人的圈子自己玩死了自己。我后来有面试一家日本的这样的IT派遣公司,

  • 首先人家抽成率会给你说好,比如30%,其实日本派遣业界差不多抽成率都只有20%--30%这个区间,高出去那可真是黑心!
  • 和现场签订的合同会给你看,那么你就知道合同单价,抽成率,项目期间之类的细节。
  • 公司和你不绑定,假如做完一个现场,下个现场最后那个月没找到,而且通过现在这家公司也没找到合适的,你可以同时进行其他公司的现场介绍以及面试,你就不用看着这一家公司脸色,在一根树子上吊死。这里,你是工作签就可以这样。企业内转勤签证不可以。

实际工作现场情况

NEC

大型SIer企业,刚来日本遇到的第一个现场第一个项目就是非常紧迫而恶心的保险方面的项目,这是个二包的项目,就是日本的一包公司接了NEC,我们公司接了这个一包公司的活,所以我们公司派出两个人是属于二包,每天加班到晚上8、9点是很正常的事,有几周经常加班到深夜终电,11点左右,真是恶心至极,上司是一包公司的日本人,性格古怪而且不吃早饭不吃中午饭,另外这团队里的日本人同事也是那种宅男型的二货,沟通交流感觉很费劲,喜欢划船,推卸责任,着实恶心了一把。

然后

不过,不管怎样感谢所以的一切成就了我的今天。然后就没有然后了,前面现场完了之后就一直待机,因为新冠肺炎,待在家根本就是40%工资了,待机2个月后面就不发钱了,简直恶心至极。而且因为是业务委托合同,労働基準監督署管不了这个事,找律师标的太小,律师都不敢兴趣。我就开始了转职活动。

40天面试了20次,和猎头面谈了30次,咨询换签证找了行政书士50家。现在等签证变更结果。希望努力到感动自己,拼搏到无能为力。

1 (3).png

感想

1.关于签证的结论:

  • 成人学历可以来日本,成人大专,成人本科,自考大专,自考本科都可以。
  • 但是,如果做IT的,学历上的专业最好是计算机方面的专业。
  • 办工作签困难的情况下,可以考虑企业内转勤,投资经营签证,留学签证,技能研修签证。如果都试了无果可以问我免费解答。
  • 投资经营签证最好准备50万元资金,最好不要投资房地产,而是去开一家公司。
  • 想先好好学下日语的伙伴,留学签证合适,不要信中介的,留学签证很好办,而且啥时候想到了就去着手,不用考虑一年只有两次机会什么的。留学期间可以打工,而且加入没有学历,留学期间再去考个国内的成人学历。
  • 办什么签证都好,找一个律师或者行政书士会事半功倍,前提是要自己信得过的。
  • 申请签证后的那些申请材料自己一定要保存好,你上面填写的工作经历这些,防止下次变更签证什么的不要写错。而且行政书士的好处是事无巨细的会把资料给你整理好,包括申请材料,申请后会告诉你申请的什么,用的哪些材料。这个就是花钱的好处。
  • 行政书士也好,律师也好,申请签证前让你准备资料,准备好资料后会签委托合同,你委托让他代办这件事情,而且事先开具收据给你要收多少钱。然后你再给钱他办事。反正就是你给资料他收钱后提交申请了,签证就成功了80%了,因为材料不对他不会接这活。签证不许可,他会去给你找出原因重新申请或者怎样,都是会帮你做最后努力的。唯一就是你材料造假什么的,那么被拒签他就管不了了。所有材料都没准备,收据也没开,委托合同没签字就让你交定金,或者给钱的都是不正当操作。
  • 办签证不放心可以先办个旅游签证过来日本找好你信任的行政书士,或者确认你要去的公司的真实性。题外话,就是日本有很多侦探事务所,可以调查很多事情,你觉得不好办的事情可以去探侦事务所试试。不会日语找翻译给钱也就搞定啦。
  • 找一家好的公司,签证啥都不用操心了。
  • 给中介费来日本的很多,存在即合理,只要给钱办事还好,给了钱不办事的就当交学费了。中介介绍工作赴日的基本都是人不愿意干的,到时候你就要花更多时间在换工作方面。但,哪有那么完美的呢?有时候想想能来就先来吧。

2.拖家带口可以来日本,两口子都上班交税,日本给小孩有补助,而且免学费。一家人在日本生活更好点。房租水电什么的都均摊了。家人办家族签证就好了,办之前先去国内做一下亲属公证省得后面麻烦。

3.不通过中介也可以来日本,上面也说了存在即合理。通过中介可以让你来日本的日程更具体。但是中介这些鱼龙混杂,而且大多数赚的钱很黑心,间接使得多数赴日小伙伴体验太差。因为做的工作都是很底层的,而且介绍的工作行业和薪资都是拖后腿型的,让你刚来日本真是苦不堪言。

4.只会日语不会IT可以来日本,我很推荐来日本做IT行业,不过现在哪个国家做IT行业都还可以。因为我个人做了这么多年日语业务经验来看,当初踏入IT行业的彷徨,以及进入IT行业后的感受到的好处来看。其实编程方面的入门什么的不难,而且IT行业收入稍微高一些,而且将来做Freelance,也就是自由职业者的吸引力来说,真是一个很推荐的行业。

最后

世上没有最好,只有最适合你!是金子哪里都会发光,但是最适合你发光的岂不更好。

致所有一脚踏入日语这个世界的小伙伴。做IT程序员,翻译,日语业务,品管,财务,工程师,采购等等。

相信这篇文章给了一些日语从业者想来日本的一条出路。

这篇文章肯定得罪很多人,中介,以及各种商家。现在想来,当初就是想空手套白狼,真是想来日本但是有没有任何资金准备,途径也是寥寥无几,凡是都想省着,凡是都想甄别不要被蒙。所以一条路走下来有很多感想,也经历了很多人没有经历的惨状。而且现在想来华南地区做了10来年日语业务,当初就不应该进制造业,活多钱少离家远,位低权轻责任重。一脚踏进IT行业,感觉真是360度大变样。

我的邮箱:1019354192@qq.com

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

Mac Javaホームディレクトリ

Macで、Javaのバージョンアップをする際に、毎回ホームディレクトリの場所を忘れてしまう。

.bashrc に記載している環境変数を見て、「そーだった」と納得しつつ、覚えられない自分がいる。

export JAVA_HOME="/Library/Java/JavaVirtualMachines/adoptopenjdk-14.jdk/Contents/Home"

ついでに brewでのJavaインストールコマンドも残しておきます。

brew tap AdoptOpenJDK/openjdk
brew cask install adoptopenjdk14

https://github.com/AdoptOpenJDK/homebrew-openjdk

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

【Folio LSP】求められる技術的スキル

プロジェクトで募集している人材

具体的なロールと、何時間の稼働が求められるかはこちらConfluenceの募集ページ「Technical Skills in Demand」をご確認ください。

バックエンド開発者

  • Java 8 と オブジェクト指向設計・プログラミング
  • マイクロサービスモデル(RAML, JSON, XML)でのRESTful interfacesの実装
  • データベース(SQLとNoSQLの両方) 特にPostgreSQL
  • 仮想化– Docker,Vagrant

フロントエンド開発者

  • クライアントサイド Javascript (React) と関連するテストツール/フレームワーク

フロントエンド開発者・バックエンド開発者共通

  • テストツール/フレームワーク
  • CI/CD – Jenkins
  • サーバーサイドJavaScript (Node JS)と関連するテストツール/フレームワーク
  • セキュリティ– SSO, OAUTH, SAML, LDAP等
  • ソフトウェア構成管理(SCM) - Git/GitHub

DevOpsエンジニア

※Folioで使っているツールセットを使って、CI/CD環境をコーディングするエンジニア

  • ソフトウェア構成管理(SCM) - Git/GitHub
  • CI/CD – Jenkins
  • 仮想化– Docker, Vagrant
  • インフラ自動化 – Ansible, Puppet, Chef
  • オーケストレーション– Kubernetes/ECS, Rancher
  • セキュリティ-- – SSO, OAUTH, SAML, LDAP
  • クラウド - AWS, GoogleCloud

システム管理者

  • Linuxベースシステムの管理
  • Slack/Github/Confluence/JIRAのためのアプリケーション管理(ユーザ、グループ、アプリケーション設定、ライセンシングなど)

参考

https://wiki.folio.org/display/TC/Technical+Skills+in+Demand

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

【Folio LSP】プロジェクトで必要な技術的スキル

プロジェクトで必要としている人材

具体的なロールと、何時間の稼働が求められるかはこちらConfluenceの募集ページ「Technical Skills in Demand」をご確認ください。

バックエンド開発者

  • Java 8 と オブジェクト指向設計・プログラミング
  • マイクロサービスモデル(RAML, JSON, XML)でのRESTful interfacesの実装
  • データベース(SQLとNoSQLの両方) 特にPostgreSQL
  • 仮想化– Docker,Vagrant

フロントエンド開発者

  • クライアントサイド Javascript (React) と関連するテストツール/フレームワーク

フロントエンド開発者・バックエンド開発者共通

  • テストツール/フレームワーク
  • CI/CD – Jenkins
  • サーバーサイドJavaScript (Node JS)と関連するテストツール/フレームワーク
  • セキュリティ– SSO, OAUTH, SAML, LDAP等
  • ソフトウェア構成管理(SCM) - Git/GitHub

DevOpsエンジニア

※Folioで使っているツールセットを使って、CI/CD環境をコーディングするエンジニア

  • ソフトウェア構成管理(SCM) - Git/GitHub
  • CI/CD – Jenkins
  • 仮想化– Docker, Vagrant
  • インフラ自動化 – Ansible, Puppet, Chef
  • オーケストレーション– Kubernetes/ECS, Rancher
  • セキュリティ-- – SSO, OAUTH, SAML, LDAP
  • クラウド - AWS, GoogleCloud

システム管理者

  • Linuxベースシステムの管理
  • Slack/Github/Confluence/JIRAのためのアプリケーション管理(ユーザ、グループ、アプリケーション設定、ライセンシングなど)

参考

https://wiki.folio.org/display/TC/Technical+Skills+in+Demand

変更履歴

2020年7月5日 新規作成

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

【Folio LSP】Okapi-Stripes Platformとは

チュートリアルの前提となる知識を書いていきます。
ざっと読んでからチュートリアルに進んだほうがいいかもしれない。

Okapi-Stripes Platformとは

  • Folio LSPプロジェクトのインフラストラクチャとして開発されたもので、Okapi、Stripesとその他のコンポーネントで構成されています。
  • OSみたいなもので、Folioの基本的な部分です。

Okapi-Stripes Platformのいいところ

  • マルチテナント
  • 拡張可能(テナントがそれぞれ、ニーズに合わせてアプリを選択できる)

Okapi-Stripes Platformの決まりごと

  • 機能をアプリに分割すること。例:請求書のアプリ、ベンダーのアプリ、支払いのアプリ など
  • 個々のアプリはそれぞれビジネスロジックとデータストレージを持っています。
  • アプリ同士は、APIを通じて通信します。※APIは同じバージョンでないといけない

逆を言うと、他のアプリが持っているデータを、SQLとかで直接データを取るようなアプリはダメよ、ということです。

Okapi-Stripes Platformのコンポーネント

①ランタイム・コンポーネントとフレームワーク

  • バックエンドは、マイクロサービス・アーキテクチャで、Okapiと呼ばれています。
  • マイクロサービスのAPI Gatewayパターンを採用しています。
  • RAMLモジュールビルダー(RMB)フレームワークを使ってOkapiを作成・保守します。

②基本的なアプリケーションのビルディングブロック

Okapi-Stripes Platformが提供する機能

  • mod-users: システム内のユーザーの詳細の管理
  • mod-permissions: 要求された操作が許可されているかどうかを判断する機能
  • mod-authtoken: セッション管理
  • mod-configuration: 構成管理

③ソフトウェア開発のライフサイクルのプラクティスとユーティリティ

  • モダンな開発をします。
  • 以下のプラクティスが、インフラ管理コード(folio-infrastructure、folio-ansible、folio-install)とドキュメントに書かれています。
    • 継続的インテグレーション/継続的デリバリーパイプライン(CI/CD)
    • 自動テスト
    • ツール標準化
    • 期待されるコードカバレッジ
    • デプロイメントパターン
    • 開発チームが新しいコードや機能を統合できるように確立された環境(多分Dockerとかのこと)
  • プラットフォームでの開発に便利な、以下のCLI(Command Line Tool)があります。
    • Okapi-CLI
    • Stripes-CLI

補足

参考

変更履歴

2020年7月5日 新規作成

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

EclipseからIntelliJへの移行(途中)

はじめに

ついにIntelliJを購入!なんて思ってたんですが....
元々Eclipseを使ってた人向けのキーバインドもあるのですがそれでも完全ではなかったので備忘録代わりにしようと思います。
ついでに個人的な設定情報も書いておきます。

キーコンフィグ

まずは本題のキーコンフィグですが現在でもよく書き換えているので暫定的ですが以下を変更しました。

  • キーマップのプリセットをEclipseに変更
  • エディタアクション
    • フォントサイズを縮小 【Alt + マイナス】
    • フォントサイズを拡大 【Alt + プラス】
  • メインメニュー
    • ファイル
      • 新規 【Ctrl + N】
    • コード
      • コードの整形 【Ctrl + Shift + F】
    • リファクタリング
      • ファイル名変更 【Ctrl + R】
    • プラグイン
      • Java
        • GetterとSetter 【Alt + S, Alt + R】
        • toString 【Alt + S, Alt + S】
      • JavaScript and TypeScript
        • GetterとSetter 【Alt + S, Alt + R】
        • toString 【Alt + S, Alt + S】
      • Kotlin
        • toString 【Alt + S, Alt + S】

こんな感じです。
ちなみに被ったキーコンフィグは削除しています。

ファイル名変更は最近のmacbookのファンクションキー(F1 ~ F12)が押しづらいため Ctrl + Rになってます。
また、フォントサイズに関しては画面サイズの違う開発環境でもすぐ対応できるように設定してあります。

フォント設定

フォント設定についても少し悩んだ部分があったので書いておきます。

Windowsの場合外部フォントは全ユーザにインストールしないと使用できない様です。
ここで1時間ぐらい悩んでました・・・

プラグイン

  • Japanese Language Pack (日本語化)
  • Material Theme UI (マテリアルテーマ)
  • Atom Material Icon (アイコンのマテリアル化)
  • Rainbow Brackets (括弧に色付け)

最後に

ここまで読んでくださってありがとうございます。
現在設定は他にも少しいじってますが大きく変更した点は以上の項目です。

私の個人設定なので参考になるか微妙ですが備忘録として残しておきます。
それではまたどこかで・・・

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