简体   繁体   中英

java.lang.NullPointerException when setting value of attributes within a loop

Trying to make it so if the user types "end", in the second input which is "Enter the first name of student", the loop automatically assigns each object in the array the attributes of "null" for id and name, and 0 for age and id, as well as breaking the outerloop. However, I get the error java.lang.NullPointerException. Any help would be appreciated.

import java.util.*;


class Main {
    public static void main(String[] args) {
        Scanner myObj = new Scanner(System.in);
        System.out.println("Enter number of students");
        int numberof = myObj.nextInt();
        System.out.println("Number of students is " + numberof);
        Student Studentz[] = new Student[numberof];
        outerloop:
            for (int i = 0; i < numberof; ++i) {
                Studentz[i] = new Student();

                System.out.println("Enter first name of student " + (i + 1));
                Scanner myObj1 = new Scanner(System.in);

                String firstname = myObj1.nextLine();
                System.out.println("Firstname is: " + firstname);
                if (firstname.equals("end")) {
                    for (int g = i; g < numberof; ++g) {
                        Studentz[g].Setfirst("null");
                        Studentz[g].Setlast("null");
                        Studentz[g].Setage(0);
                        Studentz[g].Setid(0);
                    }
                    break outerloop;
                } else {
                    Studentz[i].Setfirst(firstname);

                    System.out.println("Enter last name of student " + (i + 1));
                    Scanner myObj2 = new Scanner(System.in);

                    String lastname = myObj2.nextLine();

                    System.out.println("Last name is: " + lastname);
                    Studentz[i].Setlast(lastname);;

                    System.out.println("Enter age of student " + (i + 1));
                    Scanner myObj3 = new Scanner(System.in);

                    int nazca = myObj3.nextInt();
                    System.out.println("Age is: " + nazca);
                    Studentz[i].Setage(nazca);


                    System.out.println("Enter ID of student " + (i + 1));
                    Scanner myObj4 = new Scanner(System.in);

                    int nazca1 = myObj4.nextInt();
                    System.out.println("ID is: " + nazca1);
                    Studentz[i].Setid(nazca1);

                }

                for (int c = 0; c < numberof; ++c) {
                    System.out.println(Studentz[c].Snake());
                }
            }
    }
}

public class Student {
    private String first;
    private String last;
    private int age;
    private int id;
    public int getid() {
        return
        this.id;
    }


    public void Studentss(String f, String l, int a, int i) {
        first = f;
        last = l;
        age = a;
        id = i;
    }
    public void Setfirst(String z) {
        this.first = z;
    }
    public void Setlast(String za) {
        this.last = za;
    }
    public void Setage(int zb) {
        this.age = zb;
    }
    public void Setid(int zc) {
        this.id = zc;
    }
    public String Snake() {
        String snek = "Name is " + this.first + " " + this.last + " , Age is " + this.age + " ,ID is " + this.id;
        return snek;
    }
}

you fail here because you try to print students data before you initialized all elements of Studentz array:

for (int c = 0; c < numberof; ++c) {
    System.out.println(Studentz[c].Snake());
}

also your code fails here by the same reason:

