1

I searched quite a bit and I'm very new to java so I'm trying to create a basic program that will do the following

User will insert birth date or horoscope sign

The program will then tell the user what their sign is or what range of dates that particular horoscope goes through.

I'm currently at the point where this line... " ourDate.getIndexByDate(myDay); " Gives a null pointer exception. At the same time, so does the method that ourDate is calling.

java.lang.NullPointerException
    at Dates.getIndexByDate(Dates.java:68)
    at Dates.main(Dates.java:49)

I have the following code:

import java.util.ArrayList;
import java.util.Scanner;

public class Dates {

    private static ArrayList<Double> zodiacDates;
    private static ArrayList<String> zodiac;

    public static void main(String[] args) {
        ArrayList<Double> zodiacDates = new ArrayList<Double>()
        {{ 
            add(1.19);
            add(2.18);
            add(3.20);
            add(4.21);
            add(5.20);
            add(6.20);
            add(7.22);
            add(8.22);
            add(9.22);
            add(10.22);
            add(11.21);
            add(12.21);
        }};
        ArrayList<String> zodiac = new ArrayList<String>()
        {{
            add("Capricorn");
            add("Aquarius");
            add("Pisces");
            add("Aries");
            add("Taurus");
            add("Gemini");
            add("Cancer");
            add("Leo");
            add("Virgo");
            add("Libra");
            add("Scorpio");
            add("Sagittarius");     
        }};
        Scanner keyboard = new Scanner(System.in);
        System.out.println("Enter your birthdate in the form of a double");
        Double myDay = keyboard.nextDouble();
        System.out.println("Enter your horoscope sign if you know it");
        String ourSign = keyboard.nextLine();
        Dates ourDate = new Dates();
        System.out.println("Enter your birthdate in the form of a double");
        ourDate.getIndexByDate(myDay);
        ourDate.getIndexBySign(ourSign);    
    }

    public int getIndexBySign(String pName)
    {
        for (int w = 0; w < zodiac.size(); w++)
        {
            String sign = zodiac.get(w);
            if (pName.equals(sign))
            {
                return w;
            }
        } 

        return -1;
    }

    public int getIndexByDate(Double myDay)
    {
        for (int i = 0; i <= zodiacDates.size(); i++)
        {
            Double variable = zodiacDates.get(i);
            if ((myDay <= variable && myDay > zodiacDates.get(i-1)) || (myDay < 1.19 || myDay >= 12.22 ) )
            {
                return i;
            }   
        }
        return -1;
    }
}

3 Answers3

3

Debugging a NullPointerException should be fairly straightforward when you have a stack with line number. With an IDE, you should be able to break on that line and see exactly the problem. There is also the good ole print statement debugging. If you are able to step through your conditional operators, I bet you will find the problem. You almost always want to use && when doing Boolean logic as it will short-circuit and avoid problems like you have found. & is really for a bitwise and; it will execute BOTH sides of the operator causing what looks like to be your problem.

  • 1
    Thanks! It looks like the zodiacDates.size(); was null in my conditions! – Donald McKay Feb 14 '16 at 23:02
  • Nice, good job man. Glad I could help you in the right direction. – DarylFranklin52 Feb 14 '16 at 23:06
  • After a lot more work I finally figured out that my arrays weren't instantiated when I tried to call for them in the method. The solution was to put the arrays physically in the two methods that needed them all though I'm 100% sure this isn't memory efficient! For my first homework assignment it'll certainly do the job though. – Donald McKay Feb 15 '16 at 00:00
  • Good deal. I remember when I took my first Java class. The teacher wrote programs on the chalkboard and we used notepad to write ours. It wasn't until later that I learned the power of a good IDE like Netbeans or Eclipse. You can still compile your code from the command line, but you should check one of them out and it will make your life much easier :) – DarylFranklin52 Feb 15 '16 at 19:12
1
  1. Use && for & and || for | in your if condition. ( the & and | are bit-wise operators)
  2. Change your for loop to for ( int i = 0 ; i < zodiacDates.size(); i++ ) if you use <= , zodiacDates.get(i) will return null when it reaches the end.

  3. Also populate your arraylist with statements such as :

ArrayList<Double> zodiacDates = new ArrayList<Double>(); zodiacDates.add( 9.22 ); zodiacDates.add( 10.22 );

.....

and zodiac as

