質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

1回答

1651閲覧

JTableを用いたメモ帳の「Save/Open」機能の実装

DaiAoki

総合スコア67

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2015/09/25 14:04

現在、JavaのSwingでJTableを用いたメモ帳を作っています。(前回の質問

文字のフォントや行の追加・削除といった機能の実装ができ、次に「Save/Open」機能の実装をしようとしているのですが、やり方がよくわかりません。
参考書で勉強していて、シリアライズ・デシリアライズといったことが必要になってくると思ったのですが、いまいち理解しきれていない状況です。
参考書を見ながら見様見真似でやってみましたが、うまくいきませんでした。おそらく、writeObject( )の( )内がうまくいっていないのだと思います(他にも原因があるのかもしれません)。

Jave

1FileOutputStream fileStream=new FileOutputStream("保存先ファイル名"); 2ObjectOutputStream os=new ObjectOutputStream(fileStream); 3os.writeObject(ここに何を書けば良いかわかりません); 4os.close( );

一応、writeObjectするクラスはSerializableインタフェースをインプリメントしました。(下のコードでいうとMemo)
そもそも、JTableにおいて、クラスとして保存するべきなのは何なのかといったことも理解しきれていないと思います。

考え方として、JTableの場合、何を保存対象とすれば良いのでしょうか?
今のところのコードの概要としては以下のようになっています。

Java

1public class Memo{ 2 public static void main(String[] args){ 3 new Memo( ).buildGUI( ); 4 } 5 public void buildGUI( ){ 6//ここにUIに関するコードを記述 7 } 8//いくつかのリスナークラスとレンダラークラス 9} 10

または、シリアライズ・デシリアライズといった方法以外で適切なやり方があるのでしょうか?
よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

writeObjectメソッドの引数には、Serializableをimplementsしたクラスのインスタンスを指定します。

DaiAoki30さんのメモ帳でうまく動作するかは分かりませんが、JTableSerializableをimplementsしているので、JTableのインスタンスをそのままwriteObjectできるはずです。
もしくは、Memoの中にJTableインスタンスを持たせても良いです。JTable以外にも保存する項目がある場合はこちらの方が良いでしょう。ただし、Memoに新しく項目を追加すると、Memoクラスの互換性が失われるので注意してください。

ただJTable内にSerializableでない項目が含まれているとダメだと思います。
うまくいかないようでしたら、JTableの中身のシリアライズ可能なオブジェクトだけを別の入れ物(HashMapなど)に移してMemoに保存することになります。
このやり方には一般的な方法はたぶん無いので、独自に考えることになります。

参考リンク:
Javaとシリアライズと互換性 - CLOVER
Java オブジェクト直列化仕様:5 - 直列化可能オブジェクトのバージョン管理

また、本題とは直接関係ありませんが、Java7以降であれば、try-with-resources機能を使って書くほうが良いです。こうすれば、writeObjectでエラーになっても必ずファイルがクローズされます。

lang

1// import省略 2 3// Java7以降 4try (FileOutputStream fileStream = new FileOutputStream("保存先ファイル名"); 5 ObjectOutputStream os = new ObjectOutputStream(fileStream)) { 6 os.writeObject(jtable); 7} 8// os.close( ); 自動でcloseされるので不要 9 10 11// Java6以前 12FileOutputStream fileStream = new FileOutputStream("保存先ファイル名.dat"); 13try { 14 ObjectOutputStream os = new ObjectOutputStream(fileStream); 15 try { 16 os.writeObject(jtable); 17 } finally { 18 os.close(); 19 } 20} finally { 21 fileStream.close(); 22}

投稿2015/09/26 06:12

argius

総合スコア9388

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

DaiAoki

2015/09/26 11:12

いつもご回答いただきありがとうございます。 ひとまず、 public class SaveItemListener implements ActionListener{ public void actionPerformed(ActionEvent a){ try(FileOutputStream fileStream=new FileOutputStream("Memo.ser"); ObjectOutputStream os=new ObjectOutputStream(fileStream)){ os.writeObject(table); } catch(Exception ex){ ex.printStackTrace( ); } } } としたところ、java.io.NotSerializableExceptionというエラーメッセージがでてきました。 このエラーメッセージを調べたところ、シリアライズが正しく実装されていないために起きるものだということでしたので、JTableのインスタンスであるtableのままだとうまくいかないようでした。 table以外は保存する必要がないため、何とかtableで保存したいと考えているのですが、この場合、tableに関連する例えばレンダラーといったクラスもSerializableをimplementsしなければならないのでしょうか? ここらへんの知識が全く備わっていないと自覚しているので、この機会にargiusさんがあげてくださったサイトで体系的に勉強していきたいと思います。
argius

2015/09/26 11:40 編集

ひとつ忘れていました。 以前ご提案させていただいたMap方式ですが、少なくともフィールドがMap<Point, Font> map = new HashMap<>();の状態だとシリアライズできません。 MapというインターフェイスはSerializableをextendsしていないからです。(インターフェイスなのでimplementsではない) シリアライズ可能にするには、こちらもHashMapにする必要があります。(HashMap<Point, Font> map = new HashMap<>();) また、Map内に格納されるオブジェクトもすべてシリアライズ可能である必要があります。(幸い、PointもFontもシリアライズ可能ですね。) 保存・復元しなくても良いフィールドは、transient修飾子をつけることで、シリアライズ対象外となります。
DaiAoki

2015/09/26 13:32

ご回答ありがとうございます。 MapをHashMapに変更して試してみましたが、やはりjava.io.NotSerializableExceptionというエラーが表示されてしまいました。 リスナーなど、Serializableをimplementsできそうなクラスは全てやってみて試してみましたがダメでした。 レンダラークラスの中にColorのインスタンスもあるのですが、こちらはSerielizableをimplementsする必要はあるのでしょうか?また、そのクラスがシリアライズ可能かどうかを判断する方法というのはあるのでしょうか?
eripong

2015/09/26 13:37

横から失礼します。 NotSerializableException: の後に、Serializableでないクラスのクラス名が出力されるので、 そのクラスをSerializableにすれば良いはずです。
argius

2015/09/26 13:45

eripongさんの書かれているように、おそらくエラーメッセージに問題のクラス名が出ると思います。 ※エラーメッセージはある程度多くの部分を書かないと問題解決に支障が出ますので注意しましょう。 > シリアライズ可能かどうかを判断する方法というのはあるのでしょうか? 標準クラスのことを言ってるのなら、APIドキュメントの「すべての実装されたインタフェース」のところにSerielizableがあればシリアライズ可能です。(Colorはimplements Serializableあり) 自作クラスのことなら、前述したとおり、transient修飾子が付いていないフィールドがすべてシリアライズ可能であれば、シリアライズ可能なはずです。(スーパークラスがあれば当然そのスーパークラスのフィールドもすべてそうなっている必要がある。)
DaiAoki

2015/09/26 15:26

>eripongさん >argiusさん ご回答ありがとうございます。 NotSerializableExceptionの後にとてつもなく長いエラーメッセージが表示されています。 (以下に全て記載します) java.io.NotSerializableException: com.apple.laf.AquaPainter$AquaSingleImagePainter at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:441) at javax.swing.JComponent.writeObject(JComponent.java:5524) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:441) at javax.swing.table.JTableHeader.writeObject(JTableHeader.java:703) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at javax.swing.event.EventListenerList.writeObject(EventListenerList.java:259) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:441) at javax.swing.JTable.writeObject(JTable.java:5832) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at ForSera.Memo$SaveItemListener.actionPerformed(Memo.java:270) at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022) at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2346) at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402) at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259) at javax.swing.AbstractButton.doClick(AbstractButton.java:376) at javax.swing.plaf.basic.BasicMenuItemUI.doClick(BasicMenuItemUI.java:833) at com.apple.laf.AquaMenuItemUI.doClick(AquaMenuItemUI.java:157) at javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(BasicMenuItemUI.java:877) at java.awt.Component.processMouseEvent(Component.java:6525) at javax.swing.JComponent.processMouseEvent(JComponent.java:3324) at java.awt.Component.processEvent(Component.java:6290) at java.awt.Container.processEvent(Container.java:2234) at java.awt.Component.dispatchEventImpl(Component.java:4881) at java.awt.Container.dispatchEventImpl(Container.java:2292) at java.awt.Component.dispatchEvent(Component.java:4703) at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4898) at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4533) at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4462) at java.awt.Container.dispatchEventImpl(Container.java:2278) at java.awt.Window.dispatchEventImpl(Window.java:2750) at java.awt.Component.dispatchEvent(Component.java:4703) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758) at java.awt.EventQueue.access$500(EventQueue.java:97) at java.awt.EventQueue$3.run(EventQueue.java:709) at java.awt.EventQueue$3.run(EventQueue.java:703) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:86) at java.awt.EventQueue$4.run(EventQueue.java:731) at java.awt.EventQueue$4.run(EventQueue.java:729) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:75) at java.awt.EventQueue.dispatchEvent(EventQueue.java:728) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93) at java.awt.EventDispatchThread.run(EventDispatchThread.java:82) 「at〜」となっているのがいわゆるエラー箇所という意味でしょうか? お恥ずかしいながら、あまりこれらのメッセージを気に留めたことがなかったので、どのように解釈すれば良いかわかりません。 エラーメッセージに構文のようなものはあるのでしょうか?
argius

