我有这个简单的网络代码(我试图实现一个简单的聊天服务器作为练习),但我被卡住了,我不知道发生了什么。我为我的客户端创建了GUI,它有一个另一个类的实例,它执行所有必要的操作,以连接和发送/接收消息到我的服务器。
package com.luca.chat;
import java.awt.*
import java.awt.event.*
import javax.swing.*
class Window extends JFrame {
private final static int WIDTH=500;
private final static int HEIGHT=300;
private String status="disconnected";
private JPanel panel;
private JTextArea area;
private JTextField field,address;
private JButton button;
private JLabel label;
private IOClass io=new IOClass(this);
Window() {
super("Chit-Chat");
setSize(new Dimension(WIDTH,HEIGHT));
ImageIcon icon=new ImageIcon("..\images\lucas.png");
setIconImage(icon.getImage());
JPanel contentPane=(JPanel)getContentPane();
// add the text area to display incoming and outgoing messages
area=new JTextArea();
area.setEditable(false);
contentPane.add(area,BorderLayout.CENTER);
// add text field to send messages
field=new JTextField(20);
field.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
String message=field.getText();
io.send(message);
field.setText("");
}
});
panel=new JPanel();
panel.add(field);
panel.setBorder(BorderFactory.createEtchedBorder());
contentPane.add(panel,BorderLayout.SOUTH);
// add status bar on top
label=new JLabel("status: "+status);
address=new JTextField(15);
button=new JButton("connect");
panel=new JPanel();
panel.add(label);
panel.add(address);
panel.add(button);
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
io.connect(address.getText());
}
});
panel.setBorder(BorderFactory.createEtchedBorder());
contentPane.add(panel,BorderLayout.NORTH);
Toolkit kit=getToolkit();
Dimension dim=kit.getScreenSize();
setLocation((dim.width-WIDTH)/2,(dim.height-HEIGHT)/2);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
loop();
}
void loop() {
while (true)
if (io.connected)
io.receive();
else // if I comment this line out it never calls receive() ?????
System.out.println("????");
}
void setStatus(String status) {
label.setText(status);
}
void setText(String received) {
area.append(received);
}
public static void main(String[] args) {
new Window();
}
}
IOClass拥有连接/发送/接收的所有方法(我认为它的行为就像一个控制器?我不知道,我刚刚开始学习这个模式)一个标志,它控制连接是否建立。如果有连接,它会调用receive方法,这是一个用于侦听传入消息的无限循环。
但是,如果我只留下if语句(在loop()内部)而不留下else。。。它不执行!我的意思是。。while循环永远不会开始(我尝试了一些打印……它永远不会到达那里),但如果我添加其他打印,它就会到达那里!
例如,这会阻塞if语句:
void loop() {
while (true)
if (io.connected)
io.receive();
}
这是有效的(while永远循环):
void loop() {
while (true)
if (io.connected)
io.receive();
else
....do something else...
}
或者甚至这样做:
void loop() {
while (true) {
System.out.println("whatever");
if (io.connected)
io.receive();
} // end of while
}
我不能理解,这在我看来是一种非常奇怪的行为
EDIT:使用Eclipse的调试功能,我看到当它工作时,while循环会按预期永远循环,当它不工作时,它只会在if条件下阻塞
void loop() {
while (true)
if (io.connected)
io.receive();
else // if I comment this line out it never calls receive() ?????
System.out.println("????");
}
在您的答案示例中,您指出此代码是罪魁祸首。由于while
没有括号{}
,因此只有下一行将被视为while循环的主体。没有括号的if
也是如此:只有true条件中的第一行才会执行。它后面可以跟else
-子句,也可以不跟。
在您的情况下,由于while
之后的下一行是if-else
,并且这两个条件都只有一行要执行,所以while循环按预期工作。
如果你想说的是:
void loop() {
while (true)
if (io.connected)
io.receive();
//else
System.out.println("????");
}
那么这就不会编译,因为您告诉System.out.println("????");
执行AFTERwhile循环已经完成,while循环的条件明确为true;永远无法访问代码。如果您使用一个变量作为标志,它将进行编译,并且当循环完成时,您将体验到System.out.println("????");
将执行一次。
在Eclipse调试器的帮助下,我一步一步地找到了捕获。由于当我实例化一个JFrame时,JVM会自动旋转一个新线程(事件队列),并且由于是用连接按钮注册的ActionListener调用connect()方法将布尔标志设置为true,因此该标志在这个线程中为true,但在主线程中仍然为false,因此if条件永远不会计算为true,我挂起等待receive()方法被调用,屏幕上没有输出。使用volatile修饰符解决了这个问题,因此更新后的变量对所有线程都是自动可见的。由于我是线程的新手,所以这并不那么明显。。但一句话让我走上了正确的方向。