NullPointerException в массиве PrintWriter [] - Java

У меня есть проблема, которую я пытаюсь выяснить в течение нескольких дней.

Простой обзор кода (псевдокод):

1 - Когда пользователь подключается, создайте для него PrintWriter.

2 - Сохраните объект PrintWriter в массиве PrintWriter[].

3 - Увеличьте количество подключенных пользователей.

В чем моя проблема?

  • Компилятор выдает исключение NullPointerException.

Что вызвало НЭП?

  • Когда метод sendMessage() вызывается объектом, созданным в классе mainWindow, массив PrintWriter, если он пуст, я не знаю, что заставляет его быть пустым, поскольку все переменные и массивы имеют статические модификаторы, а массив PrintWriter[] получает значение в конструкторе (когда пользователь подключается).

  • Внутри конструктора ServerHandler, когда я пытаюсь отправить сообщение клиенту, он работает нормально, и я вижу, что массив PrintWriter[] не пуст, но когда я вызываю метод sendMessage() из экземпляра mainWindow, массив пуст и Целочисленная переменная userCounter по-прежнему равна 0.

Что я пытался исправить?

  • Установка переменных и модификаторов массива на volatile и static.
  • Написание всего кода с нуля снова и снова.

Класс сервера (отлично работает):

package homeControl;

import java.net.*;
import java.util.Scanner;
import java.io.*;
import java.awt.*;

public class Server{
String sporocilo = null;
int id = 0;

outputHelper o = new outputHelper();


public void sprejmiPovezavo()
{
    try
    {
        ServerSocket svrSock = new ServerSocket(5000);

        while(true){

        Socket klientSocket = svrSock.accept();


        new Thread(new ServerHandler(klientSocket)).start();

        }


    }catch(IOException ex)
    {
        ex.printStackTrace();
    }
}


    public static void main(String[] args){
Server svr = new Server();
svr.sprejmiPovezavo();
}

}

Класс ServerHandler (этот класс вызывает проблемы)

package homeControl;

import java.net.*;
import java.util.ArrayList;
import java.io.*;
import java.awt.*;

public class ServerHandler implements Runnable{
static PrintWriter pw;
static PrintWriter[] writerHolder = new PrintWriter[10];
static int userCounter;
static int selectedID;


public ServerHandler(Socket socket){
    try{
        pw = new PrintWriter(socket.getOutputStream());
        writerHolder[userCounter] = pw;

        // Testing if this writerHolder[] can send the message to the client.
        writerHolder[userCounter].println("autoSent - Testint!");
        writerHolder[userCounter].flush();

        userCounter++;// Increment the number of people connected

        //Testing the values of the variables.
        System.out.println("ORG!\nuserCounter:" + userCounter +  "\nwriterHolder[userCounter]" + writerHolder[0]);
    }catch(Exception ex)
    {
        ex.printStackTrace();
    }
}
public ServerHandler()
{
    // Empty constructor
    // Used so that i don't need to pass a Socket into the class when i create a ServerHandler object from the mainWindow.class...
}



public void sendMessage(int tempID, String messageToBeSent)
{   // Testing the values of variables at the time that this method gets called.
    System.out.println("tempID:" + tempID + "\n" + "messageToBeSent:" +       messageToBeSent + "\n" + "userCounter:" + userCounter + "\n" + "selectedID:" + selectedID); 
    // End of test/Actual code:
    writerHolder[tempID].println(messageToBeSent);
    writerHolder[tempID].flush();
}

public void run()
{

}

}

класс mainWindows (где вызывается метод sendMessage())

package homeControl;

import java.awt.*;
import java.io.*;
import java.net.Socket;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JTextField;
import javax.swing.JButton;

public class mainWindow extends JFrame {

private JPanel contentPane;
private JTextField messageTextField;

ServerHandler svr = new ServerHandler(); // Thats why i use the empty constuctor in ServerHandler.class.
outputHelper o = new outputHelper();
int ID;

/**
 * Launch the application.
 */
public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                mainWindow frame = new mainWindow();
                frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

/**
 * Create the frame.
 */
public mainWindow() {
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setBounds(100, 100, 450, 300);

    JMenuBar menuBar = new JMenuBar();
    setJMenuBar(menuBar);

    JMenu mnOptions = new JMenu("Options");
    menuBar.add(mnOptions);

    JMenu mnId = new JMenu("ID");
    menuBar.add(mnId);

    JMenuItem idEnaItem = new JMenuItem("ID: 0");
    mnId.add(idEnaItem);

    JMenuItem idDvaItem = new JMenuItem("ID: 1");
    mnId.add(idDvaItem);

    contentPane = new JPanel();
    contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    setContentPane(contentPane);
    contentPane.setLayout(null);

    messageTextField = new JTextField();
    messageTextField.setBounds(10, 209, 292, 20);
    contentPane.add(messageTextField);
    messageTextField.setColumns(10);

    JButton sendButton = new JButton("Send");
    sendButton.setBounds(312, 208, 89, 23);
    contentPane.add(sendButton);
    sendButton.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent event){

            if(event.getActionCommand() != null)
            { // Calling the sendMessage() in the ServerHandler.class which should send a message to the Client.
               svr.sendMessage(ID,messageTextField.getText());
            }

        }
    });
}
}

Вывод конструктора ServerHandler при тестировании PrintWriters:

ОРГ!

счетчик пользователей: 1

WriterHolder[userCounter]java.io.PrintWriter@798c668c

Выход из метода SendMessage, когда я вручную пытаюсь отправить сообщение:

