我刚刚开始学习RMI和JFrame,我被困在一个异常上一段时间了。我正在编写一个客户端/服务器交互,它将访问雅虎数据库中的股票信息。
这是我的代码:
package stockquote;
public class StockQuote{
public double currentPrice, priceChange, dailyLow, dailyHigh;
public StockQuote(double price, double change, double low, double high){
currentPrice = price;
priceChange = change;
dailyLow = low;
dailyHigh = high;
}
public StockQuote(){
currentPrice = 0;
priceChange = 0;
dailyLow = 0;
dailyHigh = 0;
}
}
package stockquote;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface StockQuoteInterface extends Remote{
public StockQuote getQuote(String symbol) throws RemoteException;
}
package stockquote;
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.UnicastRemoteObject;
import java.io.*;
import java.util.Properties;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.rmi.RemoteException;
import java.util.*;
public class StockQuoteServer implements StockQuoteInterface{
public StockQuote getQuote(String symbol) throws RemoteException{
StockQuote information = new StockQuote();
try{
URL url = new URL("http://download.finance.yahoo.com/d/quotes.csv?s=" + symbol + "&f=l1c1hg");
URLConnection conn = url.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String quoteString = in.readLine();
in.close();
String[] data = quoteString.split(",");
information.currentPrice = Double.parseDouble(data[0]);
information.priceChange = Double.parseDouble(data[1]);
information.dailyHigh = Double.parseDouble(data[2]);
information.dailyLow = Double.parseDouble(data[3]);
}catch(Exception e){
e.printStackTrace();
}
return information;
}
public static void main(String[] args) throws Exception {
try {
StockQuoteServer obj = new StockQuoteServer();
StockQuoteInterface stub = (StockQuoteInterface) UnicastRemoteObject.exportObject(obj, 0);
Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
registry.rebind("StockQuoteServer", stub);
System.err.println("StockQuote Server is running.");
} catch (Exception e) {
e.printStackTrace();
}
}
}
package stockquote;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.rmi.*;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.*;
public class StockQuoteClient extends JFrame implements ActionListener{
private static final long serialVersionUID = 1L;
private static StockQuoteInterface stockQuote;
private JTextField symbolField = new JTextField(10);
private JTextField currentPriceField = new JTextField(10);
private JTextField priceChangeField = new JTextField(10);
private JTextField dailyHighField = new JTextField(10);
private JTextField dailyLowField = new JTextField(10);
private JButton lookup = new JButton("Lookup");
public StockQuoteClient() throws RemoteException{
try {
Registry registry = LocateRegistry.getRegistry("localhost");
stockQuote = (StockQuoteInterface) registry.lookup("StockQuoteServer");
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
this.setLayout(new GridLayout(0,1));
JPanel symbolPanel = new JPanel();
symbolPanel.add(new JLabel("Stock Symbol: "));
symbolPanel.add(symbolField);
this.add(symbolPanel);
this.setLayout(new GridLayout(0,1));
JPanel fieldsPanel = new JPanel();
fieldsPanel.add(new JLabel("Current Price: "));
fieldsPanel.add(currentPriceField);
fieldsPanel.add(new JLabel("Price Change: "));
fieldsPanel.add(priceChangeField);
fieldsPanel.add(new JLabel("Daily High: "));
fieldsPanel.add(dailyHighField);
fieldsPanel.add(new JLabel("Daily Low: "));
fieldsPanel.add(dailyLowField);
this.add(fieldsPanel);
JPanel lookupButtonPanel = new JPanel();
lookupButtonPanel.add(lookup);
this.add(lookupButtonPanel);
lookup.addActionListener(this);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setSize(240, 350);
this.setVisible(true);
}
public void actionPerformed(ActionEvent e){
StockQuote quoteInfo = new StockQuote();
Object source = e.getSource();
try{
if(source == lookup){
String symbol = symbolField.getText().trim();
quoteInfo = stockQuote.getQuote(symbol);
if(quoteInfo.currentPrice == 0.0){
currentPriceField.setText("Error");
priceChangeField.setText("Error");
dailyHighField.setText("Error");
dailyLowField.setText("Error");
}else{
currentPriceField.setText(Double.toString(quoteInfo.currentPrice));
priceChangeField.setText(Double.toString(quoteInfo.priceChange));
dailyHighField.setText(Double.toString(quoteInfo.dailyHigh));
dailyLowField.setText(Double.toString(quoteInfo.dailyLow));
}
}
}catch(RemoteException ex){
ex.printStackTrace();
}
}
public static void main(String[] args) throws RemoteException{
/*StockQuote ggg = new StockQuote();
Scanner keyboard = new Scanner(System.in);
System.out.println("Enter a symbol: ");
String symbol = keyboard.nextLine();
ggg = stockQuote.getQuote(symbol);
System.out.println(ggg.currentPrice);*/
new StockQuoteClient();
}
}
这是我收到的例外。
Exception in thread "main" java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: stockquote.StockQuote
at sun.rmi.server.UnicastRef.invoke(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
at com.sun.proxy.$Proxy0.getQuote(Unknown Source)
at stockquote.StockQuoteClient.main(StockQuoteClient.java:101)
Caused by: java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: stockquote.StockQuote
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at sun.rmi.server.UnicastRef.unmarshalValue(Unknown Source)
... 5 more
Caused by: java.io.NotSerializableException: stockquote.StockQuote
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at sun.rmi.server.UnicastRef.marshalValue(Unknown Source)
at sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at sun.rmi.transport.Transport$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$256(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
堆栈跟踪会准确地告诉您错误是什么。你需要StockQuote
影响Serializable
,因为RMI使用Java的序列化机制通过网络传输数据(如方法参数和返回值(。
堆栈跟踪中报告的根本原因是
java.io.NotSerializableException: stockquote.股票报价
显然,在尝试序列化的对象的对象图中,有一个名为 stockquote.StockQuote
的类的实例,但该类不可序列化。
从堆栈跟踪的其他部分来看,序列化尝试似乎与调用远程方法的尝试有关 - RMI 依赖于 Java 序列化将参数传递给远程方法并从中接收返回值。
当然,如果您希望能够通过 RMI 传递或返回实例,stockquote.StockQuote
必须实现java.io.Serializable
。 但是,这可能足以也可能不足以实际序列化实例,具体取决于类的成员。