0

Why, when I in method index1=path.indexOf(jdk); I pass a string variable to jdk, the compiler throws exceptions, and when I put "jdk" in quotes everything works fine(index1=path.indexOf("jdk");) . This variable already has a string type, why quote it, this variable already refers to a string type object

public class Solution {
    public static void main(String[] args) {
        String path = "/usr/java/jdk1.8/bin/";

        String jdk13 = "jdk-13";
        System.out.println(changePath(path, jdk13));
    }


    public static String changePath(String path, String jdk) {
        //System.out.println(jdk);
        int index1=path.indexOf(jdk);
        int index2=path.indexOf("/",index1);
        String oldjdk=path.substring(index1,index2);

        return path.replace(oldjdk,jdk);
    }
}

3 Answers3

1

It's substring(index1,index2) that's throwing a IndexOutOfBoundsException (a StringIndexOutOfBoundsException, to be precise).

index1 is negative, because the string "jdk-13" is not contained in path (while path does contains the string "jdk" you try to pass explicitly when it works), so indexOf returns -1 (because it didn't find the substring) and substring doesn't expect a negative number as its first parameter.

In other words, read the documentation for indexOf and substring. All those problems are explained there.

As an aside, this is not an error you're getting from the compiler, it's a runtime exception. You'd better learn the difference sooner rather than later.

Federico klez Culloca
  • 26,308
  • 17
  • 56
  • 95
0

I tried out your code as posted. It compiles fine (no compile-time errors), but it did throw this when I ran it:

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: begin -1, end 0, length 21

That error came from this line, clearly index has a value of "-1":

String oldjdk=path.substring(index1,index2);

Let's confirm that by adding this:

System.out.println("index1: " + index1);

Sure enough, it is "-1":

index1: -1

Why did it end up as "-1"? The value index1 is set from this line: int index1=path.indexOf(jdk) – what are the other values, for path and jdk? Let's print them out, too:

int index1 = path.indexOf(jdk);
System.out.println("path: " + path);
System.out.println("jdk: " + jdk);
System.out.println("index1: " + index1);

Here's the new output, which shows clearly that the value for jdk – that is, "jdk-13" – does not appear as a substring anywhere within the value for path. If we look at the Javadoc for substring() we see that the method returns -1 if no value is found in the string.

path: /usr/java/jdk1.8/bin/
jdk: jdk-13
index1: -1

Your original questions:

why does it throw an error when you use the variable jdk?

It throws an error because you're trying to replace a substring which doesn't exist, but your code isn't checking for that (remember, it does give you a "-1" return value, you could check that before proceeding in your code).

As a small addition, you could add a guard before calling substring(), this wouldn't fix the program entirely, but it would prevent it from blowing up with an exception:

if (index1 >= 0) {
    oldjdk = path.substring(index1, index2);
    return path.replace(oldjdk, jdk);
} else {
    System.out.println("index1 is a non-positive number: " + index1);
    return path; // since modification didn't work, return the original path
}

why does it work correctly when you use a hard-coded value of "jdk"?

It works correctly in that case because you're using a hard-coded value – "jdk" – which does exist as a substring in the value of path. That is, "/usr/java/jdk1.8/bin/" contains a substring "jdk", so the code that looks for index (int index1=path.indexOf(jdk)) will return a value that works with the rest of the code.

Kaan
  • 5,434
  • 3
  • 19
  • 41
0

As you may have already been apprised, the start of your dilemma is within the changePath() method on this one line of code: int index1=path.indexOf(jdk);.

This is the beginning of the end for the changePath() method and it is because the method's parameter variable named jdk is passed an argument of "jdk-13" which simply does not exist within the path string which is also supplied to this method as an argument for the path parameter. Because "jdk-13" does not exist within the supplied path of "/usr/java/jdk1.8/bin/", the String#indexOf() method returns an index of -1 which is a no can do for any argument passed to a String#substring() method or any other method that requires an index value. A value of -1 is an invalid index value and will always be returned from the String#indexOf() method if the supplied string to get an index for can not be located. This actually real handy for a lot of different situations.

On a side:

Now, I don't know why you want to access different JDK /bin/ directories and quite honestly, I don't care however you do need to know that the java directory which holds your JDK's should not necessarily be hard-coded into your code (String path = "/usr/java/jdk1.8/bin/";). I'm sure you did this just as an example but I thought I would throw this out there anyways. It's actually a bad idea for the simple reason that upon a JDK installation, this directory can potentially be named anything and placed anywhere from one system to another, for example in my System, I have the Java JRE on one drive and the JDK on another drive.

When I run an application from my IDE the System.getProperties("java.home") property points to the JDK on my local drive D: in a specific path format (the JDK also contains a jre). If I were to now compile the same application into a JAR file and now run it from a Command Window, the System.getProperties("java.home") property points to the JRE on my local drive C: in a completely different path format. It would be better to let code determine this path, for example:

When application is run from the IDE:

String javaHome = System.getProperty("java.home");
String lastFolder = javaHome.substring(javaHome.lastIndexOf("\\") + 1);

String path = javaHome.replace(java.io.File.separator, "/").replace("/jre", "/bin/");

String jdk13 = "jdk-13";
// Using the changeJdkPath() method:
String newPath = changeJdkPath(path, jdk13);
System.out.println(newPath);

When application is compiled and run as a JAR file from a Command Window:

String javaHome = System.getProperty("java.home");
String lastFolder = javaHome.substring(javaHome.lastIndexOf("\\") + 1);

String path = path = javaHome.replace(java.io.File.separator, "/") + "/bin/";

String jre13 = "jre-13";
// Using the changeJrePath() method:
String newPath = changeJrePath(path, jre13);
System.out.println(newPath);

Putting it all together:

Below is an example runnable application which you can run from the IDE or compile it and run as a JAR file from a Command Window. Run the application both ways to see the results. Here is the runnable code:

public class Change_JDK_Path_Demo {

    public static void main(String[] args) {
        /* Depending on instalation, the JDK Java home directory can be 
           named anything and placed litteraly anywhere including on any
           local drive. This is only good if you run your code within the 
           IDE. These paths change within the System Properties when the 
           application is compiled and run as a JAR file from within a 
           Command Window and it would be the JRE you need to change to 
           in order to access its' /bin/ directory.                    */ 
        String javaHome = System.getProperty("java.home");
        String lastFolder = javaHome.substring(javaHome.lastIndexOf("\\") + 1);
        String path;
        String jdk_jre;
        
        // If running from IDE - change JDK folder
        if (lastFolder.length() == 3) {
            path = javaHome.replace(java.io.File.separator, "/").replace("/jre", "/bin/");
            jdk_jre = "jdk-13";
        }
        // Running as JAR - change JRE folder
        else {
            path = javaHome.replace(java.io.File.separator, "/") + "/bin/";
            jdk_jre = "jre-13";
        }
        
        System.out.println("Original Path is: -> " + path);
        String newPath = changeJdkOrJrePath(path, jdk_jre);
        System.out.println("New Path is:      -> " + newPath);
    }
    
    public static String changeJdkOrJrePath(String jdkORjrePath, String newJDKorJRE) {
        // Handle passed arguments that are either Null or Empty.
        if (jdkORjrePath == null || jdkORjrePath.trim().isEmpty()) {
            return null;
        }
        if (newJDKorJRE == null || newJDKorJRE.trim().isEmpty()) {
            return jdkORjrePath;
        }
        
        // Get parent directory name for JDK's or JRE's from jdkORjrePath:
        String parent = "java";  // An Assumed Default.
        String[] pathParts = jdkORjrePath.split("/");
        for (int i = 0; i < pathParts.length; i++) {
            String part = pathParts[i].toLowerCase();
            if (part.startsWith(newJDKorJRE.toLowerCase().startsWith("jdk") ? "jdk" : "jre")) {
                parent = pathParts[i-1] + "/";
                break;
            }
        }
        
        /* Retrieve the current JDK or JRE directory indicated after the 
           JDK's or JRE's parent directory. This could potentialy be any 
           JDK or any JRE.                                            */
        String oldJDK = jdkORjrePath.substring(jdkORjrePath.indexOf(parent) + parent.length(),
                   jdkORjrePath.indexOf("/", (jdkORjrePath.indexOf(parent) + parent.length())));
    
        /* Now replace that determined JDK or JRE with the JDK or 
           JRE passed to this method and return the new path:                  */
        return jdkORjrePath.replace(oldJDK, newJDKorJRE);
    }
    
}

When I run this code from my IDE I get a result of:

Original Path is: -> D:/JAVA/jdk1.8.0_111/bin/
New Path is:      -> D:/JAVA/jdk-13/bin/

When I run this code from a Command Window as a compiled JAR file I get a result of:

Original Path is: -> C:/Program Files/Java/jre1.8.0_311/bin/
New Path is:      -> C:/Program Files/Java/jre-13/bin/
DevilsHnd - 退職した
  • 8,739
  • 2
  • 19
  • 22