2015/09/26 15:43

atのところは「スタックトレース」というものです。 メソッドの呼び出し履歴が最初の呼び出し元から逆順に出力されます。 詳しくは「java スタックトレースの読み方」などで検索してみてください。 場合によってはatの箇所が必要になることもありますが、 今回の場合はcom.apple.laf.AquaPainter$AquaSingleImagePainterが重要です。 例外が発生した場合、例外のクラス名の後ろに大抵なにかそのエラーに関連するメッセージが付いてきます。この例外は「インスタンスが直列化可能インタフェースを持つ必要がある場合にスローされます。」(APIドキュメント参照)なので、直列化可能インタフェースを持っていないクラス名が出力されているわけです。 これが、eripongさんのおっしゃってたことです。 で、AquaSingleImagePainterは他人のクラスなのでSerializable付けられませんね。おまけにMacでだけ使われるクラスです。 中身を移すしかなさそうです。
eripong

2015/09/26 15:57

この場合だと、com.apple.laf.AquaPainter$AquaSingleImagePainterが Serializableでは無い、と言っています。 これが出るとなると、一つ一つ対応していくのは大変と思うので、 tableを丸ごとserializeするのは諦めた方が良いかも知れません。 また、このエラーメッセージは、例外スタックトレースと呼びます。 Javaのデバッグでは、非常に有用なものなので、読み方は知っておいた方が良いです。 「スタックトレースの見方」などでGoogle検索すると、解説記事が出てきますよ。 http://d.hatena.ne.jp/wyukawa/20090301/1235914636 http://d.hatena.ne.jp/jflute/20090224/1235473382 などです。
DaiAoki

