diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java index 8a77ab230a1..14695521bc1 100644 --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -377,7 +377,7 @@ public final class Class implements java.io.Serializable, * the current class. * *

For example, the following code fragment returns the - * runtime {@code Class} descriptor for the class named + * runtime {@code Class} object for the class named * {@code java.lang.Thread}: * * {@snippet lang="java" : @@ -392,9 +392,10 @@ public final class Class implements java.io.Serializable, * caller frame on the stack (e.g. when called directly from a JNI * attached thread), the system class loader is used. * - * @param className the fully qualified name of the desired class. - * @return the {@code Class} object for the class with the - * specified name. + * @param className the {@linkplain ClassLoader##binary-name binary name} + * of the class or the string representing an array type + * @return the {@code Class} object for the class with the + * specified name. * @throws LinkageError if the linkage fails * @throws ExceptionInInitializerError if the initialization provoked * by this method fails @@ -423,45 +424,52 @@ public final class Class implements java.io.Serializable, /** * Returns the {@code Class} object associated with the class or * interface with the given string name, using the given class loader. - * Given the fully qualified name for a class or interface (in the same - * format returned by {@code getName}) this method attempts to - * locate and load the class or interface. The specified class - * loader is used to load the class or interface. If the parameter - * {@code loader} is null, the class is loaded through the bootstrap + * Given the {@linkplain ClassLoader##binary-name binary name} for a class or interface, + * this method attempts to locate and load the class or interface. The specified + * class loader is used to load the class or interface. If the parameter + * {@code loader} is {@code null}, the class is loaded through the bootstrap * class loader. The class is initialized only if the * {@code initialize} parameter is {@code true} and if it has * not been initialized earlier. * - *

If {@code name} denotes a primitive type or void, an attempt - * will be made to locate a user-defined class in the unnamed package whose - * name is {@code name}. Therefore, this method cannot be used to - * obtain any of the {@code Class} objects representing primitive - * types or void. + *

This method cannot be used to obtain any of the {@code Class} objects + * representing primitive types or void, hidden classes or interfaces, + * or array classes whose element type is a hidden class or interface. + * If {@code name} denotes a primitive type or void, for example {@code I}, + * an attempt will be made to locate a user-defined class in the unnamed package + * whose name is {@code I} instead. * - *

If {@code name} denotes an array class, the component type of - * the array class is loaded but not initialized. - * - *

For example, in an instance method the expression: + *

To obtain the {@code Class} object associated with an array class, + * the name consists of one or more {@code '['} representing the depth + * of the array nesting, followed by the element type as encoded in + * {@linkplain ##nameFormat the table} specified in {@code Class.getName()}. * + *

Examples: * {@snippet lang="java" : - * Class.forName("Foo") + * Class threadClass = Class.forName("java.lang.Thread", false, currentLoader); + * Class stringArrayClass = Class.forName("[Ljava.lang.String;", false, currentLoader); + * Class intArrayClass = Class.forName("[[[I", false, currentLoader); // Class of int[][][] + * Class nestedClass = Class.forName("java.lang.Character$UnicodeBlock", false, currentLoader); + * Class fooClass = Class.forName("Foo", true, currentLoader); * } * - * is equivalent to: + *

A call to {@code getName()} on the {@code Class} object returned + * from {@code forName(}N{@code )} returns N. * - * {@snippet lang="java" : - * Class.forName("Foo", true, this.getClass().getClassLoader()) - * } + *

A call to {@code forName("[L}N{@code ;")} causes the element type + * named N to be loaded but not initialized regardless of the value + * of the {@code initialize} parameter. * - * Note that this method throws errors related to loading, linking - * or initializing as specified in Sections {@jls 12.2}, {@jls - * 12.3}, and {@jls 12.4} of The Java Language - * Specification. - * Note that this method does not check whether the requested class + * @apiNote + * This method throws errors related to loading, linking or initializing + * as specified in Sections {@jls 12.2}, {@jls 12.3}, and {@jls 12.4} of + * The Java Language Specification. + * In addition, this method does not check whether the requested class * is accessible to its caller. * - * @param name fully qualified name of the desired class - + * @param name the {@linkplain ClassLoader##binary-name binary name} + * of the class or the string representing an array class + * * @param initialize if {@code true} the class will be initialized * (which implies linking). See Section {@jls * 12.4} of The Java Language @@ -486,6 +494,7 @@ public final class Class implements java.io.Serializable, * @jls 12.2 Loading of Classes and Interfaces * @jls 12.3 Linking of Classes and Interfaces * @jls 12.4 Initialization of Classes and Interfaces + * @jls 13.1 The Form of a Binary * @since 1.2 */ @CallerSensitive @@ -548,7 +557,9 @@ public final class Class implements java.io.Serializable, * accessible to its caller.

* * @apiNote - * This method returns {@code null} on failure rather than + * This method does not support loading of array types, unlike + * {@link #forName(String, boolean, ClassLoader)}. The class name must be + * a binary name. This method returns {@code null} on failure rather than * throwing a {@link ClassNotFoundException}, as is done by * the {@link #forName(String, boolean, ClassLoader)} method. * The security check is a stack-based permission check if the caller @@ -898,7 +909,7 @@ public final class Class implements java.io.Serializable, * representing the depth of the array nesting, followed by the element * type as encoded using the following table: * - *
+ *
* * *
Element types and encodings
Element Type Encoding @@ -925,6 +936,8 @@ public final class Class implements java.io.Serializable, *
      * String.class.getName()
      *     returns "java.lang.String"
+     * Character.UnicodeBlock.class.getName()
+     *     returns "java.lang.Character$UnicodeBlock"
      * byte.class.getName()
      *     returns "byte"
      * (new Object[3]).getClass().getName()
diff --git a/test/jdk/java/lang/Class/forName/ForNameNames.java b/test/jdk/java/lang/Class/forName/ForNameNames.java
new file mode 100644
index 00000000000..282329a3fd5
--- /dev/null
+++ b/test/jdk/java/lang/Class/forName/ForNameNames.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8310242
+ * @run junit ForNameNames
+ * @summary Verify class names for Class.forName
+ */
+
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import static org.junit.jupiter.api.Assertions.*;
+
+public class ForNameNames {
+    static class Inner {}
+    static Stream testCases() {
+        return Stream.of(
+                Arguments.of("java.lang.String", String.class),
+                Arguments.of("[Ljava.lang.String;", String[].class),
+                Arguments.of("ForNameNames$Inner", Inner.class),
+                Arguments.of("[LForNameNames$Inner;", Inner.class),
+                Arguments.of("[[I", int[][].class)
+        );
+    }
+
+    /*
+     * Test 1-arg and 3-arg Class::forName.  Class::getName on the returned
+     * Class object returns the name passed to Class::forName.
+     */
+    @ParameterizedTest
+    @MethodSource("testCases")
+    void testForName(String cn, Class expected) throws ClassNotFoundException {
+        ClassLoader loader = ForNameNames.class.getClassLoader();
+        Class c1 = Class.forName(cn, false, loader);
+        assertEquals(expected, c1);
+        assertEquals(cn, c1.getName());
+
+        Class c2 = Class.forName(cn);
+        assertEquals(expected, c2);
+        assertEquals(cn, c2.getName());
+    }
+
+    static Stream invalidNames() {
+        return Stream.of(
+                Arguments.of("I"),                   // primitive type
+                Arguments.of("int[]"),               // fully-qualified name of int array
+                Arguments.of("ForNameNames.Inner"),  // fully-qualified name of nested type
+                Arguments.of("[java.lang.String"),   // missing L and ;
+                Arguments.of("[Ljava.lang.String"),  // missing ;
+                Arguments.of("[Ljava/lang/String;")  // type descriptor
+
+        );
+    }
+    @ParameterizedTest
+    @MethodSource("invalidNames")
+    void testInvalidNames(String cn) {
+        ClassLoader loader = ForNameNames.class.getClassLoader();
+        assertThrows(ClassNotFoundException.class, () -> Class.forName(cn, false, loader));
+    }
+
+    @Test
+    void testModule() {
+        // Class.forName(Module, String) does not allow class name for array types
+        Class c = Class.forName(Object.class.getModule(), "[Ljava.lang.String;");
+        assertNull(c);
+    }
+
+}