- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
好吧,我对 Java 编程并不陌生,但我对在 Java 程序中使用线程很陌生。我正在上学,刚刚读完关于线程和 Java 网络的章节。我正在编写一个客户端 GUI,将贷款信息(年利率、年数和贷款金额)发送到服务器。服务器有自己的 GUI,计算每月还款额和贷款总额,并将其发送回客户端并显示给用户,并更新服务器 GUI。
书中有这样的代码作为示例:
public class Server extends Application {
/** Variables */
private TextArea textArea = new TextArea();
private double rate;
private int year;
private double loan;
public void start(Stage serverStage)
{
// Creating server GUI
Scene scene = new Scene(new ScrollPane(textArea), 400, 200);
serverStage.setTitle("Server");
serverStage.setScene(scene);
serverStage.show();
new Thread(() ->{
try
{
// create server socket
ServerSocket serverSocket = new ServerSocket(8000);
textArea.appendText("Server started at " + new Date() + "\n");
while(true)
{
// listen for a connection request
Socket socket = serverSocket.accept();
Platform.runLater(() -> {
InetAddress inetAddress = socket.getInetAddress();
textArea.appendText("Connected to " + inetAddress.getHostAddress() + " at " + new Date() + "\n");
});
// create and start a new thread for every connection
new Thread(new HandleAClient(socket)).start();
}
}
catch(IOException ex)
{
ex.printStackTrace();
}
}).start();
}
class HandleAClient implements Runnable {
private Socket socket; // A connected socket
private double rate;
private int year;
private double loan;
/** costruct a thread */
public HandleAClient(Socket socket)
{
this.socket = socket;
}
/** run a thread */
public void run(){
try
{
// create data input and output streams
DataInputStream inputFromClient = new DataInputStream(socket.getInputStream());
DataOutputStream outputToClient = new DataOutputStream(socket.getOutputStream());
// continuously serve the client
while(true) {
// read data from client
rate = inputFromClient.readDouble();
year = inputFromClient.readInt();
loan = inputFromClient.readDouble();
// calculate monthly payment of loan and total payment
outputToClient.writeDouble(calculateMonthlyPayment(rate, year, loan));
outputToClient.writeDouble(calculateTotalPayment(rate, year, loan));
Platform.runLater( () -> {
textArea.appendText("The rate is : " + rate + "\n");
textArea.appendText("The number of years is: " + year + "\n");
textArea.appendText("Loan amount is: " + loan + "\n\n");});
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
// calculateMonthlyPayment method calculates the monthly payment of a loan given
// the required information
public double calculateMonthlyPayment(double interestRate, int years, double loanAmt)
{
double monthlyRate;
int termInMonths;
double monthlyPayment;
// Convert the interest rate to a decimal
interestRate = interestRate / 100;
// convert annual interest rate to monthly interest rate
monthlyRate = interestRate / 12;
// calculate the term in months which is years * 12
termInMonths = years * 12;
monthlyPayment = (loanAmt*monthlyRate) / (1-Math.pow(1+monthlyRate, -termInMonths));
return monthlyPayment;
}
// method that calculates and returns the total payment of the loan
public double calculateTotalPayment(double rate, int year, double loan)
{
double totalPayment;
double monthlyPay;
monthlyPay = calculateMonthlyPayment(rate, year, loan);
totalPayment = monthlyPay * 12 * year;
return totalPayment;
}
}
正如您在示例代码中看到的,他们(本书的作者)使用一个新的线程来附加服务器 GUI 的文本。然而,为了能够处理多个客户端,需要在 while 循环内部创建一个新线程来处理每个单独的客户端。
我尝试将 HandleAClient 类创建为单独的 Java 类,而不是将其插入到 Server 类中,但这导致服务器 GUI 未使用 Platform.runLater 代码进行更新
Platform.runLater( () -> {
textArea.appendText("The rate is : " + rate + "\n");
textArea.appendText("The number of years is: " + year + "\n");
textArea.appendText("Loan amount is: " + loan + "\n\n");});
所以我的问题是:为什么当 HandleAClient 类位于 Server 类内部时它可以工作,而当 HandleAClient 类位于扩展 Server 的单独 Java 类文件中时它不起作用?我认为它必须与线程做一些事情?为了能够在自己的 Java 类文件中包含 HandleAClient 类,我需要进行哪些更改?
我很好奇并试图很好地理解线程是如何工作的。预先感谢您。
更新这是独立的类(class),对我来说不起作用。我扩展了 Server 类,并在 Server 类中保护了 TextArea 字段。
class HandleAClient extends Server implements Runnable {
private Socket socket; // A connected socket
private double rate;
private int year;
private double loan;
public HandleAClient(Socket socket)
{
this.socket = socket;
}
/** run a thread */
public void run(){
try
{
// create data input and output streams
DataInputStream inputFromClient = new DataInputStream(socket.getInputStream());
DataOutputStream outputToClient = new DataOutputStream(socket.getOutputStream());
// continuously serve the client
while(true) {
// read data from client
rate = inputFromClient.readDouble();
year = inputFromClient.readInt();
loan = inputFromClient.readDouble();
// calculate monthly payment of loan and total payment
outputToClient.writeDouble(calculateMonthlyPayment(rate, year, loan));
outputToClient.writeDouble(calculateTotalPayment(rate, year, loan));
Platform.runLater( () -> {
textArea.appendText("The rate is : " + rate + "\n");
textArea.appendText("The number of years is: " + year + "\n");
textArea.appendText("Loan amount is: " + loan + "\n\n");});
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
Platform.runLater 中的代码不会像该类位于 Server 类内部时那样出现在服务器 GUI 中。我想了解为什么会发生这种情况。
最佳答案
您需要您的HandleAClient
实例来更新属于 Server
的文本区域创建它们的对象:textArea
属于那个Server
实例是在 UI 中显示的实例。根据您的设置,每个 HandleAClient
实例有自己的 textArea
,并且每个都更新自己的 textArea
。当然,这些都不会显示。
对于我来说 HandleAClient
真的没有意义延长Server
。您需要做的(无论您是否拥有该继承)是为 HandleAClient
提供一种方法。更新属于 Server
的文本区域。最简单(但不一定是最好)的方法是将文本区域传递给 HandleAClient
实例:
class HandleAClient implements Runnable {
private Socket socket; // A connected socket
private double rate;
private int year;
private double loan;
private final TextArea textArea ;
public HandleAClient(Socket socket, TextArea textArea)
{
this.socket = socket;
this.textArea = textArea ;
}
/** run a thread */
public void run(){
try
{
// create data input and output streams
DataInputStream inputFromClient = new DataInputStream(socket.getInputStream());
DataOutputStream outputToClient = new DataOutputStream(socket.getOutputStream());
// continuously serve the client
while(true) {
// read data from client
rate = inputFromClient.readDouble();
year = inputFromClient.readInt();
loan = inputFromClient.readDouble();
// calculate monthly payment of loan and total payment
outputToClient.writeDouble(calculateMonthlyPayment(rate, year, loan));
outputToClient.writeDouble(calculateTotalPayment(rate, year, loan));
Platform.runLater( () -> {
textArea.appendText("The rate is : " + rate + "\n");
textArea.appendText("The number of years is: " + year + "\n");
textArea.appendText("Loan amount is: " + loan + "\n\n");});
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
然后当然是Server
您将其修改为
while(true)
{
// listen for a connection request
Socket socket = serverSocket.accept();
Platform.runLater(() -> {
InetAddress inetAddress = socket.getInetAddress();
textArea.appendText("Connected to " + inetAddress.getHostAddress() + " at " + new Date() + "\n");
});
// create and start a new thread for every connection
new Thread(new HandleAClient(socket, textArea)).start();
}
<小时/>
让 HandleAClient
感觉有点不自然类(负责通信)依赖于对 TextArea
的引用(这是特定于 UI 的)。更自然的方法如下。
我可能会定义一个简单的类来表示利率、年份和贷款:
public class LoanData {
private final double rate ;
private final int year ;
private final double loan ;
public LoanData(double rate, int year, double loan) {
this.rate = rate ;
this.year = year ;
this.loan = loan ;
}
public double getRate() {
return rate ;
}
public int getYear() {
return year ;
}
public double getLoan() {
return loan ;
}
}
然后给出HandleAClient
A类Consumer<LoanData>
用于处理贷款数据:
class HandleAClient implements Runnable {
private Socket socket; // A connected socket
private final Consumer<LoanData> dataProcessor ;
public HandleAClient(Socket socket, Consumer<LoanData> dataProcessor)
{
this.socket = socket;
this.dataProcessor = dataProcessor ;
}
/** run a thread */
public void run(){
try
{
// create data input and output streams
DataInputStream inputFromClient = new DataInputStream(socket.getInputStream());
DataOutputStream outputToClient = new DataOutputStream(socket.getOutputStream());
// continuously serve the client
while(true) {
// read data from client
double rate = inputFromClient.readDouble();
double year = inputFromClient.readInt();
double loan = inputFromClient.readDouble();
// calculate monthly payment of loan and total payment
outputToClient.writeDouble(calculateMonthlyPayment(rate, year, loan));
outputToClient.writeDouble(calculateTotalPayment(rate, year, loan));
dataProcessor.accept(new LoanData(rate, year, loan));
}
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
现在在服务器中执行
Consumer<LoanData> textAreaUpdater =
loanData -> Platform.runLater(() -> {
textArea.appendText("The rate is : " + loanData.getRate() + "\n");
textArea.appendText("The number of years is: " + loanData.getYear() + "\n");
textArea.appendText("Loan amount is: " + loanData.getLoan() + "\n\n");
});
new Thread(new HandleAClient(socket, textAreaUpdater)).start();
这使得 UI 数据功能正确地隔离在 UI 类中。
关于java - 有没有办法在 JavaFX 中的不同类文件中使用线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42428801/
我是一名优秀的程序员,十分优秀!