for (int g = i; g < numberof; ++g) {
    Studentz[g].Setfirst("null");  // NPE - no Student created yet in the array

Some Possibly Helpful Tips:

Follow Java naming rules for your variables and method names, Studentz should be studentz , even for your Arrays. I know...your tired of reading that but as you progress, you'll see how beneficial it really is.

If you can, don't use multiple Scanner objects, there is no need for that in your use-case. Declare one Scanner object and stick with it. You're probably doing this because you are using the nextLine() method after the nextInt() method and you're finding that it skips the first name prompt and provides a Null String (""). This happens because the nextInt() method does not consume the newline character when the Enter key is hit. To solve this problem you would either, place the code line myObj.nextLine(); directly under the int numberof = myObj.nextInt(); code line:

int numberof = myObj.nextInt();
myObj.nextLine();  // Consume ENTER key hit

or don't use the nextInt() method at all. Instead just stick with the nextLine() method and not worry about consumption:

Scanner myObj = new Scanner(System.in);
int numberof = 0;
String val = "";
while (val.equals("")) {
    System.out.println("Enter number of students");
    val = myObj.nextLine();
    if (!val.matches("\\d+")) {
        System.err.println("Invalid number supplied!");
        val = ""; 
        continue;
    }
    numberof = Integer.parseInt(val);
}
System.out.println("Number of students is " + numberof);
Student[] studentz = new Student[numberof];

It's always a good idea to validate any input from the User (as shown above), even if they're in a for loop. Give the User an opportunity to make a correct entry, after all, typo's do happen.

Get rid of the outerLoop: label. As a matter of fact, try avoid ever using them if you can....and you can. It would only be on a relatively rare occasion where you might ever need one.

Give your variables meaningful names, at least to some extent. This can benefit you later on down the road when you want to read your old code in the future.

In your Student class you made yourself a this real nice method named Studentss() . Well, I think it should be a constructor instead and save yourself a lot of code entry for calls to Setter methods. After all, that's mostly what the constructor is for. Your Student class constructor can look like this:

public Student(String firstName, String lastName, int age, int id) {
    this.first = firstName;
    this.last = lastName;
    this.age = age;
    this.id = id;
}

And be used like this. You will notice that upon each iteration of the outer for loop, all the prompt answers are placed within variables then at the end of all those prompts the constructor is used instantiate a student object, for example:

studentz[i] = new Student(firstname, lastname, age, id); 

Doing it this way mean that there is no need to make calls to Setter methods. There is nothing wrong with making setter calls, it's just easier using the constructor. This is demonstrated below:

Scanner myObj = new Scanner(System.in);

int numberof = 0;
String val = "";
while (val.equals("")) {
    System.out.println("Enter number of students");
    val = myObj.nextLine();
    if (!val.matches("\\d+")) {
        System.err.println("Invalid number supplied!");
        val = "";
        continue;
    }
    numberof = Integer.parseInt(val);
}
System.out.println("Number of students is " + numberof);

Student[] studentz = new Student[numberof];

for (int i = 0; i < numberof; ++i) {
    String firstname = "null";
    String lastname = "null";
    int age = 0;
    int id = 0;
    boolean exitOuterForLoop = false;

    // Student First Name Prompt:
    // (with option to End and default remaining to null's and 0's)
    while (firstname.equals("null")) {
        System.out.println("Enter first name of student " + (i + 1) + " (End to stop):");
        firstname = myObj.nextLine();
        if (firstname.equalsIgnoreCase("end")) {
            firstname = "null";
            //Make all remaining Student instances null and 0
            for (int g = i; g < numberof; ++g) {
                studentz[g] = new Student(firstname, lastname, age, id); // Use Student class constructor
            }
            exitOuterForLoop = true;
            break; // Exit this 'while' loop
        }
        // Validate first name (no numbers or crazy characters)
        if (!firstname.matches("(?i)[a-z']+")) {
            System.err.println("Invalid First Name! (" + firstname + ") Try Again...");
            firstname = "null";
        }
    }
    if (exitOuterForLoop) {
        break; // Exit this outer 'for' loop.
    }
    System.out.println("Firstname is: " + firstname);

    // Student Last Name Prompt
    while (lastname.equals("null")) {
        System.out.println("Enter last name of student " + (i + 1));
        lastname = myObj.nextLine();
        // Validate last name (no numbers or crazy characters)
        if (!lastname.matches("(?i)[a-z']+")) {
            System.err.println("Invalid Last Name! (" + lastname + ") Try Again...");
            lastname = "null";
        }
    }
    System.out.println("Last name is: " + lastname);

    // Student Age Prompt
    val = "";
    while (val.equals("")) {
        System.out.println("Enter age of student " + (i + 1));
        val = myObj.nextLine();
        // Validate age (digits 0 to 9 only)
        if (!val.matches("\\d+")) {
            System.err.println("Invalid Age Supplied! (" + val + ") Try Again...");
            val = "";
        }
    }
    age = Integer.parseInt(val);
    System.out.println("Student age is: " + age);

    // Student ID Prompt
    val = "";
    while (val.equals("")) {
        System.out.println("Enter ID of student " + (i + 1));
        val = myObj.nextLine();
        // Validate age (digits 0 to 9 only)
        if (!val.matches("\\d+")) {
            System.err.println("Invalid ID Supplied! (" + val + ") Try Again...");
            val = "";
        }
    }
    id = Integer.parseInt(val);
    System.out.println("Student ID is: " + id);

    studentz[i] = new Student(firstname, lastname, age, id); // Use Student class constructor
}

// Display the instances of Student contained within the 'studentz[]' array.
for (int c = 0; c < numberof; ++c) {
    System.out.println(studentz[c].toString());
}

The snake() method is actually just another toString() method and there is nothing wrong with that however you might want to consider using StringBuilder class to create the returned string rather than doing concatenations, for example:

public String snake() {
    return new StringBuilder("Name is ").append(this.first).append(" ").append(this.last)
                            .append(" , Age is ").append(this.age).append(" , ID is ")
                            .append(this.id).toString();
}

Not overly important here but it can save you memory if you do a lot concatenating with many large strings.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM