The first example shows a stack trace element consisting of * three elements, each separated by {@code "/"} followed with * the source file name and the line number of the source line * containing the execution point. * * The first element "{@code com.foo.loader}" is * the name of the class loader. The second element "{@code foo@9.0}" * is the module name and version. The third element is the method * containing the execution point; "{@code com.foo.Main"}" is the * fully-qualified class name and "{@code run}" is the name of the method. * "{@code Main.java}" is the source file name and "{@code 101}" is * the line number. * *
If a class is defined in an unnamed module * then the second element is omitted as shown in * "{@code com.foo.loader//com.foo.bar.App.run(App.java:12)}". * *
If the class loader is a * built-in class loader or is not named then the first element * and its following {@code "/"} are omitted as shown in * "{@code acme@2.1/org.acme.Lib.test(Lib.java:80)}". * If the first element is omitted and the module is an unnamed module, * the second element and its following {@code "/"} are also omitted * as shown in "{@code MyClass.mash(MyClass.java:9)}". * *
Note that different values can be returned from {@link toString()} for * StackTraceElements that are otherwise {@linkplain #equals(Object) equal}. * For instance, the class loader name is omitted for built-in class loaders, * but not for other class loaders. * * @see Throwable#printStackTrace() */ public String toString() { String s = ""; if (!has(BUILTIN_CLASS_LOADER) && classLoaderName != null && !classLoaderName.isEmpty()) { s += classLoaderName + "/"; } if (moduleName != null && !moduleName.isEmpty()) { s += moduleName; if (!has(JDK_NON_UPGRADEABLE_MODULE) && moduleVersion != null && !moduleVersion.isEmpty()) { s += "@" + moduleVersion; } } s = s.isEmpty() ? declaringClass : s + "/" + declaringClass; return s + "." + methodName + "(" + (isNativeMethod() ? "Native Method)" : (fileName != null && lineNumber >= 0 ? fileName + ":" + lineNumber + ")" : (fileName != null ? ""+fileName+")" : "Unknown Source)"))); } /** * Returns true if the specified object is another * {@code StackTraceElement} instance representing the same execution * point as this instance. Two stack trace elements {@code a} and * {@code b} are equal if and only if: *
{@code * equals(a.getClassLoaderName(), b.getClassLoaderName()) && * equals(a.getModuleName(), b.getModuleName()) && * equals(a.getModuleVersion(), b.getModuleVersion()) && * equals(a.getClassName(), b.getClassName()) && * equals(a.getMethodName(), b.getMethodName()) * equals(a.getFileName(), b.getFileName()) && * a.getLineNumber() == b.getLineNumber() * * }* where {@code equals} has the semantics of {@link * java.util.Objects#equals(Object, Object) Objects.equals}. * * @apiNote StackTraceElements can be equal, but return different * {@link toString()} values, due to the specifics of how the * {@link toString()} return value is constructed. * * @param obj the object to be compared with this stack trace element. * @return true if the specified object is another * {@code StackTraceElement} instance representing the same * execution point as this instance. */ public boolean equals(Object obj) { if (obj==this) return true; if (!(obj instanceof StackTraceElement)) return false; StackTraceElement e = (StackTraceElement)obj; return Objects.equals(classLoaderName, e.classLoaderName) && Objects.equals(moduleName, e.moduleName) && Objects.equals(moduleVersion, e.moduleVersion) && e.declaringClass.equals(declaringClass) && e.lineNumber == lineNumber && Objects.equals(methodName, e.methodName) && Objects.equals(fileName, e.fileName); } /** * Returns a hash code value for this stack trace element. */ public int hashCode() { int result = 31*declaringClass.hashCode() + methodName.hashCode(); result = 31*result + Objects.hashCode(classLoaderName); result = 31*result + Objects.hashCode(moduleName); result = 31*result + Objects.hashCode(moduleVersion); result = 31*result + Objects.hashCode(fileName); result = 31*result + lineNumber; return result; } /** * Called from of() methods to set the 'format' bitmap using the Class * reference stored in declaringClassObject, and then clear the reference. */ private synchronized void computeFormat() { try { Class> cls = (Class>) declaringClassObject; ClassLoader loader = cls.getClassLoader0(); Module m = cls.getModule(); byte bits = 0; // First element - class loader name // Call package-private ClassLoader::name method if (loader instanceof BuiltinClassLoader) { bits |= BUILTIN_CLASS_LOADER; } // Second element - module name and version // Omit if is a JDK non-upgradeable module (recorded in the hashes // in java.base) if (m != null && m.isNamed() && (isHashedInJavaBase(m) || !m.getDescriptor().version().isPresent())) { bits |= JDK_NON_UPGRADEABLE_MODULE; } format = bits; } finally { // Class reference no longer needed, clear it declaringClassObject = null; } } private static final byte BUILTIN_CLASS_LOADER = 0x1; private static final byte JDK_NON_UPGRADEABLE_MODULE = 0x2; private boolean has(byte bit) { return (format & bit) == bit; } /** * Returns true if the module is hashed with java.base. *
* This method returns false when running on the exploded image
* since JDK modules are not hashed. They have no Version attribute
* and so "@