diff --git a/src/java.base/share/classes/java/text/MessageFormat.java b/src/java.base/share/classes/java/text/MessageFormat.java
index e304d072411401b427f7c169179dbed2113e8372..5ac88f0cfaa8a6f6654fd79c01497e8e3c21c0c1 100644
--- a/src/java.base/share/classes/java/text/MessageFormat.java
+++ b/src/java.base/share/classes/java/text/MessageFormat.java
@@ -41,6 +41,7 @@ package java.text;
 import java.io.InvalidObjectException;
 import java.io.IOException;
 import java.io.ObjectInputStream;
+import java.io.ObjectStreamException;
 import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -983,6 +984,8 @@ public class MessageFormat extends Format {
                 maximumArgumentNumber = argumentNumbers[i];
             }
         }
+
+        // Constructors/applyPattern ensure that resultArray.length < MAX_ARGUMENT_INDEX
         Object[] resultArray = new Object[maximumArgumentNumber + 1];
 
         int patternOffset = 0;
@@ -1235,6 +1238,9 @@ public class MessageFormat extends Format {
      * @serial
      */
     private int[] argumentNumbers = new int[INITIAL_FORMATS];
+    // Implementation limit for ArgumentIndex pattern element. Valid indices must
+    // be less than this value
+    private static final int MAX_ARGUMENT_INDEX = 10000;
 
     /**
      * One less than the number of entries in {@code offsets}.  Can also be thought of
@@ -1459,6 +1465,11 @@ public class MessageFormat extends Format {
                                                + argumentNumber);
         }
 
+        if (argumentNumber >= MAX_ARGUMENT_INDEX) {
+            throw new IllegalArgumentException(
+                    argumentNumber + " exceeds the ArgumentIndex implementation limit");
+        }
+
         // resize format information arrays if necessary
         if (offsetNumber >= formats.length) {
             int newLength = formats.length * 2;
@@ -1606,24 +1617,53 @@ public class MessageFormat extends Format {
      */
     @java.io.Serial
     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
-        in.defaultReadObject();
-        boolean isValid = maxOffset >= -1
-                && formats.length > maxOffset
-                && offsets.length > maxOffset
-                && argumentNumbers.length > maxOffset;
+        ObjectInputStream.GetField fields = in.readFields();
+        if (fields.defaulted("argumentNumbers") || fields.defaulted("offsets")
+                || fields.defaulted("formats") || fields.defaulted("locale")
+                || fields.defaulted("pattern") || fields.defaulted("maxOffset")){
+            throw new InvalidObjectException("Stream has missing data");
+        }
+
+        locale = (Locale) fields.get("locale", null);
+        String patt = (String) fields.get("pattern", null);
+        int maxOff = fields.get("maxOffset", -2);
+        int[] argNums = ((int[]) fields.get("argumentNumbers", null)).clone();
+        int[] offs = ((int[]) fields.get("offsets", null)).clone();
+        Format[] fmts = ((Format[]) fields.get("formats", null)).clone();
+
+        // Check arrays/maxOffset have correct value/length
+        boolean isValid = maxOff >= -1 && argNums.length > maxOff
+                && offs.length > maxOff && fmts.length > maxOff;
+
+        // Check the correctness of arguments and offsets
         if (isValid) {
-            int lastOffset = pattern.length() + 1;
-            for (int i = maxOffset; i >= 0; --i) {
-                if ((offsets[i] < 0) || (offsets[i] > lastOffset)) {
+            int lastOffset = patt.length() + 1;
+            for (int i = maxOff; i >= 0; --i) {
+                if (argNums[i] < 0 || argNums[i] >= MAX_ARGUMENT_INDEX
+                        || offs[i] < 0 || offs[i] > lastOffset) {
                     isValid = false;
                     break;
                 } else {
-                    lastOffset = offsets[i];
+                    lastOffset = offs[i];
                 }
             }
         }
+
         if (!isValid) {
-            throw new InvalidObjectException("Could not reconstruct MessageFormat from corrupt stream.");
+            throw new InvalidObjectException("Stream has invalid data");
         }
+        maxOffset = maxOff;
+        pattern = patt;
+        offsets = offs;
+        formats = fmts;
+        argumentNumbers = argNums;
+    }
+
+    /**
+     * Serialization without data not supported for this class.
+     */
+    @java.io.Serial
+    private void readObjectNoData() throws ObjectStreamException {
+        throw new InvalidObjectException("Deserialized MessageFormat objects need data");
     }
 }
diff --git a/test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.java b/test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e12dabb638388d593033214123c826f4f8c3eadd
--- /dev/null
+++ b/test/jdk/java/text/Format/MessageFormat/MaxArgumentIndexTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2024, 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 8331446
+ * @summary Enforce the MAX_ARGUMENT_INDEX(10,000) implementation limit for the
+ *          ArgumentIndex element in the MessageFormat pattern syntax. This
+ *          should be checked during construction/applyPattern/readObject and should effectively
+ *          prevent parse/format from being invoked with values over the limit.
+ * @run junit MaxArgumentIndexTest
+ */
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class MaxArgumentIndexTest {
+
+    // A MessageFormat pattern that contains an ArgumentIndex value
+    // which violates this implementation's limit: MAX_ARGUMENT_INDEX(10,000)
+    // As this check is exclusive, 10,000 will violate the limit
+    private static final String VIOLATES_MAX_ARGUMENT_INDEX = "{10000}";
+
+    // Check String constructor enforces the limit
+    @Test
+    public void constructorTest() {
+        assertThrows(IllegalArgumentException.class,
+                () -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX));
+    }
+
+    // Check String, Locale constructor enforces the limit
+    @ParameterizedTest
+    @MethodSource
+    public void constructorWithLocaleTest(Locale locale) {
+        assertThrows(IllegalArgumentException.class,
+                () -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX, locale));
+    }
+
+    // Provide some basic common locale values
+    private static Stream<Locale> constructorWithLocaleTest() {
+        return Stream.of(null, Locale.US, Locale.ROOT);
+    }
+
+    // Edge case: Test a locale dependent subformat (with null locale) with a
+    // violating ArgumentIndex. In this instance, the violating ArgumentIndex
+    // will be caught and IAE thrown instead of the NPE
+    @Test
+    public void localeDependentSubFormatTest() {
+        assertThrows(IllegalArgumentException.class,
+                () -> new MessageFormat("{10000,number,short}", null));
+        // For reference
+        assertThrows(NullPointerException.class,
+                () -> new MessageFormat("{999,number,short}", null));
+    }
+
+    // Check that the static format method enforces the limit
+    @Test
+    public void staticFormatTest() {
+        assertThrows(IllegalArgumentException.class,
+                () -> MessageFormat.format(VIOLATES_MAX_ARGUMENT_INDEX, new Object[]{1}));
+    }
+
+    // Check that applyPattern(String) enforces the limit
+    @Test
+    public void applyPatternTest() {
+        MessageFormat mf = new MessageFormat("");
+        assertThrows(IllegalArgumentException.class,
+                () -> mf.applyPattern(VIOLATES_MAX_ARGUMENT_INDEX));
+    }
+}
diff --git a/test/jdk/java/text/Format/MessageFormat/SerializationTest.java b/test/jdk/java/text/Format/MessageFormat/SerializationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9191c5caef35ff290704fb909dc0443619bf8bf1
--- /dev/null
+++ b/test/jdk/java/text/Format/MessageFormat/SerializationTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2024, 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 8331446
+ * @summary Check correctness of deserialization
+ * @run junit SerializationTest
+ */
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SerializationTest {
+
+    // Ensure basic correctness of serialization round trip
+    @ParameterizedTest
+    @MethodSource
+    public void serializationRoundTrip(MessageFormat expectedMf)
+            throws IOException, ClassNotFoundException {
+        byte[] bytes = ser(expectedMf);
+        MessageFormat actualMf = (MessageFormat) deSer(bytes);
+        assertEquals(expectedMf, actualMf);
+    }
+
+    // Various valid MessageFormats
+    private static Stream<MessageFormat> serializationRoundTrip() {
+        return Stream.of(
+                // basic pattern
+                new MessageFormat("{0} foo"),
+                // Multiple arguments
+                new MessageFormat("{0} {1} foo"),
+                // duplicate arguments
+                new MessageFormat("{0} {0} {1} foo"),
+                // Non-ascending arguments
+                new MessageFormat("{1} {0} foo"),
+                // With locale
+                new MessageFormat("{1} {0} foo", Locale.UK),
+                // With null locale. (NPE not thrown, if no format defined)
+                new MessageFormat("{1} {0} foo", null),
+                // With formats
+                new MessageFormat("{0,number,short} {0} {1,date,long} foo")
+        );
+    }
+
+    // Utility method to serialize
+    private static byte[] ser(Object obj) throws IOException {
+        ByteArrayOutputStream byteArrayOutputStream = new
+                ByteArrayOutputStream();
+        ObjectOutputStream oos = new
+                ObjectOutputStream(byteArrayOutputStream);
+        oos.writeObject(obj);
+        return byteArrayOutputStream.toByteArray();
+    }
+
+    // Utility method to deserialize
+    private static Object deSer(byte[] bytes) throws
+            IOException, ClassNotFoundException {
+        ByteArrayInputStream byteArrayInputStream = new
+                ByteArrayInputStream(bytes);
+        ObjectInputStream ois = new
+                ObjectInputStream(byteArrayInputStream);
+        return ois.readObject();
+    }
+}