44import org .junit .jupiter .api .Test ;
55import org .junit .jupiter .api .Timeout ;
66
7- import java .util . Enumeration ;
8- import java .util . Hashtable ;
9- import java .util .Vector ;
7+ import java .io . ByteArrayInputStream ;
8+ import java .io . IOException ;
9+ import java .util .* ;
1010
1111/**
1212 * Java 1.0 (January 1996)
13- *
13+ * <p>
1414 * - LANGUAGE FEATURES:
15- * - Classes & interfaces (single inheritance, multiple interface implementation)
16- * - Packages & access modifiers
17- * - Exceptions (checked & unchecked)
18- * - Threads & `synchronized` built into the language
19- * - Garbage collection
20- * - 16-bit Unicode `char`
21- *
15+ * - Classes & interfaces (single inheritance, multiple interface implementation)
16+ * - Packages & access modifiers
17+ * - Exceptions (checked & unchecked)
18+ * - Threads & `synchronized` built into the language
19+ * - Garbage collection
20+ * - 16-bit Unicode `char`
21+ * <p>
2222 * - LIBRARIES & APIs:
23- * - Legacy collections (`Vector`, `Hashtable`, `Stack`, `Enumeration`, `BitSet`)
24- * - Byte streams (`InputStream` / `OutputStream`)
25- * - Networking (`Socket`, `ServerSocket`, `URL`)
26- * - Utilities (`Date`, `Random`, `Properties`, `StringTokenizer`, `Observable` / `Observer`)
27- * - AWT (Abstract Window Toolkit)
28- * - Applets (`java.applet`)
29- *
23+ * - Legacy collections (`Vector`, `Hashtable`, `Stack`, `Enumeration`, `BitSet`)
24+ * - Byte streams (`InputStream` / `OutputStream`)
25+ * - Networking (`Socket`, `ServerSocket`, `URL`)
26+ * - Utilities (`Date`, `Random`, `Properties`, `StringTokenizer`, `Observable` / `Observer`)
27+ * - AWT (Abstract Window Toolkit)
28+ * - Applets (`java.applet`)
29+ * <p>
3030 * - TOOLS:
31- * - `javac`, `java`, `javadoc`, `jdb`, `javap`
32- * - `appletviewer`
33- * - `javah` (native headers)
31+ * - `javac`, `java`, `javadoc`, `jdb`, `javap`
32+ * - `appletviewer`
33+ * - `javah` (native headers)
3434 */
3535public class Java0 {
3636
3737 @ Test
38- public void testLegacyCollections () {
39- // Before the Collections Framework (Java 1.2) there were only Vector, Hashtable & Enumeration (all synchronized)
40- Vector <String > vector = new Vector <>();
38+ public void testCovariantArrays () {
39+ // Arrays have been covariant since Java 1.0 - String[] is an Object[], so type errors surface only at runtime
40+ Object [] objects = new String [1 ];
41+ objects [0 ] = "text" ;
42+ Assertions .assertThrows (ArrayStoreException .class , () -> objects [0 ] = 1 );
43+ // Generics (Java 5) are invariant instead - List<Object> list = new ArrayList<String>() does not compile
44+ }
45+
46+ @ Test
47+ @ SuppressWarnings ({"rawtypes" , "unchecked" })
48+ public void testRawTypes () {
49+ // Before generics (Java 5) collections held plain Objects - any type could be added and every read needed a cast
50+ Vector vector = new Vector ();
4151 vector .addElement ("a" );
42- vector .addElement ("b" );
43- Assertions .assertEquals ("a" , vector .firstElement ());
44- Assertions .assertEquals ("b" , vector .elementAt (1 ));
52+ vector .addElement (1 );
53+ Assertions .assertEquals ("a" , (String ) vector .elementAt (0 ));
54+ Assertions .assertThrows (ClassCastException .class , () -> {
55+ String text = (String ) vector .elementAt (1 ); // type error surfaces only at runtime
56+ });
57+ }
4558
46- Enumeration <String > elements = vector .elements ();
47- Assertions .assertEquals ("a" , elements .nextElement ());
48- Assertions .assertEquals ("b" , elements .nextElement ());
49- Assertions .assertFalse (elements .hasMoreElements ());
59+ @ Test
60+ public void testStringTokenizer () {
61+ // Before String.split (Java 1.4) StringTokenizer was the only built-in way to split text
62+ StringTokenizer tokenizer = new StringTokenizer ("a,,b" , "," );
63+ Assertions .assertEquals (2 , tokenizer .countTokens ()); // empty tokens are skipped
64+ Assertions .assertEquals ("a" , tokenizer .nextToken ());
65+ Assertions .assertEquals ("b" , tokenizer .nextToken ());
66+ Assertions .assertFalse (tokenizer .hasMoreTokens ());
5067
51- Hashtable <String , Integer > hashtable = new Hashtable <>();
52- hashtable .put ("one" , 1 );
53- Assertions .assertEquals (1 , hashtable .get ("one" ));
54- Assertions .assertThrows (NullPointerException .class , () -> hashtable .put ("two" , null )); // null keys & values are not allowed
68+ Assertions .assertArrayEquals (new String []{"a" , "" , "b" }, "a,,b" .split ("," )); // split keeps empty tokens
69+ }
70+
71+ @ Test
72+ @ SuppressWarnings ("deprecation" )
73+ public void testDateQuirks () {
74+ // java.util.Date counts years from 1900 and months from 0 - Java 1.0 release date:
75+ Date releaseDate = new Date (96 , 0 , 23 );
76+ Assertions .assertEquals (96 , releaseDate .getYear ());
77+ Assertions .assertEquals (0 , releaseDate .getMonth ());
78+ Assertions .assertEquals (23 , releaseDate .getDate ());
79+
80+ // Date is mutable, so a shared instance can be changed by anyone holding a reference (java.time is immutable since Java 8)
81+ Date sharedDate = releaseDate ;
82+ sharedDate .setDate (24 );
83+ Assertions .assertEquals (24 , releaseDate .getDate ());
84+ }
85+
86+ @ Test
87+ public void testProperties () throws IOException {
88+ // Properties loads key=value text and falls back to defaults for missing keys
89+ Properties defaults = new Properties ();
90+ defaults .setProperty ("timeout" , "30" );
91+ Properties properties = new Properties (defaults );
92+ properties .load (new ByteArrayInputStream ("""
93+ # comment
94+ host = localhost
95+ """ .getBytes ()));
96+
97+ Assertions .assertEquals ("localhost" , properties .getProperty ("host" ));
98+ Assertions .assertEquals ("30" , properties .getProperty ("timeout" ));
99+ Assertions .assertNull (properties .get ("timeout" )); // Hashtable.get ignores defaults
100+
101+ // Properties extends Hashtable<Object, Object> - a design flaw, as non-String values can be put but not read back
102+ properties .put ("port" , 8080 );
103+ Assertions .assertNull (properties .getProperty ("port" ));
104+ }
105+
106+ @ Test
107+ public void testRandomWithSeed () {
108+ // Random is a deterministic pseudo-random generator - the same seed always yields the same sequence
109+ Random first = new Random (42 );
110+ Random second = new Random (42 );
111+ for (int i = 0 ; i < 5 ; i ++) {
112+ Assertions .assertEquals (first .nextInt (), second .nextInt ());
113+ }
114+ }
115+
116+ @ Test
117+ public void testUnicodeSupport () {
118+ // Since Java 1.0 `char` is a 16-bit Unicode code unit, covering the Basic Multilingual Plane (BMP)
119+ char englishLetter = '\u0041' ; // Latin capital letter A (A)
120+ char polishLetter = '\u0104' ; // Latin capital letter A with ogonek (Ą)
121+ char greekLetter = '\u0391' ; // Greek capital letter Alpha (Α)
122+ char chineseCharacter = '\u4E2D' ; // Chinese character for "middle" (中)
123+
124+ Assertions .assertEquals ('A' , englishLetter );
125+ Assertions .assertEquals ('Ą' , polishLetter );
126+ Assertions .assertEquals ('Α' , greekLetter );
127+ Assertions .assertEquals ('中' , chineseCharacter );
128+ Assertions .assertNotEquals (englishLetter , greekLetter ); // look alike, but different characters
129+ }
130+
131+ @ Test
132+ public void testExceptionsAndFinally () {
133+ // Checked exceptions must be declared with `throws`, unchecked ones (RuntimeException subclasses) need not be
134+ Assertions .assertThrows (IOException .class , Java0 ::throwChecked );
135+ Assertions .assertThrows (IllegalStateException .class , Java0 ::throwUnchecked );
136+
137+ // `return` in `finally` overrides the result of `try` and silently swallows any thrown exception
138+ Assertions .assertEquals ("finally" , returnFromTryAndFinally ());
139+ Assertions .assertEquals ("finally" , throwFromTryAndReturnFromFinally ());
140+ }
141+
142+ private static void throwChecked () throws IOException {
143+ throw new IOException ("must be declared" );
144+ }
145+
146+ private static void throwUnchecked () {
147+ throw new IllegalStateException ("no declaration needed" );
148+ }
149+
150+ @ SuppressWarnings ("finally" )
151+ private static String returnFromTryAndFinally () {
152+ try {
153+ return "try" ;
154+ } finally {
155+ return "finally" ;
156+ }
157+ }
158+
159+ @ SuppressWarnings ("finally" )
160+ private static String throwFromTryAndReturnFromFinally () {
161+ try {
162+ throw new IllegalStateException ("lost" );
163+ } finally {
164+ return "finally" ;
165+ }
55166 }
56167
57168 @ Test
@@ -67,6 +178,7 @@ public void testWaitAndNotify() throws InterruptedException {
67178 }
68179
69180 static class Mailbox {
181+
70182 private String message ;
71183
72184 synchronized void put (String message ) {
@@ -80,21 +192,64 @@ synchronized String take() throws InterruptedException {
80192 }
81193 return message ;
82194 }
195+
83196 }
84197
85198 @ Test
86- public void testUnicodeSupport () {
87- // Since Java 1.0 `char` is a 16-bit Unicode code unit, covering the Basic Multilingual Plane (BMP)
88- char englishLetter = '\u0041' ; // Latin capital letter A (A)
89- char polishLetter = '\u0104' ; // Latin capital letter A with ogonek (Ą)
90- char greekLetter = '\u0391' ; // Greek capital letter Alpha (Α)
91- char chineseCharacter = '\u4E2D' ; // Chinese character for "middle" (中)
199+ public void testLegacyCollections () {
200+ // Before the Collections Framework (Java 1.2) there were only Vector, Hashtable, Stack, Enumeration & BitSet
201+ assertVectorAndEnumeration ();
202+ assertHashtable ();
203+ assertStack ();
204+ assertBitSet ();
205+ }
92206
93- Assertions .assertEquals ('A' , englishLetter );
94- Assertions .assertEquals ('Ą' , polishLetter );
95- Assertions .assertEquals ('Α' , greekLetter );
96- Assertions .assertEquals ('中' , chineseCharacter );
97- Assertions .assertNotEquals (englishLetter , greekLetter ); // look alike, but different characters
207+ private void assertVectorAndEnumeration () {
208+ Vector <String > vector = new Vector <>();
209+ vector .addElement ("a" );
210+ vector .addElement ("b" );
211+ Assertions .assertEquals ("a" , vector .firstElement ());
212+ Assertions .assertEquals ("b" , vector .elementAt (1 ));
213+
214+ Enumeration <String > elements = vector .elements ();
215+ Assertions .assertEquals ("a" , elements .nextElement ());
216+ Assertions .assertEquals ("b" , elements .nextElement ());
217+ Assertions .assertFalse (elements .hasMoreElements ());
218+ }
219+
220+ private void assertHashtable () {
221+ Hashtable <String , Integer > hashtable = new Hashtable <>();
222+ hashtable .put ("one" , 1 );
223+ Assertions .assertEquals (1 , hashtable .get ("one" ));
224+ Assertions .assertThrows (NullPointerException .class , () -> hashtable .put ("two" , null )); // null keys & values are not allowed
225+ }
226+
227+ private void assertStack () {
228+ // Stack extends Vector - a design flaw, as it exposes index-based methods that break LIFO (Deque is preferred since Java 6)
229+ Stack <String > stack = new Stack <>();
230+ stack .push ("bottom" );
231+ stack .push ("top" );
232+ Assertions .assertEquals ("top" , stack .peek ());
233+ Assertions .assertEquals (1 , stack .search ("top" )); // 1-based distance from the top
234+
235+ stack .insertElementAt ("sneaky" , 1 ); // inherited from Vector
236+ Assertions .assertEquals ("top" , stack .pop ());
237+ Assertions .assertEquals ("sneaky" , stack .pop ());
238+ Assertions .assertEquals ("bottom" , stack .pop ());
239+ Assertions .assertTrue (stack .empty ());
240+ }
241+
242+ private void assertBitSet () {
243+ // BitSet is a growable vector of bits supporting logical operations
244+ BitSet evens = new BitSet ();
245+ BitSet lowNumbers = new BitSet ();
246+ for (int i = 0 ; i < 10 ; i ++) {
247+ if (i % 2 == 0 ) evens .set (i );
248+ if (i < 5 ) lowNumbers .set (i );
249+ }
250+ evens .and (lowNumbers );
251+ Assertions .assertEquals ("{0, 2, 4}" , evens .toString ());
252+ Assertions .assertFalse (evens .get (6 ));
98253 }
99254
100255}
0 commit comments