Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions JavaReleases/src/test/java/pl/mperor/lab/java/Java0.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ public void testProperties() throws IOException {
Assertions.assertNull(properties.get("timeout")); // Hashtable.get ignores defaults

// Properties extends Hashtable<Object, Object> - 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"));
}
Expand Down
131 changes: 110 additions & 21 deletions JavaReleases/src/test/java/pl/mperor/lab/java/Java1.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,47 +13,52 @@
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;
import java.rmi.registry.LocateRegistry;
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;

import static pl.mperor.lab.java.lang.OuterClass.StaticNestedClass;

/**
* Java 1.1 (February 1997)
*
* <p>
* - 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
* <p>
* - 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)
* <p>
* - 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);

Expand All @@ -65,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<String> 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
Expand Down Expand Up @@ -151,7 +220,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.
Expand All @@ -167,7 +236,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"));
}
}
}
Expand All @@ -190,4 +259,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);
}

}
Loading