米GoogleがAndroidでJavaの著作権を侵害しているとして米Oracleが2010年8月に提訴した裁判で、米連邦最高裁判所は4月5日(現地時間)、Googleは著作権を侵害しなかったとの判断を下した。
GoogleがOracleとの10年越しの裁判で勝訴 最高裁はJava著作権侵害せずの判断 - ITmedia NEWS
⇧ う~ん、エンジニアからしてみたら、APIのドキュメントをちゃんとしてくれてるんであれば他に何もいらないって思っちゃいますけどね。
そして、
マイクロソフトは同社独自のOpenJDKディストリビューションとなる「Microsoft Build of OpenJDK」のプレビューリリースを発表しました。
マイクロソフトが無償でJavaの長期サポートを提供へ、「Microsoft Build of OpenJDK」をリリース - Publickey
⇧ 絶妙なタイミングですね、Microsoft様。
というわけで、今回はJavaの話です。
レッツトライ~。
file URI schemeって?
Wikipediaさんに聞いてみた。
The File URI Scheme is a URI scheme defined in RFC 8089, typically used to retrieve files from within one's own computer.
Previously the file URI scheme was specified in RFC 1630 and RFC 1738. The Internet Engineering Task Force (IETF) published RFC 8089, updating the latter RFC, with "a syntax based on the generic syntax of RFC 3986 that is compatible with most existing usages."
⇧ ってな感じで、「The File URI Scheme is a URI scheme」 ってところからして、What's なんだけどな...
Uniform Resource Identifier (URI) Schemes
Registration Procedure(s)
Expert Review for Permanent and Historical registrations, First Come First Served for Provisional registrationsExpert(s)
Graham KlyneReference[RFC7595][RFC Errata 4420][RFC8615]Note
Requests for permanent registration must be preceded by mailing list review, per Section 7.2 of [RFC7595].
Available Formats
CSV
https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
⇧ ってなってるんで、「RFC 7595」を確認してみたところ、
「URI scheme」自体は「RFC 3986」で定義されてるらしい。
1. Introduction
The Uniform Resource Identifier (URI) protocol element and generic syntax is defined by [RFC3986]. Each URI begins with a scheme name, as defined by Section 3.1 of RFC 3986, that refers to a specification for identifiers within that scheme. The URI syntax provides a federated and extensible naming system, where each scheme's specification can further restrict the syntax and define the semantics of identifiers using that scheme.
⇧「RFC 7595」は「URI scheme」の登録方法なんかについての情報が載ってるらしい、というか「RFC 3986」で「URI scheme」が定義されてからの議論の多さが...
で、その「scheme」ってどんだけあんの?
This article lists common URI schemes. A Uniform Resource Identifier helps identify a source without ambiguity. Many URI schemes are registered with the IANA; however, there exist many unofficial URI schemes as well. Mobile deep links are one example of a class of unofficial URI schemes that allow for linking directly to a specific location in a mobile app.
⇧ はい、出ました。「非公式のスキーマも複数存在します」って、本当に止めて欲しい...これ、誰かちゃんと把握してるのかね?
っていうか、「登録方法」云々が出てきたあたりで嫌な予感はしてましたけど...
ちなみに、Javaがサポートしてる「URI scehme」ってどれぐらいあるのかな?って思ってたら、
⇧ 同じような疑問を抱いてる方が。
と、その前に、
- 「URI(Uniform Resource Identifier)」
- 「URL(Uniform Resource Locator)」
の関係って?
⇧ 上図がイメージしやすいかと。
見たまんまで超ザックリまとめると、「URI(Uniform Resource Identifier)」は「URL(Uniform Resource Locator)」を包括してる、ってことみたいですね。
Javaがデフォルトで対応してる「scheme」を確認してみる
stackoverflowの話では「java.protocol.handler.pkgs」っていう「system property」で確認できるんだそうな。
「system property」って?
System Properties
In Properties, we examined the way an application can use Properties
objects to maintain its configuration. The Java platform itself uses a Properties
object to maintain its own configuration. The System
class maintains a Properties
object that describes the configuration of the current working environment. System properties include information about the current user, the current version of the Java runtime, and the character used to separate components of a file path name.
System Properties (The Java™ Tutorials > Essential Classes > The Platform Environment)
⇧ おそらく「System.class」の「Properties」の内の1種ってことなのかな?
そんじゃあ、「Property」って?
Properties
Properties are configuration values managed as key/value pairs. In each pair, the key and value are both String
values. The key identifies, and is used to retrieve, the value, much as a variable name is used to retrieve the variable's value. For example, an application capable of downloading files might use a property named "download.lastDirectory" to keep track of the directory used for the last download.
Properties (The Java™ Tutorials > Essential Classes > The Platform Environment)
⇧ う~ん、サッパリ分からんですな...
To manage properties, create instances of java.util.Properties
. This class provides methods for the following:
- loading key/value pairs into a
Properties
object from a stream, - retrieving a value from its key,
- listing the keys and their values,
- enumerating over the keys, and
- saving the properties to a stream.
Properties (The Java™ Tutorials > Essential Classes > The Platform Environment)
⇧ 1つだけ分かったのは、「java.util.Properties
」をインスタンス化すれば、「properties」を管理できるってことかね。
The System
class maintains a Properties
object that defines the configuration of the current working environment. For more about these properties, see System Properties. The remainder of this section explains how to use properties to manage application configuration.
Properties (The Java™ Tutorials > Essential Classes > The Platform Environment)
⇧ まぁ「defines the current working enviroment」ってのが何のことを言ってるのかが全く分からんのだけど、何かの設定値ってことみたいね。
Javaのアプリケーションでは、
Properties in the Application Life Cycle
The following figure illustrates how a typical application might manage its configuration data with a Properties
object over the course of its execution.
Properties (The Java™ Tutorials > Essential Classes > The Platform Environment)
Starting Up
The actions given in the first three boxes occur when the application is starting up. First, the application loads the default properties from a well-known location into aProperties
object. Normally, the default properties are stored in a file on disk along with the.class
and other resource files for the application.
Next, the application creates anotherProperties
object and loads the properties that were saved from the last time the application was run. Many applications store properties on a per-user basis, so the properties loaded in this step are usually in a specific file in a particular directory maintained by this application in the user's home directory. Finally, the application uses the default and remembered properties to initialize itself.
The key here is consistency. The application must always load and save properties to the same location so that it can find them the next time it's executed.Running
During the execution of the application, the user may change some settings, perhaps in a Preferences window, and theProperties
object is updated to reflect these changes. If the users changes are to be remembered in future sessions, they must be saved.Exiting
Upon exiting, the application saves the properties to its well-known location, to be loaded again when the application is next started up.
Properties (The Java™ Tutorials > Essential Classes > The Platform Environment)
⇧上図のような感じで「Properties object」が利用されるってことみたいね。
Google翻訳した感じでは、
- First, the application loads the default properties from a well-known location into a
Properties
object. - Next, the application creates another
Properties
object and loads the properties that were saved from the last time the application was run.
の少なくとも2つの「Properties object」が利用されるっぽいですかね。
で、話は戻って、「java.protocol.handler.pkgs」って「system property」を確認すれば、Javaがデフォルトで扱ってる「scheme」が確認できるし、
⇧ 「カスタムプロトコル」ってことで「scheme」を追加できるみたいですね。
⇧ ってことで、確認してみました。
package java_one_hundred_nock.seventynine; import java.util.Properties; public class FileSaveDone { public static void main(String[] args) { // TODO 自動生成されたメソッド・スタブ Properties properties = System.getProperties(); properties.list(System.out); } }
で、実行結果が、
-- listing properties -- sun.desktop=windows awt.toolkit=sun.awt.windows.WToolkit java.specification.version=11 sun.cpu.isalist=amd64 sun.jnu.encoding=MS932 java.class.path=C:\Eclipse-2020-06\pleiades-2020-06-j... java.vm.vendor=AdoptOpenJDK sun.arch.data.model=64 user.variant= java.vendor.url=https://adoptopenjdk.net/ user.timezone= os.name=Windows 10 java.vm.specification.version=11 sun.java.launcher=SUN_STANDARD user.country=JP sun.boot.library.path=C:\Eclipse-2020-06\pleiades-2020-06-j... sun.java.command=java_one_hundred_nock.seventynine.Fil... jdk.debug=release sun.cpu.endian=little user.home=C:\Users\Toshinobu user.language=ja java.specification.vendor=Oracle Corporation java.version.date=2020-04-14 java.home=C:\Eclipse-2020-06\pleiades-2020-06-j... file.separator=\ java.vm.compressedOopsMode=32-bit line.separator= java.specification.name=Java Platform API Specification java.vm.specification.vendor=Oracle Corporation java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment user.script= sun.management.compiler=HotSpot 64-Bit Tiered Compilers java.runtime.version=11.0.7+10 user.name=Toshinobu path.separator=; os.version=10.0 java.runtime.name=OpenJDK Runtime Environment file.encoding=UTF-8 java.vm.name=OpenJDK 64-Bit Server VM java.vendor.version=AdoptOpenJDK java.vendor.url.bug=https://github.com/AdoptOpenJDK/openj... java.io.tmpdir=C:\Users\TOSHIN~1\AppData\Local\Temp\ java.version=11.0.7 user.dir=C:\Eclipse-2020-06\pleiades-2020-06-j... os.arch=amd64 java.vm.specification.name=Java Virtual Machine Specification java.awt.printerjob=sun.awt.windows.WPrinterJob sun.os.patch.level= java.library.path=C:\Eclipse-2020-06\pleiades-2020-06-j... java.vendor=AdoptOpenJDK java.vm.info=mixed mode java.vm.version=11.0.7+10 sun.io.unicode.encoding=UnicodeLittle java.class.version=55.0
⇧ というわけで、「java.protocol.handler.pkgs」ていう「key」の「property」なんて存在しないんですけど...
どういうこと?
「ドキュメント」を見る限りでは、
指定されたプロトコルで最初にURLオブジェクトを生成するときには、そのプロトコルのためのストリーム・プロトコル・ハンドラ・オブジェクトが生成されます。このストリーム・プロトコル・ハンドラ・オブジェクトは、URLStreamHandler
クラスのインスタンスです。
- 以前にアプリケーションが
URLStreamHandlerFactory
のインスタンスをストリーム・ハンドラ・ファクトリとして設定している場合は、そのインスタンスのcreateURLStreamHandler
メソッドがプロトコル文字列を引数として呼び出されて、ストリーム・プロトコル・ハンドラを作成する。 URLStreamHandlerFactory
がまだ設定されていない場合、またはcreateURLStreamHandler
メソッドがnull
を返した場合は、ServiceLoaderメカニズムを使用して、システム・クラス・ローダーを使用してURLStreamHandlerProvider実装を特定します。 プロバイダが配置される順序は実装固有であり、実装は配置されたプロバイダを自由にキャッシュできます。createURLStreamHandler
からスローされたServiceConfigurationError、Error
またはRuntimeException
が発生した場合は、呼び出しスレッドに伝播されます。 各プロバイダのcreateURLStreamHandler
メソッド(インスタンス化されている場合)は、プロバイダがnull以外を返すか、すべてのプロバイダが使い果たされるまで、プロトコル文字列とともに呼び出されます。- 前のステップでプロトコル・ハンドラが見つからない場合、コンストラクタはシステム・プロパティの値を読み取ります:
このシステム・プロパティの値がjava.protocol.handler.pkgs
null
でなければ、値は、垂直スラッシュ文字「|
」で区切られた、パッケージのリストとして解釈される。 コンストラクタは次の名前を持つクラスをロードしようとする。
ここで、<package>.<protocol>.Handler
<package>
はパッケージの名前に置き換えられ、<protocol>
はプロトコルの名前に置き換えられます。 このクラスが存在しない場合、あるいはクラスは存在してもそれがURLStreamHandler
のサブクラスではない場合には、リストにある次のパッケージを試すことになる。 - 前のステップでプロトコル・ハンドラが見つからない場合、コンストラクタは組み込みのプロトコル・ハンドラをロードしようとします。 このクラスが存在しない場合、またはクラスは存在するが
URLStreamHandler
のサブクラスではない場合は、MalformedURLException
がスローされる。
次のプロトコルのプロトコル・ハンドラは、検索パス上に存在することが保証されています。
http, https, file, and jar
その他のプロトコルのプロトコル・ハンドラも使用可能になっている可能性があります。 クラスパス上のプラットフォーム・クラスまたはクラスをロードするために使用されるプロトコル・ハンドラなどの一部のプロトコル・ハンドラは、オーバーライドできません。 そのような制限の詳細、およびそれらの制限が(実行時の初期化中など)に適用される場合、実装固有であり、したがって指定されていない
⇧「java.protocol.handler.pkgs」っていう「システム・プロパティ」が存在するって言ってますな。
で、
⇧「System.getProperties()」の一覧に「java.protocol.handler.pkgs」が存在しないんだけど...
何すか、もう、これ「ドキュメント」からデフォルトの「プロトコル・ハンドラ(「scheme」なことだと思うけど)」を転記してコーディングしろってこと?
痛恨過ぎるでしょ...
まさか「AdoptOpenJDK」だからってことないよね?
ちなみに「AdoptOpenJDK」は「Eclipse Foundation」に寄贈されて「Eclipse Adoptium」になるらしい。
Eclipse Foundationは、AdoptOpenJDK Technical Steering Committeeとの協力による、Adoptiumワーキンググループの正式な設立を3月23日(現地時間)に発表した。
Eclipse Foundation、企業向けJavaランタイムのマルチベンダー配信のためのAdoptiumワーキンググループを設立:CodeZine(コードジン)
⇧ 2021年3月23日(現地時間)ってことは、結構、最近の話よね。
すみません、脱線しましたが、まぁ、Javaがデフォルト対応してる「scheme」についてコーディングで取得できないみたいなんですが、Java 11のドキュメント(「Oracle JDK」のドキュメントだと思うけど)を見る限りでは、
- http
- https
- file
- jar
の4つがデフォルトで対応してる「scheme」ってことみたいね。
Javaっていうと、
- JAR(Java ARchive)
- WAR (Web application ARchive)
- EAR(Enterprise ARchive)
の圧縮ファイルがあるっぽいんだけど、「jar」以外の圧縮ファイルを対応させたい場合は、「scheme」を追加してあげる必要があるってことですかね。
まぁ、このあたりは、フレームワークなんかが良しなにやってくれるんですかね?
それにしても、Javaのバージョンをどんどん上げていくんなら、どの「システム・プロパティ」が取得できるのかできないかの記載をちゃんとして欲しいよね...
取得できないんならできないんで良いんだけど、明示して欲しい...
そして、まさかの
⇧ 1つのファイルパスで複数の「scheme」が混在してることがあるケースがあるみたいね...いや~地獄ですな...
java.nioだとエラー出るやん...
何か、ネットの情報を見ると、『「java.io」でコーディングなんてマジでイケてない、これからは「java.nio」でしょ』とか宣う方々がおられたんですが、普通に「java.nio」だと対応しきれない場合があるみたいね...
で、ファイルのパスとかも「文字列」で受け取ることもあるあるじゃないですか。
Fo checking if it's a local file you can simply do this:
public static boolean isLocalFile(String path) {
return new File(path).exists();
}
To check if it's a url Cameron Ketcham's answer is the best one.
https://stackoverflow.com/questions/6575114/how-do-i-determine-whether-a-path-is-a-local-file-or-not
⇧ 上記サイト様を見た感じ、
- local file ←ローカルファイル
- or not ←ローカルファイル以外
ってことを判定しようとすると、「java.nio」だと何か無理っぽい気がするんですよね。
でもって、「java.io」パッケージを使って「new File(path).exists()」とかすれば判定できるよ、って仰ってる方がおられたのですが、確かに「ローカルファイル」が存在する場合は良いのですが、「ローカルファイル」が存在しない場合のパターンが判断できないっぽい気がするんよね...。
どういうことかと言うと、例えば、
- 実在するファイル
- http://example.com/folder/file.png
- C:\temp\test.txt
- \\host\public\file.html
- 実在しないファイル
- http://example.com/folder/error.png
- C:\temp\popo.txt
- file.html
というようなファイルパスの文字列があったとして、
- 「ローカルファイル以外のファイルが存在するでFalse」
- 「ローカルファイル以外のファイルが存在しなくてFalse」
- 「ローカルファイルが存在しなくてFalse」
という感じで、「False」の内容の区別ができませんと。(「False」としか返してくれないからね...)
じゃあ、どうするの?
これについては、私自身がJavaの有識者ってわけではないので、何とも言えないですが、「文字列」から判断するってことをするしかないんじゃないかと。
つまり、『この文字や文字列が先頭に含まれてたら~』、って感じで判断していくしかなさそう。
まぁ、「ファイルパス」の文字列が、ちゃんとしたフォーマットになって渡されるって担保されてるような環境であれば、悩まなくても済む話だとは思うんだけど、実際問題、開発現場とかだとどうなのかね?
幸い、Javaがデフォルトで対応してる「scheme」が『http』『https』『file』『jar』の4つしか存在しないらしいので、チェックリストっぽいものは作れそうですと。
のパッケージのAPIを組み合わせていけば、「ローカルファイル」「ローカルファイル以外」とか混在してるファイルパスに対応できるんじゃないかと。
とりあえずは、Windowsの場合でコーディングしてみる。(他のOSのファイルパスのパターンが分からないので...というかWindowsもファイルパスのパターンの全量も分からんけども...)
■/java_one_hundred_nock/src/java_one_hundred_nock/seventynine/FileSave.java
package java_one_hundred_nock.seventynine; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.List; public class FileSave { private static String DEFAULT_ERROR_TXT = "error.txt"; public static void saveUrlFiles(List<String> getFileList, String saveFolder) { List<String> errorFilePathList = new ArrayList<>(); getFileList.forEach(filePath ->{ try { if ("file".equals(filePath.split(":")[0])) { saveOtherFile(filePath, saveFolder, errorFilePathList); } else { URL url = new URL(filePath); if (url != null) { Files.copy(url.openStream(), Paths.get(saveFolder + Paths.get(url.getPath().substring(url.getPath().lastIndexOf("/")+1)).getFileName()), StandardCopyOption.REPLACE_EXISTING); } } } catch (MalformedURLException e) { // TODO 自動生成された catch ブロック if (Files.notExists(Paths.get(saveFolder + DEFAULT_ERROR_TXT))) { try { Files.createFile(Paths.get(saveFolder + DEFAULT_ERROR_TXT)); } catch (IOException e1) { // TODO 自動生成された catch ブロック e1.printStackTrace(); } } //e.printStackTrace(); errorFilePathList.add(filePath); } catch (IOException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } writeErrorFilePath(errorFilePathList, saveFolder); }); } public static void saveOtherFiles(List<String> getFileList, String saveFolder) { List<String> errorFilePathList = new ArrayList<>(); getFileList.forEach(filePath ->{ saveOtherFile(filePath, saveFolder, errorFilePathList); writeErrorFilePath(errorFilePathList, saveFolder); }); } private static void saveOtherFile(String filePath, String saveFolder, List<String> errorFilePathList) { if (new File(filePath).exists()) { try { Files.copy(Paths.get(filePath), Paths.get(saveFolder + Paths.get(filePath).getFileName()), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { // TODO 自動生成された catch ブロック if (Files.notExists(Paths.get(saveFolder + DEFAULT_ERROR_TXT))) { try { Files.createFile(Paths.get(saveFolder + DEFAULT_ERROR_TXT)); } catch (IOException e1) { // TODO 自動生成された catch ブロック e1.printStackTrace(); } } //e.printStackTrace(); errorFilePathList.add(filePath); } } } /** * エラーになるファイルパスを記録 * @param errorFilePathList * @param saveFolder */ private static void writeErrorFilePath(List<String> errorFilePathList, String saveFolder) { if (errorFilePathList.size() > 0) { try { Files.write(Paths.get(saveFolder + DEFAULT_ERROR_TXT), errorFilePathList, Charset.forName("UTF-8"), StandardOpenOption.TRUNCATE_EXISTING); } catch (IOException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } } } }
■/java_one_hundred_nock/src/java_one_hundred_nock/seventynine/FileSaveDone.java
package java_one_hundred_nock.seventynine; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class FileSaveDone { public static void main(String[] args) { // Java SEのデフォルトで対応してる「scheme」 String[] defaultSchemeArr = {"http", "https", "file", "jar"}; List<String> defaultSchemeList = new ArrayList<>(Arrays.asList(defaultSchemeArr)); String saveFolder = "C:\\temp\\"; // 保存先フォルダ // ファイルパスの文字列 String[] fileListArr = { "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Wikipedia-logo-v2.svg/200px-Wikipedia-logo-v2.svg.png", "file://C:\\Users\\Toshinobu\\Desktop\\soft_work\\01_尤度_m05-s20.png", "file://\\\\wsl$\\docker-desktop\\docker-desktop-deploy-version", "file://C:/Users/Toshinobu/Desktop/soft_work/01_尤度_m05-s20.png", "file:////wsl$/docker-desktop/docker-desktop-deploy-version", "ppp://C:/Users/Toshinobu/Desktop/soft_work/01_尤度_m05-s20.png", ":////wsl$/docker-desktop/docker-desktop-deploy-version", "C:/Users/Toshinobu/Desktop/soft_work/01_尤度_m05-s20.png", "//wsl$/docker-desktop/docker-desktop-deploy-version", "C:\\Users\\Toshinobu\\Desktop\\soft_work\\01_尤度_m05-s20.png", "\\\\wsl$\\docker-desktop\\docker-desktop-deploy-version", "/01_尤度_m05-s20.png", "jar://wsl$/docker-desktop/docker-desktop-deploy-version", "jar:////wsl$/docker-desktop/docker-desktop-deploy-version", "jar://C:/Users/Toshinobu/Desktop/soft_work/01_尤度_m05-s20.png", "01_尤度_m05-s20.png" }; // ファイルパスの文字列を「scheme」のあるなしで分けたものを格納する用 Map<Integer, String> urlFilePathMap = new HashMap<>(); List<Map<Integer, String>> urlFilePathList = new ArrayList<>(); Map<Integer, String> otherFilePathMap = new HashMap<>(); List<Map<Integer, String>> otherFilePathList = new ArrayList<>(); // 実際に分ける for (int i = 0; i < fileListArr.length; i++) { // java.net.URLパッケージのAPIで対応するファイルパス if (defaultSchemeList.contains(fileListArr[i].split(":")[0].toLowerCase())) { urlFilePathMap.put(i, fileListArr[i]); urlFilePathList.add(urlFilePathMap); } else { // java.net.URLパッケージのAPI以外で対応するファイルパス otherFilePathMap.put(i, fileListArr[i]); otherFilePathList.add(otherFilePathMap); } } // List<Map<Integer, String>>からList<String>にする List<String> urlFileList = new ArrayList<>(); for (int i =0; i < urlFilePathList.size();) { for (Integer key: urlFilePathList.get(i).keySet()) { urlFileList.add(urlFilePathList.get(i).get(key)); } break; } // List<Map<Integer, String>>からList<String>にする List<String> otherFileList = new ArrayList<>(); for (int i =0; i < otherFilePathList.size();) { for (Integer key: otherFilePathList.get(i).keySet()) { otherFileList.add(otherFilePathList.get(i).get(key)); } break; } FileSave.saveOtherFiles(otherFileList, saveFolder); FileSave.saveUrlFiles(urlFileList, saveFolder); } }
⇧ う~ん...何か収拾のつかない感じになってしまった...
というか、仮に「ファイルパス」が1000万個とかあったら、1000万回new File(filePath)されるかもしれないこともあるって、駄目駄目なコーディングですな...
実行してみると、
⇧ 存在するファイルパスについては、保存先に指定したフォルダに保存され、
⇧「scheme」付きで存在しないファイルパスを「error.txt」に保存、なんだけど、「scheme」が「file://」で存在しないファイルパスと「ローカルファイル」で存在しないファイルパスとか考慮できてないっす...
もちろん、1つのファイルパスに複数「scheme」が混在してる場合も対応できておらず...
って言うか、「ローカルファイル」「ローカルファイル以外」が混在してるファイルパスが存在するかどうかの判定って、どうするのが良いんですかね?
ベストプラクティス的なものを知りたい...
いや~、何か疲れた...
まぁ、一つだけ言えることは、「java.io」「java.nio」云々の前に、「ファイルパス」の文字列をどう処理するかってことのほうが大事な気がするんだけどな...
それとも、通常の業務であれば、絶対確実な「ファイルパス」の形の文字列が来る想定になってるとかで、心配無用なんですかね?
今回はこのへんで。