2015/09/26 16:31

ご回答ありがとうございます。 スタックトレースの読み方について調べてきました。 少し自信がない箇所があるので、理解が正しいか確認していただいてもよろしいでしょうか? ① 例えば、 at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) の1184というのは、ObjectOutputStreamクラスの1184行目に問題があるということを示しているのでしょうか? ② また、eripongさんがおっしゃっていたことというのは、 java.io.NotSerializableExceptionの場合は「:」の後ろにシリアライズ化されていないクラス名が出るということでしょうか?(今回の場合、com.apple.laf.AquaPainter$AquaSingleImagePainter) これが、他の例外になった場合は、:の持つ意味も変わってくるのでしょうか? ③ AquaSingleImagePainterというクラスを明示的に使用していないのですが、なぜこのクラスがコードに含まれると判断されたのでしょうか?Macのデフォルトの設定か何かなのでしょうか? 質問が多岐にわたってしまい申し訳ございません。 よろしくお願いします。
argius

2015/09/26 16:44

① シリアライズ不可能という問題が発生したのは確かですが、一番上のatが意味するのは「例外がスローされた場所」になります。 ここでは、シリアライズ不可能なオブジェクトをwriteObjectしようとしたために例外をスローしています。 ② コロン(:)自体は(たぶん)英語の文法によるもので、大抵の場合はコロンの右は左のものを説明する文章が入ります。 スタックトレースでは、ほとんどの場合、「例外クラス名:説明」になりますが、説明が無い例外もあります。 ③ コードに含まれているわけでは無く、使用している、です。 クラス名の先頭がcom.apple.laf.AquaXXXとなっていることから、JavaGUI(Swing) APIのMac用の実装に含まれるクラスと推測しました。
DaiAoki

