diff --git a/core-java-modules/core-java-15/pom.xml b/core-java-modules/core-java-15/pom.xml
index 8cb6c2410d..a4923054ac 100644
--- a/core-java-modules/core-java-15/pom.xml
+++ b/core-java-modules/core-java-15/pom.xml
@@ -21,6 +21,11 @@
commons-lang3
${commons-lang3.version}
+
+ commons-io
+ commons-io
+ ${commons-io.version}
+
diff --git a/core-java-modules/core-java-15/src/main/java/com/baeldung/hidden/classes/HiddenClass.java b/core-java-modules/core-java-15/src/main/java/com/baeldung/hidden/classes/HiddenClass.java
new file mode 100644
index 0000000000..d90e512b39
--- /dev/null
+++ b/core-java-modules/core-java-15/src/main/java/com/baeldung/hidden/classes/HiddenClass.java
@@ -0,0 +1,7 @@
+package com.baeldung.hidden.classes;
+
+public class HiddenClass {
+ public String convertToUpperCase(String s) {
+ return s.toUpperCase();
+ }
+}
diff --git a/core-java-modules/core-java-15/src/test/java/com/baeldung/hidden/classes/HiddenClassUnitTest.java b/core-java-modules/core-java-15/src/test/java/com/baeldung/hidden/classes/HiddenClassUnitTest.java
new file mode 100644
index 0000000000..ac6670b208
--- /dev/null
+++ b/core-java-modules/core-java-15/src/test/java/com/baeldung/hidden/classes/HiddenClassUnitTest.java
@@ -0,0 +1,60 @@
+package com.baeldung.hidden.classes;
+
+
+import java.io.InputStream;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup.ClassOption;
+import java.lang.reflect.Method;
+
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HiddenClassUnitTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(HiddenClassUnitTest.class);
+
+ @Test
+ void whenInvokeMethodOnHiddenClass_thenSuccess() {
+ // Initiate lookup class
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+ // Create a byte code of a class
+
+ Class> clazz = HiddenClass.class;
+ String className = clazz.getName();
+ String classAsPath = className.replace('.', '/') + ".class";
+ InputStream stream = clazz.getClassLoader()
+ .getResourceAsStream(classAsPath);
+
+ try {
+ // Define hidden class with byte code
+ Class> hiddenClass = lookup.defineHiddenClass(IOUtils.toByteArray(stream), true, ClassOption.NESTMATE)
+ .lookupClass();
+ Object hiddenClassObject = hiddenClass.getConstructor()
+ .newInstance();
+
+ Method method = hiddenClassObject.getClass()
+ .getDeclaredMethod("convertToUpperCase", String.class);
+
+ Assertions.assertEquals(true, hiddenClass.isHidden());
+
+ Assertions.assertEquals("HELLO", method.invoke(hiddenClassObject, "Hello"));
+
+ Assertions.assertEquals(this.getClass()
+ .getClassLoader(), hiddenClass.getClassLoader());
+
+ Assertions.assertEquals(null, hiddenClass.getCanonicalName());
+
+ Assertions.assertThrows(ClassNotFoundException.class, () -> Class.forName(hiddenClass.getName()));
+
+ Assertions.assertThrows(ClassNotFoundException.class, () -> lookup.findClass(hiddenClass.getName()));
+
+ } catch (Exception e) {
+ LOG.error("Couldn't instantiate hidden class" + e);
+ }
+ }
+
+}