сообщениеToBeSent:dsda

пользовательский счетчик: 0

выбранныйID:0

ОШИБКА:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at homeControl.ServerHandler.sendMessage(ServerHandler.java:45)
at homeControl.mainWindow$2.actionPerformed(mainWindow.java:84)
at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$200(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.awt.EventQueue$4.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

person dHoja    schedule 20.09.2013    source источник
comment
Компилятор не генерирует исключение. Исключение генерируется во время выполнения, спустя много времени после завершения компилятора. Кроме того, вы предоставили огромное количество кода — попробуйте сократить его до короткой, но полной программы, демонстрирующей проблему. Кроме того, предоставьте полную трассировку стека, чтобы мы могли видеть, где генерируется исключение.   -  person Jon Skeet    schedule 20.09.2013
comment
Вы используете переменную с именем ID, чтобы определить, какой PrintWriter будет использоваться. Где он устанавливается? Я не мог найти его. Возможно, вы пытаетесь получить доступ к позиции в вашем массиве, которая еще не была установлена.   -  person Mauren    schedule 20.09.2013
comment
@MAuren По умолчанию 0.   -  person Sotirios Delimanolis    schedule 20.09.2013
comment
@SotiriosDelimanolis Хорошо, но то, как он / она раскрывает проблему, заставляет меня думать, что для нее следует установить другое значение.   -  person Mauren    schedule 20.09.2013
comment
Извините, я новичок в java и программировании в целом. Я вставил весь код, чтобы все зрители этого вопроса могли проверить его в своих компиляторах.   -  person dHoja    schedule 20.09.2013
comment
я удалил строки, где я ввожу идентификатор, потому что вставленный здесь код уже огромен.   -  person dHoja    schedule 20.09.2013
comment
вы запускаете спреймиПовезаво и mainWindow параллельно в разных процессах? (например: разные окна командной строки)   -  person domi    schedule 20.09.2013


Ответы (3)


Насколько я могу судить, это проблема (на сервере):

new Thread(new ServerHandler(klientSocket)).start();

Здесь вы создаете новый ServerHandler. Это полностью отличается от экземпляра ServerHandler, который вы создаете в mainWindow. Таким образом, тот, который создается Server, будет заполнен заполненным массивом PrintWriter... а тот, который вы создаете в mainWindow, всегда будет иметь массив нулевых значений элементов.

У вас должен быть один экземпляр ServerHandler, общий для mainWindow и Server.

person Jon Skeet    schedule 20.09.2013
comment
Как создать экземпляр ServerHandler, который будет использоваться обоими? - person dHoja; 20.09.2013
comment
@user2794550: user2794550: Я только что заметил, что у вас есть два разных метода main, так что, вероятно, у вас есть два совершенно отдельных процесса — как вы ожидали, что они будут совместно использовать массив? Вам потребуется запустить один процесс. Честно говоря, не совсем понятно, что вы пытаетесь сделать в первую очередь... - person Jon Skeet; 20.09.2013
comment
Я удалил основной метод из класса mainWindow, и он больше не работал с NPE, и я могу без проблем отправлять сообщения клиентам. Спасибо!! - person dHoja; 20.09.2013

В вашем mainWindow() классе у вас есть

ServerHandler svr = new ServerHandler(); // Thats why i use the empty constuctor in ServerHandler.class.

который вызывает пустой конструктор, который не устанавливает никаких экземпляров PrintWriter в PrintWriter[].

public ServerHandler()
{
    // you don't initialize any array elements
}

Затем вы пытаетесь sendMessage() получить доступ к

writerHolder[tempID].println(messageToBeSent);
writerHolder[tempID].flush();

унифицированный элемент в массиве, вызывающий NPE.


Кроме того, ваши классы mainWindow и Server совершенно не пересекаются. Между ними нет никакой связи. Должно быть?

person Sotirios Delimanolis    schedule 20.09.2013
comment
Я вызываю этот конструктор, потому что я не хочу, чтобы вызывался основной конструктор, потому что ему нужно передать сокет, и я не знаю, как создать экземпляр класса без вызова конструктора (если это возможно). - person dHoja; 20.09.2013
comment
@ user2794550 Вы не можете создать экземпляр без вызова конструктора. Как вы собираетесь отправить сообщение, если у вас нет сокета для подключения? - person Sotirios Delimanolis; 20.09.2013
comment
Я думал, что это не вызывает проблем, потому что у меня есть статические модификаторы. - person dHoja; 20.09.2013
comment
@user Но вы не запустили код Server, инициализируйте их. Помните, порядок имеет значение. - person Sotirios Delimanolis; 20.09.2013
comment
Извините, но я не понимаю, что вы пытаетесь сказать, не могли бы вы вставить пример кода? - person dHoja; 20.09.2013

Я предлагаю вам запустить mainWindow с сервера:

public void sprejmiPovezavo()
{
    try
    {
        ServerSocket svrSock = new ServerSocket(5000);
        MainWindow mainWindow frame = new mainWindow();
        frame.setVisible(true);

        while(true){

        Socket klientSocket = svrSock.accept();


        new Thread(new ServerHandler(klientSocket)).start();

        }


    }catch(IOException ex)
    {
        ex.printStackTrace();
    }
}

и запускать только сервер (спрейПовезаво), а не запускать mainWindow и сервер (спреймиПовезаво).

person domi    schedule 20.09.2013
comment
Я попробовал код выше, однако он все еще дает мне NullPointerException - person dHoja; 20.09.2013