2015/09/27 09:48

ご回答ありがとうございます。 うまくいかない原因がつかめました。 writeObjectするクラス(インスタンス)内にあるものはシリアライズしなければならないのですね。また、今回のようにMac用のクラスなどが裏で働いているということも理解しました。 実装するにあたって、JTableのインスタンスまるごとでは厳しいということでしたので、セルの情報を一度MapやListに格納して、そのMap(List)のインスタンスを保存するという形で考えていきたいと思います。 少し時間がかかるかもしれませんが、コードを書いたらまた来ます。
DaiAoki

2015/09/30 13:50 編集

遅くなってしまい申し訳ございません。 コレクションフレームワークのどれを使うべきかということで最初に悩んで、ArrayListを使うことにしました。順番通り格納されるということでしたので、セルの情報が前後しないと思ったからです。うまくいくかどうかは実装してみなければわかりませんが・・・。 コードの主な流れとしては、 ①ArrayList<Component> list=new ArrayList<Component>( );と宣言する ②JTableの各セルの情報をlist.add(セル情報);で追加する 未完成なコードではありますが、以下のように考えてみました。 for(int i=0 ; i<table.getColumnCount( ) ; i++){ list.add(行がi,列が1のセル); list.add(行がi,列が2のセル); } 私の作ろうとしているメモ帳は列数が2で固定で、行数はユーザーが追加ボタンや削除ボタンを押すことで変化します。 そこで、getColumnCount( )で行数を受け取り、その分だけaddを繰り返すことで理論上は保存できるようになったかに思えるのですが、「行がi、列が1のセル」の情報をどのように引数として渡すのかがわかりませんでした。 そのような方法がないのならそもそも考え方自体を変えなければならないことになってくると思うのですが・・・ ご教授いただければ幸いです。
argius

2015/09/30 14:22

ArrayListは、配列ではないけれど配列のように要素0,要素1,要素2...要素nと要素を持つことはでき、配列と違ってサイズを自動的に増やすことはできますが、それ以外の情報は持つことができませんので、独自の「列単位のクラス」を作った方が良いと思います。 例えば、RowDataというクラスにして、フィールドはComponent comp1, comp2にして、ArrayList<RowData>で持つようにすればできそうです。
DaiAoki

2015/09/30 15:25

ご回答ありがとうございます。 argiusさんのご回答の中で、理解できたか不安な部分がいくつかあるので確認してもよろしいでしょうか。 >それ以外の情報は持つことができませんので、独自の「列単位のクラス」を作った方が良いと思います。 それ以外の情報というのは、どういう意味でしょうか? >例えば、RowDataというクラスにして、フィールドはComponent comp1, comp2にして、ArrayList<RowData>で持つようにすればできそうです。 最終的にwriteObjectするインスタンスはArrayListになり、そのArrayListの中に列ごとのデータを格納したRowDateを格納するといった意味で捉えたのですが、その場合、わざわざArrayListに格納せずとも、RowDateクラスにSerializableをimplementsし、RowDateクラスのインスタンスをwriteObjectすれば良いと思いました。 ですので、こちらも自分が何か理解する上で勘違いしている部分があるかと思います。
argius

2015/09/30 16:33

すみません、読み違えていました。2015/09/30 23:22のコメントは無視してください。 それと、割と特殊なことをされているので、この辺になると、もう決まったやり方というのは無いですし、やってみないと分からないことも多いので、不確実な内容になることをご了承ください。 「行がi、列が1のセル」の情報は、CellInfoのようなクラスを作って、その中に保存したい情報をコピーするようにする、というごく普通のやり方になると思います。コピーする情報はもちろんシリアライズ可能なものだけにします。 JTable自体を保存できない以上、これしかないでしょうね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問