ArrayList<String> zodiac = new ArrayList<String>();
zodiac.add("Capricorn");
zodiac.add("Leo");
SomeDude
  • 13,876
  • 5
  • 21
  • 44
  • Thanks, I updated the program but I still receive the same error error on the same lines. You spared me trouble down the road for sure. – Donald McKay Feb 14 '16 at 22:51
  • Pls update your code in the question and update the stack trace. – SomeDude Feb 14 '16 at 22:57
  • 1
    I agree with the advice to use && and ||, but not for the reason given. & and | are only bit-wise operators when applied to integer arguments. For booleans, they are logical operators just like && and ||, but do not short-circuit evaluation of the second operand. See the [JLS](https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.22.2) – Patricia Shanahan Feb 14 '16 at 23:52
  • I populated the arraylist as you described but I had to move them out of the main and put one of each arraylist in each method, I'm entirely sure this wasn't efficient but it did work. – Donald McKay Feb 15 '16 at 00:04
0

The error comes from static variables and how they can be overwritten in functions. You have your static (~= global) lists private static ArrayList<Double> zodiacDates; and private static ArrayList<String> zodiac;.

The problem is that you declare them again in your main function, but not static, the variables you initialize are only local to your main function. When you then call the "global" static variables in your 2 functions, they are uninitialized, meaning you get a nice and pretty NullPointerException, since nothing is in the variable. Just remove the declaration part of your initialization in the main:

public static void main(String[] args) {
    zodiacDates = new ArrayList<Double>()
    {{ 
        add(1.19);
        add(2.18);
        add(3.20);
        add(4.21);
        add(5.20);
        add(6.20);
        add(7.22);
        add(8.22);
        add(9.22);
        add(10.22);
        add(11.21);
        add(12.21);
    }};
    zodiac = new ArrayList<String>()
    {{
        add("Capricorn");
        add("Aquarius");
        add("Pisces");
        add("Aries");
        add("Taurus");
        add("Gemini");
        add("Cancer");
        add("Leo");
        add("Virgo");
        add("Libra");
        add("Scorpio");
        add("Sagittarius");     
    }};
    Scanner keyboard = new Scanner(System.in);
    System.out.println("Enter your birthdate in the form of a double");
    Double myDay = keyboard.nextDouble();
    System.out.println("Enter your horoscope sign if you know it");
    String ourSign = keyboard.nextLine();
    System.out.println("Enter your birthdate in the form of a double");
    Dates.getIndexByDate(myDay);
    Dates.getIndexBySign(ourSign);    
}

You would also get a ArrayIndexOutOfBoundsException because you try to access an unexisting element of your ArrayList in some cases :

for (int i = 0; i <= zodiacDates.size(); i++)
{
    Double variable = zodiacDates.get(i);
    if ((myDay <= variable && myDay > zodiacDates.get(i-1)) || (myDay < 1.19 || myDay >= 12.22 ) )
    {
        return i;
    }   
}

You use zodiacDates.get(i-1) even when i = 0 (on the first loop execution). This means you will try to access the -1th element, of course unexisting.
To avoid it start with i = 1 in your for loop. Fixing this would also show that you use i <= zodiacDates.size().
The problem is when you will reach the last element in your for loop (when i = zodiacDates.size()) you will be one further than the last element. In fact, the indexing starts with 0 so the last element has the index zodiacDates.size() - 1.
The solution is to use < instead of <= as you did with the zodiac list.

The remaining problem, as you tried to solve in the if statement, is when a date is between december and january. Your if statement contains this check (myDay < 1.19 || myDay >= 12.22 ), meaning if you have a input date in this interval, it will make the satetement true on the first time it reaches it, meaning it will return 1 (value of i)
Therefore you have to check this interval before your loop... or you dont ! You actually know that if none of the date was detected in the loop, it can only be Sagitarius. Now if you don't need an invalid input detection (which you don't really since someone could type in a date like 5.98 and still be cast into a zodiac sign), you can use Sagittarius as default, and return 0 after the loop. Giving :

public static int getIndexByDate(Double myDay)
{
    if (myDay < 1.19 || myDay >= 12.22 ) return 0; //Not necessary
    for (int i = 1; i < zodiacDates.size(); i++)
    {
        Double variable = zodiacDates.get(i);
        if (myDay <= variable && myDay > zodiacDates.get(i-1) )
        {
            return i;
        }   
    }

    return -1; //Or return 0; as default case.
}

Last Class detail, you have some static variables in your Dates class, and your functions getIndexBySign and getIndexByDate only use static stuff, no Object dependent variables. Idealy you would make them static, then call them with Dates.getIndexBySign(...) and Dates.getIndexByDate(...) and the Dates object declaration could be removed.

That's all for me !

Jomi
  • 121
  • 2
  • 4