From c68847921435c6202f216d9ce8b4df25f958908f Mon Sep 17 00:00:00 2001 From: mperor Date: Sat, 26 Sep 2026 13:19:55 +0200 Subject: [PATCH 1/3] Clarify Properties internals, comment nested classes and use placeholder name in JDBC test --- .../test/java/pl/mperor/lab/java/Java0.java | 2 + .../test/java/pl/mperor/lab/java/Java1.java | 44 ++++++++++--------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/JavaReleases/src/test/java/pl/mperor/lab/java/Java0.java b/JavaReleases/src/test/java/pl/mperor/lab/java/Java0.java index e340177..9ecbee7 100644 --- a/JavaReleases/src/test/java/pl/mperor/lab/java/Java0.java +++ b/JavaReleases/src/test/java/pl/mperor/lab/java/Java0.java @@ -99,6 +99,8 @@ public void testProperties() throws IOException { Assertions.assertNull(properties.get("timeout")); // Hashtable.get ignores defaults // Properties extends Hashtable - a design flaw, as non-String values can be put but not read back + // Since Java 9 the inherited Hashtable storage is unused - entries live in an internal ConcurrentHashMap (lock-free reads) + Assertions.assertInstanceOf(Hashtable.class, properties); properties.put("port", 8080); Assertions.assertNull(properties.getProperty("port")); } diff --git a/JavaReleases/src/test/java/pl/mperor/lab/java/Java1.java b/JavaReleases/src/test/java/pl/mperor/lab/java/Java1.java index 5f6f6e4..3619bfe 100644 --- a/JavaReleases/src/test/java/pl/mperor/lab/java/Java1.java +++ b/JavaReleases/src/test/java/pl/mperor/lab/java/Java1.java @@ -27,33 +27,35 @@ /** * Java 1.1 (February 1997) - * + *

* - LANGUAGE FEATURES: - * - Inner classes (member, local & anonymous) - * - Instance initializer blocks - * - Class literals (`String.class`) - * - Blank finals - * + * - Inner classes (member, local & anonymous) + * - Instance initializer blocks + * - Class literals (`String.class`) + * - Blank finals + *

* - LIBRARIES & APIs: - * - JavaBeans - * - JDBC (Java Database Connectivity) - * - RMI (Remote Method Invocation) - * - Reflection API - * - Object Serialization - * - Character Streams (`Reader` / `Writer`) & Unicode 2.0 - * - Internationalization (`java.text`, `Locale`, `ResourceBundle`) - * - Arbitrary-Precision Arithmetic (`java.math`) - * - AWT Event Delegation Model - * - JNI (Java Native Interface) - * + * - JavaBeans + * - JDBC (Java Database Connectivity) + * - RMI (Remote Method Invocation) + * - Reflection API + * - Object Serialization + * - Character Streams (`Reader` / `Writer`) & Unicode 2.0 + * - Internationalization (`java.text`, `Locale`, `ResourceBundle`) + * - Arbitrary-Precision Arithmetic (`java.math`) + * - AWT Event Delegation Model + * - JNI (Java Native Interface) + *

* - TOOLS: - * - JAR files (`jar`) - * - Signed JARs (`javakey`) + * - JAR files (`jar`) + * - Signed JARs (`javakey`) */ public class Java1 { @Test public void testInnerAndNestedStaticClasses() { + // Java 1.1 added nested classes - a static nested class needs no outer instance, + // while an inner class is bound to one and can access its members via OuterClass.this StaticNestedClass nested = new StaticNestedClass(); Assertions.assertNotNull(nested); @@ -151,7 +153,7 @@ public String getMessage() throws RemoteException { @Test public void testJavaDatabaseConnectivityAkaJDBC() throws SQLException, ClassNotFoundException { String createTableUsersSqlCommand = "CREATE TABLE IF NOT EXISTS users (id INT AUTO_INCREMENT PRIMARY KEY, name TEXT)"; - String insertUserSqlCommand = "INSERT INTO users (name) VALUES ('Mark Pi')"; + String insertUserSqlCommand = "INSERT INTO users (name) VALUES ('John Doe')"; String queryAllUsersSqlCommand = "SELECT * FROM users"; // Load the H2 JDBC driver: its static initializer registers it in DriverManager. @@ -167,7 +169,7 @@ public void testJavaDatabaseConnectivityAkaJDBC() throws SQLException, ClassNotF try (ResultSet resultSet = statement.executeQuery(queryAllUsersSqlCommand)) { Assertions.assertTrue(resultSet.next()); Assertions.assertEquals(1, resultSet.getInt("id")); - Assertions.assertEquals("Mark Pi", resultSet.getString("name")); + Assertions.assertEquals("John Doe", resultSet.getString("name")); } } } From 92b48de0d49b60862744100085c66254ec0fe7ec Mon Sep 17 00:00:00 2001 From: mperor Date: Sun, 27 Sep 2026 10:49:17 +0200 Subject: [PATCH 2/3] Add arbitrary-precision arithmetic test to Java 1.1 --- .../test/java/pl/mperor/lab/java/Java1.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/JavaReleases/src/test/java/pl/mperor/lab/java/Java1.java b/JavaReleases/src/test/java/pl/mperor/lab/java/Java1.java index 3619bfe..304e0b9 100644 --- a/JavaReleases/src/test/java/pl/mperor/lab/java/Java1.java +++ b/JavaReleases/src/test/java/pl/mperor/lab/java/Java1.java @@ -13,6 +13,8 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.math.BigInteger; import java.rmi.NotBoundException; import java.rmi.Remote; import java.rmi.RemoteException; @@ -192,4 +194,24 @@ public void testCharacterStreams() throws IOException { } } + @Test + public void testArbitraryPrecisionArithmetic() { + // Java 1.1 added java.math - double cannot represent most decimal fractions exactly, BigDecimal can + Assertions.assertNotEquals(0.3, 0.1 + 0.2); + Assertions.assertEquals(new BigDecimal("0.3"), new BigDecimal("0.1").add(new BigDecimal("0.2"))); + + // BigDecimal(double) keeps the binary approximation of the double - prefer the String constructor + Assertions.assertNotEquals(new BigDecimal("0.1"), new BigDecimal(0.1)); + Assertions.assertEquals(new BigDecimal("0.1"), BigDecimal.valueOf(0.1)); // valueOf (Java 5) goes through Double.toString + + // equals compares value & scale, while compareTo compares only the numerical value + Assertions.assertNotEquals(new BigDecimal("2.0"), new BigDecimal("2.00")); + Assertions.assertEquals(0, new BigDecimal("2.0").compareTo(new BigDecimal("2.00"))); + + // long silently overflows, while BigInteger has no fixed upper bound + Assertions.assertEquals(Long.MIN_VALUE, Long.MAX_VALUE + 1); + BigInteger beyondLong = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE); + Assertions.assertEquals(new BigInteger("9223372036854775808"), beyondLong); + } + } \ No newline at end of file From 7c1294ed1ce6039c6d3b1acd0a45264e08e8f974 Mon Sep 17 00:00:00 2001 From: mperor Date: Mon, 28 Sep 2026 17:36:53 +0200 Subject: [PATCH 3/3] Add Java 1.1 language feature tests: local & anonymous classes, initializers, blank finals, class literals --- .../test/java/pl/mperor/lab/java/Java1.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/JavaReleases/src/test/java/pl/mperor/lab/java/Java1.java b/JavaReleases/src/test/java/pl/mperor/lab/java/Java1.java index 304e0b9..9d5bd1b 100644 --- a/JavaReleases/src/test/java/pl/mperor/lab/java/Java1.java +++ b/JavaReleases/src/test/java/pl/mperor/lab/java/Java1.java @@ -22,6 +22,7 @@ import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import java.sql.*; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -69,6 +70,70 @@ public void testInnerAndNestedStaticClasses() { Assertions.assertEquals("inner secret", inner.getSecret()); } + @Test + public void testLocalAndAnonymousClasses() { + // Local & anonymous classes can capture local variables - they had to be final in Java 1.1, effectively final since Java 8 + String greeting = "Hello"; + + class LocalGreeter { + String greet(String name) { + return greeting + " " + name; + } + } + + Object anonymous = new Object() { + @Override + public String toString() { + return greeting + " from anonymous"; + } + }; + + Assertions.assertEquals("Hello John", new LocalGreeter().greet("John")); + Assertions.assertEquals("Hello from anonymous", anonymous.toString()); + Assertions.assertTrue(LocalGreeter.class.isLocalClass()); + Assertions.assertTrue(anonymous.getClass().isAnonymousClass()); + } + + @Test + public void testInstanceInitializerAndBlankFinal() { + // Instance initializer blocks run after field initializers (in textual order) and before the constructor body + var initialization = new Initialization(42); + Assertions.assertEquals(List.of("field", "initializer", "constructor"), initialization.steps); + + // A blank final is declared without a value and must be assigned exactly once in every constructor + Assertions.assertEquals(42, initialization.id); + } + + static class Initialization { + + final List steps = new ArrayList<>(List.of("field")); + final int id; // blank final + + { + steps.add("initializer"); + } + + Initialization(int id) { + this.id = id; + steps.add("constructor"); + } + + } + + @Test + public void testClassLiterals() { + // Class literals give the Class object at compile time - no instance or Class.forName lookup needed + Assertions.assertSame(String.class, "text".getClass()); + Assertions.assertEquals("java.lang.String", String.class.getName()); + + // Primitives, arrays & void have their own Class objects, distinct from the wrapper classes + Assertions.assertTrue(int.class.isPrimitive()); + Assertions.assertSame(int.class, Integer.TYPE); + Assertions.assertNotSame(int.class, Integer.class); + Assertions.assertSame(void.class, Void.TYPE); + Assertions.assertEquals("[I", int[].class.getName()); + } + @Test public void testJavaBean() throws IntrospectionException { var bean = new JavaBean(); // public no-arg constructor