From c47f63d48480eaf3e694d815706612a97468f2b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=E5=98=89=E9=98=B3?= Date: Sun, 17 Jun 2018 19:26:58 +0800 Subject: [PATCH] 11.10 Map --- .idea/Thinking in Java源代码.iml | 13 + .idea/inspectionProfiles/Project_Default.xml | 36 + .idea/misc.xml | 4 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + Copyright.txt | 66 ++ DEclipse.py | 25 + Eclipse.py | 115 +++ FindBugsExcluder.py | 53 ++ FindBugsFilter.xml | 547 ++++++++++++ JavaLint.py | 31 + OutputGenerator.py | 133 +++ OutputVerifier.py | 200 +++++ RedundantImportDetector.py | 54 ++ build.xml | 128 +++ chapterOrder.xml | 28 + src/access/Cake.java | 11 + src/access/ChocolateChip.java | 19 + src/access/ChocolateChip2.java | 17 + src/access/Dinner.java | 12 + src/access/FullQualification.java | 7 + src/access/IceCream.java | 16 + src/access/ImportedMyClass.java | 8 + src/access/LibTest.java | 13 + src/access/Lunch.java | 36 + src/access/OrganizedByAccess.java | 12 + src/access/Pie.java | 6 + src/access/PrintTest.java | 17 + src/access/QualifiedMyClass.java | 8 + src/access/SingleImport.java | 8 + src/access/build.xml | 189 +++++ src/access/cookie2/Cookie.java | 11 + src/access/dessert/Cookie.java | 10 + src/access/mypackage/MyClass.java | 6 + src/annotations/AtUnitComposition.java | 26 + src/annotations/AtUnitExample1.java | 39 + src/annotations/AtUnitExample2.java | 48 ++ src/annotations/AtUnitExample3.java | 36 + src/annotations/AtUnitExample4.java | 75 ++ src/annotations/AtUnitExample5.java | 52 ++ src/annotations/AtUnitExternalTest.java | 22 + src/annotations/ExtractInterface.java | 10 + src/annotations/HashSetTest.java | 31 + .../InterfaceExtractorProcessor.java | 64 ++ .../InterfaceExtractorProcessorFactory.java | 22 + src/annotations/Multiplier.java | 21 + src/annotations/PasswordUtils.java | 20 + src/annotations/SimulatingNull.java | 9 + src/annotations/StackL.java | 11 + src/annotations/StackLStringTest.java | 36 + src/annotations/Testable.java | 10 + src/annotations/UseCase.java | 9 + src/annotations/UseCaseTracker.java | 30 + src/annotations/build.xml | 225 +++++ src/annotations/database/Constraints.java | 11 + src/annotations/database/DBTable.java | 9 + src/annotations/database/Member.java | 18 + src/annotations/database/SQLInteger.java | 10 + src/annotations/database/SQLString.java | 11 + .../TableCreationProcessorFactory.java | 105 +++ src/annotations/database/TableCreator.java | 103 +++ src/annotations/database/Uniqueness.java | 8 + src/arrays/AlphabeticSearch.java | 20 + src/arrays/ArrayOfGenericType.java | 13 + src/arrays/ArrayOfGenerics.java | 29 + src/arrays/ArrayOptions.java | 71 ++ src/arrays/ArraySearching.java | 28 + .../AssemblingMultidimensionalArrays.java | 19 + src/arrays/AutoboxingArrays.java | 16 + src/arrays/CompType.java | 55 ++ src/arrays/ComparatorTest.java | 36 + src/arrays/ComparingArrays.java | 25 + src/arrays/ContainerComparison.java | 47 ++ src/arrays/CopyingArrays.java | 42 + src/arrays/FillingArrays.java | 51 ++ src/arrays/GeneratorsTest.java | 33 + src/arrays/IceCream.java | 42 + src/arrays/MultiDimWrapperArray.java | 29 + src/arrays/MultidimensionalObjectArrays.java | 19 + .../MultidimensionalPrimitiveArray.java | 15 + src/arrays/ParameterizedArrayType.java | 22 + .../PrimitiveConversionDemonstration.java | 19 + src/arrays/RaggedArray.java | 19 + src/arrays/RandomGeneratorsTest.java | 18 + src/arrays/Reverse.java | 30 + src/arrays/StringSorting.java | 24 + src/arrays/TestArrayGeneration.java | 45 + src/arrays/TestGenerated.java | 19 + src/arrays/ThreeDWithNew.java | 12 + src/arrays/build.xml | 330 ++++++++ src/bangbean/BangBean.java | 83 ++ src/bangbean/BangBeanTest.java | 32 + src/bangbean/build.xml | 73 ++ src/concurrency/ActiveObjectDemo.java | 92 ++ src/concurrency/AtomicEvenGenerator.java | 15 + src/concurrency/AtomicIntegerTest.java | 33 + src/concurrency/Atomicity.java | 29 + src/concurrency/AtomicityTest.java | 27 + src/concurrency/AttemptLocking.java | 57 ++ src/concurrency/BankTellerSimulation.java | 212 +++++ src/concurrency/BasicThreads.java | 13 + src/concurrency/CachedThreadPool.java | 14 + src/concurrency/CallableDemo.java | 48 ++ src/concurrency/CaptureUncaughtException.java | 47 ++ src/concurrency/CarBuilder.java | 211 +++++ src/concurrency/Chopstick.java | 17 + src/concurrency/CloseResource.java | 37 + src/concurrency/CountDownLatchDemo.java | 68 ++ src/concurrency/CriticalSection.java | 141 ++++ src/concurrency/DaemonFromFactory.java | 27 + src/concurrency/Daemons.java | 44 + src/concurrency/DaemonsDontRunFinally.java | 27 + .../DeadlockingDiningPhilosophers.java | 33 + src/concurrency/DelayQueueDemo.java | 94 +++ src/concurrency/EvenChecker.java | 33 + src/concurrency/EvenGenerator.java | 18 + src/concurrency/ExceptionThread.java | 13 + src/concurrency/ExchangerDemo.java | 79 ++ src/concurrency/ExplicitCriticalSection.java | 48 ++ src/concurrency/FastSimulation.java | 57 ++ src/concurrency/Fat.java | 16 + src/concurrency/FixedDiningPhilosophers.java | 38 + src/concurrency/FixedThreadPool.java | 15 + src/concurrency/GreenhouseScheduler.java | 159 ++++ src/concurrency/HorseRace.java | 91 ++ src/concurrency/IntGenerator.java | 9 + src/concurrency/Interrupting.java | 87 ++ src/concurrency/Interrupting2.java | 47 ++ src/concurrency/InterruptingIdiom.java | 77 ++ src/concurrency/Joining.java | 56 ++ src/concurrency/LiftOff.java | 22 + src/concurrency/ListComparisons.java | 92 ++ src/concurrency/MainThread.java | 10 + src/concurrency/MapComparisons.java | 98 +++ src/concurrency/MoreBasicThreads.java | 14 + src/concurrency/MultiLock.java | 37 + src/concurrency/MutexEvenGenerator.java | 23 + src/concurrency/NIOInterruption.java | 53 ++ src/concurrency/NaiveExceptionHandling.java | 16 + src/concurrency/NotifyVsNotifyAll.java | 79 ++ src/concurrency/OrnamentalGarden.java | 108 +++ src/concurrency/Philosopher.java | 47 ++ src/concurrency/PipedIO.java | 58 ++ src/concurrency/Pool.java | 55 ++ .../PriorityBlockingQueueDemo.java | 122 +++ src/concurrency/ReaderWriterList.java | 86 ++ src/concurrency/ResponsiveUI.java | 32 + src/concurrency/Restaurant.java | 89 ++ src/concurrency/SelfManaged.java | 27 + src/concurrency/SemaphoreDemo.java | 70 ++ src/concurrency/SerialNumberChecker.java | 68 ++ src/concurrency/SerialNumberGenerator.java | 8 + src/concurrency/SettingDefaultHandler.java | 13 + src/concurrency/SimpleDaemons.java | 39 + src/concurrency/SimpleMicroBenchmark.java | 46 + src/concurrency/SimplePriorities.java | 53 ++ src/concurrency/SimpleThread.java | 30 + src/concurrency/SingleThreadExecutor.java | 15 + src/concurrency/SleepingTask.java | 28 + src/concurrency/SyncObject.java | 44 + .../SynchronizationComparisons.java | 268 ++++++ .../SynchronizedEvenGenerator.java | 17 + src/concurrency/TestBlockingQueues.java | 68 ++ src/concurrency/Tester.java | 79 ++ .../ThreadLocalVariableHolder.java | 54 ++ src/concurrency/ThreadVariations.java | 163 ++++ src/concurrency/ToastOMatic.java | 134 +++ src/concurrency/build.xml | 779 +++++++++++++++++ .../restaurant2/RestaurantWithQueues.java | 207 +++++ src/concurrency/waxomatic/WaxOMatic.java | 81 ++ src/concurrency/waxomatic2/WaxOMatic2.java | 102 +++ src/containers/AssociativeArray.java | 64 ++ src/containers/Bits.java | 79 ++ src/containers/CanonicalMapping.java | 48 ++ src/containers/CollectionDataGeneration.java | 18 + src/containers/CollectionDataTest.java | 23 + src/containers/CollectionMethods.java | 74 ++ src/containers/CountedString.java | 66 ++ src/containers/DequeTest.java | 31 + src/containers/Enumerations.java | 19 + src/containers/FailFast.java | 18 + src/containers/FillingLists.java | 24 + src/containers/Groundhog.java | 10 + src/containers/Groundhog2.java | 12 + src/containers/IndividualTest.java | 19 + src/containers/LinkedHashMapDemo.java | 31 + src/containers/ListPerformance.java | 220 +++++ src/containers/ListSortSearch.java | 43 + src/containers/Lists.java | 126 +++ src/containers/MapDataTest.java | 48 ++ src/containers/MapEntry.java | 35 + src/containers/MapPerformance.java | 97 +++ src/containers/Maps.java | 60 ++ src/containers/Prediction.java | 15 + src/containers/QueueBehavior.java | 39 + src/containers/RandomBounds.java | 31 + src/containers/ReadOnly.java | 47 ++ src/containers/References.java | 62 ++ src/containers/SetPerformance.java | 76 ++ src/containers/SimpleHashMap.java | 75 ++ src/containers/SlowMap.java | 45 + src/containers/SortedMapDemo.java | 42 + src/containers/SortedSetDemo.java | 43 + src/containers/SpringDetector.java | 33 + src/containers/SpringDetector2.java | 12 + src/containers/Stacks.java | 54 ++ src/containers/StringHashCode.java | 12 + src/containers/Synchronization.java | 22 + src/containers/Test.java | 10 + src/containers/TestParam.java | 29 + src/containers/Tester.java | 85 ++ src/containers/ToDoList.java | 55 ++ src/containers/TypesForSets.java | 75 ++ src/containers/Unsupported.java | 64 ++ src/containers/Utilities.java | 80 ++ src/containers/build.xml | 464 ++++++++++ src/control/BreakAndContinue.java | 46 + src/control/CommaOperator.java | 14 + src/control/ForEachFloat.java | 26 + src/control/ForEachInt.java | 27 + src/control/ForEachString.java | 11 + src/control/IfElse.java | 27 + src/control/IfElse2.java | 23 + src/control/LabeledFor.java | 62 ++ src/control/LabeledWhile.java | 48 ++ src/control/ListCharacters.java | 26 + src/control/VowelsAndConsonants.java | 41 + src/control/WhileTest.java | 16 + src/control/build.xml | 190 +++++ src/enumerated/AlarmPoints.java | 6 + src/enumerated/BigEnumSet.java | 18 + src/enumerated/Burrito.java | 18 + src/enumerated/CarWash.java | 60 ++ src/enumerated/Competitor.java | 7 + src/enumerated/ConstantSpecificMethod.java | 28 + src/enumerated/EnumClass.java | 43 + src/enumerated/EnumMaps.java | 34 + src/enumerated/EnumSets.java | 30 + src/enumerated/Input.java | 27 + src/enumerated/NonEnum.java | 16 + src/enumerated/NotClasses.java | 23 + src/enumerated/Outcome.java | 3 + src/enumerated/OverrideConstantSpecific.java | 20 + src/enumerated/OzWitch.java | 28 + src/enumerated/PostOffice.java | 154 ++++ src/enumerated/RandomTest.java | 15 + src/enumerated/Reflection.java | 60 ++ src/enumerated/RoShamBo.java | 19 + src/enumerated/RoShamBo1.java | 79 ++ src/enumerated/RoShamBo2.java | 48 ++ src/enumerated/RoShamBo3.java | 41 + src/enumerated/RoShamBo4.java | 28 + src/enumerated/RoShamBo5.java | 35 + src/enumerated/RoShamBo6.java | 19 + src/enumerated/SecurityCategory.java | 37 + src/enumerated/SpaceShip.java | 23 + src/enumerated/Spiciness.java | 6 + src/enumerated/TrafficLight.java | 40 + src/enumerated/UpcastEnum.java | 18 + src/enumerated/VendingMachine.java | 163 ++++ src/enumerated/VendingMachineInput.txt | 7 + src/enumerated/build.xml | 360 ++++++++ .../cartoons/EnumImplementation.java | 29 + src/enumerated/menu/Course.java | 17 + src/enumerated/menu/Food.java | 21 + src/enumerated/menu/Meal.java | 40 + src/enumerated/menu/Meal2.java | 43 + src/enumerated/menu/TypeOfFood.java | 12 + src/exceptions/AlwaysFinally.java | 30 + src/exceptions/Cleanup.java | 25 + src/exceptions/CleanupIdiom.java | 66 ++ src/exceptions/DynamicFields.java | 130 +++ src/exceptions/ExceptionMethods.java | 27 + src/exceptions/ExceptionSilencer.java | 13 + src/exceptions/ExtraFeatures.java | 64 ++ src/exceptions/FinallyWorks.java | 31 + src/exceptions/FullConstructors.java | 38 + src/exceptions/Human.java | 27 + src/exceptions/InheritingExceptions.java | 22 + src/exceptions/InputFile.java | 44 + src/exceptions/LoggingExceptions.java | 40 + src/exceptions/LoggingExceptions2.java | 25 + src/exceptions/LostMessage.java | 37 + src/exceptions/MainException.java | 14 + src/exceptions/MultipleReturns.java | 50 ++ src/exceptions/NeverCaught.java | 15 + src/exceptions/OnOffException1.java | 4 + src/exceptions/OnOffException2.java | 4 + src/exceptions/OnOffSwitch.java | 25 + src/exceptions/RethrowNew.java | 42 + src/exceptions/Rethrowing.java | 63 ++ src/exceptions/StormyInning.java | 76 ++ src/exceptions/Switch.java | 10 + src/exceptions/TurnOffChecking.java | 57 ++ src/exceptions/WhoCalled.java | 36 + src/exceptions/WithFinally.java | 22 + src/exceptions/build.xml | 311 +++++++ src/frogbean/Frog.java | 44 + src/frogbean/build.xml | 61 ++ src/generics/Apply.java | 73 ++ src/generics/ArrayMaker.java | 20 + src/generics/ArrayOfGeneric.java | 20 + src/generics/ArrayOfGenericReference.java | 7 + src/generics/BankTeller.java | 61 ++ src/generics/BasicBounds.java | 58 ++ src/generics/BasicGeneratorDemo.java | 18 + src/generics/BasicHolder.java | 10 + src/generics/ByteSet.java | 11 + src/generics/CRGWithBasicHolder.java | 14 + src/generics/CaptureConversion.java | 27 + src/generics/CheckedList.java | 29 + src/generics/ClassCasting.java | 15 + src/generics/ClassTypeCapture.java | 29 + src/generics/ComparablePet.java | 6 + src/generics/CompilerIntelligence.java | 12 + src/generics/CountedObject.java | 8 + src/generics/CovariantArrays.java | 26 + src/generics/CovariantReturnTypes.java | 19 + src/generics/CreatorGeneric.java | 25 + src/generics/CuriouslyRecurringGeneric.java | 6 + src/generics/DogsAndRobots.cpp | 27 + src/generics/DogsAndRobots.java | 38 + src/generics/DynamicProxyMixin.java | 59 ++ src/generics/EpicBattle.java | 64 ++ src/generics/Erased.java | 12 + src/generics/ErasedTypeEquivalence.java | 12 + src/generics/ErasureAndInheritance.java | 25 + src/generics/ExplicitTypeSpecification.java | 11 + src/generics/FactoryConstraint.java | 34 + src/generics/Fibonacci.java | 22 + src/generics/Fill.java | 54 ++ src/generics/Fill2.java | 92 ++ src/generics/FilledListMaker.java | 20 + src/generics/Functional.java | 183 ++++ src/generics/Generators.java | 33 + src/generics/GenericArray.java | 23 + src/generics/GenericArray2.java | 34 + src/generics/GenericArrayWithTypeToken.java | 23 + src/generics/GenericCast.java | 29 + src/generics/GenericHolder.java | 13 + src/generics/GenericMethods.java | 23 + src/generics/GenericReading.java | 42 + src/generics/GenericVarargs.java | 24 + src/generics/GenericWriting.java | 24 + src/generics/GenericsAndCovariance.java | 16 + src/generics/GenericsAndReturnTypes.java | 14 + src/generics/HasF.java | 5 + src/generics/HijackedInterface.java | 8 + src/generics/Holder.java | 30 + src/generics/Holder1.java | 9 + src/generics/Holder2.java | 16 + src/generics/Holder3.java | 15 + src/generics/InheritBounds.java | 36 + src/generics/InstantiateGenericType.cpp | 17 + src/generics/InstantiateGenericType.java | 32 + src/generics/IterableFibonacci.java | 28 + src/generics/LatentReflection.java | 56 ++ src/generics/LimitsOfInference.java | 11 + src/generics/LinkedStack.java | 40 + src/generics/ListMaker.java | 10 + src/generics/ListOfGenerics.java | 8 + src/generics/ListOfInt.java | 18 + src/generics/LostInformation.java | 29 + src/generics/Manipulation.java | 18 + src/generics/Manipulator2.java | 7 + src/generics/Manipulator3.java | 7 + src/generics/Mixins.cpp | 43 + src/generics/Mixins.java | 57 ++ src/generics/MultipleInterfaceVariants.java | 8 + src/generics/NeedCasting.java | 12 + src/generics/NonCovariantGenerics.java | 8 + src/generics/NotSelfBounded.java | 21 + src/generics/OrdinaryArguments.java | 26 + src/generics/Performs.java | 6 + src/generics/PlainGenericInheritance.java | 26 + src/generics/PrimitiveGenericTest.java | 45 + src/generics/RandomList.java | 23 + src/generics/RestrictedComparablePets.java | 12 + src/generics/ReturnGenericType.java | 7 + src/generics/SelfBounding.java | 36 + .../SelfBoundingAndCovariantArguments.java | 16 + src/generics/SelfBoundingMethods.java | 10 + src/generics/SimpleDogsAndRobots.java | 21 + src/generics/SimpleHolder.java | 12 + src/generics/SimpleQueue.java | 12 + src/generics/SimplerPets.java | 11 + src/generics/Store.java | 83 ++ src/generics/SuperTypeWildcards.java | 10 + src/generics/Templates.cpp | 23 + src/generics/ThrowGenericException.java | 77 ++ src/generics/TupleList.java | 20 + src/generics/TupleTest.java | 41 + src/generics/TupleTest2.java | 36 + src/generics/UnboundedWildcards1.java | 40 + src/generics/UnboundedWildcards2.java | 21 + src/generics/Unconstrained.java | 15 + src/generics/UseList.java | 8 + src/generics/UseList2.java | 7 + src/generics/WatercolorSets.java | 34 + src/generics/Wildcards.java | 122 +++ src/generics/build.xml | 789 ++++++++++++++++++ src/generics/coffee/Americano.java | 3 + src/generics/coffee/Breve.java | 3 + src/generics/coffee/Cappuccino.java | 3 + src/generics/coffee/Coffee.java | 10 + src/generics/coffee/CoffeeGenerator.java | 59 ++ src/generics/coffee/Latte.java | 3 + src/generics/coffee/Mocha.java | 3 + src/generics/decorator/Decoration.java | 45 + src/generics/watercolors/Watercolors.java | 11 + src/gui/BangBean2.java | 112 +++ src/gui/BeanDumper.java | 92 ++ src/gui/BorderLayout1.java | 18 + src/gui/Borders.java | 37 + src/gui/Button1.java | 19 + src/gui/Button2.java | 31 + src/gui/Button2b.java | 30 + src/gui/ButtonGroups.java | 48 ++ src/gui/Buttons.java | 33 + src/gui/CheckBoxes.java | 46 + src/gui/ColorBoxes.java | 56 ++ src/gui/ComboBoxes.java | 43 + src/gui/Dialogs.java | 38 + src/gui/Face0.gif | Bin 0 -> 261 bytes src/gui/Face1.gif | Bin 0 -> 289 bytes src/gui/Face2.gif | Bin 0 -> 310 bytes src/gui/Face3.gif | Bin 0 -> 235 bytes src/gui/Face4.gif | Bin 0 -> 259 bytes src/gui/Faces.java | 57 ++ src/gui/FileChooserTest.java | 63 ++ src/gui/FlowLayout1.java | 17 + src/gui/GridLayout1.java | 17 + src/gui/HTMLButton.java | 27 + src/gui/HelloLabel.java | 16 + src/gui/HelloSwing.java | 11 + src/gui/InterruptableLongRunningCallable.java | 61 ++ src/gui/InterruptableLongRunningTask.java | 52 ++ src/gui/List.java | 66 ++ src/gui/LongRunningTask.java | 38 + src/gui/LookAndFeel.java | 64 ++ src/gui/Menus.java | 156 ++++ src/gui/MessageBoxes.java | 63 ++ src/gui/MonitoredLongRunningCallable.java | 90 ++ src/gui/Popup.java | 52 ++ src/gui/Progress.java | 36 + src/gui/RadioButtons.java | 36 + src/gui/ShowAddListeners.java | 59 ++ src/gui/SimpleMenus.java | 41 + src/gui/SineWave.java | 62 ++ src/gui/SubmitLabelManipulationTask.java | 20 + src/gui/SubmitSwingProgram.java | 27 + src/gui/TabbedPane1.java | 34 + src/gui/TextArea.java | 40 + src/gui/TextFields.java | 85 ++ src/gui/TextPane.java | 28 + src/gui/TicTacToe.java | 84 ++ src/gui/TrackEvent.java | 93 +++ src/gui/build.xml | 595 +++++++++++++ src/gui/flex/Song.java | 35 + src/gui/flex/SongService.java | 22 + src/gui/flex/build-command.txt | 1 + src/gui/flex/helloflex1.mxml | 6 + src/gui/flex/helloflex2.mxml | 15 + src/gui/flex/songScript.as | 22 + src/gui/flex/songStyles.css | 11 + src/gui/flex/songs.mxml | 64 ++ src/gui/jnlp/JnlpFileChooser.java | 97 +++ src/gui/jnlp/filechooser.html | 5 + src/gui/jnlp/filechooser.jnlp | 24 + src/gui/jnlp/mindview.gif | Bin 0 -> 12103 bytes src/holding/AdapterMethodIdiom.java | 42 + src/holding/AddingGroups.java | 21 + src/holding/ApplesAndOrangesWithGenerics.java | 27 + .../ApplesAndOrangesWithoutGenerics.java | 28 + src/holding/ArrayIsNotIterable.java | 20 + src/holding/AsListInference.java | 33 + src/holding/CollectionSequence.java | 29 + src/holding/ContainerMethods.java | 38 + src/holding/CrossContainerIteration.java | 28 + src/holding/EnvironmentVariables.java | 11 + src/holding/ForEachCollections.java | 16 + src/holding/GenericsAndUpcasting.java | 25 + src/holding/InterfaceVsIterator.java | 47 ++ src/holding/IterableClass.java | 27 + src/holding/LinkedListFeatures.java | 49 ++ src/holding/ListFeatures.java | 89 ++ src/holding/ListIteration.java | 32 + src/holding/MapOfList.java | 55 ++ src/holding/ModifyingArraysAsList.java | 28 + src/holding/MultiIterableClass.java | 48 ++ src/holding/NonCollectionSequence.java | 28 + src/holding/PetMap.java | 23 + src/holding/PrintingContainers.java | 40 + src/holding/PriorityQueueDemo.java | 48 ++ src/holding/QueueDemo.java | 28 + src/holding/SetOfInteger.java | 15 + src/holding/SetOperations.java | 32 + src/holding/SimpleCollection.java | 16 + src/holding/SimpleIteration.java | 31 + src/holding/SortedSetOfInteger.java | 15 + src/holding/StackCollision.java | 27 + src/holding/StackTest.java | 16 + src/holding/Statistics.java | 20 + src/holding/UniqueWords.java | 13 + src/holding/UniqueWordsAlphabetic.java | 16 + src/holding/build.xml | 430 ++++++++++ src/initialization/Apricot.java | 7 + src/initialization/ArrayClassObj.java | 19 + src/initialization/ArrayInit.java | 23 + src/initialization/ArrayNew.java | 17 + src/initialization/ArraysOfPrimitives.java | 22 + src/initialization/AutoboxingVarargs.java | 19 + src/initialization/BananaPeel.java | 12 + src/initialization/Burrito.java | 32 + src/initialization/Counter.java | 8 + src/initialization/DefaultConstructor.java | 9 + src/initialization/Demotion.java | 60 ++ src/initialization/DynamicArray.java | 18 + src/initialization/EnumOrder.java | 15 + src/initialization/ExplicitStatic.java | 38 + src/initialization/Flower.java | 40 + src/initialization/InitialValues.java | 45 + src/initialization/InitialValues2.java | 13 + src/initialization/Leaf.java | 19 + src/initialization/Measurement.java | 9 + src/initialization/MethodInit.java | 7 + src/initialization/MethodInit2.java | 9 + src/initialization/MethodInit3.java | 9 + src/initialization/Mugs.java | 47 ++ src/initialization/NewVarArgs.java | 28 + src/initialization/NoSynthesis.java | 14 + .../OptionalTrailingArguments.java | 20 + src/initialization/OrderOfInitialization.java | 35 + src/initialization/Overloading.java | 52 ++ src/initialization/OverloadingOrder.java | 19 + src/initialization/OverloadingVarargs.java | 35 + src/initialization/OverloadingVarargs2.java | 15 + src/initialization/OverloadingVarargs3.java | 17 + src/initialization/PassingThis.java | 27 + src/initialization/PrimitiveOverloading.java | 101 +++ src/initialization/SimpleConstructor.java | 18 + src/initialization/SimpleConstructor2.java | 18 + src/initialization/SimpleEnumUse.java | 10 + src/initialization/Spiciness.java | 5 + src/initialization/Spoon.java | 9 + src/initialization/StaticInitialization.java | 70 ++ src/initialization/TerminationCondition.java | 34 + src/initialization/VarArgs.java | 24 + src/initialization/VarargType.java | 25 + src/initialization/build.xml | 401 +++++++++ src/innerclasses/AnonymousConstructor.java | 29 + src/innerclasses/BigEgg.java | 26 + src/innerclasses/BigEgg2.java | 32 + src/innerclasses/Callbacks.java | 72 ++ src/innerclasses/ClassInInterface.java | 16 + src/innerclasses/Contents.java | 6 + src/innerclasses/Destination.java | 6 + src/innerclasses/DotNew.java | 10 + src/innerclasses/DotThis.java | 20 + src/innerclasses/Factories.java | 53 ++ src/innerclasses/Games.java | 52 ++ src/innerclasses/GreenhouseController.java | 38 + src/innerclasses/GreenhouseControls.java | 109 +++ src/innerclasses/InheritInner.java | 17 + src/innerclasses/LocalInnerClass.java | 64 ++ src/innerclasses/MultiImplementation.java | 22 + src/innerclasses/MultiInterfaces.java | 28 + src/innerclasses/MultiNestingAccess.java | 25 + src/innerclasses/Parcel1.java | 29 + src/innerclasses/Parcel10.java | 27 + src/innerclasses/Parcel11.java | 34 + src/innerclasses/Parcel2.java | 37 + src/innerclasses/Parcel3.java | 21 + src/innerclasses/Parcel5.java | 19 + src/innerclasses/Parcel6.java | 25 + src/innerclasses/Parcel7.java | 15 + src/innerclasses/Parcel7b.java | 14 + src/innerclasses/Parcel8.java | 17 + src/innerclasses/Parcel9.java | 18 + src/innerclasses/Sequence.java | 44 + src/innerclasses/TestBed.java | 15 + src/innerclasses/TestParcel.java | 31 + src/innerclasses/Wrapping.java | 8 + src/innerclasses/build.xml | 362 ++++++++ src/innerclasses/controller/Controller.java | 24 + src/innerclasses/controller/Event.java | 19 + src/interfaces/AdaptedRandomDoubles.java | 28 + src/interfaces/Adventure.java | 38 + src/interfaces/Factories.java | 53 ++ src/interfaces/Games.java | 52 ++ src/interfaces/HorrorShow.java | 48 ++ src/interfaces/InterfaceCollision.java | 25 + src/interfaces/Months.java | 11 + src/interfaces/RandVals.java | 12 + src/interfaces/RandomDoubles.java | 15 + src/interfaces/RandomWords.java | 45 + src/interfaces/TestRandVals.java | 16 + src/interfaces/build.xml | 219 +++++ src/interfaces/classprocessor/Apply.java | 51 ++ src/interfaces/filters/BandPass.java | 11 + src/interfaces/filters/Filter.java | 9 + src/interfaces/filters/HighPass.java | 8 + src/interfaces/filters/LowPass.java | 10 + src/interfaces/filters/Waveform.java | 8 + src/interfaces/interfaceprocessor/Apply.java | 10 + .../interfaceprocessor/FilterProcessor.java | 31 + .../interfaceprocessor/Processor.java | 7 + .../interfaceprocessor/StringProcessor.java | 42 + src/interfaces/music4/Music4.java | 81 ++ src/interfaces/music5/Music5.java | 76 ++ src/interfaces/nesting/NestingInterfaces.java | 89 ++ src/io/Alien.java | 4 + src/io/AvailableCharSets.java | 38 + src/io/BasicFileOutput.java | 22 + src/io/Blip3.java | 61 ++ src/io/Blips.java | 65 ++ src/io/BufferToText.java | 55 ++ src/io/BufferedInputFile.java | 23 + src/io/ChangeSystemOut.java | 12 + src/io/ChannelCopy.java | 25 + src/io/DirList.java | 37 + src/io/DirList2.java | 36 + src/io/DirList3.java | 32 + src/io/DirectoryDemo.java | 40 + src/io/Echo.java | 17 + src/io/Endians.java | 25 + src/io/FileLocking.java | 21 + src/io/FileOutputShortcut.java | 22 + src/io/FormattedMemoryInput.java | 19 + src/io/FreezeAlien.java | 12 + src/io/GZIPcompress.java | 37 + src/io/GetChannel.java | 32 + src/io/GetData.java | 56 ++ src/io/IntBufferDemo.java | 31 + src/io/LargeMappedFiles.java | 23 + src/io/LockingMappedFiles.java | 49 ++ src/io/Logon.java | 45 + src/io/MakeDirectories.java | 86 ++ src/io/MappedIO.java | 114 +++ src/io/MemoryInput.java | 14 + src/io/MyWorld.java | 70 ++ src/io/OSExecuteDemo.java | 15 + src/io/PreferencesDemo.java | 30 + src/io/RecoverCADState.java | 31 + src/io/Redirecting.java | 26 + src/io/SerialCtl.java | 44 + src/io/StoreCADState.java | 108 +++ src/io/StoringAndRecoveringData.java | 30 + src/io/TestEOF.java | 15 + src/io/TransferTo.java | 20 + src/io/UsingBuffers.java | 30 + src/io/UsingRandomAccessFile.java | 47 ++ src/io/ViewBuffers.java | 65 ++ src/io/Worm.java | 84 ++ src/io/ZipCompress.java | 65 ++ src/io/build.xml | 529 ++++++++++++ src/io/xfiles/ThawAlien.java | 16 + src/net/build.xml | 200 +++++ src/net/mindview/atunit/AtUnit.java | 168 ++++ src/net/mindview/atunit/AtUnitRemover.java | 70 ++ src/net/mindview/atunit/ClassNameFinder.java | 82 ++ src/net/mindview/atunit/Test.java | 8 + .../mindview/atunit/TestObjectCleanup.java | 8 + src/net/mindview/atunit/TestObjectCreate.java | 8 + src/net/mindview/atunit/TestProperty.java | 9 + src/net/mindview/simple/List.java | 9 + src/net/mindview/simple/Vector.java | 9 + src/net/mindview/util/BasicGenerator.java | 21 + src/net/mindview/util/BinaryFile.java | 22 + src/net/mindview/util/CollectionData.java | 17 + .../util/ContainerMethodDifferences.java | 54 ++ src/net/mindview/util/ConvertTo.java | 61 ++ src/net/mindview/util/CountingGenerator.java | 76 ++ .../mindview/util/CountingIntegerList.java | 21 + src/net/mindview/util/CountingMapData.java | 52 ++ src/net/mindview/util/Countries.java | 244 ++++++ .../mindview/util/DaemonThreadFactory.java | 11 + .../util/DaemonThreadPoolExecutor.java | 12 + src/net/mindview/util/Deque.java | 17 + src/net/mindview/util/Directory.java | 79 ++ src/net/mindview/util/Enums.java | 13 + src/net/mindview/util/FiveTuple.java | 15 + src/net/mindview/util/FourTuple.java | 14 + src/net/mindview/util/Generated.java | 18 + src/net/mindview/util/Generator.java | 4 + src/net/mindview/util/Hex.java | 41 + src/net/mindview/util/MapData.java | 60 ++ src/net/mindview/util/New.java | 31 + src/net/mindview/util/Null.java | 3 + src/net/mindview/util/OSExecute.java | 41 + src/net/mindview/util/OSExecuteException.java | 6 + src/net/mindview/util/PPrint.java | 30 + src/net/mindview/util/Pair.java | 11 + src/net/mindview/util/Print.java | 25 + src/net/mindview/util/ProcessFiles.java | 53 ++ src/net/mindview/util/RandomGenerator.java | 73 ++ src/net/mindview/util/Range.java | 33 + src/net/mindview/util/Sets.java | 28 + src/net/mindview/util/Stack.java | 30 + src/net/mindview/util/SwingConsole.java | 19 + src/net/mindview/util/TaskItem.java | 13 + src/net/mindview/util/TaskManager.java | 44 + src/net/mindview/util/TextFile.java | 85 ++ src/net/mindview/util/ThreeTuple.java | 13 + src/net/mindview/util/Tuple.java | 21 + src/net/mindview/util/TwoTuple.java | 11 + src/net/mindview/util/TypeCounter.java | 41 + src/object/Documentation1.java | 8 + src/object/Documentation2.java | 8 + src/object/Documentation3.java | 11 + src/object/HelloDate.java | 22 + src/object/ShowProperties.java | 10 + src/object/build.xml | 81 ++ src/operators/AllOps.java | 408 +++++++++ src/operators/Assignment.java | 28 + src/operators/AutoInc.java | 24 + src/operators/BitManipulation.java | 93 +++ src/operators/Bool.java | 39 + src/operators/Casting.java | 13 + src/operators/CastingNumbers.java | 20 + src/operators/EqualsMethod.java | 11 + src/operators/EqualsMethod2.java | 17 + src/operators/Equivalence.java | 13 + src/operators/Exponents.java | 17 + src/operators/HelloDate.java | 13 + src/operators/Literals.java | 35 + src/operators/MathOps.java | 72 ++ src/operators/Overflow.java | 14 + src/operators/PassObject.java | 24 + src/operators/Precedence.java | 12 + src/operators/RoundingNumbers.java | 19 + src/operators/ShortCircuit.java | 32 + src/operators/StringOperators.java | 19 + src/operators/TernaryIfElse.java | 26 + src/operators/URShift.java | 38 + src/operators/build.xml | 280 +++++++ src/polymorphism/CovariantReturn.java | 31 + src/polymorphism/FieldAccess.java | 30 + src/polymorphism/Frog.java | 114 +++ src/polymorphism/PolyConstructors.java | 35 + src/polymorphism/PrivateOverride.java | 18 + src/polymorphism/RTTI.java | 31 + src/polymorphism/ReferenceCounting.java | 60 ++ src/polymorphism/Sandwich.java | 46 + src/polymorphism/Shapes.java | 29 + src/polymorphism/StaticPolymorphism.java | 31 + src/polymorphism/Transmogrify.java | 34 + src/polymorphism/build.xml | 220 +++++ src/polymorphism/music/Instrument.java | 10 + src/polymorphism/music/Music.java | 16 + src/polymorphism/music/Music2.java | 40 + src/polymorphism/music/Note.java | 7 + src/polymorphism/music/Wind.java | 11 + src/polymorphism/music3/Music3.java | 70 ++ src/polymorphism/shape/Circle.java | 8 + .../shape/RandomShapeGenerator.java | 16 + src/polymorphism/shape/Shape.java | 7 + src/polymorphism/shape/Square.java | 8 + src/polymorphism/shape/Triangle.java | 8 + src/reusing/Bath.java | 58 ++ src/reusing/Beetle.java | 40 + src/reusing/BlankFinal.java | 26 + src/reusing/CADSystem.java | 104 +++ src/reusing/Car.java | 41 + src/reusing/Cartoon.java | 22 + src/reusing/Chess.java | 30 + src/reusing/Detergent.java | 42 + src/reusing/FinalArguments.java | 24 + src/reusing/FinalData.java | 53 ++ src/reusing/FinalOverridingIllusion.java | 49 ++ src/reusing/Hide.java | 38 + src/reusing/Jurassic.java | 23 + src/reusing/Lisa.java | 8 + src/reusing/Orc.java | 36 + src/reusing/PlaceSetting.java | 79 ++ src/reusing/SpaceShip.java | 11 + src/reusing/SpaceShipControls.java | 11 + src/reusing/SpaceShipDelegation.java | 37 + src/reusing/SprinklerSystem.java | 35 + src/reusing/Wind.java | 19 + src/reusing/build.xml | 261 ++++++ src/strings/ArrayListDisplay.java | 15 + src/strings/BetterRead.java | 33 + src/strings/Concatenation.java | 11 + src/strings/Conversion.java | 109 +++ src/strings/DatabaseException.java | 18 + src/strings/Finding.java | 22 + src/strings/Groups.java | 35 + src/strings/Immutable.java | 19 + src/strings/InfiniteRecursion.java | 18 + src/strings/IntegerMatch.java | 15 + src/strings/JGrep.java | 43 + src/strings/ReFlags.java | 20 + src/strings/Receipt.java | 38 + src/strings/Replacing.java | 13 + src/strings/ReplacingStringTokenizer.java | 22 + src/strings/Resetting.java | 20 + src/strings/Rudolph.java | 15 + src/strings/ScannerDelimiter.java | 18 + src/strings/SimpleFormat.java | 18 + src/strings/SimpleRead.java | 38 + src/strings/SplitDemo.java | 19 + src/strings/Splitting.java | 22 + src/strings/StartEnd.java | 83 ++ src/strings/TestRegularExpression.java | 39 + src/strings/TheReplacements.java | 52 ++ src/strings/ThreatAnalyzer.java | 31 + src/strings/Turtle.java | 35 + src/strings/UsingStringBuilder.java | 22 + src/strings/WhitherStringBuilder.java | 18 + src/strings/build.xml | 366 ++++++++ src/swt/ColorBoxes.java | 82 ++ src/swt/DisplayEnvironment.java | 20 + src/swt/DisplayProperties.java | 25 + src/swt/HelloSWT.java | 19 + src/swt/Menus.java | 47 ++ src/swt/ShellsAreMainWindows.java | 28 + src/swt/SineWave.java | 74 ++ src/swt/TabbedPane.java | 147 ++++ src/swt/build.xml | 173 ++++ src/swt/util/SWTApplication.java | 7 + src/swt/util/SWTConsole.java | 21 + src/typeinfo/AnonymousImplementation.java | 36 + src/typeinfo/BoundedClassReferences.java | 10 + src/typeinfo/ClassCasts.java | 13 + src/typeinfo/ClassInitialization.java | 52 ++ src/typeinfo/FamilyVsExactType.java | 49 ++ src/typeinfo/FilledList.java | 31 + src/typeinfo/GenericClassReferences.java | 11 + src/typeinfo/HiddenImplementation.java | 37 + src/typeinfo/InnerImplementation.java | 35 + src/typeinfo/InterfaceViolation.java | 23 + src/typeinfo/ModifyingPrivateFields.java | 41 + src/typeinfo/NullRobot.java | 56 ++ src/typeinfo/Operation.java | 6 + src/typeinfo/Person.java | 24 + src/typeinfo/PetCount.java | 71 ++ src/typeinfo/PetCount2.java | 8 + src/typeinfo/PetCount3.java | 49 ++ src/typeinfo/PetCount4.java | 19 + src/typeinfo/Position.java | 31 + src/typeinfo/RegisteredFactories.java | 107 +++ src/typeinfo/Robot.java | 22 + src/typeinfo/SelectingMethods.java | 54 ++ src/typeinfo/Shapes.java | 34 + src/typeinfo/ShowMethods.java | 68 ++ src/typeinfo/SimpleDynamicProxy.java | 46 + src/typeinfo/SimpleProxyDemo.java | 47 ++ src/typeinfo/SnowRemovalRobot.java | 49 ++ src/typeinfo/Staff.java | 53 ++ src/typeinfo/SweetShop.java | 39 + src/typeinfo/WildcardClassReferences.java | 8 + src/typeinfo/build.xml | 371 ++++++++ src/typeinfo/factory/Factory.java | 3 + src/typeinfo/interfacea/A.java | 6 + src/typeinfo/packageaccess/HiddenC.java | 16 + src/typeinfo/pets/Cat.java | 7 + src/typeinfo/pets/Cymric.java | 7 + src/typeinfo/pets/Dog.java | 7 + src/typeinfo/pets/EgyptianMau.java | 7 + src/typeinfo/pets/ForNameCreator.java | 32 + src/typeinfo/pets/Hamster.java | 7 + src/typeinfo/pets/Individual.java | 44 + src/typeinfo/pets/LiteralPetCreator.java | 26 + src/typeinfo/pets/Manx.java | 7 + src/typeinfo/pets/Mouse.java | 7 + src/typeinfo/pets/Mutt.java | 7 + src/typeinfo/pets/Person.java | 6 + src/typeinfo/pets/Pet.java | 7 + src/typeinfo/pets/PetCreator.java | 32 + src/typeinfo/pets/Pets.java | 18 + src/typeinfo/pets/Pug.java | 7 + src/typeinfo/pets/Rat.java | 7 + src/typeinfo/pets/Rodent.java | 7 + src/typeinfo/toys/GenericToyTest.java | 16 + src/typeinfo/toys/ToyTest.java | 71 ++ xml/People.java | 22 + xml/Person.java | 72 ++ xml/build.xml | 86 ++ 879 files changed, 39735 insertions(+) create mode 100644 .idea/Thinking in Java源代码.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 Copyright.txt create mode 100644 DEclipse.py create mode 100644 Eclipse.py create mode 100644 FindBugsExcluder.py create mode 100644 FindBugsFilter.xml create mode 100644 JavaLint.py create mode 100644 OutputGenerator.py create mode 100644 OutputVerifier.py create mode 100644 RedundantImportDetector.py create mode 100644 build.xml create mode 100644 chapterOrder.xml create mode 100644 src/access/Cake.java create mode 100644 src/access/ChocolateChip.java create mode 100644 src/access/ChocolateChip2.java create mode 100644 src/access/Dinner.java create mode 100644 src/access/FullQualification.java create mode 100644 src/access/IceCream.java create mode 100644 src/access/ImportedMyClass.java create mode 100644 src/access/LibTest.java create mode 100644 src/access/Lunch.java create mode 100644 src/access/OrganizedByAccess.java create mode 100644 src/access/Pie.java create mode 100644 src/access/PrintTest.java create mode 100644 src/access/QualifiedMyClass.java create mode 100644 src/access/SingleImport.java create mode 100644 src/access/build.xml create mode 100644 src/access/cookie2/Cookie.java create mode 100644 src/access/dessert/Cookie.java create mode 100644 src/access/mypackage/MyClass.java create mode 100644 src/annotations/AtUnitComposition.java create mode 100644 src/annotations/AtUnitExample1.java create mode 100644 src/annotations/AtUnitExample2.java create mode 100644 src/annotations/AtUnitExample3.java create mode 100644 src/annotations/AtUnitExample4.java create mode 100644 src/annotations/AtUnitExample5.java create mode 100644 src/annotations/AtUnitExternalTest.java create mode 100644 src/annotations/ExtractInterface.java create mode 100644 src/annotations/HashSetTest.java create mode 100644 src/annotations/InterfaceExtractorProcessor.java create mode 100644 src/annotations/InterfaceExtractorProcessorFactory.java create mode 100644 src/annotations/Multiplier.java create mode 100644 src/annotations/PasswordUtils.java create mode 100644 src/annotations/SimulatingNull.java create mode 100644 src/annotations/StackL.java create mode 100644 src/annotations/StackLStringTest.java create mode 100644 src/annotations/Testable.java create mode 100644 src/annotations/UseCase.java create mode 100644 src/annotations/UseCaseTracker.java create mode 100644 src/annotations/build.xml create mode 100644 src/annotations/database/Constraints.java create mode 100644 src/annotations/database/DBTable.java create mode 100644 src/annotations/database/Member.java create mode 100644 src/annotations/database/SQLInteger.java create mode 100644 src/annotations/database/SQLString.java create mode 100644 src/annotations/database/TableCreationProcessorFactory.java create mode 100644 src/annotations/database/TableCreator.java create mode 100644 src/annotations/database/Uniqueness.java create mode 100644 src/arrays/AlphabeticSearch.java create mode 100644 src/arrays/ArrayOfGenericType.java create mode 100644 src/arrays/ArrayOfGenerics.java create mode 100644 src/arrays/ArrayOptions.java create mode 100644 src/arrays/ArraySearching.java create mode 100644 src/arrays/AssemblingMultidimensionalArrays.java create mode 100644 src/arrays/AutoboxingArrays.java create mode 100644 src/arrays/CompType.java create mode 100644 src/arrays/ComparatorTest.java create mode 100644 src/arrays/ComparingArrays.java create mode 100644 src/arrays/ContainerComparison.java create mode 100644 src/arrays/CopyingArrays.java create mode 100644 src/arrays/FillingArrays.java create mode 100644 src/arrays/GeneratorsTest.java create mode 100644 src/arrays/IceCream.java create mode 100644 src/arrays/MultiDimWrapperArray.java create mode 100644 src/arrays/MultidimensionalObjectArrays.java create mode 100644 src/arrays/MultidimensionalPrimitiveArray.java create mode 100644 src/arrays/ParameterizedArrayType.java create mode 100644 src/arrays/PrimitiveConversionDemonstration.java create mode 100644 src/arrays/RaggedArray.java create mode 100644 src/arrays/RandomGeneratorsTest.java create mode 100644 src/arrays/Reverse.java create mode 100644 src/arrays/StringSorting.java create mode 100644 src/arrays/TestArrayGeneration.java create mode 100644 src/arrays/TestGenerated.java create mode 100644 src/arrays/ThreeDWithNew.java create mode 100644 src/arrays/build.xml create mode 100644 src/bangbean/BangBean.java create mode 100644 src/bangbean/BangBeanTest.java create mode 100644 src/bangbean/build.xml create mode 100644 src/concurrency/ActiveObjectDemo.java create mode 100644 src/concurrency/AtomicEvenGenerator.java create mode 100644 src/concurrency/AtomicIntegerTest.java create mode 100644 src/concurrency/Atomicity.java create mode 100644 src/concurrency/AtomicityTest.java create mode 100644 src/concurrency/AttemptLocking.java create mode 100644 src/concurrency/BankTellerSimulation.java create mode 100644 src/concurrency/BasicThreads.java create mode 100644 src/concurrency/CachedThreadPool.java create mode 100644 src/concurrency/CallableDemo.java create mode 100644 src/concurrency/CaptureUncaughtException.java create mode 100644 src/concurrency/CarBuilder.java create mode 100644 src/concurrency/Chopstick.java create mode 100644 src/concurrency/CloseResource.java create mode 100644 src/concurrency/CountDownLatchDemo.java create mode 100644 src/concurrency/CriticalSection.java create mode 100644 src/concurrency/DaemonFromFactory.java create mode 100644 src/concurrency/Daemons.java create mode 100644 src/concurrency/DaemonsDontRunFinally.java create mode 100644 src/concurrency/DeadlockingDiningPhilosophers.java create mode 100644 src/concurrency/DelayQueueDemo.java create mode 100644 src/concurrency/EvenChecker.java create mode 100644 src/concurrency/EvenGenerator.java create mode 100644 src/concurrency/ExceptionThread.java create mode 100644 src/concurrency/ExchangerDemo.java create mode 100644 src/concurrency/ExplicitCriticalSection.java create mode 100644 src/concurrency/FastSimulation.java create mode 100644 src/concurrency/Fat.java create mode 100644 src/concurrency/FixedDiningPhilosophers.java create mode 100644 src/concurrency/FixedThreadPool.java create mode 100644 src/concurrency/GreenhouseScheduler.java create mode 100644 src/concurrency/HorseRace.java create mode 100644 src/concurrency/IntGenerator.java create mode 100644 src/concurrency/Interrupting.java create mode 100644 src/concurrency/Interrupting2.java create mode 100644 src/concurrency/InterruptingIdiom.java create mode 100644 src/concurrency/Joining.java create mode 100644 src/concurrency/LiftOff.java create mode 100644 src/concurrency/ListComparisons.java create mode 100644 src/concurrency/MainThread.java create mode 100644 src/concurrency/MapComparisons.java create mode 100644 src/concurrency/MoreBasicThreads.java create mode 100644 src/concurrency/MultiLock.java create mode 100644 src/concurrency/MutexEvenGenerator.java create mode 100644 src/concurrency/NIOInterruption.java create mode 100644 src/concurrency/NaiveExceptionHandling.java create mode 100644 src/concurrency/NotifyVsNotifyAll.java create mode 100644 src/concurrency/OrnamentalGarden.java create mode 100644 src/concurrency/Philosopher.java create mode 100644 src/concurrency/PipedIO.java create mode 100644 src/concurrency/Pool.java create mode 100644 src/concurrency/PriorityBlockingQueueDemo.java create mode 100644 src/concurrency/ReaderWriterList.java create mode 100644 src/concurrency/ResponsiveUI.java create mode 100644 src/concurrency/Restaurant.java create mode 100644 src/concurrency/SelfManaged.java create mode 100644 src/concurrency/SemaphoreDemo.java create mode 100644 src/concurrency/SerialNumberChecker.java create mode 100644 src/concurrency/SerialNumberGenerator.java create mode 100644 src/concurrency/SettingDefaultHandler.java create mode 100644 src/concurrency/SimpleDaemons.java create mode 100644 src/concurrency/SimpleMicroBenchmark.java create mode 100644 src/concurrency/SimplePriorities.java create mode 100644 src/concurrency/SimpleThread.java create mode 100644 src/concurrency/SingleThreadExecutor.java create mode 100644 src/concurrency/SleepingTask.java create mode 100644 src/concurrency/SyncObject.java create mode 100644 src/concurrency/SynchronizationComparisons.java create mode 100644 src/concurrency/SynchronizedEvenGenerator.java create mode 100644 src/concurrency/TestBlockingQueues.java create mode 100644 src/concurrency/Tester.java create mode 100644 src/concurrency/ThreadLocalVariableHolder.java create mode 100644 src/concurrency/ThreadVariations.java create mode 100644 src/concurrency/ToastOMatic.java create mode 100644 src/concurrency/build.xml create mode 100644 src/concurrency/restaurant2/RestaurantWithQueues.java create mode 100644 src/concurrency/waxomatic/WaxOMatic.java create mode 100644 src/concurrency/waxomatic2/WaxOMatic2.java create mode 100644 src/containers/AssociativeArray.java create mode 100644 src/containers/Bits.java create mode 100644 src/containers/CanonicalMapping.java create mode 100644 src/containers/CollectionDataGeneration.java create mode 100644 src/containers/CollectionDataTest.java create mode 100644 src/containers/CollectionMethods.java create mode 100644 src/containers/CountedString.java create mode 100644 src/containers/DequeTest.java create mode 100644 src/containers/Enumerations.java create mode 100644 src/containers/FailFast.java create mode 100644 src/containers/FillingLists.java create mode 100644 src/containers/Groundhog.java create mode 100644 src/containers/Groundhog2.java create mode 100644 src/containers/IndividualTest.java create mode 100644 src/containers/LinkedHashMapDemo.java create mode 100644 src/containers/ListPerformance.java create mode 100644 src/containers/ListSortSearch.java create mode 100644 src/containers/Lists.java create mode 100644 src/containers/MapDataTest.java create mode 100644 src/containers/MapEntry.java create mode 100644 src/containers/MapPerformance.java create mode 100644 src/containers/Maps.java create mode 100644 src/containers/Prediction.java create mode 100644 src/containers/QueueBehavior.java create mode 100644 src/containers/RandomBounds.java create mode 100644 src/containers/ReadOnly.java create mode 100644 src/containers/References.java create mode 100644 src/containers/SetPerformance.java create mode 100644 src/containers/SimpleHashMap.java create mode 100644 src/containers/SlowMap.java create mode 100644 src/containers/SortedMapDemo.java create mode 100644 src/containers/SortedSetDemo.java create mode 100644 src/containers/SpringDetector.java create mode 100644 src/containers/SpringDetector2.java create mode 100644 src/containers/Stacks.java create mode 100644 src/containers/StringHashCode.java create mode 100644 src/containers/Synchronization.java create mode 100644 src/containers/Test.java create mode 100644 src/containers/TestParam.java create mode 100644 src/containers/Tester.java create mode 100644 src/containers/ToDoList.java create mode 100644 src/containers/TypesForSets.java create mode 100644 src/containers/Unsupported.java create mode 100644 src/containers/Utilities.java create mode 100644 src/containers/build.xml create mode 100644 src/control/BreakAndContinue.java create mode 100644 src/control/CommaOperator.java create mode 100644 src/control/ForEachFloat.java create mode 100644 src/control/ForEachInt.java create mode 100644 src/control/ForEachString.java create mode 100644 src/control/IfElse.java create mode 100644 src/control/IfElse2.java create mode 100644 src/control/LabeledFor.java create mode 100644 src/control/LabeledWhile.java create mode 100644 src/control/ListCharacters.java create mode 100644 src/control/VowelsAndConsonants.java create mode 100644 src/control/WhileTest.java create mode 100644 src/control/build.xml create mode 100644 src/enumerated/AlarmPoints.java create mode 100644 src/enumerated/BigEnumSet.java create mode 100644 src/enumerated/Burrito.java create mode 100644 src/enumerated/CarWash.java create mode 100644 src/enumerated/Competitor.java create mode 100644 src/enumerated/ConstantSpecificMethod.java create mode 100644 src/enumerated/EnumClass.java create mode 100644 src/enumerated/EnumMaps.java create mode 100644 src/enumerated/EnumSets.java create mode 100644 src/enumerated/Input.java create mode 100644 src/enumerated/NonEnum.java create mode 100644 src/enumerated/NotClasses.java create mode 100644 src/enumerated/Outcome.java create mode 100644 src/enumerated/OverrideConstantSpecific.java create mode 100644 src/enumerated/OzWitch.java create mode 100644 src/enumerated/PostOffice.java create mode 100644 src/enumerated/RandomTest.java create mode 100644 src/enumerated/Reflection.java create mode 100644 src/enumerated/RoShamBo.java create mode 100644 src/enumerated/RoShamBo1.java create mode 100644 src/enumerated/RoShamBo2.java create mode 100644 src/enumerated/RoShamBo3.java create mode 100644 src/enumerated/RoShamBo4.java create mode 100644 src/enumerated/RoShamBo5.java create mode 100644 src/enumerated/RoShamBo6.java create mode 100644 src/enumerated/SecurityCategory.java create mode 100644 src/enumerated/SpaceShip.java create mode 100644 src/enumerated/Spiciness.java create mode 100644 src/enumerated/TrafficLight.java create mode 100644 src/enumerated/UpcastEnum.java create mode 100644 src/enumerated/VendingMachine.java create mode 100644 src/enumerated/VendingMachineInput.txt create mode 100644 src/enumerated/build.xml create mode 100644 src/enumerated/cartoons/EnumImplementation.java create mode 100644 src/enumerated/menu/Course.java create mode 100644 src/enumerated/menu/Food.java create mode 100644 src/enumerated/menu/Meal.java create mode 100644 src/enumerated/menu/Meal2.java create mode 100644 src/enumerated/menu/TypeOfFood.java create mode 100644 src/exceptions/AlwaysFinally.java create mode 100644 src/exceptions/Cleanup.java create mode 100644 src/exceptions/CleanupIdiom.java create mode 100644 src/exceptions/DynamicFields.java create mode 100644 src/exceptions/ExceptionMethods.java create mode 100644 src/exceptions/ExceptionSilencer.java create mode 100644 src/exceptions/ExtraFeatures.java create mode 100644 src/exceptions/FinallyWorks.java create mode 100644 src/exceptions/FullConstructors.java create mode 100644 src/exceptions/Human.java create mode 100644 src/exceptions/InheritingExceptions.java create mode 100644 src/exceptions/InputFile.java create mode 100644 src/exceptions/LoggingExceptions.java create mode 100644 src/exceptions/LoggingExceptions2.java create mode 100644 src/exceptions/LostMessage.java create mode 100644 src/exceptions/MainException.java create mode 100644 src/exceptions/MultipleReturns.java create mode 100644 src/exceptions/NeverCaught.java create mode 100644 src/exceptions/OnOffException1.java create mode 100644 src/exceptions/OnOffException2.java create mode 100644 src/exceptions/OnOffSwitch.java create mode 100644 src/exceptions/RethrowNew.java create mode 100644 src/exceptions/Rethrowing.java create mode 100644 src/exceptions/StormyInning.java create mode 100644 src/exceptions/Switch.java create mode 100644 src/exceptions/TurnOffChecking.java create mode 100644 src/exceptions/WhoCalled.java create mode 100644 src/exceptions/WithFinally.java create mode 100644 src/exceptions/build.xml create mode 100644 src/frogbean/Frog.java create mode 100644 src/frogbean/build.xml create mode 100644 src/generics/Apply.java create mode 100644 src/generics/ArrayMaker.java create mode 100644 src/generics/ArrayOfGeneric.java create mode 100644 src/generics/ArrayOfGenericReference.java create mode 100644 src/generics/BankTeller.java create mode 100644 src/generics/BasicBounds.java create mode 100644 src/generics/BasicGeneratorDemo.java create mode 100644 src/generics/BasicHolder.java create mode 100644 src/generics/ByteSet.java create mode 100644 src/generics/CRGWithBasicHolder.java create mode 100644 src/generics/CaptureConversion.java create mode 100644 src/generics/CheckedList.java create mode 100644 src/generics/ClassCasting.java create mode 100644 src/generics/ClassTypeCapture.java create mode 100644 src/generics/ComparablePet.java create mode 100644 src/generics/CompilerIntelligence.java create mode 100644 src/generics/CountedObject.java create mode 100644 src/generics/CovariantArrays.java create mode 100644 src/generics/CovariantReturnTypes.java create mode 100644 src/generics/CreatorGeneric.java create mode 100644 src/generics/CuriouslyRecurringGeneric.java create mode 100644 src/generics/DogsAndRobots.cpp create mode 100644 src/generics/DogsAndRobots.java create mode 100644 src/generics/DynamicProxyMixin.java create mode 100644 src/generics/EpicBattle.java create mode 100644 src/generics/Erased.java create mode 100644 src/generics/ErasedTypeEquivalence.java create mode 100644 src/generics/ErasureAndInheritance.java create mode 100644 src/generics/ExplicitTypeSpecification.java create mode 100644 src/generics/FactoryConstraint.java create mode 100644 src/generics/Fibonacci.java create mode 100644 src/generics/Fill.java create mode 100644 src/generics/Fill2.java create mode 100644 src/generics/FilledListMaker.java create mode 100644 src/generics/Functional.java create mode 100644 src/generics/Generators.java create mode 100644 src/generics/GenericArray.java create mode 100644 src/generics/GenericArray2.java create mode 100644 src/generics/GenericArrayWithTypeToken.java create mode 100644 src/generics/GenericCast.java create mode 100644 src/generics/GenericHolder.java create mode 100644 src/generics/GenericMethods.java create mode 100644 src/generics/GenericReading.java create mode 100644 src/generics/GenericVarargs.java create mode 100644 src/generics/GenericWriting.java create mode 100644 src/generics/GenericsAndCovariance.java create mode 100644 src/generics/GenericsAndReturnTypes.java create mode 100644 src/generics/HasF.java create mode 100644 src/generics/HijackedInterface.java create mode 100644 src/generics/Holder.java create mode 100644 src/generics/Holder1.java create mode 100644 src/generics/Holder2.java create mode 100644 src/generics/Holder3.java create mode 100644 src/generics/InheritBounds.java create mode 100644 src/generics/InstantiateGenericType.cpp create mode 100644 src/generics/InstantiateGenericType.java create mode 100644 src/generics/IterableFibonacci.java create mode 100644 src/generics/LatentReflection.java create mode 100644 src/generics/LimitsOfInference.java create mode 100644 src/generics/LinkedStack.java create mode 100644 src/generics/ListMaker.java create mode 100644 src/generics/ListOfGenerics.java create mode 100644 src/generics/ListOfInt.java create mode 100644 src/generics/LostInformation.java create mode 100644 src/generics/Manipulation.java create mode 100644 src/generics/Manipulator2.java create mode 100644 src/generics/Manipulator3.java create mode 100644 src/generics/Mixins.cpp create mode 100644 src/generics/Mixins.java create mode 100644 src/generics/MultipleInterfaceVariants.java create mode 100644 src/generics/NeedCasting.java create mode 100644 src/generics/NonCovariantGenerics.java create mode 100644 src/generics/NotSelfBounded.java create mode 100644 src/generics/OrdinaryArguments.java create mode 100644 src/generics/Performs.java create mode 100644 src/generics/PlainGenericInheritance.java create mode 100644 src/generics/PrimitiveGenericTest.java create mode 100644 src/generics/RandomList.java create mode 100644 src/generics/RestrictedComparablePets.java create mode 100644 src/generics/ReturnGenericType.java create mode 100644 src/generics/SelfBounding.java create mode 100644 src/generics/SelfBoundingAndCovariantArguments.java create mode 100644 src/generics/SelfBoundingMethods.java create mode 100644 src/generics/SimpleDogsAndRobots.java create mode 100644 src/generics/SimpleHolder.java create mode 100644 src/generics/SimpleQueue.java create mode 100644 src/generics/SimplerPets.java create mode 100644 src/generics/Store.java create mode 100644 src/generics/SuperTypeWildcards.java create mode 100644 src/generics/Templates.cpp create mode 100644 src/generics/ThrowGenericException.java create mode 100644 src/generics/TupleList.java create mode 100644 src/generics/TupleTest.java create mode 100644 src/generics/TupleTest2.java create mode 100644 src/generics/UnboundedWildcards1.java create mode 100644 src/generics/UnboundedWildcards2.java create mode 100644 src/generics/Unconstrained.java create mode 100644 src/generics/UseList.java create mode 100644 src/generics/UseList2.java create mode 100644 src/generics/WatercolorSets.java create mode 100644 src/generics/Wildcards.java create mode 100644 src/generics/build.xml create mode 100644 src/generics/coffee/Americano.java create mode 100644 src/generics/coffee/Breve.java create mode 100644 src/generics/coffee/Cappuccino.java create mode 100644 src/generics/coffee/Coffee.java create mode 100644 src/generics/coffee/CoffeeGenerator.java create mode 100644 src/generics/coffee/Latte.java create mode 100644 src/generics/coffee/Mocha.java create mode 100644 src/generics/decorator/Decoration.java create mode 100644 src/generics/watercolors/Watercolors.java create mode 100644 src/gui/BangBean2.java create mode 100644 src/gui/BeanDumper.java create mode 100644 src/gui/BorderLayout1.java create mode 100644 src/gui/Borders.java create mode 100644 src/gui/Button1.java create mode 100644 src/gui/Button2.java create mode 100644 src/gui/Button2b.java create mode 100644 src/gui/ButtonGroups.java create mode 100644 src/gui/Buttons.java create mode 100644 src/gui/CheckBoxes.java create mode 100644 src/gui/ColorBoxes.java create mode 100644 src/gui/ComboBoxes.java create mode 100644 src/gui/Dialogs.java create mode 100644 src/gui/Face0.gif create mode 100644 src/gui/Face1.gif create mode 100644 src/gui/Face2.gif create mode 100644 src/gui/Face3.gif create mode 100644 src/gui/Face4.gif create mode 100644 src/gui/Faces.java create mode 100644 src/gui/FileChooserTest.java create mode 100644 src/gui/FlowLayout1.java create mode 100644 src/gui/GridLayout1.java create mode 100644 src/gui/HTMLButton.java create mode 100644 src/gui/HelloLabel.java create mode 100644 src/gui/HelloSwing.java create mode 100644 src/gui/InterruptableLongRunningCallable.java create mode 100644 src/gui/InterruptableLongRunningTask.java create mode 100644 src/gui/List.java create mode 100644 src/gui/LongRunningTask.java create mode 100644 src/gui/LookAndFeel.java create mode 100644 src/gui/Menus.java create mode 100644 src/gui/MessageBoxes.java create mode 100644 src/gui/MonitoredLongRunningCallable.java create mode 100644 src/gui/Popup.java create mode 100644 src/gui/Progress.java create mode 100644 src/gui/RadioButtons.java create mode 100644 src/gui/ShowAddListeners.java create mode 100644 src/gui/SimpleMenus.java create mode 100644 src/gui/SineWave.java create mode 100644 src/gui/SubmitLabelManipulationTask.java create mode 100644 src/gui/SubmitSwingProgram.java create mode 100644 src/gui/TabbedPane1.java create mode 100644 src/gui/TextArea.java create mode 100644 src/gui/TextFields.java create mode 100644 src/gui/TextPane.java create mode 100644 src/gui/TicTacToe.java create mode 100644 src/gui/TrackEvent.java create mode 100644 src/gui/build.xml create mode 100644 src/gui/flex/Song.java create mode 100644 src/gui/flex/SongService.java create mode 100644 src/gui/flex/build-command.txt create mode 100644 src/gui/flex/helloflex1.mxml create mode 100644 src/gui/flex/helloflex2.mxml create mode 100644 src/gui/flex/songScript.as create mode 100644 src/gui/flex/songStyles.css create mode 100644 src/gui/flex/songs.mxml create mode 100644 src/gui/jnlp/JnlpFileChooser.java create mode 100644 src/gui/jnlp/filechooser.html create mode 100644 src/gui/jnlp/filechooser.jnlp create mode 100644 src/gui/jnlp/mindview.gif create mode 100644 src/holding/AdapterMethodIdiom.java create mode 100644 src/holding/AddingGroups.java create mode 100644 src/holding/ApplesAndOrangesWithGenerics.java create mode 100644 src/holding/ApplesAndOrangesWithoutGenerics.java create mode 100644 src/holding/ArrayIsNotIterable.java create mode 100644 src/holding/AsListInference.java create mode 100644 src/holding/CollectionSequence.java create mode 100644 src/holding/ContainerMethods.java create mode 100644 src/holding/CrossContainerIteration.java create mode 100644 src/holding/EnvironmentVariables.java create mode 100644 src/holding/ForEachCollections.java create mode 100644 src/holding/GenericsAndUpcasting.java create mode 100644 src/holding/InterfaceVsIterator.java create mode 100644 src/holding/IterableClass.java create mode 100644 src/holding/LinkedListFeatures.java create mode 100644 src/holding/ListFeatures.java create mode 100644 src/holding/ListIteration.java create mode 100644 src/holding/MapOfList.java create mode 100644 src/holding/ModifyingArraysAsList.java create mode 100644 src/holding/MultiIterableClass.java create mode 100644 src/holding/NonCollectionSequence.java create mode 100644 src/holding/PetMap.java create mode 100644 src/holding/PrintingContainers.java create mode 100644 src/holding/PriorityQueueDemo.java create mode 100644 src/holding/QueueDemo.java create mode 100644 src/holding/SetOfInteger.java create mode 100644 src/holding/SetOperations.java create mode 100644 src/holding/SimpleCollection.java create mode 100644 src/holding/SimpleIteration.java create mode 100644 src/holding/SortedSetOfInteger.java create mode 100644 src/holding/StackCollision.java create mode 100644 src/holding/StackTest.java create mode 100644 src/holding/Statistics.java create mode 100644 src/holding/UniqueWords.java create mode 100644 src/holding/UniqueWordsAlphabetic.java create mode 100644 src/holding/build.xml create mode 100644 src/initialization/Apricot.java create mode 100644 src/initialization/ArrayClassObj.java create mode 100644 src/initialization/ArrayInit.java create mode 100644 src/initialization/ArrayNew.java create mode 100644 src/initialization/ArraysOfPrimitives.java create mode 100644 src/initialization/AutoboxingVarargs.java create mode 100644 src/initialization/BananaPeel.java create mode 100644 src/initialization/Burrito.java create mode 100644 src/initialization/Counter.java create mode 100644 src/initialization/DefaultConstructor.java create mode 100644 src/initialization/Demotion.java create mode 100644 src/initialization/DynamicArray.java create mode 100644 src/initialization/EnumOrder.java create mode 100644 src/initialization/ExplicitStatic.java create mode 100644 src/initialization/Flower.java create mode 100644 src/initialization/InitialValues.java create mode 100644 src/initialization/InitialValues2.java create mode 100644 src/initialization/Leaf.java create mode 100644 src/initialization/Measurement.java create mode 100644 src/initialization/MethodInit.java create mode 100644 src/initialization/MethodInit2.java create mode 100644 src/initialization/MethodInit3.java create mode 100644 src/initialization/Mugs.java create mode 100644 src/initialization/NewVarArgs.java create mode 100644 src/initialization/NoSynthesis.java create mode 100644 src/initialization/OptionalTrailingArguments.java create mode 100644 src/initialization/OrderOfInitialization.java create mode 100644 src/initialization/Overloading.java create mode 100644 src/initialization/OverloadingOrder.java create mode 100644 src/initialization/OverloadingVarargs.java create mode 100644 src/initialization/OverloadingVarargs2.java create mode 100644 src/initialization/OverloadingVarargs3.java create mode 100644 src/initialization/PassingThis.java create mode 100644 src/initialization/PrimitiveOverloading.java create mode 100644 src/initialization/SimpleConstructor.java create mode 100644 src/initialization/SimpleConstructor2.java create mode 100644 src/initialization/SimpleEnumUse.java create mode 100644 src/initialization/Spiciness.java create mode 100644 src/initialization/Spoon.java create mode 100644 src/initialization/StaticInitialization.java create mode 100644 src/initialization/TerminationCondition.java create mode 100644 src/initialization/VarArgs.java create mode 100644 src/initialization/VarargType.java create mode 100644 src/initialization/build.xml create mode 100644 src/innerclasses/AnonymousConstructor.java create mode 100644 src/innerclasses/BigEgg.java create mode 100644 src/innerclasses/BigEgg2.java create mode 100644 src/innerclasses/Callbacks.java create mode 100644 src/innerclasses/ClassInInterface.java create mode 100644 src/innerclasses/Contents.java create mode 100644 src/innerclasses/Destination.java create mode 100644 src/innerclasses/DotNew.java create mode 100644 src/innerclasses/DotThis.java create mode 100644 src/innerclasses/Factories.java create mode 100644 src/innerclasses/Games.java create mode 100644 src/innerclasses/GreenhouseController.java create mode 100644 src/innerclasses/GreenhouseControls.java create mode 100644 src/innerclasses/InheritInner.java create mode 100644 src/innerclasses/LocalInnerClass.java create mode 100644 src/innerclasses/MultiImplementation.java create mode 100644 src/innerclasses/MultiInterfaces.java create mode 100644 src/innerclasses/MultiNestingAccess.java create mode 100644 src/innerclasses/Parcel1.java create mode 100644 src/innerclasses/Parcel10.java create mode 100644 src/innerclasses/Parcel11.java create mode 100644 src/innerclasses/Parcel2.java create mode 100644 src/innerclasses/Parcel3.java create mode 100644 src/innerclasses/Parcel5.java create mode 100644 src/innerclasses/Parcel6.java create mode 100644 src/innerclasses/Parcel7.java create mode 100644 src/innerclasses/Parcel7b.java create mode 100644 src/innerclasses/Parcel8.java create mode 100644 src/innerclasses/Parcel9.java create mode 100644 src/innerclasses/Sequence.java create mode 100644 src/innerclasses/TestBed.java create mode 100644 src/innerclasses/TestParcel.java create mode 100644 src/innerclasses/Wrapping.java create mode 100644 src/innerclasses/build.xml create mode 100644 src/innerclasses/controller/Controller.java create mode 100644 src/innerclasses/controller/Event.java create mode 100644 src/interfaces/AdaptedRandomDoubles.java create mode 100644 src/interfaces/Adventure.java create mode 100644 src/interfaces/Factories.java create mode 100644 src/interfaces/Games.java create mode 100644 src/interfaces/HorrorShow.java create mode 100644 src/interfaces/InterfaceCollision.java create mode 100644 src/interfaces/Months.java create mode 100644 src/interfaces/RandVals.java create mode 100644 src/interfaces/RandomDoubles.java create mode 100644 src/interfaces/RandomWords.java create mode 100644 src/interfaces/TestRandVals.java create mode 100644 src/interfaces/build.xml create mode 100644 src/interfaces/classprocessor/Apply.java create mode 100644 src/interfaces/filters/BandPass.java create mode 100644 src/interfaces/filters/Filter.java create mode 100644 src/interfaces/filters/HighPass.java create mode 100644 src/interfaces/filters/LowPass.java create mode 100644 src/interfaces/filters/Waveform.java create mode 100644 src/interfaces/interfaceprocessor/Apply.java create mode 100644 src/interfaces/interfaceprocessor/FilterProcessor.java create mode 100644 src/interfaces/interfaceprocessor/Processor.java create mode 100644 src/interfaces/interfaceprocessor/StringProcessor.java create mode 100644 src/interfaces/music4/Music4.java create mode 100644 src/interfaces/music5/Music5.java create mode 100644 src/interfaces/nesting/NestingInterfaces.java create mode 100644 src/io/Alien.java create mode 100644 src/io/AvailableCharSets.java create mode 100644 src/io/BasicFileOutput.java create mode 100644 src/io/Blip3.java create mode 100644 src/io/Blips.java create mode 100644 src/io/BufferToText.java create mode 100644 src/io/BufferedInputFile.java create mode 100644 src/io/ChangeSystemOut.java create mode 100644 src/io/ChannelCopy.java create mode 100644 src/io/DirList.java create mode 100644 src/io/DirList2.java create mode 100644 src/io/DirList3.java create mode 100644 src/io/DirectoryDemo.java create mode 100644 src/io/Echo.java create mode 100644 src/io/Endians.java create mode 100644 src/io/FileLocking.java create mode 100644 src/io/FileOutputShortcut.java create mode 100644 src/io/FormattedMemoryInput.java create mode 100644 src/io/FreezeAlien.java create mode 100644 src/io/GZIPcompress.java create mode 100644 src/io/GetChannel.java create mode 100644 src/io/GetData.java create mode 100644 src/io/IntBufferDemo.java create mode 100644 src/io/LargeMappedFiles.java create mode 100644 src/io/LockingMappedFiles.java create mode 100644 src/io/Logon.java create mode 100644 src/io/MakeDirectories.java create mode 100644 src/io/MappedIO.java create mode 100644 src/io/MemoryInput.java create mode 100644 src/io/MyWorld.java create mode 100644 src/io/OSExecuteDemo.java create mode 100644 src/io/PreferencesDemo.java create mode 100644 src/io/RecoverCADState.java create mode 100644 src/io/Redirecting.java create mode 100644 src/io/SerialCtl.java create mode 100644 src/io/StoreCADState.java create mode 100644 src/io/StoringAndRecoveringData.java create mode 100644 src/io/TestEOF.java create mode 100644 src/io/TransferTo.java create mode 100644 src/io/UsingBuffers.java create mode 100644 src/io/UsingRandomAccessFile.java create mode 100644 src/io/ViewBuffers.java create mode 100644 src/io/Worm.java create mode 100644 src/io/ZipCompress.java create mode 100644 src/io/build.xml create mode 100644 src/io/xfiles/ThawAlien.java create mode 100644 src/net/build.xml create mode 100644 src/net/mindview/atunit/AtUnit.java create mode 100644 src/net/mindview/atunit/AtUnitRemover.java create mode 100644 src/net/mindview/atunit/ClassNameFinder.java create mode 100644 src/net/mindview/atunit/Test.java create mode 100644 src/net/mindview/atunit/TestObjectCleanup.java create mode 100644 src/net/mindview/atunit/TestObjectCreate.java create mode 100644 src/net/mindview/atunit/TestProperty.java create mode 100644 src/net/mindview/simple/List.java create mode 100644 src/net/mindview/simple/Vector.java create mode 100644 src/net/mindview/util/BasicGenerator.java create mode 100644 src/net/mindview/util/BinaryFile.java create mode 100644 src/net/mindview/util/CollectionData.java create mode 100644 src/net/mindview/util/ContainerMethodDifferences.java create mode 100644 src/net/mindview/util/ConvertTo.java create mode 100644 src/net/mindview/util/CountingGenerator.java create mode 100644 src/net/mindview/util/CountingIntegerList.java create mode 100644 src/net/mindview/util/CountingMapData.java create mode 100644 src/net/mindview/util/Countries.java create mode 100644 src/net/mindview/util/DaemonThreadFactory.java create mode 100644 src/net/mindview/util/DaemonThreadPoolExecutor.java create mode 100644 src/net/mindview/util/Deque.java create mode 100644 src/net/mindview/util/Directory.java create mode 100644 src/net/mindview/util/Enums.java create mode 100644 src/net/mindview/util/FiveTuple.java create mode 100644 src/net/mindview/util/FourTuple.java create mode 100644 src/net/mindview/util/Generated.java create mode 100644 src/net/mindview/util/Generator.java create mode 100644 src/net/mindview/util/Hex.java create mode 100644 src/net/mindview/util/MapData.java create mode 100644 src/net/mindview/util/New.java create mode 100644 src/net/mindview/util/Null.java create mode 100644 src/net/mindview/util/OSExecute.java create mode 100644 src/net/mindview/util/OSExecuteException.java create mode 100644 src/net/mindview/util/PPrint.java create mode 100644 src/net/mindview/util/Pair.java create mode 100644 src/net/mindview/util/Print.java create mode 100644 src/net/mindview/util/ProcessFiles.java create mode 100644 src/net/mindview/util/RandomGenerator.java create mode 100644 src/net/mindview/util/Range.java create mode 100644 src/net/mindview/util/Sets.java create mode 100644 src/net/mindview/util/Stack.java create mode 100644 src/net/mindview/util/SwingConsole.java create mode 100644 src/net/mindview/util/TaskItem.java create mode 100644 src/net/mindview/util/TaskManager.java create mode 100644 src/net/mindview/util/TextFile.java create mode 100644 src/net/mindview/util/ThreeTuple.java create mode 100644 src/net/mindview/util/Tuple.java create mode 100644 src/net/mindview/util/TwoTuple.java create mode 100644 src/net/mindview/util/TypeCounter.java create mode 100644 src/object/Documentation1.java create mode 100644 src/object/Documentation2.java create mode 100644 src/object/Documentation3.java create mode 100644 src/object/HelloDate.java create mode 100644 src/object/ShowProperties.java create mode 100644 src/object/build.xml create mode 100644 src/operators/AllOps.java create mode 100644 src/operators/Assignment.java create mode 100644 src/operators/AutoInc.java create mode 100644 src/operators/BitManipulation.java create mode 100644 src/operators/Bool.java create mode 100644 src/operators/Casting.java create mode 100644 src/operators/CastingNumbers.java create mode 100644 src/operators/EqualsMethod.java create mode 100644 src/operators/EqualsMethod2.java create mode 100644 src/operators/Equivalence.java create mode 100644 src/operators/Exponents.java create mode 100644 src/operators/HelloDate.java create mode 100644 src/operators/Literals.java create mode 100644 src/operators/MathOps.java create mode 100644 src/operators/Overflow.java create mode 100644 src/operators/PassObject.java create mode 100644 src/operators/Precedence.java create mode 100644 src/operators/RoundingNumbers.java create mode 100644 src/operators/ShortCircuit.java create mode 100644 src/operators/StringOperators.java create mode 100644 src/operators/TernaryIfElse.java create mode 100644 src/operators/URShift.java create mode 100644 src/operators/build.xml create mode 100644 src/polymorphism/CovariantReturn.java create mode 100644 src/polymorphism/FieldAccess.java create mode 100644 src/polymorphism/Frog.java create mode 100644 src/polymorphism/PolyConstructors.java create mode 100644 src/polymorphism/PrivateOverride.java create mode 100644 src/polymorphism/RTTI.java create mode 100644 src/polymorphism/ReferenceCounting.java create mode 100644 src/polymorphism/Sandwich.java create mode 100644 src/polymorphism/Shapes.java create mode 100644 src/polymorphism/StaticPolymorphism.java create mode 100644 src/polymorphism/Transmogrify.java create mode 100644 src/polymorphism/build.xml create mode 100644 src/polymorphism/music/Instrument.java create mode 100644 src/polymorphism/music/Music.java create mode 100644 src/polymorphism/music/Music2.java create mode 100644 src/polymorphism/music/Note.java create mode 100644 src/polymorphism/music/Wind.java create mode 100644 src/polymorphism/music3/Music3.java create mode 100644 src/polymorphism/shape/Circle.java create mode 100644 src/polymorphism/shape/RandomShapeGenerator.java create mode 100644 src/polymorphism/shape/Shape.java create mode 100644 src/polymorphism/shape/Square.java create mode 100644 src/polymorphism/shape/Triangle.java create mode 100644 src/reusing/Bath.java create mode 100644 src/reusing/Beetle.java create mode 100644 src/reusing/BlankFinal.java create mode 100644 src/reusing/CADSystem.java create mode 100644 src/reusing/Car.java create mode 100644 src/reusing/Cartoon.java create mode 100644 src/reusing/Chess.java create mode 100644 src/reusing/Detergent.java create mode 100644 src/reusing/FinalArguments.java create mode 100644 src/reusing/FinalData.java create mode 100644 src/reusing/FinalOverridingIllusion.java create mode 100644 src/reusing/Hide.java create mode 100644 src/reusing/Jurassic.java create mode 100644 src/reusing/Lisa.java create mode 100644 src/reusing/Orc.java create mode 100644 src/reusing/PlaceSetting.java create mode 100644 src/reusing/SpaceShip.java create mode 100644 src/reusing/SpaceShipControls.java create mode 100644 src/reusing/SpaceShipDelegation.java create mode 100644 src/reusing/SprinklerSystem.java create mode 100644 src/reusing/Wind.java create mode 100644 src/reusing/build.xml create mode 100644 src/strings/ArrayListDisplay.java create mode 100644 src/strings/BetterRead.java create mode 100644 src/strings/Concatenation.java create mode 100644 src/strings/Conversion.java create mode 100644 src/strings/DatabaseException.java create mode 100644 src/strings/Finding.java create mode 100644 src/strings/Groups.java create mode 100644 src/strings/Immutable.java create mode 100644 src/strings/InfiniteRecursion.java create mode 100644 src/strings/IntegerMatch.java create mode 100644 src/strings/JGrep.java create mode 100644 src/strings/ReFlags.java create mode 100644 src/strings/Receipt.java create mode 100644 src/strings/Replacing.java create mode 100644 src/strings/ReplacingStringTokenizer.java create mode 100644 src/strings/Resetting.java create mode 100644 src/strings/Rudolph.java create mode 100644 src/strings/ScannerDelimiter.java create mode 100644 src/strings/SimpleFormat.java create mode 100644 src/strings/SimpleRead.java create mode 100644 src/strings/SplitDemo.java create mode 100644 src/strings/Splitting.java create mode 100644 src/strings/StartEnd.java create mode 100644 src/strings/TestRegularExpression.java create mode 100644 src/strings/TheReplacements.java create mode 100644 src/strings/ThreatAnalyzer.java create mode 100644 src/strings/Turtle.java create mode 100644 src/strings/UsingStringBuilder.java create mode 100644 src/strings/WhitherStringBuilder.java create mode 100644 src/strings/build.xml create mode 100644 src/swt/ColorBoxes.java create mode 100644 src/swt/DisplayEnvironment.java create mode 100644 src/swt/DisplayProperties.java create mode 100644 src/swt/HelloSWT.java create mode 100644 src/swt/Menus.java create mode 100644 src/swt/ShellsAreMainWindows.java create mode 100644 src/swt/SineWave.java create mode 100644 src/swt/TabbedPane.java create mode 100644 src/swt/build.xml create mode 100644 src/swt/util/SWTApplication.java create mode 100644 src/swt/util/SWTConsole.java create mode 100644 src/typeinfo/AnonymousImplementation.java create mode 100644 src/typeinfo/BoundedClassReferences.java create mode 100644 src/typeinfo/ClassCasts.java create mode 100644 src/typeinfo/ClassInitialization.java create mode 100644 src/typeinfo/FamilyVsExactType.java create mode 100644 src/typeinfo/FilledList.java create mode 100644 src/typeinfo/GenericClassReferences.java create mode 100644 src/typeinfo/HiddenImplementation.java create mode 100644 src/typeinfo/InnerImplementation.java create mode 100644 src/typeinfo/InterfaceViolation.java create mode 100644 src/typeinfo/ModifyingPrivateFields.java create mode 100644 src/typeinfo/NullRobot.java create mode 100644 src/typeinfo/Operation.java create mode 100644 src/typeinfo/Person.java create mode 100644 src/typeinfo/PetCount.java create mode 100644 src/typeinfo/PetCount2.java create mode 100644 src/typeinfo/PetCount3.java create mode 100644 src/typeinfo/PetCount4.java create mode 100644 src/typeinfo/Position.java create mode 100644 src/typeinfo/RegisteredFactories.java create mode 100644 src/typeinfo/Robot.java create mode 100644 src/typeinfo/SelectingMethods.java create mode 100644 src/typeinfo/Shapes.java create mode 100644 src/typeinfo/ShowMethods.java create mode 100644 src/typeinfo/SimpleDynamicProxy.java create mode 100644 src/typeinfo/SimpleProxyDemo.java create mode 100644 src/typeinfo/SnowRemovalRobot.java create mode 100644 src/typeinfo/Staff.java create mode 100644 src/typeinfo/SweetShop.java create mode 100644 src/typeinfo/WildcardClassReferences.java create mode 100644 src/typeinfo/build.xml create mode 100644 src/typeinfo/factory/Factory.java create mode 100644 src/typeinfo/interfacea/A.java create mode 100644 src/typeinfo/packageaccess/HiddenC.java create mode 100644 src/typeinfo/pets/Cat.java create mode 100644 src/typeinfo/pets/Cymric.java create mode 100644 src/typeinfo/pets/Dog.java create mode 100644 src/typeinfo/pets/EgyptianMau.java create mode 100644 src/typeinfo/pets/ForNameCreator.java create mode 100644 src/typeinfo/pets/Hamster.java create mode 100644 src/typeinfo/pets/Individual.java create mode 100644 src/typeinfo/pets/LiteralPetCreator.java create mode 100644 src/typeinfo/pets/Manx.java create mode 100644 src/typeinfo/pets/Mouse.java create mode 100644 src/typeinfo/pets/Mutt.java create mode 100644 src/typeinfo/pets/Person.java create mode 100644 src/typeinfo/pets/Pet.java create mode 100644 src/typeinfo/pets/PetCreator.java create mode 100644 src/typeinfo/pets/Pets.java create mode 100644 src/typeinfo/pets/Pug.java create mode 100644 src/typeinfo/pets/Rat.java create mode 100644 src/typeinfo/pets/Rodent.java create mode 100644 src/typeinfo/toys/GenericToyTest.java create mode 100644 src/typeinfo/toys/ToyTest.java create mode 100644 xml/People.java create mode 100644 xml/Person.java create mode 100644 xml/build.xml diff --git a/.idea/Thinking in Java源代码.iml b/.idea/Thinking in Java源代码.iml new file mode 100644 index 0000000..166d2b9 --- /dev/null +++ b/.idea/Thinking in Java源代码.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..6560a98 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,36 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..794aa67 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..887b1d4 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Copyright.txt b/Copyright.txt new file mode 100644 index 0000000..7833890 --- /dev/null +++ b/Copyright.txt @@ -0,0 +1,66 @@ +This computer source code is Copyright (c)2006 MindView, Inc. +All Rights Reserved. + +Permission to use, copy, modify, and distribute this +computer source code (Source Code) and its documentation +without fee and without a written agreement for the +purposes set forth below is hereby granted, provided that +the above copyright notice, this paragraph and the +following five numbered paragraphs appear in all copies. + +1. Permission is granted to compile the Source Code and to +include the compiled code, in executable format only, in +personal and commercial software programs. + +2. Permission is granted to use the Source Code without +modification in classroom situations, including in +presentation materials, provided that the book "Thinking in +Java" is cited as the origin. + +3. Permission to incorporate the Source Code into printed +media may be obtained by contacting: + +MindView, Inc. 5343 Valle Vista La Mesa, California 91941 +Wayne@MindView.net + +4. The Source Code and documentation are copyrighted by +MindView, Inc. The Source code is provided without express +or implied warranty of any kind, including any implied +warranty of merchantability, fitness for a particular +purpose or non-infringement. MindView, Inc. does not +warrant that the operation of any program that includes the Source Code will be uninterrupted or error-free. MindView, +Inc. makes no representation about the suitability of the +Source Code or of any software that includes the Source +Code for any purpose. The entire risk as to the quality +and performance of any program that includes the Source +Code is with the user of the Source Code. The user +understands that the Source Code was developed for research and instructional purposes and is advised not to rely +exclusively for any reason on the Source Code or any +program that includes the Source Code. Should the Source +Code or any resulting software prove defective, the user +assumes the cost of all necessary servicing, repair, or +correction. + +5. IN NO EVENT SHALL MINDVIEW, INC., OR ITS PUBLISHER BE +LIABLE TO ANY PARTY UNDER ANY LEGAL THEORY FOR DIRECT, +INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, +INCLUDING LOST PROFITS, BUSINESS INTERRUPTION, LOSS OF +BUSINESS INFORMATION, OR ANY OTHER PECUNIARY LOSS, OR FOR +PERSONAL INJURIES, ARISING OUT OF THE USE OF THIS SOURCE +CODE AND ITS DOCUMENTATION, OR ARISING OUT OF THE INABILITY TO USE ANY RESULTING PROGRAM, EVEN IF MINDVIEW, INC., OR +ITS PUBLISHER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. MINDVIEW, INC. SPECIFICALLY DISCLAIMS ANY +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE SOURCE CODE AND DOCUMENTATION PROVIDED +HEREUNDER IS ON AN "AS IS" BASIS, WITHOUT ANY ACCOMPANYING +SERVICES FROM MINDVIEW, INC., AND MINDVIEW, INC. HAS NO +OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, +ENHANCEMENTS, OR MODIFICATIONS. + +Please note that MindView, Inc. maintains a Web site which +is the sole distribution point for electronic copies of the Source Code, http://www.MindView.net (and official mirror +sites), where it is freely available under the terms stated above. + +If you think you've found an error in the Source Code, +please submit a correction using the feedback system that you will find at http://www.MindView.net. diff --git a/DEclipse.py b/DEclipse.py new file mode 100644 index 0000000..fc787fe --- /dev/null +++ b/DEclipse.py @@ -0,0 +1,25 @@ +#!/usr/bin/python +""" +DEclipse.py by Bruce Eckel, for Thinking in Java 4e + +Undoes the effect of Eclipse.py, so that Ant can be used +again to build the code tree. + +You must have Python 2.3 installed to run this program. See www.python.org. +""" +import os + +for path, dirs, files in os.walk('.'): + for file in files: + if file.endswith(".java"): + filepath = path + os.sep + file + code = open(filepath).readlines() + found = False + for n, line in enumerate(code): + if line.find(" /* Added by Eclipse.py */") != -1: + del code[n] + open(filepath, 'w').writelines(code) + + +print "Project ready to be built with Ant." + \ No newline at end of file diff --git a/Eclipse.py b/Eclipse.py new file mode 100644 index 0000000..65c4203 --- /dev/null +++ b/Eclipse.py @@ -0,0 +1,115 @@ +#!/usr/bin/python +""" +Eclipse.py by Bruce Eckel, for Thinking in Java 4e +Modify or insert package statments so that Eclipse is happy with the code tree. +Run this with no arguments from the root of the code tree. + +The Ant build will not work once you run this program! + +You may also want to modify the dotproject and dotclasspath text below. + +You must have Python 2.3 installed to run this program. See www.python.org. +""" +import os + +os.remove("reusing/Lisa.java"); + +for path, dirs, files in os.walk('.'): + for file in files: + if file.endswith(".java"): + filepath = path + os.sep + file + firstLine = open(filepath).readline().strip() + tagPath = firstLine.split()[1] + tagPath = ".".join(tagPath.split('/')[:-1]) + packageStatement = "package " + tagPath + ";" + code = open(filepath).readlines() + found = False + for line in code: + if line.startswith("package "): + found = True + if not found: + code.insert(1, packageStatement + " /* Added by Eclipse.py */\n") + open(filepath, 'w').writelines(code) + +here = os.path.abspath('.').replace("\\", "/") +if here.startswith("/cygdrive/"): # If using cygwin + here = here.replace("/cygdrive/", "", 1) + here = here[0] + ":" + here[1:] +print "here", here +open(".classpath", 'w').write(\ +""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""") # % (here, here)) + +open(".project", 'w').write(\ +""" + + TIJ4 + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + + +""") + +if not os.path.exists(".settings"): + os.mkdir(".settings") +os.chdir(".settings") +open("org.eclipse.jdt.core.prefs", 'w').write(\ +"""#Fri Jan 14 11:03:37 MST 2005 +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.source=1.5 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +""") + +print """Project ready to be opened with Eclipse (see www.Eclipse.org) +Use DEclipse.py if you want to go back to building with Ant.""" + \ No newline at end of file diff --git a/FindBugsExcluder.py b/FindBugsExcluder.py new file mode 100644 index 0000000..e094854 --- /dev/null +++ b/FindBugsExcluder.py @@ -0,0 +1,53 @@ +"""FindBugsExcluder.py +Creates a filter file from the xml and text output of FindBugs +To prepare, you must run +findbugs -textui . > findbugs.txt +findbugs -textui -xml . > findbugs.xml +Once you've run this program you can then run +findbugs -textui -exclude FindBugsFilter-auto.xml . +To exclude the bugs that have been discovered. + +The program includes the suggested changes with each exclusion, +so you can go through FindBugsFilter-auto.xml and decide +to fix things and remove their "Match" nodes. +""" +from xml.dom.minidom import parse +import xml.dom +import os, sys, re, pprint + +xml_buglist = 'findbugs.xml' #'D:\\aaa-TIJ4\\code\\findbugs.xml' +text_buglist = 'findbugs.txt' # 'D:\\aaa-TIJ4\\code\\findbugs.txt' +findbugs_filter = 'FindBugsFilter-auto.xml' # 'D:\\aaa-TIJ4\\code\\FindBugsFilter-auto.xml' + +def main(): + textbugs = [bug.split(':', 1) for bug in file(text_buglist) + if bug.startswith("M ") or bug.startswith("H ")] + textbugs = [(bug[0].split()[2], bug[1].strip()) for bug in textbugs] + dom1 = parse(xml_buglist) + dom2 = xml.dom.getDOMImplementation().createDocument( + None, "FindBugsFilter", None) + bugsDone = [] + for bugNode in [bug for bug in dom1.firstChild.childNodes + if bug.nodeName == "BugInstance"]: + for child in bugNode.childNodes: + if child.nodeName == "Class": + classname = child.attributes.item(0).value + bugtype = bugNode.attributes.item(2).value + if (bugtype, classname) in bugsDone: + continue + else: + bugsDone.append((bugtype, classname)) + match = dom2.createElement("Match") + match.setAttribute("class", classname) + bugCode = dom2.createElement("BugCode") + bugCode.setAttribute("name", bugtype) + match.appendChild(bugCode) + for textbug in textbugs: + if textbug[0] == bugtype and classname in textbug[1]: + match.appendChild(dom2.createComment(textbug[1])) + dom2.documentElement.appendChild(match) + break # out of inner for loop + file(findbugs_filter, 'w').write(dom2.toprettyxml(' ', '\n')) + +if __name__ == "__main__": main() + diff --git a/FindBugsFilter.xml b/FindBugsFilter.xml new file mode 100644 index 0000000..e394446 --- /dev/null +++ b/FindBugsFilter.xml @@ -0,0 +1,547 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/JavaLint.py b/JavaLint.py new file mode 100644 index 0000000..4774e0f --- /dev/null +++ b/JavaLint.py @@ -0,0 +1,31 @@ +#!/usr/bin/python +""" +Runs javac -Xlint on all files in all subdirectories. +Collects results into JavaLint.txt +""" +import os + +outputfile = "JavaLint.txt" + +javadirs = [] +for path, dirs, files in os.walk('.'): + for file in files: + if file.endswith(".java"): + javadirs.append(path) + break + +start = os.getcwd() + +for jd in javadirs: + os.chdir(jd) + print jd + os.system("javac -source 1.5 -Xlint -Xlint:-serial *.java -Xstdout " + outputfile) + os.chdir(start) + +results = open(start + os.sep + outputfile, 'w') + +for jd in javadirs: + messages = open(jd + os.sep + outputfile).read() + if len(messages): + print >>results, '='*40 + "\n" + jd + "\n" + '='*40 + "\n" + messages + \ No newline at end of file diff --git a/OutputGenerator.py b/OutputGenerator.py new file mode 100644 index 0000000..9861141 --- /dev/null +++ b/OutputGenerator.py @@ -0,0 +1,133 @@ +#!/usr/bin/python +""" +Runs a Java program, appends output if it's not there + +-force as first argument when doing batch files forces overwrite + +""" +import os, re, sys + +argTag = '// {Args: ' +oldOutput = re.compile("/* Output:.*?\n(.*)\n\*///:~(?s)") + +def makeOutputIncludedFile(path, fileName, changeReport, force = False): + oldDir = os.getcwd() + os.chdir(path) + base = fileName.split('.')[0] + package = '' + args = '' + command = None + for line in file(fileName): + if line.startswith("} /*"): + break # Out of for loop + if line.startswith("package"): + words = line.strip().split() + package = words[1][:-1] + '.' # remove ';' + if line.startswith(argTag): + args = line[len(argTag):].strip() + assert args.rfind('}') != -1, "%s, %s" % (args, fileName) + args = " " +args[:args.rfind('}')] + if line.startswith("// {main:"): + base = line.split()[-1] + base = base[:-1] + if line.startswith("// {Exec:"): + command = line.split(':', 1)[1].strip()[:-1] + if not command: + command = "java " + package + base + args + command += " > " + base + "-output.txt" + print command + result = os.system(command) + if(result != 0): + raise Exception, "Command returned nonzero value: " + str(result) + # Read output file that was just generated: + results = file(base + "-output.txt").read().strip() + # Strip off trailing spaces on each line: + results = "\n".join([line.rstrip() for line in results.split("\n")]) + results = results.replace('\t', ' ') + if results: + if force or not oldOutput.findall(file(fileName).read()): + processedText = createProcessedJavaText(results, fileName) + open(fileName, 'w').write(processedText + "\n") + if changeReport: + changeReport.write(os.path.join(path, fileName) + "\n") + return # Don't need to try for error output + ##### Duplicate for standard error output: + command += " 2> " + base + "-erroroutput.txt" + print command + result = os.system(command) + if(result != 0): + raise Exception, "Command returned nonzero value: " + str(result) + # Read error file that was just generated: + results = file(base + "-erroroutput.txt").read().strip() + # Strip off trailing spaces on each line: + results = "\n".join([line.rstrip() for line in results.split("\n")]) + results = results.replace('\t', ' ') + if results: + if force or not oldOutput.findall(file(fileName).read()): + processedText = createProcessedJavaText(results, fileName) + open(fileName, 'w').write(processedText + "\n") + if changeReport: + changeReport.write(os.path.join(path, fileName) + "\n") + os.chdir(oldDir) + +def createProcessedJavaText(results, fileName): + processedJava = '' + for line in [line.rstrip() for line in file(fileName)]: + if line.startswith("} ///:~"): + processedJava += "} /* Output:\n" + results + "\n*///:~" + return processedJava + if line.startswith("} /* Output:"): + processedJava += line + "\n" + results + "\n*///:~" # Preserve modifiers + return processedJava + processedJava += line + "\n" + raise Exception, "No marker found at end of file " + path + " " + fileName + +class ReportFile: + def __init__(self, filePath): + self.filePath = filePath + self.file = None + def write(self, line): + if not self.file: + self.file = file(self.filePath, 'w') + self.file.write(line) + print line + def close(self): + if self.file: + self.file.close() + +if __name__ == "__main__": + start = os.getcwd() + args = sys.argv[1:] + forceFlag = False + if len(args): + if args[0] == "-force": + forceFlag = True + print "forceFlag = ", forceFlag + del args[0] + if len(args) > 0: + for javaSource in args: + if javaSource.endswith("."): javaSource = javaSource[:-1] + if not javaSource.endswith(".java"): javaSource += ".java" + os.system("javac " + javaSource) + makeOutputIncludedFile(os.getcwd(), javaSource, None, force = True) + else: + changeReport = ReportFile(os.path.join(start, "Changes.txt")) + for root, dirs, files in os.walk('.'): + if (os.sep + "gui") in root: continue + path = os.path.normpath(os.path.join(start,root)) + print path + for name in [name for name in files if name.endswith(".java")]: + java = file(os.path.join(path, name)).read() + if "public static void main(String" in java and \ + not "{RunByHand}" in java and \ + not "{ThrowsException}" in java and \ + not "/* (Execute to see output) *///:~" in java and \ + not "} /* Same output as" in java: + if forceFlag or not "} /* Output:" in java: + print "\t", name + makeOutputIncludedFile(path, name, changeReport, force = forceFlag) + changeReport.close() + os.system("uedit32 /f Changes.txt &") + + + \ No newline at end of file diff --git a/OutputVerifier.py b/OutputVerifier.py new file mode 100644 index 0000000..4300b54 --- /dev/null +++ b/OutputVerifier.py @@ -0,0 +1,200 @@ +#!/usr/bin/python +""" +To do: + +3) command-line argument (to test a single file) + +- What about exceptions and aborts? + +-If ...is embedded anywhere in a line, that portion becomes a .*? regexp + +--------------- +Find files with +/* Output: + +Run the programs and capture the output, compare with anticipated output. + +/* Output: (80% match) + +For files that vary from run to run + +Complete punt: +/* Output: (Sample) + +(More elaborate design in SimpleTest1.py) +""" +import os, re, glob, sys, string, codecs +from difflib import SequenceMatcher + +argTag = '// {Args: ' +targetOutput = re.compile("/* Output:(.*?)\n(.*)\n\*///:~", re.DOTALL) + +class SimpleTest: + + def __init__(self, fileName, text, referencePath, reportFile): + self.fileName = fileName + self.normalOutput = self.fileName + "-output.txt" + self.errorOutput = self.fileName + "-erroroutput.txt" + self.text = text + self.referencePath = referencePath + self.reportFile = reportFile + self.package = "" + self.args = "" + self.runTest = True + self.insertOutput = True + self.EmbeddedComparisonOutput = False + self.comparisonFile = None + self.lines = self.text.split("\n") + for line in self.lines: + if "{RunByHand}" in line or \ + line.startswith("import javax.swing.*;") or \ + "c12:ZipCompress.java" in line or \ + "/* (Execute to see output) *///:~" in line: + self.runTest = False + if line.startswith("package"): + self.package = line.split()[1][:-1] + "." + if line.startswith(argTag): + self.args = line[len(argTag):].strip() + assert self.args.rfind('}') != -1, "%s, %s" % (self.args, referencePath) + self.args = self.args[:self.args.rfind('}')] + if line.startswith("// {main:"): + self.fileName = line.split()[-1][:-1] + if line.startswith("// {Exec:"): + self.command = line.split(':', 1)[1].strip()[:-1] + if "/* Output:" in line: + self.EmbeddedComparisonOutput = True + if line.startswith("} /*"): + break # Out of for loop + #if "} ///:~" in line: # Extra space + # self.insertOutput = False + + def run(self): + if not self.runTest: return + if not hasattr(self, "command"): + self.command = "java " + self.package + self.fileName + " " + self.args + # Capture standard output into a local file. + self.command = self.command + " > " + self.normalOutput + print self.command + os.system(self.command) + if os.stat(self.normalOutput).st_size: + return self.compareResults(self.normalOutput) + # Capture error output into a local file. + # The '2>' requires cygwin under Windows, or *nix: + self.command = self.command + " 2> " + self.errorOutput + print self.command + os.system(self.command) + return self.compareResults(self.errorOutput) + + def compareResults(self, fileName): + # Read output file that was just generated: + results = makePrintable(file(fileName).read()) + results = results.replace('\t', ' ') + results = results.strip() + file("Generated.txt",'w').write(results) + # Strip off trailing spaces on each line: + results = "\n".join([line.rstrip() for line in results.split("\n")]) + controlSample = self.getControlSample() + ratio = 1.0 + if controlSample: + controlOutput = controlSample.group(2).rstrip() + if "\n..." in controlOutput: + controlLines = controlOutput.split("\n")[:-1] + resultLines = results.split("\n")[:len(controlLines)] + controlOutput = "\n".join(controlLines) + results = "\n".join(resultLines) + file("controlOutput.txt",'w').write(controlOutput) + modifier = controlSample.group(1) + if "match" in modifier: + ratio = float(re.findall("\d+", modifier)[0]) / 100 + print "Looking for", ratio, "match" + if "Sample" in modifier: + ratio = 0.0 + actualRatio = SequenceMatcher(None, controlOutput, results).ratio() + if actualRatio < ratio: + self.reportFile.write("mismatch in " + self.referencePath + "\n") + self.reportFile.write("Actual ratio " + str(actualRatio) + "\n") + self.reportFile.write("expected:\n") + self.reportFile.write(controlOutput + "\n") + self.reportFile.write("----------actual:----------\n") + self.reportFile.write(results + "\n") + file(self.fileName + "-control.txt", 'w').write(controlOutput) + file(self.fileName + "-results.txt", 'w').write(results) + self.reportFile.write("---------------------------\n") + os.system("cmp " + self.fileName + "-control.txt " + + self.fileName + "-results.txt" + + " > cmp-out.txt") + self.reportFile.write(file("cmp-out.txt").read()) + self.reportFile.write("=" * 40 + "\n") + + else: + pass #!!! No control sample, create initial one here + + def appendOutput(self): + if self.insertOutput: + # Rewrite the tail of the source file if the result is nonzero + self.lines[-2] = '}' + self.lines[-1] = "/* Output:" + for tline in file(self.fileName + "-output.txt"): + self.lines.append(tline.rstrip()) + self.lines.append("*///:~") + self.lines.append("") + file(self.fileName + ".java", 'w').write("\n".join(self.lines)) + + def getControlSample(self): + """Finds the control sample, returns an re group + First element is the arguments, second is the actual data""" + if self.EmbeddedComparisonOutput: + self.sourceOutput = targetOutput.search(self.text) + else: + return None + return self.sourceOutput + +def makePrintable(s): + for c in s: + if c not in string.printable: return _makePrintable(s) + return s + +def _makePrintable(s): + result = '' + for c in s: + if c not in string.printable: result += ' ' + else: result += c + return result + +class ReportFile: + def __init__(self, filePath): + self.filePath = filePath + self.file = None + def write(self, line): + if not self.file: + self.file = file(self.filePath, 'w') + self.file.write(line) + print line + def close(self): + if self.file: + self.file.close() + +if __name__ == "__main__": + if len(sys.argv) > 1: + javaSource = sys.argv[1] + if javaSource.endswith("."): javaSource = javaSource[:-1] + if not javaSource.endswith(".java"): javaSource += ".java" + os.system("javac " + javaSource) + SimpleTest(javaSource.split('.')[0], file(javaSource).read(), javaSource, sys.stdout).run() + sys.exit() + start = os.getcwd() + reportFile = ReportFile(start + os.sep + "OutputErrors.txt") + for root, dirs, files in os.walk('.'): + print root + os.chdir(root) + for f in [name.split('.')[0] for name in files if name.endswith(".java")]: + text = file(f + ".java").read() + # Only perform verification if there is an output tag: + if text.find("/* Output:") != -1: + referencePath = os.path.join(root, f + ".java") + SimpleTest(f, text, referencePath, reportFile).run() + os.chdir(start) + reportFile.close() + if reportFile.file: + print "Errors in OutputErrors.txt" + diff --git a/RedundantImportDetector.py b/RedundantImportDetector.py new file mode 100644 index 0000000..8215196 --- /dev/null +++ b/RedundantImportDetector.py @@ -0,0 +1,54 @@ +"""RedundantImportDetector.py +Discover redundant java imports using brute force. +Requires Python 2.3""" +import os, sys, re +from glob import glob + +reportFile = file("RedundantImports.txt", 'w') + +startDir = 'D:\\aaa-TIJ4\\code' + +# Regular expression to find the block of import statements: +findImports = re.compile("\n(?:import .*?\n)+") + +baseDir = os.path.abspath(".") + +print "basDir:", baseDir + +def main(): + for javaFile in glob("*.java") + glob("**/*.java"): + print javaFile + checkImports(os.path.join(baseDir, javaFile)) + +def checkImports(javaFile): + java = file(javaFile).read() + imports = findImports.search(java) + if imports: + imports = [f for f in imports.group(0).split('\n') if f != ''] + fileParts = findImports.split(java) + assert len(fileParts) == 2 + for mutated in mutateImports(imports): + file(javaFile, 'w').write(fileParts[0] + mutated + fileParts[1]) + print "changing to", os.path.dirname(javaFile) + os.chdir(os.path.dirname(javaFile)) + if os.system("javac " + os.path.basename(javaFile)) == 0: + print >>reportFile, javaFile + "\n" + mutated + redundantRemoved = "\n".join( + [m for m in mutated.split("\n") + if not m.startswith("//")]) + print >>reportFile, redundantRemoved + file(javaFile, 'w').write(fileParts[0] + + redundantRemoved + fileParts[1]) + return # No further attempts + file(javaFile, 'w').write(java) # Restore original file + +def mutateImports(imports): + '''Generates different versions of imports, each with a + different line commented out''' + for i in range(len(imports)): + mutated = imports[:] + mutated[i] = '//' + mutated[i] + yield "\n".join([''] + mutated + ['']) + +if __name__ == "__main__": main() + diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..272ed2a --- /dev/null +++ b/build.xml @@ -0,0 +1,128 @@ + + + + + + Main build.xml for the source code for + Thinking in Java, 4th Edition by Bruce Eckel + Code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/chapterOrder.xml b/chapterOrder.xml new file mode 100644 index 0000000..4f5a16e --- /dev/null +++ b/chapterOrder.xml @@ -0,0 +1,28 @@ + \ No newline at end of file diff --git a/src/access/Cake.java b/src/access/Cake.java new file mode 100644 index 0000000..a377cb2 --- /dev/null +++ b/src/access/Cake.java @@ -0,0 +1,11 @@ +package access;//: access/Cake.java +// Accesses a class in a separate compilation unit. + +class Cake { + public static void main(String[] args) { + Pie x = new Pie(); + x.f(); + } +} /* Output: +Pie.f() +*///:~ diff --git a/src/access/ChocolateChip.java b/src/access/ChocolateChip.java new file mode 100644 index 0000000..9b53d43 --- /dev/null +++ b/src/access/ChocolateChip.java @@ -0,0 +1,19 @@ +package access;//: access/ChocolateChip.java +// Can't use package-access member from another package. +import access.dessert.*; + +public class ChocolateChip extends Cookie { + public ChocolateChip() { + System.out.println("ChocolateChip constructor"); + } + public void chomp() { + //! bite(); // Can't access bite + } + public static void main(String[] args) { + ChocolateChip x = new ChocolateChip(); + x.chomp(); + } +} /* Output: +Cookie constructor +ChocolateChip constructor +*///:~ diff --git a/src/access/ChocolateChip2.java b/src/access/ChocolateChip2.java new file mode 100644 index 0000000..58cc11f --- /dev/null +++ b/src/access/ChocolateChip2.java @@ -0,0 +1,17 @@ +package access;//: access/ChocolateChip2.java +import access.cookie2.*; + +public class ChocolateChip2 extends Cookie { + public ChocolateChip2() { + System.out.println("ChocolateChip2 constructor"); + } + public void chomp() { bite(); } // Protected method + public static void main(String[] args) { + ChocolateChip2 x = new ChocolateChip2(); + x.chomp(); + } +} /* Output: +Cookie constructor +ChocolateChip2 constructor +bite +*///:~ diff --git a/src/access/Dinner.java b/src/access/Dinner.java new file mode 100644 index 0000000..6504788 --- /dev/null +++ b/src/access/Dinner.java @@ -0,0 +1,12 @@ +package access;//: access/Dinner.java +// Uses the library. +import access.dessert.*; + +public class Dinner { + public static void main(String[] args) { + Cookie x = new Cookie(); + //! x.bite(); // Can't access + } +} /* Output: +Cookie constructor +*///:~ diff --git a/src/access/FullQualification.java b/src/access/FullQualification.java new file mode 100644 index 0000000..e22d16b --- /dev/null +++ b/src/access/FullQualification.java @@ -0,0 +1,7 @@ +package access;//: access/FullQualification.java + +public class FullQualification { + public static void main(String[] args) { + java.util.ArrayList list = new java.util.ArrayList(); + } +} ///:~ diff --git a/src/access/IceCream.java b/src/access/IceCream.java new file mode 100644 index 0000000..6aab73a --- /dev/null +++ b/src/access/IceCream.java @@ -0,0 +1,16 @@ +package access;//: access/IceCream.java +// Demonstrates "private" keyword. + +class Sundae { + private Sundae() {} + static Sundae makeASundae() { + return new Sundae(); + } +} + +public class IceCream { + public static void main(String[] args) { + //! Sundae x = new Sundae(); + Sundae x = Sundae.makeASundae(); + } +} ///:~ diff --git a/src/access/ImportedMyClass.java b/src/access/ImportedMyClass.java new file mode 100644 index 0000000..d0d6511 --- /dev/null +++ b/src/access/ImportedMyClass.java @@ -0,0 +1,8 @@ +package access;//: access/ImportedMyClass.java +import access.mypackage.*; + +public class ImportedMyClass { + public static void main(String[] args) { + MyClass m = new MyClass(); + } +} ///:~ diff --git a/src/access/LibTest.java b/src/access/LibTest.java new file mode 100644 index 0000000..47ee8c1 --- /dev/null +++ b/src/access/LibTest.java @@ -0,0 +1,13 @@ +package access;//: access/LibTest.java +// Uses the library. +import net.mindview.simple.*; + +public class LibTest { + public static void main(String[] args) { + Vector v = new Vector(); + List l = new List(); + } +} /* Output: +net.mindview.simple.Vector +net.mindview.simple.List +*///:~ diff --git a/src/access/Lunch.java b/src/access/Lunch.java new file mode 100644 index 0000000..569d5a4 --- /dev/null +++ b/src/access/Lunch.java @@ -0,0 +1,36 @@ +package access;//: access/Lunch.java +// Demonstrates class access specifiers. Make a class +// effectively private with private constructors: + +class Soup1 { + private Soup1() {} + // (1) Allow creation via static method: + public static Soup1 makeSoup() { + return new Soup1(); + } +} + +class Soup2 { + private Soup2() {} + // (2) Create a static object and return a reference + // upon request.(The "Singleton" pattern): + private static Soup2 ps1 = new Soup2(); + public static Soup2 access() { + return ps1; + } + public void f() {} +} + +// Only one public class allowed per file: +public class Lunch { + void testPrivate() { + // Can't do this! Private constructor: + //! Soup1 soup = new Soup1(); + } + void testStatic() { + Soup1 soup = Soup1.makeSoup(); + } + void testSingleton() { + Soup2.access().f(); + } +} ///:~ diff --git a/src/access/OrganizedByAccess.java b/src/access/OrganizedByAccess.java new file mode 100644 index 0000000..4e61be0 --- /dev/null +++ b/src/access/OrganizedByAccess.java @@ -0,0 +1,12 @@ +package access;//: access/OrganizedByAccess.java + +public class OrganizedByAccess { + public void pub1() { /* ... */ } + public void pub2() { /* ... */ } + public void pub3() { /* ... */ } + private void priv1() { /* ... */ } + private void priv2() { /* ... */ } + private void priv3() { /* ... */ } + private int i; + // ... +} ///:~ diff --git a/src/access/Pie.java b/src/access/Pie.java new file mode 100644 index 0000000..9895042 --- /dev/null +++ b/src/access/Pie.java @@ -0,0 +1,6 @@ +package access;//: access/Pie.java +// The other class. + +class Pie { + void f() { System.out.println("Pie.f()"); } +} ///:~ diff --git a/src/access/PrintTest.java b/src/access/PrintTest.java new file mode 100644 index 0000000..85fc276 --- /dev/null +++ b/src/access/PrintTest.java @@ -0,0 +1,17 @@ +package access;//: access/PrintTest.java +// Uses the static printing methods in Print.java. +import static net.mindview.util.Print.*; + +public class PrintTest { + public static void main(String[] args) { + print("Available from now on!"); + print(100); + print(100L); + print(3.14159); + } +} /* Output: +Available from now on! +100 +100 +3.14159 +*///:~ diff --git a/src/access/QualifiedMyClass.java b/src/access/QualifiedMyClass.java new file mode 100644 index 0000000..34574ff --- /dev/null +++ b/src/access/QualifiedMyClass.java @@ -0,0 +1,8 @@ +package access;//: access/QualifiedMyClass.java + +public class QualifiedMyClass { + public static void main(String[] args) { + access.mypackage.MyClass m = + new access.mypackage.MyClass(); + } +} ///:~ diff --git a/src/access/SingleImport.java b/src/access/SingleImport.java new file mode 100644 index 0000000..4e8c317 --- /dev/null +++ b/src/access/SingleImport.java @@ -0,0 +1,8 @@ +package access;//: access/SingleImport.java +import java.util.ArrayList; + +public class SingleImport { + public static void main(String[] args) { + ArrayList list = new java.util.ArrayList(); + } +} ///:~ diff --git a/src/access/build.xml b/src/access/build.xml new file mode 100644 index 0000000..bc6c41d --- /dev/null +++ b/src/access/build.xml @@ -0,0 +1,189 @@ + + + + + + build.xml for the source code for the access chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/access/cookie2/Cookie.java b/src/access/cookie2/Cookie.java new file mode 100644 index 0000000..a53e651 --- /dev/null +++ b/src/access/cookie2/Cookie.java @@ -0,0 +1,11 @@ +//: access/cookie2/Cookie.java +package access.cookie2; + +public class Cookie { + public Cookie() { + System.out.println("Cookie constructor"); + } + protected void bite() { + System.out.println("bite"); + } +} ///:~ diff --git a/src/access/dessert/Cookie.java b/src/access/dessert/Cookie.java new file mode 100644 index 0000000..3c7624f --- /dev/null +++ b/src/access/dessert/Cookie.java @@ -0,0 +1,10 @@ +//: access/dessert/Cookie.java +// Creates a library. +package access.dessert; + +public class Cookie { + public Cookie() { + System.out.println("Cookie constructor"); + } + void bite() { System.out.println("bite"); } +} ///:~ diff --git a/src/access/mypackage/MyClass.java b/src/access/mypackage/MyClass.java new file mode 100644 index 0000000..fdcfcf2 --- /dev/null +++ b/src/access/mypackage/MyClass.java @@ -0,0 +1,6 @@ +//: access/mypackage/MyClass.java +package access.mypackage; + +public class MyClass { + // ... +} ///:~ diff --git a/src/annotations/AtUnitComposition.java b/src/annotations/AtUnitComposition.java new file mode 100644 index 0000000..a80fd7c --- /dev/null +++ b/src/annotations/AtUnitComposition.java @@ -0,0 +1,26 @@ +//: annotations/AtUnitComposition.java +// Creating non-embedded tests. +package annotations; +import net.mindview.atunit.*; +import net.mindview.util.*; + +public class AtUnitComposition { + AtUnitExample1 testObject = new AtUnitExample1(); + @Test boolean _methodOne() { + return + testObject.methodOne().equals("This is methodOne"); + } + @Test boolean _methodTwo() { + return testObject.methodTwo() == 2; + } + public static void main(String[] args) throws Exception { + OSExecute.command( + "java net.mindview.atunit.AtUnit AtUnitComposition"); + } +} /* Output: +annotations.AtUnitComposition + . _methodOne + . _methodTwo This is methodTwo + +OK (2 tests) +*///:~ diff --git a/src/annotations/AtUnitExample1.java b/src/annotations/AtUnitExample1.java new file mode 100644 index 0000000..890f2a4 --- /dev/null +++ b/src/annotations/AtUnitExample1.java @@ -0,0 +1,39 @@ +//: annotations/AtUnitExample1.java +package annotations; +import net.mindview.atunit.*; +import net.mindview.util.*; + +public class AtUnitExample1 { + public String methodOne() { + return "This is methodOne"; + } + public int methodTwo() { + System.out.println("This is methodTwo"); + return 2; + } + @Test boolean methodOneTest() { + return methodOne().equals("This is methodOne"); + } + @Test boolean m2() { return methodTwo() == 2; } + @Test private boolean m3() { return true; } + // Shows output for failure: + @Test boolean failureTest() { return false; } + @Test boolean anotherDisappointment() { return false; } + public static void main(String[] args) throws Exception { + OSExecute.command( + "java net.mindview.atunit.AtUnit AtUnitExample1"); + } +} /* Output: +annotations.AtUnitExample1 + . methodOneTest + . m2 This is methodTwo + + . m3 + . failureTest (failed) + . anotherDisappointment (failed) +(5 tests) + +>>> 2 FAILURES <<< + annotations.AtUnitExample1: failureTest + annotations.AtUnitExample1: anotherDisappointment +*///:~ diff --git a/src/annotations/AtUnitExample2.java b/src/annotations/AtUnitExample2.java new file mode 100644 index 0000000..88f9b39 --- /dev/null +++ b/src/annotations/AtUnitExample2.java @@ -0,0 +1,48 @@ +//: annotations/AtUnitExample2.java +// Assertions and exceptions can be used in @Tests. +package annotations; +import java.io.*; +import net.mindview.atunit.*; +import net.mindview.util.*; + +public class AtUnitExample2 { + public String methodOne() { + return "This is methodOne"; + } + public int methodTwo() { + System.out.println("This is methodTwo"); + return 2; + } + @Test void assertExample() { + assert methodOne().equals("This is methodOne"); + } + @Test void assertFailureExample() { + assert 1 == 2: "What a surprise!"; + } + @Test void exceptionExample() throws IOException { + new FileInputStream("nofile.txt"); // Throws + } + @Test boolean assertAndReturn() { + // Assertion with message: + assert methodTwo() == 2: "methodTwo must equal 2"; + return methodOne().equals("This is methodOne"); + } + public static void main(String[] args) throws Exception { + OSExecute.command( + "java net.mindview.atunit.AtUnit AtUnitExample2"); + } +} /* Output: +annotations.AtUnitExample2 + . assertExample + . assertFailureExample java.lang.AssertionError: What a surprise! +(failed) + . exceptionExample java.io.FileNotFoundException: nofile.txt (The system cannot find the file specified) +(failed) + . assertAndReturn This is methodTwo + +(4 tests) + +>>> 2 FAILURES <<< + annotations.AtUnitExample2: assertFailureExample + annotations.AtUnitExample2: exceptionExample +*///:~ diff --git a/src/annotations/AtUnitExample3.java b/src/annotations/AtUnitExample3.java new file mode 100644 index 0000000..583c4a6 --- /dev/null +++ b/src/annotations/AtUnitExample3.java @@ -0,0 +1,36 @@ +//: annotations/AtUnitExample3.java +package annotations; +import net.mindview.atunit.*; +import net.mindview.util.*; + +public class AtUnitExample3 { + private int n; + public AtUnitExample3(int n) { this.n = n; } + public int getN() { return n; } + public String methodOne() { + return "This is methodOne"; + } + public int methodTwo() { + System.out.println("This is methodTwo"); + return 2; + } + @TestObjectCreate static AtUnitExample3 create() { + return new AtUnitExample3(47); + } + @Test boolean initialization() { return n == 47; } + @Test boolean methodOneTest() { + return methodOne().equals("This is methodOne"); + } + @Test boolean m2() { return methodTwo() == 2; } + public static void main(String[] args) throws Exception { + OSExecute.command( + "java net.mindview.atunit.AtUnit AtUnitExample3"); + } +} /* Output: +annotations.AtUnitExample3 + . initialization + . methodOneTest + . m2 This is methodTwo + +OK (3 tests) +*///:~ diff --git a/src/annotations/AtUnitExample4.java b/src/annotations/AtUnitExample4.java new file mode 100644 index 0000000..67f8c8f --- /dev/null +++ b/src/annotations/AtUnitExample4.java @@ -0,0 +1,75 @@ +//: annotations/AtUnitExample4.java +package annotations; +import java.util.*; +import net.mindview.atunit.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class AtUnitExample4 { + static String theory = "All brontosauruses " + + "are thin at one end, much MUCH thicker in the " + + "middle, and then thin again at the far end."; + private String word; + private Random rand = new Random(); // Time-based seed + public AtUnitExample4(String word) { this.word = word; } + public String getWord() { return word; } + public String scrambleWord() { + List chars = new ArrayList(); + for(Character c : word.toCharArray()) { + chars.add(c); + } + Collections.shuffle(chars, rand); + StringBuilder result = new StringBuilder(); + for(char ch : chars) { + result.append(ch); + } + return result.toString(); + } + @TestProperty static List input = + Arrays.asList(theory.split(" ")); + @TestProperty + static Iterator words = input.iterator(); + @TestObjectCreate static AtUnitExample4 create() { + if(words.hasNext()) { + return new AtUnitExample4(words.next()); + } else { + return null; + } + } + @Test boolean words() { + print("'" + getWord() + "'"); + return getWord().equals("are"); + } + @Test boolean scramble1() { + // Change to a specific seed to get verifiable results: + rand = new Random(47); + print("'" + getWord() + "'"); + String scrambled = scrambleWord(); + print(scrambled); + return scrambled.equals("lAl"); + } + @Test boolean scramble2() { + rand = new Random(74); + print("'" + getWord() + "'"); + String scrambled = scrambleWord(); + print(scrambled); + return scrambled.equals("tsaeborornussu"); + } + public static void main(String[] args) throws Exception { + System.out.println("starting"); + OSExecute.command( + "java net.mindview.atunit.AtUnit AtUnitExample4"); + } +} /* Output: +starting +annotations.AtUnitExample4 + . scramble1 'All' +lAl + + . scramble2 'brontosauruses' +tsaeborornussu + + . words 'are' + +OK (3 tests) +*///:~ diff --git a/src/annotations/AtUnitExample5.java b/src/annotations/AtUnitExample5.java new file mode 100644 index 0000000..28c0bb8 --- /dev/null +++ b/src/annotations/AtUnitExample5.java @@ -0,0 +1,52 @@ +//: annotations/AtUnitExample5.java +package annotations; +import java.io.*; +import net.mindview.atunit.*; +import net.mindview.util.*; + +public class AtUnitExample5 { + private String text; + public AtUnitExample5(String text) { this.text = text; } + public String toString() { return text; } + @TestProperty static PrintWriter output; + @TestProperty static int counter; + @TestObjectCreate static AtUnitExample5 create() { + String id = Integer.toString(counter++); + try { + output = new PrintWriter("Test" + id + ".txt"); + } catch(IOException e) { + throw new RuntimeException(e); + } + return new AtUnitExample5(id); + } + @TestObjectCleanup static void + cleanup(AtUnitExample5 tobj) { + System.out.println("Running cleanup"); + output.close(); + } + @Test boolean test1() { + output.print("test1"); + return true; + } + @Test boolean test2() { + output.print("test2"); + return true; + } + @Test boolean test3() { + output.print("test3"); + return true; + } + public static void main(String[] args) throws Exception { + OSExecute.command( + "java net.mindview.atunit.AtUnit AtUnitExample5"); + } +} /* Output: +annotations.AtUnitExample5 + . test1 +Running cleanup + . test2 +Running cleanup + . test3 +Running cleanup +OK (3 tests) +*///:~ diff --git a/src/annotations/AtUnitExternalTest.java b/src/annotations/AtUnitExternalTest.java new file mode 100644 index 0000000..2b5b057 --- /dev/null +++ b/src/annotations/AtUnitExternalTest.java @@ -0,0 +1,22 @@ +//: annotations/AtUnitExternalTest.java +// Creating non-embedded tests. +package annotations; +import net.mindview.atunit.*; +import net.mindview.util.*; + +public class AtUnitExternalTest extends AtUnitExample1 { + @Test boolean _methodOne() { + return methodOne().equals("This is methodOne"); + } + @Test boolean _methodTwo() { return methodTwo() == 2; } + public static void main(String[] args) throws Exception { + OSExecute.command( + "java net.mindview.atunit.AtUnit AtUnitExternalTest"); + } +} /* Output: +annotations.AtUnitExternalTest + . _methodOne + . _methodTwo This is methodTwo + +OK (2 tests) +*///:~ diff --git a/src/annotations/ExtractInterface.java b/src/annotations/ExtractInterface.java new file mode 100644 index 0000000..27e5b96 --- /dev/null +++ b/src/annotations/ExtractInterface.java @@ -0,0 +1,10 @@ +//: annotations/ExtractInterface.java +// APT-based annotation processing. +package annotations; +import java.lang.annotation.*; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.SOURCE) +public @interface ExtractInterface { + public String value(); +} ///:~ diff --git a/src/annotations/HashSetTest.java b/src/annotations/HashSetTest.java new file mode 100644 index 0000000..2aa0533 --- /dev/null +++ b/src/annotations/HashSetTest.java @@ -0,0 +1,31 @@ +//: annotations/HashSetTest.java +package annotations; +import java.util.*; +import net.mindview.atunit.*; +import net.mindview.util.*; + +public class HashSetTest { + HashSet testObject = new HashSet(); + @Test void initialization() { + assert testObject.isEmpty(); + } + @Test void _contains() { + testObject.add("one"); + assert testObject.contains("one"); + } + @Test void _remove() { + testObject.add("one"); + testObject.remove("one"); + assert testObject.isEmpty(); + } + public static void main(String[] args) throws Exception { + OSExecute.command( + "java net.mindview.atunit.AtUnit HashSetTest"); + } +} /* Output: +annotations.HashSetTest + . initialization + . _remove + . _contains +OK (3 tests) +*///:~ diff --git a/src/annotations/InterfaceExtractorProcessor.java b/src/annotations/InterfaceExtractorProcessor.java new file mode 100644 index 0000000..b204051 --- /dev/null +++ b/src/annotations/InterfaceExtractorProcessor.java @@ -0,0 +1,64 @@ +//: annotations/InterfaceExtractorProcessor.java +// APT-based annotation processing. +// {Exec: apt -factory +// annotations.InterfaceExtractorProcessorFactory +// Multiplier.java -s ../annotations} +package annotations; +import com.sun.mirror.apt.*; +import com.sun.mirror.declaration.*; +import java.io.*; +import java.util.*; + +public class InterfaceExtractorProcessor + implements AnnotationProcessor { + private final AnnotationProcessorEnvironment env; + private ArrayList interfaceMethods = + new ArrayList(); + public InterfaceExtractorProcessor( + AnnotationProcessorEnvironment env) { this.env = env; } + public void process() { + for(TypeDeclaration typeDecl : + env.getSpecifiedTypeDeclarations()) { + ExtractInterface annot = + typeDecl.getAnnotation(ExtractInterface.class); + if(annot == null) { + break; + } + for(MethodDeclaration m : typeDecl.getMethods()) { + if(m.getModifiers().contains(Modifier.PUBLIC) && + !(m.getModifiers().contains(Modifier.STATIC))) { + interfaceMethods.add(m); + } + } + if(interfaceMethods.size() > 0) { + try { + PrintWriter writer = + env.getFiler().createSourceFile(annot.value()); + writer.println("package " + + typeDecl.getPackage().getQualifiedName() +";"); + writer.println("public interface " + + annot.value() + " {"); + for(MethodDeclaration m : interfaceMethods) { + writer.print(" public "); + writer.print(m.getReturnType() + " "); + writer.print(m.getSimpleName() + " ("); + int i = 0; + for(ParameterDeclaration parm : + m.getParameters()) { + writer.print(parm.getType() + " " + + parm.getSimpleName()); + if(++i < m.getParameters().size()) { + writer.print(", "); + } + } + writer.println(");"); + } + writer.println("}"); + writer.close(); + } catch(IOException ioe) { + throw new RuntimeException(ioe); + } + } + } + } +} ///:~ diff --git a/src/annotations/InterfaceExtractorProcessorFactory.java b/src/annotations/InterfaceExtractorProcessorFactory.java new file mode 100644 index 0000000..a502563 --- /dev/null +++ b/src/annotations/InterfaceExtractorProcessorFactory.java @@ -0,0 +1,22 @@ +//: annotations/InterfaceExtractorProcessorFactory.java +// APT-based annotation processing. +package annotations; +import com.sun.mirror.apt.*; +import com.sun.mirror.declaration.*; +import java.util.*; + +public class InterfaceExtractorProcessorFactory + implements AnnotationProcessorFactory { + public AnnotationProcessor getProcessorFor( + Set atds, + AnnotationProcessorEnvironment env) { + return new InterfaceExtractorProcessor(env); + } + public Collection supportedAnnotationTypes() { + return + Collections.singleton("annotations.ExtractInterface"); + } + public Collection supportedOptions() { + return Collections.emptySet(); + } +} ///:~ diff --git a/src/annotations/Multiplier.java b/src/annotations/Multiplier.java new file mode 100644 index 0000000..96aca8f --- /dev/null +++ b/src/annotations/Multiplier.java @@ -0,0 +1,21 @@ +//: annotations/Multiplier.java +// APT-based annotation processing. +package annotations; + +@ExtractInterface("IMultiplier") +public class Multiplier { + public int multiply(int x, int y) { + int total = 0; + for(int i = 0; i < x; i++) { + total = add(total, y); + } + return total; + } + private int add(int x, int y) { return x + y; } + public static void main(String[] args) { + Multiplier m = new Multiplier(); + System.out.println("11*16 = " + m.multiply(11, 16)); + } +} /* Output: +11*16 = 176 +*///:~ diff --git a/src/annotations/PasswordUtils.java b/src/annotations/PasswordUtils.java new file mode 100644 index 0000000..34fca6a --- /dev/null +++ b/src/annotations/PasswordUtils.java @@ -0,0 +1,20 @@ +package annotations;//: annotations/PasswordUtils.java +import java.util.*; + +public class PasswordUtils { + @UseCase(id = 47, description = + "Passwords must contain at least one numeric") + public boolean validatePassword(String password) { + return (password.matches("\\w*\\d\\w*")); + } + @UseCase(id = 48) + public String encryptPassword(String password) { + return new StringBuilder(password).reverse().toString(); + } + @UseCase(id = 49, description = + "New passwords can't equal previously used ones") + public boolean checkForNewPassword( + List prevPasswords, String password) { + return !prevPasswords.contains(password); + } +} ///:~ diff --git a/src/annotations/SimulatingNull.java b/src/annotations/SimulatingNull.java new file mode 100644 index 0000000..7cc2e3f --- /dev/null +++ b/src/annotations/SimulatingNull.java @@ -0,0 +1,9 @@ +package annotations;//: annotations/SimulatingNull.java +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface SimulatingNull { + public int id() default -1; + public String description() default ""; +} ///:~ diff --git a/src/annotations/StackL.java b/src/annotations/StackL.java new file mode 100644 index 0000000..80fd0c0 --- /dev/null +++ b/src/annotations/StackL.java @@ -0,0 +1,11 @@ +//: annotations/StackL.java +// A stack built on a linkedList. +package annotations; +import java.util.*; + +public class StackL { + private LinkedList list = new LinkedList(); + public void push(T v) { list.addFirst(v); } + public T top() { return list.getFirst(); } + public T pop() { return list.removeFirst(); } +} ///:~ diff --git a/src/annotations/StackLStringTest.java b/src/annotations/StackLStringTest.java new file mode 100644 index 0000000..710e8c2 --- /dev/null +++ b/src/annotations/StackLStringTest.java @@ -0,0 +1,36 @@ +//: annotations/StackLStringTest.java +// Applying @Unit to generics. +package annotations; +import net.mindview.atunit.*; +import net.mindview.util.*; + +public class StackLStringTest extends StackL { + @Test void _push() { + push("one"); + assert top().equals("one"); + push("two"); + assert top().equals("two"); + } + @Test void _pop() { + push("one"); + push("two"); + assert pop().equals("two"); + assert pop().equals("one"); + } + @Test void _top() { + push("A"); + push("B"); + assert top().equals("B"); + assert top().equals("B"); + } + public static void main(String[] args) throws Exception { + OSExecute.command( + "java net.mindview.atunit.AtUnit StackLStringTest"); + } +} /* Output: +annotations.StackLStringTest + . _push + . _pop + . _top +OK (3 tests) +*///:~ diff --git a/src/annotations/Testable.java b/src/annotations/Testable.java new file mode 100644 index 0000000..8ec59c4 --- /dev/null +++ b/src/annotations/Testable.java @@ -0,0 +1,10 @@ +//: annotations/Testable.java +package annotations; +import net.mindview.atunit.*; + +public class Testable { + public void execute() { + System.out.println("Executing.."); + } + @Test void testExecute() { execute(); } +} ///:~ diff --git a/src/annotations/UseCase.java b/src/annotations/UseCase.java new file mode 100644 index 0000000..6b35f32 --- /dev/null +++ b/src/annotations/UseCase.java @@ -0,0 +1,9 @@ +package annotations;//: annotations/UseCase.java +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface UseCase { + public int id(); + public String description() default "no description"; +} ///:~ diff --git a/src/annotations/UseCaseTracker.java b/src/annotations/UseCaseTracker.java new file mode 100644 index 0000000..08f5db9 --- /dev/null +++ b/src/annotations/UseCaseTracker.java @@ -0,0 +1,30 @@ +package annotations;//: annotations/UseCaseTracker.java +import java.lang.reflect.*; +import java.util.*; + +public class UseCaseTracker { + public static void + trackUseCases(List useCases, Class cl) { + for(Method m : cl.getDeclaredMethods()) { + UseCase uc = m.getAnnotation(UseCase.class); + if(uc != null) { + System.out.println("Found Use Case:" + uc.id() + + " " + uc.description()); + useCases.remove(new Integer(uc.id())); + } + } + for(int i : useCases) { + System.out.println("Warning: Missing use case-" + i); + } + } + public static void main(String[] args) { + List useCases = new ArrayList(); + Collections.addAll(useCases, 47, 48, 49, 50); + trackUseCases(useCases, PasswordUtils.class); + } +} /* Output: +Found Use Case:47 Passwords must contain at least one numeric +Found Use Case:48 no description +Found Use Case:49 New passwords can't equal previously used ones +Warning: Missing use case-50 +*///:~ diff --git a/src/annotations/build.xml b/src/annotations/build.xml new file mode 100644 index 0000000..5d099fb --- /dev/null +++ b/src/annotations/build.xml @@ -0,0 +1,225 @@ + + + + + + build.xml for the source code for the annotations chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/annotations/database/Constraints.java b/src/annotations/database/Constraints.java new file mode 100644 index 0000000..4abaf1f --- /dev/null +++ b/src/annotations/database/Constraints.java @@ -0,0 +1,11 @@ +//: annotations/database/Constraints.java +package annotations.database; +import java.lang.annotation.*; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Constraints { + boolean primaryKey() default false; + boolean allowNull() default true; + boolean unique() default false; +} ///:~ diff --git a/src/annotations/database/DBTable.java b/src/annotations/database/DBTable.java new file mode 100644 index 0000000..7021636 --- /dev/null +++ b/src/annotations/database/DBTable.java @@ -0,0 +1,9 @@ +//: annotations/database/DBTable.java +package annotations.database; +import java.lang.annotation.*; + +@Target(ElementType.TYPE) // Applies to classes only +@Retention(RetentionPolicy.RUNTIME) +public @interface DBTable { + public String name() default ""; +} ///:~ diff --git a/src/annotations/database/Member.java b/src/annotations/database/Member.java new file mode 100644 index 0000000..4e832de --- /dev/null +++ b/src/annotations/database/Member.java @@ -0,0 +1,18 @@ +//: annotations/database/Member.java +package annotations.database; + +@DBTable(name = "MEMBER") +public class Member { + @SQLString(30) String firstName; + @SQLString(50) String lastName; + @SQLInteger Integer age; + @SQLString(value = 30, + constraints = @Constraints(primaryKey = true)) + String handle; + static int memberCount; + public String getHandle() { return handle; } + public String getFirstName() { return firstName; } + public String getLastName() { return lastName; } + public String toString() { return handle; } + public Integer getAge() { return age; } +} ///:~ diff --git a/src/annotations/database/SQLInteger.java b/src/annotations/database/SQLInteger.java new file mode 100644 index 0000000..c772ea1 --- /dev/null +++ b/src/annotations/database/SQLInteger.java @@ -0,0 +1,10 @@ +//: annotations/database/SQLInteger.java +package annotations.database; +import java.lang.annotation.*; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface SQLInteger { + String name() default ""; + Constraints constraints() default @Constraints; +} ///:~ diff --git a/src/annotations/database/SQLString.java b/src/annotations/database/SQLString.java new file mode 100644 index 0000000..0e458b8 --- /dev/null +++ b/src/annotations/database/SQLString.java @@ -0,0 +1,11 @@ +//: annotations/database/SQLString.java +package annotations.database; +import java.lang.annotation.*; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface SQLString { + int value() default 0; + String name() default ""; + Constraints constraints() default @Constraints; +} ///:~ diff --git a/src/annotations/database/TableCreationProcessorFactory.java b/src/annotations/database/TableCreationProcessorFactory.java new file mode 100644 index 0000000..cd38ba0 --- /dev/null +++ b/src/annotations/database/TableCreationProcessorFactory.java @@ -0,0 +1,105 @@ +//: annotations/database/TableCreationProcessorFactory.java +// The database example using Visitor. +// {Exec: apt -factory +// annotations.database.TableCreationProcessorFactory +// database/Member.java -s database} +package annotations.database; +import com.sun.mirror.apt.*; +import com.sun.mirror.declaration.*; +import com.sun.mirror.util.*; +import java.util.*; +import static com.sun.mirror.util.DeclarationVisitors.*; + +public class TableCreationProcessorFactory + implements AnnotationProcessorFactory { + public AnnotationProcessor getProcessorFor( + Set atds, + AnnotationProcessorEnvironment env) { + return new TableCreationProcessor(env); + } + public Collection supportedAnnotationTypes() { + return Arrays.asList( + "annotations.database.DBTable", + "annotations.database.Constraints", + "annotations.database.SQLString", + "annotations.database.SQLInteger"); + } + public Collection supportedOptions() { + return Collections.emptySet(); + } + private static class TableCreationProcessor + implements AnnotationProcessor { + private final AnnotationProcessorEnvironment env; + private String sql = ""; + public TableCreationProcessor( + AnnotationProcessorEnvironment env) { + this.env = env; + } + public void process() { + for(TypeDeclaration typeDecl : + env.getSpecifiedTypeDeclarations()) { + typeDecl.accept(getDeclarationScanner( + new TableCreationVisitor(), NO_OP)); + sql = sql.substring(0, sql.length() - 1) + ");"; + System.out.println("creation SQL is :\n" + sql); + sql = ""; + } + } + private class TableCreationVisitor + extends SimpleDeclarationVisitor { + public void visitClassDeclaration( + ClassDeclaration d) { + DBTable dbTable = d.getAnnotation(DBTable.class); + if(dbTable != null) { + sql += "CREATE TABLE "; + sql += (dbTable.name().length() < 1) + ? d.getSimpleName().toUpperCase() + : dbTable.name(); + sql += " ("; + } + } + public void visitFieldDeclaration( + FieldDeclaration d) { + String columnName = ""; + if(d.getAnnotation(SQLInteger.class) != null) { + SQLInteger sInt = d.getAnnotation( + SQLInteger.class); + // Use field name if name not specified + if(sInt.name().length() < 1) { + columnName = d.getSimpleName().toUpperCase(); + } else { + columnName = sInt.name(); + } + sql += "\n " + columnName + " INT" + + getConstraints(sInt.constraints()) + ","; + } + if(d.getAnnotation(SQLString.class) != null) { + SQLString sString = d.getAnnotation( + SQLString.class); + // Use field name if name not specified. + if(sString.name().length() < 1) { + columnName = d.getSimpleName().toUpperCase(); + } else { + columnName = sString.name(); + } + sql += "\n " + columnName + " VARCHAR(" + + sString.value() + ")" + + getConstraints(sString.constraints()) + ","; + } + } + private String getConstraints(Constraints con) { + String constraints = ""; + if(!con.allowNull()) { + constraints += " NOT NULL"; + } + if(con.primaryKey()) { + constraints += " PRIMARY KEY"; + } + if(con.unique()) { + constraints += " UNIQUE"; + } + return constraints; + } + } + } +} ///:~ diff --git a/src/annotations/database/TableCreator.java b/src/annotations/database/TableCreator.java new file mode 100644 index 0000000..0eb2c25 --- /dev/null +++ b/src/annotations/database/TableCreator.java @@ -0,0 +1,103 @@ +//: annotations/database/TableCreator.java +// Reflection-based annotation processor. +// {Args: annotations.database.Member} +package annotations.database; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.util.*; + +public class TableCreator { + public static void main(String[] args) throws Exception { + if(args.length < 1) { + System.out.println("arguments: annotated classes"); + System.exit(0); + } + for(String className : args) { + Class cl = Class.forName(className); + DBTable dbTable = cl.getAnnotation(DBTable.class); + if(dbTable == null) { + System.out.println( + "No DBTable annotations in class " + className); + continue; + } + String tableName = dbTable.name(); + // If the name is empty, use the Class name: + if(tableName.length() < 1) { + tableName = cl.getName().toUpperCase(); + } + List columnDefs = new ArrayList(); + for(Field field : cl.getDeclaredFields()) { + String columnName = null; + Annotation[] anns = field.getDeclaredAnnotations(); + if(anns.length < 1) { + continue; // Not a db table column + } + if(anns[0] instanceof SQLInteger) { + SQLInteger sInt = (SQLInteger) anns[0]; + // Use field name if name not specified + if(sInt.name().length() < 1) { + columnName = field.getName().toUpperCase(); + } else { + columnName = sInt.name(); + } + columnDefs.add(columnName + " INT" + + getConstraints(sInt.constraints())); + } + if(anns[0] instanceof SQLString) { + SQLString sString = (SQLString) anns[0]; + // Use field name if name not specified. + if(sString.name().length() < 1) { + columnName = field.getName().toUpperCase(); + } else { + columnName = sString.name(); + } + columnDefs.add(columnName + " VARCHAR(" + + sString.value() + ")" + + getConstraints(sString.constraints())); + } + StringBuilder createCommand = new StringBuilder( + "CREATE TABLE " + tableName + "("); + for(String columnDef : columnDefs) { + createCommand.append("\n " + columnDef + ","); + } + // Remove trailing comma + String tableCreate = createCommand.substring( + 0, createCommand.length() - 1) + ");"; + System.out.println("Table Creation SQL for " + + className + " is :\n" + tableCreate); + } + } + } + private static String getConstraints(Constraints con) { + String constraints = ""; + if(!con.allowNull()) { + constraints += " NOT NULL"; + } + if(con.primaryKey()) { + constraints += " PRIMARY KEY"; + } + if(con.unique()) { + constraints += " UNIQUE"; + } + return constraints; + } +} /* Output: +Table Creation SQL for annotations.database.Member is : +CREATE TABLE MEMBER( + FIRSTNAME VARCHAR(30)); +Table Creation SQL for annotations.database.Member is : +CREATE TABLE MEMBER( + FIRSTNAME VARCHAR(30), + LASTNAME VARCHAR(50)); +Table Creation SQL for annotations.database.Member is : +CREATE TABLE MEMBER( + FIRSTNAME VARCHAR(30), + LASTNAME VARCHAR(50), + AGE INT); +Table Creation SQL for annotations.database.Member is : +CREATE TABLE MEMBER( + FIRSTNAME VARCHAR(30), + LASTNAME VARCHAR(50), + AGE INT, + HANDLE VARCHAR(30) PRIMARY KEY); +*///:~ diff --git a/src/annotations/database/Uniqueness.java b/src/annotations/database/Uniqueness.java new file mode 100644 index 0000000..e17a597 --- /dev/null +++ b/src/annotations/database/Uniqueness.java @@ -0,0 +1,8 @@ +//: annotations/database/Uniqueness.java +// Sample of nested annotations +package annotations.database; + +public @interface Uniqueness { + Constraints constraints() + default @Constraints(unique=true); +} ///:~ diff --git a/src/arrays/AlphabeticSearch.java b/src/arrays/AlphabeticSearch.java new file mode 100644 index 0000000..daecae0 --- /dev/null +++ b/src/arrays/AlphabeticSearch.java @@ -0,0 +1,20 @@ +package arrays;//: arrays/AlphabeticSearch.java +// Searching with a Comparator. +import java.util.*; +import net.mindview.util.*; + +public class AlphabeticSearch { + public static void main(String[] args) { + String[] sa = Generated.array(new String[30], + new RandomGenerator.String(5)); + Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER); + System.out.println(Arrays.toString(sa)); + int index = Arrays.binarySearch(sa, sa[10], + String.CASE_INSENSITIVE_ORDER); + System.out.println("Index: "+ index + "\n"+ sa[index]); + } +} /* Output: +[bkIna, cQrGs, cXZJo, dLsmw, eGZMm, EqUCB, gwsqP, hKcxr, HLGEa, HqXum, HxxHv, JMRoE, JmzMs, Mesbt, MNvqe, nyGcF, ogoYW, OneOE, OWZnT, RFJQA, rUkZP, sgqia, slJrL, suEcU, uTpnX, vpfFv, WHkjU, xxEAJ, YNzbr, zDyCy] +Index: 10 +HxxHv +*///:~ diff --git a/src/arrays/ArrayOfGenericType.java b/src/arrays/ArrayOfGenericType.java new file mode 100644 index 0000000..74d2087 --- /dev/null +++ b/src/arrays/ArrayOfGenericType.java @@ -0,0 +1,13 @@ +package arrays;//: arrays/ArrayOfGenericType.java +// Arrays of generic types won't compile. + +public class ArrayOfGenericType { + T[] array; // OK + @SuppressWarnings("unchecked") + public ArrayOfGenericType(int size) { + //! array = new T[size]; // Illegal + array = (T[])new Object[size]; // "unchecked" Warning + } + // Illegal: + //! public U[] makeArray() { return new U[10]; } +} ///:~ diff --git a/src/arrays/ArrayOfGenerics.java b/src/arrays/ArrayOfGenerics.java new file mode 100644 index 0000000..4e6a8bb --- /dev/null +++ b/src/arrays/ArrayOfGenerics.java @@ -0,0 +1,29 @@ +package arrays;//: arrays/ArrayOfGenerics.java +// It is possible to create arrays of generics. +import java.util.*; + +public class ArrayOfGenerics { + @SuppressWarnings("unchecked") + public static void main(String[] args) { + List[] ls; + List[] la = new List[10]; + ls = (List[])la; // "Unchecked" warning + ls[0] = new ArrayList(); + // Compile-time checking produces an error: + //! ls[1] = new ArrayList(); + + // The problem: List is a subtype of Object + Object[] objects = ls; // So assignment is OK + // Compiles and runs without complaint: + objects[1] = new ArrayList(); + + // However, if your needs are straightforward it is + // possible to create an array of generics, albeit + // with an "unchecked" warning: + List[] spheres = + (List[])new List[10]; + for(int i = 0; i < spheres.length; i++) { + spheres[i] = new ArrayList(); + } + } +} ///:~ diff --git a/src/arrays/ArrayOptions.java b/src/arrays/ArrayOptions.java new file mode 100644 index 0000000..42145b1 --- /dev/null +++ b/src/arrays/ArrayOptions.java @@ -0,0 +1,71 @@ +package arrays;//: arrays/ArrayOptions.java +// Initialization & re-assignment of arrays. +import java.util.*; +import static net.mindview.util.Print.*; + +public class ArrayOptions { + public static void main(String[] args) { + // Arrays of objects: + BerylliumSphere[] a; // Local uninitialized variable + BerylliumSphere[] b = new BerylliumSphere[5]; + // The references inside the array are + // automatically initialized to null: + print("b: " + Arrays.toString(b)); + BerylliumSphere[] c = new BerylliumSphere[4]; + for(int i = 0; i < c.length; i++) { + if(c[i] == null) // Can test for null reference + { + c[i] = new BerylliumSphere(); + } + } + // Aggregate initialization: + BerylliumSphere[] d = { new BerylliumSphere(), + new BerylliumSphere(), new BerylliumSphere() + }; + // Dynamic aggregate initialization: + a = new BerylliumSphere[]{ + new BerylliumSphere(), new BerylliumSphere(), + }; + // (Trailing comma is optional in both cases) + print("a.length = " + a.length); + print("b.length = " + b.length); + print("c.length = " + c.length); + print("d.length = " + d.length); + a = d; + print("a.length = " + a.length); + + // Arrays of primitives: + int[] e; // Null reference + int[] f = new int[5]; + // The primitives inside the array are + // automatically initialized to zero: + print("f: " + Arrays.toString(f)); + int[] g = new int[4]; + for(int i = 0; i < g.length; i++) { + g[i] = i*i; + } + int[] h = { 11, 47, 93 }; + // Compile error: variable e not initialized: + //!print("e.length = " + e.length); + print("f.length = " + f.length); + print("g.length = " + g.length); + print("h.length = " + h.length); + e = h; + print("e.length = " + e.length); + e = new int[]{ 1, 2 }; + print("e.length = " + e.length); + } +} /* Output: +b: [null, null, null, null, null] +a.length = 2 +b.length = 5 +c.length = 4 +d.length = 3 +a.length = 3 +f: [0, 0, 0, 0, 0] +f.length = 5 +g.length = 4 +h.length = 3 +e.length = 3 +e.length = 2 +*///:~ diff --git a/src/arrays/ArraySearching.java b/src/arrays/ArraySearching.java new file mode 100644 index 0000000..1d415f0 --- /dev/null +++ b/src/arrays/ArraySearching.java @@ -0,0 +1,28 @@ +package arrays;//: arrays/ArraySearching.java +// Using Arrays.binarySearch(). +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class ArraySearching { + public static void main(String[] args) { + Generator gen = + new RandomGenerator.Integer(1000); + int[] a = ConvertTo.primitive( + Generated.array(new Integer[25], gen)); + Arrays.sort(a); + print("Sorted array: " + Arrays.toString(a)); + while(true) { + int r = gen.next(); + int location = Arrays.binarySearch(a, r); + if(location >= 0) { + print("Location of " + r + " is " + location + + ", a[" + location + "] = " + a[location]); + break; // Out of while loop + } + } + } +} /* Output: +Sorted array: [128, 140, 200, 207, 258, 258, 278, 288, 322, 429, 511, 520, 522, 551, 555, 589, 693, 704, 809, 861, 861, 868, 916, 961, 998] +Location of 322 is 8, a[8] = 322 +*///:~ diff --git a/src/arrays/AssemblingMultidimensionalArrays.java b/src/arrays/AssemblingMultidimensionalArrays.java new file mode 100644 index 0000000..8518b14 --- /dev/null +++ b/src/arrays/AssemblingMultidimensionalArrays.java @@ -0,0 +1,19 @@ +package arrays;//: arrays/AssemblingMultidimensionalArrays.java +// Creating multidimensional arrays. +import java.util.*; + +public class AssemblingMultidimensionalArrays { + public static void main(String[] args) { + Integer[][] a; + a = new Integer[3][]; + for(int i = 0; i < a.length; i++) { + a[i] = new Integer[3]; + for(int j = 0; j < a[i].length; j++) { + a[i][j] = i * j; // Autoboxing + } + } + System.out.println(Arrays.deepToString(a)); + } +} /* Output: +[[0, 0, 0], [0, 1, 2], [0, 2, 4]] +*///:~ diff --git a/src/arrays/AutoboxingArrays.java b/src/arrays/AutoboxingArrays.java new file mode 100644 index 0000000..9c885b7 --- /dev/null +++ b/src/arrays/AutoboxingArrays.java @@ -0,0 +1,16 @@ +package arrays;//: arrays/AutoboxingArrays.java +import java.util.*; + +public class AutoboxingArrays { + public static void main(String[] args) { + Integer[][] a = { // Autoboxing: + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, + { 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }, + { 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 }, + { 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 }, + }; + System.out.println(Arrays.deepToString(a)); + } +} /* Output: +[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [21, 22, 23, 24, 25, 26, 27, 28, 29, 30], [51, 52, 53, 54, 55, 56, 57, 58, 59, 60], [71, 72, 73, 74, 75, 76, 77, 78, 79, 80]] +*///:~ diff --git a/src/arrays/CompType.java b/src/arrays/CompType.java new file mode 100644 index 0000000..6c11cbe --- /dev/null +++ b/src/arrays/CompType.java @@ -0,0 +1,55 @@ +package arrays;//: arrays/CompType.java +// Implementing Comparable in a class. +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class CompType implements Comparable { + int i; + int j; + private static int count = 1; + public CompType(int n1, int n2) { + i = n1; + j = n2; + } + public String toString() { + String result = "[i = " + i + ", j = " + j + "]"; + if(count++ % 3 == 0) { + result += "\n"; + } + return result; + } + public int compareTo(CompType rv) { + return (i < rv.i ? -1 : (i == rv.i ? 0 : 1)); + } + private static Random r = new Random(47); + public static Generator generator() { + return new Generator() { + public CompType next() { + return new CompType(r.nextInt(100),r.nextInt(100)); + } + }; + } + public static void main(String[] args) { + CompType[] a = + Generated.array(new CompType[12], generator()); + print("before sorting:"); + print(Arrays.toString(a)); + Arrays.sort(a); + print("after sorting:"); + print(Arrays.toString(a)); + } +} /* Output: +before sorting: +[[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29] +, [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28] +, [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61] +, [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22] +] +after sorting: +[[i = 9, j = 78], [i = 11, j = 22], [i = 16, j = 40] +, [i = 20, j = 58], [i = 22, j = 7], [i = 51, j = 89] +, [i = 58, j = 55], [i = 61, j = 29], [i = 68, j = 0] +, [i = 88, j = 28], [i = 93, j = 61], [i = 98, j = 61] +] +*///:~ diff --git a/src/arrays/ComparatorTest.java b/src/arrays/ComparatorTest.java new file mode 100644 index 0000000..840bb45 --- /dev/null +++ b/src/arrays/ComparatorTest.java @@ -0,0 +1,36 @@ +package arrays;//: arrays/ComparatorTest.java +// Implementing a Comparator for a class. +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +class CompTypeComparator implements Comparator { + public int compare(CompType o1, CompType o2) { + return (o1.j < o2.j ? -1 : (o1.j == o2.j ? 0 : 1)); + } +} + +public class ComparatorTest { + public static void main(String[] args) { + CompType[] a = Generated.array( + new CompType[12], CompType.generator()); + print("before sorting:"); + print(Arrays.toString(a)); + Arrays.sort(a, new CompTypeComparator()); + print("after sorting:"); + print(Arrays.toString(a)); + } +} /* Output: +before sorting: +[[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29] +, [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28] +, [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61] +, [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22] +] +after sorting: +[[i = 68, j = 0], [i = 22, j = 7], [i = 11, j = 22] +, [i = 88, j = 28], [i = 61, j = 29], [i = 16, j = 40] +, [i = 58, j = 55], [i = 20, j = 58], [i = 93, j = 61] +, [i = 98, j = 61], [i = 9, j = 78], [i = 51, j = 89] +] +*///:~ diff --git a/src/arrays/ComparingArrays.java b/src/arrays/ComparingArrays.java new file mode 100644 index 0000000..07c943c --- /dev/null +++ b/src/arrays/ComparingArrays.java @@ -0,0 +1,25 @@ +package arrays;//: arrays/ComparingArrays.java +// Using Arrays.equals() +import java.util.*; +import static net.mindview.util.Print.*; + +public class ComparingArrays { + public static void main(String[] args) { + int[] a1 = new int[10]; + int[] a2 = new int[10]; + Arrays.fill(a1, 47); + Arrays.fill(a2, 47); + print(Arrays.equals(a1, a2)); + a2[3] = 11; + print(Arrays.equals(a1, a2)); + String[] s1 = new String[4]; + Arrays.fill(s1, "Hi"); + String[] s2 = { new String("Hi"), new String("Hi"), + new String("Hi"), new String("Hi") }; + print(Arrays.equals(s1, s2)); + } +} /* Output: +true +false +true +*///:~ diff --git a/src/arrays/ContainerComparison.java b/src/arrays/ContainerComparison.java new file mode 100644 index 0000000..a66e92d --- /dev/null +++ b/src/arrays/ContainerComparison.java @@ -0,0 +1,47 @@ +package arrays;//: arrays/ContainerComparison.java +import java.util.*; +import static net.mindview.util.Print.*; + +class BerylliumSphere { + private static long counter; + private final long id = counter++; + public String toString() { return "Sphere " + id; } +} + +public class ContainerComparison { + public static void main(String[] args) { + BerylliumSphere[] spheres = new BerylliumSphere[10]; + for(int i = 0; i < 5; i++) { + spheres[i] = new BerylliumSphere(); + } + print(Arrays.toString(spheres)); + print(spheres[4]); + + List sphereList = + new ArrayList(); + for(int i = 0; i < 5; i++) { + sphereList.add(new BerylliumSphere()); + } + print(sphereList); + print(sphereList.get(4)); + + int[] integers = { 0, 1, 2, 3, 4, 5 }; + print(Arrays.toString(integers)); + print(integers[4]); + + List intList = new ArrayList( + Arrays.asList(0, 1, 2, 3, 4, 5)); + intList.add(97); + print(intList); + print(intList.get(4)); + } +} /* Output: +[Sphere 0, Sphere 1, Sphere 2, Sphere 3, Sphere 4, null, null, null, null, null] +Sphere 4 +[Sphere 5, Sphere 6, Sphere 7, Sphere 8, Sphere 9] +Sphere 9 +[0, 1, 2, 3, 4, 5] +4 +[0, 1, 2, 3, 4, 5, 97] +4 +*///:~ diff --git a/src/arrays/CopyingArrays.java b/src/arrays/CopyingArrays.java new file mode 100644 index 0000000..7190f4f --- /dev/null +++ b/src/arrays/CopyingArrays.java @@ -0,0 +1,42 @@ +package arrays;//: arrays/CopyingArrays.java +// Using System.arraycopy() +import java.util.*; +import static net.mindview.util.Print.*; + +public class CopyingArrays { + public static void main(String[] args) { + int[] i = new int[7]; + int[] j = new int[10]; + Arrays.fill(i, 47); + Arrays.fill(j, 99); + print("i = " + Arrays.toString(i)); + print("j = " + Arrays.toString(j)); + System.arraycopy(i, 0, j, 0, i.length); + print("j = " + Arrays.toString(j)); + int[] k = new int[5]; + Arrays.fill(k, 103); + System.arraycopy(i, 0, k, 0, k.length); + print("k = " + Arrays.toString(k)); + Arrays.fill(k, 103); + System.arraycopy(k, 0, i, 0, k.length); + print("i = " + Arrays.toString(i)); + // Objects: + Integer[] u = new Integer[10]; + Integer[] v = new Integer[5]; + Arrays.fill(u, new Integer(47)); + Arrays.fill(v, new Integer(99)); + print("u = " + Arrays.toString(u)); + print("v = " + Arrays.toString(v)); + System.arraycopy(v, 0, u, u.length/2, v.length); + print("u = " + Arrays.toString(u)); + } +} /* Output: +i = [47, 47, 47, 47, 47, 47, 47] +j = [99, 99, 99, 99, 99, 99, 99, 99, 99, 99] +j = [47, 47, 47, 47, 47, 47, 47, 99, 99, 99] +k = [47, 47, 47, 47, 47] +i = [103, 103, 103, 103, 103, 47, 47] +u = [47, 47, 47, 47, 47, 47, 47, 47, 47, 47] +v = [99, 99, 99, 99, 99] +u = [47, 47, 47, 47, 47, 99, 99, 99, 99, 99] +*///:~ diff --git a/src/arrays/FillingArrays.java b/src/arrays/FillingArrays.java new file mode 100644 index 0000000..1494fca --- /dev/null +++ b/src/arrays/FillingArrays.java @@ -0,0 +1,51 @@ +package arrays;//: arrays/FillingArrays.java +// Using Arrays.fill() +import java.util.*; +import static net.mindview.util.Print.*; + +public class FillingArrays { + public static void main(String[] args) { + int size = 6; + boolean[] a1 = new boolean[size]; + byte[] a2 = new byte[size]; + char[] a3 = new char[size]; + short[] a4 = new short[size]; + int[] a5 = new int[size]; + long[] a6 = new long[size]; + float[] a7 = new float[size]; + double[] a8 = new double[size]; + String[] a9 = new String[size]; + Arrays.fill(a1, true); + print("a1 = " + Arrays.toString(a1)); + Arrays.fill(a2, (byte)11); + print("a2 = " + Arrays.toString(a2)); + Arrays.fill(a3, 'x'); + print("a3 = " + Arrays.toString(a3)); + Arrays.fill(a4, (short)17); + print("a4 = " + Arrays.toString(a4)); + Arrays.fill(a5, 19); + print("a5 = " + Arrays.toString(a5)); + Arrays.fill(a6, 23); + print("a6 = " + Arrays.toString(a6)); + Arrays.fill(a7, 29); + print("a7 = " + Arrays.toString(a7)); + Arrays.fill(a8, 47); + print("a8 = " + Arrays.toString(a8)); + Arrays.fill(a9, "Hello"); + print("a9 = " + Arrays.toString(a9)); + // Manipulating ranges: + Arrays.fill(a9, 3, 5, "World"); + print("a9 = " + Arrays.toString(a9)); + } +} /* Output: +a1 = [true, true, true, true, true, true] +a2 = [11, 11, 11, 11, 11, 11] +a3 = [x, x, x, x, x, x] +a4 = [17, 17, 17, 17, 17, 17] +a5 = [19, 19, 19, 19, 19, 19] +a6 = [23, 23, 23, 23, 23, 23] +a7 = [29.0, 29.0, 29.0, 29.0, 29.0, 29.0] +a8 = [47.0, 47.0, 47.0, 47.0, 47.0, 47.0] +a9 = [Hello, Hello, Hello, Hello, Hello, Hello] +a9 = [Hello, Hello, Hello, World, World, Hello] +*///:~ diff --git a/src/arrays/GeneratorsTest.java b/src/arrays/GeneratorsTest.java new file mode 100644 index 0000000..88a3514 --- /dev/null +++ b/src/arrays/GeneratorsTest.java @@ -0,0 +1,33 @@ +package arrays;//: arrays/GeneratorsTest.java +import net.mindview.util.*; + +public class GeneratorsTest { + public static int size = 10; + public static void test(Class surroundingClass) { + for(Class type : surroundingClass.getClasses()) { + System.out.print(type.getSimpleName() + ": "); + try { + Generator g = (Generator)type.newInstance(); + for(int i = 0; i < size; i++) { + System.out.printf(g.next() + " "); + } + System.out.println(); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + } + public static void main(String[] args) { + test(CountingGenerator.class); + } +} /* Output: +Double: 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 +Float: 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 +Long: 0 1 2 3 4 5 6 7 8 9 +Integer: 0 1 2 3 4 5 6 7 8 9 +Short: 0 1 2 3 4 5 6 7 8 9 +String: abcdefg hijklmn opqrstu vwxyzAB CDEFGHI JKLMNOP QRSTUVW XYZabcd efghijk lmnopqr +Character: a b c d e f g h i j +Byte: 0 1 2 3 4 5 6 7 8 9 +Boolean: true false true false true false true false true false +*///:~ diff --git a/src/arrays/IceCream.java b/src/arrays/IceCream.java new file mode 100644 index 0000000..9666681 --- /dev/null +++ b/src/arrays/IceCream.java @@ -0,0 +1,42 @@ +package arrays;//: arrays/IceCream.java +// Returning arrays from methods. +import java.util.*; + +public class IceCream { + private static Random rand = new Random(47); + static final String[] FLAVORS = { + "Chocolate", "Strawberry", "Vanilla Fudge Swirl", + "Mint Chip", "Mocha Almond Fudge", "Rum Raisin", + "Praline Cream", "Mud Pie" + }; + public static String[] flavorSet(int n) { + if(n > FLAVORS.length) { + throw new IllegalArgumentException("Set too big"); + } + String[] results = new String[n]; + boolean[] picked = new boolean[FLAVORS.length]; + for(int i = 0; i < n; i++) { + int t; + do { + t = rand.nextInt(FLAVORS.length); + } + while(picked[t]); + results[i] = FLAVORS[t]; + picked[t] = true; + } + return results; + } + public static void main(String[] args) { + for(int i = 0; i < 7; i++) { + System.out.println(Arrays.toString(flavorSet(3))); + } + } +} /* Output: +[Rum Raisin, Mint Chip, Mocha Almond Fudge] +[Chocolate, Strawberry, Mocha Almond Fudge] +[Strawberry, Mint Chip, Mocha Almond Fudge] +[Rum Raisin, Vanilla Fudge Swirl, Mud Pie] +[Vanilla Fudge Swirl, Chocolate, Mocha Almond Fudge] +[Praline Cream, Strawberry, Mocha Almond Fudge] +[Mocha Almond Fudge, Strawberry, Mint Chip] +*///:~ diff --git a/src/arrays/MultiDimWrapperArray.java b/src/arrays/MultiDimWrapperArray.java new file mode 100644 index 0000000..324a397 --- /dev/null +++ b/src/arrays/MultiDimWrapperArray.java @@ -0,0 +1,29 @@ +package arrays;//: arrays/MultiDimWrapperArray.java +// Multidimensional arrays of "wrapper" objects. +import java.util.*; + +public class MultiDimWrapperArray { + public static void main(String[] args) { + Integer[][] a1 = { // Autoboxing + { 1, 2, 3, }, + { 4, 5, 6, }, + }; + Double[][][] a2 = { // Autoboxing + { { 1.1, 2.2 }, { 3.3, 4.4 } }, + { { 5.5, 6.6 }, { 7.7, 8.8 } }, + { { 9.9, 1.2 }, { 2.3, 3.4 } }, + }; + String[][] a3 = { + { "The", "Quick", "Sly", "Fox" }, + { "Jumped", "Over" }, + { "The", "Lazy", "Brown", "Dog", "and", "friend" }, + }; + System.out.println("a1: " + Arrays.deepToString(a1)); + System.out.println("a2: " + Arrays.deepToString(a2)); + System.out.println("a3: " + Arrays.deepToString(a3)); + } +} /* Output: +a1: [[1, 2, 3], [4, 5, 6]] +a2: [[[1.1, 2.2], [3.3, 4.4]], [[5.5, 6.6], [7.7, 8.8]], [[9.9, 1.2], [2.3, 3.4]]] +a3: [[The, Quick, Sly, Fox], [Jumped, Over], [The, Lazy, Brown, Dog, and, friend]] +*///:~ diff --git a/src/arrays/MultidimensionalObjectArrays.java b/src/arrays/MultidimensionalObjectArrays.java new file mode 100644 index 0000000..716a683 --- /dev/null +++ b/src/arrays/MultidimensionalObjectArrays.java @@ -0,0 +1,19 @@ +package arrays;//: arrays/MultidimensionalObjectArrays.java +import java.util.*; + +public class MultidimensionalObjectArrays { + public static void main(String[] args) { + BerylliumSphere[][] spheres = { + { new BerylliumSphere(), new BerylliumSphere() }, + { new BerylliumSphere(), new BerylliumSphere(), + new BerylliumSphere(), new BerylliumSphere() }, + { new BerylliumSphere(), new BerylliumSphere(), + new BerylliumSphere(), new BerylliumSphere(), + new BerylliumSphere(), new BerylliumSphere(), + new BerylliumSphere(), new BerylliumSphere() }, + }; + System.out.println(Arrays.deepToString(spheres)); + } +} /* Output: +[[Sphere 0, Sphere 1], [Sphere 2, Sphere 3, Sphere 4, Sphere 5], [Sphere 6, Sphere 7, Sphere 8, Sphere 9, Sphere 10, Sphere 11, Sphere 12, Sphere 13]] +*///:~ diff --git a/src/arrays/MultidimensionalPrimitiveArray.java b/src/arrays/MultidimensionalPrimitiveArray.java new file mode 100644 index 0000000..6b67022 --- /dev/null +++ b/src/arrays/MultidimensionalPrimitiveArray.java @@ -0,0 +1,15 @@ +package arrays;//: arrays/MultidimensionalPrimitiveArray.java +// Creating multidimensional arrays. +import java.util.*; + +public class MultidimensionalPrimitiveArray { + public static void main(String[] args) { + int[][] a = { + { 1, 2, 3, }, + { 4, 5, 6, }, + }; + System.out.println(Arrays.deepToString(a)); + } +} /* Output: +[[1, 2, 3], [4, 5, 6]] +*///:~ diff --git a/src/arrays/ParameterizedArrayType.java b/src/arrays/ParameterizedArrayType.java new file mode 100644 index 0000000..9bce806 --- /dev/null +++ b/src/arrays/ParameterizedArrayType.java @@ -0,0 +1,22 @@ +package arrays;//: arrays/ParameterizedArrayType.java + +class ClassParameter { + public T[] f(T[] arg) { return arg; } +} + +class MethodParameter { + public static T[] f(T[] arg) { return arg; } +} + +public class ParameterizedArrayType { + public static void main(String[] args) { + Integer[] ints = { 1, 2, 3, 4, 5 }; + Double[] doubles = { 1.1, 2.2, 3.3, 4.4, 5.5 }; + Integer[] ints2 = + new ClassParameter().f(ints); + Double[] doubles2 = + new ClassParameter().f(doubles); + ints2 = MethodParameter.f(ints); + doubles2 = MethodParameter.f(doubles); + } +} ///:~ diff --git a/src/arrays/PrimitiveConversionDemonstration.java b/src/arrays/PrimitiveConversionDemonstration.java new file mode 100644 index 0000000..277d600 --- /dev/null +++ b/src/arrays/PrimitiveConversionDemonstration.java @@ -0,0 +1,19 @@ +package arrays;//: arrays/PrimitiveConversionDemonstration.java +import java.util.*; +import net.mindview.util.*; + +public class PrimitiveConversionDemonstration { + public static void main(String[] args) { + Integer[] a = Generated.array(Integer.class, + new CountingGenerator.Integer(), 15); + int[] b = ConvertTo.primitive(a); + System.out.println(Arrays.toString(b)); + boolean[] c = ConvertTo.primitive( + Generated.array(Boolean.class, + new CountingGenerator.Boolean(), 7)); + System.out.println(Arrays.toString(c)); + } +} /* Output: +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] +[true, false, true, false, true, false, true] +*///:~ diff --git a/src/arrays/RaggedArray.java b/src/arrays/RaggedArray.java new file mode 100644 index 0000000..b3aa13b --- /dev/null +++ b/src/arrays/RaggedArray.java @@ -0,0 +1,19 @@ +package arrays;//: arrays/RaggedArray.java +import java.util.*; + +public class RaggedArray { + public static void main(String[] args) { + Random rand = new Random(47); + // 3-D array with varied-length vectors: + int[][][] a = new int[rand.nextInt(7)][][]; + for(int i = 0; i < a.length; i++) { + a[i] = new int[rand.nextInt(5)][]; + for(int j = 0; j < a[i].length; j++) { + a[i][j] = new int[rand.nextInt(5)]; + } + } + System.out.println(Arrays.deepToString(a)); + } +} /* Output: +[[], [[0], [0], [0, 0, 0, 0]], [[], [0, 0], [0, 0]], [[0, 0, 0], [0], [0, 0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0], []], [[0], [], [0]]] +*///:~ diff --git a/src/arrays/RandomGeneratorsTest.java b/src/arrays/RandomGeneratorsTest.java new file mode 100644 index 0000000..f1b26d3 --- /dev/null +++ b/src/arrays/RandomGeneratorsTest.java @@ -0,0 +1,18 @@ +package arrays;//: arrays/RandomGeneratorsTest.java +import net.mindview.util.*; + +public class RandomGeneratorsTest { + public static void main(String[] args) { + GeneratorsTest.test(RandomGenerator.class); + } +} /* Output: +Double: 0.73 0.53 0.16 0.19 0.52 0.27 0.26 0.05 0.8 0.76 +Float: 0.53 0.16 0.53 0.4 0.49 0.25 0.8 0.11 0.02 0.8 +Long: 7674 8804 8950 7826 4322 896 8033 2984 2344 5810 +Integer: 8303 3141 7138 6012 9966 8689 7185 6992 5746 3976 +Short: 3358 20592 284 26791 12834 -8092 13656 29324 -1423 5327 +String: bkInaMe sbtWHkj UrUkZPg wsqPzDy CyRFJQA HxxHvHq XumcXZJ oogoYWM NvqeuTp nXsgqia +Character: x x E A J J m z M s +Byte: -60 -17 55 -14 -5 115 39 -37 79 115 +Boolean: false true false false true true true true true true +*///:~ diff --git a/src/arrays/Reverse.java b/src/arrays/Reverse.java new file mode 100644 index 0000000..41d723b --- /dev/null +++ b/src/arrays/Reverse.java @@ -0,0 +1,30 @@ +package arrays;//: arrays/Reverse.java +// The Collections.reverseOrder() Comparator +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class Reverse { + public static void main(String[] args) { + CompType[] a = Generated.array( + new CompType[12], CompType.generator()); + print("before sorting:"); + print(Arrays.toString(a)); + Arrays.sort(a, Collections.reverseOrder()); + print("after sorting:"); + print(Arrays.toString(a)); + } +} /* Output: +before sorting: +[[i = 58, j = 55], [i = 93, j = 61], [i = 61, j = 29] +, [i = 68, j = 0], [i = 22, j = 7], [i = 88, j = 28] +, [i = 51, j = 89], [i = 9, j = 78], [i = 98, j = 61] +, [i = 20, j = 58], [i = 16, j = 40], [i = 11, j = 22] +] +after sorting: +[[i = 98, j = 61], [i = 93, j = 61], [i = 88, j = 28] +, [i = 68, j = 0], [i = 61, j = 29], [i = 58, j = 55] +, [i = 51, j = 89], [i = 22, j = 7], [i = 20, j = 58] +, [i = 16, j = 40], [i = 11, j = 22], [i = 9, j = 78] +] +*///:~ diff --git a/src/arrays/StringSorting.java b/src/arrays/StringSorting.java new file mode 100644 index 0000000..31fde70 --- /dev/null +++ b/src/arrays/StringSorting.java @@ -0,0 +1,24 @@ +package arrays;//: arrays/StringSorting.java +// Sorting an array of Strings. +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class StringSorting { + public static void main(String[] args) { + String[] sa = Generated.array(new String[20], + new RandomGenerator.String(5)); + print("Before sort: " + Arrays.toString(sa)); + Arrays.sort(sa); + print("After sort: " + Arrays.toString(sa)); + Arrays.sort(sa, Collections.reverseOrder()); + print("Reverse sort: " + Arrays.toString(sa)); + Arrays.sort(sa, String.CASE_INSENSITIVE_ORDER); + print("Case-insensitive sort: " + Arrays.toString(sa)); + } +} /* Output: +Before sort: [YNzbr, nyGcF, OWZnT, cQrGs, eGZMm, JMRoE, suEcU, OneOE, dLsmw, HLGEa, hKcxr, EqUCB, bkIna, Mesbt, WHkjU, rUkZP, gwsqP, zDyCy, RFJQA, HxxHv] +After sort: [EqUCB, HLGEa, HxxHv, JMRoE, Mesbt, OWZnT, OneOE, RFJQA, WHkjU, YNzbr, bkIna, cQrGs, dLsmw, eGZMm, gwsqP, hKcxr, nyGcF, rUkZP, suEcU, zDyCy] +Reverse sort: [zDyCy, suEcU, rUkZP, nyGcF, hKcxr, gwsqP, eGZMm, dLsmw, cQrGs, bkIna, YNzbr, WHkjU, RFJQA, OneOE, OWZnT, Mesbt, JMRoE, HxxHv, HLGEa, EqUCB] +Case-insensitive sort: [bkIna, cQrGs, dLsmw, eGZMm, EqUCB, gwsqP, hKcxr, HLGEa, HxxHv, JMRoE, Mesbt, nyGcF, OneOE, OWZnT, RFJQA, rUkZP, suEcU, WHkjU, YNzbr, zDyCy] +*///:~ diff --git a/src/arrays/TestArrayGeneration.java b/src/arrays/TestArrayGeneration.java new file mode 100644 index 0000000..68e2b05 --- /dev/null +++ b/src/arrays/TestArrayGeneration.java @@ -0,0 +1,45 @@ +package arrays;//: arrays/TestArrayGeneration.java +// Test the tools that use generators to fill arrays. +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class TestArrayGeneration { + public static void main(String[] args) { + int size = 6; + boolean[] a1 = ConvertTo.primitive(Generated.array( + Boolean.class, new RandomGenerator.Boolean(), size)); + print("a1 = " + Arrays.toString(a1)); + byte[] a2 = ConvertTo.primitive(Generated.array( + Byte.class, new RandomGenerator.Byte(), size)); + print("a2 = " + Arrays.toString(a2)); + char[] a3 = ConvertTo.primitive(Generated.array( + Character.class, + new RandomGenerator.Character(), size)); + print("a3 = " + Arrays.toString(a3)); + short[] a4 = ConvertTo.primitive(Generated.array( + Short.class, new RandomGenerator.Short(), size)); + print("a4 = " + Arrays.toString(a4)); + int[] a5 = ConvertTo.primitive(Generated.array( + Integer.class, new RandomGenerator.Integer(), size)); + print("a5 = " + Arrays.toString(a5)); + long[] a6 = ConvertTo.primitive(Generated.array( + Long.class, new RandomGenerator.Long(), size)); + print("a6 = " + Arrays.toString(a6)); + float[] a7 = ConvertTo.primitive(Generated.array( + Float.class, new RandomGenerator.Float(), size)); + print("a7 = " + Arrays.toString(a7)); + double[] a8 = ConvertTo.primitive(Generated.array( + Double.class, new RandomGenerator.Double(), size)); + print("a8 = " + Arrays.toString(a8)); + } +} /* Output: +a1 = [true, false, true, false, false, true] +a2 = [104, -79, -76, 126, 33, -64] +a3 = [Z, n, T, c, Q, r] +a4 = [-13408, 22612, 15401, 15161, -28466, -12603] +a5 = [7704, 7383, 7706, 575, 8410, 6342] +a6 = [7674, 8804, 8950, 7826, 4322, 896] +a7 = [0.01, 0.2, 0.4, 0.79, 0.27, 0.45] +a8 = [0.16, 0.87, 0.7, 0.66, 0.87, 0.59] +*///:~ diff --git a/src/arrays/TestGenerated.java b/src/arrays/TestGenerated.java new file mode 100644 index 0000000..8c2cd71 --- /dev/null +++ b/src/arrays/TestGenerated.java @@ -0,0 +1,19 @@ +package arrays;//: arrays/TestGenerated.java +import java.util.*; +import net.mindview.util.*; + +public class TestGenerated { + public static void main(String[] args) { + Integer[] a = { 9, 8, 7, 6 }; + System.out.println(Arrays.toString(a)); + a = Generated.array(a,new CountingGenerator.Integer()); + System.out.println(Arrays.toString(a)); + Integer[] b = Generated.array(Integer.class, + new CountingGenerator.Integer(), 15); + System.out.println(Arrays.toString(b)); + } +} /* Output: +[9, 8, 7, 6] +[0, 1, 2, 3] +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] +*///:~ diff --git a/src/arrays/ThreeDWithNew.java b/src/arrays/ThreeDWithNew.java new file mode 100644 index 0000000..d297229 --- /dev/null +++ b/src/arrays/ThreeDWithNew.java @@ -0,0 +1,12 @@ +package arrays;//: arrays/ThreeDWithNew.java +import java.util.*; + +public class ThreeDWithNew { + public static void main(String[] args) { + // 3-D array with fixed length: + int[][][] a = new int[2][2][4]; + System.out.println(Arrays.deepToString(a)); + } +} /* Output: +[[[0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0]]] +*///:~ diff --git a/src/arrays/build.xml b/src/arrays/build.xml new file mode 100644 index 0000000..024d519 --- /dev/null +++ b/src/arrays/build.xml @@ -0,0 +1,330 @@ + + + + + + build.xml for the source code for the arrays chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/bangbean/BangBean.java b/src/bangbean/BangBean.java new file mode 100644 index 0000000..332f4b1 --- /dev/null +++ b/src/bangbean/BangBean.java @@ -0,0 +1,83 @@ +//: bangbean/BangBean.java +// A graphical Bean. +package bangbean; +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.util.*; + +public class +BangBean extends JPanel implements Serializable { + private int xm, ym; + private int cSize = 20; // Circle size + private String text = "Bang!"; + private int fontSize = 48; + private Color tColor = Color.RED; + private ActionListener actionListener; + public BangBean() { + addMouseListener(new ML()); + addMouseMotionListener(new MML()); + } + public int getCircleSize() { return cSize; } + public void setCircleSize(int newSize) { + cSize = newSize; + } + public String getBangText() { return text; } + public void setBangText(String newText) { + text = newText; + } + public int getFontSize() { return fontSize; } + public void setFontSize(int newSize) { + fontSize = newSize; + } + public Color getTextColor() { return tColor; } + public void setTextColor(Color newColor) { + tColor = newColor; + } + public void paintComponent(Graphics g) { + super.paintComponent(g); + g.setColor(Color.BLACK); + g.drawOval(xm - cSize/2, ym - cSize/2, cSize, cSize); + } + // This is a unicast listener, which is + // the simplest form of listener management: + public void addActionListener(ActionListener l) + throws TooManyListenersException { + if(actionListener != null) { + throw new TooManyListenersException(); + } + actionListener = l; + } + public void removeActionListener(ActionListener l) { + actionListener = null; + } + class ML extends MouseAdapter { + public void mousePressed(MouseEvent e) { + Graphics g = getGraphics(); + g.setColor(tColor); + g.setFont( + new Font("TimesRoman", Font.BOLD, fontSize)); + int width = g.getFontMetrics().stringWidth(text); + g.drawString(text, (getSize().width - width) /2, + getSize().height/2); + g.dispose(); + // Call the listener's method: + if(actionListener != null) { + actionListener.actionPerformed( + new ActionEvent(BangBean.this, + ActionEvent.ACTION_PERFORMED, null)); + } + } + } + class MML extends MouseMotionAdapter { + public void mouseMoved(MouseEvent e) { + xm = e.getX(); + ym = e.getY(); + repaint(); + } + } + public Dimension getPreferredSize() { + return new Dimension(200, 200); + } +} ///:~ diff --git a/src/bangbean/BangBeanTest.java b/src/bangbean/BangBeanTest.java new file mode 100644 index 0000000..9a72d40 --- /dev/null +++ b/src/bangbean/BangBeanTest.java @@ -0,0 +1,32 @@ +//: bangbean/BangBeanTest.java +// {Timeout: 5} Abort after 5 seconds when testing +package bangbean; +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import static net.mindview.util.SwingConsole.*; + +public class BangBeanTest extends JFrame { + private JTextField txt = new JTextField(20); + // During testing, report actions: + class BBL implements ActionListener { + private int count = 0; + public void actionPerformed(ActionEvent e) { + txt.setText("BangBean action "+ count++); + } + } + public BangBeanTest() { + BangBean bb = new BangBean(); + try { + bb.addActionListener(new BBL()); + } catch(TooManyListenersException e) { + txt.setText("Too many listeners"); + } + add(bb); + add(BorderLayout.SOUTH, txt); + } + public static void main(String[] args) { + run(new BangBeanTest(), 400, 500); + } +} ///:~ diff --git a/src/bangbean/build.xml b/src/bangbean/build.xml new file mode 100644 index 0000000..1bbe9aa --- /dev/null +++ b/src/bangbean/build.xml @@ -0,0 +1,73 @@ + + + + + + build.xml for the source code for the bangbean chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/concurrency/ActiveObjectDemo.java b/src/concurrency/ActiveObjectDemo.java new file mode 100644 index 0000000..ac8ca37 --- /dev/null +++ b/src/concurrency/ActiveObjectDemo.java @@ -0,0 +1,92 @@ +package concurrency;//: concurrency/ActiveObjectDemo.java +// Can only pass constants, immutables, "disconnected +// objects," or other active objects as arguments +// to asynch methods. +import java.util.concurrent.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class ActiveObjectDemo { + private ExecutorService ex = + Executors.newSingleThreadExecutor(); + private Random rand = new Random(47); + // Insert a random delay to produce the effect + // of a calculation time: + private void pause(int factor) { + try { + TimeUnit.MILLISECONDS.sleep( + 100 + rand.nextInt(factor)); + } catch(InterruptedException e) { + print("sleep() interrupted"); + } + } + public Future + calculateInt(final int x, final int y) { + return ex.submit(new Callable() { + public Integer call() { + print("starting " + x + " + " + y); + pause(500); + return x + y; + } + }); + } + public Future + calculateFloat(final float x, final float y) { + return ex.submit(new Callable() { + public Float call() { + print("starting " + x + " + " + y); + pause(2000); + return x + y; + } + }); + } + public void shutdown() { ex.shutdown(); } + public static void main(String[] args) { + ActiveObjectDemo d1 = new ActiveObjectDemo(); + // Prevents ConcurrentModificationException: + List> results = + new CopyOnWriteArrayList>(); + for(float f = 0.0f; f < 1.0f; f += 0.2f) { + results.add(d1.calculateFloat(f, f)); + } + for(int i = 0; i < 5; i++) { + results.add(d1.calculateInt(i, i)); + } + print("All asynch calls made"); + while(results.size() > 0) { + for(Future f : results) { + if(f.isDone()) { + try { + print(f.get()); + } catch(Exception e) { + throw new RuntimeException(e); + } + results.remove(f); + } + } + } + d1.shutdown(); + } +} /* Output: (85% match) +All asynch calls made +starting 0.0 + 0.0 +starting 0.2 + 0.2 +0.0 +starting 0.4 + 0.4 +0.4 +starting 0.6 + 0.6 +0.8 +starting 0.8 + 0.8 +1.2 +starting 0 + 0 +1.6 +starting 1 + 1 +0 +starting 2 + 2 +2 +starting 3 + 3 +4 +starting 4 + 4 +6 +8 +*///:~ diff --git a/src/concurrency/AtomicEvenGenerator.java b/src/concurrency/AtomicEvenGenerator.java new file mode 100644 index 0000000..7f589dd --- /dev/null +++ b/src/concurrency/AtomicEvenGenerator.java @@ -0,0 +1,15 @@ +package concurrency;//: concurrency/AtomicEvenGenerator.java +// Atomic classes are occasionally useful in regular code. +// {RunByHand} +import java.util.concurrent.atomic.*; + +public class AtomicEvenGenerator extends IntGenerator { + private AtomicInteger currentEvenValue = + new AtomicInteger(0); + public int next() { + return currentEvenValue.addAndGet(2); + } + public static void main(String[] args) { + EvenChecker.test(new AtomicEvenGenerator()); + } +} ///:~ diff --git a/src/concurrency/AtomicIntegerTest.java b/src/concurrency/AtomicIntegerTest.java new file mode 100644 index 0000000..874993a --- /dev/null +++ b/src/concurrency/AtomicIntegerTest.java @@ -0,0 +1,33 @@ +package concurrency;//: concurrency/AtomicIntegerTest.java +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.*; + +public class AtomicIntegerTest implements Runnable { + private AtomicInteger i = new AtomicInteger(0); + public int getValue() { return i.get(); } + private void evenIncrement() { i.addAndGet(2); } + public void run() { + while(true) { + evenIncrement(); + } + } + public static void main(String[] args) { + new Timer().schedule(new TimerTask() { + public void run() { + System.err.println("Aborting"); + System.exit(0); + } + }, 5000); // Terminate after 5 seconds + ExecutorService exec = Executors.newCachedThreadPool(); + AtomicIntegerTest ait = new AtomicIntegerTest(); + exec.execute(ait); + while(true) { + int val = ait.getValue(); + if(val % 2 != 0) { + System.out.println(val); + System.exit(0); + } + } + } +} ///:~ diff --git a/src/concurrency/Atomicity.java b/src/concurrency/Atomicity.java new file mode 100644 index 0000000..8d593e1 --- /dev/null +++ b/src/concurrency/Atomicity.java @@ -0,0 +1,29 @@ +package concurrency;//: concurrency/Atomicity.java +// {Exec: javap -c Atomicity} + +public class Atomicity { + int i; + void f1() { i++; } + void f2() { i += 3; } +} /* Output: (Sample) +... +void f1(); + Code: + 0: aload_0 + 1: dup + 2: getfield #2; //Field i:I + 5: iconst_1 + 6: iadd + 7: putfield #2; //Field i:I + 10: return + +void f2(); + Code: + 0: aload_0 + 1: dup + 2: getfield #2; //Field i:I + 5: iconst_3 + 6: iadd + 7: putfield #2; //Field i:I + 10: return +*///:~ diff --git a/src/concurrency/AtomicityTest.java b/src/concurrency/AtomicityTest.java new file mode 100644 index 0000000..3542457 --- /dev/null +++ b/src/concurrency/AtomicityTest.java @@ -0,0 +1,27 @@ +package concurrency;//: concurrency/AtomicityTest.java +import java.util.concurrent.*; + +public class AtomicityTest implements Runnable { + private int i = 0; + public int getValue() { return i; } + private synchronized void evenIncrement() { i++; i++; } + public void run() { + while(true) { + evenIncrement(); + } + } + public static void main(String[] args) { + ExecutorService exec = Executors.newCachedThreadPool(); + AtomicityTest at = new AtomicityTest(); + exec.execute(at); + while(true) { + int val = at.getValue(); + if(val % 2 != 0) { + System.out.println(val); + System.exit(0); + } + } + } +} /* Output: (Sample) +191583767 +*///:~ diff --git a/src/concurrency/AttemptLocking.java b/src/concurrency/AttemptLocking.java new file mode 100644 index 0000000..7ec176a --- /dev/null +++ b/src/concurrency/AttemptLocking.java @@ -0,0 +1,57 @@ +package concurrency;//: concurrency/AttemptLocking.java +// Locks in the concurrent library allow you +// to give up on trying to acquire a lock. +import java.util.concurrent.*; +import java.util.concurrent.locks.*; + +public class AttemptLocking { + private ReentrantLock lock = new ReentrantLock(); + public void untimed() { + boolean captured = lock.tryLock(); + try { + System.out.println("tryLock(): " + captured); + } finally { + if(captured) { + lock.unlock(); + } + } + } + public void timed() { + boolean captured = false; + try { + captured = lock.tryLock(2, TimeUnit.SECONDS); + } catch(InterruptedException e) { + throw new RuntimeException(e); + } + try { + System.out.println("tryLock(2, TimeUnit.SECONDS): " + + captured); + } finally { + if(captured) { + lock.unlock(); + } + } + } + public static void main(String[] args) { + final AttemptLocking al = new AttemptLocking(); + al.untimed(); // True -- lock is available + al.timed(); // True -- lock is available + // Now create a separate task to grab the lock: + new Thread() { + { setDaemon(true); } + public void run() { + al.lock.lock(); + System.out.println("acquired"); + } + }.start(); + Thread.yield(); // Give the 2nd task a chance + al.untimed(); // False -- lock grabbed by task + al.timed(); // False -- lock grabbed by task + } +} /* Output: +tryLock(): true +tryLock(2, TimeUnit.SECONDS): true +acquired +tryLock(): false +tryLock(2, TimeUnit.SECONDS): false +*///:~ diff --git a/src/concurrency/BankTellerSimulation.java b/src/concurrency/BankTellerSimulation.java new file mode 100644 index 0000000..f100124 --- /dev/null +++ b/src/concurrency/BankTellerSimulation.java @@ -0,0 +1,212 @@ +package concurrency;//: concurrency/BankTellerSimulation.java +// Using queues and multithreading. +// {Args: 5} +import java.util.concurrent.*; +import java.util.*; + +// Read-only objects don't require synchronization: +class Customer { + private final int serviceTime; + public Customer(int tm) { serviceTime = tm; } + public int getServiceTime() { return serviceTime; } + public String toString() { + return "[" + serviceTime + "]"; + } +} + +// Teach the customer line to display itself: +class CustomerLine extends ArrayBlockingQueue { + public CustomerLine(int maxLineSize) { + super(maxLineSize); + } + public String toString() { + if(this.size() == 0) { + return "[Empty]"; + } + StringBuilder result = new StringBuilder(); + for(Customer customer : this) { + result.append(customer); + } + return result.toString(); + } +} + +// Randomly add customers to a queue: +class CustomerGenerator implements Runnable { + private CustomerLine customers; + private static Random rand = new Random(47); + public CustomerGenerator(CustomerLine cq) { + customers = cq; + } + public void run() { + try { + while(!Thread.interrupted()) { + TimeUnit.MILLISECONDS.sleep(rand.nextInt(300)); + customers.put(new Customer(rand.nextInt(1000))); + } + } catch(InterruptedException e) { + System.out.println("CustomerGenerator interrupted"); + } + System.out.println("CustomerGenerator terminating"); + } +} + +class Teller implements Runnable, Comparable { + private static int counter = 0; + private final int id = counter++; + // Customers served during this shift: + private int customersServed = 0; + private CustomerLine customers; + private boolean servingCustomerLine = true; + public Teller(CustomerLine cq) { customers = cq; } + public void run() { + try { + while(!Thread.interrupted()) { + Customer customer = customers.take(); + TimeUnit.MILLISECONDS.sleep( + customer.getServiceTime()); + synchronized(this) { + customersServed++; + while(!servingCustomerLine) { + wait(); + } + } + } + } catch(InterruptedException e) { + System.out.println(this + "interrupted"); + } + System.out.println(this + "terminating"); + } + public synchronized void doSomethingElse() { + customersServed = 0; + servingCustomerLine = false; + } + public synchronized void serveCustomerLine() { + assert !servingCustomerLine:"already serving: " + this; + servingCustomerLine = true; + notifyAll(); + } + public String toString() { return "Teller " + id + " "; } + public String shortString() { return "T" + id; } + // Used by priority queue: + public synchronized int compareTo(Teller other) { + return customersServed < other.customersServed ? -1 : + (customersServed == other.customersServed ? 0 : 1); + } +} + +class TellerManager implements Runnable { + private ExecutorService exec; + private CustomerLine customers; + private PriorityQueue workingTellers = + new PriorityQueue(); + private Queue tellersDoingOtherThings = + new LinkedList(); + private int adjustmentPeriod; + private static Random rand = new Random(47); + public TellerManager(ExecutorService e, + CustomerLine customers, int adjustmentPeriod) { + exec = e; + this.customers = customers; + this.adjustmentPeriod = adjustmentPeriod; + // Start with a single teller: + Teller teller = new Teller(customers); + exec.execute(teller); + workingTellers.add(teller); + } + public void adjustTellerNumber() { + // This is actually a control system. By adjusting + // the numbers, you can reveal stability issues in + // the control mechanism. + // If line is too long, add another teller: + if(customers.size() / workingTellers.size() > 2) { + // If tellers are on break or doing + // another job, bring one back: + if(tellersDoingOtherThings.size() > 0) { + Teller teller = tellersDoingOtherThings.remove(); + teller.serveCustomerLine(); + workingTellers.offer(teller); + return; + } + // Else create (hire) a new teller + Teller teller = new Teller(customers); + exec.execute(teller); + workingTellers.add(teller); + return; + } + // If line is short enough, remove a teller: + if(workingTellers.size() > 1 && + customers.size() / workingTellers.size() < 2) { + reassignOneTeller(); + } + // If there is no line, we only need one teller: + if(customers.size() == 0) { + while(workingTellers.size() > 1) { + reassignOneTeller(); + } + } + } + // Give a teller a different job or a break: + private void reassignOneTeller() { + Teller teller = workingTellers.poll(); + teller.doSomethingElse(); + tellersDoingOtherThings.offer(teller); + } + public void run() { + try { + while(!Thread.interrupted()) { + TimeUnit.MILLISECONDS.sleep(adjustmentPeriod); + adjustTellerNumber(); + System.out.print(customers + " { "); + for(Teller teller : workingTellers) { + System.out.print(teller.shortString() + " "); + } + System.out.println("}"); + } + } catch(InterruptedException e) { + System.out.println(this + "interrupted"); + } + System.out.println(this + "terminating"); + } + public String toString() { return "TellerManager "; } +} + +public class BankTellerSimulation { + static final int MAX_LINE_SIZE = 50; + static final int ADJUSTMENT_PERIOD = 1000; + public static void main(String[] args) throws Exception { + ExecutorService exec = Executors.newCachedThreadPool(); + // If line is too long, customers will leave: + CustomerLine customers = + new CustomerLine(MAX_LINE_SIZE); + exec.execute(new CustomerGenerator(customers)); + // Manager will add and remove tellers as necessary: + exec.execute(new TellerManager( + exec, customers, ADJUSTMENT_PERIOD)); + if(args.length > 0) // Optional argument + { + TimeUnit.SECONDS.sleep(new Integer(args[0])); + } else { + System.out.println("Press 'Enter' to quit"); + System.in.read(); + } + exec.shutdownNow(); + } +} /* Output: (Sample) +[429][200][207] { T0 T1 } +[861][258][140][322] { T0 T1 } +[575][342][804][826][896][984] { T0 T1 T2 } +[984][810][141][12][689][992][976][368][395][354] { T0 T1 T2 T3 } +Teller 2 interrupted +Teller 2 terminating +Teller 1 interrupted +Teller 1 terminating +TellerManager interrupted +TellerManager terminating +Teller 3 interrupted +Teller 3 terminating +Teller 0 interrupted +Teller 0 terminating +CustomerGenerator interrupted +CustomerGenerator terminating +*///:~ diff --git a/src/concurrency/BasicThreads.java b/src/concurrency/BasicThreads.java new file mode 100644 index 0000000..0e0d524 --- /dev/null +++ b/src/concurrency/BasicThreads.java @@ -0,0 +1,13 @@ +package concurrency;//: concurrency/BasicThreads.java +// The most basic use of the Thread class. + +public class BasicThreads { + public static void main(String[] args) { + Thread t = new Thread(new LiftOff()); + t.start(); + System.out.println("Waiting for LiftOff"); + } +} /* Output: (90% match) +Waiting for LiftOff +#0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(Liftoff!), +*///:~ diff --git a/src/concurrency/CachedThreadPool.java b/src/concurrency/CachedThreadPool.java new file mode 100644 index 0000000..c3ab560 --- /dev/null +++ b/src/concurrency/CachedThreadPool.java @@ -0,0 +1,14 @@ +package concurrency;//: concurrency/CachedThreadPool.java +import java.util.concurrent.*; + +public class CachedThreadPool { + public static void main(String[] args) { + ExecutorService exec = Executors.newCachedThreadPool(); + for(int i = 0; i < 5; i++) { + exec.execute(new LiftOff()); + } + exec.shutdown(); + } +} /* Output: (Sample) +#0(9), #0(8), #1(9), #2(9), #3(9), #4(9), #0(7), #1(8), #2(8), #3(8), #4(8), #0(6), #1(7), #2(7), #3(7), #4(7), #0(5), #1(6), #2(6), #3(6), #4(6), #0(4), #1(5), #2(5), #3(5), #4(5), #0(3), #1(4), #2(4), #3(4), #4(4), #0(2), #1(3), #2(3), #3(3), #4(3), #0(1), #1(2), #2(2), #3(2), #4(2), #0(Liftoff!), #1(1), #2(1), #3(1), #4(1), #1(Liftoff!), #2(Liftoff!), #3(Liftoff!), #4(Liftoff!), +*///:~ diff --git a/src/concurrency/CallableDemo.java b/src/concurrency/CallableDemo.java new file mode 100644 index 0000000..cbc7b9e --- /dev/null +++ b/src/concurrency/CallableDemo.java @@ -0,0 +1,48 @@ +package concurrency;//: concurrency/CallableDemo.java +import java.util.concurrent.*; +import java.util.*; + +class TaskWithResult implements Callable { + private int id; + public TaskWithResult(int id) { + this.id = id; + } + public String call() { + return "result of TaskWithResult " + id; + } +} + +public class CallableDemo { + public static void main(String[] args) { + ExecutorService exec = Executors.newCachedThreadPool(); + ArrayList> results = + new ArrayList>(); + for(int i = 0; i < 10; i++) { + results.add(exec.submit(new TaskWithResult(i))); + } + for(Future fs : results) { + try { + // get() blocks until completion: + System.out.println(fs.get()); + } catch(InterruptedException e) { + System.out.println(e); + return; + } catch(ExecutionException e) { + System.out.println(e); + } finally { + exec.shutdown(); + } + } + } +} /* Output: +result of TaskWithResult 0 +result of TaskWithResult 1 +result of TaskWithResult 2 +result of TaskWithResult 3 +result of TaskWithResult 4 +result of TaskWithResult 5 +result of TaskWithResult 6 +result of TaskWithResult 7 +result of TaskWithResult 8 +result of TaskWithResult 9 +*///:~ diff --git a/src/concurrency/CaptureUncaughtException.java b/src/concurrency/CaptureUncaughtException.java new file mode 100644 index 0000000..4846c2e --- /dev/null +++ b/src/concurrency/CaptureUncaughtException.java @@ -0,0 +1,47 @@ +package concurrency;//: concurrency/CaptureUncaughtException.java +import java.util.concurrent.*; + +class ExceptionThread2 implements Runnable { + public void run() { + Thread t = Thread.currentThread(); + System.out.println("run() by " + t); + System.out.println( + "eh = " + t.getUncaughtExceptionHandler()); + throw new RuntimeException(); + } +} + +class MyUncaughtExceptionHandler implements +Thread.UncaughtExceptionHandler { + public void uncaughtException(Thread t, Throwable e) { + System.out.println("caught " + e); + } +} + +class HandlerThreadFactory implements ThreadFactory { + public Thread newThread(Runnable r) { + System.out.println(this + " creating new Thread"); + Thread t = new Thread(r); + System.out.println("created " + t); + t.setUncaughtExceptionHandler( + new MyUncaughtExceptionHandler()); + System.out.println( + "eh = " + t.getUncaughtExceptionHandler()); + return t; + } +} + +public class CaptureUncaughtException { + public static void main(String[] args) { + ExecutorService exec = Executors.newCachedThreadPool( + new HandlerThreadFactory()); + exec.execute(new ExceptionThread2()); + } +} /* Output: (90% match) +HandlerThreadFactory@de6ced creating new Thread +created Thread[Thread-0,5,main] +eh = MyUncaughtExceptionHandler@1fb8ee3 +run() by Thread[Thread-0,5,main] +eh = MyUncaughtExceptionHandler@1fb8ee3 +caught java.lang.RuntimeException +*///:~ diff --git a/src/concurrency/CarBuilder.java b/src/concurrency/CarBuilder.java new file mode 100644 index 0000000..f518b98 --- /dev/null +++ b/src/concurrency/CarBuilder.java @@ -0,0 +1,211 @@ +package concurrency;//: concurrency/CarBuilder.java +// A complex example of tasks working together. +import java.util.concurrent.*; +import java.util.*; +import static net.mindview.util.Print.*; + +class Car { + private final int id; + private boolean + engine = false, driveTrain = false, wheels = false; + public Car(int idn) { id = idn; } + // Empty Car object: + public Car() { id = -1; } + public synchronized int getId() { return id; } + public synchronized void addEngine() { engine = true; } + public synchronized void addDriveTrain() { + driveTrain = true; + } + public synchronized void addWheels() { wheels = true; } + public synchronized String toString() { + return "Car " + id + " [" + " engine: " + engine + + " driveTrain: " + driveTrain + + " wheels: " + wheels + " ]"; + } +} + +class CarQueue extends LinkedBlockingQueue {} + +class ChassisBuilder implements Runnable { + private CarQueue carQueue; + private int counter = 0; + public ChassisBuilder(CarQueue cq) { carQueue = cq; } + public void run() { + try { + while(!Thread.interrupted()) { + TimeUnit.MILLISECONDS.sleep(500); + // Make chassis: + Car c = new Car(counter++); + print("ChassisBuilder created " + c); + // Insert into queue + carQueue.put(c); + } + } catch(InterruptedException e) { + print("Interrupted: ChassisBuilder"); + } + print("ChassisBuilder off"); + } +} + +class Assembler implements Runnable { + private CarQueue chassisQueue, finishingQueue; + private Car car; + private CyclicBarrier barrier = new CyclicBarrier(4); + private RobotPool robotPool; + public Assembler(CarQueue cq, CarQueue fq, RobotPool rp){ + chassisQueue = cq; + finishingQueue = fq; + robotPool = rp; + } + public Car car() { return car; } + public CyclicBarrier barrier() { return barrier; } + public void run() { + try { + while(!Thread.interrupted()) { + // Blocks until chassis is available: + car = chassisQueue.take(); + // Hire robots to perform work: + robotPool.hire(EngineRobot.class, this); + robotPool.hire(DriveTrainRobot.class, this); + robotPool.hire(WheelRobot.class, this); + barrier.await(); // Until the robots finish + // Put car into finishingQueue for further work + finishingQueue.put(car); + } + } catch(InterruptedException e) { + print("Exiting Assembler via interrupt"); + } catch(BrokenBarrierException e) { + // This one we want to know about + throw new RuntimeException(e); + } + print("Assembler off"); + } +} + +class Reporter implements Runnable { + private CarQueue carQueue; + public Reporter(CarQueue cq) { carQueue = cq; } + public void run() { + try { + while(!Thread.interrupted()) { + print(carQueue.take()); + } + } catch(InterruptedException e) { + print("Exiting Reporter via interrupt"); + } + print("Reporter off"); + } +} + +abstract class Robot implements Runnable { + private RobotPool pool; + public Robot(RobotPool p) { pool = p; } + protected Assembler assembler; + public Robot assignAssembler(Assembler assembler) { + this.assembler = assembler; + return this; + } + private boolean engage = false; + public synchronized void engage() { + engage = true; + notifyAll(); + } + // The part of run() that's different for each robot: + abstract protected void performService(); + public void run() { + try { + powerDown(); // Wait until needed + while(!Thread.interrupted()) { + performService(); + assembler.barrier().await(); // Synchronize + // We're done with that job... + powerDown(); + } + } catch(InterruptedException e) { + print("Exiting " + this + " via interrupt"); + } catch(BrokenBarrierException e) { + // This one we want to know about + throw new RuntimeException(e); + } + print(this + " off"); + } + private synchronized void + powerDown() throws InterruptedException { + engage = false; + assembler = null; // Disconnect from the Assembler + // Put ourselves back in the available pool: + pool.release(this); + while(engage == false) // Power down + { + wait(); + } + } + public String toString() { return getClass().getName(); } +} + +class EngineRobot extends Robot { + public EngineRobot(RobotPool pool) { super(pool); } + protected void performService() { + print(this + " installing engine"); + assembler.car().addEngine(); + } +} + +class DriveTrainRobot extends Robot { + public DriveTrainRobot(RobotPool pool) { super(pool); } + protected void performService() { + print(this + " installing DriveTrain"); + assembler.car().addDriveTrain(); + } +} + +class WheelRobot extends Robot { + public WheelRobot(RobotPool pool) { super(pool); } + protected void performService() { + print(this + " installing Wheels"); + assembler.car().addWheels(); + } +} + +class RobotPool { + // Quietly prevents identical entries: + private Set pool = new HashSet(); + public synchronized void add(Robot r) { + pool.add(r); + notifyAll(); + } + public synchronized void + hire(Class robotType, Assembler d) + throws InterruptedException { + for(Robot r : pool) { + if(r.getClass().equals(robotType)) { + pool.remove(r); + r.assignAssembler(d); + r.engage(); // Power it up to do the task + return; + } + } + wait(); // None available + hire(robotType, d); // Try again, recursively + } + public synchronized void release(Robot r) { add(r); } +} + +public class CarBuilder { + public static void main(String[] args) throws Exception { + CarQueue chassisQueue = new CarQueue(), + finishingQueue = new CarQueue(); + ExecutorService exec = Executors.newCachedThreadPool(); + RobotPool robotPool = new RobotPool(); + exec.execute(new EngineRobot(robotPool)); + exec.execute(new DriveTrainRobot(robotPool)); + exec.execute(new WheelRobot(robotPool)); + exec.execute(new Assembler( + chassisQueue, finishingQueue, robotPool)); + exec.execute(new Reporter(finishingQueue)); + // Start everything running by producing chassis: + exec.execute(new ChassisBuilder(chassisQueue)); + TimeUnit.SECONDS.sleep(7); + exec.shutdownNow(); + } +} /* (Execute to see output) *///:~ diff --git a/src/concurrency/Chopstick.java b/src/concurrency/Chopstick.java new file mode 100644 index 0000000..51d94ba --- /dev/null +++ b/src/concurrency/Chopstick.java @@ -0,0 +1,17 @@ +package concurrency;//: concurrency/Chopstick.java +// Chopsticks for dining philosophers. + +public class Chopstick { + private boolean taken = false; + public synchronized + void take() throws InterruptedException { + while(taken) { + wait(); + } + taken = true; + } + public synchronized void drop() { + taken = false; + notifyAll(); + } +} ///:~ diff --git a/src/concurrency/CloseResource.java b/src/concurrency/CloseResource.java new file mode 100644 index 0000000..e1b26af --- /dev/null +++ b/src/concurrency/CloseResource.java @@ -0,0 +1,37 @@ +package concurrency;//: concurrency/CloseResource.java +// Interrupting a blocked task by +// closing the underlying resource. +// {RunByHand} +import java.net.*; +import java.util.concurrent.*; +import java.io.*; +import static net.mindview.util.Print.*; + +public class CloseResource { + public static void main(String[] args) throws Exception { + ExecutorService exec = Executors.newCachedThreadPool(); + ServerSocket server = new ServerSocket(8080); + InputStream socketInput = + new Socket("localhost", 8080).getInputStream(); + exec.execute(new IOBlocked(socketInput)); + exec.execute(new IOBlocked(System.in)); + TimeUnit.MILLISECONDS.sleep(100); + print("Shutting down all threads"); + exec.shutdownNow(); + TimeUnit.SECONDS.sleep(1); + print("Closing " + socketInput.getClass().getName()); + socketInput.close(); // Releases blocked thread + TimeUnit.SECONDS.sleep(1); + print("Closing " + System.in.getClass().getName()); + System.in.close(); // Releases blocked thread + } +} /* Output: (85% match) +Waiting for read(): +Waiting for read(): +Shutting down all threads +Closing java.net.SocketInputStream +Interrupted from blocked I/O +Exiting IOBlocked.run() +Closing java.io.BufferedInputStream +Exiting IOBlocked.run() +*///:~ diff --git a/src/concurrency/CountDownLatchDemo.java b/src/concurrency/CountDownLatchDemo.java new file mode 100644 index 0000000..0ffdbef --- /dev/null +++ b/src/concurrency/CountDownLatchDemo.java @@ -0,0 +1,68 @@ +package concurrency;//: concurrency/CountDownLatchDemo.java +import java.util.concurrent.*; +import java.util.*; +import static net.mindview.util.Print.*; + +// Performs some portion of a task: +class TaskPortion implements Runnable { + private static int counter = 0; + private final int id = counter++; + private static Random rand = new Random(47); + private final CountDownLatch latch; + TaskPortion(CountDownLatch latch) { + this.latch = latch; + } + public void run() { + try { + doWork(); + latch.countDown(); + } catch(InterruptedException ex) { + // Acceptable way to exit + } + } + public void doWork() throws InterruptedException { + TimeUnit.MILLISECONDS.sleep(rand.nextInt(2000)); + print(this + "completed"); + } + public String toString() { + return String.format("%1$-3d ", id); + } +} + +// Waits on the CountDownLatch: +class WaitingTask implements Runnable { + private static int counter = 0; + private final int id = counter++; + private final CountDownLatch latch; + WaitingTask(CountDownLatch latch) { + this.latch = latch; + } + public void run() { + try { + latch.await(); + print("Latch barrier passed for " + this); + } catch(InterruptedException ex) { + print(this + " interrupted"); + } + } + public String toString() { + return String.format("WaitingTask %1$-3d ", id); + } +} + +public class CountDownLatchDemo { + static final int SIZE = 100; + public static void main(String[] args) throws Exception { + ExecutorService exec = Executors.newCachedThreadPool(); + // All must share a single CountDownLatch object: + CountDownLatch latch = new CountDownLatch(SIZE); + for(int i = 0; i < 10; i++) { + exec.execute(new WaitingTask(latch)); + } + for(int i = 0; i < SIZE; i++) { + exec.execute(new TaskPortion(latch)); + } + print("Launched all tasks"); + exec.shutdown(); // Quit when all tasks complete + } +} /* (Execute to see output) *///:~ diff --git a/src/concurrency/CriticalSection.java b/src/concurrency/CriticalSection.java new file mode 100644 index 0000000..c1d0017 --- /dev/null +++ b/src/concurrency/CriticalSection.java @@ -0,0 +1,141 @@ +//: concurrency/CriticalSection.java +// Synchronizing blocks instead of entire methods. Also +// demonstrates protection of a non-thread-safe class +// with a thread-safe one. +package concurrency; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.*; + +class Pair { // Not thread-safe + private int x, y; + public Pair(int x, int y) { + this.x = x; + this.y = y; + } + public Pair() { this(0, 0); } + public int getX() { return x; } + public int getY() { return y; } + public void incrementX() { x++; } + public void incrementY() { y++; } + public String toString() { + return "x: " + x + ", y: " + y; + } + public class PairValuesNotEqualException + extends RuntimeException { + public PairValuesNotEqualException() { + super("Pair values not equal: " + Pair.this); + } + } + // Arbitrary invariant -- both variables must be equal: + public void checkState() { + if(x != y) { + throw new PairValuesNotEqualException(); + } + } +} + +// Protect a Pair inside a thread-safe class: +abstract class PairManager { + AtomicInteger checkCounter = new AtomicInteger(0); + protected Pair p = new Pair(); + private List storage = + Collections.synchronizedList(new ArrayList()); + public synchronized Pair getPair() { + // Make a copy to keep the original safe: + return new Pair(p.getX(), p.getY()); + } + // Assume this is a time consuming operation + protected void store(Pair p) { + storage.add(p); + try { + TimeUnit.MILLISECONDS.sleep(50); + } catch(InterruptedException ignore) {} + } + public abstract void increment(); +} + +// Synchronize the entire method: +class PairManager1 extends PairManager { + public synchronized void increment() { + p.incrementX(); + p.incrementY(); + store(getPair()); + } +} + +// Use a critical section: +class PairManager2 extends PairManager { + public void increment() { + Pair temp; + synchronized(this) { + p.incrementX(); + p.incrementY(); + temp = getPair(); + } + store(temp); + } +} + +class PairManipulator implements Runnable { + private PairManager pm; + public PairManipulator(PairManager pm) { + this.pm = pm; + } + public void run() { + while(true) { + pm.increment(); + } + } + public String toString() { + return "Pair: " + pm.getPair() + + " checkCounter = " + pm.checkCounter.get(); + } +} + +class PairChecker implements Runnable { + private PairManager pm; + public PairChecker(PairManager pm) { + this.pm = pm; + } + public void run() { + while(true) { + pm.checkCounter.incrementAndGet(); + pm.getPair().checkState(); + } + } +} + +public class CriticalSection { + // Test the two different approaches: + static void + testApproaches(PairManager pman1, PairManager pman2) { + ExecutorService exec = Executors.newCachedThreadPool(); + PairManipulator + pm1 = new PairManipulator(pman1), + pm2 = new PairManipulator(pman2); + PairChecker + pcheck1 = new PairChecker(pman1), + pcheck2 = new PairChecker(pman2); + exec.execute(pm1); + exec.execute(pm2); + exec.execute(pcheck1); + exec.execute(pcheck2); + try { + TimeUnit.MILLISECONDS.sleep(500); + } catch(InterruptedException e) { + System.out.println("Sleep interrupted"); + } + System.out.println("pm1: " + pm1 + "\npm2: " + pm2); + System.exit(0); + } + public static void main(String[] args) { + PairManager + pman1 = new PairManager1(), + pman2 = new PairManager2(); + testApproaches(pman1, pman2); + } +} /* Output: (Sample) +pm1: Pair: x: 15, y: 15 checkCounter = 272565 +pm2: Pair: x: 16, y: 16 checkCounter = 3956974 +*///:~ diff --git a/src/concurrency/DaemonFromFactory.java b/src/concurrency/DaemonFromFactory.java new file mode 100644 index 0000000..805b79d --- /dev/null +++ b/src/concurrency/DaemonFromFactory.java @@ -0,0 +1,27 @@ +package concurrency;//: concurrency/DaemonFromFactory.java +// Using a Thread Factory to create daemons. +import java.util.concurrent.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class DaemonFromFactory implements Runnable { + public void run() { + try { + while(true) { + TimeUnit.MILLISECONDS.sleep(100); + print(Thread.currentThread() + " " + this); + } + } catch(InterruptedException e) { + print("Interrupted"); + } + } + public static void main(String[] args) throws Exception { + ExecutorService exec = Executors.newCachedThreadPool( + new DaemonThreadFactory()); + for(int i = 0; i < 10; i++) { + exec.execute(new DaemonFromFactory()); + } + print("All daemons started"); + TimeUnit.MILLISECONDS.sleep(500); // Run for a while + } +} /* (Execute to see output) *///:~ diff --git a/src/concurrency/Daemons.java b/src/concurrency/Daemons.java new file mode 100644 index 0000000..1fa8664 --- /dev/null +++ b/src/concurrency/Daemons.java @@ -0,0 +1,44 @@ +package concurrency;//: concurrency/Daemons.java +// Daemon threads spawn other daemon threads. +import java.util.concurrent.*; +import static net.mindview.util.Print.*; + +class Daemon implements Runnable { + private Thread[] t = new Thread[10]; + public void run() { + for(int i = 0; i < t.length; i++) { + t[i] = new Thread(new DaemonSpawn()); + t[i].start(); + printnb("DaemonSpawn " + i + " started, "); + } + for(int i = 0; i < t.length; i++) { + printnb("t[" + i + "].isDaemon() = " + + t[i].isDaemon() + ", "); + } + while(true) { + Thread.yield(); + } + } +} + +class DaemonSpawn implements Runnable { + public void run() { + while(true) { + Thread.yield(); + } + } +} + +public class Daemons { + public static void main(String[] args) throws Exception { + Thread d = new Thread(new Daemon()); + d.setDaemon(true); + d.start(); + printnb("d.isDaemon() = " + d.isDaemon() + ", "); + // Allow the daemon threads to + // finish their startup processes: + TimeUnit.SECONDS.sleep(1); + } +} /* Output: (Sample) +d.isDaemon() = true, DaemonSpawn 0 started, DaemonSpawn 1 started, DaemonSpawn 2 started, DaemonSpawn 3 started, DaemonSpawn 4 started, DaemonSpawn 5 started, DaemonSpawn 6 started, DaemonSpawn 7 started, DaemonSpawn 8 started, DaemonSpawn 9 started, t[0].isDaemon() = true, t[1].isDaemon() = true, t[2].isDaemon() = true, t[3].isDaemon() = true, t[4].isDaemon() = true, t[5].isDaemon() = true, t[6].isDaemon() = true, t[7].isDaemon() = true, t[8].isDaemon() = true, t[9].isDaemon() = true, +*///:~ diff --git a/src/concurrency/DaemonsDontRunFinally.java b/src/concurrency/DaemonsDontRunFinally.java new file mode 100644 index 0000000..ad4c3c3 --- /dev/null +++ b/src/concurrency/DaemonsDontRunFinally.java @@ -0,0 +1,27 @@ +package concurrency;//: concurrency/DaemonsDontRunFinally.java +// Daemon threads don't run the finally clause +import java.util.concurrent.*; +import static net.mindview.util.Print.*; + +class ADaemon implements Runnable { + public void run() { + try { + print("Starting ADaemon"); + TimeUnit.SECONDS.sleep(1); + } catch(InterruptedException e) { + print("Exiting via InterruptedException"); + } finally { + print("This should always run?"); + } + } +} + +public class DaemonsDontRunFinally { + public static void main(String[] args) throws Exception { + Thread t = new Thread(new ADaemon()); + t.setDaemon(true); + t.start(); + } +} /* Output: +Starting ADaemon +*///:~ diff --git a/src/concurrency/DeadlockingDiningPhilosophers.java b/src/concurrency/DeadlockingDiningPhilosophers.java new file mode 100644 index 0000000..2335169 --- /dev/null +++ b/src/concurrency/DeadlockingDiningPhilosophers.java @@ -0,0 +1,33 @@ +package concurrency;//: concurrency/DeadlockingDiningPhilosophers.java +// Demonstrates how deadlock can be hidden in a program. +// {Args: 0 5 timeout} +import java.util.concurrent.*; + +public class DeadlockingDiningPhilosophers { + public static void main(String[] args) throws Exception { + int ponder = 5; + if(args.length > 0) { + ponder = Integer.parseInt(args[0]); + } + int size = 5; + if(args.length > 1) { + size = Integer.parseInt(args[1]); + } + ExecutorService exec = Executors.newCachedThreadPool(); + Chopstick[] sticks = new Chopstick[size]; + for(int i = 0; i < size; i++) { + sticks[i] = new Chopstick(); + } + for(int i = 0; i < size; i++) { + exec.execute(new Philosopher( + sticks[i], sticks[(i+1) % size], i, ponder)); + } + if(args.length == 3 && args[2].equals("timeout")) { + TimeUnit.SECONDS.sleep(5); + } else { + System.out.println("Press 'Enter' to quit"); + System.in.read(); + } + exec.shutdownNow(); + } +} /* (Execute to see output) *///:~ diff --git a/src/concurrency/DelayQueueDemo.java b/src/concurrency/DelayQueueDemo.java new file mode 100644 index 0000000..ff33222 --- /dev/null +++ b/src/concurrency/DelayQueueDemo.java @@ -0,0 +1,94 @@ +package concurrency;//: concurrency/DelayQueueDemo.java +import java.util.concurrent.*; +import java.util.*; +import static java.util.concurrent.TimeUnit.*; +import static net.mindview.util.Print.*; + +class DelayedTask implements Runnable, Delayed { + private static int counter = 0; + private final int id = counter++; + private final int delta; + private final long trigger; + protected static List sequence = + new ArrayList(); + public DelayedTask(int delayInMilliseconds) { + delta = delayInMilliseconds; + trigger = System.nanoTime() + + NANOSECONDS.convert(delta, MILLISECONDS); + sequence.add(this); + } + public long getDelay(TimeUnit unit) { + return unit.convert( + trigger - System.nanoTime(), NANOSECONDS); + } + public int compareTo(Delayed arg) { + DelayedTask that = (DelayedTask)arg; + if(trigger < that.trigger) { + return -1; + } + if(trigger > that.trigger) { + return 1; + } + return 0; + } + public void run() { printnb(this + " "); } + public String toString() { + return String.format("[%1$-4d]", delta) + + " Task " + id; + } + public String summary() { + return "(" + id + ":" + delta + ")"; + } + public static class EndSentinel extends DelayedTask { + private ExecutorService exec; + public EndSentinel(int delay, ExecutorService e) { + super(delay); + exec = e; + } + public void run() { + for(DelayedTask pt : sequence) { + printnb(pt.summary() + " "); + } + print(); + print(this + " Calling shutdownNow()"); + exec.shutdownNow(); + } + } +} + +class DelayedTaskConsumer implements Runnable { + private DelayQueue q; + public DelayedTaskConsumer(DelayQueue q) { + this.q = q; + } + public void run() { + try { + while(!Thread.interrupted()) { + q.take().run(); // Run task with the current thread + } + } catch(InterruptedException e) { + // Acceptable way to exit + } + print("Finished DelayedTaskConsumer"); + } +} + +public class DelayQueueDemo { + public static void main(String[] args) { + Random rand = new Random(47); + ExecutorService exec = Executors.newCachedThreadPool(); + DelayQueue queue = + new DelayQueue(); + // Fill with tasks that have random delays: + for(int i = 0; i < 20; i++) { + queue.put(new DelayedTask(rand.nextInt(5000))); + } + // Set the stopping point + queue.add(new DelayedTask.EndSentinel(5000, exec)); + exec.execute(new DelayedTaskConsumer(queue)); + } +} /* Output: +[128 ] Task 11 [200 ] Task 7 [429 ] Task 5 [520 ] Task 18 [555 ] Task 1 [961 ] Task 4 [998 ] Task 16 [1207] Task 9 [1693] Task 2 [1809] Task 14 [1861] Task 3 [2278] Task 15 [3288] Task 10 [3551] Task 12 [4258] Task 0 [4258] Task 19 [4522] Task 8 [4589] Task 13 [4861] Task 17 [4868] Task 6 (0:4258) (1:555) (2:1693) (3:1861) (4:961) (5:429) (6:4868) (7:200) (8:4522) (9:1207) (10:3288) (11:128) (12:3551) (13:4589) (14:1809) (15:2278) (16:998) (17:4861) (18:520) (19:4258) (20:5000) +[5000] Task 20 Calling shutdownNow() +Finished DelayedTaskConsumer +*///:~ diff --git a/src/concurrency/EvenChecker.java b/src/concurrency/EvenChecker.java new file mode 100644 index 0000000..8d0faaa --- /dev/null +++ b/src/concurrency/EvenChecker.java @@ -0,0 +1,33 @@ +package concurrency;//: concurrency/EvenChecker.java +import java.util.concurrent.*; + +public class EvenChecker implements Runnable { + private IntGenerator generator; + private final int id; + public EvenChecker(IntGenerator g, int ident) { + generator = g; + id = ident; + } + public void run() { + while(!generator.isCanceled()) { + int val = generator.next(); + if(val % 2 != 0) { + System.out.println(val + " not even!"); + generator.cancel(); // Cancels all EvenCheckers + } + } + } + // Test any type of IntGenerator: + public static void test(IntGenerator gp, int count) { + System.out.println("Press Control-C to exit"); + ExecutorService exec = Executors.newCachedThreadPool(); + for(int i = 0; i < count; i++) { + exec.execute(new EvenChecker(gp, i)); + } + exec.shutdown(); + } + // Default value for count: + public static void test(IntGenerator gp) { + test(gp, 10); + } +} ///:~ diff --git a/src/concurrency/EvenGenerator.java b/src/concurrency/EvenGenerator.java new file mode 100644 index 0000000..bd844b4 --- /dev/null +++ b/src/concurrency/EvenGenerator.java @@ -0,0 +1,18 @@ +package concurrency;//: concurrency/EvenGenerator.java +// When threads collide. + +public class EvenGenerator extends IntGenerator { + private int currentEvenValue = 0; + public int next() { + ++currentEvenValue; // Danger point here! + ++currentEvenValue; + return currentEvenValue; + } + public static void main(String[] args) { + EvenChecker.test(new EvenGenerator()); + } +} /* Output: (Sample) +Press Control-C to exit +89476993 not even! +89476993 not even! +*///:~ diff --git a/src/concurrency/ExceptionThread.java b/src/concurrency/ExceptionThread.java new file mode 100644 index 0000000..781d05a --- /dev/null +++ b/src/concurrency/ExceptionThread.java @@ -0,0 +1,13 @@ +package concurrency;//: concurrency/ExceptionThread.java +// {ThrowsException} +import java.util.concurrent.*; + +public class ExceptionThread implements Runnable { + public void run() { + throw new RuntimeException(); + } + public static void main(String[] args) { + ExecutorService exec = Executors.newCachedThreadPool(); + exec.execute(new ExceptionThread()); + } +} ///:~ diff --git a/src/concurrency/ExchangerDemo.java b/src/concurrency/ExchangerDemo.java new file mode 100644 index 0000000..56359b3 --- /dev/null +++ b/src/concurrency/ExchangerDemo.java @@ -0,0 +1,79 @@ +package concurrency;//: concurrency/ExchangerDemo.java +import java.util.concurrent.*; +import java.util.*; +import net.mindview.util.*; + +class ExchangerProducer implements Runnable { + private Generator generator; + private Exchanger> exchanger; + private List holder; + ExchangerProducer(Exchanger> exchg, + Generator gen, List holder) { + exchanger = exchg; + generator = gen; + this.holder = holder; + } + public void run() { + try { + while(!Thread.interrupted()) { + for(int i = 0; i < ExchangerDemo.size; i++) { + holder.add(generator.next()); + } + // Exchange full for empty: + holder = exchanger.exchange(holder); + } + } catch(InterruptedException e) { + // OK to terminate this way. + } + } +} + +class ExchangerConsumer implements Runnable { + private Exchanger> exchanger; + private List holder; + private volatile T value; + ExchangerConsumer(Exchanger> ex, List holder){ + exchanger = ex; + this.holder = holder; + } + public void run() { + try { + while(!Thread.interrupted()) { + holder = exchanger.exchange(holder); + for(T x : holder) { + value = x; // Fetch out value + holder.remove(x); // OK for CopyOnWriteArrayList + } + } + } catch(InterruptedException e) { + // OK to terminate this way. + } + System.out.println("Final value: " + value); + } +} + +public class ExchangerDemo { + static int size = 10; + static int delay = 5; // Seconds + public static void main(String[] args) throws Exception { + if(args.length > 0) { + size = new Integer(args[0]); + } + if(args.length > 1) { + delay = new Integer(args[1]); + } + ExecutorService exec = Executors.newCachedThreadPool(); + Exchanger> xc = new Exchanger>(); + List + producerList = new CopyOnWriteArrayList(), + consumerList = new CopyOnWriteArrayList(); + exec.execute(new ExchangerProducer(xc, + BasicGenerator.create(Fat.class), producerList)); + exec.execute( + new ExchangerConsumer(xc,consumerList)); + TimeUnit.SECONDS.sleep(delay); + exec.shutdownNow(); + } +} /* Output: (Sample) +Final value: Fat id: 29999 +*///:~ diff --git a/src/concurrency/ExplicitCriticalSection.java b/src/concurrency/ExplicitCriticalSection.java new file mode 100644 index 0000000..c118d1e --- /dev/null +++ b/src/concurrency/ExplicitCriticalSection.java @@ -0,0 +1,48 @@ +//: concurrency/ExplicitCriticalSection.java +// Using explicit Lock objects to create critical sections. +package concurrency; +import java.util.concurrent.locks.*; + +// Synchronize the entire method: +class ExplicitPairManager1 extends PairManager { + private Lock lock = new ReentrantLock(); + public synchronized void increment() { + lock.lock(); + try { + p.incrementX(); + p.incrementY(); + store(getPair()); + } finally { + lock.unlock(); + } + } +} + +// Use a critical section: +class ExplicitPairManager2 extends PairManager { + private Lock lock = new ReentrantLock(); + public void increment() { + Pair temp; + lock.lock(); + try { + p.incrementX(); + p.incrementY(); + temp = getPair(); + } finally { + lock.unlock(); + } + store(temp); + } +} + +public class ExplicitCriticalSection { + public static void main(String[] args) throws Exception { + PairManager + pman1 = new ExplicitPairManager1(), + pman2 = new ExplicitPairManager2(); + CriticalSection.testApproaches(pman1, pman2); + } +} /* Output: (Sample) +pm1: Pair: x: 15, y: 15 checkCounter = 174035 +pm2: Pair: x: 16, y: 16 checkCounter = 2608588 +*///:~ diff --git a/src/concurrency/FastSimulation.java b/src/concurrency/FastSimulation.java new file mode 100644 index 0000000..9bbd035 --- /dev/null +++ b/src/concurrency/FastSimulation.java @@ -0,0 +1,57 @@ +package concurrency;//: concurrency/FastSimulation.java +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class FastSimulation { + static final int N_ELEMENTS = 100000; + static final int N_GENES = 30; + static final int N_EVOLVERS = 50; + static final AtomicInteger[][] GRID = + new AtomicInteger[N_ELEMENTS][N_GENES]; + static Random rand = new Random(47); + static class Evolver implements Runnable { + public void run() { + while(!Thread.interrupted()) { + // Randomly select an element to work on: + int element = rand.nextInt(N_ELEMENTS); + for(int i = 0; i < N_GENES; i++) { + int previous = element - 1; + if(previous < 0) { + previous = N_ELEMENTS - 1; + } + int next = element + 1; + if(next >= N_ELEMENTS) { + next = 0; + } + int oldvalue = GRID[element][i].get(); + // Perform some kind of modeling calculation: + int newvalue = oldvalue + + GRID[previous][i].get() + GRID[next][i].get(); + newvalue /= 3; // Average the three values + if(!GRID[element][i] + .compareAndSet(oldvalue, newvalue)) { + // Policy here to deal with failure. Here, we + // just report it and ignore it; our model + // will eventually deal with it. + print("Old value changed from " + oldvalue); + } + } + } + } + } + public static void main(String[] args) throws Exception { + ExecutorService exec = Executors.newCachedThreadPool(); + for(int i = 0; i < N_ELEMENTS; i++) { + for(int j = 0; j < N_GENES; j++) { + GRID[i][j] = new AtomicInteger(rand.nextInt(1000)); + } + } + for(int i = 0; i < N_EVOLVERS; i++) { + exec.execute(new Evolver()); + } + TimeUnit.SECONDS.sleep(5); + exec.shutdownNow(); + } +} /* (Execute to see output) *///:~ diff --git a/src/concurrency/Fat.java b/src/concurrency/Fat.java new file mode 100644 index 0000000..b2bdb92 --- /dev/null +++ b/src/concurrency/Fat.java @@ -0,0 +1,16 @@ +package concurrency;//: concurrency/Fat.java +// Objects that are expensive to create. + +public class Fat { + private volatile double d; // Prevent optimization + private static int counter = 0; + private final int id = counter++; + public Fat() { + // Expensive, interruptible operation: + for(int i = 1; i < 10000; i++) { + d += (Math.PI + Math.E) / (double)i; + } + } + public void operation() { System.out.println(this); } + public String toString() { return "Fat id: " + id; } +} ///:~ diff --git a/src/concurrency/FixedDiningPhilosophers.java b/src/concurrency/FixedDiningPhilosophers.java new file mode 100644 index 0000000..488fa09 --- /dev/null +++ b/src/concurrency/FixedDiningPhilosophers.java @@ -0,0 +1,38 @@ +package concurrency;//: concurrency/FixedDiningPhilosophers.java +// Dining philosophers without deadlock. +// {Args: 5 5 timeout} +import java.util.concurrent.*; + +public class FixedDiningPhilosophers { + public static void main(String[] args) throws Exception { + int ponder = 5; + if(args.length > 0) { + ponder = Integer.parseInt(args[0]); + } + int size = 5; + if(args.length > 1) { + size = Integer.parseInt(args[1]); + } + ExecutorService exec = Executors.newCachedThreadPool(); + Chopstick[] sticks = new Chopstick[size]; + for(int i = 0; i < size; i++) { + sticks[i] = new Chopstick(); + } + for(int i = 0; i < size; i++) { + if(i < (size-1)) { + exec.execute(new Philosopher( + sticks[i], sticks[i + 1], i, ponder)); + } else { + exec.execute(new Philosopher( + sticks[0], sticks[i], i, ponder)); + } + } + if(args.length == 3 && args[2].equals("timeout")) { + TimeUnit.SECONDS.sleep(5); + } else { + System.out.println("Press 'Enter' to quit"); + System.in.read(); + } + exec.shutdownNow(); + } +} /* (Execute to see output) *///:~ diff --git a/src/concurrency/FixedThreadPool.java b/src/concurrency/FixedThreadPool.java new file mode 100644 index 0000000..1282d6d --- /dev/null +++ b/src/concurrency/FixedThreadPool.java @@ -0,0 +1,15 @@ +package concurrency;//: concurrency/FixedThreadPool.java +import java.util.concurrent.*; + +public class FixedThreadPool { + public static void main(String[] args) { + // Constructor argument is number of threads: + ExecutorService exec = Executors.newFixedThreadPool(5); + for(int i = 0; i < 5; i++) { + exec.execute(new LiftOff()); + } + exec.shutdown(); + } +} /* Output: (Sample) +#0(9), #0(8), #1(9), #2(9), #3(9), #4(9), #0(7), #1(8), #2(8), #3(8), #4(8), #0(6), #1(7), #2(7), #3(7), #4(7), #0(5), #1(6), #2(6), #3(6), #4(6), #0(4), #1(5), #2(5), #3(5), #4(5), #0(3), #1(4), #2(4), #3(4), #4(4), #0(2), #1(3), #2(3), #3(3), #4(3), #0(1), #1(2), #2(2), #3(2), #4(2), #0(Liftoff!), #1(1), #2(1), #3(1), #4(1), #1(Liftoff!), #2(Liftoff!), #3(Liftoff!), #4(Liftoff!), +*///:~ diff --git a/src/concurrency/GreenhouseScheduler.java b/src/concurrency/GreenhouseScheduler.java new file mode 100644 index 0000000..fec62bf --- /dev/null +++ b/src/concurrency/GreenhouseScheduler.java @@ -0,0 +1,159 @@ +package concurrency;//: concurrency/GreenhouseScheduler.java +// Rewriting innerclasses/GreenhouseController.java +// to use a ScheduledThreadPoolExecutor. +// {Args: 5000} +import java.util.concurrent.*; +import java.util.*; + +public class GreenhouseScheduler { + private volatile boolean light = false; + private volatile boolean water = false; + private String thermostat = "Day"; + public synchronized String getThermostat() { + return thermostat; + } + public synchronized void setThermostat(String value) { + thermostat = value; + } + ScheduledThreadPoolExecutor scheduler = + new ScheduledThreadPoolExecutor(10); + public void schedule(Runnable event, long delay) { + scheduler.schedule(event,delay,TimeUnit.MILLISECONDS); + } + public void + repeat(Runnable event, long initialDelay, long period) { + scheduler.scheduleAtFixedRate( + event, initialDelay, period, TimeUnit.MILLISECONDS); + } + class LightOn implements Runnable { + public void run() { + // Put hardware control code here to + // physically turn on the light. + System.out.println("Turning on lights"); + light = true; + } + } + class LightOff implements Runnable { + public void run() { + // Put hardware control code here to + // physically turn off the light. + System.out.println("Turning off lights"); + light = false; + } + } + class WaterOn implements Runnable { + public void run() { + // Put hardware control code here. + System.out.println("Turning greenhouse water on"); + water = true; + } + } + class WaterOff implements Runnable { + public void run() { + // Put hardware control code here. + System.out.println("Turning greenhouse water off"); + water = false; + } + } + class ThermostatNight implements Runnable { + public void run() { + // Put hardware control code here. + System.out.println("Thermostat to night setting"); + setThermostat("Night"); + } + } + class ThermostatDay implements Runnable { + public void run() { + // Put hardware control code here. + System.out.println("Thermostat to day setting"); + setThermostat("Day"); + } + } + class Bell implements Runnable { + public void run() { System.out.println("Bing!"); } + } + class Terminate implements Runnable { + public void run() { + System.out.println("Terminating"); + scheduler.shutdownNow(); + // Must start a separate task to do this job, + // since the scheduler has been shut down: + new Thread() { + public void run() { + for(DataPoint d : data) { + System.out.println(d); + } + } + }.start(); + } + } + // New feature: data collection + static class DataPoint { + final Calendar time; + final float temperature; + final float humidity; + public DataPoint(Calendar d, float temp, float hum) { + time = d; + temperature = temp; + humidity = hum; + } + public String toString() { + return time.getTime() + + String.format( + " temperature: %1$.1f humidity: %2$.2f", + temperature, humidity); + } + } + private Calendar lastTime = Calendar.getInstance(); + { // Adjust date to the half hour + lastTime.set(Calendar.MINUTE, 30); + lastTime.set(Calendar.SECOND, 00); + } + private float lastTemp = 65.0f; + private int tempDirection = +1; + private float lastHumidity = 50.0f; + private int humidityDirection = +1; + private Random rand = new Random(47); + List data = Collections.synchronizedList( + new ArrayList()); + class CollectData implements Runnable { + public void run() { + System.out.println("Collecting data"); + synchronized(GreenhouseScheduler.this) { + // Pretend the interval is longer than it is: + lastTime.set(Calendar.MINUTE, + lastTime.get(Calendar.MINUTE) + 30); + // One in 5 chances of reversing the direction: + if(rand.nextInt(5) == 4) { + tempDirection = -tempDirection; + } + // Store previous value: + lastTemp = lastTemp + + tempDirection * (1.0f + rand.nextFloat()); + if(rand.nextInt(5) == 4) { + humidityDirection = -humidityDirection; + } + lastHumidity = lastHumidity + + humidityDirection * rand.nextFloat(); + // Calendar must be cloned, otherwise all + // DataPoints hold references to the same lastTime. + // For a basic object like Calendar, clone() is OK. + data.add(new DataPoint((Calendar)lastTime.clone(), + lastTemp, lastHumidity)); + } + } + } + public static void main(String[] args) { + GreenhouseScheduler gh = new GreenhouseScheduler(); + gh.schedule(gh.new Terminate(), 5000); + // Former "Restart" class not necessary: + gh.repeat(gh.new Bell(), 0, 1000); + gh.repeat(gh.new ThermostatNight(), 0, 2000); + gh.repeat(gh.new LightOn(), 0, 200); + gh.repeat(gh.new LightOff(), 0, 400); + gh.repeat(gh.new WaterOn(), 0, 600); + gh.repeat(gh.new WaterOff(), 0, 800); + gh.repeat(gh.new ThermostatDay(), 0, 1400); + gh.repeat(gh.new CollectData(), 500, 500); + } +} /* (Execute to see output) *///:~ diff --git a/src/concurrency/HorseRace.java b/src/concurrency/HorseRace.java new file mode 100644 index 0000000..b937a8e --- /dev/null +++ b/src/concurrency/HorseRace.java @@ -0,0 +1,91 @@ +package concurrency;//: concurrency/HorseRace.java +// Using CyclicBarriers. +import java.util.concurrent.*; +import java.util.*; +import static net.mindview.util.Print.*; + +class Horse implements Runnable { + private static int counter = 0; + private final int id = counter++; + private int strides = 0; + private static Random rand = new Random(47); + private static CyclicBarrier barrier; + public Horse(CyclicBarrier b) { barrier = b; } + public synchronized int getStrides() { return strides; } + public void run() { + try { + while(!Thread.interrupted()) { + synchronized(this) { + strides += rand.nextInt(3); // Produces 0, 1 or 2 + } + barrier.await(); + } + } catch(InterruptedException e) { + // A legitimate way to exit + } catch(BrokenBarrierException e) { + // This one we want to know about + throw new RuntimeException(e); + } + } + public String toString() { return "Horse " + id + " "; } + public String tracks() { + StringBuilder s = new StringBuilder(); + for(int i = 0; i < getStrides(); i++) { + s.append("*"); + } + s.append(id); + return s.toString(); + } +} + +public class HorseRace { + static final int FINISH_LINE = 75; + private List horses = new ArrayList(); + private ExecutorService exec = + Executors.newCachedThreadPool(); + private CyclicBarrier barrier; + public HorseRace(int nHorses, final int pause) { + barrier = new CyclicBarrier(nHorses, new Runnable() { + public void run() { + StringBuilder s = new StringBuilder(); + for(int i = 0; i < FINISH_LINE; i++) { + s.append("="); // The fence on the racetrack + } + print(s); + for(Horse horse : horses) { + print(horse.tracks()); + } + for(Horse horse : horses) { + if(horse.getStrides() >= FINISH_LINE) { + print(horse + "won!"); + exec.shutdownNow(); + return; + } + } + try { + TimeUnit.MILLISECONDS.sleep(pause); + } catch(InterruptedException e) { + print("barrier-action sleep interrupted"); + } + } + }); + for(int i = 0; i < nHorses; i++) { + Horse horse = new Horse(barrier); + horses.add(horse); + exec.execute(horse); + } + } + public static void main(String[] args) { + int nHorses = 7; + int pause = 200; + if(args.length > 0) { // Optional argument + int n = new Integer(args[0]); + nHorses = n > 0 ? n : nHorses; + } + if(args.length > 1) { // Optional argument + int p = new Integer(args[1]); + pause = p > -1 ? p : pause; + } + new HorseRace(nHorses, pause); + } +} /* (Execute to see output) *///:~ diff --git a/src/concurrency/IntGenerator.java b/src/concurrency/IntGenerator.java new file mode 100644 index 0000000..c73b577 --- /dev/null +++ b/src/concurrency/IntGenerator.java @@ -0,0 +1,9 @@ +package concurrency;//: concurrency/IntGenerator.java + +public abstract class IntGenerator { + private volatile boolean canceled = false; + public abstract int next(); + // Allow this to be canceled: + public void cancel() { canceled = true; } + public boolean isCanceled() { return canceled; } +} ///:~ diff --git a/src/concurrency/Interrupting.java b/src/concurrency/Interrupting.java new file mode 100644 index 0000000..6743fed --- /dev/null +++ b/src/concurrency/Interrupting.java @@ -0,0 +1,87 @@ +package concurrency;//: concurrency/Interrupting.java +// Interrupting a blocked thread. +import java.util.concurrent.*; +import java.io.*; +import static net.mindview.util.Print.*; + +class SleepBlocked implements Runnable { + public void run() { + try { + TimeUnit.SECONDS.sleep(100); + } catch(InterruptedException e) { + print("InterruptedException"); + } + print("Exiting SleepBlocked.run()"); + } +} + +class IOBlocked implements Runnable { + private InputStream in; + public IOBlocked(InputStream is) { in = is; } + public void run() { + try { + print("Waiting for read():"); + in.read(); + } catch(IOException e) { + if(Thread.currentThread().isInterrupted()) { + print("Interrupted from blocked I/O"); + } else { + throw new RuntimeException(e); + } + } + print("Exiting IOBlocked.run()"); + } +} + +class SynchronizedBlocked implements Runnable { + public synchronized void f() { + while(true) // Never releases lock + { + Thread.yield(); + } + } + public SynchronizedBlocked() { + new Thread() { + public void run() { + f(); // Lock acquired by this thread + } + }.start(); + } + public void run() { + print("Trying to call f()"); + f(); + print("Exiting SynchronizedBlocked.run()"); + } +} + +public class Interrupting { + private static ExecutorService exec = + Executors.newCachedThreadPool(); + static void test(Runnable r) throws InterruptedException{ + Future f = exec.submit(r); + TimeUnit.MILLISECONDS.sleep(100); + print("Interrupting " + r.getClass().getName()); + f.cancel(true); // Interrupts if running + print("Interrupt sent to " + r.getClass().getName()); + } + public static void main(String[] args) throws Exception { + test(new SleepBlocked()); + test(new IOBlocked(System.in)); + test(new SynchronizedBlocked()); + TimeUnit.SECONDS.sleep(3); + print("Aborting with System.exit(0)"); + System.exit(0); // ... since last 2 interrupts failed + } +} /* Output: (95% match) +Interrupting SleepBlocked +InterruptedException +Exiting SleepBlocked.run() +Interrupt sent to SleepBlocked +Waiting for read(): +Interrupting IOBlocked +Interrupt sent to IOBlocked +Trying to call f() +Interrupting SynchronizedBlocked +Interrupt sent to SynchronizedBlocked +Aborting with System.exit(0) +*///:~ diff --git a/src/concurrency/Interrupting2.java b/src/concurrency/Interrupting2.java new file mode 100644 index 0000000..584e6fb --- /dev/null +++ b/src/concurrency/Interrupting2.java @@ -0,0 +1,47 @@ +package concurrency;//: concurrency/Interrupting2.java +// Interrupting a task blocked with a ReentrantLock. +import java.util.concurrent.*; +import java.util.concurrent.locks.*; +import static net.mindview.util.Print.*; + +class BlockedMutex { + private Lock lock = new ReentrantLock(); + public BlockedMutex() { + // Acquire it right away, to demonstrate interruption + // of a task blocked on a ReentrantLock: + lock.lock(); + } + public void f() { + try { + // This will never be available to a second task + lock.lockInterruptibly(); // Special call + print("lock acquired in f()"); + } catch(InterruptedException e) { + print("Interrupted from lock acquisition in f()"); + } + } +} + +class Blocked2 implements Runnable { + BlockedMutex blocked = new BlockedMutex(); + public void run() { + print("Waiting for f() in BlockedMutex"); + blocked.f(); + print("Broken out of blocked call"); + } +} + +public class Interrupting2 { + public static void main(String[] args) throws Exception { + Thread t = new Thread(new Blocked2()); + t.start(); + TimeUnit.SECONDS.sleep(1); + System.out.println("Issuing t.interrupt()"); + t.interrupt(); + } +} /* Output: +Waiting for f() in BlockedMutex +Issuing t.interrupt() +Interrupted from lock acquisition in f() +Broken out of blocked call +*///:~ diff --git a/src/concurrency/InterruptingIdiom.java b/src/concurrency/InterruptingIdiom.java new file mode 100644 index 0000000..cf85bc0 --- /dev/null +++ b/src/concurrency/InterruptingIdiom.java @@ -0,0 +1,77 @@ +package concurrency;//: concurrency/InterruptingIdiom.java +// General idiom for interrupting a task. +// {Args: 1100} +import java.util.concurrent.*; +import static net.mindview.util.Print.*; + +class NeedsCleanup { + private final int id; + public NeedsCleanup(int ident) { + id = ident; + print("NeedsCleanup " + id); + } + public void cleanup() { + print("Cleaning up " + id); + } +} + +class Blocked3 implements Runnable { + private volatile double d = 0.0; + public void run() { + try { + while(!Thread.interrupted()) { + // point1 + NeedsCleanup n1 = new NeedsCleanup(1); + // Start try-finally immediately after definition + // of n1, to guarantee proper cleanup of n1: + try { + print("Sleeping"); + TimeUnit.SECONDS.sleep(1); + // point2 + NeedsCleanup n2 = new NeedsCleanup(2); + // Guarantee proper cleanup of n2: + try { + print("Calculating"); + // A time-consuming, non-blocking operation: + for(int i = 1; i < 2500000; i++) { + d = d + (Math.PI + Math.E) / d; + } + print("Finished time-consuming operation"); + } finally { + n2.cleanup(); + } + } finally { + n1.cleanup(); + } + } + print("Exiting via while() test"); + } catch(InterruptedException e) { + print("Exiting via InterruptedException"); + } + } +} + +public class InterruptingIdiom { + public static void main(String[] args) throws Exception { + if(args.length != 1) { + print("usage: java InterruptingIdiom delay-in-mS"); + System.exit(1); + } + Thread t = new Thread(new Blocked3()); + t.start(); + TimeUnit.MILLISECONDS.sleep(new Integer(args[0])); + t.interrupt(); + } +} /* Output: (Sample) +NeedsCleanup 1 +Sleeping +NeedsCleanup 2 +Calculating +Finished time-consuming operation +Cleaning up 2 +Cleaning up 1 +NeedsCleanup 1 +Sleeping +Cleaning up 1 +Exiting via InterruptedException +*///:~ diff --git a/src/concurrency/Joining.java b/src/concurrency/Joining.java new file mode 100644 index 0000000..541881d --- /dev/null +++ b/src/concurrency/Joining.java @@ -0,0 +1,56 @@ +package concurrency;//: concurrency/Joining.java +// Understanding join(). +import static net.mindview.util.Print.*; + +class Sleeper extends Thread { + private int duration; + public Sleeper(String name, int sleepTime) { + super(name); + duration = sleepTime; + start(); + } + public void run() { + try { + sleep(duration); + } catch(InterruptedException e) { + print(getName() + " was interrupted. " + + "isInterrupted(): " + isInterrupted()); + return; + } + print(getName() + " has awakened"); + } +} + +class Joiner extends Thread { + private Sleeper sleeper; + public Joiner(String name, Sleeper sleeper) { + super(name); + this.sleeper = sleeper; + start(); + } + public void run() { + try { + sleeper.join(); + } catch(InterruptedException e) { + print("Interrupted"); + } + print(getName() + " join completed"); + } +} + +public class Joining { + public static void main(String[] args) { + Sleeper + sleepy = new Sleeper("Sleepy", 1500), + grumpy = new Sleeper("Grumpy", 1500); + Joiner + dopey = new Joiner("Dopey", sleepy), + doc = new Joiner("Doc", grumpy); + grumpy.interrupt(); + } +} /* Output: +Grumpy was interrupted. isInterrupted(): false +Doc join completed +Sleepy has awakened +Dopey join completed +*///:~ diff --git a/src/concurrency/LiftOff.java b/src/concurrency/LiftOff.java new file mode 100644 index 0000000..cf69b5b --- /dev/null +++ b/src/concurrency/LiftOff.java @@ -0,0 +1,22 @@ +package concurrency;//: concurrency/LiftOff.java +// Demonstration of the Runnable interface. + +public class LiftOff implements Runnable { + protected int countDown = 10; // Default + private static int taskCount = 0; + private final int id = taskCount++; + public LiftOff() {} + public LiftOff(int countDown) { + this.countDown = countDown; + } + public String status() { + return "#" + id + "(" + + (countDown > 0 ? countDown : "Liftoff!") + "), "; + } + public void run() { + while(countDown-- > 0) { + System.out.print(status()); + Thread.yield(); + } + } +} ///:~ diff --git a/src/concurrency/ListComparisons.java b/src/concurrency/ListComparisons.java new file mode 100644 index 0000000..e43be93 --- /dev/null +++ b/src/concurrency/ListComparisons.java @@ -0,0 +1,92 @@ +package concurrency;//: concurrency/ListComparisons.java +// {Args: 1 10 10} (Fast verification check during build) +// Rough comparison of thread-safe List performance. +import java.util.concurrent.*; +import java.util.*; +import net.mindview.util.*; + +abstract class ListTest extends Tester> { + ListTest(String testId, int nReaders, int nWriters) { + super(testId, nReaders, nWriters); + } + class Reader extends TestTask { + long result = 0; + void test() { + for(long i = 0; i < testCycles; i++) { + for(int index = 0; index < containerSize; index++) { + result += testContainer.get(index); + } + } + } + void putResults() { + readResult += result; + readTime += duration; + } + } + class Writer extends TestTask { + void test() { + for(long i = 0; i < testCycles; i++) { + for(int index = 0; index < containerSize; index++) { + testContainer.set(index, writeData[index]); + } + } + } + void putResults() { + writeTime += duration; + } + } + void startReadersAndWriters() { + for(int i = 0; i < nReaders; i++) { + exec.execute(new Reader()); + } + for(int i = 0; i < nWriters; i++) { + exec.execute(new Writer()); + } + } +} + +class SynchronizedArrayListTest extends ListTest { + List containerInitializer() { + return Collections.synchronizedList( + new ArrayList( + new CountingIntegerList(containerSize))); + } + SynchronizedArrayListTest(int nReaders, int nWriters) { + super("Synched ArrayList", nReaders, nWriters); + } +} + +class CopyOnWriteArrayListTest extends ListTest { + List containerInitializer() { + return new CopyOnWriteArrayList( + new CountingIntegerList(containerSize)); + } + CopyOnWriteArrayListTest(int nReaders, int nWriters) { + super("CopyOnWriteArrayList", nReaders, nWriters); + } +} + +public class ListComparisons { + public static void main(String[] args) { + Tester.initMain(args); + new SynchronizedArrayListTest(10, 0); + new SynchronizedArrayListTest(9, 1); + new SynchronizedArrayListTest(5, 5); + new CopyOnWriteArrayListTest(10, 0); + new CopyOnWriteArrayListTest(9, 1); + new CopyOnWriteArrayListTest(5, 5); + Tester.exec.shutdown(); + } +} /* Output: (Sample) +Type Read time Write time +Synched ArrayList 10r 0w 232158294700 0 +Synched ArrayList 9r 1w 198947618203 24918613399 +readTime + writeTime = 223866231602 +Synched ArrayList 5r 5w 117367305062 132176613508 +readTime + writeTime = 249543918570 +CopyOnWriteArrayList 10r 0w 758386889 0 +CopyOnWriteArrayList 9r 1w 741305671 136145237 +readTime + writeTime = 877450908 +CopyOnWriteArrayList 5r 5w 212763075 67967464300 +readTime + writeTime = 68180227375 +*///:~ diff --git a/src/concurrency/MainThread.java b/src/concurrency/MainThread.java new file mode 100644 index 0000000..4f808b5 --- /dev/null +++ b/src/concurrency/MainThread.java @@ -0,0 +1,10 @@ +package concurrency;//: concurrency/MainThread.java + +public class MainThread { + public static void main(String[] args) { + LiftOff launch = new LiftOff(); + launch.run(); + } +} /* Output: +#0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(Liftoff!), +*///:~ diff --git a/src/concurrency/MapComparisons.java b/src/concurrency/MapComparisons.java new file mode 100644 index 0000000..068ca34 --- /dev/null +++ b/src/concurrency/MapComparisons.java @@ -0,0 +1,98 @@ +package concurrency;//: concurrency/MapComparisons.java +// {Args: 1 10 10} (Fast verification check during build) +// Rough comparison of thread-safe Map performance. +import java.util.concurrent.*; +import java.util.*; +import net.mindview.util.*; + +abstract class MapTest +extends Tester> { + MapTest(String testId, int nReaders, int nWriters) { + super(testId, nReaders, nWriters); + } + class Reader extends TestTask { + long result = 0; + void test() { + for(long i = 0; i < testCycles; i++) { + for(int index = 0; index < containerSize; index++) { + result += testContainer.get(index); + } + } + } + void putResults() { + readResult += result; + readTime += duration; + } + } + class Writer extends TestTask { + void test() { + for(long i = 0; i < testCycles; i++) { + for(int index = 0; index < containerSize; index++) { + testContainer.put(index, writeData[index]); + } + } + } + void putResults() { + writeTime += duration; + } + } + void startReadersAndWriters() { + for(int i = 0; i < nReaders; i++) { + exec.execute(new Reader()); + } + for(int i = 0; i < nWriters; i++) { + exec.execute(new Writer()); + } + } +} + +class SynchronizedHashMapTest extends MapTest { + Map containerInitializer() { + return Collections.synchronizedMap( + new HashMap( + MapData.map( + new CountingGenerator.Integer(), + new CountingGenerator.Integer(), + containerSize))); + } + SynchronizedHashMapTest(int nReaders, int nWriters) { + super("Synched HashMap", nReaders, nWriters); + } +} + +class ConcurrentHashMapTest extends MapTest { + Map containerInitializer() { + return new ConcurrentHashMap( + MapData.map( + new CountingGenerator.Integer(), + new CountingGenerator.Integer(), containerSize)); + } + ConcurrentHashMapTest(int nReaders, int nWriters) { + super("ConcurrentHashMap", nReaders, nWriters); + } +} + +public class MapComparisons { + public static void main(String[] args) { + Tester.initMain(args); + new SynchronizedHashMapTest(10, 0); + new SynchronizedHashMapTest(9, 1); + new SynchronizedHashMapTest(5, 5); + new ConcurrentHashMapTest(10, 0); + new ConcurrentHashMapTest(9, 1); + new ConcurrentHashMapTest(5, 5); + Tester.exec.shutdown(); + } +} /* Output: (Sample) +Type Read time Write time +Synched HashMap 10r 0w 306052025049 0 +Synched HashMap 9r 1w 428319156207 47697347568 +readTime + writeTime = 476016503775 +Synched HashMap 5r 5w 243956877760 244012003202 +readTime + writeTime = 487968880962 +ConcurrentHashMap 10r 0w 23352654318 0 +ConcurrentHashMap 9r 1w 18833089400 1541853224 +readTime + writeTime = 20374942624 +ConcurrentHashMap 5r 5w 12037625732 11850489099 +readTime + writeTime = 23888114831 +*///:~ diff --git a/src/concurrency/MoreBasicThreads.java b/src/concurrency/MoreBasicThreads.java new file mode 100644 index 0000000..33ea9d1 --- /dev/null +++ b/src/concurrency/MoreBasicThreads.java @@ -0,0 +1,14 @@ +package concurrency;//: concurrency/MoreBasicThreads.java +// Adding more threads. + +public class MoreBasicThreads { + public static void main(String[] args) { + for(int i = 0; i < 5; i++) { + new Thread(new LiftOff()).start(); + } + System.out.println("Waiting for LiftOff"); + } +} /* Output: (Sample) +Waiting for LiftOff +#0(9), #1(9), #2(9), #3(9), #4(9), #0(8), #1(8), #2(8), #3(8), #4(8), #0(7), #1(7), #2(7), #3(7), #4(7), #0(6), #1(6), #2(6), #3(6), #4(6), #0(5), #1(5), #2(5), #3(5), #4(5), #0(4), #1(4), #2(4), #3(4), #4(4), #0(3), #1(3), #2(3), #3(3), #4(3), #0(2), #1(2), #2(2), #3(2), #4(2), #0(1), #1(1), #2(1), #3(1), #4(1), #0(Liftoff!), #1(Liftoff!), #2(Liftoff!), #3(Liftoff!), #4(Liftoff!), +*///:~ diff --git a/src/concurrency/MultiLock.java b/src/concurrency/MultiLock.java new file mode 100644 index 0000000..2de38db --- /dev/null +++ b/src/concurrency/MultiLock.java @@ -0,0 +1,37 @@ +package concurrency;//: concurrency/MultiLock.java +// One thread can reacquire the same lock. +import static net.mindview.util.Print.*; + +public class MultiLock { + public synchronized void f1(int count) { + if(count-- > 0) { + print("f1() calling f2() with count " + count); + f2(count); + } + } + public synchronized void f2(int count) { + if(count-- > 0) { + print("f2() calling f1() with count " + count); + f1(count); + } + } + public static void main(String[] args) throws Exception { + final MultiLock multiLock = new MultiLock(); + new Thread() { + public void run() { + multiLock.f1(10); + } + }.start(); + } +} /* Output: +f1() calling f2() with count 9 +f2() calling f1() with count 8 +f1() calling f2() with count 7 +f2() calling f1() with count 6 +f1() calling f2() with count 5 +f2() calling f1() with count 4 +f1() calling f2() with count 3 +f2() calling f1() with count 2 +f1() calling f2() with count 1 +f2() calling f1() with count 0 +*///:~ diff --git a/src/concurrency/MutexEvenGenerator.java b/src/concurrency/MutexEvenGenerator.java new file mode 100644 index 0000000..02b8738 --- /dev/null +++ b/src/concurrency/MutexEvenGenerator.java @@ -0,0 +1,23 @@ +package concurrency;//: concurrency/MutexEvenGenerator.java +// Preventing thread collisions with mutexes. +// {RunByHand} +import java.util.concurrent.locks.*; + +public class MutexEvenGenerator extends IntGenerator { + private int currentEvenValue = 0; + private Lock lock = new ReentrantLock(); + public int next() { + lock.lock(); + try { + ++currentEvenValue; + Thread.yield(); // Cause failure faster + ++currentEvenValue; + return currentEvenValue; + } finally { + lock.unlock(); + } + } + public static void main(String[] args) { + EvenChecker.test(new MutexEvenGenerator()); + } +} ///:~ diff --git a/src/concurrency/NIOInterruption.java b/src/concurrency/NIOInterruption.java new file mode 100644 index 0000000..33e436f --- /dev/null +++ b/src/concurrency/NIOInterruption.java @@ -0,0 +1,53 @@ +package concurrency;//: concurrency/NIOInterruption.java +// Interrupting a blocked NIO channel. +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.concurrent.*; +import java.io.*; +import static net.mindview.util.Print.*; + +class NIOBlocked implements Runnable { + private final SocketChannel sc; + public NIOBlocked(SocketChannel sc) { this.sc = sc; } + public void run() { + try { + print("Waiting for read() in " + this); + sc.read(ByteBuffer.allocate(1)); + } catch(ClosedByInterruptException e) { + print("ClosedByInterruptException"); + } catch(AsynchronousCloseException e) { + print("AsynchronousCloseException"); + } catch(IOException e) { + throw new RuntimeException(e); + } + print("Exiting NIOBlocked.run() " + this); + } +} + +public class NIOInterruption { + public static void main(String[] args) throws Exception { + ExecutorService exec = Executors.newCachedThreadPool(); + ServerSocket server = new ServerSocket(8080); + InetSocketAddress isa = + new InetSocketAddress("localhost", 8080); + SocketChannel sc1 = SocketChannel.open(isa); + SocketChannel sc2 = SocketChannel.open(isa); + Future f = exec.submit(new NIOBlocked(sc1)); + exec.execute(new NIOBlocked(sc2)); + exec.shutdown(); + TimeUnit.SECONDS.sleep(1); + // Produce an interrupt via cancel: + f.cancel(true); + TimeUnit.SECONDS.sleep(1); + // Release the block by closing the channel: + sc2.close(); + } +} /* Output: (Sample) +Waiting for read() in NIOBlocked@7a84e4 +Waiting for read() in NIOBlocked@15c7850 +ClosedByInterruptException +Exiting NIOBlocked.run() NIOBlocked@15c7850 +AsynchronousCloseException +Exiting NIOBlocked.run() NIOBlocked@7a84e4 +*///:~ diff --git a/src/concurrency/NaiveExceptionHandling.java b/src/concurrency/NaiveExceptionHandling.java new file mode 100644 index 0000000..2b6ca4d --- /dev/null +++ b/src/concurrency/NaiveExceptionHandling.java @@ -0,0 +1,16 @@ +package concurrency;//: concurrency/NaiveExceptionHandling.java +// {ThrowsException} +import java.util.concurrent.*; + +public class NaiveExceptionHandling { + public static void main(String[] args) { + try { + ExecutorService exec = + Executors.newCachedThreadPool(); + exec.execute(new ExceptionThread()); + } catch(RuntimeException ue) { + // This statement will NOT execute! + System.out.println("Exception has been handled!"); + } + } +} ///:~ diff --git a/src/concurrency/NotifyVsNotifyAll.java b/src/concurrency/NotifyVsNotifyAll.java new file mode 100644 index 0000000..7552edf --- /dev/null +++ b/src/concurrency/NotifyVsNotifyAll.java @@ -0,0 +1,79 @@ +package concurrency;//: concurrency/NotifyVsNotifyAll.java +import java.util.concurrent.*; +import java.util.*; + +class Blocker { + synchronized void waitingCall() { + try { + while(!Thread.interrupted()) { + wait(); + System.out.print(Thread.currentThread() + " "); + } + } catch(InterruptedException e) { + // OK to exit this way + } + } + synchronized void prod() { notify(); } + synchronized void prodAll() { notifyAll(); } +} + +class Task implements Runnable { + static Blocker blocker = new Blocker(); + public void run() { blocker.waitingCall(); } +} + +class Task2 implements Runnable { + // A separate Blocker object: + static Blocker blocker = new Blocker(); + public void run() { blocker.waitingCall(); } +} + +public class NotifyVsNotifyAll { + public static void main(String[] args) throws Exception { + ExecutorService exec = Executors.newCachedThreadPool(); + for(int i = 0; i < 5; i++) { + exec.execute(new Task()); + } + exec.execute(new Task2()); + Timer timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + boolean prod = true; + public void run() { + if(prod) { + System.out.print("\nnotify() "); + Task.blocker.prod(); + prod = false; + } else { + System.out.print("\nnotifyAll() "); + Task.blocker.prodAll(); + prod = true; + } + } + }, 400, 400); // Run every .4 second + TimeUnit.SECONDS.sleep(5); // Run for a while... + timer.cancel(); + System.out.println("\nTimer canceled"); + TimeUnit.MILLISECONDS.sleep(500); + System.out.print("Task2.blocker.prodAll() "); + Task2.blocker.prodAll(); + TimeUnit.MILLISECONDS.sleep(500); + System.out.println("\nShutting down"); + exec.shutdownNow(); // Interrupt all tasks + } +} /* Output: (Sample) +notify() Thread[pool-1-thread-1,5,main] +notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-5,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-2,5,main] +notify() Thread[pool-1-thread-1,5,main] +notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-2,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-5,5,main] +notify() Thread[pool-1-thread-1,5,main] +notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-5,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-2,5,main] +notify() Thread[pool-1-thread-1,5,main] +notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-2,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-5,5,main] +notify() Thread[pool-1-thread-1,5,main] +notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-5,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-2,5,main] +notify() Thread[pool-1-thread-1,5,main] +notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-2,5,main] Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-5,5,main] +Timer canceled +Task2.blocker.prodAll() Thread[pool-1-thread-6,5,main] +Shutting down +*///:~ diff --git a/src/concurrency/OrnamentalGarden.java b/src/concurrency/OrnamentalGarden.java new file mode 100644 index 0000000..e72d712 --- /dev/null +++ b/src/concurrency/OrnamentalGarden.java @@ -0,0 +1,108 @@ +package concurrency;//: concurrency/OrnamentalGarden.java +import java.util.concurrent.*; +import java.util.*; +import static net.mindview.util.Print.*; + +class Count { + private int count = 0; + private Random rand = new Random(47); + // Remove the synchronized keyword to see counting fail: + public synchronized int increment() { + int temp = count; + if(rand.nextBoolean()) // Yield half the time + { + Thread.yield(); + } + return (count = ++temp); + } + public synchronized int value() { return count; } +} + +class Entrance implements Runnable { + private static Count count = new Count(); + private static List entrances = + new ArrayList(); + private int number = 0; + // Doesn't need synchronization to read: + private final int id; + private static volatile boolean canceled = false; + // Atomic operation on a volatile field: + public static void cancel() { canceled = true; } + public Entrance(int id) { + this.id = id; + // Keep this task in a list. Also prevents + // garbage collection of dead tasks: + entrances.add(this); + } + public void run() { + while(!canceled) { + synchronized(this) { + ++number; + } + print(this + " Total: " + count.increment()); + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch(InterruptedException e) { + print("sleep interrupted"); + } + } + print("Stopping " + this); + } + public synchronized int getValue() { return number; } + public String toString() { + return "Entrance " + id + ": " + getValue(); + } + public static int getTotalCount() { + return count.value(); + } + public static int sumEntrances() { + int sum = 0; + for(Entrance entrance : entrances) { + sum += entrance.getValue(); + } + return sum; + } +} + +public class OrnamentalGarden { + public static void main(String[] args) throws Exception { + ExecutorService exec = Executors.newCachedThreadPool(); + for(int i = 0; i < 5; i++) { + exec.execute(new Entrance(i)); + } + // Run for a while, then stop and collect the data: + TimeUnit.SECONDS.sleep(3); + Entrance.cancel(); + exec.shutdown(); + if(!exec.awaitTermination(250, TimeUnit.MILLISECONDS)) { + print("Some tasks were not terminated!"); + } + print("Total: " + Entrance.getTotalCount()); + print("Sum of Entrances: " + Entrance.sumEntrances()); + } +} /* Output: (Sample) +Entrance 0: 1 Total: 1 +Entrance 2: 1 Total: 3 +Entrance 1: 1 Total: 2 +Entrance 4: 1 Total: 5 +Entrance 3: 1 Total: 4 +Entrance 2: 2 Total: 6 +Entrance 4: 2 Total: 7 +Entrance 0: 2 Total: 8 +... +Entrance 3: 29 Total: 143 +Entrance 0: 29 Total: 144 +Entrance 4: 29 Total: 145 +Entrance 2: 30 Total: 147 +Entrance 1: 30 Total: 146 +Entrance 0: 30 Total: 149 +Entrance 3: 30 Total: 148 +Entrance 4: 30 Total: 150 +Stopping Entrance 2: 30 +Stopping Entrance 1: 30 +Stopping Entrance 0: 30 +Stopping Entrance 3: 30 +Stopping Entrance 4: 30 +Total: 150 +Sum of Entrances: 150 +*///:~ diff --git a/src/concurrency/Philosopher.java b/src/concurrency/Philosopher.java new file mode 100644 index 0000000..348b7c9 --- /dev/null +++ b/src/concurrency/Philosopher.java @@ -0,0 +1,47 @@ +package concurrency;//: concurrency/Philosopher.java +// A dining philosopher +import java.util.concurrent.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class Philosopher implements Runnable { + private Chopstick left; + private Chopstick right; + private final int id; + private final int ponderFactor; + private Random rand = new Random(47); + private void pause() throws InterruptedException { + if(ponderFactor == 0) { + return; + } + TimeUnit.MILLISECONDS.sleep( + rand.nextInt(ponderFactor * 250)); + } + public Philosopher(Chopstick left, Chopstick right, + int ident, int ponder) { + this.left = left; + this.right = right; + id = ident; + ponderFactor = ponder; + } + public void run() { + try { + while(!Thread.interrupted()) { + print(this + " " + "thinking"); + pause(); + // Philosopher becomes hungry + print(this + " " + "grabbing right"); + right.take(); + print(this + " " + "grabbing left"); + left.take(); + print(this + " " + "eating"); + pause(); + right.drop(); + left.drop(); + } + } catch(InterruptedException e) { + print(this + " " + "exiting via interrupt"); + } + } + public String toString() { return "Philosopher " + id; } +} ///:~ diff --git a/src/concurrency/PipedIO.java b/src/concurrency/PipedIO.java new file mode 100644 index 0000000..ae29110 --- /dev/null +++ b/src/concurrency/PipedIO.java @@ -0,0 +1,58 @@ +package concurrency;//: concurrency/PipedIO.java +// Using pipes for inter-task I/O +import java.util.concurrent.*; +import java.io.*; +import java.util.*; +import static net.mindview.util.Print.*; + +class Sender implements Runnable { + private Random rand = new Random(47); + private PipedWriter out = new PipedWriter(); + public PipedWriter getPipedWriter() { return out; } + public void run() { + try { + while(true) { + for(char c = 'A'; c <= 'z'; c++) { + out.write(c); + TimeUnit.MILLISECONDS.sleep(rand.nextInt(500)); + } + } + } catch(IOException e) { + print(e + " Sender write exception"); + } catch(InterruptedException e) { + print(e + " Sender sleep interrupted"); + } + } +} + +class Receiver implements Runnable { + private PipedReader in; + public Receiver(Sender sender) throws IOException { + in = new PipedReader(sender.getPipedWriter()); + } + public void run() { + try { + while(true) { + // Blocks until characters are there: + printnb("Read: " + (char)in.read() + ", "); + } + } catch(IOException e) { + print(e + " Receiver read exception"); + } + } +} + +public class PipedIO { + public static void main(String[] args) throws Exception { + Sender sender = new Sender(); + Receiver receiver = new Receiver(sender); + ExecutorService exec = Executors.newCachedThreadPool(); + exec.execute(sender); + exec.execute(receiver); + TimeUnit.SECONDS.sleep(4); + exec.shutdownNow(); + } +} /* Output: (65% match) +Read: A, Read: B, Read: C, Read: D, Read: E, Read: F, Read: G, Read: H, Read: I, Read: J, Read: K, Read: L, Read: M, java.lang.InterruptedException: sleep interrupted Sender sleep interrupted +java.io.InterruptedIOException Receiver read exception +*///:~ diff --git a/src/concurrency/Pool.java b/src/concurrency/Pool.java new file mode 100644 index 0000000..b86ae79 --- /dev/null +++ b/src/concurrency/Pool.java @@ -0,0 +1,55 @@ +package concurrency;//: concurrency/Pool.java +// Using a Semaphore inside a Pool, to restrict +// the number of tasks that can use a resource. +import java.util.concurrent.*; +import java.util.*; + +public class Pool { + private int size; + private List items = new ArrayList(); + private volatile boolean[] checkedOut; + private Semaphore available; + public Pool(Class classObject, int size) { + this.size = size; + checkedOut = new boolean[size]; + available = new Semaphore(size, true); + // Load pool with objects that can be checked out: + for(int i = 0; i < size; ++i) { + try { + // Assumes a default constructor: + items.add(classObject.newInstance()); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + } + public T checkOut() throws InterruptedException { + available.acquire(); + return getItem(); + } + public void checkIn(T x) { + if(releaseItem(x)) { + available.release(); + } + } + private synchronized T getItem() { + for(int i = 0; i < size; ++i) { + if(!checkedOut[i]) { + checkedOut[i] = true; + return items.get(i); + } + } + return null; // Semaphore prevents reaching here + } + private synchronized boolean releaseItem(T item) { + int index = items.indexOf(item); + if(index == -1) { + return false; // Not in the list + } + if(checkedOut[index]) { + checkedOut[index] = false; + return true; + } + return false; // Wasn't checked out + } +} ///:~ diff --git a/src/concurrency/PriorityBlockingQueueDemo.java b/src/concurrency/PriorityBlockingQueueDemo.java new file mode 100644 index 0000000..dbb0535 --- /dev/null +++ b/src/concurrency/PriorityBlockingQueueDemo.java @@ -0,0 +1,122 @@ +package concurrency;//: concurrency/PriorityBlockingQueueDemo.java +import java.util.concurrent.*; +import java.util.*; +import static net.mindview.util.Print.*; + +class PrioritizedTask implements +Runnable, Comparable { + private Random rand = new Random(47); + private static int counter = 0; + private final int id = counter++; + private final int priority; + protected static List sequence = + new ArrayList(); + public PrioritizedTask(int priority) { + this.priority = priority; + sequence.add(this); + } + public int compareTo(PrioritizedTask arg) { + return priority < arg.priority ? 1 : + (priority > arg.priority ? -1 : 0); + } + public void run() { + try { + TimeUnit.MILLISECONDS.sleep(rand.nextInt(250)); + } catch(InterruptedException e) { + // Acceptable way to exit + } + print(this); + } + public String toString() { + return String.format("[%1$-3d]", priority) + + " Task " + id; + } + public String summary() { + return "(" + id + ":" + priority + ")"; + } + public static class EndSentinel extends PrioritizedTask { + private ExecutorService exec; + public EndSentinel(ExecutorService e) { + super(-1); // Lowest priority in this program + exec = e; + } + public void run() { + int count = 0; + for(PrioritizedTask pt : sequence) { + printnb(pt.summary()); + if(++count % 5 == 0) { + print(); + } + } + print(); + print(this + " Calling shutdownNow()"); + exec.shutdownNow(); + } + } +} + +class PrioritizedTaskProducer implements Runnable { + private Random rand = new Random(47); + private Queue queue; + private ExecutorService exec; + public PrioritizedTaskProducer( + Queue q, ExecutorService e) { + queue = q; + exec = e; // Used for EndSentinel + } + public void run() { + // Unbounded queue; never blocks. + // Fill it up fast with random priorities: + for(int i = 0; i < 20; i++) { + queue.add(new PrioritizedTask(rand.nextInt(10))); + Thread.yield(); + } + // Trickle in highest-priority jobs: + try { + for(int i = 0; i < 10; i++) { + TimeUnit.MILLISECONDS.sleep(250); + queue.add(new PrioritizedTask(10)); + } + // Add jobs, lowest priority first: + for(int i = 0; i < 10; i++) { + queue.add(new PrioritizedTask(i)); + } + // A sentinel to stop all the tasks: + queue.add(new PrioritizedTask.EndSentinel(exec)); + } catch(InterruptedException e) { + // Acceptable way to exit + } + print("Finished PrioritizedTaskProducer"); + } +} + +class PrioritizedTaskConsumer implements Runnable { + private PriorityBlockingQueue q; + public PrioritizedTaskConsumer( + PriorityBlockingQueue q) { + this.q = q; + } + public void run() { + try { + while(!Thread.interrupted()) + // Use current thread to run the task: + { + q.take().run(); + } + } catch(InterruptedException e) { + // Acceptable way to exit + } + print("Finished PrioritizedTaskConsumer"); + } +} + +public class PriorityBlockingQueueDemo { + public static void main(String[] args) throws Exception { + Random rand = new Random(47); + ExecutorService exec = Executors.newCachedThreadPool(); + PriorityBlockingQueue queue = + new PriorityBlockingQueue(); + exec.execute(new PrioritizedTaskProducer(queue, exec)); + exec.execute(new PrioritizedTaskConsumer(queue)); + } +} /* (Execute to see output) *///:~ diff --git a/src/concurrency/ReaderWriterList.java b/src/concurrency/ReaderWriterList.java new file mode 100644 index 0000000..8f84266 --- /dev/null +++ b/src/concurrency/ReaderWriterList.java @@ -0,0 +1,86 @@ +package concurrency;//: concurrency/ReaderWriterList.java +import java.util.concurrent.*; +import java.util.concurrent.locks.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class ReaderWriterList { + private ArrayList lockedList; + // Make the ordering fair: + private ReentrantReadWriteLock lock = + new ReentrantReadWriteLock(true); + public ReaderWriterList(int size, T initialValue) { + lockedList = new ArrayList( + Collections.nCopies(size, initialValue)); + } + public T set(int index, T element) { + Lock wlock = lock.writeLock(); + wlock.lock(); + try { + return lockedList.set(index, element); + } finally { + wlock.unlock(); + } + } + public T get(int index) { + Lock rlock = lock.readLock(); + rlock.lock(); + try { + // Show that multiple readers + // may acquire the read lock: + if(lock.getReadLockCount() > 1) { + print(lock.getReadLockCount()); + } + return lockedList.get(index); + } finally { + rlock.unlock(); + } + } + public static void main(String[] args) throws Exception { + new ReaderWriterListTest(30, 1); + } +} + +class ReaderWriterListTest { + ExecutorService exec = Executors.newCachedThreadPool(); + private final static int SIZE = 100; + private static Random rand = new Random(47); + private ReaderWriterList list = + new ReaderWriterList(SIZE, 0); + private class Writer implements Runnable { + public void run() { + try { + for(int i = 0; i < 20; i++) { // 2 second test + list.set(i, rand.nextInt()); + TimeUnit.MILLISECONDS.sleep(100); + } + } catch(InterruptedException e) { + // Acceptable way to exit + } + print("Writer finished, shutting down"); + exec.shutdownNow(); + } + } + private class Reader implements Runnable { + public void run() { + try { + while(!Thread.interrupted()) { + for(int i = 0; i < SIZE; i++) { + list.get(i); + TimeUnit.MILLISECONDS.sleep(1); + } + } + } catch(InterruptedException e) { + // Acceptable way to exit + } + } + } + public ReaderWriterListTest(int readers, int writers) { + for(int i = 0; i < readers; i++) { + exec.execute(new Reader()); + } + for(int i = 0; i < writers; i++) { + exec.execute(new Writer()); + } + } +} /* (Execute to see output) *///:~ diff --git a/src/concurrency/ResponsiveUI.java b/src/concurrency/ResponsiveUI.java new file mode 100644 index 0000000..6977c4c --- /dev/null +++ b/src/concurrency/ResponsiveUI.java @@ -0,0 +1,32 @@ +package concurrency;//: concurrency/ResponsiveUI.java +// User interface responsiveness. +// {RunByHand} + +class UnresponsiveUI { + private volatile double d = 1; + public UnresponsiveUI() throws Exception { + while(d > 0) { + d = d + (Math.PI + Math.E) / d; + } + System.in.read(); // Never gets here + } +} + +public class ResponsiveUI extends Thread { + private static volatile double d = 1; + public ResponsiveUI() { + setDaemon(true); + start(); + } + public void run() { + while(true) { + d = d + (Math.PI + Math.E) / d; + } + } + public static void main(String[] args) throws Exception { + //! new UnresponsiveUI(); // Must kill this process + new ResponsiveUI(); + System.in.read(); + System.out.println(d); // Shows progress + } +} ///:~ diff --git a/src/concurrency/Restaurant.java b/src/concurrency/Restaurant.java new file mode 100644 index 0000000..8b9cef5 --- /dev/null +++ b/src/concurrency/Restaurant.java @@ -0,0 +1,89 @@ +package concurrency;//: concurrency/Restaurant.java +// The producer-consumer approach to task cooperation. +import java.util.concurrent.*; +import static net.mindview.util.Print.*; + +class Meal { + private final int orderNum; + public Meal(int orderNum) { this.orderNum = orderNum; } + public String toString() { return "Meal " + orderNum; } +} + +class WaitPerson implements Runnable { + private Restaurant restaurant; + public WaitPerson(Restaurant r) { restaurant = r; } + public void run() { + try { + while(!Thread.interrupted()) { + synchronized(this) { + while(restaurant.meal == null) { + wait(); // ... for the chef to produce a meal + } + } + print("Waitperson got " + restaurant.meal); + synchronized(restaurant.chef) { + restaurant.meal = null; + restaurant.chef.notifyAll(); // Ready for another + } + } + } catch(InterruptedException e) { + print("WaitPerson interrupted"); + } + } +} + +class Chef implements Runnable { + private Restaurant restaurant; + private int count = 0; + public Chef(Restaurant r) { restaurant = r; } + public void run() { + try { + while(!Thread.interrupted()) { + synchronized(this) { + while(restaurant.meal != null) { + wait(); // ... for the meal to be taken + } + } + if(++count == 10) { + print("Out of food, closing"); + restaurant.exec.shutdownNow(); + } + printnb("Order up! "); + synchronized(restaurant.waitPerson) { + restaurant.meal = new Meal(count); + restaurant.waitPerson.notifyAll(); + } + TimeUnit.MILLISECONDS.sleep(100); + } + } catch(InterruptedException e) { + print("Chef interrupted"); + } + } +} + +public class Restaurant { + Meal meal; + ExecutorService exec = Executors.newCachedThreadPool(); + WaitPerson waitPerson = new WaitPerson(this); + Chef chef = new Chef(this); + public Restaurant() { + exec.execute(chef); + exec.execute(waitPerson); + } + public static void main(String[] args) { + new Restaurant(); + } +} /* Output: +Order up! Waitperson got Meal 1 +Order up! Waitperson got Meal 2 +Order up! Waitperson got Meal 3 +Order up! Waitperson got Meal 4 +Order up! Waitperson got Meal 5 +Order up! Waitperson got Meal 6 +Order up! Waitperson got Meal 7 +Order up! Waitperson got Meal 8 +Order up! Waitperson got Meal 9 +Out of food, closing +WaitPerson interrupted +Order up! Chef interrupted +*///:~ diff --git a/src/concurrency/SelfManaged.java b/src/concurrency/SelfManaged.java new file mode 100644 index 0000000..d23cf14 --- /dev/null +++ b/src/concurrency/SelfManaged.java @@ -0,0 +1,27 @@ +package concurrency;//: concurrency/SelfManaged.java +// A Runnable containing its own driver Thread. + +public class SelfManaged implements Runnable { + private int countDown = 5; + private Thread t = new Thread(this); + public SelfManaged() { t.start(); } + public String toString() { + return Thread.currentThread().getName() + + "(" + countDown + "), "; + } + public void run() { + while(true) { + System.out.print(this); + if(--countDown == 0) { + return; + } + } + } + public static void main(String[] args) { + for(int i = 0; i < 5; i++) { + new SelfManaged(); + } + } +} /* Output: +Thread-0(5), Thread-0(4), Thread-0(3), Thread-0(2), Thread-0(1), Thread-1(5), Thread-1(4), Thread-1(3), Thread-1(2), Thread-1(1), Thread-2(5), Thread-2(4), Thread-2(3), Thread-2(2), Thread-2(1), Thread-3(5), Thread-3(4), Thread-3(3), Thread-3(2), Thread-3(1), Thread-4(5), Thread-4(4), Thread-4(3), Thread-4(2), Thread-4(1), +*///:~ diff --git a/src/concurrency/SemaphoreDemo.java b/src/concurrency/SemaphoreDemo.java new file mode 100644 index 0000000..9e64d34 --- /dev/null +++ b/src/concurrency/SemaphoreDemo.java @@ -0,0 +1,70 @@ +package concurrency;//: concurrency/SemaphoreDemo.java +// Testing the Pool class +import java.util.concurrent.*; +import java.util.*; +import static net.mindview.util.Print.*; + +// A task to check a resource out of a pool: +class CheckoutTask implements Runnable { + private static int counter = 0; + private final int id = counter++; + private Pool pool; + public CheckoutTask(Pool pool) { + this.pool = pool; + } + public void run() { + try { + T item = pool.checkOut(); + print(this + "checked out " + item); + TimeUnit.SECONDS.sleep(1); + print(this +"checking in " + item); + pool.checkIn(item); + } catch(InterruptedException e) { + // Acceptable way to terminate + } + } + public String toString() { + return "CheckoutTask " + id + " "; + } +} + +public class SemaphoreDemo { + final static int SIZE = 25; + public static void main(String[] args) throws Exception { + final Pool pool = + new Pool(Fat.class, SIZE); + ExecutorService exec = Executors.newCachedThreadPool(); + for(int i = 0; i < SIZE; i++) { + exec.execute(new CheckoutTask(pool)); + } + print("All CheckoutTasks created"); + List list = new ArrayList(); + for(int i = 0; i < SIZE; i++) { + Fat f = pool.checkOut(); + printnb(i + ": main() thread checked out "); + f.operation(); + list.add(f); + } + Future blocked = exec.submit(new Runnable() { + public void run() { + try { + // Semaphore prevents additional checkout, + // so call is blocked: + pool.checkOut(); + } catch(InterruptedException e) { + print("checkOut() Interrupted"); + } + } + }); + TimeUnit.SECONDS.sleep(2); + blocked.cancel(true); // Break out of blocked call + print("Checking in objects in " + list); + for(Fat f : list) { + pool.checkIn(f); + } + for(Fat f : list) { + pool.checkIn(f); // Second checkIn ignored + } + exec.shutdown(); + } +} /* (Execute to see output) *///:~ diff --git a/src/concurrency/SerialNumberChecker.java b/src/concurrency/SerialNumberChecker.java new file mode 100644 index 0000000..6fcb192 --- /dev/null +++ b/src/concurrency/SerialNumberChecker.java @@ -0,0 +1,68 @@ +package concurrency;//: concurrency/SerialNumberChecker.java +// Operations that may seem safe are not, +// when threads are present. +// {Args: 4} +import java.util.concurrent.*; + +// Reuses storage so we don't run out of memory: +class CircularSet { + private int[] array; + private int len; + private int index = 0; + public CircularSet(int size) { + array = new int[size]; + len = size; + // Initialize to a value not produced + // by the SerialNumberGenerator: + for(int i = 0; i < size; i++) { + array[i] = -1; + } + } + public synchronized void add(int i) { + array[index] = i; + // Wrap index and write over old elements: + index = ++index % len; + } + public synchronized boolean contains(int val) { + for(int i = 0; i < len; i++) { + if(array[i] == val) { + return true; + } + } + return false; + } +} + +public class SerialNumberChecker { + private static final int SIZE = 10; + private static CircularSet serials = + new CircularSet(1000); + private static ExecutorService exec = + Executors.newCachedThreadPool(); + static class SerialChecker implements Runnable { + public void run() { + while(true) { + int serial = + SerialNumberGenerator.nextSerialNumber(); + if(serials.contains(serial)) { + System.out.println("Duplicate: " + serial); + System.exit(0); + } + serials.add(serial); + } + } + } + public static void main(String[] args) throws Exception { + for(int i = 0; i < SIZE; i++) { + exec.execute(new SerialChecker()); + } + // Stop after n seconds if there's an argument: + if(args.length > 0) { + TimeUnit.SECONDS.sleep(new Integer(args[0])); + System.out.println("No duplicates detected"); + System.exit(0); + } + } +} /* Output: (Sample) +Duplicate: 8468656 +*///:~ diff --git a/src/concurrency/SerialNumberGenerator.java b/src/concurrency/SerialNumberGenerator.java new file mode 100644 index 0000000..dec4605 --- /dev/null +++ b/src/concurrency/SerialNumberGenerator.java @@ -0,0 +1,8 @@ +package concurrency;//: concurrency/SerialNumberGenerator.java + +public class SerialNumberGenerator { + private static volatile int serialNumber = 0; + public static int nextSerialNumber() { + return serialNumber++; // Not thread-safe + } +} ///:~ diff --git a/src/concurrency/SettingDefaultHandler.java b/src/concurrency/SettingDefaultHandler.java new file mode 100644 index 0000000..8765e0f --- /dev/null +++ b/src/concurrency/SettingDefaultHandler.java @@ -0,0 +1,13 @@ +package concurrency;//: concurrency/SettingDefaultHandler.java +import java.util.concurrent.*; + +public class SettingDefaultHandler { + public static void main(String[] args) { + Thread.setDefaultUncaughtExceptionHandler( + new MyUncaughtExceptionHandler()); + ExecutorService exec = Executors.newCachedThreadPool(); + exec.execute(new ExceptionThread()); + } +} /* Output: +caught java.lang.RuntimeException +*///:~ diff --git a/src/concurrency/SimpleDaemons.java b/src/concurrency/SimpleDaemons.java new file mode 100644 index 0000000..4f3cbbc --- /dev/null +++ b/src/concurrency/SimpleDaemons.java @@ -0,0 +1,39 @@ +package concurrency;//: concurrency/SimpleDaemons.java +// Daemon threads don't prevent the program from ending. +import java.util.concurrent.*; +import static net.mindview.util.Print.*; + +public class SimpleDaemons implements Runnable { + public void run() { + try { + while(true) { + TimeUnit.MILLISECONDS.sleep(100); + print(Thread.currentThread() + " " + this); + } + } catch(InterruptedException e) { + print("sleep() interrupted"); + } + } + public static void main(String[] args) throws Exception { + for(int i = 0; i < 10; i++) { + Thread daemon = new Thread(new SimpleDaemons()); + daemon.setDaemon(true); // Must call before start() + daemon.start(); + } + print("All daemons started"); + TimeUnit.MILLISECONDS.sleep(175); + } +} /* Output: (Sample) +All daemons started +Thread[Thread-0,5,main] SimpleDaemons@530daa +Thread[Thread-1,5,main] SimpleDaemons@a62fc3 +Thread[Thread-2,5,main] SimpleDaemons@89ae9e +Thread[Thread-3,5,main] SimpleDaemons@1270b73 +Thread[Thread-4,5,main] SimpleDaemons@60aeb0 +Thread[Thread-5,5,main] SimpleDaemons@16caf43 +Thread[Thread-6,5,main] SimpleDaemons@66848c +Thread[Thread-7,5,main] SimpleDaemons@8813f2 +Thread[Thread-8,5,main] SimpleDaemons@1d58aae +Thread[Thread-9,5,main] SimpleDaemons@83cc67 +... +*///:~ diff --git a/src/concurrency/SimpleMicroBenchmark.java b/src/concurrency/SimpleMicroBenchmark.java new file mode 100644 index 0000000..0d2e1d8 --- /dev/null +++ b/src/concurrency/SimpleMicroBenchmark.java @@ -0,0 +1,46 @@ +package concurrency;//: concurrency/SimpleMicroBenchmark.java +// The dangers of microbenchmarking. +import java.util.concurrent.locks.*; + +abstract class Incrementable { + protected long counter = 0; + public abstract void increment(); +} + +class SynchronizingTest extends Incrementable { + public synchronized void increment() { ++counter; } +} + +class LockingTest extends Incrementable { + private Lock lock = new ReentrantLock(); + public void increment() { + lock.lock(); + try { + ++counter; + } finally { + lock.unlock(); + } + } +} + +public class SimpleMicroBenchmark { + static long test(Incrementable incr) { + long start = System.nanoTime(); + for(long i = 0; i < 10000000L; i++) { + incr.increment(); + } + return System.nanoTime() - start; + } + public static void main(String[] args) { + long synchTime = test(new SynchronizingTest()); + long lockTime = test(new LockingTest()); + System.out.printf("synchronized: %1$10d\n", synchTime); + System.out.printf("Lock: %1$10d\n", lockTime); + System.out.printf("Lock/synchronized = %1$.3f", + (double)lockTime/(double)synchTime); + } +} /* Output: (75% match) +synchronized: 244919117 +Lock: 939098964 +Lock/synchronized = 3.834 +*///:~ diff --git a/src/concurrency/SimplePriorities.java b/src/concurrency/SimplePriorities.java new file mode 100644 index 0000000..440b8a0 --- /dev/null +++ b/src/concurrency/SimplePriorities.java @@ -0,0 +1,53 @@ +package concurrency;//: concurrency/SimplePriorities.java +// Shows the use of thread priorities. +import java.util.concurrent.*; + +public class SimplePriorities implements Runnable { + private int countDown = 5; + private volatile double d; // No optimization + private int priority; + public SimplePriorities(int priority) { + this.priority = priority; + } + public String toString() { + return Thread.currentThread() + ": " + countDown; + } + public void run() { + Thread.currentThread().setPriority(priority); + while(true) { + // An expensive, interruptable operation: + for(int i = 1; i < 100000; i++) { + d += (Math.PI + Math.E) / (double)i; + if(i % 1000 == 0) { + Thread.yield(); + } + } + System.out.println(this); + if(--countDown == 0) { + return; + } + } + } + public static void main(String[] args) { + ExecutorService exec = Executors.newCachedThreadPool(); + for(int i = 0; i < 5; i++) { + exec.execute( + new SimplePriorities(Thread.MIN_PRIORITY)); + } + exec.execute( + new SimplePriorities(Thread.MAX_PRIORITY)); + exec.shutdown(); + } +} /* Output: (70% match) +Thread[pool-1-thread-6,10,main]: 5 +Thread[pool-1-thread-6,10,main]: 4 +Thread[pool-1-thread-6,10,main]: 3 +Thread[pool-1-thread-6,10,main]: 2 +Thread[pool-1-thread-6,10,main]: 1 +Thread[pool-1-thread-3,1,main]: 5 +Thread[pool-1-thread-2,1,main]: 5 +Thread[pool-1-thread-1,1,main]: 5 +Thread[pool-1-thread-5,1,main]: 5 +Thread[pool-1-thread-4,1,main]: 5 +... +*///:~ diff --git a/src/concurrency/SimpleThread.java b/src/concurrency/SimpleThread.java new file mode 100644 index 0000000..9850f19 --- /dev/null +++ b/src/concurrency/SimpleThread.java @@ -0,0 +1,30 @@ +package concurrency;//: concurrency/SimpleThread.java +// Inheriting directly from the Thread class. + +public class SimpleThread extends Thread { + private int countDown = 5; + private static int threadCount = 0; + public SimpleThread() { + // Store the thread name: + super(Integer.toString(++threadCount)); + start(); + } + public String toString() { + return "#" + getName() + "(" + countDown + "), "; + } + public void run() { + while(true) { + System.out.print(this); + if(--countDown == 0) { + return; + } + } + } + public static void main(String[] args) { + for(int i = 0; i < 5; i++) { + new SimpleThread(); + } + } +} /* Output: +#1(5), #1(4), #1(3), #1(2), #1(1), #2(5), #2(4), #2(3), #2(2), #2(1), #3(5), #3(4), #3(3), #3(2), #3(1), #4(5), #4(4), #4(3), #4(2), #4(1), #5(5), #5(4), #5(3), #5(2), #5(1), +*///:~ diff --git a/src/concurrency/SingleThreadExecutor.java b/src/concurrency/SingleThreadExecutor.java new file mode 100644 index 0000000..f034256 --- /dev/null +++ b/src/concurrency/SingleThreadExecutor.java @@ -0,0 +1,15 @@ +package concurrency;//: concurrency/SingleThreadExecutor.java +import java.util.concurrent.*; + +public class SingleThreadExecutor { + public static void main(String[] args) { + ExecutorService exec = + Executors.newSingleThreadExecutor(); + for(int i = 0; i < 5; i++) { + exec.execute(new LiftOff()); + } + exec.shutdown(); + } +} /* Output: +#0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1), #0(Liftoff!), #1(9), #1(8), #1(7), #1(6), #1(5), #1(4), #1(3), #1(2), #1(1), #1(Liftoff!), #2(9), #2(8), #2(7), #2(6), #2(5), #2(4), #2(3), #2(2), #2(1), #2(Liftoff!), #3(9), #3(8), #3(7), #3(6), #3(5), #3(4), #3(3), #3(2), #3(1), #3(Liftoff!), #4(9), #4(8), #4(7), #4(6), #4(5), #4(4), #4(3), #4(2), #4(1), #4(Liftoff!), +*///:~ diff --git a/src/concurrency/SleepingTask.java b/src/concurrency/SleepingTask.java new file mode 100644 index 0000000..1f58fe5 --- /dev/null +++ b/src/concurrency/SleepingTask.java @@ -0,0 +1,28 @@ +package concurrency;//: concurrency/SleepingTask.java +// Calling sleep() to pause for a while. +import java.util.concurrent.*; + +public class SleepingTask extends LiftOff { + public void run() { + try { + while(countDown-- > 0) { + System.out.print(status()); + // Old-style: + // Thread.sleep(100); + // Java SE5/6-style: + TimeUnit.MILLISECONDS.sleep(100); + } + } catch(InterruptedException e) { + System.err.println("Interrupted"); + } + } + public static void main(String[] args) { + ExecutorService exec = Executors.newCachedThreadPool(); + for(int i = 0; i < 5; i++) { + exec.execute(new SleepingTask()); + } + exec.shutdown(); + } +} /* Output: +#0(9), #1(9), #2(9), #3(9), #4(9), #0(8), #1(8), #2(8), #3(8), #4(8), #0(7), #1(7), #2(7), #3(7), #4(7), #0(6), #1(6), #2(6), #3(6), #4(6), #0(5), #1(5), #2(5), #3(5), #4(5), #0(4), #1(4), #2(4), #3(4), #4(4), #0(3), #1(3), #2(3), #3(3), #4(3), #0(2), #1(2), #2(2), #3(2), #4(2), #0(1), #1(1), #2(1), #3(1), #4(1), #0(Liftoff!), #1(Liftoff!), #2(Liftoff!), #3(Liftoff!), #4(Liftoff!), +*///:~ diff --git a/src/concurrency/SyncObject.java b/src/concurrency/SyncObject.java new file mode 100644 index 0000000..4774c15 --- /dev/null +++ b/src/concurrency/SyncObject.java @@ -0,0 +1,44 @@ +package concurrency;//: concurrency/SyncObject.java +// Synchronizing on another object. +import static net.mindview.util.Print.*; + +class DualSynch { + private Object syncObject = new Object(); + public synchronized void f() { + for(int i = 0; i < 5; i++) { + print("f()"); + Thread.yield(); + } + } + public void g() { + synchronized(syncObject) { + for(int i = 0; i < 5; i++) { + print("g()"); + Thread.yield(); + } + } + } +} + +public class SyncObject { + public static void main(String[] args) { + final DualSynch ds = new DualSynch(); + new Thread() { + public void run() { + ds.f(); + } + }.start(); + ds.g(); + } +} /* Output: (Sample) +g() +f() +g() +f() +g() +f() +g() +f() +g() +f() +*///:~ diff --git a/src/concurrency/SynchronizationComparisons.java b/src/concurrency/SynchronizationComparisons.java new file mode 100644 index 0000000..1e58195 --- /dev/null +++ b/src/concurrency/SynchronizationComparisons.java @@ -0,0 +1,268 @@ +package concurrency;//: concurrency/SynchronizationComparisons.java +// Comparing the performance of explicit Locks +// and Atomics versus the synchronized keyword. +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.concurrent.locks.*; +import java.util.*; +import static net.mindview.util.Print.*; + +abstract class Accumulator { + public static long cycles = 50000L; + // Number of Modifiers and Readers during each test: + private static final int N = 4; + public static ExecutorService exec = + Executors.newFixedThreadPool(N*2); + private static CyclicBarrier barrier = + new CyclicBarrier(N*2 + 1); + protected volatile int index = 0; + protected volatile long value = 0; + protected long duration = 0; + protected String id = "error"; + protected final static int SIZE = 100000; + protected static int[] preLoaded = new int[SIZE]; + static { + // Load the array of random numbers: + Random rand = new Random(47); + for(int i = 0; i < SIZE; i++) { + preLoaded[i] = rand.nextInt(); + } + } + public abstract void accumulate(); + public abstract long read(); + private class Modifier implements Runnable { + public void run() { + for(long i = 0; i < cycles; i++) { + accumulate(); + } + try { + barrier.await(); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + } + private class Reader implements Runnable { + private volatile long value; + public void run() { + for(long i = 0; i < cycles; i++) { + value = read(); + } + try { + barrier.await(); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + } + public void timedTest() { + long start = System.nanoTime(); + for(int i = 0; i < N; i++) { + exec.execute(new Modifier()); + exec.execute(new Reader()); + } + try { + barrier.await(); + } catch(Exception e) { + throw new RuntimeException(e); + } + duration = System.nanoTime() - start; + printf("%-13s: %13d\n", id, duration); + } + public static void + report(Accumulator acc1, Accumulator acc2) { + printf("%-22s: %.2f\n", acc1.id + "/" + acc2.id, + (double)acc1.duration/(double)acc2.duration); + } +} + +class BaseLine extends Accumulator { + { id = "BaseLine"; } + public void accumulate() { + value += preLoaded[index++]; + if(index >= SIZE) { + index = 0; + } + } + public long read() { return value; } +} + +class SynchronizedTest extends Accumulator { + { id = "synchronized"; } + public synchronized void accumulate() { + value += preLoaded[index++]; + if(index >= SIZE) { + index = 0; + } + } + public synchronized long read() { + return value; + } +} + +class LockTest extends Accumulator { + { id = "Lock"; } + private Lock lock = new ReentrantLock(); + public void accumulate() { + lock.lock(); + try { + value += preLoaded[index++]; + if(index >= SIZE) { + index = 0; + } + } finally { + lock.unlock(); + } + } + public long read() { + lock.lock(); + try { + return value; + } finally { + lock.unlock(); + } + } +} + +class AtomicTest extends Accumulator { + { id = "Atomic"; } + private AtomicInteger index = new AtomicInteger(0); + private AtomicLong value = new AtomicLong(0); + public void accumulate() { + // Oops! Relying on more than one Atomic at + // a time doesn't work. But it still gives us + // a performance indicator: + int i = index.getAndIncrement(); + value.getAndAdd(preLoaded[i]); + if(++i >= SIZE) { + index.set(0); + } + } + public long read() { return value.get(); } +} + +public class SynchronizationComparisons { + static BaseLine baseLine = new BaseLine(); + static SynchronizedTest synch = new SynchronizedTest(); + static LockTest lock = new LockTest(); + static AtomicTest atomic = new AtomicTest(); + static void test() { + print("============================"); + printf("%-12s : %13d\n", "Cycles", Accumulator.cycles); + baseLine.timedTest(); + synch.timedTest(); + lock.timedTest(); + atomic.timedTest(); + Accumulator.report(synch, baseLine); + Accumulator.report(lock, baseLine); + Accumulator.report(atomic, baseLine); + Accumulator.report(synch, lock); + Accumulator.report(synch, atomic); + Accumulator.report(lock, atomic); + } + public static void main(String[] args) { + int iterations = 5; // Default + if(args.length > 0) // Optionally change iterations + { + iterations = new Integer(args[0]); + } + // The first time fills the thread pool: + print("Warmup"); + baseLine.timedTest(); + // Now the initial test doesn't include the cost + // of starting the threads for the first time. + // Produce multiple data points: + for(int i = 0; i < iterations; i++) { + test(); + Accumulator.cycles *= 2; + } + Accumulator.exec.shutdown(); + } +} /* Output: (Sample) +Warmup +BaseLine : 34237033 +============================ +Cycles : 50000 +BaseLine : 20966632 +synchronized : 24326555 +Lock : 53669950 +Atomic : 30552487 +synchronized/BaseLine : 1.16 +Lock/BaseLine : 2.56 +Atomic/BaseLine : 1.46 +synchronized/Lock : 0.45 +synchronized/Atomic : 0.79 +Lock/Atomic : 1.76 +============================ +Cycles : 100000 +BaseLine : 41512818 +synchronized : 43843003 +Lock : 87430386 +Atomic : 51892350 +synchronized/BaseLine : 1.06 +Lock/BaseLine : 2.11 +Atomic/BaseLine : 1.25 +synchronized/Lock : 0.50 +synchronized/Atomic : 0.84 +Lock/Atomic : 1.68 +============================ +Cycles : 200000 +BaseLine : 80176670 +synchronized : 5455046661 +Lock : 177686829 +Atomic : 101789194 +synchronized/BaseLine : 68.04 +Lock/BaseLine : 2.22 +Atomic/BaseLine : 1.27 +synchronized/Lock : 30.70 +synchronized/Atomic : 53.59 +Lock/Atomic : 1.75 +============================ +Cycles : 400000 +BaseLine : 160383513 +synchronized : 780052493 +Lock : 362187652 +Atomic : 202030984 +synchronized/BaseLine : 4.86 +Lock/BaseLine : 2.26 +Atomic/BaseLine : 1.26 +synchronized/Lock : 2.15 +synchronized/Atomic : 3.86 +Lock/Atomic : 1.79 +============================ +Cycles : 800000 +BaseLine : 322064955 +synchronized : 336155014 +Lock : 704615531 +Atomic : 393231542 +synchronized/BaseLine : 1.04 +Lock/BaseLine : 2.19 +Atomic/BaseLine : 1.22 +synchronized/Lock : 0.47 +synchronized/Atomic : 0.85 +Lock/Atomic : 1.79 +============================ +Cycles : 1600000 +BaseLine : 650004120 +synchronized : 52235762925 +Lock : 1419602771 +Atomic : 796950171 +synchronized/BaseLine : 80.36 +Lock/BaseLine : 2.18 +Atomic/BaseLine : 1.23 +synchronized/Lock : 36.80 +synchronized/Atomic : 65.54 +Lock/Atomic : 1.78 +============================ +Cycles : 3200000 +BaseLine : 1285664519 +synchronized : 96336767661 +Lock : 2846988654 +Atomic : 1590545726 +synchronized/BaseLine : 74.93 +Lock/BaseLine : 2.21 +Atomic/BaseLine : 1.24 +synchronized/Lock : 33.84 +synchronized/Atomic : 60.57 +Lock/Atomic : 1.79 +*///:~ diff --git a/src/concurrency/SynchronizedEvenGenerator.java b/src/concurrency/SynchronizedEvenGenerator.java new file mode 100644 index 0000000..e990d14 --- /dev/null +++ b/src/concurrency/SynchronizedEvenGenerator.java @@ -0,0 +1,17 @@ +package concurrency;//: concurrency/SynchronizedEvenGenerator.java +// Simplifying mutexes with the synchronized keyword. +// {RunByHand} + +public class +SynchronizedEvenGenerator extends IntGenerator { + private int currentEvenValue = 0; + public synchronized int next() { + ++currentEvenValue; + Thread.yield(); // Cause failure faster + ++currentEvenValue; + return currentEvenValue; + } + public static void main(String[] args) { + EvenChecker.test(new SynchronizedEvenGenerator()); + } +} ///:~ diff --git a/src/concurrency/TestBlockingQueues.java b/src/concurrency/TestBlockingQueues.java new file mode 100644 index 0000000..a97c16d --- /dev/null +++ b/src/concurrency/TestBlockingQueues.java @@ -0,0 +1,68 @@ +package concurrency;//: concurrency/TestBlockingQueues.java +// {RunByHand} +import java.util.concurrent.*; +import java.io.*; +import static net.mindview.util.Print.*; + +class LiftOffRunner implements Runnable { + private BlockingQueue rockets; + public LiftOffRunner(BlockingQueue queue) { + rockets = queue; + } + public void add(LiftOff lo) { + try { + rockets.put(lo); + } catch(InterruptedException e) { + print("Interrupted during put()"); + } + } + public void run() { + try { + while(!Thread.interrupted()) { + LiftOff rocket = rockets.take(); + rocket.run(); // Use this thread + } + } catch(InterruptedException e) { + print("Waking from take()"); + } + print("Exiting LiftOffRunner"); + } +} + +public class TestBlockingQueues { + static void getkey() { + try { + // Compensate for Windows/Linux difference in the + // length of the result produced by the Enter key: + new BufferedReader( + new InputStreamReader(System.in)).readLine(); + } catch(java.io.IOException e) { + throw new RuntimeException(e); + } + } + static void getkey(String message) { + print(message); + getkey(); + } + static void + test(String msg, BlockingQueue queue) { + print(msg); + LiftOffRunner runner = new LiftOffRunner(queue); + Thread t = new Thread(runner); + t.start(); + for(int i = 0; i < 5; i++) { + runner.add(new LiftOff(5)); + } + getkey("Press 'Enter' (" + msg + ")"); + t.interrupt(); + print("Finished " + msg + " test"); + } + public static void main(String[] args) { + test("LinkedBlockingQueue", // Unlimited size + new LinkedBlockingQueue()); + test("ArrayBlockingQueue", // Fixed size + new ArrayBlockingQueue(3)); + test("SynchronousQueue", // Size of 1 + new SynchronousQueue()); + } +} ///:~ diff --git a/src/concurrency/Tester.java b/src/concurrency/Tester.java new file mode 100644 index 0000000..116bff9 --- /dev/null +++ b/src/concurrency/Tester.java @@ -0,0 +1,79 @@ +package concurrency;//: concurrency/Tester.java +// Framework to test performance of concurrency containers. +import java.util.concurrent.*; +import net.mindview.util.*; + +public abstract class Tester { + static int testReps = 10; + static int testCycles = 1000; + static int containerSize = 1000; + abstract C containerInitializer(); + abstract void startReadersAndWriters(); + C testContainer; + String testId; + int nReaders; + int nWriters; + volatile long readResult = 0; + volatile long readTime = 0; + volatile long writeTime = 0; + CountDownLatch endLatch; + static ExecutorService exec = + Executors.newCachedThreadPool(); + Integer[] writeData; + Tester(String testId, int nReaders, int nWriters) { + this.testId = testId + " " + + nReaders + "r " + nWriters + "w"; + this.nReaders = nReaders; + this.nWriters = nWriters; + writeData = Generated.array(Integer.class, + new RandomGenerator.Integer(), containerSize); + for(int i = 0; i < testReps; i++) { + runTest(); + readTime = 0; + writeTime = 0; + } + } + void runTest() { + endLatch = new CountDownLatch(nReaders + nWriters); + testContainer = containerInitializer(); + startReadersAndWriters(); + try { + endLatch.await(); + } catch(InterruptedException ex) { + System.out.println("endLatch interrupted"); + } + System.out.printf("%-27s %14d %14d\n", + testId, readTime, writeTime); + if(readTime != 0 && writeTime != 0) { + System.out.printf("%-27s %14d\n", + "readTime + writeTime =", readTime + writeTime); + } + } + abstract class TestTask implements Runnable { + abstract void test(); + abstract void putResults(); + long duration; + public void run() { + long startTime = System.nanoTime(); + test(); + duration = System.nanoTime() - startTime; + synchronized(Tester.this) { + putResults(); + } + endLatch.countDown(); + } + } + public static void initMain(String[] args) { + if(args.length > 0) { + testReps = new Integer(args[0]); + } + if(args.length > 1) { + testCycles = new Integer(args[1]); + } + if(args.length > 2) { + containerSize = new Integer(args[2]); + } + System.out.printf("%-27s %14s %14s\n", + "Type", "Read time", "Write time"); + } +} ///:~ diff --git a/src/concurrency/ThreadLocalVariableHolder.java b/src/concurrency/ThreadLocalVariableHolder.java new file mode 100644 index 0000000..21cf5bd --- /dev/null +++ b/src/concurrency/ThreadLocalVariableHolder.java @@ -0,0 +1,54 @@ +package concurrency;//: concurrency/ThreadLocalVariableHolder.java +// Automatically giving each thread its own storage. +import java.util.concurrent.*; +import java.util.*; + +class Accessor implements Runnable { + private final int id; + public Accessor(int idn) { id = idn; } + public void run() { + while(!Thread.currentThread().isInterrupted()) { + ThreadLocalVariableHolder.increment(); + System.out.println(this); + Thread.yield(); + } + } + public String toString() { + return "#" + id + ": " + + ThreadLocalVariableHolder.get(); + } +} + +public class ThreadLocalVariableHolder { + private static ThreadLocal value = + new ThreadLocal() { + private Random rand = new Random(47); + protected synchronized Integer initialValue() { + return rand.nextInt(10000); + } + }; + public static void increment() { + value.set(value.get() + 1); + } + public static int get() { return value.get(); } + public static void main(String[] args) throws Exception { + ExecutorService exec = Executors.newCachedThreadPool(); + for(int i = 0; i < 5; i++) { + exec.execute(new Accessor(i)); + } + TimeUnit.SECONDS.sleep(3); // Run for a while + exec.shutdownNow(); // All Accessors will quit + } +} /* Output: (Sample) +#0: 9259 +#1: 556 +#2: 6694 +#3: 1862 +#4: 962 +#0: 9260 +#1: 557 +#2: 6695 +#3: 1863 +#4: 963 +... +*///:~ diff --git a/src/concurrency/ThreadVariations.java b/src/concurrency/ThreadVariations.java new file mode 100644 index 0000000..a439165 --- /dev/null +++ b/src/concurrency/ThreadVariations.java @@ -0,0 +1,163 @@ +package concurrency;//: concurrency/ThreadVariations.java +// Creating threads with inner classes. +import java.util.concurrent.*; +import static net.mindview.util.Print.*; + +// Using a named inner class: +class InnerThread1 { + private int countDown = 5; + private Inner inner; + private class Inner extends Thread { + Inner(String name) { + super(name); + start(); + } + public void run() { + try { + while(true) { + print(this); + if(--countDown == 0) { + return; + } + sleep(10); + } + } catch(InterruptedException e) { + print("interrupted"); + } + } + public String toString() { + return getName() + ": " + countDown; + } + } + public InnerThread1(String name) { + inner = new Inner(name); + } +} + +// Using an anonymous inner class: +class InnerThread2 { + private int countDown = 5; + private Thread t; + public InnerThread2(String name) { + t = new Thread(name) { + public void run() { + try { + while(true) { + print(this); + if(--countDown == 0) { + return; + } + sleep(10); + } + } catch(InterruptedException e) { + print("sleep() interrupted"); + } + } + public String toString() { + return getName() + ": " + countDown; + } + }; + t.start(); + } +} + +// Using a named Runnable implementation: +class InnerRunnable1 { + private int countDown = 5; + private Inner inner; + private class Inner implements Runnable { + Thread t; + Inner(String name) { + t = new Thread(this, name); + t.start(); + } + public void run() { + try { + while(true) { + print(this); + if(--countDown == 0) { + return; + } + TimeUnit.MILLISECONDS.sleep(10); + } + } catch(InterruptedException e) { + print("sleep() interrupted"); + } + } + public String toString() { + return t.getName() + ": " + countDown; + } + } + public InnerRunnable1(String name) { + inner = new Inner(name); + } +} + +// Using an anonymous Runnable implementation: +class InnerRunnable2 { + private int countDown = 5; + private Thread t; + public InnerRunnable2(String name) { + t = new Thread(new Runnable() { + public void run() { + try { + while(true) { + print(this); + if(--countDown == 0) { + return; + } + TimeUnit.MILLISECONDS.sleep(10); + } + } catch(InterruptedException e) { + print("sleep() interrupted"); + } + } + public String toString() { + return Thread.currentThread().getName() + + ": " + countDown; + } + }, name); + t.start(); + } +} + +// A separate method to run some code as a task: +class ThreadMethod { + private int countDown = 5; + private Thread t; + private String name; + public ThreadMethod(String name) { this.name = name; } + public void runTask() { + if(t == null) { + t = new Thread(name) { + public void run() { + try { + while(true) { + print(this); + if(--countDown == 0) { + return; + } + sleep(10); + } + } catch(InterruptedException e) { + print("sleep() interrupted"); + } + } + public String toString() { + return getName() + ": " + countDown; + } + }; + t.start(); + } + } +} + +public class ThreadVariations { + public static void main(String[] args) { + new InnerThread1("InnerThread1"); + new InnerThread2("InnerThread2"); + new InnerRunnable1("InnerRunnable1"); + new InnerRunnable2("InnerRunnable2"); + new ThreadMethod("ThreadMethod").runTask(); + } +} /* (Execute to see output) *///:~ diff --git a/src/concurrency/ToastOMatic.java b/src/concurrency/ToastOMatic.java new file mode 100644 index 0000000..1a85484 --- /dev/null +++ b/src/concurrency/ToastOMatic.java @@ -0,0 +1,134 @@ +package concurrency;//: concurrency/ToastOMatic.java +// A toaster that uses queues. +import java.util.concurrent.*; +import java.util.*; +import static net.mindview.util.Print.*; + +class Toast { + public enum Status { DRY, BUTTERED, JAMMED } + private Status status = Status.DRY; + private final int id; + public Toast(int idn) { id = idn; } + public void butter() { status = Status.BUTTERED; } + public void jam() { status = Status.JAMMED; } + public Status getStatus() { return status; } + public int getId() { return id; } + public String toString() { + return "Toast " + id + ": " + status; + } +} + +class ToastQueue extends LinkedBlockingQueue {} + +class Toaster implements Runnable { + private ToastQueue toastQueue; + private int count = 0; + private Random rand = new Random(47); + public Toaster(ToastQueue tq) { toastQueue = tq; } + public void run() { + try { + while(!Thread.interrupted()) { + TimeUnit.MILLISECONDS.sleep( + 100 + rand.nextInt(500)); + // Make toast + Toast t = new Toast(count++); + print(t); + // Insert into queue + toastQueue.put(t); + } + } catch(InterruptedException e) { + print("Toaster interrupted"); + } + print("Toaster off"); + } +} + +// Apply butter to toast: +class Butterer implements Runnable { + private ToastQueue dryQueue, butteredQueue; + public Butterer(ToastQueue dry, ToastQueue buttered) { + dryQueue = dry; + butteredQueue = buttered; + } + public void run() { + try { + while(!Thread.interrupted()) { + // Blocks until next piece of toast is available: + Toast t = dryQueue.take(); + t.butter(); + print(t); + butteredQueue.put(t); + } + } catch(InterruptedException e) { + print("Butterer interrupted"); + } + print("Butterer off"); + } +} + +// Apply jam to buttered toast: +class Jammer implements Runnable { + private ToastQueue butteredQueue, finishedQueue; + public Jammer(ToastQueue buttered, ToastQueue finished) { + butteredQueue = buttered; + finishedQueue = finished; + } + public void run() { + try { + while(!Thread.interrupted()) { + // Blocks until next piece of toast is available: + Toast t = butteredQueue.take(); + t.jam(); + print(t); + finishedQueue.put(t); + } + } catch(InterruptedException e) { + print("Jammer interrupted"); + } + print("Jammer off"); + } +} + +// Consume the toast: +class Eater implements Runnable { + private ToastQueue finishedQueue; + private int counter = 0; + public Eater(ToastQueue finished) { + finishedQueue = finished; + } + public void run() { + try { + while(!Thread.interrupted()) { + // Blocks until next piece of toast is available: + Toast t = finishedQueue.take(); + // Verify that the toast is coming in order, + // and that all pieces are getting jammed: + if(t.getId() != counter++ || + t.getStatus() != Toast.Status.JAMMED) { + print(">>>> Error: " + t); + System.exit(1); + } else { + print("Chomp! " + t); + } + } + } catch(InterruptedException e) { + print("Eater interrupted"); + } + print("Eater off"); + } +} + +public class ToastOMatic { + public static void main(String[] args) throws Exception { + ToastQueue dryQueue = new ToastQueue(), + butteredQueue = new ToastQueue(), + finishedQueue = new ToastQueue(); + ExecutorService exec = Executors.newCachedThreadPool(); + exec.execute(new Toaster(dryQueue)); + exec.execute(new Butterer(dryQueue, butteredQueue)); + exec.execute(new Jammer(butteredQueue, finishedQueue)); + exec.execute(new Eater(finishedQueue)); + TimeUnit.SECONDS.sleep(5); + exec.shutdownNow(); + } +} /* (Execute to see output) *///:~ diff --git a/src/concurrency/build.xml b/src/concurrency/build.xml new file mode 100644 index 0000000..8770d84 --- /dev/null +++ b/src/concurrency/build.xml @@ -0,0 +1,779 @@ + + + + + + build.xml for the source code for the concurrency chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/concurrency/restaurant2/RestaurantWithQueues.java b/src/concurrency/restaurant2/RestaurantWithQueues.java new file mode 100644 index 0000000..c75770d --- /dev/null +++ b/src/concurrency/restaurant2/RestaurantWithQueues.java @@ -0,0 +1,207 @@ +//: concurrency/restaurant2/RestaurantWithQueues.java +// {Args: 5} +package concurrency.restaurant2; +import enumerated.menu.*; +import java.util.concurrent.*; +import java.util.*; +import static net.mindview.util.Print.*; + +// This is given to the waiter, who gives it to the chef: +class Order { // (A data-transfer object) + private static int counter = 0; + private final int id = counter++; + private final Customer customer; + private final WaitPerson waitPerson; + private final Food food; + public Order(Customer cust, WaitPerson wp, Food f) { + customer = cust; + waitPerson = wp; + food = f; + } + public Food item() { return food; } + public Customer getCustomer() { return customer; } + public WaitPerson getWaitPerson() { return waitPerson; } + public String toString() { + return "Order: " + id + " item: " + food + + " for: " + customer + + " served by: " + waitPerson; + } +} + +// This is what comes back from the chef: +class Plate { + private final Order order; + private final Food food; + public Plate(Order ord, Food f) { + order = ord; + food = f; + } + public Order getOrder() { return order; } + public Food getFood() { return food; } + public String toString() { return food.toString(); } +} + +class Customer implements Runnable { + private static int counter = 0; + private final int id = counter++; + private final WaitPerson waitPerson; + // Only one course at a time can be received: + private SynchronousQueue placeSetting = + new SynchronousQueue(); + public Customer(WaitPerson w) { waitPerson = w; } + public void + deliver(Plate p) throws InterruptedException { + // Only blocks if customer is still + // eating the previous course: + placeSetting.put(p); + } + public void run() { + for(Course course : Course.values()) { + Food food = course.randomSelection(); + try { + waitPerson.placeOrder(this, food); + // Blocks until course has been delivered: + print(this + "eating " + placeSetting.take()); + } catch(InterruptedException e) { + print(this + "waiting for " + + course + " interrupted"); + break; + } + } + print(this + "finished meal, leaving"); + } + public String toString() { + return "Customer " + id + " "; + } +} + +class WaitPerson implements Runnable { + private static int counter = 0; + private final int id = counter++; + private final Restaurant restaurant; + BlockingQueue filledOrders = + new LinkedBlockingQueue(); + public WaitPerson(Restaurant rest) { restaurant = rest; } + public void placeOrder(Customer cust, Food food) { + try { + // Shouldn't actually block because this is + // a LinkedBlockingQueue with no size limit: + restaurant.orders.put(new Order(cust, this, food)); + } catch(InterruptedException e) { + print(this + " placeOrder interrupted"); + } + } + public void run() { + try { + while(!Thread.interrupted()) { + // Blocks until a course is ready + Plate plate = filledOrders.take(); + print(this + "received " + plate + + " delivering to " + + plate.getOrder().getCustomer()); + plate.getOrder().getCustomer().deliver(plate); + } + } catch(InterruptedException e) { + print(this + " interrupted"); + } + print(this + " off duty"); + } + public String toString() { + return "WaitPerson " + id + " "; + } +} + +class Chef implements Runnable { + private static int counter = 0; + private final int id = counter++; + private final Restaurant restaurant; + private static Random rand = new Random(47); + public Chef(Restaurant rest) { restaurant = rest; } + public void run() { + try { + while(!Thread.interrupted()) { + // Blocks until an order appears: + Order order = restaurant.orders.take(); + Food requestedItem = order.item(); + // Time to prepare order: + TimeUnit.MILLISECONDS.sleep(rand.nextInt(500)); + Plate plate = new Plate(order, requestedItem); + order.getWaitPerson().filledOrders.put(plate); + } + } catch(InterruptedException e) { + print(this + " interrupted"); + } + print(this + " off duty"); + } + public String toString() { return "Chef " + id + " "; } +} + +class Restaurant implements Runnable { + private List waitPersons = + new ArrayList(); + private List chefs = new ArrayList(); + private ExecutorService exec; + private static Random rand = new Random(47); + BlockingQueue + orders = new LinkedBlockingQueue(); + public Restaurant(ExecutorService e, int nWaitPersons, + int nChefs) { + exec = e; + for(int i = 0; i < nWaitPersons; i++) { + WaitPerson waitPerson = new WaitPerson(this); + waitPersons.add(waitPerson); + exec.execute(waitPerson); + } + for(int i = 0; i < nChefs; i++) { + Chef chef = new Chef(this); + chefs.add(chef); + exec.execute(chef); + } + } + public void run() { + try { + while(!Thread.interrupted()) { + // A new customer arrives; assign a WaitPerson: + WaitPerson wp = waitPersons.get( + rand.nextInt(waitPersons.size())); + Customer c = new Customer(wp); + exec.execute(c); + TimeUnit.MILLISECONDS.sleep(100); + } + } catch(InterruptedException e) { + print("Restaurant interrupted"); + } + print("Restaurant closing"); + } +} + +public class RestaurantWithQueues { + public static void main(String[] args) throws Exception { + ExecutorService exec = Executors.newCachedThreadPool(); + Restaurant restaurant = new Restaurant(exec, 5, 2); + exec.execute(restaurant); + if(args.length > 0) // Optional argument + { + TimeUnit.SECONDS.sleep(new Integer(args[0])); + } else { + print("Press 'Enter' to quit"); + System.in.read(); + } + exec.shutdownNow(); + } +} /* Output: (Sample) +WaitPerson 0 received SPRING_ROLLS delivering to Customer 1 +Customer 1 eating SPRING_ROLLS +WaitPerson 3 received SPRING_ROLLS delivering to Customer 0 +Customer 0 eating SPRING_ROLLS +WaitPerson 0 received BURRITO delivering to Customer 1 +Customer 1 eating BURRITO +WaitPerson 3 received SPRING_ROLLS delivering to Customer 2 +Customer 2 eating SPRING_ROLLS +WaitPerson 1 received SOUP delivering to Customer 3 +Customer 3 eating SOUP +WaitPerson 3 received VINDALOO delivering to Customer 0 +Customer 0 eating VINDALOO +WaitPerson 0 received FRUIT delivering to Customer 1 +... +*///:~ diff --git a/src/concurrency/waxomatic/WaxOMatic.java b/src/concurrency/waxomatic/WaxOMatic.java new file mode 100644 index 0000000..b1981fe --- /dev/null +++ b/src/concurrency/waxomatic/WaxOMatic.java @@ -0,0 +1,81 @@ +//: concurrency/waxomatic/WaxOMatic.java +// Basic task cooperation. +package concurrency.waxomatic; +import java.util.concurrent.*; +import static net.mindview.util.Print.*; + +class Car { + private boolean waxOn = false; + public synchronized void waxed() { + waxOn = true; // Ready to buff + notifyAll(); + } + public synchronized void buffed() { + waxOn = false; // Ready for another coat of wax + notifyAll(); + } + public synchronized void waitForWaxing() + throws InterruptedException { + while(waxOn == false) { + wait(); + } + } + public synchronized void waitForBuffing() + throws InterruptedException { + while(waxOn == true) { + wait(); + } + } +} + +class WaxOn implements Runnable { + private Car car; + public WaxOn(Car c) { car = c; } + public void run() { + try { + while(!Thread.interrupted()) { + printnb("Wax On! "); + TimeUnit.MILLISECONDS.sleep(200); + car.waxed(); + car.waitForBuffing(); + } + } catch(InterruptedException e) { + print("Exiting via interrupt"); + } + print("Ending Wax On task"); + } +} + +class WaxOff implements Runnable { + private Car car; + public WaxOff(Car c) { car = c; } + public void run() { + try { + while(!Thread.interrupted()) { + car.waitForWaxing(); + printnb("Wax Off! "); + TimeUnit.MILLISECONDS.sleep(200); + car.buffed(); + } + } catch(InterruptedException e) { + print("Exiting via interrupt"); + } + print("Ending Wax Off task"); + } +} + +public class WaxOMatic { + public static void main(String[] args) throws Exception { + Car car = new Car(); + ExecutorService exec = Executors.newCachedThreadPool(); + exec.execute(new WaxOff(car)); + exec.execute(new WaxOn(car)); + TimeUnit.SECONDS.sleep(5); // Run for a while... + exec.shutdownNow(); // Interrupt all tasks + } +} /* Output: (95% match) +Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Exiting via interrupt +Ending Wax On task +Exiting via interrupt +Ending Wax Off task +*///:~ diff --git a/src/concurrency/waxomatic2/WaxOMatic2.java b/src/concurrency/waxomatic2/WaxOMatic2.java new file mode 100644 index 0000000..612252a --- /dev/null +++ b/src/concurrency/waxomatic2/WaxOMatic2.java @@ -0,0 +1,102 @@ +//: concurrency/waxomatic2/WaxOMatic2.java +// Using Lock and Condition objects. +package concurrency.waxomatic2; +import java.util.concurrent.*; +import java.util.concurrent.locks.*; +import static net.mindview.util.Print.*; + +class Car { + private Lock lock = new ReentrantLock(); + private Condition condition = lock.newCondition(); + private boolean waxOn = false; + public void waxed() { + lock.lock(); + try { + waxOn = true; // Ready to buff + condition.signalAll(); + } finally { + lock.unlock(); + } + } + public void buffed() { + lock.lock(); + try { + waxOn = false; // Ready for another coat of wax + condition.signalAll(); + } finally { + lock.unlock(); + } + } + public void waitForWaxing() throws InterruptedException { + lock.lock(); + try { + while(waxOn == false) { + condition.await(); + } + } finally { + lock.unlock(); + } + } + public void waitForBuffing() throws InterruptedException{ + lock.lock(); + try { + while(waxOn == true) { + condition.await(); + } + } finally { + lock.unlock(); + } + } +} + +class WaxOn implements Runnable { + private Car car; + public WaxOn(Car c) { car = c; } + public void run() { + try { + while(!Thread.interrupted()) { + printnb("Wax On! "); + TimeUnit.MILLISECONDS.sleep(200); + car.waxed(); + car.waitForBuffing(); + } + } catch(InterruptedException e) { + print("Exiting via interrupt"); + } + print("Ending Wax On task"); + } +} + +class WaxOff implements Runnable { + private Car car; + public WaxOff(Car c) { car = c; } + public void run() { + try { + while(!Thread.interrupted()) { + car.waitForWaxing(); + printnb("Wax Off! "); + TimeUnit.MILLISECONDS.sleep(200); + car.buffed(); + } + } catch(InterruptedException e) { + print("Exiting via interrupt"); + } + print("Ending Wax Off task"); + } +} + +public class WaxOMatic2 { + public static void main(String[] args) throws Exception { + Car car = new Car(); + ExecutorService exec = Executors.newCachedThreadPool(); + exec.execute(new WaxOff(car)); + exec.execute(new WaxOn(car)); + TimeUnit.SECONDS.sleep(5); + exec.shutdownNow(); + } +} /* Output: (90% match) +Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Wax Off! Wax On! Exiting via interrupt +Ending Wax Off task +Exiting via interrupt +Ending Wax On task +*///:~ diff --git a/src/containers/AssociativeArray.java b/src/containers/AssociativeArray.java new file mode 100644 index 0000000..ed9cb30 --- /dev/null +++ b/src/containers/AssociativeArray.java @@ -0,0 +1,64 @@ +package containers;//: containers/AssociativeArray.java +// Associates keys with values. +import static net.mindview.util.Print.*; + +public class AssociativeArray { + private Object[][] pairs; + private int index; + public AssociativeArray(int length) { + pairs = new Object[length][2]; + } + public void put(K key, V value) { + if(index >= pairs.length) { + throw new ArrayIndexOutOfBoundsException(); + } + pairs[index++] = new Object[]{ key, value }; + } + @SuppressWarnings("unchecked") + public V get(K key) { + for(int i = 0; i < index; i++) { + if(key.equals(pairs[i][0])) { + return (V) pairs[i][1]; + } + } + return null; // Did not find key + } + public String toString() { + StringBuilder result = new StringBuilder(); + for(int i = 0; i < index; i++) { + result.append(pairs[i][0].toString()); + result.append(" : "); + result.append(pairs[i][1].toString()); + if(i < index - 1) { + result.append("\n"); + } + } + return result.toString(); + } + public static void main(String[] args) { + AssociativeArray map = + new AssociativeArray(6); + map.put("sky", "blue"); + map.put("grass", "green"); + map.put("ocean", "dancing"); + map.put("tree", "tall"); + map.put("earth", "brown"); + map.put("sun", "warm"); + try { + map.put("extra", "object"); // Past the end + } catch(ArrayIndexOutOfBoundsException e) { + print("Too many objects!"); + } + print(map); + print(map.get("ocean")); + } +} /* Output: +Too many objects! +sky : blue +grass : green +ocean : dancing +tree : tall +earth : brown +sun : warm +dancing +*///:~ diff --git a/src/containers/Bits.java b/src/containers/Bits.java new file mode 100644 index 0000000..3b878f2 --- /dev/null +++ b/src/containers/Bits.java @@ -0,0 +1,79 @@ +package containers;//: containers/Bits.java +// Demonstration of BitSet. +import java.util.*; +import static net.mindview.util.Print.*; + +public class Bits { + public static void printBitSet(BitSet b) { + print("bits: " + b); + StringBuilder bbits = new StringBuilder(); + for(int j = 0; j < b.size() ; j++) { + bbits.append(b.get(j) ? "1" : "0"); + } + print("bit pattern: " + bbits); + } + public static void main(String[] args) { + Random rand = new Random(47); + // Take the LSB of nextInt(): + byte bt = (byte)rand.nextInt(); + BitSet bb = new BitSet(); + for(int i = 7; i >= 0; i--) { + if(((1 << i) & bt) != 0) { + bb.set(i); + } else { + bb.clear(i); + } + } + print("byte value: " + bt); + printBitSet(bb); + + short st = (short)rand.nextInt(); + BitSet bs = new BitSet(); + for(int i = 15; i >= 0; i--) { + if(((1 << i) & st) != 0) { + bs.set(i); + } else { + bs.clear(i); + } + } + print("short value: " + st); + printBitSet(bs); + + int it = rand.nextInt(); + BitSet bi = new BitSet(); + for(int i = 31; i >= 0; i--) { + if(((1 << i) & it) != 0) { + bi.set(i); + } else { + bi.clear(i); + } + } + print("int value: " + it); + printBitSet(bi); + + // Test bitsets >= 64 bits: + BitSet b127 = new BitSet(); + b127.set(127); + print("set bit 127: " + b127); + BitSet b255 = new BitSet(65); + b255.set(255); + print("set bit 255: " + b255); + BitSet b1023 = new BitSet(512); + b1023.set(1023); + b1023.set(1024); + print("set bit 1023: " + b1023); + } +} /* Output: +byte value: -107 +bits: {0, 2, 4, 7} +bit pattern: 1010100100000000000000000000000000000000000000000000000000000000 +short value: 1302 +bits: {1, 2, 4, 8, 10} +bit pattern: 0110100010100000000000000000000000000000000000000000000000000000 +int value: -2014573909 +bits: {0, 1, 3, 5, 7, 9, 11, 18, 19, 21, 22, 23, 24, 25, 26, 31} +bit pattern: 1101010101010000001101111110000100000000000000000000000000000000 +set bit 127: {127} +set bit 255: {255} +set bit 1023: {1023, 1024} +*///:~ diff --git a/src/containers/CanonicalMapping.java b/src/containers/CanonicalMapping.java new file mode 100644 index 0000000..cec1ffa --- /dev/null +++ b/src/containers/CanonicalMapping.java @@ -0,0 +1,48 @@ +package containers;//: containers/CanonicalMapping.java +// Demonstrates WeakHashMap. +import java.util.*; + +class Element { + private String ident; + public Element(String id) { ident = id; } + public String toString() { return ident; } + public int hashCode() { return ident.hashCode(); } + public boolean equals(Object r) { + return r instanceof Element && + ident.equals(((Element)r).ident); + } + protected void finalize() { + System.out.println("Finalizing " + + getClass().getSimpleName() + " " + ident); + } +} + +class Key extends Element { + public Key(String id) { super(id); } +} + +class Value extends Element { + public Value(String id) { super(id); } +} + +public class CanonicalMapping { + public static void main(String[] args) { + int size = 1000; + // Or, choose size via the command line: + if(args.length > 0) { + size = new Integer(args[0]); + } + Key[] keys = new Key[size]; + WeakHashMap map = + new WeakHashMap(); + for(int i = 0; i < size; i++) { + Key k = new Key(Integer.toString(i)); + Value v = new Value(Integer.toString(i)); + if(i % 3 == 0) { + keys[i] = k; // Save as "real" references + } + map.put(k, v); + } + System.gc(); + } +} /* (Execute to see output) *///:~ diff --git a/src/containers/CollectionDataGeneration.java b/src/containers/CollectionDataGeneration.java new file mode 100644 index 0000000..2b48604 --- /dev/null +++ b/src/containers/CollectionDataGeneration.java @@ -0,0 +1,18 @@ +package containers;//: containers/CollectionDataGeneration.java +// Using the Generators defined in the Arrays chapter. +import java.util.*; +import net.mindview.util.*; + +public class CollectionDataGeneration { + public static void main(String[] args) { + System.out.println(new ArrayList( + CollectionData.list( // Convenience method + new RandomGenerator.String(9), 10))); + System.out.println(new HashSet( + new CollectionData( + new RandomGenerator.Integer(), 10))); + } +} /* Output: +[YNzbrnyGc, FOWZnTcQr, GseGZMmJM, RoEsuEcUO, neOEdLsmw, HLGEahKcx, rEqUCBbkI, naMesbtWH, kjUrUkZPg, wsqPzDyCy] +[573, 4779, 871, 4367, 6090, 7882, 2017, 8037, 3455, 299] +*///:~ diff --git a/src/containers/CollectionDataTest.java b/src/containers/CollectionDataTest.java new file mode 100644 index 0000000..5cf5d41 --- /dev/null +++ b/src/containers/CollectionDataTest.java @@ -0,0 +1,23 @@ +package containers;//: containers/CollectionDataTest.java +import java.util.*; +import net.mindview.util.*; + +class Government implements Generator { + String[] foundation = ("strange women lying in ponds " + + "distributing swords is no basis for a system of " + + "government").split(" "); + private int index; + public String next() { return foundation[index++]; } +} + +public class CollectionDataTest { + public static void main(String[] args) { + Set set = new LinkedHashSet( + new CollectionData(new Government(), 15)); + // Using the convenience method: + set.addAll(CollectionData.list(new Government(), 15)); + System.out.println(set); + } +} /* Output: +[strange, women, lying, in, ponds, distributing, swords, is, no, basis, for, a, system, of, government] +*///:~ diff --git a/src/containers/CollectionMethods.java b/src/containers/CollectionMethods.java new file mode 100644 index 0000000..11785a2 --- /dev/null +++ b/src/containers/CollectionMethods.java @@ -0,0 +1,74 @@ +package containers;//: containers/CollectionMethods.java +// Things you can do with all Collections. +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class CollectionMethods { + public static void main(String[] args) { + Collection c = new ArrayList(); + c.addAll(Countries.names(6)); + c.add("ten"); + c.add("eleven"); + print(c); + // Make an array from the List: + Object[] array = c.toArray(); + // Make a String array from the List: + String[] str = c.toArray(new String[0]); + // Find max and min elements; this means + // different things depending on the way + // the Comparable interface is implemented: + print("Collections.max(c) = " + Collections.max(c)); + print("Collections.min(c) = " + Collections.min(c)); + // Add a Collection to another Collection + Collection c2 = new ArrayList(); + c2.addAll(Countries.names(6)); + c.addAll(c2); + print(c); + c.remove(Countries.DATA[0][0]); + print(c); + c.remove(Countries.DATA[1][0]); + print(c); + // Remove all components that are + // in the argument collection: + c.removeAll(c2); + print(c); + c.addAll(c2); + print(c); + // Is an element in this Collection? + String val = Countries.DATA[3][0]; + print("c.contains(" + val + ") = " + c.contains(val)); + // Is a Collection in this Collection? + print("c.containsAll(c2) = " + c.containsAll(c2)); + Collection c3 = + ((List)c).subList(3, 5); + // Keep all the elements that are in both + // c2 and c3 (an intersection of sets): + c2.retainAll(c3); + print(c2); + // Throw away all the elements + // in c2 that also appear in c3: + c2.removeAll(c3); + print("c2.isEmpty() = " + c2.isEmpty()); + c = new ArrayList(); + c.addAll(Countries.names(6)); + print(c); + c.clear(); // Remove all elements + print("after c.clear():" + c); + } +} /* Output: +[ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO, ten, eleven] +Collections.max(c) = ten +Collections.min(c) = ALGERIA +[ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO, ten, eleven, ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO] +[ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO, ten, eleven, ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO] +[BENIN, BOTSWANA, BULGARIA, BURKINA FASO, ten, eleven, ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO] +[ten, eleven] +[ten, eleven, ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO] +c.contains(BOTSWANA) = true +c.containsAll(c2) = true +[ANGOLA, BENIN] +c2.isEmpty() = true +[ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO] +after c.clear():[] +*///:~ diff --git a/src/containers/CountedString.java b/src/containers/CountedString.java new file mode 100644 index 0000000..6dd39fe --- /dev/null +++ b/src/containers/CountedString.java @@ -0,0 +1,66 @@ +package containers;//: containers/CountedString.java +// Creating a good hashCode(). +import java.util.*; +import static net.mindview.util.Print.*; + +public class CountedString { + private static List created = + new ArrayList(); + private String s; + private int id = 0; + public CountedString(String str) { + s = str; + created.add(s); + // id is the total number of instances + // of this string in use by CountedString: + for(String s2 : created) { + if(s2.equals(s)) { + id++; + } + } + } + public String toString() { + return "String: " + s + " id: " + id + + " hashCode(): " + hashCode(); + } + public int hashCode() { + // The very simple approach: + // return s.hashCode() * id; + // Using Joshua Bloch's recipe: + int result = 17; + result = 37 * result + s.hashCode(); + result = 37 * result + id; + return result; + } + public boolean equals(Object o) { + return o instanceof CountedString && + s.equals(((CountedString)o).s) && + id == ((CountedString)o).id; + } + public static void main(String[] args) { + Map map = + new HashMap(); + CountedString[] cs = new CountedString[5]; + for(int i = 0; i < cs.length; i++) { + cs[i] = new CountedString("hi"); + map.put(cs[i], i); // Autobox int -> Integer + } + print(map); + for(CountedString cstring : cs) { + print("Looking up " + cstring); + print(map.get(cstring)); + } + } +} /* Output: (Sample) +{String: hi id: 4 hashCode(): 146450=3, String: hi id: 1 hashCode(): 146447=0, String: hi id: 3 hashCode(): 146449=2, String: hi id: 5 hashCode(): 146451=4, String: hi id: 2 hashCode(): 146448=1} +Looking up String: hi id: 1 hashCode(): 146447 +0 +Looking up String: hi id: 2 hashCode(): 146448 +1 +Looking up String: hi id: 3 hashCode(): 146449 +2 +Looking up String: hi id: 4 hashCode(): 146450 +3 +Looking up String: hi id: 5 hashCode(): 146451 +4 +*///:~ diff --git a/src/containers/DequeTest.java b/src/containers/DequeTest.java new file mode 100644 index 0000000..4663ce3 --- /dev/null +++ b/src/containers/DequeTest.java @@ -0,0 +1,31 @@ +package containers;//: containers/DequeTest.java +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class DequeTest { + static void fillTest(Deque deque) { + for(int i = 20; i < 27; i++) { + deque.addFirst(i); + } + for(int i = 50; i < 55; i++) { + deque.addLast(i); + } + } + public static void main(String[] args) { + Deque di = new Deque(); + fillTest(di); + print(di); + while(di.size() != 0) { + printnb(di.removeFirst() + " "); + } + print(); + fillTest(di); + while(di.size() != 0) { + printnb(di.removeLast() + " "); + } + } +} /* Output: +[26, 25, 24, 23, 22, 21, 20, 50, 51, 52, 53, 54] +26 25 24 23 22 21 20 50 51 52 53 54 +54 53 52 51 50 20 21 22 23 24 25 26 +*///:~ diff --git a/src/containers/Enumerations.java b/src/containers/Enumerations.java new file mode 100644 index 0000000..e723741 --- /dev/null +++ b/src/containers/Enumerations.java @@ -0,0 +1,19 @@ +package containers;//: containers/Enumerations.java +// Java 1.0/1.1 Vector and Enumeration. +import java.util.*; +import net.mindview.util.*; + +public class Enumerations { + public static void main(String[] args) { + Vector v = + new Vector(Countries.names(10)); + Enumeration e = v.elements(); + while(e.hasMoreElements()) { + System.out.print(e.nextElement() + ", "); + } + // Produce an Enumeration from a Collection: + e = Collections.enumeration(new ArrayList()); + } +} /* Output: +ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO, BURUNDI, CAMEROON, CAPE VERDE, CENTRAL AFRICAN REPUBLIC, +*///:~ diff --git a/src/containers/FailFast.java b/src/containers/FailFast.java new file mode 100644 index 0000000..39d4314 --- /dev/null +++ b/src/containers/FailFast.java @@ -0,0 +1,18 @@ +package containers;//: containers/FailFast.java +// Demonstrates the "fail-fast" behavior. +import java.util.*; + +public class FailFast { + public static void main(String[] args) { + Collection c = new ArrayList(); + Iterator it = c.iterator(); + c.add("An object"); + try { + String s = it.next(); + } catch(ConcurrentModificationException e) { + System.out.println(e); + } + } +} /* Output: +java.util.ConcurrentModificationException +*///:~ diff --git a/src/containers/FillingLists.java b/src/containers/FillingLists.java new file mode 100644 index 0000000..5e2a1e0 --- /dev/null +++ b/src/containers/FillingLists.java @@ -0,0 +1,24 @@ +package containers;//: containers/FillingLists.java +// The Collections.fill() & Collections.nCopies() methods. +import java.util.*; + +class StringAddress { + private String s; + public StringAddress(String s) { this.s = s; } + public String toString() { + return super.toString() + " " + s; + } +} + +public class FillingLists { + public static void main(String[] args) { + List list= new ArrayList( + Collections.nCopies(4, new StringAddress("Hello"))); + System.out.println(list); + Collections.fill(list, new StringAddress("World!")); + System.out.println(list); + } +} /* Output: (Sample) +[StringAddress@82ba41 Hello, StringAddress@82ba41 Hello, StringAddress@82ba41 Hello, StringAddress@82ba41 Hello] +[StringAddress@923e30 World!, StringAddress@923e30 World!, StringAddress@923e30 World!, StringAddress@923e30 World!] +*///:~ diff --git a/src/containers/Groundhog.java b/src/containers/Groundhog.java new file mode 100644 index 0000000..dc1c12c --- /dev/null +++ b/src/containers/Groundhog.java @@ -0,0 +1,10 @@ +package containers;//: containers/Groundhog.java +// Looks plausible, but doesn't work as a HashMap key. + +public class Groundhog { + protected int number; + public Groundhog(int n) { number = n; } + public String toString() { + return "Groundhog #" + number; + } +} ///:~ diff --git a/src/containers/Groundhog2.java b/src/containers/Groundhog2.java new file mode 100644 index 0000000..ec78979 --- /dev/null +++ b/src/containers/Groundhog2.java @@ -0,0 +1,12 @@ +package containers;//: containers/Groundhog2.java +// A class that's used as a key in a HashMap +// must override hashCode() and equals(). + +public class Groundhog2 extends Groundhog { + public Groundhog2(int n) { super(n); } + public int hashCode() { return number; } + public boolean equals(Object o) { + return o instanceof Groundhog2 && + (number == ((Groundhog2)o).number); + } +} ///:~ diff --git a/src/containers/IndividualTest.java b/src/containers/IndividualTest.java new file mode 100644 index 0000000..aa86101 --- /dev/null +++ b/src/containers/IndividualTest.java @@ -0,0 +1,19 @@ +package containers;//: containers/IndividualTest.java +import holding.MapOfList; +import typeinfo.pets.*; +import java.util.*; + +public class IndividualTest { + public static void main(String[] args) { + Set pets = new TreeSet(); + for(List lp : + MapOfList.petPeople.values()) { + for(Pet p : lp) { + pets.add(p); + } + } + System.out.println(pets); + } +} /* Output: +[Cat Elsie May, Cat Pinkola, Cat Shackleton, Cat Stanford aka Stinky el Negro, Cymric Molly, Dog Margrett, Mutt Spot, Pug Louie aka Louis Snorkelstein Dupree, Rat Fizzy, Rat Freckly, Rat Fuzzy] +*///:~ diff --git a/src/containers/LinkedHashMapDemo.java b/src/containers/LinkedHashMapDemo.java new file mode 100644 index 0000000..bc6ad9f --- /dev/null +++ b/src/containers/LinkedHashMapDemo.java @@ -0,0 +1,31 @@ +package containers;//: containers/LinkedHashMapDemo.java +// What you can do with a LinkedHashMap. +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class LinkedHashMapDemo { + public static void main(String[] args) { + LinkedHashMap linkedMap = + new LinkedHashMap( + new CountingMapData(9)); + print(linkedMap); + // Least-recently-used order: + linkedMap = + new LinkedHashMap(16, 0.75f, true); + linkedMap.putAll(new CountingMapData(9)); + print(linkedMap); + for(int i = 0; i < 6; i++) // Cause accesses: + { + linkedMap.get(i); + } + print(linkedMap); + linkedMap.get(0); + print(linkedMap); + } +} /* Output: +{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0} +{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0} +{6=G0, 7=H0, 8=I0, 0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0} +{6=G0, 7=H0, 8=I0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 0=A0} +*///:~ diff --git a/src/containers/ListPerformance.java b/src/containers/ListPerformance.java new file mode 100644 index 0000000..70d8162 --- /dev/null +++ b/src/containers/ListPerformance.java @@ -0,0 +1,220 @@ +package containers;//: containers/ListPerformance.java +// Demonstrates performance differences in Lists. +// {Args: 100 500} Small to keep build testing short +import java.util.*; +import net.mindview.util.*; + +public class ListPerformance { + static Random rand = new Random(); + static int reps = 1000; + static List>> tests = + new ArrayList>>(); + static List>> qTests = + new ArrayList>>(); + static { + tests.add(new Test>("add") { + int test(List list, TestParam tp) { + int loops = tp.loops; + int listSize = tp.size; + for(int i = 0; i < loops; i++) { + list.clear(); + for(int j = 0; j < listSize; j++) { + list.add(j); + } + } + return loops * listSize; + } + }); + tests.add(new Test>("get") { + int test(List list, TestParam tp) { + int loops = tp.loops * reps; + int listSize = list.size(); + for(int i = 0; i < loops; i++) { + list.get(rand.nextInt(listSize)); + } + return loops; + } + }); + tests.add(new Test>("set") { + int test(List list, TestParam tp) { + int loops = tp.loops * reps; + int listSize = list.size(); + for(int i = 0; i < loops; i++) { + list.set(rand.nextInt(listSize), 47); + } + return loops; + } + }); + tests.add(new Test>("iteradd") { + int test(List list, TestParam tp) { + final int LOOPS = 1000000; + int half = list.size() / 2; + ListIterator it = list.listIterator(half); + for(int i = 0; i < LOOPS; i++) { + it.add(47); + } + return LOOPS; + } + }); + tests.add(new Test>("insert") { + int test(List list, TestParam tp) { + int loops = tp.loops; + for(int i = 0; i < loops; i++) { + list.add(5, 47); // Minimize random-access cost + } + return loops; + } + }); + tests.add(new Test>("remove") { + int test(List list, TestParam tp) { + int loops = tp.loops; + int size = tp.size; + for(int i = 0; i < loops; i++) { + list.clear(); + list.addAll(new CountingIntegerList(size)); + while(list.size() > 5) { + list.remove(5); // Minimize random-access cost + } + } + return loops * size; + } + }); + // Tests for queue behavior: + qTests.add(new Test>("addFirst") { + int test(LinkedList list, TestParam tp) { + int loops = tp.loops; + int size = tp.size; + for(int i = 0; i < loops; i++) { + list.clear(); + for(int j = 0; j < size; j++) { + list.addFirst(47); + } + } + return loops * size; + } + }); + qTests.add(new Test>("addLast") { + int test(LinkedList list, TestParam tp) { + int loops = tp.loops; + int size = tp.size; + for(int i = 0; i < loops; i++) { + list.clear(); + for(int j = 0; j < size; j++) { + list.addLast(47); + } + } + return loops * size; + } + }); + qTests.add( + new Test>("rmFirst") { + int test(LinkedList list, TestParam tp) { + int loops = tp.loops; + int size = tp.size; + for(int i = 0; i < loops; i++) { + list.clear(); + list.addAll(new CountingIntegerList(size)); + while(list.size() > 0) { + list.removeFirst(); + } + } + return loops * size; + } + }); + qTests.add(new Test>("rmLast") { + int test(LinkedList list, TestParam tp) { + int loops = tp.loops; + int size = tp.size; + for(int i = 0; i < loops; i++) { + list.clear(); + list.addAll(new CountingIntegerList(size)); + while(list.size() > 0) { + list.removeLast(); + } + } + return loops * size; + } + }); + } + static class ListTester extends Tester> { + public ListTester(List container, + List>> tests) { + super(container, tests); + } + // Fill to the appropriate size before each test: + @Override protected List initialize(int size){ + container.clear(); + container.addAll(new CountingIntegerList(size)); + return container; + } + // Convenience method: + public static void run(List list, + List>> tests) { + new ListTester(list, tests).timedTest(); + } + } + public static void main(String[] args) { + if(args.length > 0) { + Tester.defaultParams = TestParam.array(args); + } + // Can only do these two tests on an array: + Tester> arrayTest = + new Tester>(null, tests.subList(1, 3)){ + // This will be called before each test. It + // produces a non-resizeable array-backed list: + @Override protected + List initialize(int size) { + Integer[] ia = Generated.array(Integer.class, + new CountingGenerator.Integer(), size); + return Arrays.asList(ia); + } + }; + arrayTest.setHeadline("Array as List"); + arrayTest.timedTest(); + Tester.defaultParams= TestParam.array( + 10, 5000, 100, 5000, 1000, 1000, 10000, 200); + if(args.length > 0) { + Tester.defaultParams = TestParam.array(args); + } + ListTester.run(new ArrayList(), tests); + ListTester.run(new LinkedList(), tests); + ListTester.run(new Vector(), tests); + Tester.fieldWidth = 12; + Tester> qTest = + new Tester>( + new LinkedList(), qTests); + qTest.setHeadline("Queue tests"); + qTest.timedTest(); + } +} /* Output: (Sample) +--- Array as List --- + size get set + 10 130 183 + 100 130 164 + 1000 129 165 +10000 129 165 +--------------------- ArrayList --------------------- + size add get set iteradd insert remove + 10 121 139 191 435 3952 446 + 100 72 141 191 247 3934 296 + 1000 98 141 194 839 2202 923 +10000 122 144 190 6880 14042 7333 +--------------------- LinkedList --------------------- + size add get set iteradd insert remove + 10 182 164 198 658 366 262 + 100 106 202 230 457 108 201 + 1000 133 1289 1353 430 136 239 +10000 172 13648 13187 435 255 239 +----------------------- Vector ----------------------- + size add get set iteradd insert remove + 10 129 145 187 290 3635 253 + 100 72 144 190 263 3691 292 + 1000 99 145 193 846 2162 927 +10000 108 145 186 6871 14730 7135 +-------------------- Queue tests -------------------- + size addFirst addLast rmFirst rmLast + 10 199 163 251 253 + 100 98 92 180 179 + 1000 99 93 216 212 +10000 111 109 262 384 +*///:~ diff --git a/src/containers/ListSortSearch.java b/src/containers/ListSortSearch.java new file mode 100644 index 0000000..715b89f --- /dev/null +++ b/src/containers/ListSortSearch.java @@ -0,0 +1,43 @@ +package containers;//: containers/ListSortSearch.java +// Sorting and searching Lists with Collections utilities. +import java.util.*; +import static net.mindview.util.Print.*; + +public class ListSortSearch { + public static void main(String[] args) { + List list = + new ArrayList(Utilities.list); + list.addAll(Utilities.list); + print(list); + Collections.shuffle(list, new Random(47)); + print("Shuffled: " + list); + // Use a ListIterator to trim off the last elements: + ListIterator it = list.listIterator(10); + while(it.hasNext()) { + it.next(); + it.remove(); + } + print("Trimmed: " + list); + Collections.sort(list); + print("Sorted: " + list); + String key = list.get(7); + int index = Collections.binarySearch(list, key); + print("Location of " + key + " is " + index + + ", list.get(" + index + ") = " + list.get(index)); + Collections.sort(list, String.CASE_INSENSITIVE_ORDER); + print("Case-insensitive sorted: " + list); + key = list.get(7); + index = Collections.binarySearch(list, key, + String.CASE_INSENSITIVE_ORDER); + print("Location of " + key + " is " + index + + ", list.get(" + index + ") = " + list.get(index)); + } +} /* Output: +[one, Two, three, Four, five, six, one, one, Two, three, Four, five, six, one] +Shuffled: [Four, five, one, one, Two, six, six, three, three, five, Four, Two, one, one] +Trimmed: [Four, five, one, one, Two, six, six, three, three, five] +Sorted: [Four, Two, five, five, one, one, six, six, three, three] +Location of six is 7, list.get(7) = six +Case-insensitive sorted: [five, five, Four, one, one, six, six, three, three, Two] +Location of three is 7, list.get(7) = three +*///:~ diff --git a/src/containers/Lists.java b/src/containers/Lists.java new file mode 100644 index 0000000..50cffcf --- /dev/null +++ b/src/containers/Lists.java @@ -0,0 +1,126 @@ +package containers;//: containers/Lists.java +// Things you can do with Lists. +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class Lists { + private static boolean b; + private static String s; + private static int i; + private static Iterator it; + private static ListIterator lit; + public static void basicTest(List a) { + a.add(1, "x"); // Add at location 1 + a.add("x"); // Add at end + // Add a collection: + a.addAll(Countries.names(25)); + // Add a collection starting at location 3: + a.addAll(3, Countries.names(25)); + b = a.contains("1"); // Is it in there? + // Is the entire collection in there? + b = a.containsAll(Countries.names(25)); + // Lists allow random access, which is cheap + // for ArrayList, expensive for LinkedList: + s = a.get(1); // Get (typed) object at location 1 + i = a.indexOf("1"); // Tell index of object + b = a.isEmpty(); // Any elements inside? + it = a.iterator(); // Ordinary Iterator + lit = a.listIterator(); // ListIterator + lit = a.listIterator(3); // Start at loc 3 + i = a.lastIndexOf("1"); // Last match + a.remove(1); // Remove location 1 + a.remove("3"); // Remove this object + a.set(1, "y"); // Set location 1 to "y" + // Keep everything that's in the argument + // (the intersection of the two sets): + a.retainAll(Countries.names(25)); + // Remove everything that's in the argument: + a.removeAll(Countries.names(25)); + i = a.size(); // How big is it? + a.clear(); // Remove all elements + } + public static void iterMotion(List a) { + ListIterator it = a.listIterator(); + b = it.hasNext(); + b = it.hasPrevious(); + s = it.next(); + i = it.nextIndex(); + s = it.previous(); + i = it.previousIndex(); + } + public static void iterManipulation(List a) { + ListIterator it = a.listIterator(); + it.add("47"); + // Must move to an element after add(): + it.next(); + // Remove the element after the newly produced one: + it.remove(); + // Must move to an element after remove(): + it.next(); + // Change the element after the deleted one: + it.set("47"); + } + public static void testVisual(List a) { + print(a); + List b = Countries.names(25); + print("b = " + b); + a.addAll(b); + a.addAll(b); + print(a); + // Insert, remove, and replace elements + // using a ListIterator: + ListIterator x = a.listIterator(a.size()/2); + x.add("one"); + print(a); + print(x.next()); + x.remove(); + print(x.next()); + x.set("47"); + print(a); + // Traverse the list backwards: + x = a.listIterator(a.size()); + while(x.hasPrevious()) { + printnb(x.previous() + " "); + } + print(); + print("testVisual finished"); + } + // There are some things that only LinkedLists can do: + public static void testLinkedList() { + LinkedList ll = new LinkedList(); + ll.addAll(Countries.names(25)); + print(ll); + // Treat it like a stack, pushing: + ll.addFirst("one"); + ll.addFirst("two"); + print(ll); + // Like "peeking" at the top of a stack: + print(ll.getFirst()); + // Like popping a stack: + print(ll.removeFirst()); + print(ll.removeFirst()); + // Treat it like a queue, pulling elements + // off the tail end: + print(ll.removeLast()); + print(ll); + } + public static void main(String[] args) { + // Make and fill a new list each time: + basicTest( + new LinkedList(Countries.names(25))); + basicTest( + new ArrayList(Countries.names(25))); + iterMotion( + new LinkedList(Countries.names(25))); + iterMotion( + new ArrayList(Countries.names(25))); + iterManipulation( + new LinkedList(Countries.names(25))); + iterManipulation( + new ArrayList(Countries.names(25))); + testVisual( + new LinkedList(Countries.names(25))); + testLinkedList(); + } +} /* (Execute to see output) *///:~ diff --git a/src/containers/MapDataTest.java b/src/containers/MapDataTest.java new file mode 100644 index 0000000..75948f6 --- /dev/null +++ b/src/containers/MapDataTest.java @@ -0,0 +1,48 @@ +package containers;//: containers/MapDataTest.java +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +class Letters implements Generator>, + Iterable { + private int size = 9; + private int number = 1; + private char letter = 'A'; + public Pair next() { + return new Pair( + number++, "" + letter++); + } + public Iterator iterator() { + return new Iterator() { + public Integer next() { return number++; } + public boolean hasNext() { return number < size; } + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } +} + +public class MapDataTest { + public static void main(String[] args) { + // Pair Generator: + print(MapData.map(new Letters(), 11)); + // Two separate generators: + print(MapData.map(new CountingGenerator.Character(), + new RandomGenerator.String(3), 8)); + // A key Generator and a single value: + print(MapData.map(new CountingGenerator.Character(), + "Value", 6)); + // An Iterable and a value Generator: + print(MapData.map(new Letters(), + new RandomGenerator.String(3))); + // An Iterable and a single value: + print(MapData.map(new Letters(), "Pop")); + } +} /* Output: +{1=A, 2=B, 3=C, 4=D, 5=E, 6=F, 7=G, 8=H, 9=I, 10=J, 11=K} +{a=YNz, b=brn, c=yGc, d=FOW, e=ZnT, f=cQr, g=Gse, h=GZM} +{a=Value, b=Value, c=Value, d=Value, e=Value, f=Value} +{1=mJM, 2=RoE, 3=suE, 4=cUO, 5=neO, 6=EdL, 7=smw, 8=HLG} +{1=Pop, 2=Pop, 3=Pop, 4=Pop, 5=Pop, 6=Pop, 7=Pop, 8=Pop} +*///:~ diff --git a/src/containers/MapEntry.java b/src/containers/MapEntry.java new file mode 100644 index 0000000..120b7d5 --- /dev/null +++ b/src/containers/MapEntry.java @@ -0,0 +1,35 @@ +package containers;//: containers/MapEntry.java +// A simple Map.Entry for sample Map implementations. +import java.util.*; + +public class MapEntry implements Map.Entry { + private K key; + private V value; + public MapEntry(K key, V value) { + this.key = key; + this.value = value; + } + public K getKey() { return key; } + public V getValue() { return value; } + public V setValue(V v) { + V result = value; + value = v; + return result; + } + public int hashCode() { + return (key==null ? 0 : key.hashCode()) ^ + (value==null ? 0 : value.hashCode()); + } + public boolean equals(Object o) { + if(!(o instanceof MapEntry)) { + return false; + } + MapEntry me = (MapEntry)o; + return + (key == null ? + me.getKey() == null : key.equals(me.getKey())) && + (value == null ? + me.getValue()== null : value.equals(me.getValue())); + } + public String toString() { return key + "=" + value; } +} ///:~ diff --git a/src/containers/MapPerformance.java b/src/containers/MapPerformance.java new file mode 100644 index 0000000..5d786df --- /dev/null +++ b/src/containers/MapPerformance.java @@ -0,0 +1,97 @@ +package containers;//: containers/MapPerformance.java +// Demonstrates performance differences in Maps. +// {Args: 100 5000} Small to keep build testing short +import java.util.*; + +public class MapPerformance { + static List>> tests = + new ArrayList>>(); + static { + tests.add(new Test>("put") { + int test(Map map, TestParam tp) { + int loops = tp.loops; + int size = tp.size; + for(int i = 0; i < loops; i++) { + map.clear(); + for(int j = 0; j < size; j++) { + map.put(j, j); + } + } + return loops * size; + } + }); + tests.add(new Test>("get") { + int test(Map map, TestParam tp) { + int loops = tp.loops; + int span = tp.size * 2; + for(int i = 0; i < loops; i++) { + for(int j = 0; j < span; j++) { + map.get(j); + } + } + return loops * span; + } + }); + tests.add(new Test>("iterate") { + int test(Map map, TestParam tp) { + int loops = tp.loops * 10; + for(int i = 0; i < loops; i ++) { + Iterator it = map.entrySet().iterator(); + while(it.hasNext()) { + it.next(); + } + } + return loops * map.size(); + } + }); + } + public static void main(String[] args) { + if(args.length > 0) { + Tester.defaultParams = TestParam.array(args); + } + Tester.run(new TreeMap(), tests); + Tester.run(new HashMap(), tests); + Tester.run(new LinkedHashMap(),tests); + Tester.run( + new IdentityHashMap(), tests); + Tester.run(new WeakHashMap(), tests); + Tester.run(new Hashtable(), tests); + } +} /* Output: (Sample) +---------- TreeMap ---------- + size put get iterate + 10 748 168 100 + 100 506 264 76 + 1000 771 450 78 +10000 2962 561 83 +---------- HashMap ---------- + size put get iterate + 10 281 76 93 + 100 179 70 73 + 1000 267 102 72 +10000 1305 265 97 +------- LinkedHashMap ------- + size put get iterate + 10 354 100 72 + 100 273 89 50 + 1000 385 222 56 +10000 2787 341 56 +------ IdentityHashMap ------ + size put get iterate + 10 290 144 101 + 100 204 287 132 + 1000 508 336 77 +10000 767 266 56 +-------- WeakHashMap -------- + size put get iterate + 10 484 146 151 + 100 292 126 117 + 1000 411 136 152 +10000 2165 138 555 +--------- Hashtable --------- + size put get iterate + 10 264 113 113 + 100 181 105 76 + 1000 260 201 80 +10000 1245 134 77 +*///:~ diff --git a/src/containers/Maps.java b/src/containers/Maps.java new file mode 100644 index 0000000..2efbe9e --- /dev/null +++ b/src/containers/Maps.java @@ -0,0 +1,60 @@ +package containers;//: containers/Maps.java +// Things you can do with Maps. +import java.util.concurrent.*; +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class Maps { + public static void printKeys(Map map) { + printnb("Size = " + map.size() + ", "); + printnb("Keys: "); + print(map.keySet()); // Produce a Set of the keys + } + public static void test(Map map) { + print(map.getClass().getSimpleName()); + map.putAll(new CountingMapData(25)); + // Map has 'Set' behavior for keys: + map.putAll(new CountingMapData(25)); + printKeys(map); + // Producing a Collection of the values: + printnb("Values: "); + print(map.values()); + print(map); + print("map.containsKey(11): " + map.containsKey(11)); + print("map.get(11): " + map.get(11)); + print("map.containsValue(\"F0\"): " + + map.containsValue("F0")); + Integer key = map.keySet().iterator().next(); + print("First key in map: " + key); + map.remove(key); + printKeys(map); + map.clear(); + print("map.isEmpty(): " + map.isEmpty()); + map.putAll(new CountingMapData(25)); + // Operations on the Set change the Map: + map.keySet().removeAll(map.keySet()); + print("map.isEmpty(): " + map.isEmpty()); + } + public static void main(String[] args) { + test(new HashMap()); + test(new TreeMap()); + test(new LinkedHashMap()); + test(new IdentityHashMap()); + test(new ConcurrentHashMap()); + test(new WeakHashMap()); + } +} /* Output: +HashMap +Size = 25, Keys: [15, 8, 23, 16, 7, 22, 9, 21, 6, 1, 14, 24, 4, 19, 11, 18, 3, 12, 17, 2, 13, 20, 10, 5, 0] +Values: [P0, I0, X0, Q0, H0, W0, J0, V0, G0, B0, O0, Y0, E0, T0, L0, S0, D0, M0, R0, C0, N0, U0, K0, F0, A0] +{15=P0, 8=I0, 23=X0, 16=Q0, 7=H0, 22=W0, 9=J0, 21=V0, 6=G0, 1=B0, 14=O0, 24=Y0, 4=E0, 19=T0, 11=L0, 18=S0, 3=D0, 12=M0, 17=R0, 2=C0, 13=N0, 20=U0, 10=K0, 5=F0, 0=A0} +map.containsKey(11): true +map.get(11): L0 +map.containsValue("F0"): true +First key in map: 15 +Size = 24, Keys: [8, 23, 16, 7, 22, 9, 21, 6, 1, 14, 24, 4, 19, 11, 18, 3, 12, 17, 2, 13, 20, 10, 5, 0] +map.isEmpty(): true +map.isEmpty(): true +... +*///:~ diff --git a/src/containers/Prediction.java b/src/containers/Prediction.java new file mode 100644 index 0000000..a5e56b3 --- /dev/null +++ b/src/containers/Prediction.java @@ -0,0 +1,15 @@ +package containers;//: containers/Prediction.java +// Predicting the weather with groundhogs. +import java.util.*; + +public class Prediction { + private static Random rand = new Random(47); + private boolean shadow = rand.nextDouble() > 0.5; + public String toString() { + if(shadow) { + return "Six more weeks of Winter!"; + } else { + return "Early Spring!"; + } + } +} ///:~ diff --git a/src/containers/QueueBehavior.java b/src/containers/QueueBehavior.java new file mode 100644 index 0000000..a8081b4 --- /dev/null +++ b/src/containers/QueueBehavior.java @@ -0,0 +1,39 @@ +package containers;//: containers/QueueBehavior.java +// Compares the behavior of some of the queues +import java.util.concurrent.*; +import java.util.*; +import net.mindview.util.*; + +public class QueueBehavior { + private static int count = 10; + static void test(Queue queue, Generator gen) { + for(int i = 0; i < count; i++) { + queue.offer(gen.next()); + } + while(queue.peek() != null) { + System.out.print(queue.remove() + " "); + } + System.out.println(); + } + static class Gen implements Generator { + String[] s = ("one two three four five six seven " + + "eight nine ten").split(" "); + int i; + public String next() { return s[i++]; } + } + public static void main(String[] args) { + test(new LinkedList(), new Gen()); + test(new PriorityQueue(), new Gen()); + test(new ArrayBlockingQueue(count), new Gen()); + test(new ConcurrentLinkedQueue(), new Gen()); + test(new LinkedBlockingQueue(), new Gen()); + test(new PriorityBlockingQueue(), new Gen()); + } +} /* Output: +one two three four five six seven eight nine ten +eight five four nine one seven six ten three two +one two three four five six seven eight nine ten +one two three four five six seven eight nine ten +one two three four five six seven eight nine ten +eight five four nine one seven six ten three two +*///:~ diff --git a/src/containers/RandomBounds.java b/src/containers/RandomBounds.java new file mode 100644 index 0000000..3145baa --- /dev/null +++ b/src/containers/RandomBounds.java @@ -0,0 +1,31 @@ +package containers;//: containers/RandomBounds.java +// Does Math.random() produce 0.0 and 1.0? +// {RunByHand} +import static net.mindview.util.Print.*; + +public class RandomBounds { + static void usage() { + print("Usage:"); + print("\tRandomBounds lower"); + print("\tRandomBounds upper"); + System.exit(1); + } + public static void main(String[] args) { + if(args.length != 1) { + usage(); + } + if(args[0].equals("lower")) { + while(Math.random() != 0.0) { + } + print("Produced 0.0!"); + } + else if(args[0].equals("upper")) { + while(Math.random() != 1.0) { + } + print("Produced 1.0!"); + } + else { + usage(); + } + } +} ///:~ diff --git a/src/containers/ReadOnly.java b/src/containers/ReadOnly.java new file mode 100644 index 0000000..738b506 --- /dev/null +++ b/src/containers/ReadOnly.java @@ -0,0 +1,47 @@ +package containers;//: containers/ReadOnly.java +// Using the Collections.unmodifiable methods. +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class ReadOnly { + static Collection data = + new ArrayList(Countries.names(6)); + public static void main(String[] args) { + Collection c = + Collections.unmodifiableCollection( + new ArrayList(data)); + print(c); // Reading is OK + //! c.add("one"); // Can't change it + + List a = Collections.unmodifiableList( + new ArrayList(data)); + ListIterator lit = a.listIterator(); + print(lit.next()); // Reading is OK + //! lit.add("one"); // Can't change it + + Set s = Collections.unmodifiableSet( + new HashSet(data)); + print(s); // Reading is OK + //! s.add("one"); // Can't change it + + // For a SortedSet: + Set ss = Collections.unmodifiableSortedSet( + new TreeSet(data)); + + Map m = Collections.unmodifiableMap( + new HashMap(Countries.capitals(6))); + print(m); // Reading is OK + //! m.put("Ralph", "Howdy!"); + + // For a SortedMap: + Map sm = + Collections.unmodifiableSortedMap( + new TreeMap(Countries.capitals(6))); + } +} /* Output: +[ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO] +ALGERIA +[BULGARIA, BURKINA FASO, BOTSWANA, BENIN, ANGOLA, ALGERIA] +{BULGARIA=Sofia, BURKINA FASO=Ouagadougou, BOTSWANA=Gaberone, BENIN=Porto-Novo, ANGOLA=Luanda, ALGERIA=Algiers} +*///:~ diff --git a/src/containers/References.java b/src/containers/References.java new file mode 100644 index 0000000..f3f6113 --- /dev/null +++ b/src/containers/References.java @@ -0,0 +1,62 @@ +package containers;//: containers/References.java +// Demonstrates Reference objects +import java.lang.ref.*; +import java.util.*; + +class VeryBig { + private static final int SIZE = 10000; + private long[] la = new long[SIZE]; + private String ident; + public VeryBig(String id) { ident = id; } + public String toString() { return ident; } + protected void finalize() { + System.out.println("Finalizing " + ident); + } +} + +public class References { + private static ReferenceQueue rq = + new ReferenceQueue(); + public static void checkQueue() { + Reference inq = rq.poll(); + if(inq != null) { + System.out.println("In queue: " + inq.get()); + } + } + public static void main(String[] args) { + int size = 10; + // Or, choose size via the command line: + if(args.length > 0) { + size = new Integer(args[0]); + } + LinkedList> sa = + new LinkedList>(); + for(int i = 0; i < size; i++) { + sa.add(new SoftReference( + new VeryBig("Soft " + i), rq)); + System.out.println("Just created: " + sa.getLast()); + checkQueue(); + } + LinkedList> wa = + new LinkedList>(); + for(int i = 0; i < size; i++) { + wa.add(new WeakReference( + new VeryBig("Weak " + i), rq)); + System.out.println("Just created: " + wa.getLast()); + checkQueue(); + } + SoftReference s = + new SoftReference(new VeryBig("Soft")); + WeakReference w = + new WeakReference(new VeryBig("Weak")); + System.gc(); + LinkedList> pa = + new LinkedList>(); + for(int i = 0; i < size; i++) { + pa.add(new PhantomReference( + new VeryBig("Phantom " + i), rq)); + System.out.println("Just created: " + pa.getLast()); + checkQueue(); + } + } +} /* (Execute to see output) *///:~ diff --git a/src/containers/SetPerformance.java b/src/containers/SetPerformance.java new file mode 100644 index 0000000..625144d --- /dev/null +++ b/src/containers/SetPerformance.java @@ -0,0 +1,76 @@ +package containers;//: containers/SetPerformance.java +// Demonstrates performance differences in Sets. +// {Args: 100 5000} Small to keep build testing short +import java.util.*; + +public class SetPerformance { + static List>> tests = + new ArrayList>>(); + static { + tests.add(new Test>("add") { + int test(Set set, TestParam tp) { + int loops = tp.loops; + int size = tp.size; + for(int i = 0; i < loops; i++) { + set.clear(); + for(int j = 0; j < size; j++) { + set.add(j); + } + } + return loops * size; + } + }); + tests.add(new Test>("contains") { + int test(Set set, TestParam tp) { + int loops = tp.loops; + int span = tp.size * 2; + for(int i = 0; i < loops; i++) { + for(int j = 0; j < span; j++) { + set.contains(j); + } + } + return loops * span; + } + }); + tests.add(new Test>("iterate") { + int test(Set set, TestParam tp) { + int loops = tp.loops * 10; + for(int i = 0; i < loops; i++) { + Iterator it = set.iterator(); + while(it.hasNext()) { + it.next(); + } + } + return loops * set.size(); + } + }); + } + public static void main(String[] args) { + if(args.length > 0) { + Tester.defaultParams = TestParam.array(args); + } + Tester.fieldWidth = 10; + Tester.run(new TreeSet(), tests); + Tester.run(new HashSet(), tests); + Tester.run(new LinkedHashSet(), tests); + } +} /* Output: (Sample) +------------- TreeSet ------------- + size add contains iterate + 10 746 173 89 + 100 501 264 68 + 1000 714 410 69 +10000 1975 552 69 +------------- HashSet ------------- + size add contains iterate + 10 308 91 94 + 100 178 75 73 + 1000 216 110 72 +10000 711 215 100 +---------- LinkedHashSet ---------- + size add contains iterate + 10 350 65 83 + 100 270 74 55 + 1000 303 111 54 +10000 1615 256 58 +*///:~ diff --git a/src/containers/SimpleHashMap.java b/src/containers/SimpleHashMap.java new file mode 100644 index 0000000..9c1a4f0 --- /dev/null +++ b/src/containers/SimpleHashMap.java @@ -0,0 +1,75 @@ +package containers;//: containers/SimpleHashMap.java +// A demonstration hashed Map. +import java.util.*; +import net.mindview.util.*; + +public class SimpleHashMap extends AbstractMap { + // Choose a prime number for the hash table + // size, to achieve a uniform distribution: + static final int SIZE = 997; + // You can't have a physical array of generics, + // but you can upcast to one: + @SuppressWarnings("unchecked") + LinkedList>[] buckets = + new LinkedList[SIZE]; + public V put(K key, V value) { + V oldValue = null; + int index = Math.abs(key.hashCode()) % SIZE; + if(buckets[index] == null) { + buckets[index] = new LinkedList>(); + } + LinkedList> bucket = buckets[index]; + MapEntry pair = new MapEntry(key, value); + boolean found = false; + ListIterator> it = bucket.listIterator(); + while(it.hasNext()) { + MapEntry iPair = it.next(); + if(iPair.getKey().equals(key)) { + oldValue = iPair.getValue(); + it.set(pair); // Replace old with new + found = true; + break; + } + } + if(!found) { + buckets[index].add(pair); + } + return oldValue; + } + public V get(Object key) { + int index = Math.abs(key.hashCode()) % SIZE; + if(buckets[index] == null) { + return null; + } + for(MapEntry iPair : buckets[index]) { + if(iPair.getKey().equals(key)) { + return iPair.getValue(); + } + } + return null; + } + public Set> entrySet() { + Set> set= new HashSet>(); + for(LinkedList> bucket : buckets) { + if(bucket == null) { + continue; + } + for(MapEntry mpair : bucket) { + set.add(mpair); + } + } + return set; + } + public static void main(String[] args) { + SimpleHashMap m = + new SimpleHashMap(); + m.putAll(Countries.capitals(25)); + System.out.println(m); + System.out.println(m.get("ERITREA")); + System.out.println(m.entrySet()); + } +} /* Output: +{CAMEROON=Yaounde, CONGO=Brazzaville, CHAD=N'djamena, COTE D'IVOIR (IVORY COAST)=Yamoussoukro, CENTRAL AFRICAN REPUBLIC=Bangui, GUINEA=Conakry, BOTSWANA=Gaberone, BISSAU=Bissau, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FASO=Ouagadougou, ERITREA=Asmara, THE GAMBIA=Banjul, KENYA=Nairobi, GABON=Libreville, CAPE VERDE=Praia, ALGERIA=Algiers, COMOROS=Moroni, EQUATORIAL GUINEA=Malabo, BURUNDI=Bujumbura, BENIN=Porto-Novo, BULGARIA=Sofia, GHANA=Accra, DJIBOUTI=Dijibouti, ETHIOPIA=Addis Ababa} +Asmara +[CAMEROON=Yaounde, CONGO=Brazzaville, CHAD=N'djamena, COTE D'IVOIR (IVORY COAST)=Yamoussoukro, CENTRAL AFRICAN REPUBLIC=Bangui, GUINEA=Conakry, BOTSWANA=Gaberone, BISSAU=Bissau, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FASO=Ouagadougou, ERITREA=Asmara, THE GAMBIA=Banjul, KENYA=Nairobi, GABON=Libreville, CAPE VERDE=Praia, ALGERIA=Algiers, COMOROS=Moroni, EQUATORIAL GUINEA=Malabo, BURUNDI=Bujumbura, BENIN=Porto-Novo, BULGARIA=Sofia, GHANA=Accra, DJIBOUTI=Dijibouti, ETHIOPIA=Addis Ababa] +*///:~ diff --git a/src/containers/SlowMap.java b/src/containers/SlowMap.java new file mode 100644 index 0000000..7487abc --- /dev/null +++ b/src/containers/SlowMap.java @@ -0,0 +1,45 @@ +package containers;//: containers/SlowMap.java +// A Map implemented with ArrayLists. +import java.util.*; +import net.mindview.util.*; + +public class SlowMap extends AbstractMap { + private List keys = new ArrayList(); + private List values = new ArrayList(); + public V put(K key, V value) { + V oldValue = get(key); // The old value or null + if(!keys.contains(key)) { + keys.add(key); + values.add(value); + } else { + values.set(keys.indexOf(key), value); + } + return oldValue; + } + public V get(Object key) { // key is type Object, not K + if(!keys.contains(key)) { + return null; + } + return values.get(keys.indexOf(key)); + } + public Set> entrySet() { + Set> set= new HashSet>(); + Iterator ki = keys.iterator(); + Iterator vi = values.iterator(); + while(ki.hasNext()) { + set.add(new MapEntry(ki.next(), vi.next())); + } + return set; + } + public static void main(String[] args) { + SlowMap m= new SlowMap(); + m.putAll(Countries.capitals(15)); + System.out.println(m); + System.out.println(m.get("BULGARIA")); + System.out.println(m.entrySet()); + } +} /* Output: +{CAMEROON=Yaounde, CHAD=N'djamena, CONGO=Brazzaville, CAPE VERDE=Praia, ALGERIA=Algiers, COMOROS=Moroni, CENTRAL AFRICAN REPUBLIC=Bangui, BOTSWANA=Gaberone, BURUNDI=Bujumbura, BENIN=Porto-Novo, BULGARIA=Sofia, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FASO=Ouagadougou, DJIBOUTI=Dijibouti} +Sofia +[CAMEROON=Yaounde, CHAD=N'djamena, CONGO=Brazzaville, CAPE VERDE=Praia, ALGERIA=Algiers, COMOROS=Moroni, CENTRAL AFRICAN REPUBLIC=Bangui, BOTSWANA=Gaberone, BURUNDI=Bujumbura, BENIN=Porto-Novo, BULGARIA=Sofia, EGYPT=Cairo, ANGOLA=Luanda, BURKINA FASO=Ouagadougou, DJIBOUTI=Dijibouti] +*///:~ diff --git a/src/containers/SortedMapDemo.java b/src/containers/SortedMapDemo.java new file mode 100644 index 0000000..b0ef138 --- /dev/null +++ b/src/containers/SortedMapDemo.java @@ -0,0 +1,42 @@ +package containers;//: containers/SortedMapDemo.java +// What you can do with a TreeMap. +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class SortedMapDemo { + public static void main(String[] args) { + TreeMap sortedMap = + new TreeMap(new CountingMapData(10)); + print(sortedMap); + Integer low = sortedMap.firstKey(); + Integer high = sortedMap.lastKey(); + print(low); + print(high); + Iterator it = sortedMap.keySet().iterator(); + for(int i = 0; i <= 6; i++) { + if(i == 3) { + low = it.next(); + } + if(i == 6) { + high = it.next(); + } else { + it.next(); + } + } + print(low); + print(high); + print(sortedMap.subMap(low, high)); + print(sortedMap.headMap(high)); + print(sortedMap.tailMap(low)); + } +} /* Output: +{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0, 9=J0} +0 +9 +3 +7 +{3=D0, 4=E0, 5=F0, 6=G0} +{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0} +{3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0, 9=J0} +*///:~ diff --git a/src/containers/SortedSetDemo.java b/src/containers/SortedSetDemo.java new file mode 100644 index 0000000..e07c596 --- /dev/null +++ b/src/containers/SortedSetDemo.java @@ -0,0 +1,43 @@ +package containers;//: containers/SortedSetDemo.java +// What you can do with a TreeSet. +import java.util.*; +import static net.mindview.util.Print.*; + +public class SortedSetDemo { + public static void main(String[] args) { + SortedSet sortedSet = new TreeSet(); + Collections.addAll(sortedSet, + "one two three four five six seven eight" + .split(" ")); + print(sortedSet); + String low = sortedSet.first(); + String high = sortedSet.last(); + print(low); + print(high); + Iterator it = sortedSet.iterator(); + for(int i = 0; i <= 6; i++) { + if(i == 3) { + low = it.next(); + } + if(i == 6) { + high = it.next(); + } else { + it.next(); + } + } + print(low); + print(high); + print(sortedSet.subSet(low, high)); + print(sortedSet.headSet(high)); + print(sortedSet.tailSet(low)); + } +} /* Output: +[eight, five, four, one, seven, six, three, two] +eight +two +one +two +[one, seven, six, three] +[eight, five, four, one, seven, six, three] +[one, seven, six, three, two] +*///:~ diff --git a/src/containers/SpringDetector.java b/src/containers/SpringDetector.java new file mode 100644 index 0000000..6c1deb1 --- /dev/null +++ b/src/containers/SpringDetector.java @@ -0,0 +1,33 @@ +package containers;//: containers/SpringDetector.java +// What will the weather be? +import java.lang.reflect.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class SpringDetector { + // Uses a Groundhog or class derived from Groundhog: + public static + void detectSpring(Class type) throws Exception { + Constructor ghog = type.getConstructor(int.class); + Map map = + new HashMap(); + for(int i = 0; i < 10; i++) { + map.put(ghog.newInstance(i), new Prediction()); + } + print("map = " + map); + Groundhog gh = ghog.newInstance(3); + print("Looking up prediction for " + gh); + if(map.containsKey(gh)) { + print(map.get(gh)); + } else { + print("Key not found: " + gh); + } + } + public static void main(String[] args) throws Exception { + detectSpring(Groundhog.class); + } +} /* Output: +map = {Groundhog #3=Early Spring!, Groundhog #7=Early Spring!, Groundhog #5=Early Spring!, Groundhog #9=Six more weeks of Winter!, Groundhog #8=Six more weeks of Winter!, Groundhog #0=Six more weeks of Winter!, Groundhog #6=Early Spring!, Groundhog #4=Six more weeks of Winter!, Groundhog #1=Six more weeks of Winter!, Groundhog #2=Early Spring!} +Looking up prediction for Groundhog #3 +Key not found: Groundhog #3 +*///:~ diff --git a/src/containers/SpringDetector2.java b/src/containers/SpringDetector2.java new file mode 100644 index 0000000..6b40782 --- /dev/null +++ b/src/containers/SpringDetector2.java @@ -0,0 +1,12 @@ +package containers;//: containers/SpringDetector2.java +// A working key. + +public class SpringDetector2 { + public static void main(String[] args) throws Exception { + SpringDetector.detectSpring(Groundhog2.class); + } +} /* Output: +map = {Groundhog #2=Early Spring!, Groundhog #4=Six more weeks of Winter!, Groundhog #9=Six more weeks of Winter!, Groundhog #8=Six more weeks of Winter!, Groundhog #6=Early Spring!, Groundhog #1=Six more weeks of Winter!, Groundhog #3=Early Spring!, Groundhog #7=Early Spring!, Groundhog #5=Early Spring!, Groundhog #0=Six more weeks of Winter!} +Looking up prediction for Groundhog #3 +Early Spring! +*///:~ diff --git a/src/containers/Stacks.java b/src/containers/Stacks.java new file mode 100644 index 0000000..d8c1eb8 --- /dev/null +++ b/src/containers/Stacks.java @@ -0,0 +1,54 @@ +package containers;//: containers/Stacks.java +// Demonstration of Stack Class. +import java.util.*; +import static net.mindview.util.Print.*; + +enum Month { JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, + JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER } + +public class Stacks { + public static void main(String[] args) { + Stack stack = new Stack(); + for(Month m : Month.values()) { + stack.push(m.toString()); + } + print("stack = " + stack); + // Treating a stack as a Vector: + stack.addElement("The last line"); + print("element 5 = " + stack.elementAt(5)); + print("popping elements:"); + while(!stack.empty()) { + printnb(stack.pop() + " "); + } + + // Using a LinkedList as a Stack: + LinkedList lstack = new LinkedList(); + for(Month m : Month.values()) { + lstack.addFirst(m.toString()); + } + print("lstack = " + lstack); + while(!lstack.isEmpty()) { + printnb(lstack.removeFirst() + " "); + } + + // Using the Stack class from + // the Holding Your Objects Chapter: + net.mindview.util.Stack stack2 = + new net.mindview.util.Stack(); + for(Month m : Month.values()) { + stack2.push(m.toString()); + } + print("stack2 = " + stack2); + while(!stack2.empty()) { + printnb(stack2.pop() + " "); + } + + } +} /* Output: +stack = [JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER] +element 5 = JUNE +popping elements: +The last line NOVEMBER OCTOBER SEPTEMBER AUGUST JULY JUNE MAY APRIL MARCH FEBRUARY JANUARY lstack = [NOVEMBER, OCTOBER, SEPTEMBER, AUGUST, JULY, JUNE, MAY, APRIL, MARCH, FEBRUARY, JANUARY] +NOVEMBER OCTOBER SEPTEMBER AUGUST JULY JUNE MAY APRIL MARCH FEBRUARY JANUARY stack2 = [NOVEMBER, OCTOBER, SEPTEMBER, AUGUST, JULY, JUNE, MAY, APRIL, MARCH, FEBRUARY, JANUARY] +NOVEMBER OCTOBER SEPTEMBER AUGUST JULY JUNE MAY APRIL MARCH FEBRUARY JANUARY +*///:~ diff --git a/src/containers/StringHashCode.java b/src/containers/StringHashCode.java new file mode 100644 index 0000000..78ca10d --- /dev/null +++ b/src/containers/StringHashCode.java @@ -0,0 +1,12 @@ +package containers;//: containers/StringHashCode.java + +public class StringHashCode { + public static void main(String[] args) { + String[] hellos = "Hello Hello".split(" "); + System.out.println(hellos[0].hashCode()); + System.out.println(hellos[1].hashCode()); + } +} /* Output: (Sample) +69609650 +69609650 +*///:~ diff --git a/src/containers/Synchronization.java b/src/containers/Synchronization.java new file mode 100644 index 0000000..53ca0d5 --- /dev/null +++ b/src/containers/Synchronization.java @@ -0,0 +1,22 @@ +package containers;//: containers/Synchronization.java +// Using the Collections.synchronized methods. +import java.util.*; + +public class Synchronization { + public static void main(String[] args) { + Collection c = + Collections.synchronizedCollection( + new ArrayList()); + List list = Collections.synchronizedList( + new ArrayList()); + Set s = Collections.synchronizedSet( + new HashSet()); + Set ss = Collections.synchronizedSortedSet( + new TreeSet()); + Map m = Collections.synchronizedMap( + new HashMap()); + Map sm = + Collections.synchronizedSortedMap( + new TreeMap()); + } +} ///:~ diff --git a/src/containers/Test.java b/src/containers/Test.java new file mode 100644 index 0000000..de37a2e --- /dev/null +++ b/src/containers/Test.java @@ -0,0 +1,10 @@ +package containers;//: containers/Test.java +// Framework for performing timed tests of containers. + +public abstract class Test { + String name; + public Test(String name) { this.name = name; } + // Override this method for different tests. + // Returns actual number of repetitions of test. + abstract int test(C container, TestParam tp); +} ///:~ diff --git a/src/containers/TestParam.java b/src/containers/TestParam.java new file mode 100644 index 0000000..719d915 --- /dev/null +++ b/src/containers/TestParam.java @@ -0,0 +1,29 @@ +package containers;//: containers/TestParam.java +// A "data transfer object." + +public class TestParam { + public final int size; + public final int loops; + public TestParam(int size, int loops) { + this.size = size; + this.loops = loops; + } + // Create an array of TestParam from a varargs sequence: + public static TestParam[] array(int... values) { + int size = values.length/2; + TestParam[] result = new TestParam[size]; + int n = 0; + for(int i = 0; i < size; i++) { + result[i] = new TestParam(values[n++], values[n++]); + } + return result; + } + // Convert a String array to a TestParam array: + public static TestParam[] array(String[] values) { + int[] vals = new int[values.length]; + for(int i = 0; i < vals.length; i++) { + vals[i] = Integer.decode(values[i]); + } + return array(vals); + } +} ///:~ diff --git a/src/containers/Tester.java b/src/containers/Tester.java new file mode 100644 index 0000000..f0813be --- /dev/null +++ b/src/containers/Tester.java @@ -0,0 +1,85 @@ +package containers;//: containers/Tester.java +// Applies Test objects to lists of different containers. +import java.util.*; + +public class Tester { + public static int fieldWidth = 8; + public static TestParam[] defaultParams= TestParam.array( + 10, 5000, 100, 5000, 1000, 5000, 10000, 500); + // Override this to modify pre-test initialization: + protected C initialize(int size) { return container; } + protected C container; + private String headline = ""; + private List> tests; + private static String stringField() { + return "%" + fieldWidth + "s"; + } + private static String numberField() { + return "%" + fieldWidth + "d"; + } + private static int sizeWidth = 5; + private static String sizeField = "%" + sizeWidth + "s"; + private TestParam[] paramList = defaultParams; + public Tester(C container, List> tests) { + this.container = container; + this.tests = tests; + if(container != null) { + headline = container.getClass().getSimpleName(); + } + } + public Tester(C container, List> tests, + TestParam[] paramList) { + this(container, tests); + this.paramList = paramList; + } + public void setHeadline(String newHeadline) { + headline = newHeadline; + } + // Generic methods for convenience : + public static void run(C cntnr, List> tests){ + new Tester(cntnr, tests).timedTest(); + } + public static void run(C cntnr, + List> tests, TestParam[] paramList) { + new Tester(cntnr, tests, paramList).timedTest(); + } + private void displayHeader() { + // Calculate width and pad with '-': + int width = fieldWidth * tests.size() + sizeWidth; + int dashLength = width - headline.length() - 1; + StringBuilder head = new StringBuilder(width); + for(int i = 0; i < dashLength/2; i++) { + head.append('-'); + } + head.append(' '); + head.append(headline); + head.append(' '); + for(int i = 0; i < dashLength/2; i++) { + head.append('-'); + } + System.out.println(head); + // Print column headers: + System.out.format(sizeField, "size"); + for(Test test : tests) { + System.out.format(stringField(), test.name); + } + System.out.println(); + } + // Run the tests for this container: + public void timedTest() { + displayHeader(); + for(TestParam param : paramList) { + System.out.format(sizeField, param.size); + for(Test test : tests) { + C kontainer = initialize(param.size); + long start = System.nanoTime(); + // Call the overriden method: + int reps = test.test(kontainer, param); + long duration = System.nanoTime() - start; + long timePerRep = duration / reps; // Nanoseconds + System.out.format(numberField(), timePerRep); + } + System.out.println(); + } + } +} ///:~ diff --git a/src/containers/ToDoList.java b/src/containers/ToDoList.java new file mode 100644 index 0000000..d2cebbc --- /dev/null +++ b/src/containers/ToDoList.java @@ -0,0 +1,55 @@ +package containers;//: containers/ToDoList.java +// A more complex use of PriorityQueue. +import java.util.*; + +class ToDoList extends PriorityQueue { + static class ToDoItem implements Comparable { + private char primary; + private int secondary; + private String item; + public ToDoItem(String td, char pri, int sec) { + primary = pri; + secondary = sec; + item = td; + } + public int compareTo(ToDoItem arg) { + if(primary > arg.primary) { + return +1; + } + if(primary == arg.primary) { + if(secondary > arg.secondary) { + return +1; + } else if(secondary == arg.secondary) { + return 0; + } + } + return -1; + } + public String toString() { + return Character.toString(primary) + + secondary + ": " + item; + } + } + public void add(String td, char pri, int sec) { + super.add(new ToDoItem(td, pri, sec)); + } + public static void main(String[] args) { + ToDoList toDoList = new ToDoList(); + toDoList.add("Empty trash", 'C', 4); + toDoList.add("Feed dog", 'A', 2); + toDoList.add("Feed bird", 'B', 7); + toDoList.add("Mow lawn", 'C', 3); + toDoList.add("Water lawn", 'A', 1); + toDoList.add("Feed cat", 'B', 1); + while(!toDoList.isEmpty()) { + System.out.println(toDoList.remove()); + } + } +} /* Output: +A1: Water lawn +A2: Feed dog +B1: Feed cat +B7: Feed bird +C3: Mow lawn +C4: Empty trash +*///:~ diff --git a/src/containers/TypesForSets.java b/src/containers/TypesForSets.java new file mode 100644 index 0000000..6c007f3 --- /dev/null +++ b/src/containers/TypesForSets.java @@ -0,0 +1,75 @@ +package containers;//: containers/TypesForSets.java +// Methods necessary to put your own type in a Set. +import java.util.*; + +class SetType { + int i; + public SetType(int n) { i = n; } + public boolean equals(Object o) { + return o instanceof SetType && (i == ((SetType)o).i); + } + public String toString() { return Integer.toString(i); } +} + +class HashType extends SetType { + public HashType(int n) { super(n); } + public int hashCode() { return i; } +} + +class TreeType extends SetType +implements Comparable { + public TreeType(int n) { super(n); } + public int compareTo(TreeType arg) { + return (arg.i < i ? -1 : (arg.i == i ? 0 : 1)); + } +} + +public class TypesForSets { + static Set fill(Set set, Class type) { + try { + for(int i = 0; i < 10; i++) { + set.add( + type.getConstructor(int.class).newInstance(i)); + } + } catch(Exception e) { + throw new RuntimeException(e); + } + return set; + } + static void test(Set set, Class type) { + fill(set, type); + fill(set, type); // Try to add duplicates + fill(set, type); + System.out.println(set); + } + public static void main(String[] args) { + test(new HashSet(), HashType.class); + test(new LinkedHashSet(), HashType.class); + test(new TreeSet(), TreeType.class); + // Things that don't work: + test(new HashSet(), SetType.class); + test(new HashSet(), TreeType.class); + test(new LinkedHashSet(), SetType.class); + test(new LinkedHashSet(), TreeType.class); + try { + test(new TreeSet(), SetType.class); + } catch(Exception e) { + System.out.println(e.getMessage()); + } + try { + test(new TreeSet(), HashType.class); + } catch(Exception e) { + System.out.println(e.getMessage()); + } + } +} /* Output: (Sample) +[2, 4, 9, 8, 6, 1, 3, 7, 5, 0] +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +[9, 8, 7, 6, 5, 4, 3, 2, 1, 0] +[9, 9, 7, 5, 1, 2, 6, 3, 0, 7, 2, 4, 4, 7, 9, 1, 3, 6, 2, 4, 3, 0, 5, 0, 8, 8, 8, 6, 5, 1] +[0, 5, 5, 6, 5, 0, 3, 1, 9, 8, 4, 2, 3, 9, 7, 3, 4, 4, 0, 7, 1, 9, 6, 2, 1, 8, 2, 8, 6, 7] +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +java.lang.ClassCastException: SetType cannot be cast to java.lang.Comparable +java.lang.ClassCastException: HashType cannot be cast to java.lang.Comparable +*///:~ diff --git a/src/containers/Unsupported.java b/src/containers/Unsupported.java new file mode 100644 index 0000000..c39a36a --- /dev/null +++ b/src/containers/Unsupported.java @@ -0,0 +1,64 @@ +package containers;//: containers/Unsupported.java +// Unsupported operations in Java containers. +import java.util.*; + +public class Unsupported { + static void test(String msg, List list) { + System.out.println("--- " + msg + " ---"); + Collection c = list; + Collection subList = list.subList(1,8); + // Copy of the sublist: + Collection c2 = new ArrayList(subList); + try { c.retainAll(c2); } catch(Exception e) { + System.out.println("retainAll(): " + e); + } + try { c.removeAll(c2); } catch(Exception e) { + System.out.println("removeAll(): " + e); + } + try { c.clear(); } catch(Exception e) { + System.out.println("clear(): " + e); + } + try { c.add("X"); } catch(Exception e) { + System.out.println("add(): " + e); + } + try { c.addAll(c2); } catch(Exception e) { + System.out.println("addAll(): " + e); + } + try { c.remove("C"); } catch(Exception e) { + System.out.println("remove(): " + e); + } + // The List.set() method modifies the value but + // doesn't change the size of the data structure: + try { + list.set(0, "X"); + } catch(Exception e) { + System.out.println("List.set(): " + e); + } + } + public static void main(String[] args) { + List list = + Arrays.asList("A B C D E F G H I J K L".split(" ")); + test("Modifiable Copy", new ArrayList(list)); + test("Arrays.asList()", list); + test("unmodifiableList()", + Collections.unmodifiableList( + new ArrayList(list))); + } +} /* Output: +--- Modifiable Copy --- +--- Arrays.asList() --- +retainAll(): java.lang.UnsupportedOperationException +removeAll(): java.lang.UnsupportedOperationException +clear(): java.lang.UnsupportedOperationException +add(): java.lang.UnsupportedOperationException +addAll(): java.lang.UnsupportedOperationException +remove(): java.lang.UnsupportedOperationException +--- unmodifiableList() --- +retainAll(): java.lang.UnsupportedOperationException +removeAll(): java.lang.UnsupportedOperationException +clear(): java.lang.UnsupportedOperationException +add(): java.lang.UnsupportedOperationException +addAll(): java.lang.UnsupportedOperationException +remove(): java.lang.UnsupportedOperationException +List.set(): java.lang.UnsupportedOperationException +*///:~ diff --git a/src/containers/Utilities.java b/src/containers/Utilities.java new file mode 100644 index 0000000..e96ab50 --- /dev/null +++ b/src/containers/Utilities.java @@ -0,0 +1,80 @@ +package containers;//: containers/Utilities.java +// Simple demonstrations of the Collections utilities. +import java.util.*; +import static net.mindview.util.Print.*; + +public class Utilities { + static List list = Arrays.asList( + "one Two three Four five six one".split(" ")); + public static void main(String[] args) { + print(list); + print("'list' disjoint (Four)?: " + + Collections.disjoint(list, + Collections.singletonList("Four"))); + print("max: " + Collections.max(list)); + print("min: " + Collections.min(list)); + print("max w/ comparator: " + Collections.max(list, + String.CASE_INSENSITIVE_ORDER)); + print("min w/ comparator: " + Collections.min(list, + String.CASE_INSENSITIVE_ORDER)); + List sublist = + Arrays.asList("Four five six".split(" ")); + print("indexOfSubList: " + + Collections.indexOfSubList(list, sublist)); + print("lastIndexOfSubList: " + + Collections.lastIndexOfSubList(list, sublist)); + Collections.replaceAll(list, "one", "Yo"); + print("replaceAll: " + list); + Collections.reverse(list); + print("reverse: " + list); + Collections.rotate(list, 3); + print("rotate: " + list); + List source = + Arrays.asList("in the matrix".split(" ")); + Collections.copy(list, source); + print("copy: " + list); + Collections.swap(list, 0, list.size() - 1); + print("swap: " + list); + Collections.shuffle(list, new Random(47)); + print("shuffled: " + list); + Collections.fill(list, "pop"); + print("fill: " + list); + print("frequency of 'pop': " + + Collections.frequency(list, "pop")); + List dups = Collections.nCopies(3, "snap"); + print("dups: " + dups); + print("'list' disjoint 'dups'?: " + + Collections.disjoint(list, dups)); + // Getting an old-style Enumeration: + Enumeration e = Collections.enumeration(dups); + Vector v = new Vector(); + while(e.hasMoreElements()) { + v.addElement(e.nextElement()); + } + // Converting an old-style Vector + // to a List via an Enumeration: + ArrayList arrayList = + Collections.list(v.elements()); + print("arrayList: " + arrayList); + } +} /* Output: +[one, Two, three, Four, five, six, one] +'list' disjoint (Four)?: false +max: three +min: Four +max w/ comparator: Two +min w/ comparator: five +indexOfSubList: 3 +lastIndexOfSubList: 3 +replaceAll: [Yo, Two, three, Four, five, six, Yo] +reverse: [Yo, six, five, Four, three, Two, Yo] +rotate: [three, Two, Yo, Yo, six, five, Four] +copy: [in, the, matrix, Yo, six, five, Four] +swap: [Four, the, matrix, Yo, six, five, in] +shuffled: [six, matrix, the, Four, Yo, five, in] +fill: [pop, pop, pop, pop, pop, pop, pop] +frequency of 'pop': 7 +dups: [snap, snap, snap] +'list' disjoint 'dups'?: true +arrayList: [snap, snap, snap] +*///:~ diff --git a/src/containers/build.xml b/src/containers/build.xml new file mode 100644 index 0000000..fb2f18c --- /dev/null +++ b/src/containers/build.xml @@ -0,0 +1,464 @@ + + + + + + build.xml for the source code for the containers chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/control/BreakAndContinue.java b/src/control/BreakAndContinue.java new file mode 100644 index 0000000..7113920 --- /dev/null +++ b/src/control/BreakAndContinue.java @@ -0,0 +1,46 @@ +package control;//: control/BreakAndContinue.java +// Demonstrates break and continue keywords. +import static net.mindview.util.Range.*; + +public class BreakAndContinue { + public static void main(String[] args) { + for(int i = 0; i < 100; i++) { + if(i == 74) { + break; // Out of for loop + } + if(i % 9 != 0) { + continue; // Next iteration + } + System.out.print(i + " "); + } + System.out.println(); + // Using foreach: + for(int i : range(100)) { + if(i == 74) { + break; // Out of for loop + } + if(i % 9 != 0) { + continue; // Next iteration + } + System.out.print(i + " "); + } + System.out.println(); + int i = 0; + // An "infinite loop": + while(true) { + i++; + int j = i * 27; + if(j == 1269) { + break; // Out of loop + } + if(i % 10 != 0) { + continue; // Top of loop + } + System.out.print(i + " "); + } + } +} /* Output: +0 9 18 27 36 45 54 63 72 +0 9 18 27 36 45 54 63 72 +10 20 30 40 +*///:~ diff --git a/src/control/CommaOperator.java b/src/control/CommaOperator.java new file mode 100644 index 0000000..1c852de --- /dev/null +++ b/src/control/CommaOperator.java @@ -0,0 +1,14 @@ +package control;//: control/CommaOperator.java + +public class CommaOperator { + public static void main(String[] args) { + for(int i = 1, j = i + 10; i < 5; i++, j = i * 2) { + System.out.println("i = " + i + " j = " + j); + } + } +} /* Output: +i = 1 j = 11 +i = 2 j = 4 +i = 3 j = 6 +i = 4 j = 8 +*///:~ diff --git a/src/control/ForEachFloat.java b/src/control/ForEachFloat.java new file mode 100644 index 0000000..a8dc22b --- /dev/null +++ b/src/control/ForEachFloat.java @@ -0,0 +1,26 @@ +package control;//: control/ForEachFloat.java +import java.util.*; + +public class ForEachFloat { + public static void main(String[] args) { + Random rand = new Random(47); + float f[] = new float[10]; + for(int i = 0; i < 10; i++) { + f[i] = rand.nextFloat(); + } + for(float x : f) { + System.out.println(x); + } + } +} /* Output: +0.72711575 +0.39982635 +0.5309454 +0.0534122 +0.16020656 +0.57799757 +0.18847865 +0.4170137 +0.51660204 +0.73734957 +*///:~ diff --git a/src/control/ForEachInt.java b/src/control/ForEachInt.java new file mode 100644 index 0000000..0bd5b07 --- /dev/null +++ b/src/control/ForEachInt.java @@ -0,0 +1,27 @@ +package control;//: control/ForEachInt.java +import static net.mindview.util.Range.*; +import static net.mindview.util.Print.*; + +public class ForEachInt { + public static void main(String[] args) { + for(int i : range(10)) // 0..9 + { + printnb(i + " "); + } + print(); + for(int i : range(5, 10)) // 5..9 + { + printnb(i + " "); + } + print(); + for(int i : range(5, 20, 3)) // 5..20 step 3 + { + printnb(i + " "); + } + print(); + } +} /* Output: +0 1 2 3 4 5 6 7 8 9 +5 6 7 8 9 +5 8 11 14 17 +*///:~ diff --git a/src/control/ForEachString.java b/src/control/ForEachString.java new file mode 100644 index 0000000..411f249 --- /dev/null +++ b/src/control/ForEachString.java @@ -0,0 +1,11 @@ +package control;//: control/ForEachString.java + +public class ForEachString { + public static void main(String[] args) { + for(char c : "An African Swallow".toCharArray() ) { + System.out.print(c + " "); + } + } +} /* Output: +A n A f r i c a n S w a l l o w +*///:~ diff --git a/src/control/IfElse.java b/src/control/IfElse.java new file mode 100644 index 0000000..18ff47e --- /dev/null +++ b/src/control/IfElse.java @@ -0,0 +1,27 @@ +package control;//: control/IfElse.java +import static net.mindview.util.Print.*; + +public class IfElse { + static int result = 0; + static void test(int testval, int target) { + if(testval > target) { + result = +1; + } else if(testval < target) { + result = -1; + } else { + result = 0; // Match + } + } + public static void main(String[] args) { + test(10, 5); + print(result); + test(5, 10); + print(result); + test(5, 5); + print(result); + } +} /* Output: +1 +-1 +0 +*///:~ diff --git a/src/control/IfElse2.java b/src/control/IfElse2.java new file mode 100644 index 0000000..737a74e --- /dev/null +++ b/src/control/IfElse2.java @@ -0,0 +1,23 @@ +package control;//: control/IfElse2.java +import static net.mindview.util.Print.*; + +public class IfElse2 { + static int test(int testval, int target) { + if(testval > target) { + return +1; + } else if(testval < target) { + return -1; + } else { + return 0; // Match + } + } + public static void main(String[] args) { + print(test(10, 5)); + print(test(5, 10)); + print(test(5, 5)); + } +} /* Output: +1 +-1 +0 +*///:~ diff --git a/src/control/LabeledFor.java b/src/control/LabeledFor.java new file mode 100644 index 0000000..7124897 --- /dev/null +++ b/src/control/LabeledFor.java @@ -0,0 +1,62 @@ +package control;//: control/LabeledFor.java +// For loops with "labeled break" and "labeled continue." +import static net.mindview.util.Print.*; + +public class LabeledFor { + public static void main(String[] args) { + int i = 0; + outer: // Can't have statements here + for(; true ;) { // infinite loop + inner: // Can't have statements here + for(; i < 10; i++) { + print("i = " + i); + if(i == 2) { + print("continue"); + continue; + } + if(i == 3) { + print("break"); + i++; // Otherwise i never + // gets incremented. + break; + } + if(i == 7) { + print("continue outer"); + i++; // Otherwise i never + // gets incremented. + continue outer; + } + if(i == 8) { + print("break outer"); + break outer; + } + for(int k = 0; k < 5; k++) { + if(k == 3) { + print("continue inner"); + continue inner; + } + } + } + } + // Can't break or continue to labels here + } +} /* Output: +i = 0 +continue inner +i = 1 +continue inner +i = 2 +continue +i = 3 +break +i = 4 +continue inner +i = 5 +continue inner +i = 6 +continue inner +i = 7 +continue outer +i = 8 +break outer +*///:~ diff --git a/src/control/LabeledWhile.java b/src/control/LabeledWhile.java new file mode 100644 index 0000000..4170cac --- /dev/null +++ b/src/control/LabeledWhile.java @@ -0,0 +1,48 @@ +package control;//: control/LabeledWhile.java +// While loops with "labeled break" and "labeled continue." +import static net.mindview.util.Print.*; + +public class LabeledWhile { + public static void main(String[] args) { + int i = 0; + outer: + while(true) { + print("Outer while loop"); + while(true) { + i++; + print("i = " + i); + if(i == 1) { + print("continue"); + continue; + } + if(i == 3) { + print("continue outer"); + continue outer; + } + if(i == 5) { + print("break"); + break; + } + if(i == 7) { + print("break outer"); + break outer; + } + } + } + } +} /* Output: +Outer while loop +i = 1 +continue +i = 2 +i = 3 +continue outer +Outer while loop +i = 4 +i = 5 +break +Outer while loop +i = 6 +i = 7 +break outer +*///:~ diff --git a/src/control/ListCharacters.java b/src/control/ListCharacters.java new file mode 100644 index 0000000..0efed8a --- /dev/null +++ b/src/control/ListCharacters.java @@ -0,0 +1,26 @@ +package control;//: control/ListCharacters.java +// Demonstrates "for" loop by listing +// all the lowercase ASCII letters. + +public class ListCharacters { + public static void main(String[] args) { + for(char c = 0; c < 128; c++) { + if(Character.isLowerCase(c)) { + System.out.println("value: " + (int) c + + " character: " + c); + } + } + } +} /* Output: +value: 97 character: a +value: 98 character: b +value: 99 character: c +value: 100 character: d +value: 101 character: e +value: 102 character: f +value: 103 character: g +value: 104 character: h +value: 105 character: i +value: 106 character: j +... +*///:~ diff --git a/src/control/VowelsAndConsonants.java b/src/control/VowelsAndConsonants.java new file mode 100644 index 0000000..8bbd86a --- /dev/null +++ b/src/control/VowelsAndConsonants.java @@ -0,0 +1,41 @@ +package control;//: control/VowelsAndConsonants.java +// Demonstrates the switch statement. +import java.util.*; +import static net.mindview.util.Print.*; + +public class VowelsAndConsonants { + public static void main(String[] args) { + Random rand = new Random(47); + for(int i = 0; i < 100; i++) { + int c = rand.nextInt(26) + 'a'; + printnb((char)c + ", " + c + ": "); + switch(c) { + case 'a': + case 'e': + case 'i': + case 'o': + case 'u': print("vowel"); + break; + case 'y': + case 'w': print("Sometimes a vowel"); + break; + default: print("consonant"); + } + } + } +} /* Output: +y, 121: Sometimes a vowel +n, 110: consonant +z, 122: consonant +b, 98: consonant +r, 114: consonant +n, 110: consonant +y, 121: Sometimes a vowel +g, 103: consonant +c, 99: consonant +f, 102: consonant +o, 111: vowel +w, 119: Sometimes a vowel +z, 122: consonant +... +*///:~ diff --git a/src/control/WhileTest.java b/src/control/WhileTest.java new file mode 100644 index 0000000..7bf2c8f --- /dev/null +++ b/src/control/WhileTest.java @@ -0,0 +1,16 @@ +package control;//: control/WhileTest.java +// Demonstrates the while loop. + +public class WhileTest { + static boolean condition() { + boolean result = Math.random() < 0.99; + System.out.print(result + ", "); + return result; + } + public static void main(String[] args) { + while(condition()) { + System.out.println("Inside 'while'"); + } + System.out.println("Exited 'while'"); + } +} /* (Execute to see output) *///:~ diff --git a/src/control/build.xml b/src/control/build.xml new file mode 100644 index 0000000..4c7bcab --- /dev/null +++ b/src/control/build.xml @@ -0,0 +1,190 @@ + + + + + + build.xml for the source code for the control chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/enumerated/AlarmPoints.java b/src/enumerated/AlarmPoints.java new file mode 100644 index 0000000..2ab3be8 --- /dev/null +++ b/src/enumerated/AlarmPoints.java @@ -0,0 +1,6 @@ +//: enumerated/AlarmPoints.java +package enumerated; +public enum AlarmPoints { + STAIR1, STAIR2, LOBBY, OFFICE1, OFFICE2, OFFICE3, + OFFICE4, BATHROOM, UTILITY, KITCHEN +} ///:~ diff --git a/src/enumerated/BigEnumSet.java b/src/enumerated/BigEnumSet.java new file mode 100644 index 0000000..21f2309 --- /dev/null +++ b/src/enumerated/BigEnumSet.java @@ -0,0 +1,18 @@ +package enumerated;//: enumerated/BigEnumSet.java +import java.util.*; + +public class BigEnumSet { + enum Big { A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, + A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, + A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, + A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, + A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, + A55, A56, A57, A58, A59, A60, A61, A62, A63, A64, A65, + A66, A67, A68, A69, A70, A71, A72, A73, A74, A75 } + public static void main(String[] args) { + EnumSet bigEnumSet = EnumSet.allOf(Big.class); + System.out.println(bigEnumSet); + } +} /* Output: +[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, A70, A71, A72, A73, A74, A75] +*///:~ diff --git a/src/enumerated/Burrito.java b/src/enumerated/Burrito.java new file mode 100644 index 0000000..b9b1961 --- /dev/null +++ b/src/enumerated/Burrito.java @@ -0,0 +1,18 @@ +//: enumerated/Burrito.java +package enumerated; +import static enumerated.Spiciness.*; + +public class Burrito { + Spiciness degree; + public Burrito(Spiciness degree) { this.degree = degree;} + public String toString() { return "Burrito is "+ degree;} + public static void main(String[] args) { + System.out.println(new Burrito(NOT)); + System.out.println(new Burrito(MEDIUM)); + System.out.println(new Burrito(HOT)); + } +} /* Output: +Burrito is NOT +Burrito is MEDIUM +Burrito is HOT +*///:~ diff --git a/src/enumerated/CarWash.java b/src/enumerated/CarWash.java new file mode 100644 index 0000000..fd112eb --- /dev/null +++ b/src/enumerated/CarWash.java @@ -0,0 +1,60 @@ +package enumerated;//: enumerated/CarWash.java +import java.util.*; +import static net.mindview.util.Print.*; + +public class CarWash { + public enum Cycle { + UNDERBODY { + void action() { print("Spraying the underbody"); } + }, + WHEELWASH { + void action() { print("Washing the wheels"); } + }, + PREWASH { + void action() { print("Loosening the dirt"); } + }, + BASIC { + void action() { print("The basic wash"); } + }, + HOTWAX { + void action() { print("Applying hot wax"); } + }, + RINSE { + void action() { print("Rinsing"); } + }, + BLOWDRY { + void action() { print("Blowing dry"); } + }; + abstract void action(); + } + EnumSet cycles = + EnumSet.of(Cycle.BASIC, Cycle.RINSE); + public void add(Cycle cycle) { cycles.add(cycle); } + public void washCar() { + for(Cycle c : cycles) { + c.action(); + } + } + public String toString() { return cycles.toString(); } + public static void main(String[] args) { + CarWash wash = new CarWash(); + print(wash); + wash.washCar(); + // Order of addition is unimportant: + wash.add(Cycle.BLOWDRY); + wash.add(Cycle.BLOWDRY); // Duplicates ignored + wash.add(Cycle.RINSE); + wash.add(Cycle.HOTWAX); + print(wash); + wash.washCar(); + } +} /* Output: +[BASIC, RINSE] +The basic wash +Rinsing +[BASIC, HOTWAX, RINSE, BLOWDRY] +The basic wash +Applying hot wax +Rinsing +Blowing dry +*///:~ diff --git a/src/enumerated/Competitor.java b/src/enumerated/Competitor.java new file mode 100644 index 0000000..7d82546 --- /dev/null +++ b/src/enumerated/Competitor.java @@ -0,0 +1,7 @@ +//: enumerated/Competitor.java +// Switching one enum on another. +package enumerated; + +public interface Competitor> { + Outcome compete(T competitor); +} ///:~ diff --git a/src/enumerated/ConstantSpecificMethod.java b/src/enumerated/ConstantSpecificMethod.java new file mode 100644 index 0000000..705b67d --- /dev/null +++ b/src/enumerated/ConstantSpecificMethod.java @@ -0,0 +1,28 @@ +package enumerated;//: enumerated/ConstantSpecificMethod.java +import java.util.*; +import java.text.*; + +public enum ConstantSpecificMethod { + DATE_TIME { + String getInfo() { + return + DateFormat.getDateInstance().format(new Date()); + } + }, + CLASSPATH { + String getInfo() { + return System.getenv("CLASSPATH"); + } + }, + VERSION { + String getInfo() { + return System.getProperty("java.version"); + } + }; + abstract String getInfo(); + public static void main(String[] args) { + for(ConstantSpecificMethod csm : values()) { + System.out.println(csm.getInfo()); + } + } +} /* (Execute to see output) *///:~ diff --git a/src/enumerated/EnumClass.java b/src/enumerated/EnumClass.java new file mode 100644 index 0000000..1fc16d7 --- /dev/null +++ b/src/enumerated/EnumClass.java @@ -0,0 +1,43 @@ +package enumerated;//: enumerated/EnumClass.java +// Capabilities of the Enum class +import static net.mindview.util.Print.*; + +enum Shrubbery { GROUND, CRAWLING, HANGING } + +public class EnumClass { + public static void main(String[] args) { + for(Shrubbery s : Shrubbery.values()) { + print(s + " ordinal: " + s.ordinal()); + printnb(s.compareTo(Shrubbery.CRAWLING) + " "); + printnb(s.equals(Shrubbery.CRAWLING) + " "); + print(s == Shrubbery.CRAWLING); + print(s.getDeclaringClass()); + print(s.name()); + print("----------------------"); + } + // Produce an enum value from a string name: + for(String s : "HANGING CRAWLING GROUND".split(" ")) { + Shrubbery shrub = Enum.valueOf(Shrubbery.class, s); + print(shrub); + } + } +} /* Output: +GROUND ordinal: 0 +-1 false false +class Shrubbery +GROUND +---------------------- +CRAWLING ordinal: 1 +0 true true +class Shrubbery +CRAWLING +---------------------- +HANGING ordinal: 2 +1 false false +class Shrubbery +HANGING +---------------------- +HANGING +CRAWLING +GROUND +*///:~ diff --git a/src/enumerated/EnumMaps.java b/src/enumerated/EnumMaps.java new file mode 100644 index 0000000..59d122f --- /dev/null +++ b/src/enumerated/EnumMaps.java @@ -0,0 +1,34 @@ +//: enumerated/EnumMaps.java +// Basics of EnumMaps. +package enumerated; +import java.util.*; +import static enumerated.AlarmPoints.*; +import static net.mindview.util.Print.*; + +interface Command { void action(); } + +public class EnumMaps { + public static void main(String[] args) { + EnumMap em = + new EnumMap(AlarmPoints.class); + em.put(KITCHEN, new Command() { + public void action() { print("Kitchen fire!"); } + }); + em.put(BATHROOM, new Command() { + public void action() { print("Bathroom alert!"); } + }); + for(Map.Entry e : em.entrySet()) { + printnb(e.getKey() + ": "); + e.getValue().action(); + } + try { // If there's no value for a particular key: + em.get(UTILITY).action(); + } catch(Exception e) { + print(e); + } + } +} /* Output: +BATHROOM: Bathroom alert! +KITCHEN: Kitchen fire! +java.lang.NullPointerException +*///:~ diff --git a/src/enumerated/EnumSets.java b/src/enumerated/EnumSets.java new file mode 100644 index 0000000..0f04b7a --- /dev/null +++ b/src/enumerated/EnumSets.java @@ -0,0 +1,30 @@ +//: enumerated/EnumSets.java +// Operations on EnumSets +package enumerated; +import java.util.*; +import static enumerated.AlarmPoints.*; +import static net.mindview.util.Print.*; + +public class EnumSets { + public static void main(String[] args) { + EnumSet points = + EnumSet.noneOf(AlarmPoints.class); // Empty set + points.add(BATHROOM); + print(points); + points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN)); + print(points); + points = EnumSet.allOf(AlarmPoints.class); + points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN)); + print(points); + points.removeAll(EnumSet.range(OFFICE1, OFFICE4)); + print(points); + points = EnumSet.complementOf(points); + print(points); + } +} /* Output: +[BATHROOM] +[STAIR1, STAIR2, BATHROOM, KITCHEN] +[LOBBY, OFFICE1, OFFICE2, OFFICE3, OFFICE4, BATHROOM, UTILITY] +[LOBBY, BATHROOM, UTILITY] +[STAIR1, STAIR2, OFFICE1, OFFICE2, OFFICE3, OFFICE4, KITCHEN] +*///:~ diff --git a/src/enumerated/Input.java b/src/enumerated/Input.java new file mode 100644 index 0000000..bd7b6d1 --- /dev/null +++ b/src/enumerated/Input.java @@ -0,0 +1,27 @@ +//: enumerated/Input.java +package enumerated; +import java.util.*; + +public enum Input { + NICKEL(5), DIME(10), QUARTER(25), DOLLAR(100), + TOOTHPASTE(200), CHIPS(75), SODA(100), SOAP(50), + ABORT_TRANSACTION { + public int amount() { // Disallow + throw new RuntimeException("ABORT.amount()"); + } + }, + STOP { // This must be the last instance. + public int amount() { // Disallow + throw new RuntimeException("SHUT_DOWN.amount()"); + } + }; + int value; // In cents + Input(int value) { this.value = value; } + Input() {} + int amount() { return value; }; // In cents + static Random rand = new Random(47); + public static Input randomSelection() { + // Don't include STOP: + return values()[rand.nextInt(values().length - 1)]; + } +} ///:~ diff --git a/src/enumerated/NonEnum.java b/src/enumerated/NonEnum.java new file mode 100644 index 0000000..95b9c52 --- /dev/null +++ b/src/enumerated/NonEnum.java @@ -0,0 +1,16 @@ +package enumerated;//: enumerated/NonEnum.java + +public class NonEnum { + public static void main(String[] args) { + Class intClass = Integer.class; + try { + for(Object en : intClass.getEnumConstants()) { + System.out.println(en); + } + } catch(Exception e) { + System.out.println(e); + } + } +} /* Output: +java.lang.NullPointerException +*///:~ diff --git a/src/enumerated/NotClasses.java b/src/enumerated/NotClasses.java new file mode 100644 index 0000000..8613b14 --- /dev/null +++ b/src/enumerated/NotClasses.java @@ -0,0 +1,23 @@ +package enumerated;//: enumerated/NotClasses.java +// {Exec: javap -c LikeClasses} +import static net.mindview.util.Print.*; + +enum LikeClasses { + WINKEN { void behavior() { print("Behavior1"); } }, + BLINKEN { void behavior() { print("Behavior2"); } }, + NOD { void behavior() { print("Behavior3"); } }; + abstract void behavior(); +} + +public class NotClasses { + // void f1(LikeClasses.WINKEN instance) {} // Nope +} /* Output: +Compiled from "NotClasses.java" +abstract class LikeClasses extends java.lang.Enum{ +public static final LikeClasses WINKEN; + +public static final LikeClasses BLINKEN; + +public static final LikeClasses NOD; +... +*///:~ diff --git a/src/enumerated/Outcome.java b/src/enumerated/Outcome.java new file mode 100644 index 0000000..d9ada31 --- /dev/null +++ b/src/enumerated/Outcome.java @@ -0,0 +1,3 @@ +//: enumerated/Outcome.java +package enumerated; +public enum Outcome { WIN, LOSE, DRAW } ///:~ diff --git a/src/enumerated/OverrideConstantSpecific.java b/src/enumerated/OverrideConstantSpecific.java new file mode 100644 index 0000000..7bdc2da --- /dev/null +++ b/src/enumerated/OverrideConstantSpecific.java @@ -0,0 +1,20 @@ +package enumerated;//: enumerated/OverrideConstantSpecific.java +import static net.mindview.util.Print.*; + +public enum OverrideConstantSpecific { + NUT, BOLT, + WASHER { + void f() { print("Overridden method"); } + }; + void f() { print("default behavior"); } + public static void main(String[] args) { + for(OverrideConstantSpecific ocs : values()) { + printnb(ocs + ": "); + ocs.f(); + } + } +} /* Output: +NUT: default behavior +BOLT: default behavior +WASHER: Overridden method +*///:~ diff --git a/src/enumerated/OzWitch.java b/src/enumerated/OzWitch.java new file mode 100644 index 0000000..044d5a9 --- /dev/null +++ b/src/enumerated/OzWitch.java @@ -0,0 +1,28 @@ +package enumerated;//: enumerated/OzWitch.java +// The witches in the land of Oz. +import static net.mindview.util.Print.*; + +public enum OzWitch { + // Instances must be defined first, before methods: + WEST("Miss Gulch, aka the Wicked Witch of the West"), + NORTH("Glinda, the Good Witch of the North"), + EAST("Wicked Witch of the East, wearer of the Ruby " + + "Slippers, crushed by Dorothy's house"), + SOUTH("Good by inference, but missing"); + private String description; + // Constructor must be package or private access: + private OzWitch(String description) { + this.description = description; + } + public String getDescription() { return description; } + public static void main(String[] args) { + for(OzWitch witch : OzWitch.values()) { + print(witch + ": " + witch.getDescription()); + } + } +} /* Output: +WEST: Miss Gulch, aka the Wicked Witch of the West +NORTH: Glinda, the Good Witch of the North +EAST: Wicked Witch of the East, wearer of the Ruby Slippers, crushed by Dorothy's house +SOUTH: Good by inference, but missing +*///:~ diff --git a/src/enumerated/PostOffice.java b/src/enumerated/PostOffice.java new file mode 100644 index 0000000..50c403d --- /dev/null +++ b/src/enumerated/PostOffice.java @@ -0,0 +1,154 @@ +package enumerated;//: enumerated/PostOffice.java +// Modeling a post office. +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +class Mail { + // The NO's lower the probability of random selection: + enum GeneralDelivery {YES,NO1,NO2,NO3,NO4,NO5} + enum Scannability {UNSCANNABLE,YES1,YES2,YES3,YES4} + enum Readability {ILLEGIBLE,YES1,YES2,YES3,YES4} + enum Address {INCORRECT,OK1,OK2,OK3,OK4,OK5,OK6} + enum ReturnAddress {MISSING,OK1,OK2,OK3,OK4,OK5} + GeneralDelivery generalDelivery; + Scannability scannability; + Readability readability; + Address address; + ReturnAddress returnAddress; + static long counter = 0; + long id = counter++; + public String toString() { return "Mail " + id; } + public String details() { + return toString() + + ", General Delivery: " + generalDelivery + + ", Address Scanability: " + scannability + + ", Address Readability: " + readability + + ", Address Address: " + address + + ", Return address: " + returnAddress; + } + // Generate test Mail: + public static Mail randomMail() { + Mail m = new Mail(); + m.generalDelivery= Enums.random(GeneralDelivery.class); + m.scannability = Enums.random(Scannability.class); + m.readability = Enums.random(Readability.class); + m.address = Enums.random(Address.class); + m.returnAddress = Enums.random(ReturnAddress.class); + return m; + } + public static Iterable generator(final int count) { + return new Iterable() { + int n = count; + public Iterator iterator() { + return new Iterator() { + public boolean hasNext() { return n-- > 0; } + public Mail next() { return randomMail(); } + public void remove() { // Not implemented + throw new UnsupportedOperationException(); + } + }; + } + }; + } +} + +public class PostOffice { + enum MailHandler { + GENERAL_DELIVERY { + boolean handle(Mail m) { + switch(m.generalDelivery) { + case YES: + print("Using general delivery for " + m); + return true; + default: return false; + } + } + }, + MACHINE_SCAN { + boolean handle(Mail m) { + switch(m.scannability) { + case UNSCANNABLE: return false; + default: + switch(m.address) { + case INCORRECT: return false; + default: + print("Delivering "+ m + " automatically"); + return true; + } + } + } + }, + VISUAL_INSPECTION { + boolean handle(Mail m) { + switch(m.readability) { + case ILLEGIBLE: return false; + default: + switch(m.address) { + case INCORRECT: return false; + default: + print("Delivering " + m + " normally"); + return true; + } + } + } + }, + RETURN_TO_SENDER { + boolean handle(Mail m) { + switch(m.returnAddress) { + case MISSING: return false; + default: + print("Returning " + m + " to sender"); + return true; + } + } + }; + abstract boolean handle(Mail m); + } + static void handle(Mail m) { + for(MailHandler handler : MailHandler.values()) { + if(handler.handle(m)) { + return; + } + } + print(m + " is a dead letter"); + } + public static void main(String[] args) { + for(Mail mail : Mail.generator(10)) { + print(mail.details()); + handle(mail); + print("*****"); + } + } +} /* Output: +Mail 0, General Delivery: NO2, Address Scanability: UNSCANNABLE, Address Readability: YES3, Address Address: OK1, Return address: OK1 +Delivering Mail 0 normally +***** +Mail 1, General Delivery: NO5, Address Scanability: YES3, Address Readability: ILLEGIBLE, Address Address: OK5, Return address: OK1 +Delivering Mail 1 automatically +***** +Mail 2, General Delivery: YES, Address Scanability: YES3, Address Readability: YES1, Address Address: OK1, Return address: OK5 +Using general delivery for Mail 2 +***** +Mail 3, General Delivery: NO4, Address Scanability: YES3, Address Readability: YES1, Address Address: INCORRECT, Return address: OK4 +Returning Mail 3 to sender +***** +Mail 4, General Delivery: NO4, Address Scanability: UNSCANNABLE, Address Readability: YES1, Address Address: INCORRECT, Return address: OK2 +Returning Mail 4 to sender +***** +Mail 5, General Delivery: NO3, Address Scanability: YES1, Address Readability: ILLEGIBLE, Address Address: OK4, Return address: OK2 +Delivering Mail 5 automatically +***** +Mail 6, General Delivery: YES, Address Scanability: YES4, Address Readability: ILLEGIBLE, Address Address: OK4, Return address: OK4 +Using general delivery for Mail 6 +***** +Mail 7, General Delivery: YES, Address Scanability: YES3, Address Readability: YES4, Address Address: OK2, Return address: MISSING +Using general delivery for Mail 7 +***** +Mail 8, General Delivery: NO3, Address Scanability: YES1, Address Readability: YES3, Address Address: INCORRECT, Return address: MISSING +Mail 8 is a dead letter +***** +Mail 9, General Delivery: NO1, Address Scanability: UNSCANNABLE, Address Readability: YES2, Address Address: OK1, Return address: OK4 +Delivering Mail 9 normally +***** +*///:~ diff --git a/src/enumerated/RandomTest.java b/src/enumerated/RandomTest.java new file mode 100644 index 0000000..094ac8e --- /dev/null +++ b/src/enumerated/RandomTest.java @@ -0,0 +1,15 @@ +package enumerated;//: enumerated/RandomTest.java +import net.mindview.util.*; + +enum Activity { SITTING, LYING, STANDING, HOPPING, + RUNNING, DODGING, JUMPING, FALLING, FLYING } + +public class RandomTest { + public static void main(String[] args) { + for(int i = 0; i < 20; i++) { + System.out.print(Enums.random(Activity.class) + " "); + } + } +} /* Output: +STANDING FLYING RUNNING STANDING RUNNING STANDING LYING DODGING SITTING RUNNING HOPPING HOPPING HOPPING RUNNING STANDING LYING FALLING RUNNING FLYING LYING +*///:~ diff --git a/src/enumerated/Reflection.java b/src/enumerated/Reflection.java new file mode 100644 index 0000000..2b1bc41 --- /dev/null +++ b/src/enumerated/Reflection.java @@ -0,0 +1,60 @@ +package enumerated;//: enumerated/Reflection.java +// Analyzing enums using reflection. +import java.lang.reflect.*; +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +enum Explore { HERE, THERE } + +public class Reflection { + public static Set analyze(Class enumClass) { + print("----- Analyzing " + enumClass + " -----"); + print("Interfaces:"); + for(Type t : enumClass.getGenericInterfaces()) { + print(t); + } + print("Base: " + enumClass.getSuperclass()); + print("Methods: "); + Set methods = new TreeSet(); + for(Method m : enumClass.getMethods()) { + methods.add(m.getName()); + } + print(methods); + return methods; + } + public static void main(String[] args) { + Set exploreMethods = analyze(Explore.class); + Set enumMethods = analyze(Enum.class); + print("Explore.containsAll(Enum)? " + + exploreMethods.containsAll(enumMethods)); + printnb("Explore.removeAll(Enum): "); + exploreMethods.removeAll(enumMethods); + print(exploreMethods); + // Decompile the code for the enum: + OSExecute.command("javap Explore"); + } +} /* Output: +----- Analyzing class Explore ----- +Interfaces: +Base: class java.lang.Enum +Methods: +[compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, values, wait] +----- Analyzing class java.lang.Enum ----- +Interfaces: +java.lang.Comparable +interface java.io.Serializable +Base: class java.lang.Object +Methods: +[compareTo, equals, getClass, getDeclaringClass, hashCode, name, notify, notifyAll, ordinal, toString, valueOf, wait] +Explore.containsAll(Enum)? true +Explore.removeAll(Enum): [values] +Compiled from "Reflection.java" +final class Explore extends java.lang.Enum{ + public static final Explore HERE; + public static final Explore THERE; + public static final Explore[] values(); + public static Explore valueOf(java.lang.String); + static {}; +} +*///:~ diff --git a/src/enumerated/RoShamBo.java b/src/enumerated/RoShamBo.java new file mode 100644 index 0000000..31ba463 --- /dev/null +++ b/src/enumerated/RoShamBo.java @@ -0,0 +1,19 @@ +//: enumerated/RoShamBo.java +// Common tools for RoShamBo examples. +package enumerated; +import net.mindview.util.*; + +public class RoShamBo { + public static > + void match(T a, T b) { + System.out.println( + a + " vs. " + b + ": " + a.compete(b)); + } + public static & Competitor> + void play(Class rsbClass, int size) { + for(int i = 0; i < size; i++) { + match( + Enums.random(rsbClass),Enums.random(rsbClass)); + } + } +} ///:~ diff --git a/src/enumerated/RoShamBo1.java b/src/enumerated/RoShamBo1.java new file mode 100644 index 0000000..1034980 --- /dev/null +++ b/src/enumerated/RoShamBo1.java @@ -0,0 +1,79 @@ +//: enumerated/RoShamBo1.java +// Demonstration of multiple dispatching. +package enumerated; +import java.util.*; +import static enumerated.Outcome.*; + +interface Item { + Outcome compete(Item it); + Outcome eval(Paper p); + Outcome eval(Scissors s); + Outcome eval(Rock r); +} + +class Paper implements Item { + public Outcome compete(Item it) { return it.eval(this); } + public Outcome eval(Paper p) { return DRAW; } + public Outcome eval(Scissors s) { return WIN; } + public Outcome eval(Rock r) { return LOSE; } + public String toString() { return "Paper"; } +} + +class Scissors implements Item { + public Outcome compete(Item it) { return it.eval(this); } + public Outcome eval(Paper p) { return LOSE; } + public Outcome eval(Scissors s) { return DRAW; } + public Outcome eval(Rock r) { return WIN; } + public String toString() { return "Scissors"; } +} + +class Rock implements Item { + public Outcome compete(Item it) { return it.eval(this); } + public Outcome eval(Paper p) { return WIN; } + public Outcome eval(Scissors s) { return LOSE; } + public Outcome eval(Rock r) { return DRAW; } + public String toString() { return "Rock"; } +} + +public class RoShamBo1 { + static final int SIZE = 20; + private static Random rand = new Random(47); + public static Item newItem() { + switch(rand.nextInt(3)) { + default: + case 0: return new Scissors(); + case 1: return new Paper(); + case 2: return new Rock(); + } + } + public static void match(Item a, Item b) { + System.out.println( + a + " vs. " + b + ": " + a.compete(b)); + } + public static void main(String[] args) { + for(int i = 0; i < SIZE; i++) { + match(newItem(), newItem()); + } + } +} /* Output: +Rock vs. Rock: DRAW +Paper vs. Rock: WIN +Paper vs. Rock: WIN +Paper vs. Rock: WIN +Scissors vs. Paper: WIN +Scissors vs. Scissors: DRAW +Scissors vs. Paper: WIN +Rock vs. Paper: LOSE +Paper vs. Paper: DRAW +Rock vs. Paper: LOSE +Paper vs. Scissors: LOSE +Paper vs. Scissors: LOSE +Rock vs. Scissors: WIN +Rock vs. Paper: LOSE +Paper vs. Rock: WIN +Scissors vs. Paper: WIN +Paper vs. Scissors: LOSE +Paper vs. Scissors: LOSE +Paper vs. Scissors: LOSE +Paper vs. Scissors: LOSE +*///:~ diff --git a/src/enumerated/RoShamBo2.java b/src/enumerated/RoShamBo2.java new file mode 100644 index 0000000..1deee4c --- /dev/null +++ b/src/enumerated/RoShamBo2.java @@ -0,0 +1,48 @@ +//: enumerated/RoShamBo2.java +// Switching one enum on another. +package enumerated; +import static enumerated.Outcome.*; + +public enum RoShamBo2 implements Competitor { + PAPER(DRAW, LOSE, WIN), + SCISSORS(WIN, DRAW, LOSE), + ROCK(LOSE, WIN, DRAW); + private Outcome vPAPER, vSCISSORS, vROCK; + RoShamBo2(Outcome paper,Outcome scissors,Outcome rock) { + this.vPAPER = paper; + this.vSCISSORS = scissors; + this.vROCK = rock; + } + public Outcome compete(RoShamBo2 it) { + switch(it) { + default: + case PAPER: return vPAPER; + case SCISSORS: return vSCISSORS; + case ROCK: return vROCK; + } + } + public static void main(String[] args) { + RoShamBo.play(RoShamBo2.class, 20); + } +} /* Output: +ROCK vs. ROCK: DRAW +SCISSORS vs. ROCK: LOSE +SCISSORS vs. ROCK: LOSE +SCISSORS vs. ROCK: LOSE +PAPER vs. SCISSORS: LOSE +PAPER vs. PAPER: DRAW +PAPER vs. SCISSORS: LOSE +ROCK vs. SCISSORS: WIN +SCISSORS vs. SCISSORS: DRAW +ROCK vs. SCISSORS: WIN +SCISSORS vs. PAPER: WIN +SCISSORS vs. PAPER: WIN +ROCK vs. PAPER: LOSE +ROCK vs. SCISSORS: WIN +SCISSORS vs. ROCK: LOSE +PAPER vs. SCISSORS: LOSE +SCISSORS vs. PAPER: WIN +SCISSORS vs. PAPER: WIN +SCISSORS vs. PAPER: WIN +SCISSORS vs. PAPER: WIN +*///:~ diff --git a/src/enumerated/RoShamBo3.java b/src/enumerated/RoShamBo3.java new file mode 100644 index 0000000..d0bcc15 --- /dev/null +++ b/src/enumerated/RoShamBo3.java @@ -0,0 +1,41 @@ +//: enumerated/RoShamBo3.java +// Using constant-specific methods. +package enumerated; +import static enumerated.Outcome.*; + +public enum RoShamBo3 implements Competitor { + PAPER { + public Outcome compete(RoShamBo3 it) { + switch(it) { + default: // To placate the compiler + case PAPER: return DRAW; + case SCISSORS: return LOSE; + case ROCK: return WIN; + } + } + }, + SCISSORS { + public Outcome compete(RoShamBo3 it) { + switch(it) { + default: + case PAPER: return WIN; + case SCISSORS: return DRAW; + case ROCK: return LOSE; + } + } + }, + ROCK { + public Outcome compete(RoShamBo3 it) { + switch(it) { + default: + case PAPER: return LOSE; + case SCISSORS: return WIN; + case ROCK: return DRAW; + } + } + }; + public abstract Outcome compete(RoShamBo3 it); + public static void main(String[] args) { + RoShamBo.play(RoShamBo3.class, 20); + } +} /* Same output as RoShamBo2.java *///:~ diff --git a/src/enumerated/RoShamBo4.java b/src/enumerated/RoShamBo4.java new file mode 100644 index 0000000..3db2098 --- /dev/null +++ b/src/enumerated/RoShamBo4.java @@ -0,0 +1,28 @@ +//: enumerated/RoShamBo4.java +package enumerated; + +public enum RoShamBo4 implements Competitor { + ROCK { + public Outcome compete(RoShamBo4 opponent) { + return compete(SCISSORS, opponent); + } + }, + SCISSORS { + public Outcome compete(RoShamBo4 opponent) { + return compete(PAPER, opponent); + } + }, + PAPER { + public Outcome compete(RoShamBo4 opponent) { + return compete(ROCK, opponent); + } + }; + Outcome compete(RoShamBo4 loser, RoShamBo4 opponent) { + return ((opponent == this) ? Outcome.DRAW + : ((opponent == loser) ? Outcome.WIN + : Outcome.LOSE)); + } + public static void main(String[] args) { + RoShamBo.play(RoShamBo4.class, 20); + } +} /* Same output as RoShamBo2.java *///:~ diff --git a/src/enumerated/RoShamBo5.java b/src/enumerated/RoShamBo5.java new file mode 100644 index 0000000..9f7fd39 --- /dev/null +++ b/src/enumerated/RoShamBo5.java @@ -0,0 +1,35 @@ +//: enumerated/RoShamBo5.java +// Multiple dispatching using an EnumMap of EnumMaps. +package enumerated; +import java.util.*; +import static enumerated.Outcome.*; + +enum RoShamBo5 implements Competitor { + PAPER, SCISSORS, ROCK; + static EnumMap> + table = new EnumMap>(RoShamBo5.class); + static { + for(RoShamBo5 it : RoShamBo5.values()) { + table.put(it, + new EnumMap(RoShamBo5.class)); + } + initRow(PAPER, DRAW, LOSE, WIN); + initRow(SCISSORS, WIN, DRAW, LOSE); + initRow(ROCK, LOSE, WIN, DRAW); + } + static void initRow(RoShamBo5 it, + Outcome vPAPER, Outcome vSCISSORS, Outcome vROCK) { + EnumMap row = + RoShamBo5.table.get(it); + row.put(RoShamBo5.PAPER, vPAPER); + row.put(RoShamBo5.SCISSORS, vSCISSORS); + row.put(RoShamBo5.ROCK, vROCK); + } + public Outcome compete(RoShamBo5 it) { + return table.get(this).get(it); + } + public static void main(String[] args) { + RoShamBo.play(RoShamBo5.class, 20); + } +} /* Same output as RoShamBo2.java *///:~ diff --git a/src/enumerated/RoShamBo6.java b/src/enumerated/RoShamBo6.java new file mode 100644 index 0000000..6b9dc0a --- /dev/null +++ b/src/enumerated/RoShamBo6.java @@ -0,0 +1,19 @@ +//: enumerated/RoShamBo6.java +// Enums using "tables" instead of multiple dispatch. +package enumerated; +import static enumerated.Outcome.*; + +enum RoShamBo6 implements Competitor { + PAPER, SCISSORS, ROCK; + private static Outcome[][] table = { + { DRAW, LOSE, WIN }, // PAPER + { WIN, DRAW, LOSE }, // SCISSORS + { LOSE, WIN, DRAW }, // ROCK + }; + public Outcome compete(RoShamBo6 other) { + return table[this.ordinal()][other.ordinal()]; + } + public static void main(String[] args) { + RoShamBo.play(RoShamBo6.class, 20); + } +} ///:~ diff --git a/src/enumerated/SecurityCategory.java b/src/enumerated/SecurityCategory.java new file mode 100644 index 0000000..eb7027c --- /dev/null +++ b/src/enumerated/SecurityCategory.java @@ -0,0 +1,37 @@ +package enumerated;//: enumerated/SecurityCategory.java +// More succinct subcategorization of enums. +import net.mindview.util.*; + +enum SecurityCategory { + STOCK(Security.Stock.class), BOND(Security.Bond.class); + Security[] values; + SecurityCategory(Class kind) { + values = kind.getEnumConstants(); + } + interface Security { + enum Stock implements Security { SHORT, LONG, MARGIN } + enum Bond implements Security { MUNICIPAL, JUNK } + } + public Security randomSelection() { + return Enums.random(values); + } + public static void main(String[] args) { + for(int i = 0; i < 10; i++) { + SecurityCategory category = + Enums.random(SecurityCategory.class); + System.out.println(category + ": " + + category.randomSelection()); + } + } +} /* Output: +BOND: MUNICIPAL +BOND: MUNICIPAL +STOCK: MARGIN +STOCK: MARGIN +BOND: JUNK +STOCK: SHORT +STOCK: LONG +STOCK: LONG +BOND: MUNICIPAL +BOND: JUNK +*///:~ diff --git a/src/enumerated/SpaceShip.java b/src/enumerated/SpaceShip.java new file mode 100644 index 0000000..0944e40 --- /dev/null +++ b/src/enumerated/SpaceShip.java @@ -0,0 +1,23 @@ +package enumerated; + +//: enumerated/SpaceShip.java +public enum SpaceShip { + SCOUT, CARGO, TRANSPORT, CRUISER, BATTLESHIP, MOTHERSHIP; + public String toString() { + String id = name(); + String lower = id.substring(1).toLowerCase(); + return id.charAt(0) + lower; + } + public static void main(String[] args) { + for(SpaceShip s : values()) { + System.out.println(s); + } + } +} /* Output: +Scout +Cargo +Transport +Cruiser +Battleship +Mothership +*///:~ diff --git a/src/enumerated/Spiciness.java b/src/enumerated/Spiciness.java new file mode 100644 index 0000000..3e481e6 --- /dev/null +++ b/src/enumerated/Spiciness.java @@ -0,0 +1,6 @@ +//: enumerated/Spiciness.java +package enumerated; + +public enum Spiciness { + NOT, MILD, MEDIUM, HOT, FLAMING +} ///:~ diff --git a/src/enumerated/TrafficLight.java b/src/enumerated/TrafficLight.java new file mode 100644 index 0000000..4f34fc2 --- /dev/null +++ b/src/enumerated/TrafficLight.java @@ -0,0 +1,40 @@ +package enumerated;//: enumerated/TrafficLight.java +// Enums in switch statements. +import static net.mindview.util.Print.*; + +// Define an enum type: +enum Signal { GREEN, YELLOW, RED, } + +public class TrafficLight { + Signal color = Signal.RED; + public void change() { + switch(color) { + // Note that you don't have to say Signal.RED + // in the case statement: + case RED: color = Signal.GREEN; + break; + case GREEN: color = Signal.YELLOW; + break; + case YELLOW: color = Signal.RED; + break; + } + } + public String toString() { + return "The traffic light is " + color; + } + public static void main(String[] args) { + TrafficLight t = new TrafficLight(); + for(int i = 0; i < 7; i++) { + print(t); + t.change(); + } + } +} /* Output: +The traffic light is RED +The traffic light is GREEN +The traffic light is YELLOW +The traffic light is RED +The traffic light is GREEN +The traffic light is YELLOW +The traffic light is RED +*///:~ diff --git a/src/enumerated/UpcastEnum.java b/src/enumerated/UpcastEnum.java new file mode 100644 index 0000000..3a59341 --- /dev/null +++ b/src/enumerated/UpcastEnum.java @@ -0,0 +1,18 @@ +package enumerated;//: enumerated/UpcastEnum.java +// No values() method if you upcast an enum + +enum Search { HITHER, YON } + +public class UpcastEnum { + public static void main(String[] args) { + Search[] vals = Search.values(); + Enum e = Search.HITHER; // Upcast + // e.values(); // No values() in Enum + for(Enum en : e.getClass().getEnumConstants()) { + System.out.println(en); + } + } +} /* Output: +HITHER +YON +*///:~ diff --git a/src/enumerated/VendingMachine.java b/src/enumerated/VendingMachine.java new file mode 100644 index 0000000..55e9c31 --- /dev/null +++ b/src/enumerated/VendingMachine.java @@ -0,0 +1,163 @@ +//: enumerated/VendingMachine.java +// {Args: VendingMachineInput.txt} +package enumerated; +import java.util.*; +import net.mindview.util.*; +import static enumerated.Input.*; +import static net.mindview.util.Print.*; + +enum Category { + MONEY(NICKEL, DIME, QUARTER, DOLLAR), + ITEM_SELECTION(TOOTHPASTE, CHIPS, SODA, SOAP), + QUIT_TRANSACTION(ABORT_TRANSACTION), + SHUT_DOWN(STOP); + private Input[] values; + Category(Input... types) { values = types; } + private static EnumMap categories = + new EnumMap(Input.class); + static { + for(Category c : Category.class.getEnumConstants()) { + for(Input type : c.values) { + categories.put(type, c); + } + } + } + public static Category categorize(Input input) { + return categories.get(input); + } +} + +public class VendingMachine { + private static State state = State.RESTING; + private static int amount = 0; + private static Input selection = null; + enum StateDuration { TRANSIENT } // Tagging enum + enum State { + RESTING { + void next(Input input) { + switch(Category.categorize(input)) { + case MONEY: + amount += input.amount(); + state = ADDING_MONEY; + break; + case SHUT_DOWN: + state = TERMINAL; + default: + } + } + }, + ADDING_MONEY { + void next(Input input) { + switch(Category.categorize(input)) { + case MONEY: + amount += input.amount(); + break; + case ITEM_SELECTION: + selection = input; + if(amount < selection.amount()) { + print("Insufficient money for " + selection); + } else { + state = DISPENSING; + } + break; + case QUIT_TRANSACTION: + state = GIVING_CHANGE; + break; + case SHUT_DOWN: + state = TERMINAL; + default: + } + } + }, + DISPENSING(StateDuration.TRANSIENT) { + void next() { + print("here is your " + selection); + amount -= selection.amount(); + state = GIVING_CHANGE; + } + }, + GIVING_CHANGE(StateDuration.TRANSIENT) { + void next() { + if(amount > 0) { + print("Your change: " + amount); + amount = 0; + } + state = RESTING; + } + }, + TERMINAL { void output() { print("Halted"); } }; + private boolean isTransient = false; + State() {} + State(StateDuration trans) { isTransient = true; } + void next(Input input) { + throw new RuntimeException("Only call " + + "next(Input input) for non-transient states"); + } + void next() { + throw new RuntimeException("Only call next() for " + + "StateDuration.TRANSIENT states"); + } + void output() { print(amount); } + } + static void run(Generator gen) { + while(state != State.TERMINAL) { + state.next(gen.next()); + while(state.isTransient) { + state.next(); + } + state.output(); + } + } + public static void main(String[] args) { + Generator gen = new RandomInputGenerator(); + if(args.length == 1) { + gen = new FileInputGenerator(args[0]); + } + run(gen); + } +} + +// For a basic sanity check: +class RandomInputGenerator implements Generator { + public Input next() { return Input.randomSelection(); } +} + +// Create Inputs from a file of ';'-separated strings: +class FileInputGenerator implements Generator { + private Iterator input; + public FileInputGenerator(String fileName) { + input = new TextFile(fileName, ";").iterator(); + } + public Input next() { + if(!input.hasNext()) { + return null; + } + return Enum.valueOf(Input.class, input.next().trim()); + } +} /* Output: +25 +50 +75 +here is your CHIPS +0 +100 +200 +here is your TOOTHPASTE +0 +25 +35 +Your change: 35 +0 +25 +35 +Insufficient money for SODA +35 +60 +70 +75 +Insufficient money for SODA +75 +Your change: 75 +0 +Halted +*///:~ diff --git a/src/enumerated/VendingMachineInput.txt b/src/enumerated/VendingMachineInput.txt new file mode 100644 index 0000000..9a2c79f --- /dev/null +++ b/src/enumerated/VendingMachineInput.txt @@ -0,0 +1,7 @@ +QUARTER; QUARTER; QUARTER; CHIPS; +DOLLAR; DOLLAR; TOOTHPASTE; +QUARTER; DIME; ABORT_TRANSACTION; +QUARTER; DIME; SODA; +QUARTER; DIME; NICKEL; SODA; +ABORT_TRANSACTION; +STOP; diff --git a/src/enumerated/build.xml b/src/enumerated/build.xml new file mode 100644 index 0000000..63f5b86 --- /dev/null +++ b/src/enumerated/build.xml @@ -0,0 +1,360 @@ + + + + + + build.xml for the source code for the enumerated chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/enumerated/cartoons/EnumImplementation.java b/src/enumerated/cartoons/EnumImplementation.java new file mode 100644 index 0000000..3c56cb4 --- /dev/null +++ b/src/enumerated/cartoons/EnumImplementation.java @@ -0,0 +1,29 @@ +//: enumerated/cartoons/EnumImplementation.java +// An enum can implement an interface +package enumerated.cartoons; +import java.util.*; +import net.mindview.util.*; + +enum CartoonCharacter +implements Generator { + SLAPPY, SPANKY, PUNCHY, SILLY, BOUNCY, NUTTY, BOB; + private Random rand = new Random(47); + public CartoonCharacter next() { + return values()[rand.nextInt(values().length)]; + } +} + +public class EnumImplementation { + public static void printNext(Generator rg) { + System.out.print(rg.next() + ", "); + } + public static void main(String[] args) { + // Choose any instance: + CartoonCharacter cc = CartoonCharacter.BOB; + for(int i = 0; i < 10; i++) { + printNext(cc); + } + } +} /* Output: +BOB, PUNCHY, BOB, SPANKY, NUTTY, PUNCHY, SLAPPY, NUTTY, NUTTY, SLAPPY, +*///:~ diff --git a/src/enumerated/menu/Course.java b/src/enumerated/menu/Course.java new file mode 100644 index 0000000..4a667fd --- /dev/null +++ b/src/enumerated/menu/Course.java @@ -0,0 +1,17 @@ +//: enumerated/menu/Course.java +package enumerated.menu; +import net.mindview.util.*; + +public enum Course { + APPETIZER(Food.Appetizer.class), + MAINCOURSE(Food.MainCourse.class), + DESSERT(Food.Dessert.class), + COFFEE(Food.Coffee.class); + private Food[] values; + private Course(Class kind) { + values = kind.getEnumConstants(); + } + public Food randomSelection() { + return Enums.random(values); + } +} ///:~ diff --git a/src/enumerated/menu/Food.java b/src/enumerated/menu/Food.java new file mode 100644 index 0000000..8edb498 --- /dev/null +++ b/src/enumerated/menu/Food.java @@ -0,0 +1,21 @@ +//: enumerated/menu/Food.java +// Subcategorization of enums within interfaces. +package enumerated.menu; + +public interface Food { + enum Appetizer implements Food { + SALAD, SOUP, SPRING_ROLLS; + } + enum MainCourse implements Food { + LASAGNE, BURRITO, PAD_THAI, + LENTILS, HUMMOUS, VINDALOO; + } + enum Dessert implements Food { + TIRAMISU, GELATO, BLACK_FOREST_CAKE, + FRUIT, CREME_CARAMEL; + } + enum Coffee implements Food { + BLACK_COFFEE, DECAF_COFFEE, ESPRESSO, + LATTE, CAPPUCCINO, TEA, HERB_TEA; + } +} ///:~ diff --git a/src/enumerated/menu/Meal.java b/src/enumerated/menu/Meal.java new file mode 100644 index 0000000..3b001d8 --- /dev/null +++ b/src/enumerated/menu/Meal.java @@ -0,0 +1,40 @@ +//: enumerated/menu/Meal.java +package enumerated.menu; + +public class Meal { + public static void main(String[] args) { + for(int i = 0; i < 5; i++) { + for(Course course : Course.values()) { + Food food = course.randomSelection(); + System.out.println(food); + } + System.out.println("---"); + } + } +} /* Output: +SPRING_ROLLS +VINDALOO +FRUIT +DECAF_COFFEE +--- +SOUP +VINDALOO +FRUIT +TEA +--- +SALAD +BURRITO +FRUIT +TEA +--- +SALAD +BURRITO +CREME_CARAMEL +LATTE +--- +SOUP +BURRITO +TIRAMISU +ESPRESSO +--- +*///:~ diff --git a/src/enumerated/menu/Meal2.java b/src/enumerated/menu/Meal2.java new file mode 100644 index 0000000..38d8df8 --- /dev/null +++ b/src/enumerated/menu/Meal2.java @@ -0,0 +1,43 @@ +//: enumerated/menu/Meal2.java +package enumerated.menu; +import net.mindview.util.*; + +public enum Meal2 { + APPETIZER(Food.Appetizer.class), + MAINCOURSE(Food.MainCourse.class), + DESSERT(Food.Dessert.class), + COFFEE(Food.Coffee.class); + private Food[] values; + private Meal2(Class kind) { + values = kind.getEnumConstants(); + } + public interface Food { + enum Appetizer implements Food { + SALAD, SOUP, SPRING_ROLLS; + } + enum MainCourse implements Food { + LASAGNE, BURRITO, PAD_THAI, + LENTILS, HUMMOUS, VINDALOO; + } + enum Dessert implements Food { + TIRAMISU, GELATO, BLACK_FOREST_CAKE, + FRUIT, CREME_CARAMEL; + } + enum Coffee implements Food { + BLACK_COFFEE, DECAF_COFFEE, ESPRESSO, + LATTE, CAPPUCCINO, TEA, HERB_TEA; + } + } + public Food randomSelection() { + return Enums.random(values); + } + public static void main(String[] args) { + for(int i = 0; i < 5; i++) { + for(Meal2 meal : Meal2.values()) { + Food food = meal.randomSelection(); + System.out.println(food); + } + System.out.println("---"); + } + } +} /* Same output as Meal.java *///:~ diff --git a/src/enumerated/menu/TypeOfFood.java b/src/enumerated/menu/TypeOfFood.java new file mode 100644 index 0000000..fb1138b --- /dev/null +++ b/src/enumerated/menu/TypeOfFood.java @@ -0,0 +1,12 @@ +//: enumerated/menu/TypeOfFood.java +package enumerated.menu; +import static enumerated.menu.Food.*; + +public class TypeOfFood { + public static void main(String[] args) { + Food food = Appetizer.SALAD; + food = MainCourse.LASAGNE; + food = Dessert.GELATO; + food = Coffee.CAPPUCCINO; + } +} ///:~ diff --git a/src/exceptions/AlwaysFinally.java b/src/exceptions/AlwaysFinally.java new file mode 100644 index 0000000..97bfdee --- /dev/null +++ b/src/exceptions/AlwaysFinally.java @@ -0,0 +1,30 @@ +package exceptions;//: exceptions/AlwaysFinally.java +// Finally is always executed. +import static net.mindview.util.Print.*; + +class FourException extends Exception {} + +public class AlwaysFinally { + public static void main(String[] args) { + print("Entering first try block"); + try { + print("Entering second try block"); + try { + throw new FourException(); + } finally { + print("finally in 2nd try block"); + } + } catch(FourException e) { + System.out.println( + "Caught FourException in 1st try block"); + } finally { + System.out.println("finally in 1st try block"); + } + } +} /* Output: +Entering first try block +Entering second try block +finally in 2nd try block +Caught FourException in 1st try block +finally in 1st try block +*///:~ diff --git a/src/exceptions/Cleanup.java b/src/exceptions/Cleanup.java new file mode 100644 index 0000000..1b7c65d --- /dev/null +++ b/src/exceptions/Cleanup.java @@ -0,0 +1,25 @@ +package exceptions;//: exceptions/Cleanup.java +// Guaranteeing proper cleanup of a resource. + +public class Cleanup { + public static void main(String[] args) { + try { + InputFile in = new InputFile("Cleanup.java"); + try { + String s; + int i = 1; + while((s = in.getLine()) != null) { + } + } catch(Exception e) { + System.out.println("Caught Exception in main"); + e.printStackTrace(System.out); + } finally { + in.dispose(); + } + } catch(Exception e) { + System.out.println("InputFile construction failed"); + } + } +} /* Output: +dispose() successful +*///:~ diff --git a/src/exceptions/CleanupIdiom.java b/src/exceptions/CleanupIdiom.java new file mode 100644 index 0000000..cdcc7d1 --- /dev/null +++ b/src/exceptions/CleanupIdiom.java @@ -0,0 +1,66 @@ +package exceptions;//: exceptions/CleanupIdiom.java +// Each disposable object must be followed by a try-finally + +class NeedsCleanup { // Construction can't fail + private static long counter = 1; + private final long id = counter++; + public void dispose() { + System.out.println("NeedsCleanup " + id + " disposed"); + } +} + +class ConstructionException extends Exception {} + +class NeedsCleanup2 extends NeedsCleanup { + // Construction can fail: + public NeedsCleanup2() throws ConstructionException {} +} + +public class CleanupIdiom { + public static void main(String[] args) { + // Section 1: + NeedsCleanup nc1 = new NeedsCleanup(); + try { + // ... + } finally { + nc1.dispose(); + } + + // Section 2: + // If construction cannot fail you can group objects: + NeedsCleanup nc2 = new NeedsCleanup(); + NeedsCleanup nc3 = new NeedsCleanup(); + try { + // ... + } finally { + nc3.dispose(); // Reverse order of construction + nc2.dispose(); + } + + // Section 3: + // If construction can fail you must guard each one: + try { + NeedsCleanup2 nc4 = new NeedsCleanup2(); + try { + NeedsCleanup2 nc5 = new NeedsCleanup2(); + try { + // ... + } finally { + nc5.dispose(); + } + } catch(ConstructionException e) { // nc5 constructor + System.out.println(e); + } finally { + nc4.dispose(); + } + } catch(ConstructionException e) { // nc4 constructor + System.out.println(e); + } + } +} /* Output: +NeedsCleanup 1 disposed +NeedsCleanup 3 disposed +NeedsCleanup 2 disposed +NeedsCleanup 5 disposed +NeedsCleanup 4 disposed +*///:~ diff --git a/src/exceptions/DynamicFields.java b/src/exceptions/DynamicFields.java new file mode 100644 index 0000000..340f072 --- /dev/null +++ b/src/exceptions/DynamicFields.java @@ -0,0 +1,130 @@ +package exceptions;//: exceptions/DynamicFields.java +// A Class that dynamically adds fields to itself. +// Demonstrates exception chaining. +import static net.mindview.util.Print.*; + +class DynamicFieldsException extends Exception {} + +public class DynamicFields { + private Object[][] fields; + public DynamicFields(int initialSize) { + fields = new Object[initialSize][2]; + for(int i = 0; i < initialSize; i++) { + fields[i] = new Object[] { null, null }; + } + } + public String toString() { + StringBuilder result = new StringBuilder(); + for(Object[] obj : fields) { + result.append(obj[0]); + result.append(": "); + result.append(obj[1]); + result.append("\n"); + } + return result.toString(); + } + private int hasField(String id) { + for(int i = 0; i < fields.length; i++) { + if(id.equals(fields[i][0])) { + return i; + } + } + return -1; + } + private int + getFieldNumber(String id) throws NoSuchFieldException { + int fieldNum = hasField(id); + if(fieldNum == -1) { + throw new NoSuchFieldException(); + } + return fieldNum; + } + private int makeField(String id) { + for(int i = 0; i < fields.length; i++) { + if(fields[i][0] == null) { + fields[i][0] = id; + return i; + } + } + // No empty fields. Add one: + Object[][] tmp = new Object[fields.length + 1][2]; + for(int i = 0; i < fields.length; i++) { + tmp[i] = fields[i]; + } + for(int i = fields.length; i < tmp.length; i++) { + tmp[i] = new Object[] { null, null }; + } + fields = tmp; + // Recursive call with expanded fields: + return makeField(id); + } + public Object + getField(String id) throws NoSuchFieldException { + return fields[getFieldNumber(id)][1]; + } + public Object setField(String id, Object value) + throws DynamicFieldsException { + if(value == null) { + // Most exceptions don't have a "cause" constructor. + // In these cases you must use initCause(), + // available in all Throwable subclasses. + DynamicFieldsException dfe = + new DynamicFieldsException(); + dfe.initCause(new NullPointerException()); + throw dfe; + } + int fieldNumber = hasField(id); + if(fieldNumber == -1) { + fieldNumber = makeField(id); + } + Object result = null; + try { + result = getField(id); // Get old value + } catch(NoSuchFieldException e) { + // Use constructor that takes "cause": + throw new RuntimeException(e); + } + fields[fieldNumber][1] = value; + return result; + } + public static void main(String[] args) { + DynamicFields df = new DynamicFields(3); + print(df); + try { + df.setField("d", "A value for d"); + df.setField("number", 47); + df.setField("number2", 48); + print(df); + df.setField("d", "A new value for d"); + df.setField("number3", 11); + print("df: " + df); + print("df.getField(\"d\") : " + df.getField("d")); + Object field = df.setField("d", null); // Exception + } catch(NoSuchFieldException e) { + e.printStackTrace(System.out); + } catch(DynamicFieldsException e) { + e.printStackTrace(System.out); + } + } +} /* Output: +null: null +null: null +null: null + +d: A value for d +number: 47 +number2: 48 + +df: d: A new value for d +number: 47 +number2: 48 +number3: 11 + +df.getField("d") : A new value for d +DynamicFieldsException + at DynamicFields.setField(DynamicFields.java:64) + at DynamicFields.main(DynamicFields.java:94) +Caused by: java.lang.NullPointerException + at DynamicFields.setField(DynamicFields.java:66) + ... 1 more +*///:~ diff --git a/src/exceptions/ExceptionMethods.java b/src/exceptions/ExceptionMethods.java new file mode 100644 index 0000000..8e728ad --- /dev/null +++ b/src/exceptions/ExceptionMethods.java @@ -0,0 +1,27 @@ +package exceptions;//: exceptions/ExceptionMethods.java +// Demonstrating the Exception Methods. +import static net.mindview.util.Print.*; + +public class ExceptionMethods { + public static void main(String[] args) { + try { + throw new Exception("My Exception"); + } catch(Exception e) { + print("Caught Exception"); + print("getMessage():" + e.getMessage()); + print("getLocalizedMessage():" + + e.getLocalizedMessage()); + print("toString():" + e); + print("printStackTrace():"); + e.printStackTrace(System.out); + } + } +} /* Output: +Caught Exception +getMessage():My Exception +getLocalizedMessage():My Exception +toString():java.lang.Exception: My Exception +printStackTrace(): +java.lang.Exception: My Exception + at ExceptionMethods.main(ExceptionMethods.java:8) +*///:~ diff --git a/src/exceptions/ExceptionSilencer.java b/src/exceptions/ExceptionSilencer.java new file mode 100644 index 0000000..cbcd3b6 --- /dev/null +++ b/src/exceptions/ExceptionSilencer.java @@ -0,0 +1,13 @@ +package exceptions;//: exceptions/ExceptionSilencer.java + +public class ExceptionSilencer { + public static void main(String[] args) { + try { + throw new RuntimeException(); + } finally { + // Using 'return' inside the finally block + // will silence any thrown exception. + return; + } + } +} ///:~ diff --git a/src/exceptions/ExtraFeatures.java b/src/exceptions/ExtraFeatures.java new file mode 100644 index 0000000..ed2ac57 --- /dev/null +++ b/src/exceptions/ExtraFeatures.java @@ -0,0 +1,64 @@ +package exceptions;//: exceptions/ExtraFeatures.java +// Further embellishment of exception classes. +import static net.mindview.util.Print.*; + +class MyException2 extends Exception { + private int x; + public MyException2() {} + public MyException2(String msg) { super(msg); } + public MyException2(String msg, int x) { + super(msg); + this.x = x; + } + public int val() { return x; } + public String getMessage() { + return "Detail Message: "+ x + " "+ super.getMessage(); + } +} + +public class ExtraFeatures { + public static void f() throws MyException2 { + print("Throwing MyException2 from f()"); + throw new MyException2(); + } + public static void g() throws MyException2 { + print("Throwing MyException2 from g()"); + throw new MyException2("Originated in g()"); + } + public static void h() throws MyException2 { + print("Throwing MyException2 from h()"); + throw new MyException2("Originated in h()", 47); + } + public static void main(String[] args) { + try { + f(); + } catch(MyException2 e) { + e.printStackTrace(System.out); + } + try { + g(); + } catch(MyException2 e) { + e.printStackTrace(System.out); + } + try { + h(); + } catch(MyException2 e) { + e.printStackTrace(System.out); + System.out.println("e.val() = " + e.val()); + } + } +} /* Output: +Throwing MyException2 from f() +MyException2: Detail Message: 0 null + at ExtraFeatures.f(ExtraFeatures.java:22) + at ExtraFeatures.main(ExtraFeatures.java:34) +Throwing MyException2 from g() +MyException2: Detail Message: 0 Originated in g() + at ExtraFeatures.g(ExtraFeatures.java:26) + at ExtraFeatures.main(ExtraFeatures.java:39) +Throwing MyException2 from h() +MyException2: Detail Message: 47 Originated in h() + at ExtraFeatures.h(ExtraFeatures.java:30) + at ExtraFeatures.main(ExtraFeatures.java:44) +e.val() = 47 +*///:~ diff --git a/src/exceptions/FinallyWorks.java b/src/exceptions/FinallyWorks.java new file mode 100644 index 0000000..3d36b5f --- /dev/null +++ b/src/exceptions/FinallyWorks.java @@ -0,0 +1,31 @@ +package exceptions;//: exceptions/FinallyWorks.java +// The finally clause is always executed. + +class ThreeException extends Exception {} + +public class FinallyWorks { + static int count = 0; + public static void main(String[] args) { + while(true) { + try { + // Post-increment is zero first time: + if(count++ == 0) { + throw new ThreeException(); + } + System.out.println("No exception"); + } catch(ThreeException e) { + System.out.println("ThreeException"); + } finally { + System.out.println("In finally clause"); + if(count == 2) { + break; // out of "while" + } + } + } + } +} /* Output: +ThreeException +In finally clause +No exception +In finally clause +*///:~ diff --git a/src/exceptions/FullConstructors.java b/src/exceptions/FullConstructors.java new file mode 100644 index 0000000..4b20664 --- /dev/null +++ b/src/exceptions/FullConstructors.java @@ -0,0 +1,38 @@ +package exceptions;//: exceptions/FullConstructors.java + +class MyException extends Exception { + public MyException() {} + public MyException(String msg) { super(msg); } +} + +public class FullConstructors { + public static void f() throws MyException { + System.out.println("Throwing MyException from f()"); + throw new MyException(); + } + public static void g() throws MyException { + System.out.println("Throwing MyException from g()"); + throw new MyException("Originated in g()"); + } + public static void main(String[] args) { + try { + f(); + } catch(MyException e) { + e.printStackTrace(System.out); + } + try { + g(); + } catch(MyException e) { + e.printStackTrace(System.out); + } + } +} /* Output: +Throwing MyException from f() +MyException + at FullConstructors.f(FullConstructors.java:11) + at FullConstructors.main(FullConstructors.java:19) +Throwing MyException from g() +MyException: Originated in g() + at FullConstructors.g(FullConstructors.java:15) + at FullConstructors.main(FullConstructors.java:24) +*///:~ diff --git a/src/exceptions/Human.java b/src/exceptions/Human.java new file mode 100644 index 0000000..e0736f8 --- /dev/null +++ b/src/exceptions/Human.java @@ -0,0 +1,27 @@ +package exceptions;//: exceptions/Human.java +// Catching exception hierarchies. + +class Annoyance extends Exception {} +class Sneeze extends Annoyance {} + +public class Human { + public static void main(String[] args) { + // Catch the exact type: + try { + throw new Sneeze(); + } catch(Sneeze s) { + System.out.println("Caught Sneeze"); + } catch(Annoyance a) { + System.out.println("Caught Annoyance"); + } + // Catch the base type: + try { + throw new Sneeze(); + } catch(Annoyance a) { + System.out.println("Caught Annoyance"); + } + } +} /* Output: +Caught Sneeze +Caught Annoyance +*///:~ diff --git a/src/exceptions/InheritingExceptions.java b/src/exceptions/InheritingExceptions.java new file mode 100644 index 0000000..e4247c4 --- /dev/null +++ b/src/exceptions/InheritingExceptions.java @@ -0,0 +1,22 @@ +package exceptions;//: exceptions/InheritingExceptions.java +// Creating your own exceptions. + +class SimpleException extends Exception {} + +public class InheritingExceptions { + public void f() throws SimpleException { + System.out.println("Throw SimpleException from f()"); + throw new SimpleException(); + } + public static void main(String[] args) { + InheritingExceptions sed = new InheritingExceptions(); + try { + sed.f(); + } catch(SimpleException e) { + System.out.println("Caught it!"); + } + } +} /* Output: +Throw SimpleException from f() +Caught it! +*///:~ diff --git a/src/exceptions/InputFile.java b/src/exceptions/InputFile.java new file mode 100644 index 0000000..12118bb --- /dev/null +++ b/src/exceptions/InputFile.java @@ -0,0 +1,44 @@ +package exceptions;//: exceptions/InputFile.java +// Paying attention to exceptions in constructors. +import java.io.*; + +public class InputFile { + private BufferedReader in; + public InputFile(String fname) throws Exception { + try { + in = new BufferedReader(new FileReader(fname)); + // Other code that might throw exceptions + } catch(FileNotFoundException e) { + System.out.println("Could not open " + fname); + // Wasn't open, so don't close it + throw e; + } catch(Exception e) { + // All other exceptions must close it + try { + in.close(); + } catch(IOException e2) { + System.out.println("in.close() unsuccessful"); + } + throw e; // Rethrow + } finally { + // Don't close it here!!! + } + } + public String getLine() { + String s; + try { + s = in.readLine(); + } catch(IOException e) { + throw new RuntimeException("readLine() failed"); + } + return s; + } + public void dispose() { + try { + in.close(); + System.out.println("dispose() successful"); + } catch(IOException e2) { + throw new RuntimeException("in.close() failed"); + } + } +} ///:~ diff --git a/src/exceptions/LoggingExceptions.java b/src/exceptions/LoggingExceptions.java new file mode 100644 index 0000000..4ca6675 --- /dev/null +++ b/src/exceptions/LoggingExceptions.java @@ -0,0 +1,40 @@ +package exceptions;//: exceptions/LoggingExceptions.java +// An exception that reports through a Logger. +import java.util.logging.*; +import java.io.*; + +class LoggingException extends Exception { + private static Logger logger = + Logger.getLogger("LoggingException"); + public LoggingException() { + StringWriter trace = new StringWriter(); + printStackTrace(new PrintWriter(trace)); + logger.severe(trace.toString()); + } +} + +public class LoggingExceptions { + public static void main(String[] args) { + try { + throw new LoggingException(); + } catch(LoggingException e) { + System.err.println("Caught " + e); + } + try { + throw new LoggingException(); + } catch(LoggingException e) { + System.err.println("Caught " + e); + } + } +} /* Output: (85% match) +Aug 30, 2005 4:02:31 PM LoggingException +SEVERE: LoggingException + at LoggingExceptions.main(LoggingExceptions.java:19) + +Caught LoggingException +Aug 30, 2005 4:02:31 PM LoggingException +SEVERE: LoggingException + at LoggingExceptions.main(LoggingExceptions.java:24) + +Caught LoggingException +*///:~ diff --git a/src/exceptions/LoggingExceptions2.java b/src/exceptions/LoggingExceptions2.java new file mode 100644 index 0000000..1ee70df --- /dev/null +++ b/src/exceptions/LoggingExceptions2.java @@ -0,0 +1,25 @@ +package exceptions;//: exceptions/LoggingExceptions2.java +// Logging caught exceptions. +import java.util.logging.*; +import java.io.*; + +public class LoggingExceptions2 { + private static Logger logger = + Logger.getLogger("LoggingExceptions2"); + static void logException(Exception e) { + StringWriter trace = new StringWriter(); + e.printStackTrace(new PrintWriter(trace)); + logger.severe(trace.toString()); + } + public static void main(String[] args) { + try { + throw new NullPointerException(); + } catch(NullPointerException e) { + logException(e); + } + } +} /* Output: (90% match) +Aug 30, 2005 4:07:54 PM LoggingExceptions2 logException +SEVERE: java.lang.NullPointerException + at LoggingExceptions2.main(LoggingExceptions2.java:16) +*///:~ diff --git a/src/exceptions/LostMessage.java b/src/exceptions/LostMessage.java new file mode 100644 index 0000000..06b05bc --- /dev/null +++ b/src/exceptions/LostMessage.java @@ -0,0 +1,37 @@ +package exceptions;//: exceptions/LostMessage.java +// How an exception can be lost. + +class VeryImportantException extends Exception { + public String toString() { + return "A very important exception!"; + } +} + +class HoHumException extends Exception { + public String toString() { + return "A trivial exception"; + } +} + +public class LostMessage { + void f() throws VeryImportantException { + throw new VeryImportantException(); + } + void dispose() throws HoHumException { + throw new HoHumException(); + } + public static void main(String[] args) { + try { + LostMessage lm = new LostMessage(); + try { + lm.f(); + } finally { + lm.dispose(); + } + } catch(Exception e) { + System.out.println(e); + } + } +} /* Output: +A trivial exception +*///:~ diff --git a/src/exceptions/MainException.java b/src/exceptions/MainException.java new file mode 100644 index 0000000..9cf241e --- /dev/null +++ b/src/exceptions/MainException.java @@ -0,0 +1,14 @@ +package exceptions;//: exceptions/MainException.java +import java.io.*; + +public class MainException { + // Pass all exceptions to the console: + public static void main(String[] args) throws Exception { + // Open the file: + FileInputStream file = + new FileInputStream("MainException.java"); + // Use the file ... + // Close the file: + file.close(); + } +} ///:~ diff --git a/src/exceptions/MultipleReturns.java b/src/exceptions/MultipleReturns.java new file mode 100644 index 0000000..d98e9c7 --- /dev/null +++ b/src/exceptions/MultipleReturns.java @@ -0,0 +1,50 @@ +package exceptions;//: exceptions/MultipleReturns.java +import static net.mindview.util.Print.*; + +public class MultipleReturns { + public static void f(int i) { + print("Initialization that requires cleanup"); + try { + print("Point 1"); + if(i == 1) { + return; + } + print("Point 2"); + if(i == 2) { + return; + } + print("Point 3"); + if(i == 3) { + return; + } + print("End"); + return; + } finally { + print("Performing cleanup"); + } + } + public static void main(String[] args) { + for(int i = 1; i <= 4; i++) { + f(i); + } + } +} /* Output: +Initialization that requires cleanup +Point 1 +Performing cleanup +Initialization that requires cleanup +Point 1 +Point 2 +Performing cleanup +Initialization that requires cleanup +Point 1 +Point 2 +Point 3 +Performing cleanup +Initialization that requires cleanup +Point 1 +Point 2 +Point 3 +End +Performing cleanup +*///:~ diff --git a/src/exceptions/NeverCaught.java b/src/exceptions/NeverCaught.java new file mode 100644 index 0000000..cd33bf4 --- /dev/null +++ b/src/exceptions/NeverCaught.java @@ -0,0 +1,15 @@ +package exceptions;//: exceptions/NeverCaught.java +// Ignoring RuntimeExceptions. +// {ThrowsException} + +public class NeverCaught { + static void f() { + throw new RuntimeException("From f()"); + } + static void g() { + f(); + } + public static void main(String[] args) { + g(); + } +} ///:~ diff --git a/src/exceptions/OnOffException1.java b/src/exceptions/OnOffException1.java new file mode 100644 index 0000000..656ff1f --- /dev/null +++ b/src/exceptions/OnOffException1.java @@ -0,0 +1,4 @@ +package exceptions; + +//: exceptions/OnOffException1.java +public class OnOffException1 extends Exception {} ///:~ diff --git a/src/exceptions/OnOffException2.java b/src/exceptions/OnOffException2.java new file mode 100644 index 0000000..124dcd0 --- /dev/null +++ b/src/exceptions/OnOffException2.java @@ -0,0 +1,4 @@ +package exceptions; + +//: exceptions/OnOffException2.java +public class OnOffException2 extends Exception {} ///:~ diff --git a/src/exceptions/OnOffSwitch.java b/src/exceptions/OnOffSwitch.java new file mode 100644 index 0000000..85bc67c --- /dev/null +++ b/src/exceptions/OnOffSwitch.java @@ -0,0 +1,25 @@ +package exceptions;//: exceptions/OnOffSwitch.java +// Why use finally? + +public class OnOffSwitch { + private static Switch sw = new Switch(); + public static void f() + throws OnOffException1,OnOffException2 {} + public static void main(String[] args) { + try { + sw.on(); + // Code that can throw exceptions... + f(); + sw.off(); + } catch(OnOffException1 e) { + System.out.println("OnOffException1"); + sw.off(); + } catch(OnOffException2 e) { + System.out.println("OnOffException2"); + sw.off(); + } + } +} /* Output: +on +off +*///:~ diff --git a/src/exceptions/RethrowNew.java b/src/exceptions/RethrowNew.java new file mode 100644 index 0000000..3ab8143 --- /dev/null +++ b/src/exceptions/RethrowNew.java @@ -0,0 +1,42 @@ +package exceptions;//: exceptions/RethrowNew.java +// Rethrow a different object from the one that was caught. + +class OneException extends Exception { + public OneException(String s) { super(s); } +} + +class TwoException extends Exception { + public TwoException(String s) { super(s); } +} + +public class RethrowNew { + public static void f() throws OneException { + System.out.println("originating the exception in f()"); + throw new OneException("thrown from f()"); + } + public static void main(String[] args) { + try { + try { + f(); + } catch(OneException e) { + System.out.println( + "Caught in inner try, e.printStackTrace()"); + e.printStackTrace(System.out); + throw new TwoException("from inner try"); + } + } catch(TwoException e) { + System.out.println( + "Caught in outer try, e.printStackTrace()"); + e.printStackTrace(System.out); + } + } +} /* Output: +originating the exception in f() +Caught in inner try, e.printStackTrace() +OneException: thrown from f() + at RethrowNew.f(RethrowNew.java:15) + at RethrowNew.main(RethrowNew.java:20) +Caught in outer try, e.printStackTrace() +TwoException: from inner try + at RethrowNew.main(RethrowNew.java:25) +*///:~ diff --git a/src/exceptions/Rethrowing.java b/src/exceptions/Rethrowing.java new file mode 100644 index 0000000..01ffdb9 --- /dev/null +++ b/src/exceptions/Rethrowing.java @@ -0,0 +1,63 @@ +package exceptions;//: exceptions/Rethrowing.java +// Demonstrating fillInStackTrace() + +public class Rethrowing { + public static void f() throws Exception { + System.out.println("originating the exception in f()"); + throw new Exception("thrown from f()"); + } + public static void g() throws Exception { + try { + f(); + } catch(Exception e) { + System.out.println("Inside g(),e.printStackTrace()"); + e.printStackTrace(System.out); + throw e; + } + } + public static void h() throws Exception { + try { + f(); + } catch(Exception e) { + System.out.println("Inside h(),e.printStackTrace()"); + e.printStackTrace(System.out); + throw (Exception)e.fillInStackTrace(); + } + } + public static void main(String[] args) { + try { + g(); + } catch(Exception e) { + System.out.println("main: printStackTrace()"); + e.printStackTrace(System.out); + } + try { + h(); + } catch(Exception e) { + System.out.println("main: printStackTrace()"); + e.printStackTrace(System.out); + } + } +} /* Output: +originating the exception in f() +Inside g(),e.printStackTrace() +java.lang.Exception: thrown from f() + at Rethrowing.f(Rethrowing.java:7) + at Rethrowing.g(Rethrowing.java:11) + at Rethrowing.main(Rethrowing.java:29) +main: printStackTrace() +java.lang.Exception: thrown from f() + at Rethrowing.f(Rethrowing.java:7) + at Rethrowing.g(Rethrowing.java:11) + at Rethrowing.main(Rethrowing.java:29) +originating the exception in f() +Inside h(),e.printStackTrace() +java.lang.Exception: thrown from f() + at Rethrowing.f(Rethrowing.java:7) + at Rethrowing.h(Rethrowing.java:20) + at Rethrowing.main(Rethrowing.java:35) +main: printStackTrace() +java.lang.Exception: thrown from f() + at Rethrowing.h(Rethrowing.java:24) + at Rethrowing.main(Rethrowing.java:35) +*///:~ diff --git a/src/exceptions/StormyInning.java b/src/exceptions/StormyInning.java new file mode 100644 index 0000000..50e0f79 --- /dev/null +++ b/src/exceptions/StormyInning.java @@ -0,0 +1,76 @@ +package exceptions;//: exceptions/StormyInning.java +// Overridden methods may throw only the exceptions +// specified in their base-class versions, or exceptions +// derived from the base-class exceptions. + +class BaseballException extends Exception {} +class Foul extends BaseballException {} +class Strike extends BaseballException {} + +abstract class Inning { + public Inning() throws BaseballException {} + public void event() throws BaseballException { + // Doesn't actually have to throw anything + } + public abstract void atBat() throws Strike, Foul; + public void walk() {} // Throws no checked exceptions +} + +class StormException extends Exception {} +class RainedOut extends StormException {} +class PopFoul extends Foul {} + +interface Storm { + public void event() throws RainedOut; + public void rainHard() throws RainedOut; +} + +public class StormyInning extends Inning implements Storm { + // OK to add new exceptions for constructors, but you + // must deal with the base constructor exceptions: + public StormyInning() + throws RainedOut, BaseballException {} + public StormyInning(String s) + throws Foul, BaseballException {} + // Regular methods must conform to base class: +//! void walk() throws PopFoul {} //Compile error + // Interface CANNOT add exceptions to existing + // methods from the base class: +//! public void event() throws RainedOut {} + // If the method doesn't already exist in the + // base class, the exception is OK: + public void rainHard() throws RainedOut {} + // You can choose to not throw any exceptions, + // even if the base version does: + public void event() {} + // Overridden methods can throw inherited exceptions: + public void atBat() throws PopFoul {} + public static void main(String[] args) { + try { + StormyInning si = new StormyInning(); + si.atBat(); + } catch(PopFoul e) { + System.out.println("Pop foul"); + } catch(RainedOut e) { + System.out.println("Rained out"); + } catch(BaseballException e) { + System.out.println("Generic baseball exception"); + } + // Strike not thrown in derived version. + try { + // What happens if you upcast? + Inning i = new StormyInning(); + i.atBat(); + // You must catch the exceptions from the + // base-class version of the method: + } catch(Strike e) { + System.out.println("Strike"); + } catch(Foul e) { + System.out.println("Foul"); + } catch(RainedOut e) { + System.out.println("Rained out"); + } catch(BaseballException e) { + System.out.println("Generic baseball exception"); + } + } +} ///:~ diff --git a/src/exceptions/Switch.java b/src/exceptions/Switch.java new file mode 100644 index 0000000..512ced4 --- /dev/null +++ b/src/exceptions/Switch.java @@ -0,0 +1,10 @@ +package exceptions;//: exceptions/Switch.java +import static net.mindview.util.Print.*; + +public class Switch { + private boolean state = false; + public boolean read() { return state; } + public void on() { state = true; print(this); } + public void off() { state = false; print(this); } + public String toString() { return state ? "on" : "off"; } +} ///:~ diff --git a/src/exceptions/TurnOffChecking.java b/src/exceptions/TurnOffChecking.java new file mode 100644 index 0000000..c9866bd --- /dev/null +++ b/src/exceptions/TurnOffChecking.java @@ -0,0 +1,57 @@ +package exceptions;//: exceptions/TurnOffChecking.java +// "Turning off" Checked exceptions. +import java.io.*; +import static net.mindview.util.Print.*; + +class WrapCheckedException { + void throwRuntimeException(int type) { + try { + switch(type) { + case 0: throw new FileNotFoundException(); + case 1: throw new IOException(); + case 2: throw new RuntimeException("Where am I?"); + default: return; + } + } catch(Exception e) { // Adapt to unchecked: + throw new RuntimeException(e); + } + } +} + +class SomeOtherException extends Exception {} + +public class TurnOffChecking { + public static void main(String[] args) { + WrapCheckedException wce = new WrapCheckedException(); + // You can call throwRuntimeException() without a try + // block, and let RuntimeExceptions leave the method: + wce.throwRuntimeException(3); + // Or you can choose to catch exceptions: + for(int i = 0; i < 4; i++) { + try { + if(i < 3) { + wce.throwRuntimeException(i); + } else { + throw new SomeOtherException(); + } + } catch(SomeOtherException e) { + print("SomeOtherException: " + e); + } catch(RuntimeException re) { + try { + throw re.getCause(); + } catch(FileNotFoundException e) { + print("FileNotFoundException: " + e); + } catch(IOException e) { + print("IOException: " + e); + } catch(Throwable e) { + print("Throwable: " + e); + } + } + } + } +} /* Output: +FileNotFoundException: java.io.FileNotFoundException +IOException: java.io.IOException +Throwable: java.lang.RuntimeException: Where am I? +SomeOtherException: SomeOtherException +*///:~ diff --git a/src/exceptions/WhoCalled.java b/src/exceptions/WhoCalled.java new file mode 100644 index 0000000..725514a --- /dev/null +++ b/src/exceptions/WhoCalled.java @@ -0,0 +1,36 @@ +package exceptions;//: exceptions/WhoCalled.java +// Programmatic access to stack trace information. + +public class WhoCalled { + static void f() { + // Generate an exception to fill in the stack trace + try { + throw new Exception(); + } catch (Exception e) { + for(StackTraceElement ste : e.getStackTrace()) { + System.out.println(ste.getMethodName()); + } + } + } + static void g() { f(); } + static void h() { g(); } + public static void main(String[] args) { + f(); + System.out.println("--------------------------------"); + g(); + System.out.println("--------------------------------"); + h(); + } +} /* Output: +f +main +-------------------------------- +f +g +main +-------------------------------- +f +g +h +main +*///:~ diff --git a/src/exceptions/WithFinally.java b/src/exceptions/WithFinally.java new file mode 100644 index 0000000..074f0d1 --- /dev/null +++ b/src/exceptions/WithFinally.java @@ -0,0 +1,22 @@ +package exceptions;//: exceptions/WithFinally.java +// Finally Guarantees cleanup. + +public class WithFinally { + static Switch sw = new Switch(); + public static void main(String[] args) { + try { + sw.on(); + // Code that can throw exceptions... + OnOffSwitch.f(); + } catch(OnOffException1 e) { + System.out.println("OnOffException1"); + } catch(OnOffException2 e) { + System.out.println("OnOffException2"); + } finally { + sw.off(); + } + } +} /* Output: +on +off +*///:~ diff --git a/src/exceptions/build.xml b/src/exceptions/build.xml new file mode 100644 index 0000000..19b093a --- /dev/null +++ b/src/exceptions/build.xml @@ -0,0 +1,311 @@ + + + + + + build.xml for the source code for the exceptions chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frogbean/Frog.java b/src/frogbean/Frog.java new file mode 100644 index 0000000..76083f0 --- /dev/null +++ b/src/frogbean/Frog.java @@ -0,0 +1,44 @@ +//: frogbean/Frog.java +// A trivial JavaBean. +package frogbean; +import java.awt.*; +import java.awt.event.*; + +class Spots {} + +public class Frog { + private int jumps; + private Color color; + private Spots spots; + private boolean jmpr; + public int getJumps() { return jumps; } + public void setJumps(int newJumps) { + jumps = newJumps; + } + public Color getColor() { return color; } + public void setColor(Color newColor) { + color = newColor; + } + public Spots getSpots() { return spots; } + public void setSpots(Spots newSpots) { + spots = newSpots; + } + public boolean isJumper() { return jmpr; } + public void setJumper(boolean j) { jmpr = j; } + public void addActionListener(ActionListener l) { + //... + } + public void removeActionListener(ActionListener l) { + // ... + } + public void addKeyListener(KeyListener l) { + // ... + } + public void removeKeyListener(KeyListener l) { + // ... + } + // An "ordinary" public method: + public void croak() { + System.out.println("Ribbet!"); + } +} ///:~ diff --git a/src/frogbean/build.xml b/src/frogbean/build.xml new file mode 100644 index 0000000..92cb783 --- /dev/null +++ b/src/frogbean/build.xml @@ -0,0 +1,61 @@ + + + + + + build.xml for the source code for the frogbean chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/generics/Apply.java b/src/generics/Apply.java new file mode 100644 index 0000000..28831c1 --- /dev/null +++ b/src/generics/Apply.java @@ -0,0 +1,73 @@ +package generics;//: generics/Apply.java +// {main: ApplyTest} +import java.lang.reflect.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class Apply { + public static > + void apply(S seq, Method f, Object... args) { + try { + for(T t: seq) { + f.invoke(t, args); + } + } catch(Exception e) { + // Failures are programmer errors + throw new RuntimeException(e); + } + } +} + +class Shape { + public void rotate() { print(this + " rotate"); } + public void resize(int newSize) { + print(this + " resize " + newSize); + } +} + +class Square extends Shape {} + +class FilledList extends ArrayList { + public FilledList(Class type, int size) { + try { + for(int i = 0; i < size; i++) + // Assumes default constructor: + { + add(type.newInstance()); + } + } catch(Exception e) { + throw new RuntimeException(e); + } + } +} + +class ApplyTest { + public static void main(String[] args) throws Exception { + List shapes = new ArrayList(); + for(int i = 0; i < 10; i++) { + shapes.add(new Shape()); + } + Apply.apply(shapes, Shape.class.getMethod("rotate")); + Apply.apply(shapes, + Shape.class.getMethod("resize", int.class), 5); + List squares = new ArrayList(); + for(int i = 0; i < 10; i++) { + squares.add(new Square()); + } + Apply.apply(squares, Shape.class.getMethod("rotate")); + Apply.apply(squares, + Shape.class.getMethod("resize", int.class), 5); + + Apply.apply(new FilledList(Shape.class, 10), + Shape.class.getMethod("rotate")); + Apply.apply(new FilledList(Square.class, 10), + Shape.class.getMethod("rotate")); + + SimpleQueue shapeQ = new SimpleQueue(); + for(int i = 0; i < 5; i++) { + shapeQ.add(new Shape()); + shapeQ.add(new Square()); + } + Apply.apply(shapeQ, Shape.class.getMethod("rotate")); + } +} /* (Execute to see output) *///:~ diff --git a/src/generics/ArrayMaker.java b/src/generics/ArrayMaker.java new file mode 100644 index 0000000..d43d781 --- /dev/null +++ b/src/generics/ArrayMaker.java @@ -0,0 +1,20 @@ +package generics;//: generics/ArrayMaker.java +import java.lang.reflect.*; +import java.util.*; + +public class ArrayMaker { + private Class kind; + public ArrayMaker(Class kind) { this.kind = kind; } + @SuppressWarnings("unchecked") + T[] create(int size) { + return (T[])Array.newInstance(kind, size); + } + public static void main(String[] args) { + ArrayMaker stringMaker = + new ArrayMaker(String.class); + String[] stringArray = stringMaker.create(9); + System.out.println(Arrays.toString(stringArray)); + } +} /* Output: +[null, null, null, null, null, null, null, null, null] +*///:~ diff --git a/src/generics/ArrayOfGeneric.java b/src/generics/ArrayOfGeneric.java new file mode 100644 index 0000000..099b544 --- /dev/null +++ b/src/generics/ArrayOfGeneric.java @@ -0,0 +1,20 @@ +package generics;//: generics/ArrayOfGeneric.java + +public class ArrayOfGeneric { + static final int SIZE = 100; + static Generic[] gia; + @SuppressWarnings("unchecked") + public static void main(String[] args) { + // Compiles; produces ClassCastException: + //! gia = (Generic[])new Object[SIZE]; + // Runtime type is the raw (erased) type: + gia = (Generic[])new Generic[SIZE]; + System.out.println(gia.getClass().getSimpleName()); + gia[0] = new Generic(); + //! gia[1] = new Object(); // Compile-time error + // Discovers type mismatch at compile time: + //! gia[2] = new Generic(); + } +} /* Output: +Generic[] +*///:~ diff --git a/src/generics/ArrayOfGenericReference.java b/src/generics/ArrayOfGenericReference.java new file mode 100644 index 0000000..382b83a --- /dev/null +++ b/src/generics/ArrayOfGenericReference.java @@ -0,0 +1,7 @@ +package generics;//: generics/ArrayOfGenericReference.java + +class Generic {} + +public class ArrayOfGenericReference { + static Generic[] gia; +} ///:~ diff --git a/src/generics/BankTeller.java b/src/generics/BankTeller.java new file mode 100644 index 0000000..27a70a8 --- /dev/null +++ b/src/generics/BankTeller.java @@ -0,0 +1,61 @@ +package generics;//: generics/BankTeller.java +// A very simple bank teller simulation. +import java.util.*; +import net.mindview.util.*; + +class Customer { + private static long counter = 1; + private final long id = counter++; + private Customer() {} + public String toString() { return "Customer " + id; } + // A method to produce Generator objects: + public static Generator generator() { + return new Generator() { + public Customer next() { return new Customer(); } + }; + } +} + +class Teller { + private static long counter = 1; + private final long id = counter++; + private Teller() {} + public String toString() { return "Teller " + id; } + // A single Generator object: + public static Generator generator = + new Generator() { + public Teller next() { return new Teller(); } + }; +} + +public class BankTeller { + public static void serve(Teller t, Customer c) { + System.out.println(t + " serves " + c); + } + public static void main(String[] args) { + Random rand = new Random(47); + Queue line = new LinkedList(); + Generators.fill(line, Customer.generator(), 15); + List tellers = new ArrayList(); + Generators.fill(tellers, Teller.generator, 4); + for(Customer c : line) { + serve(tellers.get(rand.nextInt(tellers.size())), c); + } + } +} /* Output: +Teller 3 serves Customer 1 +Teller 2 serves Customer 2 +Teller 3 serves Customer 3 +Teller 1 serves Customer 4 +Teller 1 serves Customer 5 +Teller 3 serves Customer 6 +Teller 1 serves Customer 7 +Teller 2 serves Customer 8 +Teller 3 serves Customer 9 +Teller 3 serves Customer 10 +Teller 2 serves Customer 11 +Teller 4 serves Customer 12 +Teller 2 serves Customer 13 +Teller 1 serves Customer 14 +Teller 1 serves Customer 15 +*///:~ diff --git a/src/generics/BasicBounds.java b/src/generics/BasicBounds.java new file mode 100644 index 0000000..c20705c --- /dev/null +++ b/src/generics/BasicBounds.java @@ -0,0 +1,58 @@ +package generics;//: generics/BasicBounds.java + +interface HasColor { java.awt.Color getColor(); } + +class Colored { + T item; + Colored(T item) { this.item = item; } + T getItem() { return item; } + // The bound allows you to call a method: + java.awt.Color color() { return item.getColor(); } +} + +class Dimension { public int x, y, z; } + +// This won't work -- class must be first, then interfaces: +// class ColoredDimension { + +// Multiple bounds: +class ColoredDimension { + T item; + ColoredDimension(T item) { this.item = item; } + T getItem() { return item; } + java.awt.Color color() { return item.getColor(); } + int getX() { return item.x; } + int getY() { return item.y; } + int getZ() { return item.z; } +} + +interface Weight { int weight(); } + +// As with inheritance, you can have only one +// concrete class but multiple interfaces: +class Solid { + T item; + Solid(T item) { this.item = item; } + T getItem() { return item; } + java.awt.Color color() { return item.getColor(); } + int getX() { return item.x; } + int getY() { return item.y; } + int getZ() { return item.z; } + int weight() { return item.weight(); } +} + +class Bounded +extends Dimension implements HasColor, Weight { + public java.awt.Color getColor() { return null; } + public int weight() { return 0; } +} + +public class BasicBounds { + public static void main(String[] args) { + Solid solid = + new Solid(new Bounded()); + solid.color(); + solid.getY(); + solid.weight(); + } +} ///:~ diff --git a/src/generics/BasicGeneratorDemo.java b/src/generics/BasicGeneratorDemo.java new file mode 100644 index 0000000..923960f --- /dev/null +++ b/src/generics/BasicGeneratorDemo.java @@ -0,0 +1,18 @@ +package generics;//: generics/BasicGeneratorDemo.java +import net.mindview.util.*; + +public class BasicGeneratorDemo { + public static void main(String[] args) { + Generator gen = + BasicGenerator.create(CountedObject.class); + for(int i = 0; i < 5; i++) { + System.out.println(gen.next()); + } + } +} /* Output: +CountedObject 0 +CountedObject 1 +CountedObject 2 +CountedObject 3 +CountedObject 4 +*///:~ diff --git a/src/generics/BasicHolder.java b/src/generics/BasicHolder.java new file mode 100644 index 0000000..be848b7 --- /dev/null +++ b/src/generics/BasicHolder.java @@ -0,0 +1,10 @@ +package generics;//: generics/BasicHolder.java + +public class BasicHolder { + T element; + void set(T arg) { element = arg; } + T get() { return element; } + void f() { + System.out.println(element.getClass().getSimpleName()); + } +} ///:~ diff --git a/src/generics/ByteSet.java b/src/generics/ByteSet.java new file mode 100644 index 0000000..7a83c6f --- /dev/null +++ b/src/generics/ByteSet.java @@ -0,0 +1,11 @@ +package generics;//: generics/ByteSet.java +import java.util.*; + +public class ByteSet { + Byte[] possibles = { 1,2,3,4,5,6,7,8,9 }; + Set mySet = + new HashSet(Arrays.asList(possibles)); + // But you can't do this: + // Set mySet2 = new HashSet( + // Arrays.asList(1,2,3,4,5,6,7,8,9)); +} ///:~ diff --git a/src/generics/CRGWithBasicHolder.java b/src/generics/CRGWithBasicHolder.java new file mode 100644 index 0000000..c99751b --- /dev/null +++ b/src/generics/CRGWithBasicHolder.java @@ -0,0 +1,14 @@ +package generics;//: generics/CRGWithBasicHolder.java + +class Subtype extends BasicHolder {} + +public class CRGWithBasicHolder { + public static void main(String[] args) { + Subtype st1 = new Subtype(), st2 = new Subtype(); + st1.set(st2); + Subtype st3 = st1.get(); + st1.f(); + } +} /* Output: +Subtype +*///:~ diff --git a/src/generics/CaptureConversion.java b/src/generics/CaptureConversion.java new file mode 100644 index 0000000..a47e497 --- /dev/null +++ b/src/generics/CaptureConversion.java @@ -0,0 +1,27 @@ +package generics;//: generics/CaptureConversion.java + +public class CaptureConversion { + static void f1(Holder holder) { + T t = holder.get(); + System.out.println(t.getClass().getSimpleName()); + } + static void f2(Holder holder) { + f1(holder); // Call with captured type + } + @SuppressWarnings("unchecked") + public static void main(String[] args) { + Holder raw = new Holder(1); + // f1(raw); // Produces warnings + f2(raw); // No warnings + Holder rawBasic = new Holder(); + rawBasic.set(new Object()); // Warning + f2(rawBasic); // No warnings + // Upcast to Holder, still figures it out: + Holder wildcarded = new Holder(1.0); + f2(wildcarded); + } +} /* Output: +Integer +Object +Double +*///:~ diff --git a/src/generics/CheckedList.java b/src/generics/CheckedList.java new file mode 100644 index 0000000..0743ee6 --- /dev/null +++ b/src/generics/CheckedList.java @@ -0,0 +1,29 @@ +package generics;//: generics/CheckedList.java +// Using Collection.checkedList(). +import typeinfo.pets.*; +import java.util.*; + +public class CheckedList { + @SuppressWarnings("unchecked") + static void oldStyleMethod(List probablyDogs) { + probablyDogs.add(new Cat()); + } + public static void main(String[] args) { + List dogs1 = new ArrayList(); + oldStyleMethod(dogs1); // Quietly accepts a Cat + List dogs2 = Collections.checkedList( + new ArrayList(), Dog.class); + try { + oldStyleMethod(dogs2); // Throws an exception + } catch(Exception e) { + System.out.println(e); + } + // Derived types work fine: + List pets = Collections.checkedList( + new ArrayList(), Pet.class); + pets.add(new Dog()); + pets.add(new Cat()); + } +} /* Output: +java.lang.ClassCastException: Attempt to insert class typeinfo.pets.Cat element into collection with element type class typeinfo.pets.Dog +*///:~ diff --git a/src/generics/ClassCasting.java b/src/generics/ClassCasting.java new file mode 100644 index 0000000..af80d27 --- /dev/null +++ b/src/generics/ClassCasting.java @@ -0,0 +1,15 @@ +package generics;//: generics/ClassCasting.java +import java.io.*; +import java.util.*; + +public class ClassCasting { + @SuppressWarnings("unchecked") + public void f(String[] args) throws Exception { + ObjectInputStream in = new ObjectInputStream( + new FileInputStream(args[0])); + // Won't Compile: +// List lw1 = +// List.class.cast(in.readObject()); + List lw2 = List.class.cast(in.readObject()); + } +} ///:~ diff --git a/src/generics/ClassTypeCapture.java b/src/generics/ClassTypeCapture.java new file mode 100644 index 0000000..36f6d67 --- /dev/null +++ b/src/generics/ClassTypeCapture.java @@ -0,0 +1,29 @@ +package generics;//: generics/ClassTypeCapture.java + +class Building {} +class House extends Building {} + +public class ClassTypeCapture { + Class kind; + public ClassTypeCapture(Class kind) { + this.kind = kind; + } + public boolean f(Object arg) { + return kind.isInstance(arg); + } + public static void main(String[] args) { + ClassTypeCapture ctt1 = + new ClassTypeCapture(Building.class); + System.out.println(ctt1.f(new Building())); + System.out.println(ctt1.f(new House())); + ClassTypeCapture ctt2 = + new ClassTypeCapture(House.class); + System.out.println(ctt2.f(new Building())); + System.out.println(ctt2.f(new House())); + } +} /* Output: +true +true +false +true +*///:~ diff --git a/src/generics/ComparablePet.java b/src/generics/ComparablePet.java new file mode 100644 index 0000000..1d5edac --- /dev/null +++ b/src/generics/ComparablePet.java @@ -0,0 +1,6 @@ +package generics;//: generics/ComparablePet.java + +public class ComparablePet +implements Comparable { + public int compareTo(ComparablePet arg) { return 0; } +} ///:~ diff --git a/src/generics/CompilerIntelligence.java b/src/generics/CompilerIntelligence.java new file mode 100644 index 0000000..09ce30b --- /dev/null +++ b/src/generics/CompilerIntelligence.java @@ -0,0 +1,12 @@ +package generics;//: generics/CompilerIntelligence.java +import java.util.*; + +public class CompilerIntelligence { + public static void main(String[] args) { + List flist = + Arrays.asList(new Apple()); + Apple a = (Apple)flist.get(0); // No warning + flist.contains(new Apple()); // Argument is 'Object' + flist.indexOf(new Apple()); // Argument is 'Object' + } +} ///:~ diff --git a/src/generics/CountedObject.java b/src/generics/CountedObject.java new file mode 100644 index 0000000..15b6456 --- /dev/null +++ b/src/generics/CountedObject.java @@ -0,0 +1,8 @@ +package generics;//: generics/CountedObject.java + +public class CountedObject { + private static long counter = 0; + private final long id = counter++; + public long id() { return id; } + public String toString() { return "CountedObject " + id;} +} ///:~ diff --git a/src/generics/CovariantArrays.java b/src/generics/CovariantArrays.java new file mode 100644 index 0000000..585fb2b --- /dev/null +++ b/src/generics/CovariantArrays.java @@ -0,0 +1,26 @@ +package generics;//: generics/CovariantArrays.java + +class Fruit {} +class Apple extends Fruit {} +class Jonathan extends Apple {} +class Orange extends Fruit {} + +public class CovariantArrays { + public static void main(String[] args) { + Fruit[] fruit = new Apple[10]; + fruit[0] = new Apple(); // OK + fruit[1] = new Jonathan(); // OK + // Runtime type is Apple[], not Fruit[] or Orange[]: + try { + // Compiler allows you to add Fruit: + fruit[0] = new Fruit(); // ArrayStoreException + } catch(Exception e) { System.out.println(e); } + try { + // Compiler allows you to add Oranges: + fruit[0] = new Orange(); // ArrayStoreException + } catch(Exception e) { System.out.println(e); } + } +} /* Output: +java.lang.ArrayStoreException: Fruit +java.lang.ArrayStoreException: Orange +*///:~ diff --git a/src/generics/CovariantReturnTypes.java b/src/generics/CovariantReturnTypes.java new file mode 100644 index 0000000..68ef99e --- /dev/null +++ b/src/generics/CovariantReturnTypes.java @@ -0,0 +1,19 @@ +package generics;//: generics/CovariantReturnTypes.java + +class Base {} +class Derived extends Base {} + +interface OrdinaryGetter { + Base get(); +} + +interface DerivedGetter extends OrdinaryGetter { + // Return type of overridden method is allowed to vary: + Derived get(); +} + +public class CovariantReturnTypes { + void test(DerivedGetter d) { + Derived d2 = d.get(); + } +} ///:~ diff --git a/src/generics/CreatorGeneric.java b/src/generics/CreatorGeneric.java new file mode 100644 index 0000000..07c870e --- /dev/null +++ b/src/generics/CreatorGeneric.java @@ -0,0 +1,25 @@ +package generics;//: generics/CreatorGeneric.java + +abstract class GenericWithCreate { + final T element; + GenericWithCreate() { element = create(); } + abstract T create(); +} + +class X {} + +class Creator extends GenericWithCreate { + X create() { return new X(); } + void f() { + System.out.println(element.getClass().getSimpleName()); + } +} + +public class CreatorGeneric { + public static void main(String[] args) { + Creator c = new Creator(); + c.f(); + } +} /* Output: +X +*///:~ diff --git a/src/generics/CuriouslyRecurringGeneric.java b/src/generics/CuriouslyRecurringGeneric.java new file mode 100644 index 0000000..99612d9 --- /dev/null +++ b/src/generics/CuriouslyRecurringGeneric.java @@ -0,0 +1,6 @@ +package generics;//: generics/CuriouslyRecurringGeneric.java + +class GenericType {} + +public class CuriouslyRecurringGeneric + extends GenericType {} ///:~ diff --git a/src/generics/DogsAndRobots.cpp b/src/generics/DogsAndRobots.cpp new file mode 100644 index 0000000..0947e78 --- /dev/null +++ b/src/generics/DogsAndRobots.cpp @@ -0,0 +1,27 @@ +//: generics/DogsAndRobots.cpp + +class Dog { +public: + void speak() {} + void sit() {} + void reproduce() {} +}; + +class Robot { +public: + void speak() {} + void sit() {} + void oilChange() { +}; + +template void perform(T anything) { + anything.speak(); + anything.sit(); +} + +int main() { + Dog d; + Robot r; + perform(d); + perform(r); +} ///:~ diff --git a/src/generics/DogsAndRobots.java b/src/generics/DogsAndRobots.java new file mode 100644 index 0000000..5ec056f --- /dev/null +++ b/src/generics/DogsAndRobots.java @@ -0,0 +1,38 @@ +package generics;//: generics/DogsAndRobots.java +// No latent typing in Java +import typeinfo.pets.*; +import static net.mindview.util.Print.*; + +class PerformingDog extends Dog implements Performs { + public void speak() { print("Woof!"); } + public void sit() { print("Sitting"); } + public void reproduce() {} +} + +class Robot implements Performs { + public void speak() { print("Click!"); } + public void sit() { print("Clank!"); } + public void oilChange() {} +} + +class Communicate { + public static + void perform(T performer) { + performer.speak(); + performer.sit(); + } +} + +public class DogsAndRobots { + public static void main(String[] args) { + PerformingDog d = new PerformingDog(); + Robot r = new Robot(); + Communicate.perform(d); + Communicate.perform(r); + } +} /* Output: +Woof! +Sitting +Click! +Clank! +*///:~ diff --git a/src/generics/DynamicProxyMixin.java b/src/generics/DynamicProxyMixin.java new file mode 100644 index 0000000..931a756 --- /dev/null +++ b/src/generics/DynamicProxyMixin.java @@ -0,0 +1,59 @@ +package generics;//: generics/DynamicProxyMixin.java +import java.lang.reflect.*; +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Tuple.*; + +class MixinProxy implements InvocationHandler { + Map delegatesByMethod; + public MixinProxy(TwoTuple>... pairs) { + delegatesByMethod = new HashMap(); + for(TwoTuple> pair : pairs) { + for(Method method : pair.second.getMethods()) { + String methodName = method.getName(); + // The first interface in the map + // implements the method. + if (!delegatesByMethod.containsKey(methodName)) { + delegatesByMethod.put(methodName, pair.first); + } + } + } + } + public Object invoke(Object proxy, Method method, + Object[] args) throws Throwable { + String methodName = method.getName(); + Object delegate = delegatesByMethod.get(methodName); + return method.invoke(delegate, args); + } + @SuppressWarnings("unchecked") + public static Object newInstance(TwoTuple... pairs) { + Class[] interfaces = new Class[pairs.length]; + for(int i = 0; i < pairs.length; i++) { + interfaces[i] = (Class)pairs[i].second; + } + ClassLoader cl = + pairs[0].first.getClass().getClassLoader(); + return Proxy.newProxyInstance( + cl, interfaces, new MixinProxy(pairs)); + } +} + +public class DynamicProxyMixin { + public static void main(String[] args) { + Object mixin = MixinProxy.newInstance( + tuple(new BasicImp(), Basic.class), + tuple(new TimeStampedImp(), TimeStamped.class), + tuple(new SerialNumberedImp(),SerialNumbered.class)); + Basic b = (Basic)mixin; + TimeStamped t = (TimeStamped)mixin; + SerialNumbered s = (SerialNumbered)mixin; + b.set("Hello"); + System.out.println(b.get()); + System.out.println(t.getStamp()); + System.out.println(s.getSerialNumber()); + } +} /* Output: (Sample) +Hello +1132519137015 +1 +*///:~ diff --git a/src/generics/EpicBattle.java b/src/generics/EpicBattle.java new file mode 100644 index 0000000..61f8381 --- /dev/null +++ b/src/generics/EpicBattle.java @@ -0,0 +1,64 @@ +package generics;//: generics/EpicBattle.java +// Demonstrating bounds in Java generics. +import java.util.*; + +interface SuperPower {} +interface XRayVision extends SuperPower { + void seeThroughWalls(); +} +interface SuperHearing extends SuperPower { + void hearSubtleNoises(); +} +interface SuperSmell extends SuperPower { + void trackBySmell(); +} + +class SuperHero { + POWER power; + SuperHero(POWER power) { this.power = power; } + POWER getPower() { return power; } +} + +class SuperSleuth +extends SuperHero { + SuperSleuth(POWER power) { super(power); } + void see() { power.seeThroughWalls(); } +} + +class CanineHero +extends SuperHero { + CanineHero(POWER power) { super(power); } + void hear() { power.hearSubtleNoises(); } + void smell() { power.trackBySmell(); } +} + +class SuperHearSmell implements SuperHearing, SuperSmell { + public void hearSubtleNoises() {} + public void trackBySmell() {} +} + +class DogBoy extends CanineHero { + DogBoy() { super(new SuperHearSmell()); } +} + +public class EpicBattle { + // Bounds in generic methods: + static + void useSuperHearing(SuperHero hero) { + hero.getPower().hearSubtleNoises(); + } + static + void superFind(SuperHero hero) { + hero.getPower().hearSubtleNoises(); + hero.getPower().trackBySmell(); + } + public static void main(String[] args) { + DogBoy dogBoy = new DogBoy(); + useSuperHearing(dogBoy); + superFind(dogBoy); + // You can do this: + List audioBoys; + // But you can't do this: + // List dogBoys; + } +} ///:~ diff --git a/src/generics/Erased.java b/src/generics/Erased.java new file mode 100644 index 0000000..09d2b4a --- /dev/null +++ b/src/generics/Erased.java @@ -0,0 +1,12 @@ +package generics;//: generics/Erased.java +// {CompileTimeError} (Won't compile) + +public class Erased { + private final int SIZE = 100; + public static void f(Object arg) { + if(arg instanceof T) {} // Error + T var = new T(); // Error + T[] array = new T[SIZE]; // Error + T[] array = (T)new Object[SIZE]; // Unchecked warning + } +} ///:~ diff --git a/src/generics/ErasedTypeEquivalence.java b/src/generics/ErasedTypeEquivalence.java new file mode 100644 index 0000000..8b31569 --- /dev/null +++ b/src/generics/ErasedTypeEquivalence.java @@ -0,0 +1,12 @@ +package generics;//: generics/ErasedTypeEquivalence.java +import java.util.*; + +public class ErasedTypeEquivalence { + public static void main(String[] args) { + Class c1 = new ArrayList().getClass(); + Class c2 = new ArrayList().getClass(); + System.out.println(c1 == c2); + } +} /* Output: +true +*///:~ diff --git a/src/generics/ErasureAndInheritance.java b/src/generics/ErasureAndInheritance.java new file mode 100644 index 0000000..f531f64 --- /dev/null +++ b/src/generics/ErasureAndInheritance.java @@ -0,0 +1,25 @@ +package generics;//: generics/ErasureAndInheritance.java + +class GenericBase { + private T element; + public void set(T arg) { arg = element; } + public T get() { return element; } +} + +class Derived1 extends GenericBase {} + +class Derived2 extends GenericBase {} // No warning + +// class Derived3 extends GenericBase {} +// Strange error: +// unexpected type found : ? +// required: class or interface without bounds + +public class ErasureAndInheritance { + @SuppressWarnings("unchecked") + public static void main(String[] args) { + Derived2 d2 = new Derived2(); + Object obj = d2.get(); + d2.set(obj); // Warning here! + } +} ///:~ diff --git a/src/generics/ExplicitTypeSpecification.java b/src/generics/ExplicitTypeSpecification.java new file mode 100644 index 0000000..05dd5cd --- /dev/null +++ b/src/generics/ExplicitTypeSpecification.java @@ -0,0 +1,11 @@ +package generics;//: generics/ExplicitTypeSpecification.java +import typeinfo.pets.*; +import java.util.*; +import net.mindview.util.*; + +public class ExplicitTypeSpecification { + static void f(Map> petPeople) {} + public static void main(String[] args) { + f(New.>map()); + } +} ///:~ diff --git a/src/generics/FactoryConstraint.java b/src/generics/FactoryConstraint.java new file mode 100644 index 0000000..3b0756a --- /dev/null +++ b/src/generics/FactoryConstraint.java @@ -0,0 +1,34 @@ +package generics;//: generics/FactoryConstraint.java + +interface FactoryI { + T create(); +} + +class Foo2 { + private T x; + public > Foo2(F factory) { + x = factory.create(); + } + // ... +} + +class IntegerFactory implements FactoryI { + public Integer create() { + return new Integer(0); + } +} + +class Widget { + public static class Factory implements FactoryI { + public Widget create() { + return new Widget(); + } + } +} + +public class FactoryConstraint { + public static void main(String[] args) { + new Foo2(new IntegerFactory()); + new Foo2(new Widget.Factory()); + } +} ///:~ diff --git a/src/generics/Fibonacci.java b/src/generics/Fibonacci.java new file mode 100644 index 0000000..23b87ce --- /dev/null +++ b/src/generics/Fibonacci.java @@ -0,0 +1,22 @@ +package generics;//: generics/Fibonacci.java +// Generate a Fibonacci sequence. +import net.mindview.util.*; + +public class Fibonacci implements Generator { + private int count = 0; + public Integer next() { return fib(count++); } + private int fib(int n) { + if(n < 2) { + return 1; + } + return fib(n-2) + fib(n-1); + } + public static void main(String[] args) { + Fibonacci gen = new Fibonacci(); + for(int i = 0; i < 18; i++) { + System.out.print(gen.next() + " "); + } + } +} /* Output: +1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 +*///:~ diff --git a/src/generics/Fill.java b/src/generics/Fill.java new file mode 100644 index 0000000..94ff6df --- /dev/null +++ b/src/generics/Fill.java @@ -0,0 +1,54 @@ +package generics;//: generics/Fill.java +// Generalizing the FilledList idea +// {main: FillTest} +import java.util.*; + +// Doesn't work with "anything that has an add()." There is +// no "Addable" interface so we are narrowed to using a +// Collection. We cannot generalize using generics in +// this case. +public class Fill { + public static void fill(Collection collection, + Class classToken, int size) { + for(int i = 0; i < size; i++) + // Assumes default constructor: + { + try { + collection.add(classToken.newInstance()); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + } +} + +class Contract { + private static long counter = 0; + private final long id = counter++; + public String toString() { + return getClass().getName() + " " + id; + } +} + +class TitleTransfer extends Contract {} + +class FillTest { + public static void main(String[] args) { + List contracts = new ArrayList(); + Fill.fill(contracts, Contract.class, 3); + Fill.fill(contracts, TitleTransfer.class, 2); + for(Contract c: contracts) { + System.out.println(c); + } + SimpleQueue contractQueue = + new SimpleQueue(); + // Won't work. fill() is not generic enough: + // Fill.fill(contractQueue, Contract.class, 3); + } +} /* Output: +Contract 0 +Contract 1 +Contract 2 +TitleTransfer 3 +TitleTransfer 4 +*///:~ diff --git a/src/generics/Fill2.java b/src/generics/Fill2.java new file mode 100644 index 0000000..0055f3b --- /dev/null +++ b/src/generics/Fill2.java @@ -0,0 +1,92 @@ +package generics;//: generics/Fill2.java +// Using adapters to simulate latent typing. +// {main: Fill2Test} +import generics.coffee.*; +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +interface Addable { void add(T t); } + +public class Fill2 { + // Classtoken version: + public static void fill(Addable addable, + Class classToken, int size) { + for(int i = 0; i < size; i++) { + try { + addable.add(classToken.newInstance()); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + } + // Generator version: + public static void fill(Addable addable, + Generator generator, int size) { + for(int i = 0; i < size; i++) { + addable.add(generator.next()); + } + } +} + +// To adapt a base type, you must use composition. +// Make any Collection Addable using composition: +class AddableCollectionAdapter implements Addable { + private Collection c; + public AddableCollectionAdapter(Collection c) { + this.c = c; + } + public void add(T item) { c.add(item); } +} + +// A Helper to capture the type automatically: +class Adapter { + public static + Addable collectionAdapter(Collection c) { + return new AddableCollectionAdapter(c); + } +} + +// To adapt a specific type, you can use inheritance. +// Make a SimpleQueue Addable using inheritance: +class AddableSimpleQueue +extends SimpleQueue implements Addable { + public void add(T item) { super.add(item); } +} + +class Fill2Test { + public static void main(String[] args) { + // Adapt a Collection: + List carrier = new ArrayList(); + Fill2.fill( + new AddableCollectionAdapter(carrier), + Coffee.class, 3); + // Helper method captures the type: + Fill2.fill(Adapter.collectionAdapter(carrier), + Latte.class, 2); + for(Coffee c: carrier) { + print(c); + } + print("----------------------"); + // Use an adapted class: + AddableSimpleQueue coffeeQueue = + new AddableSimpleQueue(); + Fill2.fill(coffeeQueue, Mocha.class, 4); + Fill2.fill(coffeeQueue, Latte.class, 1); + for(Coffee c: coffeeQueue) { + print(c); + } + } +} /* Output: +Coffee 0 +Coffee 1 +Coffee 2 +Latte 3 +Latte 4 +---------------------- +Mocha 5 +Mocha 6 +Mocha 7 +Mocha 8 +Latte 9 +*///:~ diff --git a/src/generics/FilledListMaker.java b/src/generics/FilledListMaker.java new file mode 100644 index 0000000..84a4cbb --- /dev/null +++ b/src/generics/FilledListMaker.java @@ -0,0 +1,20 @@ +package generics;//: generics/FilledListMaker.java +import java.util.*; + +public class FilledListMaker { + List create(T t, int n) { + List result = new ArrayList(); + for(int i = 0; i < n; i++) { + result.add(t); + } + return result; + } + public static void main(String[] args) { + FilledListMaker stringMaker = + new FilledListMaker(); + List list = stringMaker.create("Hello", 4); + System.out.println(list); + } +} /* Output: +[Hello, Hello, Hello, Hello] +*///:~ diff --git a/src/generics/Functional.java b/src/generics/Functional.java new file mode 100644 index 0000000..2f3b83c --- /dev/null +++ b/src/generics/Functional.java @@ -0,0 +1,183 @@ +package generics;//: generics/Functional.java +import java.math.*; +import java.util.concurrent.atomic.*; +import java.util.*; +import static net.mindview.util.Print.*; + +// Different types of function objects: +interface Combiner { T combine(T x, T y); } +interface UnaryFunction { R function(T x); } +interface Collector extends UnaryFunction { + T result(); // Extract result of collecting parameter +} +interface UnaryPredicate { boolean test(T x); } + +public class Functional { + // Calls the Combiner object on each element to combine + // it with a running result, which is finally returned: + public static T + reduce(Iterable seq, Combiner combiner) { + Iterator it = seq.iterator(); + if(it.hasNext()) { + T result = it.next(); + while(it.hasNext()) { + result = combiner.combine(result, it.next()); + } + return result; + } + // If seq is the empty list: + return null; // Or throw exception + } + // Take a function object and call it on each object in + // the list, ignoring the return value. The function + // object may act as a collecting parameter, so it is + // returned at the end. + public static Collector + forEach(Iterable seq, Collector func) { + for(T t : seq) { + func.function(t); + } + return func; + } + // Creates a list of results by calling a + // function object for each object in the list: + public static List + transform(Iterable seq, UnaryFunction func) { + List result = new ArrayList(); + for(T t : seq) { + result.add(func.function(t)); + } + return result; + } + // Applies a unary predicate to each item in a sequence, + // and returns a list of items that produced "true": + public static List + filter(Iterable seq, UnaryPredicate pred) { + List result = new ArrayList(); + for(T t : seq) { + if(pred.test(t)) { + result.add(t); + } + } + return result; + } + // To use the above generic methods, we need to create + // function objects to adapt to our particular needs: + static class IntegerAdder implements Combiner { + public Integer combine(Integer x, Integer y) { + return x + y; + } + } + static class + IntegerSubtracter implements Combiner { + public Integer combine(Integer x, Integer y) { + return x - y; + } + } + static class + BigDecimalAdder implements Combiner { + public BigDecimal combine(BigDecimal x, BigDecimal y) { + return x.add(y); + } + } + static class + BigIntegerAdder implements Combiner { + public BigInteger combine(BigInteger x, BigInteger y) { + return x.add(y); + } + } + static class + AtomicLongAdder implements Combiner { + public AtomicLong combine(AtomicLong x, AtomicLong y) { + // Not clear whether this is meaningful: + return new AtomicLong(x.addAndGet(y.get())); + } + } + // We can even make a UnaryFunction with an "ulp" + // (Units in the last place): + static class BigDecimalUlp + implements UnaryFunction { + public BigDecimal function(BigDecimal x) { + return x.ulp(); + } + } + static class GreaterThan> + implements UnaryPredicate { + private T bound; + public GreaterThan(T bound) { this.bound = bound; } + public boolean test(T x) { + return x.compareTo(bound) > 0; + } + } + static class MultiplyingIntegerCollector + implements Collector { + private Integer val = 1; + public Integer function(Integer x) { + val *= x; + return val; + } + public Integer result() { return val; } + } + public static void main(String[] args) { + // Generics, varargs & boxing working together: + List li = Arrays.asList(1, 2, 3, 4, 5, 6, 7); + Integer result = reduce(li, new IntegerAdder()); + print(result); + + result = reduce(li, new IntegerSubtracter()); + print(result); + + print(filter(li, new GreaterThan(4))); + + print(forEach(li, + new MultiplyingIntegerCollector()).result()); + + print(forEach(filter(li, new GreaterThan(4)), + new MultiplyingIntegerCollector()).result()); + + MathContext mc = new MathContext(7); + List lbd = Arrays.asList( + new BigDecimal(1.1, mc), new BigDecimal(2.2, mc), + new BigDecimal(3.3, mc), new BigDecimal(4.4, mc)); + BigDecimal rbd = reduce(lbd, new BigDecimalAdder()); + print(rbd); + + print(filter(lbd, + new GreaterThan(new BigDecimal(3)))); + + // Use the prime-generation facility of BigInteger: + List lbi = new ArrayList(); + BigInteger bi = BigInteger.valueOf(11); + for(int i = 0; i < 11; i++) { + lbi.add(bi); + bi = bi.nextProbablePrime(); + } + print(lbi); + + BigInteger rbi = reduce(lbi, new BigIntegerAdder()); + print(rbi); + // The sum of this list of primes is also prime: + print(rbi.isProbablePrime(5)); + + List lal = Arrays.asList( + new AtomicLong(11), new AtomicLong(47), + new AtomicLong(74), new AtomicLong(133)); + AtomicLong ral = reduce(lal, new AtomicLongAdder()); + print(ral); + + print(transform(lbd,new BigDecimalUlp())); + } +} /* Output: +28 +-26 +[5, 6, 7] +5040 +210 +11.000000 +[3.300000, 4.400000] +[11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47] +311 +true +265 +[0.000001, 0.000001, 0.000001, 0.000001] +*///:~ diff --git a/src/generics/Generators.java b/src/generics/Generators.java new file mode 100644 index 0000000..6b441f9 --- /dev/null +++ b/src/generics/Generators.java @@ -0,0 +1,33 @@ +package generics;//: generics/Generators.java +// A utility to use with Generators. +import generics.coffee.*; +import java.util.*; +import net.mindview.util.*; + +public class Generators { + public static Collection + fill(Collection coll, Generator gen, int n) { + for(int i = 0; i < n; i++) { + coll.add(gen.next()); + } + return coll; + } + public static void main(String[] args) { + Collection coffee = fill( + new ArrayList(), new CoffeeGenerator(), 4); + for(Coffee c : coffee) { + System.out.println(c); + } + Collection fnumbers = fill( + new ArrayList(), new Fibonacci(), 12); + for(int i : fnumbers) { + System.out.print(i + ", "); + } + } +} /* Output: +Americano 0 +Latte 1 +Americano 2 +Mocha 3 +1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, +*///:~ diff --git a/src/generics/GenericArray.java b/src/generics/GenericArray.java new file mode 100644 index 0000000..1cb88dc --- /dev/null +++ b/src/generics/GenericArray.java @@ -0,0 +1,23 @@ +package generics;//: generics/GenericArray.java + +public class GenericArray { + private T[] array; + @SuppressWarnings("unchecked") + public GenericArray(int sz) { + array = (T[])new Object[sz]; + } + public void put(int index, T item) { + array[index] = item; + } + public T get(int index) { return array[index]; } + // Method that exposes the underlying representation: + public T[] rep() { return array; } + public static void main(String[] args) { + GenericArray gai = + new GenericArray(10); + // This causes a ClassCastException: + //! Integer[] ia = gai.rep(); + // This is OK: + Object[] oa = gai.rep(); + } +} ///:~ diff --git a/src/generics/GenericArray2.java b/src/generics/GenericArray2.java new file mode 100644 index 0000000..2132d71 --- /dev/null +++ b/src/generics/GenericArray2.java @@ -0,0 +1,34 @@ +package generics;//: generics/GenericArray2.java + +public class GenericArray2 { + private Object[] array; + public GenericArray2(int sz) { + array = new Object[sz]; + } + public void put(int index, T item) { + array[index] = item; + } + @SuppressWarnings("unchecked") + public T get(int index) { return (T)array[index]; } + @SuppressWarnings("unchecked") + public T[] rep() { + return (T[])array; // Warning: unchecked cast + } + public static void main(String[] args) { + GenericArray2 gai = + new GenericArray2(10); + for(int i = 0; i < 10; i ++) { + gai.put(i, i); + } + for(int i = 0; i < 10; i ++) { + System.out.print(gai.get(i) + " "); + } + System.out.println(); + try { + Integer[] ia = gai.rep(); + } catch(Exception e) { System.out.println(e); } + } +} /* Output: (Sample) +0 1 2 3 4 5 6 7 8 9 +java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer; +*///:~ diff --git a/src/generics/GenericArrayWithTypeToken.java b/src/generics/GenericArrayWithTypeToken.java new file mode 100644 index 0000000..5c15c74 --- /dev/null +++ b/src/generics/GenericArrayWithTypeToken.java @@ -0,0 +1,23 @@ +package generics;//: generics/GenericArrayWithTypeToken.java +import java.lang.reflect.*; + +public class GenericArrayWithTypeToken { + private T[] array; + @SuppressWarnings("unchecked") + public GenericArrayWithTypeToken(Class type, int sz) { + array = (T[])Array.newInstance(type, sz); + } + public void put(int index, T item) { + array[index] = item; + } + public T get(int index) { return array[index]; } + // Expose the underlying representation: + public T[] rep() { return array; } + public static void main(String[] args) { + GenericArrayWithTypeToken gai = + new GenericArrayWithTypeToken( + Integer.class, 10); + // This now works: + Integer[] ia = gai.rep(); + } +} ///:~ diff --git a/src/generics/GenericCast.java b/src/generics/GenericCast.java new file mode 100644 index 0000000..61a3740 --- /dev/null +++ b/src/generics/GenericCast.java @@ -0,0 +1,29 @@ +package generics;//: generics/GenericCast.java + +class FixedSizeStack { + private int index = 0; + private Object[] storage; + public FixedSizeStack(int size) { + storage = new Object[size]; + } + public void push(T item) { storage[index++] = item; } + @SuppressWarnings("unchecked") + public T pop() { return (T)storage[--index]; } +} + +public class GenericCast { + public static final int SIZE = 10; + public static void main(String[] args) { + FixedSizeStack strings = + new FixedSizeStack(SIZE); + for(String s : "A B C D E F G H I J".split(" ")) { + strings.push(s); + } + for(int i = 0; i < SIZE; i++) { + String s = strings.pop(); + System.out.print(s + " "); + } + } +} /* Output: +J I H G F E D C B A +*///:~ diff --git a/src/generics/GenericHolder.java b/src/generics/GenericHolder.java new file mode 100644 index 0000000..1815709 --- /dev/null +++ b/src/generics/GenericHolder.java @@ -0,0 +1,13 @@ +package generics;//: generics/GenericHolder.java + +public class GenericHolder { + private T obj; + public void set(T obj) { this.obj = obj; } + public T get() { return obj; } + public static void main(String[] args) { + GenericHolder holder = + new GenericHolder(); + holder.set("Item"); + String s = holder.get(); + } +} ///:~ diff --git a/src/generics/GenericMethods.java b/src/generics/GenericMethods.java new file mode 100644 index 0000000..e31a5d2 --- /dev/null +++ b/src/generics/GenericMethods.java @@ -0,0 +1,23 @@ +package generics;//: generics/GenericMethods.java + +public class GenericMethods { + public void f(T x) { + System.out.println(x.getClass().getName()); + } + public static void main(String[] args) { + GenericMethods gm = new GenericMethods(); + gm.f(""); + gm.f(1); + gm.f(1.0); + gm.f(1.0F); + gm.f('c'); + gm.f(gm); + } +} /* Output: +java.lang.String +java.lang.Integer +java.lang.Double +java.lang.Float +java.lang.Character +GenericMethods +*///:~ diff --git a/src/generics/GenericReading.java b/src/generics/GenericReading.java new file mode 100644 index 0000000..0d4fc8c --- /dev/null +++ b/src/generics/GenericReading.java @@ -0,0 +1,42 @@ +package generics;//: generics/GenericReading.java +import java.util.*; + +public class GenericReading { + static T readExact(List list) { + return list.get(0); + } + static List apples = Arrays.asList(new Apple()); + static List fruit = Arrays.asList(new Fruit()); + // A static method adapts to each call: + static void f1() { + Apple a = readExact(apples); + Fruit f = readExact(fruit); + f = readExact(apples); + } + // If, however, you have a class, then its type is + // established when the class is instantiated: + static class Reader { + T readExact(List list) { return list.get(0); } + } + static void f2() { + Reader fruitReader = new Reader(); + Fruit f = fruitReader.readExact(fruit); + // Fruit a = fruitReader.readExact(apples); // Error: + // readExact(List) cannot be + // applied to (List). + } + static class CovariantReader { + T readCovariant(List list) { + return list.get(0); + } + } + static void f3() { + CovariantReader fruitReader = + new CovariantReader(); + Fruit f = fruitReader.readCovariant(fruit); + Fruit a = fruitReader.readCovariant(apples); + } + public static void main(String[] args) { + f1(); f2(); f3(); + } +} ///:~ diff --git a/src/generics/GenericVarargs.java b/src/generics/GenericVarargs.java new file mode 100644 index 0000000..89a841b --- /dev/null +++ b/src/generics/GenericVarargs.java @@ -0,0 +1,24 @@ +package generics;//: generics/GenericVarargs.java +import java.util.*; + +public class GenericVarargs { + public static List makeList(T... args) { + List result = new ArrayList(); + for(T item : args) { + result.add(item); + } + return result; + } + public static void main(String[] args) { + List ls = makeList("A"); + System.out.println(ls); + ls = makeList("A", "B", "C"); + System.out.println(ls); + ls = makeList("ABCDEFFHIJKLMNOPQRSTUVWXYZ".split("")); + System.out.println(ls); + } +} /* Output: +[A] +[A, B, C] +[, A, B, C, D, E, F, F, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z] +*///:~ diff --git a/src/generics/GenericWriting.java b/src/generics/GenericWriting.java new file mode 100644 index 0000000..2cb90e5 --- /dev/null +++ b/src/generics/GenericWriting.java @@ -0,0 +1,24 @@ +package generics;//: generics/GenericWriting.java +import java.util.*; + +public class GenericWriting { + static void writeExact(List list, T item) { + list.add(item); + } + static List apples = new ArrayList(); + static List fruit = new ArrayList(); + static void f1() { + writeExact(apples, new Apple()); + // writeExact(fruit, new Apple()); // Error: + // Incompatible types: found Fruit, required Apple + } + static void + writeWithWildcard(List list, T item) { + list.add(item); + } + static void f2() { + writeWithWildcard(apples, new Apple()); + writeWithWildcard(fruit, new Apple()); + } + public static void main(String[] args) { f1(); f2(); } +} ///:~ diff --git a/src/generics/GenericsAndCovariance.java b/src/generics/GenericsAndCovariance.java new file mode 100644 index 0000000..56468eb --- /dev/null +++ b/src/generics/GenericsAndCovariance.java @@ -0,0 +1,16 @@ +package generics;//: generics/GenericsAndCovariance.java +import java.util.*; + +public class GenericsAndCovariance { + public static void main(String[] args) { + // Wildcards allow covariance: + List flist = new ArrayList(); + // Compile Error: can't add any type of object: + // flist.add(new Apple()); + // flist.add(new Fruit()); + // flist.add(new Object()); + flist.add(null); // Legal but uninteresting + // We know that it returns at least Fruit: + Fruit f = flist.get(0); + } +} ///:~ diff --git a/src/generics/GenericsAndReturnTypes.java b/src/generics/GenericsAndReturnTypes.java new file mode 100644 index 0000000..8576f11 --- /dev/null +++ b/src/generics/GenericsAndReturnTypes.java @@ -0,0 +1,14 @@ +package generics;//: generics/GenericsAndReturnTypes.java + +interface GenericGetter> { + T get(); +} + +interface Getter extends GenericGetter {} + +public class GenericsAndReturnTypes { + void test(Getter g) { + Getter result = g.get(); + GenericGetter gg = g.get(); // Also the base type + } +} ///:~ diff --git a/src/generics/HasF.java b/src/generics/HasF.java new file mode 100644 index 0000000..50a0d97 --- /dev/null +++ b/src/generics/HasF.java @@ -0,0 +1,5 @@ +package generics;//: generics/HasF.java + +public class HasF { + public void f() { System.out.println("HasF.f()"); } +} ///:~ diff --git a/src/generics/HijackedInterface.java b/src/generics/HijackedInterface.java new file mode 100644 index 0000000..aff9b50 --- /dev/null +++ b/src/generics/HijackedInterface.java @@ -0,0 +1,8 @@ +package generics;//: generics/HijackedInterface.java +// {CompileTimeError} (Won't compile) + +class Cat extends ComparablePet implements Comparable{ + // Error: Comparable cannot be inherited with + // different arguments: and + public int compareTo(Cat arg) { return 0; } +} ///:~ diff --git a/src/generics/Holder.java b/src/generics/Holder.java new file mode 100644 index 0000000..1a01315 --- /dev/null +++ b/src/generics/Holder.java @@ -0,0 +1,30 @@ +package generics;//: generics/Holder.java + +public class Holder { + private T value; + public Holder() {} + public Holder(T val) { value = val; } + public void set(T val) { value = val; } + public T get() { return value; } + public boolean equals(Object obj) { + return value.equals(obj); + } + public static void main(String[] args) { + Holder Apple = new Holder(new Apple()); + Apple d = Apple.get(); + Apple.set(d); + // Holder Fruit = Apple; // Cannot upcast + Holder fruit = Apple; // OK + Fruit p = fruit.get(); + d = (Apple)fruit.get(); // Returns 'Object' + try { + Orange c = (Orange)fruit.get(); // No warning + } catch(Exception e) { System.out.println(e); } + // fruit.set(new Apple()); // Cannot call set() + // fruit.set(new Fruit()); // Cannot call set() + System.out.println(fruit.equals(d)); // OK + } +} /* Output: (Sample) +java.lang.ClassCastException: Apple cannot be cast to Orange +true +*///:~ diff --git a/src/generics/Holder1.java b/src/generics/Holder1.java new file mode 100644 index 0000000..61d0bb1 --- /dev/null +++ b/src/generics/Holder1.java @@ -0,0 +1,9 @@ +package generics;//: generics/Holder1.java + +class Automobile {} + +public class Holder1 { + private Automobile a; + public Holder1(Automobile a) { this.a = a; } + Automobile get() { return a; } +} ///:~ diff --git a/src/generics/Holder2.java b/src/generics/Holder2.java new file mode 100644 index 0000000..90294d9 --- /dev/null +++ b/src/generics/Holder2.java @@ -0,0 +1,16 @@ +package generics;//: generics/Holder2.java + +public class Holder2 { + private Object a; + public Holder2(Object a) { this.a = a; } + public void set(Object a) { this.a = a; } + public Object get() { return a; } + public static void main(String[] args) { + Holder2 h2 = new Holder2(new Automobile()); + Automobile a = (Automobile)h2.get(); + h2.set("Not an Automobile"); + String s = (String)h2.get(); + h2.set(1); // Autoboxes to Integer + Integer x = (Integer)h2.get(); + } +} ///:~ diff --git a/src/generics/Holder3.java b/src/generics/Holder3.java new file mode 100644 index 0000000..60f3255 --- /dev/null +++ b/src/generics/Holder3.java @@ -0,0 +1,15 @@ +package generics;//: generics/Holder3.java + +public class Holder3 { + private T a; + public Holder3(T a) { this.a = a; } + public void set(T a) { this.a = a; } + public T get() { return a; } + public static void main(String[] args) { + Holder3 h3 = + new Holder3(new Automobile()); + Automobile a = h3.get(); // No cast needed + // h3.set("Not an Automobile"); // Error + // h3.set(1); // Error + } +} ///:~ diff --git a/src/generics/InheritBounds.java b/src/generics/InheritBounds.java new file mode 100644 index 0000000..a05c77c --- /dev/null +++ b/src/generics/InheritBounds.java @@ -0,0 +1,36 @@ +package generics;//: generics/InheritBounds.java + +class HoldItem { + T item; + HoldItem(T item) { this.item = item; } + T getItem() { return item; } +} + +class Colored2 extends HoldItem { + Colored2(T item) { super(item); } + java.awt.Color color() { return item.getColor(); } +} + +class ColoredDimension2 +extends Colored2 { + ColoredDimension2(T item) { super(item); } + int getX() { return item.x; } + int getY() { return item.y; } + int getZ() { return item.z; } +} + +class Solid2 +extends ColoredDimension2 { + Solid2(T item) { super(item); } + int weight() { return item.weight(); } +} + +public class InheritBounds { + public static void main(String[] args) { + Solid2 solid2 = + new Solid2(new Bounded()); + solid2.color(); + solid2.getY(); + solid2.weight(); + } +} ///:~ diff --git a/src/generics/InstantiateGenericType.cpp b/src/generics/InstantiateGenericType.cpp new file mode 100644 index 0000000..4f00cdd --- /dev/null +++ b/src/generics/InstantiateGenericType.cpp @@ -0,0 +1,17 @@ +//: generics/InstantiateGenericType.cpp +// C++, not Java! + +template class Foo { + T x; // Create a field of type T + T* y; // Pointer to T +public: + // Initialize the pointer: + Foo() { y = new T(); } +}; + +class Bar {}; + +int main() { + Foo fb; + Foo fi; // ... and it works with primitives +} ///:~ diff --git a/src/generics/InstantiateGenericType.java b/src/generics/InstantiateGenericType.java new file mode 100644 index 0000000..c419465 --- /dev/null +++ b/src/generics/InstantiateGenericType.java @@ -0,0 +1,32 @@ +package generics;//: generics/InstantiateGenericType.java +import static net.mindview.util.Print.*; + +class ClassAsFactory { + T x; + public ClassAsFactory(Class kind) { + try { + x = kind.newInstance(); + } catch(Exception e) { + throw new RuntimeException(e); + } + } +} + +class Employee {} + +public class InstantiateGenericType { + public static void main(String[] args) { + ClassAsFactory fe = + new ClassAsFactory(Employee.class); + print("ClassAsFactory succeeded"); + try { + ClassAsFactory fi = + new ClassAsFactory(Integer.class); + } catch(Exception e) { + print("ClassAsFactory failed"); + } + } +} /* Output: +ClassAsFactory succeeded +ClassAsFactory failed +*///:~ diff --git a/src/generics/IterableFibonacci.java b/src/generics/IterableFibonacci.java new file mode 100644 index 0000000..43f4f6b --- /dev/null +++ b/src/generics/IterableFibonacci.java @@ -0,0 +1,28 @@ +package generics;//: generics/IterableFibonacci.java +// Adapt the Fibonacci class to make it Iterable. +import java.util.*; + +public class IterableFibonacci +extends Fibonacci implements Iterable { + private int n; + public IterableFibonacci(int count) { n = count; } + public Iterator iterator() { + return new Iterator() { + public boolean hasNext() { return n > 0; } + public Integer next() { + n--; + return IterableFibonacci.this.next(); + } + public void remove() { // Not implemented + throw new UnsupportedOperationException(); + } + }; + } + public static void main(String[] args) { + for(int i : new IterableFibonacci(18)) { + System.out.print(i + " "); + } + } +} /* Output: +1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 +*///:~ diff --git a/src/generics/LatentReflection.java b/src/generics/LatentReflection.java new file mode 100644 index 0000000..acee0e5 --- /dev/null +++ b/src/generics/LatentReflection.java @@ -0,0 +1,56 @@ +package generics;//: generics/LatentReflection.java +// Using Reflection to produce latent typing. +import java.lang.reflect.*; +import static net.mindview.util.Print.*; + +// Does not implement Performs: +class Mime { + public void walkAgainstTheWind() {} + public void sit() { print("Pretending to sit"); } + public void pushInvisibleWalls() {} + public String toString() { return "Mime"; } +} + +// Does not implement Performs: +class SmartDog { + public void speak() { print("Woof!"); } + public void sit() { print("Sitting"); } + public void reproduce() {} +} + +class CommunicateReflectively { + public static void perform(Object speaker) { + Class spkr = speaker.getClass(); + try { + try { + Method speak = spkr.getMethod("speak"); + speak.invoke(speaker); + } catch(NoSuchMethodException e) { + print(speaker + " cannot speak"); + } + try { + Method sit = spkr.getMethod("sit"); + sit.invoke(speaker); + } catch(NoSuchMethodException e) { + print(speaker + " cannot sit"); + } + } catch(Exception e) { + throw new RuntimeException(speaker.toString(), e); + } + } +} + +public class LatentReflection { + public static void main(String[] args) { + CommunicateReflectively.perform(new SmartDog()); + CommunicateReflectively.perform(new Robot()); + CommunicateReflectively.perform(new Mime()); + } +} /* Output: +Woof! +Sitting +Click! +Clank! +Mime cannot speak +Pretending to sit +*///:~ diff --git a/src/generics/LimitsOfInference.java b/src/generics/LimitsOfInference.java new file mode 100644 index 0000000..1672077 --- /dev/null +++ b/src/generics/LimitsOfInference.java @@ -0,0 +1,11 @@ +package generics;//: generics/LimitsOfInference.java +import typeinfo.pets.*; +import java.util.*; + +public class LimitsOfInference { + static void + f(Map> petPeople) {} + public static void main(String[] args) { + // f(New.map()); // Does not compile + } +} ///:~ diff --git a/src/generics/LinkedStack.java b/src/generics/LinkedStack.java new file mode 100644 index 0000000..6ae917d --- /dev/null +++ b/src/generics/LinkedStack.java @@ -0,0 +1,40 @@ +package generics;//: generics/LinkedStack.java +// A stack implemented with an internal linked structure. + +public class LinkedStack { + private static class Node { + U item; + Node next; + Node() { item = null; next = null; } + Node(U item, Node next) { + this.item = item; + this.next = next; + } + boolean end() { return item == null && next == null; } + } + private Node top = new Node(); // End sentinel + public void push(T item) { + top = new Node(item, top); + } + public T pop() { + T result = top.item; + if(!top.end()) { + top = top.next; + } + return result; + } + public static void main(String[] args) { + LinkedStack lss = new LinkedStack(); + for(String s : "Phasers on stun!".split(" ")) { + lss.push(s); + } + String s; + while((s = lss.pop()) != null) { + System.out.println(s); + } + } +} /* Output: +stun! +on +Phasers +*///:~ diff --git a/src/generics/ListMaker.java b/src/generics/ListMaker.java new file mode 100644 index 0000000..c833320 --- /dev/null +++ b/src/generics/ListMaker.java @@ -0,0 +1,10 @@ +package generics;//: generics/ListMaker.java +import java.util.*; + +public class ListMaker { + List create() { return new ArrayList(); } + public static void main(String[] args) { + ListMaker stringMaker= new ListMaker(); + List stringList = stringMaker.create(); + } +} ///:~ diff --git a/src/generics/ListOfGenerics.java b/src/generics/ListOfGenerics.java new file mode 100644 index 0000000..360e9c4 --- /dev/null +++ b/src/generics/ListOfGenerics.java @@ -0,0 +1,8 @@ +package generics;//: generics/ListOfGenerics.java +import java.util.*; + +public class ListOfGenerics { + private List array = new ArrayList(); + public void add(T item) { array.add(item); } + public T get(int index) { return array.get(index); } +} ///:~ diff --git a/src/generics/ListOfInt.java b/src/generics/ListOfInt.java new file mode 100644 index 0000000..b1a1909 --- /dev/null +++ b/src/generics/ListOfInt.java @@ -0,0 +1,18 @@ +package generics;//: generics/ListOfInt.java +// Autoboxing compensates for the inability to use +// primitives in generics. +import java.util.*; + +public class ListOfInt { + public static void main(String[] args) { + List li = new ArrayList(); + for(int i = 0; i < 5; i++) { + li.add(i); + } + for(int i : li) { + System.out.print(i + " "); + } + } +} /* Output: +0 1 2 3 4 +*///:~ diff --git a/src/generics/LostInformation.java b/src/generics/LostInformation.java new file mode 100644 index 0000000..87ca836 --- /dev/null +++ b/src/generics/LostInformation.java @@ -0,0 +1,29 @@ +package generics;//: generics/LostInformation.java +import java.util.*; + +class Frob {} +class Fnorkle {} +class Quark {} +class Particle {} + +public class LostInformation { + public static void main(String[] args) { + List list = new ArrayList(); + Map map = new HashMap(); + Quark quark = new Quark(); + Particle p = new Particle(); + System.out.println(Arrays.toString( + list.getClass().getTypeParameters())); + System.out.println(Arrays.toString( + map.getClass().getTypeParameters())); + System.out.println(Arrays.toString( + quark.getClass().getTypeParameters())); + System.out.println(Arrays.toString( + p.getClass().getTypeParameters())); + } +} /* Output: +[E] +[K, V] +[Q] +[POSITION, MOMENTUM] +*///:~ diff --git a/src/generics/Manipulation.java b/src/generics/Manipulation.java new file mode 100644 index 0000000..2c2d234 --- /dev/null +++ b/src/generics/Manipulation.java @@ -0,0 +1,18 @@ +package generics;//: generics/Manipulation.java +// {CompileTimeError} (Won't compile) + +class Manipulator { + private T obj; + public Manipulator(T x) { obj = x; } + // Error: cannot find symbol: method f(): + public void manipulate() { obj.f(); } +} + +public class Manipulation { + public static void main(String[] args) { + HasF hf = new HasF(); + Manipulator manipulator = + new Manipulator(hf); + manipulator.manipulate(); + } +} ///:~ diff --git a/src/generics/Manipulator2.java b/src/generics/Manipulator2.java new file mode 100644 index 0000000..7fea43e --- /dev/null +++ b/src/generics/Manipulator2.java @@ -0,0 +1,7 @@ +package generics;//: generics/Manipulator2.java + +class Manipulator2 { + private T obj; + public Manipulator2(T x) { obj = x; } + public void manipulate() { obj.f(); } +} ///:~ diff --git a/src/generics/Manipulator3.java b/src/generics/Manipulator3.java new file mode 100644 index 0000000..e83bd1c --- /dev/null +++ b/src/generics/Manipulator3.java @@ -0,0 +1,7 @@ +package generics;//: generics/Manipulator3.java + +class Manipulator3 { + private HasF obj; + public Manipulator3(HasF x) { obj = x; } + public void manipulate() { obj.f(); } +} ///:~ diff --git a/src/generics/Mixins.cpp b/src/generics/Mixins.cpp new file mode 100644 index 0000000..0a1e519 --- /dev/null +++ b/src/generics/Mixins.cpp @@ -0,0 +1,43 @@ +//: generics/Mixins.cpp +#include +#include +#include +using namespace std; + +template class TimeStamped : public T { + long timeStamp; +public: + TimeStamped() { timeStamp = time(0); } + long getStamp() { return timeStamp; } +}; + +template class SerialNumbered : public T { + long serialNumber; + static long counter; +public: + SerialNumbered() { serialNumber = counter++; } + long getSerialNumber() { return serialNumber; } +}; + +// Define and initialize the static storage: +template long SerialNumbered::counter = 1; + +class Basic { + string value; +public: + void set(string val) { value = val; } + string get() { return value; } +}; + +int main() { + TimeStamped > mixin1, mixin2; + mixin1.set("test string 1"); + mixin2.set("test string 2"); + cout << mixin1.get() << " " << mixin1.getStamp() << + " " << mixin1.getSerialNumber() << endl; + cout << mixin2.get() << " " << mixin2.getStamp() << + " " << mixin2.getSerialNumber() << endl; +} /* Output: (Sample) +test string 1 1129840250 1 +test string 2 1129840250 2 +*///:~ diff --git a/src/generics/Mixins.java b/src/generics/Mixins.java new file mode 100644 index 0000000..2349f7c --- /dev/null +++ b/src/generics/Mixins.java @@ -0,0 +1,57 @@ +package generics;//: generics/Mixins.java +import java.util.*; + +interface TimeStamped { long getStamp(); } + +class TimeStampedImp implements TimeStamped { + private final long timeStamp; + public TimeStampedImp() { + timeStamp = new Date().getTime(); + } + public long getStamp() { return timeStamp; } +} + +interface SerialNumbered { long getSerialNumber(); } + +class SerialNumberedImp implements SerialNumbered { + private static long counter = 1; + private final long serialNumber = counter++; + public long getSerialNumber() { return serialNumber; } +} + +interface Basic { + public void set(String val); + public String get(); +} + +class BasicImp implements Basic { + private String value; + public void set(String val) { value = val; } + public String get() { return value; } +} + +class Mixin extends BasicImp +implements TimeStamped, SerialNumbered { + private TimeStamped timeStamp = new TimeStampedImp(); + private SerialNumbered serialNumber = + new SerialNumberedImp(); + public long getStamp() { return timeStamp.getStamp(); } + public long getSerialNumber() { + return serialNumber.getSerialNumber(); + } +} + +public class Mixins { + public static void main(String[] args) { + Mixin mixin1 = new Mixin(), mixin2 = new Mixin(); + mixin1.set("test string 1"); + mixin2.set("test string 2"); + System.out.println(mixin1.get() + " " + + mixin1.getStamp() + " " + mixin1.getSerialNumber()); + System.out.println(mixin2.get() + " " + + mixin2.getStamp() + " " + mixin2.getSerialNumber()); + } +} /* Output: (Sample) +test string 1 1132437151359 1 +test string 2 1132437151359 2 +*///:~ diff --git a/src/generics/MultipleInterfaceVariants.java b/src/generics/MultipleInterfaceVariants.java new file mode 100644 index 0000000..34c028d --- /dev/null +++ b/src/generics/MultipleInterfaceVariants.java @@ -0,0 +1,8 @@ +package generics;//: generics/MultipleInterfaceVariants.java +// {CompileTimeError} (Won't compile) + +interface Payable {} + +class Employee implements Payable {} +class Hourly extends Employee + implements Payable {} ///:~ diff --git a/src/generics/NeedCasting.java b/src/generics/NeedCasting.java new file mode 100644 index 0000000..ce10c79 --- /dev/null +++ b/src/generics/NeedCasting.java @@ -0,0 +1,12 @@ +package generics;//: generics/NeedCasting.java +import java.io.*; +import java.util.*; + +public class NeedCasting { + @SuppressWarnings("unchecked") + public void f(String[] args) throws Exception { + ObjectInputStream in = new ObjectInputStream( + new FileInputStream(args[0])); + List shapes = (List)in.readObject(); + } +} ///:~ diff --git a/src/generics/NonCovariantGenerics.java b/src/generics/NonCovariantGenerics.java new file mode 100644 index 0000000..600c635 --- /dev/null +++ b/src/generics/NonCovariantGenerics.java @@ -0,0 +1,8 @@ +package generics;//: generics/NonCovariantGenerics.java +// {CompileTimeError} (Won't compile) +import java.util.*; + +public class NonCovariantGenerics { + // Compile Error: incompatible types: + List flist = new ArrayList(); +} ///:~ diff --git a/src/generics/NotSelfBounded.java b/src/generics/NotSelfBounded.java new file mode 100644 index 0000000..71a7a4d --- /dev/null +++ b/src/generics/NotSelfBounded.java @@ -0,0 +1,21 @@ +package generics;//: generics/NotSelfBounded.java + +public class NotSelfBounded { + T element; + NotSelfBounded set(T arg) { + element = arg; + return this; + } + T get() { return element; } +} + +class A2 extends NotSelfBounded {} +class B2 extends NotSelfBounded {} + +class C2 extends NotSelfBounded { + C2 setAndGet(C2 arg) { set(arg); return get(); } +} + +class D2 {} +// Now this is OK: +class E2 extends NotSelfBounded {} ///:~ diff --git a/src/generics/OrdinaryArguments.java b/src/generics/OrdinaryArguments.java new file mode 100644 index 0000000..10af440 --- /dev/null +++ b/src/generics/OrdinaryArguments.java @@ -0,0 +1,26 @@ +package generics;//: generics/OrdinaryArguments.java + +class OrdinarySetter { + void set(Base base) { + System.out.println("OrdinarySetter.set(Base)"); + } +} + +class DerivedSetter extends OrdinarySetter { + void set(Derived derived) { + System.out.println("DerivedSetter.set(Derived)"); + } +} + +public class OrdinaryArguments { + public static void main(String[] args) { + Base base = new Base(); + Derived derived = new Derived(); + DerivedSetter ds = new DerivedSetter(); + ds.set(derived); + ds.set(base); // Compiles: overloaded, not overridden! + } +} /* Output: +DerivedSetter.set(Derived) +OrdinarySetter.set(Base) +*///:~ diff --git a/src/generics/Performs.java b/src/generics/Performs.java new file mode 100644 index 0000000..8d5796a --- /dev/null +++ b/src/generics/Performs.java @@ -0,0 +1,6 @@ +package generics;//: generics/Performs.java + +public interface Performs { + void speak(); + void sit(); +} ///:~ diff --git a/src/generics/PlainGenericInheritance.java b/src/generics/PlainGenericInheritance.java new file mode 100644 index 0000000..922dd03 --- /dev/null +++ b/src/generics/PlainGenericInheritance.java @@ -0,0 +1,26 @@ +package generics;//: generics/PlainGenericInheritance.java + +class GenericSetter { // Not self-bounded + void set(T arg){ + System.out.println("GenericSetter.set(Base)"); + } +} + +class DerivedGS extends GenericSetter { + void set(Derived derived){ + System.out.println("DerivedGS.set(Derived)"); + } +} + +public class PlainGenericInheritance { + public static void main(String[] args) { + Base base = new Base(); + Derived derived = new Derived(); + DerivedGS dgs = new DerivedGS(); + dgs.set(derived); + dgs.set(base); // Compiles: overloaded, not overridden! + } +} /* Output: +DerivedGS.set(Derived) +GenericSetter.set(Base) +*///:~ diff --git a/src/generics/PrimitiveGenericTest.java b/src/generics/PrimitiveGenericTest.java new file mode 100644 index 0000000..4a7a57e --- /dev/null +++ b/src/generics/PrimitiveGenericTest.java @@ -0,0 +1,45 @@ +package generics;//: generics/PrimitiveGenericTest.java +import net.mindview.util.*; + +// Fill an array using a generator: +class FArray { + public static T[] fill(T[] a, Generator gen) { + for(int i = 0; i < a.length; i++) { + a[i] = gen.next(); + } + return a; + } +} + +public class PrimitiveGenericTest { + public static void main(String[] args) { + String[] strings = FArray.fill( + new String[7], new RandomGenerator.String(10)); + for(String s : strings) { + System.out.println(s); + } + Integer[] integers = FArray.fill( + new Integer[7], new RandomGenerator.Integer()); + for(int i: integers) { + System.out.println(i); + } + // Autoboxing won't save you here. This won't compile: + // int[] b = + // FArray.fill(new int[7], new RandIntGenerator()); + } +} /* Output: +YNzbrnyGcF +OWZnTcQrGs +eGZMmJMRoE +suEcUOneOE +dLsmwHLGEa +hKcxrEqUCB +bkInaMesbt +7052 +6665 +2654 +3909 +5202 +2209 +5458 +*///:~ diff --git a/src/generics/RandomList.java b/src/generics/RandomList.java new file mode 100644 index 0000000..518f253 --- /dev/null +++ b/src/generics/RandomList.java @@ -0,0 +1,23 @@ +package generics;//: generics/RandomList.java +import java.util.*; + +public class RandomList { + private ArrayList storage = new ArrayList(); + private Random rand = new Random(47); + public void add(T item) { storage.add(item); } + public T select() { + return storage.get(rand.nextInt(storage.size())); + } + public static void main(String[] args) { + RandomList rs = new RandomList(); + for(String s: ("The quick brown fox jumped over " + + "the lazy brown dog").split(" ")) { + rs.add(s); + } + for(int i = 0; i < 11; i++) { + System.out.print(rs.select() + " "); + } + } +} /* Output: +brown over fox quick quick dog brown The brown lazy brown +*///:~ diff --git a/src/generics/RestrictedComparablePets.java b/src/generics/RestrictedComparablePets.java new file mode 100644 index 0000000..d1c78f1 --- /dev/null +++ b/src/generics/RestrictedComparablePets.java @@ -0,0 +1,12 @@ +package generics;//: generics/RestrictedComparablePets.java + +class Hamster extends ComparablePet +implements Comparable { + public int compareTo(ComparablePet arg) { return 0; } +} + +// Or just: + +class Gecko extends ComparablePet { + public int compareTo(ComparablePet arg) { return 0; } +} ///:~ diff --git a/src/generics/ReturnGenericType.java b/src/generics/ReturnGenericType.java new file mode 100644 index 0000000..3019522 --- /dev/null +++ b/src/generics/ReturnGenericType.java @@ -0,0 +1,7 @@ +package generics;//: generics/ReturnGenericType.java + +class ReturnGenericType { + private T obj; + public ReturnGenericType(T x) { obj = x; } + public T get() { return obj; } +} ///:~ diff --git a/src/generics/SelfBounding.java b/src/generics/SelfBounding.java new file mode 100644 index 0000000..db6667b --- /dev/null +++ b/src/generics/SelfBounding.java @@ -0,0 +1,36 @@ +package generics;//: generics/SelfBounding.java + +class SelfBounded> { + T element; + SelfBounded set(T arg) { + element = arg; + return this; + } + T get() { return element; } +} + +class A extends SelfBounded {} +class B extends SelfBounded {} // Also OK + +class C extends SelfBounded { + C setAndGet(C arg) { set(arg); return get(); } +} + +class D {} +// Can't do this: +// class E extends SelfBounded {} +// Compile error: Type parameter D is not within its bound + +// Alas, you can do this, so you can't force the idiom: +class F extends SelfBounded {} + +public class SelfBounding { + public static void main(String[] args) { + A a = new A(); + a.set(new A()); + a = a.set(new A()).get(); + a = a.get(); + C c = new C(); + c = c.setAndGet(new C()); + } +} ///:~ diff --git a/src/generics/SelfBoundingAndCovariantArguments.java b/src/generics/SelfBoundingAndCovariantArguments.java new file mode 100644 index 0000000..9b18a7e --- /dev/null +++ b/src/generics/SelfBoundingAndCovariantArguments.java @@ -0,0 +1,16 @@ +package generics;//: generics/SelfBoundingAndCovariantArguments.java + +interface SelfBoundSetter> { + void set(T arg); +} + +interface Setter extends SelfBoundSetter {} + +public class SelfBoundingAndCovariantArguments { + void testA(Setter s1, Setter s2, SelfBoundSetter sbs) { + s1.set(s2); + // s1.set(sbs); // Error: + // set(Setter) in SelfBoundSetter + // cannot be applied to (SelfBoundSetter) + } +} ///:~ diff --git a/src/generics/SelfBoundingMethods.java b/src/generics/SelfBoundingMethods.java new file mode 100644 index 0000000..95a5400 --- /dev/null +++ b/src/generics/SelfBoundingMethods.java @@ -0,0 +1,10 @@ +package generics;//: generics/SelfBoundingMethods.java + +public class SelfBoundingMethods { + static > T f(T arg) { + return arg.set(arg).get(); + } + public static void main(String[] args) { + A a = f(new A()); + } +} ///:~ diff --git a/src/generics/SimpleDogsAndRobots.java b/src/generics/SimpleDogsAndRobots.java new file mode 100644 index 0000000..6d078ce --- /dev/null +++ b/src/generics/SimpleDogsAndRobots.java @@ -0,0 +1,21 @@ +package generics;//: generics/SimpleDogsAndRobots.java +// Removing the generic; code still works. + +class CommunicateSimply { + static void perform(Performs performer) { + performer.speak(); + performer.sit(); + } +} + +public class SimpleDogsAndRobots { + public static void main(String[] args) { + CommunicateSimply.perform(new PerformingDog()); + CommunicateSimply.perform(new Robot()); + } +} /* Output: +Woof! +Sitting +Click! +Clank! +*///:~ diff --git a/src/generics/SimpleHolder.java b/src/generics/SimpleHolder.java new file mode 100644 index 0000000..ac9daa2 --- /dev/null +++ b/src/generics/SimpleHolder.java @@ -0,0 +1,12 @@ +package generics;//: generics/SimpleHolder.java + +public class SimpleHolder { + private Object obj; + public void set(Object obj) { this.obj = obj; } + public Object get() { return obj; } + public static void main(String[] args) { + SimpleHolder holder = new SimpleHolder(); + holder.set("Item"); + String s = (String)holder.get(); + } +} ///:~ diff --git a/src/generics/SimpleQueue.java b/src/generics/SimpleQueue.java new file mode 100644 index 0000000..280518e --- /dev/null +++ b/src/generics/SimpleQueue.java @@ -0,0 +1,12 @@ +package generics;//: generics/SimpleQueue.java +// A different kind of container that is Iterable +import java.util.*; + +public class SimpleQueue implements Iterable { + private LinkedList storage = new LinkedList(); + public void add(T t) { storage.offer(t); } + public T get() { return storage.poll(); } + public Iterator iterator() { + return storage.iterator(); + } +} ///:~ diff --git a/src/generics/SimplerPets.java b/src/generics/SimplerPets.java new file mode 100644 index 0000000..c83c53d --- /dev/null +++ b/src/generics/SimplerPets.java @@ -0,0 +1,11 @@ +package generics;//: generics/SimplerPets.java +import typeinfo.pets.*; +import java.util.*; +import net.mindview.util.*; + +public class SimplerPets { + public static void main(String[] args) { + Map> petPeople = New.map(); + // Rest of the code is the same... + } +} ///:~ diff --git a/src/generics/Store.java b/src/generics/Store.java new file mode 100644 index 0000000..8411781 --- /dev/null +++ b/src/generics/Store.java @@ -0,0 +1,83 @@ +package generics;//: generics/Store.java +// Building up a complex model using generic containers. +import java.util.*; +import net.mindview.util.*; + +class Product { + private final int id; + private String description; + private double price; + public Product(int IDnumber, String descr, double price){ + id = IDnumber; + description = descr; + this.price = price; + System.out.println(toString()); + } + public String toString() { + return id + ": " + description + ", price: $" + price; + } + public void priceChange(double change) { + price += change; + } + public static Generator generator = + new Generator() { + private Random rand = new Random(47); + public Product next() { + return new Product(rand.nextInt(1000), "Test", + Math.round(rand.nextDouble() * 1000.0) + 0.99); + } + }; +} + +class Shelf extends ArrayList { + public Shelf(int nProducts) { + Generators.fill(this, Product.generator, nProducts); + } +} + +class Aisle extends ArrayList { + public Aisle(int nShelves, int nProducts) { + for(int i = 0; i < nShelves; i++) { + add(new Shelf(nProducts)); + } + } +} + +class CheckoutStand {} +class Office {} + +public class Store extends ArrayList { + private ArrayList checkouts = + new ArrayList(); + private Office office = new Office(); + public Store(int nAisles, int nShelves, int nProducts) { + for(int i = 0; i < nAisles; i++) { + add(new Aisle(nShelves, nProducts)); + } + } + public String toString() { + StringBuilder result = new StringBuilder(); + for(Aisle a : this) { + for(Shelf s : a) { + for (Product p : s) { + result.append(p); + result.append("\n"); + } + } + } + return result.toString(); + } + public static void main(String[] args) { + System.out.println(new Store(14, 5, 10)); + } +} /* Output: +258: Test, price: $400.99 +861: Test, price: $160.99 +868: Test, price: $417.99 +207: Test, price: $268.99 +551: Test, price: $114.99 +278: Test, price: $804.99 +520: Test, price: $554.99 +140: Test, price: $530.99 +... +*///:~ diff --git a/src/generics/SuperTypeWildcards.java b/src/generics/SuperTypeWildcards.java new file mode 100644 index 0000000..4c331cc --- /dev/null +++ b/src/generics/SuperTypeWildcards.java @@ -0,0 +1,10 @@ +package generics;//: generics/SuperTypeWildcards.java +import java.util.*; + +public class SuperTypeWildcards { + static void writeTo(List apples) { + apples.add(new Apple()); + apples.add(new Jonathan()); + // apples.add(new Fruit()); // Error + } +} ///:~ diff --git a/src/generics/Templates.cpp b/src/generics/Templates.cpp new file mode 100644 index 0000000..8422a99 --- /dev/null +++ b/src/generics/Templates.cpp @@ -0,0 +1,23 @@ +//: generics/Templates.cpp +#include +using namespace std; + +template class Manipulator { + T obj; +public: + Manipulator(T x) { obj = x; } + void manipulate() { obj.f(); } +}; + +class HasF { +public: + void f() { cout << "HasF::f()" << endl; } +}; + +int main() { + HasF hf; + Manipulator manipulator(hf); + manipulator.manipulate(); +} /* Output: +HasF::f() +///:~ diff --git a/src/generics/ThrowGenericException.java b/src/generics/ThrowGenericException.java new file mode 100644 index 0000000..89cbab5 --- /dev/null +++ b/src/generics/ThrowGenericException.java @@ -0,0 +1,77 @@ +package generics;//: generics/ThrowGenericException.java +import java.util.*; + +interface Processor { + void process(List resultCollector) throws E; +} + +class ProcessRunner +extends ArrayList> { + List processAll() throws E { + List resultCollector = new ArrayList(); + for(Processor processor : this) { + processor.process(resultCollector); + } + return resultCollector; + } +} + +class Failure1 extends Exception {} + +class Processor1 implements Processor { + static int count = 3; + public void + process(List resultCollector) throws Failure1 { + if(count-- > 1) { + resultCollector.add("Hep!"); + } else { + resultCollector.add("Ho!"); + } + if(count < 0) { + throw new Failure1(); + } + } +} + +class Failure2 extends Exception {} + +class Processor2 implements Processor { + static int count = 2; + public void + process(List resultCollector) throws Failure2 { + if(count-- == 0) { + resultCollector.add(47); + } else { + resultCollector.add(11); + } + if(count < 0) { + throw new Failure2(); + } + } +} + +public class ThrowGenericException { + public static void main(String[] args) { + ProcessRunner runner = + new ProcessRunner(); + for(int i = 0; i < 3; i++) { + runner.add(new Processor1()); + } + try { + System.out.println(runner.processAll()); + } catch(Failure1 e) { + System.out.println(e); + } + + ProcessRunner runner2 = + new ProcessRunner(); + for(int i = 0; i < 3; i++) { + runner2.add(new Processor2()); + } + try { + System.out.println(runner2.processAll()); + } catch(Failure2 e) { + System.out.println(e); + } + } +} ///:~ diff --git a/src/generics/TupleList.java b/src/generics/TupleList.java new file mode 100644 index 0000000..ef5a0e2 --- /dev/null +++ b/src/generics/TupleList.java @@ -0,0 +1,20 @@ +package generics;//: generics/TupleList.java +// Combining generic types to make complex generic types. +import java.util.*; +import net.mindview.util.*; + +public class TupleList +extends ArrayList> { + public static void main(String[] args) { + TupleList tl = + new TupleList(); + tl.add(TupleTest.h()); + tl.add(TupleTest.h()); + for(FourTuple i: tl) { + System.out.println(i); + } + } +} /* Output: (75% match) +(Vehicle@11b86e7, Amphibian@35ce36, hi, 47) +(Vehicle@757aef, Amphibian@d9f9c3, hi, 47) +*///:~ diff --git a/src/generics/TupleTest.java b/src/generics/TupleTest.java new file mode 100644 index 0000000..e6d5986 --- /dev/null +++ b/src/generics/TupleTest.java @@ -0,0 +1,41 @@ +package generics;//: generics/TupleTest.java +import net.mindview.util.*; + +class Amphibian {} +class Vehicle {} + +public class TupleTest { + static TwoTuple f() { + // Autoboxing converts the int to Integer: + return new TwoTuple("hi", 47); + } + static ThreeTuple g() { + return new ThreeTuple( + new Amphibian(), "hi", 47); + } + static + FourTuple h() { + return + new FourTuple( + new Vehicle(), new Amphibian(), "hi", 47); + } + static + FiveTuple k() { + return new + FiveTuple( + new Vehicle(), new Amphibian(), "hi", 47, 11.1); + } + public static void main(String[] args) { + TwoTuple ttsi = f(); + System.out.println(ttsi); + // ttsi.first = "there"; // Compile error: final + System.out.println(g()); + System.out.println(h()); + System.out.println(k()); + } +} /* Output: (80% match) +(hi, 47) +(Amphibian@1f6a7b9, hi, 47) +(Vehicle@35ce36, Amphibian@757aef, hi, 47) +(Vehicle@9cab16, Amphibian@1a46e30, hi, 47, 11.1) +*///:~ diff --git a/src/generics/TupleTest2.java b/src/generics/TupleTest2.java new file mode 100644 index 0000000..b24aaf1 --- /dev/null +++ b/src/generics/TupleTest2.java @@ -0,0 +1,36 @@ +package generics;//: generics/TupleTest2.java +import net.mindview.util.*; +import static net.mindview.util.Tuple.*; + +public class TupleTest2 { + static TwoTuple f() { + return tuple("hi", 47); + } + static TwoTuple f2() { return tuple("hi", 47); } + static ThreeTuple g() { + return tuple(new Amphibian(), "hi", 47); + } + static + FourTuple h() { + return tuple(new Vehicle(), new Amphibian(), "hi", 47); + } + static + FiveTuple k() { + return tuple(new Vehicle(), new Amphibian(), + "hi", 47, 11.1); + } + public static void main(String[] args) { + TwoTuple ttsi = f(); + System.out.println(ttsi); + System.out.println(f2()); + System.out.println(g()); + System.out.println(h()); + System.out.println(k()); + } +} /* Output: (80% match) +(hi, 47) +(hi, 47) +(Amphibian@7d772e, hi, 47) +(Vehicle@757aef, Amphibian@d9f9c3, hi, 47) +(Vehicle@1a46e30, Amphibian@3e25a5, hi, 47, 11.1) +*///:~ diff --git a/src/generics/UnboundedWildcards1.java b/src/generics/UnboundedWildcards1.java new file mode 100644 index 0000000..4d0fd5d --- /dev/null +++ b/src/generics/UnboundedWildcards1.java @@ -0,0 +1,40 @@ +package generics;//: generics/UnboundedWildcards1.java +import java.util.*; + +public class UnboundedWildcards1 { + static List list1; + static List list2; + static List list3; + static void assign1(List list) { + list1 = list; + list2 = list; + // list3 = list; // Warning: unchecked conversion + // Found: List, Required: List + } + static void assign2(List list) { + list1 = list; + list2 = list; + list3 = list; + } + static void assign3(List list) { + list1 = list; + list2 = list; + list3 = list; + } + public static void main(String[] args) { + assign1(new ArrayList()); + assign2(new ArrayList()); + // assign3(new ArrayList()); // Warning: + // Unchecked conversion. Found: ArrayList + // Required: List + assign1(new ArrayList()); + assign2(new ArrayList()); + assign3(new ArrayList()); + // Both forms are acceptable as List: + List wildList = new ArrayList(); + wildList = new ArrayList(); + assign1(wildList); + assign2(wildList); + assign3(wildList); + } +} ///:~ diff --git a/src/generics/UnboundedWildcards2.java b/src/generics/UnboundedWildcards2.java new file mode 100644 index 0000000..31615f2 --- /dev/null +++ b/src/generics/UnboundedWildcards2.java @@ -0,0 +1,21 @@ +package generics;//: generics/UnboundedWildcards2.java +import java.util.*; + +public class UnboundedWildcards2 { + static Map map1; + static Map map2; + static Map map3; + static void assign1(Map map) { map1 = map; } + static void assign2(Map map) { map2 = map; } + static void assign3(Map map) { map3 = map; } + public static void main(String[] args) { + assign1(new HashMap()); + assign2(new HashMap()); + // assign3(new HashMap()); // Warning: + // Unchecked conversion. Found: HashMap + // Required: Map + assign1(new HashMap()); + assign2(new HashMap()); + assign3(new HashMap()); + } +} ///:~ diff --git a/src/generics/Unconstrained.java b/src/generics/Unconstrained.java new file mode 100644 index 0000000..2ab768c --- /dev/null +++ b/src/generics/Unconstrained.java @@ -0,0 +1,15 @@ +package generics;//: generics/Unconstrained.java + +class Other {} +class BasicOther extends BasicHolder {} + +public class Unconstrained { + public static void main(String[] args) { + BasicOther b = new BasicOther(), b2 = new BasicOther(); + b.set(new Other()); + Other other = b.get(); + b.f(); + } +} /* Output: +Other +*///:~ diff --git a/src/generics/UseList.java b/src/generics/UseList.java new file mode 100644 index 0000000..549faf0 --- /dev/null +++ b/src/generics/UseList.java @@ -0,0 +1,8 @@ +package generics;//: generics/UseList.java +// {CompileTimeError} (Won't compile) +import java.util.*; + +public class UseList { + void f(List v) {} + void f(List v) {} +} ///:~ diff --git a/src/generics/UseList2.java b/src/generics/UseList2.java new file mode 100644 index 0000000..29f1d3f --- /dev/null +++ b/src/generics/UseList2.java @@ -0,0 +1,7 @@ +package generics;//: generics/UseList2.java +import java.util.*; + +public class UseList2 { + void f1(List v) {} + void f2(List v) {} +} ///:~ diff --git a/src/generics/WatercolorSets.java b/src/generics/WatercolorSets.java new file mode 100644 index 0000000..c9a12b7 --- /dev/null +++ b/src/generics/WatercolorSets.java @@ -0,0 +1,34 @@ +package generics;//: generics/WatercolorSets.java +import generics.watercolors.*; +import java.util.*; +import static net.mindview.util.Print.*; +import static net.mindview.util.Sets.*; +import static generics.watercolors.Watercolors.*; + +public class WatercolorSets { + public static void main(String[] args) { + Set set1 = + EnumSet.range(BRILLIANT_RED, VIRIDIAN_HUE); + Set set2 = + EnumSet.range(CERULEAN_BLUE_HUE, BURNT_UMBER); + print("set1: " + set1); + print("set2: " + set2); + print("union(set1, set2): " + union(set1, set2)); + Set subset = intersection(set1, set2); + print("intersection(set1, set2): " + subset); + print("difference(set1, subset): " + + difference(set1, subset)); + print("difference(set2, subset): " + + difference(set2, subset)); + print("complement(set1, set2): " + + complement(set1, set2)); + } +} /* Output: (Sample) +set1: [BRILLIANT_RED, CRIMSON, MAGENTA, ROSE_MADDER, VIOLET, CERULEAN_BLUE_HUE, PHTHALO_BLUE, ULTRAMARINE, COBALT_BLUE_HUE, PERMANENT_GREEN, VIRIDIAN_HUE] +set2: [CERULEAN_BLUE_HUE, PHTHALO_BLUE, ULTRAMARINE, COBALT_BLUE_HUE, PERMANENT_GREEN, VIRIDIAN_HUE, SAP_GREEN, YELLOW_OCHRE, BURNT_SIENNA, RAW_UMBER, BURNT_UMBER] +union(set1, set2): [SAP_GREEN, ROSE_MADDER, YELLOW_OCHRE, PERMANENT_GREEN, BURNT_UMBER, COBALT_BLUE_HUE, VIOLET, BRILLIANT_RED, RAW_UMBER, ULTRAMARINE, BURNT_SIENNA, CRIMSON, CERULEAN_BLUE_HUE, PHTHALO_BLUE, MAGENTA, VIRIDIAN_HUE] +intersection(set1, set2): [ULTRAMARINE, PERMANENT_GREEN, COBALT_BLUE_HUE, PHTHALO_BLUE, CERULEAN_BLUE_HUE, VIRIDIAN_HUE] +difference(set1, subset): [ROSE_MADDER, CRIMSON, VIOLET, MAGENTA, BRILLIANT_RED] +difference(set2, subset): [RAW_UMBER, SAP_GREEN, YELLOW_OCHRE, BURNT_SIENNA, BURNT_UMBER] +complement(set1, set2): [SAP_GREEN, ROSE_MADDER, YELLOW_OCHRE, BURNT_UMBER, VIOLET, BRILLIANT_RED, RAW_UMBER, BURNT_SIENNA, CRIMSON, MAGENTA] +*///:~ diff --git a/src/generics/Wildcards.java b/src/generics/Wildcards.java new file mode 100644 index 0000000..ebb3dce --- /dev/null +++ b/src/generics/Wildcards.java @@ -0,0 +1,122 @@ +package generics;//: generics/Wildcards.java +// Exploring the meaning of wildcards. + +public class Wildcards { + // Raw argument: + static void rawArgs(Holder holder, Object arg) { + // holder.set(arg); // Warning: + // Unchecked call to set(T) as a + // member of the raw type Holder + // holder.set(new Wildcards()); // Same warning + + // Can't do this; don't have any 'T': + // T t = holder.get(); + + // OK, but type information has been lost: + Object obj = holder.get(); + } + // Similar to rawArgs(), but errors instead of warnings: + static void unboundedArg(Holder holder, Object arg) { + // holder.set(arg); // Error: + // set(capture of ?) in Holder + // cannot be applied to (Object) + // holder.set(new Wildcards()); // Same error + + // Can't do this; don't have any 'T': + // T t = holder.get(); + + // OK, but type information has been lost: + Object obj = holder.get(); + } + static T exact1(Holder holder) { + T t = holder.get(); + return t; + } + static T exact2(Holder holder, T arg) { + holder.set(arg); + T t = holder.get(); + return t; + } + static + T wildSubtype(Holder holder, T arg) { + // holder.set(arg); // Error: + // set(capture of ? extends T) in + // Holder + // cannot be applied to (T) + T t = holder.get(); + return t; + } + static + void wildSupertype(Holder holder, T arg) { + holder.set(arg); + // T t = holder.get(); // Error: + // Incompatible types: found Object, required T + + // OK, but type information has been lost: + Object obj = holder.get(); + } + public static void main(String[] args) { + Holder raw = new Holder(); + // Or: + raw = new Holder(); + Holder qualified = new Holder(); + Holder unbounded = new Holder(); + Holder bounded = new Holder(); + Long lng = 1L; + + rawArgs(raw, lng); + rawArgs(qualified, lng); + rawArgs(unbounded, lng); + rawArgs(bounded, lng); + + unboundedArg(raw, lng); + unboundedArg(qualified, lng); + unboundedArg(unbounded, lng); + unboundedArg(bounded, lng); + + // Object r1 = exact1(raw); // Warnings: + // Unchecked conversion from Holder to Holder + // Unchecked method invocation: exact1(Holder) + // is applied to (Holder) + Long r2 = exact1(qualified); + Object r3 = exact1(unbounded); // Must return Object + Long r4 = exact1(bounded); + + // Long r5 = exact2(raw, lng); // Warnings: + // Unchecked conversion from Holder to Holder + // Unchecked method invocation: exact2(Holder,T) + // is applied to (Holder,Long) + Long r6 = exact2(qualified, lng); + // Long r7 = exact2(unbounded, lng); // Error: + // exact2(Holder,T) cannot be applied to + // (Holder,Long) + // Long r8 = exact2(bounded, lng); // Error: + // exact2(Holder,T) cannot be applied + // to (Holder,Long) + + // Long r9 = wildSubtype(raw, lng); // Warnings: + // Unchecked conversion from Holder + // to Holder + // Unchecked method invocation: + // wildSubtype(Holder,T) is + // applied to (Holder,Long) + Long r10 = wildSubtype(qualified, lng); + // OK, but can only return Object: + Object r11 = wildSubtype(unbounded, lng); + Long r12 = wildSubtype(bounded, lng); + + // wildSupertype(raw, lng); // Warnings: + // Unchecked conversion from Holder + // to Holder + // Unchecked method invocation: + // wildSupertype(Holder,T) + // is applied to (Holder,Long) + wildSupertype(qualified, lng); + // wildSupertype(unbounded, lng); // Error: + // wildSupertype(Holder,T) cannot be + // applied to (Holder,Long) + // wildSupertype(bounded, lng); // Error: + // wildSupertype(Holder,T) cannot be + // applied to (Holder,Long) + } +} ///:~ diff --git a/src/generics/build.xml b/src/generics/build.xml new file mode 100644 index 0000000..978f459 --- /dev/null +++ b/src/generics/build.xml @@ -0,0 +1,789 @@ + + + + + + build.xml for the source code for the generics chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/generics/coffee/Americano.java b/src/generics/coffee/Americano.java new file mode 100644 index 0000000..51ad82f --- /dev/null +++ b/src/generics/coffee/Americano.java @@ -0,0 +1,3 @@ +//: generics/coffee/Americano.java +package generics.coffee; +public class Americano extends Coffee {} ///:~ diff --git a/src/generics/coffee/Breve.java b/src/generics/coffee/Breve.java new file mode 100644 index 0000000..8c28e3f --- /dev/null +++ b/src/generics/coffee/Breve.java @@ -0,0 +1,3 @@ +//: generics/coffee/Breve.java +package generics.coffee; +public class Breve extends Coffee {} ///:~ diff --git a/src/generics/coffee/Cappuccino.java b/src/generics/coffee/Cappuccino.java new file mode 100644 index 0000000..8e961ed --- /dev/null +++ b/src/generics/coffee/Cappuccino.java @@ -0,0 +1,3 @@ +//: generics/coffee/Cappuccino.java +package generics.coffee; +public class Cappuccino extends Coffee {} ///:~ diff --git a/src/generics/coffee/Coffee.java b/src/generics/coffee/Coffee.java new file mode 100644 index 0000000..3d34b65 --- /dev/null +++ b/src/generics/coffee/Coffee.java @@ -0,0 +1,10 @@ +//: generics/coffee/Coffee.java +package generics.coffee; + +public class Coffee { + private static long counter = 0; + private final long id = counter++; + public String toString() { + return getClass().getSimpleName() + " " + id; + } +} ///:~ diff --git a/src/generics/coffee/CoffeeGenerator.java b/src/generics/coffee/CoffeeGenerator.java new file mode 100644 index 0000000..f9f39b8 --- /dev/null +++ b/src/generics/coffee/CoffeeGenerator.java @@ -0,0 +1,59 @@ +//: generics/coffee/CoffeeGenerator.java +// Generate different types of Coffee: +package generics.coffee; +import java.util.*; +import net.mindview.util.*; + +public class CoffeeGenerator +implements Generator, Iterable { + private Class[] types = { Latte.class, Mocha.class, + Cappuccino.class, Americano.class, Breve.class, }; + private static Random rand = new Random(47); + public CoffeeGenerator() {} + // For iteration: + private int size = 0; + public CoffeeGenerator(int sz) { size = sz; } + public Coffee next() { + try { + return (Coffee) + types[rand.nextInt(types.length)].newInstance(); + // Report programmer errors at run time: + } catch(Exception e) { + throw new RuntimeException(e); + } + } + class CoffeeIterator implements Iterator { + int count = size; + public boolean hasNext() { return count > 0; } + public Coffee next() { + count--; + return CoffeeGenerator.this.next(); + } + public void remove() { // Not implemented + throw new UnsupportedOperationException(); + } + }; + public Iterator iterator() { + return new CoffeeIterator(); + } + public static void main(String[] args) { + CoffeeGenerator gen = new CoffeeGenerator(); + for(int i = 0; i < 5; i++) { + System.out.println(gen.next()); + } + for(Coffee c : new CoffeeGenerator(5)) { + System.out.println(c); + } + } +} /* Output: +Americano 0 +Latte 1 +Americano 2 +Mocha 3 +Mocha 4 +Breve 5 +Americano 6 +Latte 7 +Cappuccino 8 +Cappuccino 9 +*///:~ diff --git a/src/generics/coffee/Latte.java b/src/generics/coffee/Latte.java new file mode 100644 index 0000000..4006f19 --- /dev/null +++ b/src/generics/coffee/Latte.java @@ -0,0 +1,3 @@ +//: generics/coffee/Latte.java +package generics.coffee; +public class Latte extends Coffee {} ///:~ diff --git a/src/generics/coffee/Mocha.java b/src/generics/coffee/Mocha.java new file mode 100644 index 0000000..6bf8b29 --- /dev/null +++ b/src/generics/coffee/Mocha.java @@ -0,0 +1,3 @@ +//: generics/coffee/Mocha.java +package generics.coffee; +public class Mocha extends Coffee {} ///:~ diff --git a/src/generics/decorator/Decoration.java b/src/generics/decorator/Decoration.java new file mode 100644 index 0000000..916e008 --- /dev/null +++ b/src/generics/decorator/Decoration.java @@ -0,0 +1,45 @@ +//: generics/decorator/Decoration.java +package generics.decorator; +import java.util.*; + +class Basic { + private String value; + public void set(String val) { value = val; } + public String get() { return value; } +} + +class Decorator extends Basic { + protected Basic basic; + public Decorator(Basic basic) { this.basic = basic; } + public void set(String val) { basic.set(val); } + public String get() { return basic.get(); } +} + +class TimeStamped extends Decorator { + private final long timeStamp; + public TimeStamped(Basic basic) { + super(basic); + timeStamp = new Date().getTime(); + } + public long getStamp() { return timeStamp; } +} + +class SerialNumbered extends Decorator { + private static long counter = 1; + private final long serialNumber = counter++; + public SerialNumbered(Basic basic) { super(basic); } + public long getSerialNumber() { return serialNumber; } +} + +public class Decoration { + public static void main(String[] args) { + TimeStamped t = new TimeStamped(new Basic()); + TimeStamped t2 = new TimeStamped( + new SerialNumbered(new Basic())); + //! t2.getSerialNumber(); // Not available + SerialNumbered s = new SerialNumbered(new Basic()); + SerialNumbered s2 = new SerialNumbered( + new TimeStamped(new Basic())); + //! s2.getStamp(); // Not available + } +} ///:~ diff --git a/src/generics/watercolors/Watercolors.java b/src/generics/watercolors/Watercolors.java new file mode 100644 index 0000000..e6657db --- /dev/null +++ b/src/generics/watercolors/Watercolors.java @@ -0,0 +1,11 @@ +//: generics/watercolors/Watercolors.java +package generics.watercolors; + +public enum Watercolors { + ZINC, LEMON_YELLOW, MEDIUM_YELLOW, DEEP_YELLOW, ORANGE, + BRILLIANT_RED, CRIMSON, MAGENTA, ROSE_MADDER, VIOLET, + CERULEAN_BLUE_HUE, PHTHALO_BLUE, ULTRAMARINE, + COBALT_BLUE_HUE, PERMANENT_GREEN, VIRIDIAN_HUE, + SAP_GREEN, YELLOW_OCHRE, BURNT_SIENNA, RAW_UMBER, + BURNT_UMBER, PAYNES_GRAY, IVORY_BLACK +} ///:~ diff --git a/src/gui/BangBean2.java b/src/gui/BangBean2.java new file mode 100644 index 0000000..dcd9758 --- /dev/null +++ b/src/gui/BangBean2.java @@ -0,0 +1,112 @@ +package gui;//: gui/BangBean2.java +// You should write your Beans this way so they +// can run in a multithreaded environment. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.util.*; +import static net.mindview.util.SwingConsole.*; + +public class BangBean2 extends JPanel +implements Serializable { + private int xm, ym; + private int cSize = 20; // Circle size + private String text = "Bang!"; + private int fontSize = 48; + private Color tColor = Color.RED; + private ArrayList actionListeners = + new ArrayList(); + public BangBean2() { + addMouseListener(new ML()); + addMouseMotionListener(new MM()); + } + public synchronized int getCircleSize() { return cSize; } + public synchronized void setCircleSize(int newSize) { + cSize = newSize; + } + public synchronized String getBangText() { return text; } + public synchronized void setBangText(String newText) { + text = newText; + } + public synchronized int getFontSize(){ return fontSize; } + public synchronized void setFontSize(int newSize) { + fontSize = newSize; + } + public synchronized Color getTextColor(){ return tColor;} + public synchronized void setTextColor(Color newColor) { + tColor = newColor; + } + public void paintComponent(Graphics g) { + super.paintComponent(g); + g.setColor(Color.BLACK); + g.drawOval(xm - cSize/2, ym - cSize/2, cSize, cSize); + } + // This is a multicast listener, which is more typically + // used than the unicast approach taken in BangBean.java: + public synchronized void + addActionListener(ActionListener l) { + actionListeners.add(l); + } + public synchronized void + removeActionListener(ActionListener l) { + actionListeners.remove(l); + } + // Notice this isn't synchronized: + public void notifyListeners() { + ActionEvent a = new ActionEvent(BangBean2.this, + ActionEvent.ACTION_PERFORMED, null); + ArrayList lv = null; + // Make a shallow copy of the List in case + // someone adds a listener while we're + // calling listeners: + synchronized(this) { + lv = new ArrayList(actionListeners); + } + // Call all the listener methods: + for(ActionListener al : lv) { + al.actionPerformed(a); + } + } + class ML extends MouseAdapter { + public void mousePressed(MouseEvent e) { + Graphics g = getGraphics(); + g.setColor(tColor); + g.setFont( + new Font("TimesRoman", Font.BOLD, fontSize)); + int width = g.getFontMetrics().stringWidth(text); + g.drawString(text, (getSize().width - width) /2, + getSize().height/2); + g.dispose(); + notifyListeners(); + } + } + class MM extends MouseMotionAdapter { + public void mouseMoved(MouseEvent e) { + xm = e.getX(); + ym = e.getY(); + repaint(); + } + } + public static void main(String[] args) { + BangBean2 bb2 = new BangBean2(); + bb2.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.out.println("ActionEvent" + e); + } + }); + bb2.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.out.println("BangBean2 action"); + } + }); + bb2.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.out.println("More action"); + } + }); + JFrame frame = new JFrame(); + frame.add(bb2); + run(frame, 300, 300); + } +} ///:~ diff --git a/src/gui/BeanDumper.java b/src/gui/BeanDumper.java new file mode 100644 index 0000000..28be077 --- /dev/null +++ b/src/gui/BeanDumper.java @@ -0,0 +1,92 @@ +package gui;//: gui/BeanDumper.java +// Introspecting a Bean. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.beans.*; +import java.lang.reflect.*; +import static net.mindview.util.SwingConsole.*; + +public class BeanDumper extends JFrame { + private JTextField query = new JTextField(20); + private JTextArea results = new JTextArea(); + public void print(String s) { results.append(s + "\n"); } + public void dump(Class bean) { + results.setText(""); + BeanInfo bi = null; + try { + bi = Introspector.getBeanInfo(bean, Object.class); + } catch(IntrospectionException e) { + print("Couldn't introspect " + bean.getName()); + return; + } + for(PropertyDescriptor d: bi.getPropertyDescriptors()){ + Class p = d.getPropertyType(); + if(p == null) { + continue; + } + print("Property type:\n " + p.getName() + + "Property name:\n " + d.getName()); + Method readMethod = d.getReadMethod(); + if(readMethod != null) { + print("Read method:\n " + readMethod); + } + Method writeMethod = d.getWriteMethod(); + if(writeMethod != null) { + print("Write method:\n " + writeMethod); + } + print("===================="); + } + print("Public methods:"); + for(MethodDescriptor m : bi.getMethodDescriptors()) { + print(m.getMethod().toString()); + } + print("======================"); + print("Event support:"); + for(EventSetDescriptor e: bi.getEventSetDescriptors()){ + print("Listener type:\n " + + e.getListenerType().getName()); + for(Method lm : e.getListenerMethods()) { + print("Listener method:\n " + lm.getName()); + } + for(MethodDescriptor lmd : + e.getListenerMethodDescriptors() ) { + print("Method descriptor:\n " + lmd.getMethod()); + } + Method addListener= e.getAddListenerMethod(); + print("Add Listener Method:\n " + addListener); + Method removeListener = e.getRemoveListenerMethod(); + print("Remove Listener Method:\n "+ removeListener); + print("===================="); + } + } + class Dumper implements ActionListener { + public void actionPerformed(ActionEvent e) { + String name = query.getText(); + Class c = null; + try { + c = Class.forName(name); + } catch(ClassNotFoundException ex) { + results.setText("Couldn't find " + name); + return; + } + dump(c); + } + } + public BeanDumper() { + JPanel p = new JPanel(); + p.setLayout(new FlowLayout()); + p.add(new JLabel("Qualified bean name:")); + p.add(query); + add(BorderLayout.NORTH, p); + add(new JScrollPane(results)); + Dumper dmpr = new Dumper(); + query.addActionListener(dmpr); + query.setText("frogbean.Frog"); + // Force evaluation + dmpr.actionPerformed(new ActionEvent(dmpr, 0, "")); + } + public static void main(String[] args) { + run(new BeanDumper(), 600, 500); + } +} ///:~ diff --git a/src/gui/BorderLayout1.java b/src/gui/BorderLayout1.java new file mode 100644 index 0000000..a80275a --- /dev/null +++ b/src/gui/BorderLayout1.java @@ -0,0 +1,18 @@ +package gui;//: gui/BorderLayout1.java +// Demonstrates BorderLayout. +import javax.swing.*; +import java.awt.*; +import static net.mindview.util.SwingConsole.*; + +public class BorderLayout1 extends JFrame { + public BorderLayout1() { + add(BorderLayout.NORTH, new JButton("North")); + add(BorderLayout.SOUTH, new JButton("South")); + add(BorderLayout.EAST, new JButton("East")); + add(BorderLayout.WEST, new JButton("West")); + add(BorderLayout.CENTER, new JButton("Center")); + } + public static void main(String[] args) { + run(new BorderLayout1(), 300, 250); + } +} ///:~ diff --git a/src/gui/Borders.java b/src/gui/Borders.java new file mode 100644 index 0000000..7178c76 --- /dev/null +++ b/src/gui/Borders.java @@ -0,0 +1,37 @@ +package gui;//: gui/Borders.java +// Different Swing borders. +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; +import static net.mindview.util.SwingConsole.*; + +public class Borders extends JFrame { + static JPanel showBorder(Border b) { + JPanel jp = new JPanel(); + jp.setLayout(new BorderLayout()); + String nm = b.getClass().toString(); + nm = nm.substring(nm.lastIndexOf('.') + 1); + jp.add(new JLabel(nm, JLabel.CENTER), + BorderLayout.CENTER); + jp.setBorder(b); + return jp; + } + public Borders() { + setLayout(new GridLayout(2,4)); + add(showBorder(new TitledBorder("Title"))); + add(showBorder(new EtchedBorder())); + add(showBorder(new LineBorder(Color.BLUE))); + add(showBorder( + new MatteBorder(5,5,30,30,Color.GREEN))); + add(showBorder( + new BevelBorder(BevelBorder.RAISED))); + add(showBorder( + new SoftBevelBorder(BevelBorder.LOWERED))); + add(showBorder(new CompoundBorder( + new EtchedBorder(), + new LineBorder(Color.RED)))); + } + public static void main(String[] args) { + run(new Borders(), 500, 300); + } +} ///:~ diff --git a/src/gui/Button1.java b/src/gui/Button1.java new file mode 100644 index 0000000..915d542 --- /dev/null +++ b/src/gui/Button1.java @@ -0,0 +1,19 @@ +package gui;//: gui/Button1.java +// Putting buttons on a Swing application. +import javax.swing.*; +import java.awt.*; +import static net.mindview.util.SwingConsole.*; + +public class Button1 extends JFrame { + private JButton + b1 = new JButton("Button 1"), + b2 = new JButton("Button 2"); + public Button1() { + setLayout(new FlowLayout()); + add(b1); + add(b2); + } + public static void main(String[] args) { + run(new Button1(), 200, 100); + } +} ///:~ diff --git a/src/gui/Button2.java b/src/gui/Button2.java new file mode 100644 index 0000000..e20ecd8 --- /dev/null +++ b/src/gui/Button2.java @@ -0,0 +1,31 @@ +package gui;//: gui/Button2.java +// Responding to button presses. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import static net.mindview.util.SwingConsole.*; + +public class Button2 extends JFrame { + private JButton + b1 = new JButton("Button 1"), + b2 = new JButton("Button 2"); + private JTextField txt = new JTextField(10); + class ButtonListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + String name = ((JButton)e.getSource()).getText(); + txt.setText(name); + } + } + private ButtonListener bl = new ButtonListener(); + public Button2() { + b1.addActionListener(bl); + b2.addActionListener(bl); + setLayout(new FlowLayout()); + add(b1); + add(b2); + add(txt); + } + public static void main(String[] args) { + run(new Button2(), 200, 150); + } +} ///:~ diff --git a/src/gui/Button2b.java b/src/gui/Button2b.java new file mode 100644 index 0000000..921a331 --- /dev/null +++ b/src/gui/Button2b.java @@ -0,0 +1,30 @@ +package gui;//: gui/Button2b.java +// Using anonymous inner classes. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import static net.mindview.util.SwingConsole.*; + +public class Button2b extends JFrame { + private JButton + b1 = new JButton("Button 1"), + b2 = new JButton("Button 2"); + private JTextField txt = new JTextField(10); + private ActionListener bl = new ActionListener() { + public void actionPerformed(ActionEvent e) { + String name = ((JButton)e.getSource()).getText(); + txt.setText(name); + } + }; + public Button2b() { + b1.addActionListener(bl); + b2.addActionListener(bl); + setLayout(new FlowLayout()); + add(b1); + add(b2); + add(txt); + } + public static void main(String[] args) { + run(new Button2b(), 200, 150); + } +} ///:~ diff --git a/src/gui/ButtonGroups.java b/src/gui/ButtonGroups.java new file mode 100644 index 0000000..382bfc9 --- /dev/null +++ b/src/gui/ButtonGroups.java @@ -0,0 +1,48 @@ +package gui;//: gui/ButtonGroups.java +// Uses reflection to create groups +// of different types of AbstractButton. +import javax.swing.*; +import javax.swing.border.*; +import java.awt.*; +import java.lang.reflect.*; +import static net.mindview.util.SwingConsole.*; + +public class ButtonGroups extends JFrame { + private static String[] ids = { + "June", "Ward", "Beaver", "Wally", "Eddie", "Lumpy" + }; + static JPanel makeBPanel( + Class kind, String[] ids) { + ButtonGroup bg = new ButtonGroup(); + JPanel jp = new JPanel(); + String title = kind.getName(); + title = title.substring(title.lastIndexOf('.') + 1); + jp.setBorder(new TitledBorder(title)); + for(String id : ids) { + AbstractButton ab = new JButton("failed"); + try { + // Get the dynamic constructor method + // that takes a String argument: + Constructor ctor = + kind.getConstructor(String.class); + // Create a new object: + ab = (AbstractButton)ctor.newInstance(id); + } catch(Exception ex) { + System.err.println("can't create " + kind); + } + bg.add(ab); + jp.add(ab); + } + return jp; + } + public ButtonGroups() { + setLayout(new FlowLayout()); + add(makeBPanel(JButton.class, ids)); + add(makeBPanel(JToggleButton.class, ids)); + add(makeBPanel(JCheckBox.class, ids)); + add(makeBPanel(JRadioButton.class, ids)); + } + public static void main(String[] args) { + run(new ButtonGroups(), 500, 350); + } +} ///:~ diff --git a/src/gui/Buttons.java b/src/gui/Buttons.java new file mode 100644 index 0000000..6d462e9 --- /dev/null +++ b/src/gui/Buttons.java @@ -0,0 +1,33 @@ +package gui;//: gui/Buttons.java +// Various Swing buttons. +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.plaf.basic.*; +import java.awt.*; +import static net.mindview.util.SwingConsole.*; + +public class Buttons extends JFrame { + private JButton jb = new JButton("JButton"); + private BasicArrowButton + up = new BasicArrowButton(BasicArrowButton.NORTH), + down = new BasicArrowButton(BasicArrowButton.SOUTH), + right = new BasicArrowButton(BasicArrowButton.EAST), + left = new BasicArrowButton(BasicArrowButton.WEST); + public Buttons() { + setLayout(new FlowLayout()); + add(jb); + add(new JToggleButton("JToggleButton")); + add(new JCheckBox("JCheckBox")); + add(new JRadioButton("JRadioButton")); + JPanel jp = new JPanel(); + jp.setBorder(new TitledBorder("Directions")); + jp.add(up); + jp.add(down); + jp.add(left); + jp.add(right); + add(jp); + } + public static void main(String[] args) { + run(new Buttons(), 350, 200); + } +} ///:~ diff --git a/src/gui/CheckBoxes.java b/src/gui/CheckBoxes.java new file mode 100644 index 0000000..387c24c --- /dev/null +++ b/src/gui/CheckBoxes.java @@ -0,0 +1,46 @@ +package gui;//: gui/CheckBoxes.java +// Using JCheckBoxes. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import static net.mindview.util.SwingConsole.*; + +public class CheckBoxes extends JFrame { + private JTextArea t = new JTextArea(6, 15); + private JCheckBox + cb1 = new JCheckBox("Check Box 1"), + cb2 = new JCheckBox("Check Box 2"), + cb3 = new JCheckBox("Check Box 3"); + public CheckBoxes() { + cb1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + trace("1", cb1); + } + }); + cb2.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + trace("2", cb2); + } + }); + cb3.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + trace("3", cb3); + } + }); + setLayout(new FlowLayout()); + add(new JScrollPane(t)); + add(cb1); + add(cb2); + add(cb3); + } + private void trace(String b, JCheckBox cb) { + if(cb.isSelected()) { + t.append("Box " + b + " Set\n"); + } else { + t.append("Box " + b + " Cleared\n"); + } + } + public static void main(String[] args) { + run(new CheckBoxes(), 200, 300); + } +} ///:~ diff --git a/src/gui/ColorBoxes.java b/src/gui/ColorBoxes.java new file mode 100644 index 0000000..c1cc06d --- /dev/null +++ b/src/gui/ColorBoxes.java @@ -0,0 +1,56 @@ +package gui;//: gui/ColorBoxes.java +// A visual demonstration of threading. +import javax.swing.*; +import java.awt.*; +import java.util.concurrent.*; +import java.util.*; +import static net.mindview.util.SwingConsole.*; + +class CBox extends JPanel implements Runnable { + private int pause; + private static Random rand = new Random(); + private Color color = new Color(0); + public void paintComponent(Graphics g) { + g.setColor(color); + Dimension s = getSize(); + g.fillRect(0, 0, s.width, s.height); + } + public CBox(int pause) { this.pause = pause; } + public void run() { + try { + while(!Thread.interrupted()) { + color = new Color(rand.nextInt(0xFFFFFF)); + repaint(); // Asynchronously request a paint() + TimeUnit.MILLISECONDS.sleep(pause); + } + } catch(InterruptedException e) { + // Acceptable way to exit + } + } +} + +public class ColorBoxes extends JFrame { + private int grid = 12; + private int pause = 50; + private static ExecutorService exec = + Executors.newCachedThreadPool(); + public void setUp() { + setLayout(new GridLayout(grid, grid)); + for(int i = 0; i < grid * grid; i++) { + CBox cb = new CBox(pause); + add(cb); + exec.execute(cb); + } + } + public static void main(String[] args) { + ColorBoxes boxes = new ColorBoxes(); + if(args.length > 0) { + boxes.grid = new Integer(args[0]); + } + if(args.length > 1) { + boxes.pause = new Integer(args[1]); + } + boxes.setUp(); + run(boxes, 500, 400); + } +} ///:~ diff --git a/src/gui/ComboBoxes.java b/src/gui/ComboBoxes.java new file mode 100644 index 0000000..6d61634 --- /dev/null +++ b/src/gui/ComboBoxes.java @@ -0,0 +1,43 @@ +package gui;//: gui/ComboBoxes.java +// Using drop-down lists. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import static net.mindview.util.SwingConsole.*; + +public class ComboBoxes extends JFrame { + private String[] description = { + "Ebullient", "Obtuse", "Recalcitrant", "Brilliant", + "Somnescent", "Timorous", "Florid", "Putrescent" + }; + private JTextField t = new JTextField(15); + private JComboBox c = new JComboBox(); + private JButton b = new JButton("Add items"); + private int count = 0; + public ComboBoxes() { + for(int i = 0; i < 4; i++) { + c.addItem(description[count++]); + } + t.setEditable(false); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + if(count < description.length) { + c.addItem(description[count++]); + } + } + }); + c.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + t.setText("index: "+ c.getSelectedIndex() + " " + + ((JComboBox)e.getSource()).getSelectedItem()); + } + }); + setLayout(new FlowLayout()); + add(t); + add(c); + add(b); + } + public static void main(String[] args) { + run(new ComboBoxes(), 200, 175); + } +} ///:~ diff --git a/src/gui/Dialogs.java b/src/gui/Dialogs.java new file mode 100644 index 0000000..ba0c2d8 --- /dev/null +++ b/src/gui/Dialogs.java @@ -0,0 +1,38 @@ +package gui;//: gui/Dialogs.java +// Creating and using Dialog Boxes. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import static net.mindview.util.SwingConsole.*; + +class MyDialog extends JDialog { + public MyDialog(JFrame parent) { + super(parent, "My dialog", true); + setLayout(new FlowLayout()); + add(new JLabel("Here is my dialog")); + JButton ok = new JButton("OK"); + ok.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + dispose(); // Closes the dialog + } + }); + add(ok); + setSize(150,125); + } +} + +public class Dialogs extends JFrame { + private JButton b1 = new JButton("Dialog Box"); + private MyDialog dlg = new MyDialog(null); + public Dialogs() { + b1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + dlg.setVisible(true); + } + }); + add(b1); + } + public static void main(String[] args) { + run(new Dialogs(), 125, 75); + } +} ///:~ diff --git a/src/gui/Face0.gif b/src/gui/Face0.gif new file mode 100644 index 0000000000000000000000000000000000000000..9850c6d37fe7c6ce40eef9aeffd5fa532e47d5ee GIT binary patch literal 261 zcmV+g0s8(&Nk%v~VLt#k0Du4h00030|Nkri0000#05|{u0_2R3smrYZoD49|yDGkV zqY8H37KV*ArsS!%9P7pm`pBf2IaXP!;3J|)AQK|SSftIALuLYbgQcLKnA5qc$&^?R z*JeOoj@IMze)(^_)^aG4o#j69;$pz`I(9tnsuj> zyv(@l>Kq3>*DNhny>$&nosG(E<^2r~D?UzcXYPgmrk>7z>Rtr@EN`8$9ldmq|EE9w L5k$|Jx&i<@JBoV! literal 0 HcmV?d00001 diff --git a/src/gui/Face1.gif b/src/gui/Face1.gif new file mode 100644 index 0000000000000000000000000000000000000000..691fe54a21277cf69416ea33df003a0194739d45 GIT binary patch literal 289 zcmV++0p9*cNk%v~VMG8w0Du4h00030|Nkri0000)06zc#0{)DTsmsj(or6Hyt7_2r zXx@bx0%BYwrbdZgrK-NGJi@j`HT2CVx!((lcETdbh}0#QK&F%Ez3CuS=U(C22;_f zM+g}bvBMk*=5m7hNqprhN-Y_92pz@b9bQPTYmPLIPPuN}&if7@?>_HVPk$eikG!wn nKlBe^zz*u-6?}KFUBY#a77F_|u-6%feMm%$D27(R0RjL!*fEI& literal 0 HcmV?d00001 diff --git a/src/gui/Face2.gif b/src/gui/Face2.gif new file mode 100644 index 0000000000000000000000000000000000000000..9b325c5117a36b1e8744a9b1c04a77fedc668b7d GIT binary patch literal 310 zcmV-60m=SHNk%v~VO9V#0Du4h00030|Nkri0001105Sjo0{)DTsR+FfO`9{?YbiJy zv$}xZ_&ArDZ5~RJEcSk_q|T=;m3Y4Eea-3dgh3(@_?sbzMrL!h3`9dKDQudUBC%Lb zClXU=y4F&SoBBe+pK%6U_O0Du@|a!Jq3icIUoB?__a`DqSV&bjI0qO)Ce3|x^^mQboAtxnk5wT8MqjVv$2Gys424xiaYl!tm^A)hv+r? zns*3E!Qj%@QmMS`WXhS=ie3Dx>-cSoY;#v`f@#TE&24>XzRu1ZKTl3?Uw@wus?YD* z{|^nIz-t5b!7G>qA;5zQ@ijE~kRQW{`zYRn+a literal 0 HcmV?d00001 diff --git a/src/gui/Face3.gif b/src/gui/Face3.gif new file mode 100644 index 0000000000000000000000000000000000000000..2bbac492e0851c21a94ad0bc301a899084deb591 GIT binary patch literal 235 zcmV99>} zhwfcK_>CJ#qL7-Jk4q|W6u)-O-FQy#yyDZ%f*&9;Q#|s9#}SigdIp`!oD^EkOt##v z_Y3Y?#HSP2Jl=*_TFp4QqLi;C_v+$d-|5COJnO{Q(x(I?nD+x^SjBW0r!^&|hxRo2 zn0E+5XDNfz@S?(}R`=;c*5m1z^|#uJ>I#b&8>>2-VoQtbTC4lkyUP2kBaB7dWPFS~ lrM%3%6YLDFER9TUY)!n0%~(99>} zhwfcK_>CJ#qL7-hib=jJ9E*=hhhu*AHJGFEW(rOu7A$gxN24#oY#O7~)zfLceJnhe zvYM$zZCCBMB(0o_YqSTaYsq!(?VHSP@AT&+*RGakrZxmd)%C|T<^`ukWQKSLr6lN> z2IG}gCX_PyRbx}=Cg+m*b_j~5ScjK4*SBUmv&wlwQKFHGds|7T`&$Mq95-CtX^b40 zyo|-{%" + + "
Hello!
Press me now!"); + public HTMLButton() { + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + add(new JLabel("" + + "Kapow!")); + // Force a re-layout to include the new label: + validate(); + } + }); + setLayout(new FlowLayout()); + add(b); + } + public static void main(String[] args) { + run(new HTMLButton(), 200, 500); + } +} ///:~ diff --git a/src/gui/HelloLabel.java b/src/gui/HelloLabel.java new file mode 100644 index 0000000..1a5adc1 --- /dev/null +++ b/src/gui/HelloLabel.java @@ -0,0 +1,16 @@ +package gui;//: gui/HelloLabel.java +import javax.swing.*; +import java.util.concurrent.*; + +public class HelloLabel { + public static void main(String[] args) throws Exception { + JFrame frame = new JFrame("Hello Swing"); + JLabel label = new JLabel("A Label"); + frame.add(label); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(300, 100); + frame.setVisible(true); + TimeUnit.SECONDS.sleep(1); + label.setText("Hey! This is Different!"); + } +} ///:~ diff --git a/src/gui/HelloSwing.java b/src/gui/HelloSwing.java new file mode 100644 index 0000000..7baeb92 --- /dev/null +++ b/src/gui/HelloSwing.java @@ -0,0 +1,11 @@ +package gui;//: gui/HelloSwing.java +import javax.swing.*; + +public class HelloSwing { + public static void main(String[] args) { + JFrame frame = new JFrame("Hello Swing"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(300, 100); + frame.setVisible(true); + } +} ///:~ diff --git a/src/gui/InterruptableLongRunningCallable.java b/src/gui/InterruptableLongRunningCallable.java new file mode 100644 index 0000000..6ae78a2 --- /dev/null +++ b/src/gui/InterruptableLongRunningCallable.java @@ -0,0 +1,61 @@ +package gui;//: gui/InterruptableLongRunningCallable.java +// Using Callables for long-running tasks. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.concurrent.*; +import net.mindview.util.*; +import static net.mindview.util.SwingConsole.*; + +class CallableTask extends Task +implements Callable { + public String call() { + run(); + return "Return value of " + this; + } +} + +public class +InterruptableLongRunningCallable extends JFrame { + private JButton + b1 = new JButton("Start Long Running Task"), + b2 = new JButton("End Long Running Task"), + b3 = new JButton("Get results"); + private TaskManager manager = + new TaskManager(); + public InterruptableLongRunningCallable() { + b1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + CallableTask task = new CallableTask(); + manager.add(task); + System.out.println(task + " added to the queue"); + } + }); + b2.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + for(String result : manager.purge()) { + System.out.println(result); + } + } + }); + b3.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + // Sample call to a Task method: + for(TaskItem tt : + manager) { + tt.task.id(); // No cast required + } + for(String result : manager.getResults()) { + System.out.println(result); + } + } + }); + setLayout(new FlowLayout()); + add(b1); + add(b2); + add(b3); + } + public static void main(String[] args) { + run(new InterruptableLongRunningCallable(), 200, 150); + } +} ///:~ diff --git a/src/gui/InterruptableLongRunningTask.java b/src/gui/InterruptableLongRunningTask.java new file mode 100644 index 0000000..df96bfe --- /dev/null +++ b/src/gui/InterruptableLongRunningTask.java @@ -0,0 +1,52 @@ +package gui;//: gui/InterruptableLongRunningTask.java +// Long-running tasks in threads. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.concurrent.*; +import static net.mindview.util.SwingConsole.*; + +class Task implements Runnable { + private static int counter = 0; + private final int id = counter++; + public void run() { + System.out.println(this + " started"); + try { + TimeUnit.SECONDS.sleep(3); + } catch(InterruptedException e) { + System.out.println(this + " interrupted"); + return; + } + System.out.println(this + " completed"); + } + public String toString() { return "Task " + id; } + public long id() { return id; } +}; + +public class InterruptableLongRunningTask extends JFrame { + private JButton + b1 = new JButton("Start Long Running Task"), + b2 = new JButton("End Long Running Task"); + ExecutorService executor = + Executors.newSingleThreadExecutor(); + public InterruptableLongRunningTask() { + b1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + Task task = new Task(); + executor.execute(task); + System.out.println(task + " added to the queue"); + } + }); + b2.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + executor.shutdownNow(); // Heavy-handed + } + }); + setLayout(new FlowLayout()); + add(b1); + add(b2); + } + public static void main(String[] args) { + run(new InterruptableLongRunningTask(), 200, 150); + } +} ///:~ diff --git a/src/gui/List.java b/src/gui/List.java new file mode 100644 index 0000000..9449968 --- /dev/null +++ b/src/gui/List.java @@ -0,0 +1,66 @@ +package gui;//: gui/List.java +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.event.*; +import java.awt.*; +import java.awt.event.*; +import static net.mindview.util.SwingConsole.*; + +public class List extends JFrame { + private String[] flavors = { + "Chocolate", "Strawberry", "Vanilla Fudge Swirl", + "Mint Chip", "Mocha Almond Fudge", "Rum Raisin", + "Praline Cream", "Mud Pie" + }; + private DefaultListModel lItems = new DefaultListModel(); + private JList lst = new JList(lItems); + private JTextArea t = + new JTextArea(flavors.length, 20); + private JButton b = new JButton("Add Item"); + private ActionListener bl = new ActionListener() { + public void actionPerformed(ActionEvent e) { + if(count < flavors.length) { + lItems.add(0, flavors[count++]); + } else { + // Disable, since there are no more + // flavors left to be added to the List + b.setEnabled(false); + } + } + }; + private ListSelectionListener ll = + new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) { + if(e.getValueIsAdjusting()) { + return; + } + t.setText(""); + for(Object item : lst.getSelectedValues()) { + t.append(item + "\n"); + } + } + }; + private int count = 0; + public List() { + t.setEditable(false); + setLayout(new FlowLayout()); + // Create Borders for components: + Border brd = BorderFactory.createMatteBorder( + 1, 1, 2, 2, Color.BLACK); + lst.setBorder(brd); + t.setBorder(brd); + // Add the first four items to the List + for(int i = 0; i < 4; i++) { + lItems.addElement(flavors[count++]); + } + add(t); + add(lst); + add(b); + // Register event listeners + lst.addListSelectionListener(ll); + b.addActionListener(bl); + } + public static void main(String[] args) { + run(new List(), 250, 375); + } +} ///:~ diff --git a/src/gui/LongRunningTask.java b/src/gui/LongRunningTask.java new file mode 100644 index 0000000..9ca32e1 --- /dev/null +++ b/src/gui/LongRunningTask.java @@ -0,0 +1,38 @@ +package gui;//: gui/LongRunningTask.java +// A badly designed program. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.concurrent.*; +import static net.mindview.util.SwingConsole.*; + +public class LongRunningTask extends JFrame { + private JButton + b1 = new JButton("Start Long Running Task"), + b2 = new JButton("End Long Running Task"); + public LongRunningTask() { + b1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + try { + TimeUnit.SECONDS.sleep(3); + } catch(InterruptedException e) { + System.out.println("Task interrupted"); + return; + } + System.out.println("Task completed"); + } + }); + b2.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + // Interrupt yourself? + Thread.currentThread().interrupt(); + } + }); + setLayout(new FlowLayout()); + add(b1); + add(b2); + } + public static void main(String[] args) { + run(new LongRunningTask(), 200, 150); + } +} ///:~ diff --git a/src/gui/LookAndFeel.java b/src/gui/LookAndFeel.java new file mode 100644 index 0000000..bf40ab0 --- /dev/null +++ b/src/gui/LookAndFeel.java @@ -0,0 +1,64 @@ +package gui;//: gui/LookAndFeel.java +// Selecting different looks & feels. +// {Args: motif} +import javax.swing.*; +import java.awt.*; +import static net.mindview.util.SwingConsole.*; + +public class LookAndFeel extends JFrame { + private String[] choices = + "Eeny Meeny Minnie Mickey Moe Larry Curly".split(" "); + private Component[] samples = { + new JButton("JButton"), + new JTextField("JTextField"), + new JLabel("JLabel"), + new JCheckBox("JCheckBox"), + new JRadioButton("Radio"), + new JComboBox(choices), + new JList(choices), + }; + public LookAndFeel() { + super("Look And Feel"); + setLayout(new FlowLayout()); + for(Component component : samples) { + add(component); + } + } + private static void usageError() { + System.out.println( + "Usage:LookAndFeel [cross|system|motif]"); + System.exit(1); + } + public static void main(String[] args) { + if(args.length == 0) { + usageError(); + } + if(args[0].equals("cross")) { + try { + UIManager.setLookAndFeel(UIManager. + getCrossPlatformLookAndFeelClassName()); + } catch(Exception e) { + e.printStackTrace(); + } + } else if(args[0].equals("system")) { + try { + UIManager.setLookAndFeel(UIManager. + getSystemLookAndFeelClassName()); + } catch(Exception e) { + e.printStackTrace(); + } + } else if(args[0].equals("motif")) { + try { + UIManager.setLookAndFeel("com.sun.java."+ + "swing.plaf.motif.MotifLookAndFeel"); + } catch(Exception e) { + e.printStackTrace(); + } + } else { + usageError(); + } + // Note the look & feel must be set before + // any components are created. + run(new LookAndFeel(), 300, 300); + } +} ///:~ diff --git a/src/gui/Menus.java b/src/gui/Menus.java new file mode 100644 index 0000000..ffa342d --- /dev/null +++ b/src/gui/Menus.java @@ -0,0 +1,156 @@ +package gui;//: gui/Menus.java +// Submenus, check box menu items, swapping menus, +// mnemonics (shortcuts) and action commands. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import static net.mindview.util.SwingConsole.*; + +public class Menus extends JFrame { + private String[] flavors = { + "Chocolate", "Strawberry", "Vanilla Fudge Swirl", + "Mint Chip", "Mocha Almond Fudge", "Rum Raisin", + "Praline Cream", "Mud Pie" + }; + private JTextField t = new JTextField("No flavor", 30); + private JMenuBar mb1 = new JMenuBar(); + private JMenu + f = new JMenu("File"), + m = new JMenu("Flavors"), + s = new JMenu("Safety"); + // Alternative approach: + private JCheckBoxMenuItem[] safety = { + new JCheckBoxMenuItem("Guard"), + new JCheckBoxMenuItem("Hide") + }; + private JMenuItem[] file = { new JMenuItem("Open") }; + // A second menu bar to swap to: + private JMenuBar mb2 = new JMenuBar(); + private JMenu fooBar = new JMenu("fooBar"); + private JMenuItem[] other = { + // Adding a menu shortcut (mnemonic) is very + // simple, but only JMenuItems can have them + // in their constructors: + new JMenuItem("Foo", KeyEvent.VK_F), + new JMenuItem("Bar", KeyEvent.VK_A), + // No shortcut: + new JMenuItem("Baz"), + }; + private JButton b = new JButton("Swap Menus"); + class BL implements ActionListener { + public void actionPerformed(ActionEvent e) { + JMenuBar m = getJMenuBar(); + setJMenuBar(m == mb1 ? mb2 : mb1); + validate(); // Refresh the frame + } + } + class ML implements ActionListener { + public void actionPerformed(ActionEvent e) { + JMenuItem target = (JMenuItem)e.getSource(); + String actionCommand = target.getActionCommand(); + if(actionCommand.equals("Open")) { + String s = t.getText(); + boolean chosen = false; + for(String flavor : flavors) { + if(s.equals(flavor)) { + chosen = true; + } + } + if(!chosen) { + t.setText("Choose a flavor first!"); + } else { + t.setText("Opening " + s + ". Mmm, mm!"); + } + } + } + } + class FL implements ActionListener { + public void actionPerformed(ActionEvent e) { + JMenuItem target = (JMenuItem)e.getSource(); + t.setText(target.getText()); + } + } + // Alternatively, you can create a different + // class for each different MenuItem. Then you + // don't have to figure out which one it is: + class FooL implements ActionListener { + public void actionPerformed(ActionEvent e) { + t.setText("Foo selected"); + } + } + class BarL implements ActionListener { + public void actionPerformed(ActionEvent e) { + t.setText("Bar selected"); + } + } + class BazL implements ActionListener { + public void actionPerformed(ActionEvent e) { + t.setText("Baz selected"); + } + } + class CMIL implements ItemListener { + public void itemStateChanged(ItemEvent e) { + JCheckBoxMenuItem target = + (JCheckBoxMenuItem)e.getSource(); + String actionCommand = target.getActionCommand(); + if(actionCommand.equals("Guard")) { + t.setText("Guard the Ice Cream! " + + "Guarding is " + target.getState()); + } else if(actionCommand.equals("Hide")) { + t.setText("Hide the Ice Cream! " + + "Is it hidden? " + target.getState()); + } + } + } + public Menus() { + ML ml = new ML(); + CMIL cmil = new CMIL(); + safety[0].setActionCommand("Guard"); + safety[0].setMnemonic(KeyEvent.VK_G); + safety[0].addItemListener(cmil); + safety[1].setActionCommand("Hide"); + safety[1].setMnemonic(KeyEvent.VK_H); + safety[1].addItemListener(cmil); + other[0].addActionListener(new FooL()); + other[1].addActionListener(new BarL()); + other[2].addActionListener(new BazL()); + FL fl = new FL(); + int n = 0; + for(String flavor : flavors) { + JMenuItem mi = new JMenuItem(flavor); + mi.addActionListener(fl); + m.add(mi); + // Add separators at intervals: + if((n++ + 1) % 3 == 0) { + m.addSeparator(); + } + } + for(JCheckBoxMenuItem sfty : safety) { + s.add(sfty); + } + s.setMnemonic(KeyEvent.VK_A); + f.add(s); + f.setMnemonic(KeyEvent.VK_F); + for(int i = 0; i < file.length; i++) { + file[i].addActionListener(ml); + f.add(file[i]); + } + mb1.add(f); + mb1.add(m); + setJMenuBar(mb1); + t.setEditable(false); + add(t, BorderLayout.CENTER); + // Set up the system for swapping menus: + b.addActionListener(new BL()); + b.setMnemonic(KeyEvent.VK_S); + add(b, BorderLayout.NORTH); + for(JMenuItem oth : other) { + fooBar.add(oth); + } + fooBar.setMnemonic(KeyEvent.VK_B); + mb2.add(fooBar); + } + public static void main(String[] args) { + run(new Menus(), 300, 200); + } +} ///:~ diff --git a/src/gui/MessageBoxes.java b/src/gui/MessageBoxes.java new file mode 100644 index 0000000..29c35da --- /dev/null +++ b/src/gui/MessageBoxes.java @@ -0,0 +1,63 @@ +package gui;//: gui/MessageBoxes.java +// Demonstrates JOptionPane. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import static net.mindview.util.SwingConsole.*; + +public class MessageBoxes extends JFrame { + private JButton[] b = { + new JButton("Alert"), new JButton("Yes/No"), + new JButton("Color"), new JButton("Input"), + new JButton("3 Vals") + }; + private JTextField txt = new JTextField(15); + private ActionListener al = new ActionListener() { + public void actionPerformed(ActionEvent e) { + String id = ((JButton)e.getSource()).getText(); + if(id.equals("Alert")) { + JOptionPane.showMessageDialog(null, + "There's a bug on you!", "Hey!", + JOptionPane.ERROR_MESSAGE); + } else if(id.equals("Yes/No")) { + JOptionPane.showConfirmDialog(null, + "or no", "choose yes", + JOptionPane.YES_NO_OPTION); + } else if(id.equals("Color")) { + Object[] options = { "Red", "Green" }; + int sel = JOptionPane.showOptionDialog( + null, "Choose a Color!", "Warning", + JOptionPane.DEFAULT_OPTION, + JOptionPane.WARNING_MESSAGE, null, + options, options[0]); + if(sel != JOptionPane.CLOSED_OPTION) { + txt.setText("Color Selected: " + options[sel]); + } + } else if(id.equals("Input")) { + String val = JOptionPane.showInputDialog( + "How many fingers do you see?"); + txt.setText(val); + } else if(id.equals("3 Vals")) { + Object[] selections = {"First", "Second", "Third"}; + Object val = JOptionPane.showInputDialog( + null, "Choose one", "Input", + JOptionPane.INFORMATION_MESSAGE, + null, selections, selections[0]); + if(val != null) { + txt.setText(val.toString()); + } + } + } + }; + public MessageBoxes() { + setLayout(new FlowLayout()); + for(int i = 0; i < b.length; i++) { + b[i].addActionListener(al); + add(b[i]); + } + add(txt); + } + public static void main(String[] args) { + run(new MessageBoxes(), 200, 200); + } +} ///:~ diff --git a/src/gui/MonitoredLongRunningCallable.java b/src/gui/MonitoredLongRunningCallable.java new file mode 100644 index 0000000..37dbd45 --- /dev/null +++ b/src/gui/MonitoredLongRunningCallable.java @@ -0,0 +1,90 @@ +package gui;//: gui/MonitoredLongRunningCallable.java +// Displaying task progress with ProgressMonitors. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.concurrent.*; +import net.mindview.util.*; +import static net.mindview.util.SwingConsole.*; + +class MonitoredCallable implements Callable { + private static int counter = 0; + private final int id = counter++; + private final ProgressMonitor monitor; + private final static int MAX = 8; + public MonitoredCallable(ProgressMonitor monitor) { + this.monitor = monitor; + monitor.setNote(toString()); + monitor.setMaximum(MAX - 1); + monitor.setMillisToPopup(500); + } + public String call() { + System.out.println(this + " started"); + try { + for(int i = 0; i < MAX; i++) { + TimeUnit.MILLISECONDS.sleep(500); + if(monitor.isCanceled()) { + Thread.currentThread().interrupt(); + } + final int progress = i; + SwingUtilities.invokeLater( + new Runnable() { + public void run() { + monitor.setProgress(progress); + } + } + ); + } + } catch(InterruptedException e) { + monitor.close(); + System.out.println(this + " interrupted"); + return "Result: " + this + " interrupted"; + } + System.out.println(this + " completed"); + return "Result: " + this + " completed"; + } + public String toString() { return "Task " + id; } +}; + +public class MonitoredLongRunningCallable extends JFrame { + private JButton + b1 = new JButton("Start Long Running Task"), + b2 = new JButton("End Long Running Task"), + b3 = new JButton("Get results"); + private TaskManager manager = + new TaskManager(); + public MonitoredLongRunningCallable() { + b1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + MonitoredCallable task = new MonitoredCallable( + new ProgressMonitor( + MonitoredLongRunningCallable.this, + "Long-Running Task", "", 0, 0) + ); + manager.add(task); + System.out.println(task + " added to the queue"); + } + }); + b2.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + for(String result : manager.purge()) { + System.out.println(result); + } + } + }); + b3.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + for(String result : manager.getResults()) { + System.out.println(result); + } + } + }); + setLayout(new FlowLayout()); + add(b1); + add(b2); + add(b3); + } + public static void main(String[] args) { + run(new MonitoredLongRunningCallable(), 200, 500); + } +} ///:~ diff --git a/src/gui/Popup.java b/src/gui/Popup.java new file mode 100644 index 0000000..39eb323 --- /dev/null +++ b/src/gui/Popup.java @@ -0,0 +1,52 @@ +package gui;//: gui/Popup.java +// Creating popup menus with Swing. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import static net.mindview.util.SwingConsole.*; + +public class Popup extends JFrame { + private JPopupMenu popup = new JPopupMenu(); + private JTextField t = new JTextField(10); + public Popup() { + setLayout(new FlowLayout()); + add(t); + ActionListener al = new ActionListener() { + public void actionPerformed(ActionEvent e) { + t.setText(((JMenuItem)e.getSource()).getText()); + } + }; + JMenuItem m = new JMenuItem("Hither"); + m.addActionListener(al); + popup.add(m); + m = new JMenuItem("Yon"); + m.addActionListener(al); + popup.add(m); + m = new JMenuItem("Afar"); + m.addActionListener(al); + popup.add(m); + popup.addSeparator(); + m = new JMenuItem("Stay Here"); + m.addActionListener(al); + popup.add(m); + PopupListener pl = new PopupListener(); + addMouseListener(pl); + t.addMouseListener(pl); + } + class PopupListener extends MouseAdapter { + public void mousePressed(MouseEvent e) { + maybeShowPopup(e); + } + public void mouseReleased(MouseEvent e) { + maybeShowPopup(e); + } + private void maybeShowPopup(MouseEvent e) { + if(e.isPopupTrigger()) { + popup.show(e.getComponent(), e.getX(), e.getY()); + } + } + } + public static void main(String[] args) { + run(new Popup(), 300, 200); + } +} ///:~ diff --git a/src/gui/Progress.java b/src/gui/Progress.java new file mode 100644 index 0000000..46a00b6 --- /dev/null +++ b/src/gui/Progress.java @@ -0,0 +1,36 @@ +package gui;//: gui/Progress.java +// Using sliders, progress bars and progress monitors. +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.event.*; +import java.awt.*; +import static net.mindview.util.SwingConsole.*; + +public class Progress extends JFrame { + private JProgressBar pb = new JProgressBar(); + private ProgressMonitor pm = new ProgressMonitor( + this, "Monitoring Progress", "Test", 0, 100); + private JSlider sb = + new JSlider(JSlider.HORIZONTAL, 0, 100, 60); + public Progress() { + setLayout(new GridLayout(2,1)); + add(pb); + pm.setProgress(0); + pm.setMillisToPopup(1000); + sb.setValue(0); + sb.setPaintTicks(true); + sb.setMajorTickSpacing(20); + sb.setMinorTickSpacing(5); + sb.setBorder(new TitledBorder("Slide Me")); + pb.setModel(sb.getModel()); // Share model + add(sb); + sb.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + pm.setProgress(sb.getValue()); + } + }); + } + public static void main(String[] args) { + run(new Progress(), 300, 200); + } +} ///:~ diff --git a/src/gui/RadioButtons.java b/src/gui/RadioButtons.java new file mode 100644 index 0000000..80394b1 --- /dev/null +++ b/src/gui/RadioButtons.java @@ -0,0 +1,36 @@ +package gui;//: gui/RadioButtons.java +// Using JRadioButtons. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import static net.mindview.util.SwingConsole.*; + +public class RadioButtons extends JFrame { + private JTextField t = new JTextField(15); + private ButtonGroup g = new ButtonGroup(); + private JRadioButton + rb1 = new JRadioButton("one", false), + rb2 = new JRadioButton("two", false), + rb3 = new JRadioButton("three", false); + private ActionListener al = new ActionListener() { + public void actionPerformed(ActionEvent e) { + t.setText("Radio button " + + ((JRadioButton)e.getSource()).getText()); + } + }; + public RadioButtons() { + rb1.addActionListener(al); + rb2.addActionListener(al); + rb3.addActionListener(al); + g.add(rb1); g.add(rb2); g.add(rb3); + t.setEditable(false); + setLayout(new FlowLayout()); + add(t); + add(rb1); + add(rb2); + add(rb3); + } + public static void main(String[] args) { + run(new RadioButtons(), 200, 125); + } +} ///:~ diff --git a/src/gui/ShowAddListeners.java b/src/gui/ShowAddListeners.java new file mode 100644 index 0000000..d0c8dd9 --- /dev/null +++ b/src/gui/ShowAddListeners.java @@ -0,0 +1,59 @@ +package gui;//: gui/ShowAddListeners.java +// Display the "addXXXListener" methods of any Swing class. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.lang.reflect.*; +import java.util.regex.*; +import static net.mindview.util.SwingConsole.*; + +public class ShowAddListeners extends JFrame { + private JTextField name = new JTextField(25); + private JTextArea results = new JTextArea(40, 65); + private static Pattern addListener = + Pattern.compile("(add\\w+?Listener\\(.*?\\))"); + private static Pattern qualifier = + Pattern.compile("\\w+\\."); + class NameL implements ActionListener { + public void actionPerformed(ActionEvent e) { + String nm = name.getText().trim(); + if(nm.length() == 0) { + results.setText("No match"); + return; + } + Class kind; + try { + kind = Class.forName("javax.swing." + nm); + } catch(ClassNotFoundException ex) { + results.setText("No match"); + return; + } + Method[] methods = kind.getMethods(); + results.setText(""); + for(Method m : methods) { + Matcher matcher = + addListener.matcher(m.toString()); + if(matcher.find()) { + results.append(qualifier.matcher( + matcher.group(1)).replaceAll("") + "\n"); + } + } + } + } + public ShowAddListeners() { + NameL nameListener = new NameL(); + name.addActionListener(nameListener); + JPanel top = new JPanel(); + top.add(new JLabel("Swing class name (press Enter):")); + top.add(name); + add(BorderLayout.NORTH, top); + add(new JScrollPane(results)); + // Initial data and test: + name.setText("JTextArea"); + nameListener.actionPerformed( + new ActionEvent("", 0 ,"")); + } + public static void main(String[] args) { + run(new ShowAddListeners(), 500, 400); + } +} ///:~ diff --git a/src/gui/SimpleMenus.java b/src/gui/SimpleMenus.java new file mode 100644 index 0000000..de48fd7 --- /dev/null +++ b/src/gui/SimpleMenus.java @@ -0,0 +1,41 @@ +package gui;//: gui/SimpleMenus.java +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import static net.mindview.util.SwingConsole.*; + +public class SimpleMenus extends JFrame { + private JTextField t = new JTextField(15); + private ActionListener al = new ActionListener() { + public void actionPerformed(ActionEvent e) { + t.setText(((JMenuItem)e.getSource()).getText()); + } + }; + private JMenu[] menus = { + new JMenu("Winken"), new JMenu("Blinken"), + new JMenu("Nod") + }; + private JMenuItem[] items = { + new JMenuItem("Fee"), new JMenuItem("Fi"), + new JMenuItem("Fo"), new JMenuItem("Zip"), + new JMenuItem("Zap"), new JMenuItem("Zot"), + new JMenuItem("Olly"), new JMenuItem("Oxen"), + new JMenuItem("Free") + }; + public SimpleMenus() { + for(int i = 0; i < items.length; i++) { + items[i].addActionListener(al); + menus[i % 3].add(items[i]); + } + JMenuBar mb = new JMenuBar(); + for(JMenu jm : menus) { + mb.add(jm); + } + setJMenuBar(mb); + setLayout(new FlowLayout()); + add(t); + } + public static void main(String[] args) { + run(new SimpleMenus(), 200, 150); + } +} ///:~ diff --git a/src/gui/SineWave.java b/src/gui/SineWave.java new file mode 100644 index 0000000..0609038 --- /dev/null +++ b/src/gui/SineWave.java @@ -0,0 +1,62 @@ +package gui;//: gui/SineWave.java +// Drawing with Swing, using a JSlider. +import javax.swing.*; +import javax.swing.event.*; +import java.awt.*; +import static net.mindview.util.SwingConsole.*; + +class SineDraw extends JPanel { + private static final int SCALEFACTOR = 200; + private int cycles; + private int points; + private double[] sines; + private int[] pts; + public SineDraw() { setCycles(5); } + public void paintComponent(Graphics g) { + super.paintComponent(g); + int maxWidth = getWidth(); + double hstep = (double)maxWidth / (double)points; + int maxHeight = getHeight(); + pts = new int[points]; + for(int i = 0; i < points; i++) { + pts[i] = + (int)(sines[i] * maxHeight/2 * .95 + maxHeight/2); + } + g.setColor(Color.RED); + for(int i = 1; i < points; i++) { + int x1 = (int)((i - 1) * hstep); + int x2 = (int)(i * hstep); + int y1 = pts[i-1]; + int y2 = pts[i]; + g.drawLine(x1, y1, x2, y2); + } + } + public void setCycles(int newCycles) { + cycles = newCycles; + points = SCALEFACTOR * cycles * 2; + sines = new double[points]; + for(int i = 0; i < points; i++) { + double radians = (Math.PI / SCALEFACTOR) * i; + sines[i] = Math.sin(radians); + } + repaint(); + } +} + +public class SineWave extends JFrame { + private SineDraw sines = new SineDraw(); + private JSlider adjustCycles = new JSlider(1, 30, 5); + public SineWave() { + add(sines); + adjustCycles.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + sines.setCycles( + ((JSlider)e.getSource()).getValue()); + } + }); + add(BorderLayout.SOUTH, adjustCycles); + } + public static void main(String[] args) { + run(new SineWave(), 700, 400); + } +} ///:~ diff --git a/src/gui/SubmitLabelManipulationTask.java b/src/gui/SubmitLabelManipulationTask.java new file mode 100644 index 0000000..604c99d --- /dev/null +++ b/src/gui/SubmitLabelManipulationTask.java @@ -0,0 +1,20 @@ +package gui;//: gui/SubmitLabelManipulationTask.java +import javax.swing.*; +import java.util.concurrent.*; + +public class SubmitLabelManipulationTask { + public static void main(String[] args) throws Exception { + JFrame frame = new JFrame("Hello Swing"); + final JLabel label = new JLabel("A Label"); + frame.add(label); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(300, 100); + frame.setVisible(true); + TimeUnit.SECONDS.sleep(1); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + label.setText("Hey! This is Different!"); + } + }); + } +} ///:~ diff --git a/src/gui/SubmitSwingProgram.java b/src/gui/SubmitSwingProgram.java new file mode 100644 index 0000000..dbe2591 --- /dev/null +++ b/src/gui/SubmitSwingProgram.java @@ -0,0 +1,27 @@ +package gui;//: gui/SubmitSwingProgram.java +import javax.swing.*; +import java.util.concurrent.*; + +public class SubmitSwingProgram extends JFrame { + JLabel label; + public SubmitSwingProgram() { + super("Hello Swing"); + label = new JLabel("A Label"); + add(label); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setSize(300, 100); + setVisible(true); + } + static SubmitSwingProgram ssp; + public static void main(String[] args) throws Exception { + SwingUtilities.invokeLater(new Runnable() { + public void run() { ssp = new SubmitSwingProgram(); } + }); + TimeUnit.SECONDS.sleep(1); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + ssp.label.setText("Hey! This is Different!"); + } + }); + } +} ///:~ diff --git a/src/gui/TabbedPane1.java b/src/gui/TabbedPane1.java new file mode 100644 index 0000000..4f25da2 --- /dev/null +++ b/src/gui/TabbedPane1.java @@ -0,0 +1,34 @@ +package gui;//: gui/TabbedPane1.java +// Demonstrates the Tabbed Pane. +import javax.swing.*; +import javax.swing.event.*; +import java.awt.*; +import static net.mindview.util.SwingConsole.*; + +public class TabbedPane1 extends JFrame { + private String[] flavors = { + "Chocolate", "Strawberry", "Vanilla Fudge Swirl", + "Mint Chip", "Mocha Almond Fudge", "Rum Raisin", + "Praline Cream", "Mud Pie" + }; + private JTabbedPane tabs = new JTabbedPane(); + private JTextField txt = new JTextField(20); + public TabbedPane1() { + int i = 0; + for(String flavor : flavors) { + tabs.addTab(flavors[i], + new JButton("Tabbed pane " + i++)); + } + tabs.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + txt.setText("Tab selected: " + + tabs.getSelectedIndex()); + } + }); + add(BorderLayout.SOUTH, txt); + add(tabs); + } + public static void main(String[] args) { + run(new TabbedPane1(), 400, 250); + } +} ///:~ diff --git a/src/gui/TextArea.java b/src/gui/TextArea.java new file mode 100644 index 0000000..f00f697 --- /dev/null +++ b/src/gui/TextArea.java @@ -0,0 +1,40 @@ +package gui;//: gui/TextArea.java +// Using the JTextArea control. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.SwingConsole.*; + +public class TextArea extends JFrame { + private JButton + b = new JButton("Add Data"), + c = new JButton("Clear Data"); + private JTextArea t = new JTextArea(20, 40); + private Map m = + new HashMap(); + public TextArea() { + // Use up all the data: + m.putAll(Countries.capitals()); + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + for(Map.Entry me : m.entrySet()) { + t.append(me.getKey() + ": "+ me.getValue()+"\n"); + } + } + }); + c.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + t.setText(""); + } + }); + setLayout(new FlowLayout()); + add(new JScrollPane(t)); + add(b); + add(c); + } + public static void main(String[] args) { + run(new TextArea(), 475, 425); + } +} ///:~ diff --git a/src/gui/TextFields.java b/src/gui/TextFields.java new file mode 100644 index 0000000..3b7d45c --- /dev/null +++ b/src/gui/TextFields.java @@ -0,0 +1,85 @@ +package gui;//: gui/TextFields.java +// Text fields and Java events. +import javax.swing.*; +import javax.swing.event.*; +import javax.swing.text.*; +import java.awt.*; +import java.awt.event.*; +import static net.mindview.util.SwingConsole.*; + +public class TextFields extends JFrame { + private JButton + b1 = new JButton("Get Text"), + b2 = new JButton("Set Text"); + private JTextField + t1 = new JTextField(30), + t2 = new JTextField(30), + t3 = new JTextField(30); + private String s = ""; + private UpperCaseDocument ucd = new UpperCaseDocument(); + public TextFields() { + t1.setDocument(ucd); + ucd.addDocumentListener(new T1()); + b1.addActionListener(new B1()); + b2.addActionListener(new B2()); + t1.addActionListener(new T1A()); + setLayout(new FlowLayout()); + add(b1); + add(b2); + add(t1); + add(t2); + add(t3); + } + class T1 implements DocumentListener { + public void changedUpdate(DocumentEvent e) {} + public void insertUpdate(DocumentEvent e) { + t2.setText(t1.getText()); + t3.setText("Text: "+ t1.getText()); + } + public void removeUpdate(DocumentEvent e) { + t2.setText(t1.getText()); + } + } + class T1A implements ActionListener { + private int count = 0; + public void actionPerformed(ActionEvent e) { + t3.setText("t1 Action Event " + count++); + } + } + class B1 implements ActionListener { + public void actionPerformed(ActionEvent e) { + if(t1.getSelectedText() == null) { + s = t1.getText(); + } else { + s = t1.getSelectedText(); + } + t1.setEditable(true); + } + } + class B2 implements ActionListener { + public void actionPerformed(ActionEvent e) { + ucd.setUpperCase(false); + t1.setText("Inserted by Button 2: " + s); + ucd.setUpperCase(true); + t1.setEditable(false); + } + } + public static void main(String[] args) { + run(new TextFields(), 375, 200); + } +} + +class UpperCaseDocument extends PlainDocument { + private boolean upperCase = true; + public void setUpperCase(boolean flag) { + upperCase = flag; + } + public void + insertString(int offset, String str, AttributeSet attSet) + throws BadLocationException { + if(upperCase) { + str = str.toUpperCase(); + } + super.insertString(offset, str, attSet); + } +} ///:~ diff --git a/src/gui/TextPane.java b/src/gui/TextPane.java new file mode 100644 index 0000000..c257fa7 --- /dev/null +++ b/src/gui/TextPane.java @@ -0,0 +1,28 @@ +package gui;//: gui/TextPane.java +// The JTextPane control is a little editor. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import net.mindview.util.*; +import static net.mindview.util.SwingConsole.*; + +public class TextPane extends JFrame { + private JButton b = new JButton("Add Text"); + private JTextPane tp = new JTextPane(); + private static Generator sg = + new RandomGenerator.String(7); + public TextPane() { + b.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + for(int i = 1; i < 10; i++) { + tp.setText(tp.getText() + sg.next() + "\n"); + } + } + }); + add(new JScrollPane(tp)); + add(BorderLayout.SOUTH, b); + } + public static void main(String[] args) { + run(new TextPane(), 475, 425); + } +} ///:~ diff --git a/src/gui/TicTacToe.java b/src/gui/TicTacToe.java new file mode 100644 index 0000000..8d6229b --- /dev/null +++ b/src/gui/TicTacToe.java @@ -0,0 +1,84 @@ +package gui;//: gui/TicTacToe.java +// Dialog boxes and creating your own components. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import static net.mindview.util.SwingConsole.*; + +public class TicTacToe extends JFrame { + private JTextField + rows = new JTextField("3"), + cols = new JTextField("3"); + private enum State { BLANK, XX, OO } + static class ToeDialog extends JDialog { + private State turn = State.XX; // Start with x's turn + ToeDialog(int cellsWide, int cellsHigh) { + setTitle("The game itself"); + setLayout(new GridLayout(cellsWide, cellsHigh)); + for(int i = 0; i < cellsWide * cellsHigh; i++) { + add(new ToeButton()); + } + setSize(cellsWide * 50, cellsHigh * 50); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + } + class ToeButton extends JPanel { + private State state = State.BLANK; + public ToeButton() { addMouseListener(new ML()); } + public void paintComponent(Graphics g) { + super.paintComponent(g); + int + x1 = 0, y1 = 0, + x2 = getSize().width - 1, + y2 = getSize().height - 1; + g.drawRect(x1, y1, x2, y2); + x1 = x2/4; + y1 = y2/4; + int wide = x2/2, high = y2/2; + if(state == State.XX) { + g.drawLine(x1, y1, x1 + wide, y1 + high); + g.drawLine(x1, y1 + high, x1 + wide, y1); + } + if(state == State.OO) { + g.drawOval(x1, y1, x1 + wide/2, y1 + high/2); + } + } + class ML extends MouseAdapter { + public void mousePressed(MouseEvent e) { + if(state == State.BLANK) { + state = turn; + turn = + (turn == State.XX ? State.OO : State.XX); + } + else { + state = + (state == State.XX ? State.OO : State.XX); + } + repaint(); + } + } + } + } + class BL implements ActionListener { + public void actionPerformed(ActionEvent e) { + JDialog d = new ToeDialog( + new Integer(rows.getText()), + new Integer(cols.getText())); + d.setVisible(true); + } + } + public TicTacToe() { + JPanel p = new JPanel(); + p.setLayout(new GridLayout(2,2)); + p.add(new JLabel("Rows", JLabel.CENTER)); + p.add(rows); + p.add(new JLabel("Columns", JLabel.CENTER)); + p.add(cols); + add(p, BorderLayout.NORTH); + JButton b = new JButton("go"); + b.addActionListener(new BL()); + add(b, BorderLayout.SOUTH); + } + public static void main(String[] args) { + run(new TicTacToe(), 200, 200); + } +} ///:~ diff --git a/src/gui/TrackEvent.java b/src/gui/TrackEvent.java new file mode 100644 index 0000000..384d312 --- /dev/null +++ b/src/gui/TrackEvent.java @@ -0,0 +1,93 @@ +package gui;//: gui/TrackEvent.java +// Show events as they happen. +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import static net.mindview.util.SwingConsole.*; + +public class TrackEvent extends JFrame { + private HashMap h = + new HashMap(); + private String[] event = { + "focusGained", "focusLost", "keyPressed", + "keyReleased", "keyTyped", "mouseClicked", + "mouseEntered", "mouseExited", "mousePressed", + "mouseReleased", "mouseDragged", "mouseMoved" + }; + private MyButton + b1 = new MyButton(Color.BLUE, "test1"), + b2 = new MyButton(Color.RED, "test2"); + class MyButton extends JButton { + void report(String field, String msg) { + h.get(field).setText(msg); + } + FocusListener fl = new FocusListener() { + public void focusGained(FocusEvent e) { + report("focusGained", e.paramString()); + } + public void focusLost(FocusEvent e) { + report("focusLost", e.paramString()); + } + }; + KeyListener kl = new KeyListener() { + public void keyPressed(KeyEvent e) { + report("keyPressed", e.paramString()); + } + public void keyReleased(KeyEvent e) { + report("keyReleased", e.paramString()); + } + public void keyTyped(KeyEvent e) { + report("keyTyped", e.paramString()); + } + }; + MouseListener ml = new MouseListener() { + public void mouseClicked(MouseEvent e) { + report("mouseClicked", e.paramString()); + } + public void mouseEntered(MouseEvent e) { + report("mouseEntered", e.paramString()); + } + public void mouseExited(MouseEvent e) { + report("mouseExited", e.paramString()); + } + public void mousePressed(MouseEvent e) { + report("mousePressed", e.paramString()); + } + public void mouseReleased(MouseEvent e) { + report("mouseReleased", e.paramString()); + } + }; + MouseMotionListener mml = new MouseMotionListener() { + public void mouseDragged(MouseEvent e) { + report("mouseDragged", e.paramString()); + } + public void mouseMoved(MouseEvent e) { + report("mouseMoved", e.paramString()); + } + }; + public MyButton(Color color, String label) { + super(label); + setBackground(color); + addFocusListener(fl); + addKeyListener(kl); + addMouseListener(ml); + addMouseMotionListener(mml); + } + } + public TrackEvent() { + setLayout(new GridLayout(event.length + 1, 2)); + for(String evt : event) { + JTextField t = new JTextField(); + t.setEditable(false); + add(new JLabel(evt, JLabel.RIGHT)); + add(t); + h.put(evt, t); + } + add(b1); + add(b2); + } + public static void main(String[] args) { + run(new TrackEvent(), 700, 500); + } +} ///:~ diff --git a/src/gui/build.xml b/src/gui/build.xml new file mode 100644 index 0000000..d2344cc --- /dev/null +++ b/src/gui/build.xml @@ -0,0 +1,595 @@ + + + + + + build.xml for the source code for the gui chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/gui/flex/Song.java b/src/gui/flex/Song.java new file mode 100644 index 0000000..eb1b9d2 --- /dev/null +++ b/src/gui/flex/Song.java @@ -0,0 +1,35 @@ +//: gui/flex/Song.java +package gui.flex; + +public class Song implements java.io.Serializable { + private String name; + private String artist; + private String album; + private String albumImageUrl; + private String songMediaUrl; + public Song() {} + public Song(String name, String artist, String album, + String albumImageUrl, String songMediaUrl) { + this.name = name; + this.artist = artist; + this.album = album; + this.albumImageUrl = albumImageUrl; + this.songMediaUrl = songMediaUrl; + } + public void setAlbum(String album) { this.album = album;} + public String getAlbum() { return album; } + public void setAlbumImageUrl(String albumImageUrl) { + this.albumImageUrl = albumImageUrl; + } + public String getAlbumImageUrl() { return albumImageUrl;} + public void setArtist(String artist) { + this.artist = artist; + } + public String getArtist() { return artist; } + public void setName(String name) { this.name = name; } + public String getName() { return name; } + public void setSongMediaUrl(String songMediaUrl) { + this.songMediaUrl = songMediaUrl; + } + public String getSongMediaUrl() { return songMediaUrl; } +} ///:~ diff --git a/src/gui/flex/SongService.java b/src/gui/flex/SongService.java new file mode 100644 index 0000000..530b3f4 --- /dev/null +++ b/src/gui/flex/SongService.java @@ -0,0 +1,22 @@ +//: gui/flex/SongService.java +package gui.flex; +import java.util.*; + +public class SongService { + private List songs = new ArrayList(); + public SongService() { fillTestData(); } + public List getSongs() { return songs; } + public void addSong(Song song) { songs.add(song); } + public void removeSong(Song song) { songs.remove(song); } + private void fillTestData() { + addSong(new Song("Chocolate", "Snow Patrol", + "Final Straw", "sp-final-straw.jpg", + "chocolate.mp3")); + addSong(new Song("Concerto No. 2 in E", "Hilary Hahn", + "Bach: Violin Concertos", "hahn.jpg", + "bachviolin2.mp3")); + addSong(new Song("'Round Midnight", "Wes Montgomery", + "The Artistry of Wes Montgomery", + "wesmontgomery.jpg", "roundmidnight.mp3")); + } +} ///:~ diff --git a/src/gui/flex/build-command.txt b/src/gui/flex/build-command.txt new file mode 100644 index 0000000..48636f8 --- /dev/null +++ b/src/gui/flex/build-command.txt @@ -0,0 +1 @@ +mxmlc -flexlib C:/"Program Files"/Macromedia/Flex/jrun4/servers/default/flex/WEB-INF/flex songs.mxml diff --git a/src/gui/flex/helloflex1.mxml b/src/gui/flex/helloflex1.mxml new file mode 100644 index 0000000..cf50464 --- /dev/null +++ b/src/gui/flex/helloflex1.mxml @@ -0,0 +1,6 @@ + + + + diff --git a/src/gui/flex/helloflex2.mxml b/src/gui/flex/helloflex2.mxml new file mode 100644 index 0000000..6bd508e --- /dev/null +++ b/src/gui/flex/helloflex2.mxml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/src/gui/flex/songScript.as b/src/gui/flex/songScript.as new file mode 100644 index 0000000..2e19486 --- /dev/null +++ b/src/gui/flex/songScript.as @@ -0,0 +1,22 @@ +//: gui/flex/songScript.as +function getSongs() { + songService.getSongs(); +} + +function selectSong(event) { + var song = songGrid.getItemAt(event.itemIndex); + showSongInfo(song); +} + +function showSongInfo(song) { + songInfo.text = song.name + newline; + songInfo.text += song.artist + newline; + songInfo.text += song.album + newline; + albumImage.source = song.albumImageUrl; + songPlayer.contentPath = song.songMediaUrl; + songPlayer.visible = true; +} + +function onSongs(songs) { + songGrid.dataProvider = songs; +} ///:~ diff --git a/src/gui/flex/songStyles.css b/src/gui/flex/songStyles.css new file mode 100644 index 0000000..22fb66d --- /dev/null +++ b/src/gui/flex/songStyles.css @@ -0,0 +1,11 @@ +.headerText { + font-family: Arial, "_sans"; + font-size: 16; + font-weight: bold; +} + +.boldText { + font-family: Arial, "_sans"; + font-size: 11; + font-weight: bold; +} diff --git a/src/gui/flex/songs.mxml b/src/gui/flex/songs.mxml new file mode 100644 index 0000000..be2d047 --- /dev/null +++ b/src/gui/flex/songs.mxml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/gui/jnlp/JnlpFileChooser.java b/src/gui/jnlp/JnlpFileChooser.java new file mode 100644 index 0000000..9a8a6a7 --- /dev/null +++ b/src/gui/jnlp/JnlpFileChooser.java @@ -0,0 +1,97 @@ +//: gui/jnlp/JnlpFileChooser.java +// Opening files on a local machine with JNLP. +// {Requires: javax.jnlp.FileOpenService; +// You must have javaws.jar in your classpath} +// To create the jnlpfilechooser.jar file, do this: +// cd .. +// cd .. +// jar cvf gui/jnlp/jnlpfilechooser.jar gui/jnlp/*.class +package gui.jnlp; +import javax.jnlp.*; +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.io.*; + +public class JnlpFileChooser extends JFrame { + private JTextField fileName = new JTextField(); + private JButton + open = new JButton("Open"), + save = new JButton("Save"); + private JEditorPane ep = new JEditorPane(); + private JScrollPane jsp = new JScrollPane(); + private FileContents fileContents; + public JnlpFileChooser() { + JPanel p = new JPanel(); + open.addActionListener(new OpenL()); + p.add(open); + save.addActionListener(new SaveL()); + p.add(save); + jsp.getViewport().add(ep); + add(jsp, BorderLayout.CENTER); + add(p, BorderLayout.SOUTH); + fileName.setEditable(false); + p = new JPanel(); + p.setLayout(new GridLayout(2,1)); + p.add(fileName); + add(p, BorderLayout.NORTH); + ep.setContentType("text"); + save.setEnabled(false); + } + class OpenL implements ActionListener { + public void actionPerformed(ActionEvent e) { + FileOpenService fs = null; + try { + fs = (FileOpenService)ServiceManager.lookup( + "javax.jnlp.FileOpenService"); + } catch(UnavailableServiceException use) { + throw new RuntimeException(use); + } + if(fs != null) { + try { + fileContents = fs.openFileDialog(".", + new String[]{"txt", "*"}); + if(fileContents == null) { + return; + } + fileName.setText(fileContents.getName()); + ep.read(fileContents.getInputStream(), null); + } catch(Exception exc) { + throw new RuntimeException(exc); + } + save.setEnabled(true); + } + } + } + class SaveL implements ActionListener { + public void actionPerformed(ActionEvent e) { + FileSaveService fs = null; + try { + fs = (FileSaveService)ServiceManager.lookup( + "javax.jnlp.FileSaveService"); + } catch(UnavailableServiceException use) { + throw new RuntimeException(use); + } + if(fs != null) { + try { + fileContents = fs.saveFileDialog(".", + new String[]{"txt"}, + new ByteArrayInputStream( + ep.getText().getBytes()), + fileContents.getName()); + if(fileContents == null) { + return; + } + fileName.setText(fileContents.getName()); + } catch(Exception exc) { + throw new RuntimeException(exc); + } + } + } + } + public static void main(String[] args) { + JnlpFileChooser fc = new JnlpFileChooser(); + fc.setSize(400, 300); + fc.setVisible(true); + } +} ///:~ diff --git a/src/gui/jnlp/filechooser.html b/src/gui/jnlp/filechooser.html new file mode 100644 index 0000000..495affc --- /dev/null +++ b/src/gui/jnlp/filechooser.html @@ -0,0 +1,5 @@ + +Follow the instructions in JnlpFileChooser.java to +build jnlpfilechooser.jar, then: +
click here + diff --git a/src/gui/jnlp/filechooser.jnlp b/src/gui/jnlp/filechooser.jnlp new file mode 100644 index 0000000..4e4c364 --- /dev/null +++ b/src/gui/jnlp/filechooser.jnlp @@ -0,0 +1,24 @@ + + + + FileChooser demo application + Mindview Inc. + + Jnlp File chooser Application + + + Demonstrates opening, reading and writing a text file + + + + + + + + + + diff --git a/src/gui/jnlp/mindview.gif b/src/gui/jnlp/mindview.gif new file mode 100644 index 0000000000000000000000000000000000000000..31496411182863d4e2e9497e668c464d4c611275 GIT binary patch literal 12103 zcmWkz^;?sV+kKujqeeH5ZW$mF0*)TtrNrn^5ENyYD`1q4P)QvnA?WB(bd&-jj!?nC zA*iT`fQ5Y2z2EozaQ=XEopY}HI=8cnlet;sE*J&;0)V4eG1O}SpU=PC7G2YRxM?1E zxJb?`fMs8X)U}23F90`ggyogzF^Zst*>L{%5&nJPT%TNSE1th{ab+sHvfZS98u+=h zS&$1atfq}VKYOxDrKCRn+89uEA9(-%{fR2Mf}21|H_-Dqsk$xdT%E>QmeIZW^xAuV z7i-S+%^oSenUqy;_j~teVY6S}4S-aMTH9F4ssnOaVz+Pl9IKFMnF=nvZ^*c5y!39m zW%%&itNT|Ppe3x+m1B0j4=>-mM;m%lpUpy_zjN+NW6GDU)xz5r3%mzat*+(0goc5$ z(+fScM%~9l9>q<2X}u?}41$#%N3VAuIZ3EjLME$MWd%4T*=~%$sM^%+A@(<_R zr~}w*Y~MRij(0qYxzcQ2(VEmaBk<ntmKAg?Dv>v~TVHmLsLF)Hhl zOByhn-@a7e@yM@&WU}`3bfzz_EHw-p{M)`?(i%F@W5H^@{%MgmI$+OYAKv=$p=R85 zXr7c`CwKX(=D#+e;i2mL_2>8R*p#)}mXBYp?{%#ou+F{VmU&I*-$-gn6_o$Jk$*{! z|0<(&#By!5v1_=nsNK5uA@E|UyuMX+;vw-${pEj;ssC;f$h8(@_XwTyZG1XvV(N5P zC$6r`zj0cul*8Eh(#?NX+%O@>9|*rO>D&9lApagMuLpI#J$vLfvaUXgb_>lPQDqiM zkImotlP}hNFLhv~KeJw_Wwzzf@`HcvK5f0NRh5?iRx0kk=>M>}`uB<&>zZ@LpykqR zT2mvM`}E1um-^xoSotWDKP<(667h!5PbrhSej_%&Bbnb}(D10Le!}}tDLAqA;e4}T zaS`TaH|)(EW&Ov*hj0D!pQzmrl5X~2Wew?Hs>`UW^BEj(<4+p#r!6kE1KfrD*BeXx zCobh>Mn#td=l=a84;M_mPa1tRdgWTi^vYz#NPg$T^!sn@nx8_e%4It5H`dg~XI9JA zvjczq{oCCgb)}I~(;WG5<t}2^SdKT)S#N_B%9b=lvQcJAyEn6W*(n1mU*^}KH*Jd+{{E<#X*7koV zf^yUT3qIlq&LOa)ZJ$&iD}?p*P!Z}ESqeR>T_?5onXHTIDIwO%MXR4{E0^#HomHlT zbmD3eL9=wK2P2H5KNAF?*J#`9Hpb&8a~!n z(34AzN`wS_7a|!#PJQp)w0z-Nc>&4fQF#f;fov0F&6c&=ywkJe&?0x{!+N^3C8#Ha zW7=I(jH$3ivpROCsr%jBnla`;d-_2L0VBfPZCI*JZN)~f)Q zQV7Nz_=8Wf_890YXB{{=iUTkP1S1aG;17<>?%ggMk%sCQ(Jtz(vS4xsYdbf4hPP$y zr7aOg8-O$U0I(-!t#4H1&VX3AX^1Eb#!lHBdI&e_ z;^+?@NHN+FbzCQniqE0hS>hpGWVU29iub^(qn-&uNyK8?OAawDCQ!MvhXjrGzy`UO z&yO`~lo9sevm&F6o#JBMs^zfmhW=qu?vkh2@t*FEW3!UM-gsMyC^I8RH+G^DK0at< z{(W`hNgarriTMm1Bzd5!VZ+ZgDF!9qt*L$(a&? z`XYkx-c+!@im&u2K-~xgMgbt(U%@M_QS4hCCBq_<4;xf%{(7 z2*s8}x`pFZ7YVm90^DmDeolh5IKAeyI!_Ksz&ogmumoL*d7|kt!fw{XX1Tg!Q28Mn zCkv|NaPvs%6oe|lafF%?v6+25PVX9znp#kIqJZo4cW_KJh`AEj(uJ|m=UpNT#b5_P zA>#V5A{lVdpDyYeGkcnDpe;6qpAc; zL#<1ZMMx6%Ak4=>q@H)?T2k%an;DE}oK-3BvIn~95ct8!Y=I>1biggFqBBnSTqE&~ z4qZaP+O83Hva3$XlnFbM2S=)LZ|uK`K^{9)atXbqh7B7+ochi}E}xaLfm}H$+c>j7NT$A)~Wf zsNV|60@j1Bip;vXzBLv(Cgf3OdLATpqe%6WWPM=oWWX)??>C9UpKY~VpLJ|>)Lsa7 zS>SGV*Sk})CAm=NnPFPw!jD^R<2M5isr0{5|8bk_p)1m{I4_3(aeK`jHG^Nf8mvC6 zct^Y5Y+xXu{m{>jXw#cN2gop-xvj9t+Nv8J2x_mX5w0Anae`uDXpCLABzM?rSo5$1 zKd}L}&lJ#2;k;bh{fS_%T|~6XFT9oi)i?gvHOgoB)qBfd{ai_B=E&Uw$HMm)L%tg9VV|Sl%;=TOWo@6uAncZI+ z**bXdQ6J#;$ypQI8m8y1XEPn@%jZm0;l59b>CK)n7RUnbYyoLD2cDo@UGaP>&a`fk506_K zEgPv?W|so+en*ywrjb)y@N?|I@eNtYhwp<cd|bFNmGB6|&;CN_RKO-45C6S9)DsQ$E=% z9<@1mjpDjE6n6r7ke4kQm5VIkaKaGPvOB4SgUZhZ6vf-thWA-!@8wh6>$9RHuH(iz z`jOt?_bze8o=CJL59WrdLC)WT6&sRXy4ztP&!=*APMi@aGO?C0m9%pn;=15Sl6`iU z>&&w9XAChP+<(5K<`*7<)hHJg3|ZHDU)S~(yYd5ybpRt16BgKQrVK^EHt@kN_+ST@ zoi1c61@U&&vNXz5k+pfsa2A)#u7;8fa)oHJ_V?)X1zNJhm^$q+!xvixTHl9-jrxH5 zR7|GEXiJbU20t{xqVL%RQMLtEKU9wYIv;kMYl&Va=fGh1nQocuo2Z<6o0{Bzt|N)`PU)2C0QT zx4AT8^RZ50g{K(go%JFea*UI$@GS2Z>EMMxOo(GhS8zTsCYg>2T%e)SSrQC#NM11Q z21}xOpM#U6;}BQa_ZMx5;1C8N5~U>6AT(bDU`mz)yk(U*Qo}^;{1sG|xJQ18(3Rr? zxBH~rnSM(%NE`0OCLS_HJzF8}0;(%&5H7GE?-$OC9XlD71`4Cql|#ws*G7=-GO2a~ zrZYHSC?w?XDtd||bQD*lxam2y>D8H?Dl`WEDK)chD#B!$sdJ%^oM4(R5N)~Q$4zcK zENdyLL%2u*O?54hjHu#JZ}O#7D>bF9Sh0gc(14+!LxI9^K9E0+e&@*$WWQ=a6WD}d zE*tqB8}fU$dMP3S(0K3BRECc?S2kloQim>CUuHtvnZlV@F3>}gn(D++X6! z-gZ$Zka|UwE13Nj>GI|uPgo|#AM=I>m_sayuu#%PMtNCYh;)n&Mt3HMDOX->QEt5z zs0l;kZSCoJ(2raa>@6A&LXHt5)x6=^{bfz%6)lTWg*p-F8WH!j@LLw*Ti!r3H}qYD zM4LtB-Nnj#zbiR9;>{e%Pnas@B4Nw4N-dwt_pw#ezpJ=%)o`(d{Z2W%?RL(ZaxrhC z4e@fN&GyDx#Dn}H`BH=nPD=uOKzzI{9OpV&D{@-IEI}_u_C@*Gi~GwRa){!!#J6&iA0}#4agy_8 zlF1#!r@_)#4qX%nE(0RGO_cT_j71>O*~!YSIc}Mq5LFy4F;M-Vsr=$c3AM?yx0laGPD*-6p8&7K z_{1F!+7j(qmI`1g1U1HnB6M!b#YOIqiw=zYA96S2j-4XcMkCMfs-KaMCnZ6?^@2Gc zJrwx(>?Kh6%K|b27jT>+)%H=@v?t9>T2=+w5IDIqPc<&~J;VaiH1(o&da^a) z0pz%*##mZ&hHZ3*Uv(a(&2k8J-BQyE5BKLlCwe7ada;dY;(;xRY}*rcL-wkvw~TCW z1V<-Y*AR8gW3~>ZYa~f!QKYV7lfHJM8l=_s)hfV$wYo+n>)~OF82H5u1LKt>>rNA~ zUbQ6}a+HU7LlZd5GO(Q0kdd+<5i;hS=Sz|d7Js$%5tA-OG{$S#QD3JJop`R36_G`8THr14$C<; zJ)+mG9^p4kIO-epM+a0!0_wX7wO|>&*wz!7OR?OB>Y4yXcw;Ef=s5%?Zf|hf?Tn2v zYRVTzV?*g@z^Pi88xA2ivKKscwSue# zqDFN*qLO~xU`xeAPeskQ#YUxstS#|En|5XxOVG1FcU%aLMOg8R5$hQzL$uW5QhnD? z+$)?#6&WAAqAD;M*lE)N8&EZB={0a<>R0XS+JKUcn>eqAOT%X6|5!;X5&cJyo_cO^wl7eJrVVUnQ70o+#sS1 zsw}gwT6$qdOplHj9UcDPrej%^Y4}QDgPmz^1$**8X^x`2@FMrP5HGH=3=`3y06olx z84&dklY5|8J=Dt4VmD*@9fOQ`{SX|&QXB!n!iS>_=Io5?ZmN;=2AYn;`iO2)cTDhg zWC-)lC?_0Krm;tL&)AuW>WaHwBh_aY#3i@y8t_4v-;*`jDz~KwUpwf&1xO$D64IOK-hUS5itOsTAwq|gIb(Bc}jvP>?jU*#363K z6uFioIEX`QTEQL@)sM(PPao-=ygTJKD`fCTs1F!!o)cW`fy=OZAvt{bs7!QfIcUb{QA*0rCjJ~a3+D(+NYSl}5zeUdjXwd{OUHhm zs+yGAr(X(}I}nfbo9OZvNJ))ApHv2d^`ag;#%>A)A?7%NXB3DNrfxd2k<`+7uBCPA ztokf-R_B~(+Jek0OUg3~*;k*ykNnoNAd%Wgp&HkBZTyk2`YUnLelpXfz_N(;!VGZW z-Pw6|PJ(E|%c=G3GryOlwxM^BfNRTZsc=bkvf*BaB(hTKrP9dtK`xgq2ZY8?$3052 z7=AgMk(PrC+oRziJTM~Z?>!eYEMML}9gV(W45FjeBtj%qnGbIL+_jH=n*ZjGwXO?b zT?Q&Lk1@+&?t_RAbCaJ;y1kneT*%OzRDsvQ+QS?HB3IF!38Pv-TEgH#z^qIu?AxM$ z>)-#i&&9MNr=)r0MCA2Uhb-+IdAF35uq;$1+ZM`wme;^iFq2gb-Y=O76tyA;XwQM; zIV$A2(r1V$8oVuF0_3yHDX(1=-rh10n@xJ4TT$>5I`^5vxYYHq=cIoc4y19p3XgBV5 zoHM8KI`=ix;;N?q;J zB5xv1a8Tvv2$NLFo64U%_I_s7BYO6_HiUm7us^3O7sQ9ZcbIp(?Ep9!%wKFX*%?K+ z&tD1S!Ekawn0Rg1A*rl1ApqwN=OrGh5guL^D0pjglDkrH+;S}O~?z#0tFtt%-OutRe_Y#LfxD1ru%;iM%`Apepr!mJ zX?Yg(7*pxz2&{zttz?rItMV;^{X%;aQR3|xZ)+>=H>6zKK1NJ__1QG#Uj!QJM8`s>rRIJ1o-q@(atF7fQJlDZyNqK?d}$jI^jd# zTo>;si8O-R2!Jdy>iS>7w;~prU!|l1{T#Y?3J%u0wMc;*k*fQDt2livv&m|=hIm^j zGjZ3oFt2|4y^N26R_+1q&@z!_(o$VB=6j^QM1RDGY<7rxOGW%=?{JlYxq49Lt)w^N zw9dqS_Z^UB^~fa&+a6DaYN1?2skxMJ=kWycEtkG?lNs%^6513vk;1hj>Rownr!-U=K1 z#FPuvcTzE#YO(zcn|fhKcs1~4+xyn5-jwIJs+-!PAKx0bz34%hk%O4RUEuzvQ-P?$x_QBESqVW0BPY4|`k6aN@|s~y zX?b$>SsQj1*m0;lwu7K{$pP!4aW$r6Lc@^_+!raG(uS?;uNk;;>=@$r?^|jEZU%PsxFfBB2#nB9|A;hLoe8;B_KH|A|=HPWUm z@jcCTL!gURgCtalKDy7(!_>Nipu4HZfoh47YG^W76iosOV}NdeQ)^`sfU6-%`R_N- z-Oz<9^SYmhyXe5W#aoE}+DdqyNl0v)@o;`rRtsdYsjSIvCd!n&YLa~Lo9#?mUejJ* z*N5HdekY!MDJ7WWrB2Rk24wxJ-P^7=1r>X1;O?=WB8+|BqPu0v3x60Xt$noj_0R0< z`>^RDIeP;0f@f3kJDo(y*qvte)^k14!b{p9S)frOB~tQk;@oqdX|bK#&Y|^& z{bcJ?i}69Du$RXkTK`(ixcd6!d@65&EU`;ch$KvkB+;2CI}QvG4vrl==HfFw5A|*) zW)iMynBAR_x%;Z>b-VFb@^SAB=#S&&=Fct64qHEWc^pt7{})VkH&c=e?+!UtzHltw z7LZtmKwd-wNCzTBst=FY&!vdci7*ZGA;b#`c`v~M2uZ!eUocoP4g4&d*g~Y&?-R8w zGR|Jw>1j3zH9SLG0aspSk%4p_SYPg9@IsKyFN&erGg?(}aID4awfu;GlM}l5=cRU= zXt|ve^7@1=z&8{QQ)sFdfDrKLQ#1JyuxQyab0=eECK>EfTE8ZqBi_}1vR&Y6{dTs} zM+@@lGODstgt@ugG=^a&BzPY;JlKBrQkvuUnqXL_*mSd(=C|W9k;)mPb@RkrH=|*5 zQiDKz`fSN1Oz>^pA_>g=MfhGEQ7)k0HLz#)^6fvh@R>e}neiC?iCB=($38ockKzXk z7NQ4;+0`3^dal1_Q6Jo_OpxM0u*JhIxst7T3QVaK^c7Z^(kWMD%p5NxCnfJ>*fa??k!HH}c>g zW5bxA1q@9wTlYF0>2vM-+z9h7=E?0PZti56yDcLYPy79E)OJ=Ev(}($x<0TQSZGiz zKCpX|EX1sV^Is(ecTRF)arm19srJDp-gJE|Q;NhH4czh;42!vp!UJ}a9W0m?=j1KY zcv6H>5vZ{mfW{LYgTXriEy~`l{74T9#A0*dW*m!!DzL-6mwO6-z!APQP&uLdw6}D* z5gb~Vban0~zV$};!LUhh5Xctz{y~_FDlEvANVAG{niGsF$c#KHd5=eU5hu>*| zL~{erE91}xz%X!%MgOsDA`IC9A<=9aG-Vluv>Fx{35LNxU*&ARJ0=;$LBem;z`^NZ zcs)K_b(Ac1Fu_*nyf+LXQv;7CW(6483PsJU4v?6`K$I0(YG2UJA`dK5zmj@3gNHy>{YZ{8fp$I`L)Qq!3j1PjpB`b- z6omkk3;}x{nF=;3bA>@=+G!*+%Se&UwOFtf{L>5r^>N5gg+pQ;L1cKl8yI_H=RA^(8&qA0p=r3~dPSvM0p z_|aA|Rj4uCPOP4j_qBK#+Fwvd^DG$8Bj5)e+^Q z#8Id>wP`MuUZoc)2tiS2kmPVN=Ngp0A%qfyW2mkYg(Nh4fGFUpVi!l)mXj&i^^*Es z#13%WvBeNx1$G!Nzclj3o`t$+3u>zfMPu1Zg$i`(so<|r;t@#n*(BKBWloLfPmw6@ zT7nuI{zqw4?0x?U=Wi-V8e^zHse0kSZME@l(#eBaFbBc1o0D+f(7o4izJvMBe-*&j zLXL;3NJ5;0Ml!p=opCJoMSOEbP>Mm*SzYNWZ9B+Y>C9N^ps=ARxewbA$$lGfw zqq^s}n)1v`F9LIjF4BFYGc{_Z_{Cag#6Y8f`zQC3-nPdHNVCnX^fh=WCo5hS60k=1 z#L-nqRHPzgI;!EnU5YjqYO~WVkd&?Eou$HrhstJ!GvW3m2yz{6h5?S_=y9?%i6b|~ zUV=KTe%Zqm#c@OE0j%$l+dFnxV+;`f2!eSzkn++p%pQAs7H-8f0~5gh#Wj*RPazw zHe-@qaF0>E9a5Fm$*9?~cS;&z4Kb2*$sgxXrU1w4PGlOlZ&?s(J-=uC7 z!w$)~raNVJVxkUW-YmB(=}OQk1XN8&<+#g*ak^wmC(3gZwGRt(+hk}mask;|6VPF5 z(fGCYy@E1Oiy@euItpTF5CRo8(8piW&1u}a=gzzA>)q+dlk)c0@)_OZyKf97PgTJ= zWh5XXVkWMnPprsi!4S}2pv}w40g__p+>oiJ6Jj_ z;T+C(F?c%<$wv(Yu?`O{#$T~?R8k@8WrB1P!LN(}Q>B<{o6IemGgmGRUWkuc;x;!=SUO6PJ^RY1Vqg;V^-^(2+E`b2S!T*@kpSm-i zDFS4>{^#p$F*rAP6D{0Fcex_VyM<5?y?p}hVDS_^-++MRrkfOxEAoRY z8USAGr#6si_k>gqVpW73(R`x@kgAou>Tp7Ge!gQ1^Xxf10&(u1UIiRUN3yE6-7k3*6|T&P1b1kK96q65_{P-KW{)&l@SEN~%5*gP3Z=^kPw#91)-Y{eOrwWg;LFj1tFEoE%u{oJ;(@O{2zw z^u}BS9D6J1ojIHh3AsG|lt>l-B5R1JM4O6ea>1w{kHqkx8pgdbxUxnT+|x&?91KyA z&5Mq_Xtp5q_P5Ypl<)!TMe9Z2e}<7A({L3FvXd%ivH)w8fZGi85*K{%`amhy~q7gVR;# zwpYxZwYlLRAx{}l;la6@O={Wer**Q@molMYu@}YJe*T=rsmGxNw)8=K$Vy+sqO5aC z`|yc1)FqC+HFxF;<+(<4$C92eiSptA2V%!6xZo}|UDS4=;R(O5@a4pSkt%7SpF}vo z5lt~?S!s9Xr&pv`WI^7IUvzBr-IV&*j~;`g6E-Pd-YsqX2v2wlOrCqS^)xpwB=nuj z74F|@)dS@oMaUxWuz9;;EL`f=mlvdEMiD10B=d0g_b{I^Uj)(N+4nGKtAIZ^mIJnP z>9qVwJGUFhVOvL9UcS11QyYvIKu?qWxa2@+ncp@%vK|0Gbom+LE1v|2{P>YKqjB{_ z;`0;x#Ldm2qHBOF`;AYzTE?rXYncVB{|U;CH`6QcW$DwcvBA23Bknesv`+#evP(HE zs4WKOgUQl8ISw*|*SQ9OHpN8PFCqXnT0;fU&azRe2m65TPqv&f8+z#zL2xtsn z48rOb>ot-ER+h!*c)P*%;t{Rdpc(;!7bw-^P<&1aV-nQV$RXxeV&|FBx0p0I=UwPe z1aSe52k61UVcS;4nHDr3vDr)?@y|7ogUW&`J5bY9s$G+pch=z?vctT;2Lw-tVCXqm z3cc;+9qX9vg}6W7&=NAJ@k!jMGYevlc=x5vYKNYA?L+v-)dt-xOME9!bq>i6HxFF7 zXd7W_Krv(cML1T5q~sl1$~!#$E+>19&$yV-%X=68^Ib3lo@6ebW)OGqg$&GF;@Ml_ z&N8rz9EVHD*A9DZPkPy}9dF5vTPG)GHN9+m+peV?PiLZ3cHGma1>BCu0|_5ko_V{O zF=y%8_sN_$j)zDWg60{uzmCBN6k)Ann%a?^EuL@5!5fi}Fgxvj~JDomL ziHYU=8B{iM-JITigVuB7Gi>~$y3^yNEBXJ`({PM&gD?gzAZjq#CcxkX4qqMd_a*i;5^*H!ZUv z`R&ESkH~?6OC@@|vilBM{4d><58r9(fK3i^Cmslx;a93ldx*N~LZ&8`=a)q6b1hJ6&Sx83U!hthuu(i#}PZazI zU_UnfT=15?7DIgrDo#q1eWEpBsyFP(s94f+;arCLXDDALmaR zUk!}IbxlaecPDspSqjx~bfROPIN{s3zuV6j$gtaYV%awG(lhsr$7V*1u8}RsaeCm2&J>_K!`sH(pf_(sRt$ zM<1Y?{uTnbzDW+`)DvM=UY;Dews9G(Zpz*eq~jUc1=U$@m~5-fV~q?b+H1Nc5RD;h z@+HiZ`9o{^ zvW#FmEow#Y?@F2QRA+{hD6~9dA^qU7OMS32SimErU@SpMpGzBK&A(=6G|6V4&5$?e z<_0o%llpedHb3EK!2OrdI>s8b zZ>E|Ne#YcNTc?wtypY)T|L$BpHRyb9VBjRoQuK z*00lEBC^BgbrVh zaf9xH4!z?L)iTSj1c)A_R#ybL`O8Y!`nx3j+OQQix7+^m`IjM&Lq+nv6f?I|UF4C{ zCMoMXX|!Miu>57Wpn&)>TeA}ze@t`|AYnKfIQRX@Tu)2T&1WN09tzI_x>F6@BJ~%a z#`2EWViZ1I&pn1oS{u=TY_E==OE4$Z!j{)&8~tvlSl335=A0bg*!X*9UheI~5*f%p ztKIK@UpWDLSi;h`LTuX~cr+D!1$OQ{s~oT28$S9GczqHsPr_0KY)Y$7X{LQpri;0n zt7J*pX*e9Clgu2?#>8fg38C}MRdY2ijuUC>zkR@bgMl5@0;5TDwL;SuB(;kcpZe4; zSx=LYLBXs*D$G$nQ@z+lw_m-)!}7O!sgJ9L#^r!RnHpC@lFDP$1$9pU)+me2v(PM$ gt;p1@NZc extends ArrayList { + public ReversibleArrayList(Collection c) { super(c); } + public Iterable reversed() { + return new Iterable() { + public Iterator iterator() { + return new Iterator() { + int current = size() - 1; + public boolean hasNext() { return current > -1; } + public T next() { return get(current--); } + public void remove() { // Not implemented + throw new UnsupportedOperationException(); + } + }; + } + }; + } +} + +public class AdapterMethodIdiom { + public static void main(String[] args) { + ReversibleArrayList ral = + new ReversibleArrayList( + Arrays.asList("To be or not to be".split(" "))); + // Grabs the ordinary iterator via iterator(): + for(String s : ral) { + System.out.print(s + " "); + } + System.out.println(); + // Hand it the Iterable of your choice + for(String s : ral.reversed()) { + System.out.print(s + " "); + } + } +} /* Output: +To be or not to be +be to not or be To +*///:~ diff --git a/src/holding/AddingGroups.java b/src/holding/AddingGroups.java new file mode 100644 index 0000000..fd55b25 --- /dev/null +++ b/src/holding/AddingGroups.java @@ -0,0 +1,21 @@ +package holding;//: holding/AddingGroups.java +// Adding groups of elements to Collection objects. +import java.util.*; + +public class AddingGroups { + public static void main(String[] args) { + Collection collection = + new ArrayList(Arrays.asList(1, 2, 3, 4, 5)); + Integer[] moreInts = { 6, 7, 8, 9, 10 }; + collection.addAll(Arrays.asList(moreInts)); + // Runs significantly faster, but you can't + // construct a Collection this way: + Collections.addAll(collection, 11, 12, 13, 14, 15); + Collections.addAll(collection, moreInts); + // Produces a list "backed by" an array: + List list = Arrays.asList(16, 17, 18, 19, 20); + list.set(1, 99); // OK -- modify an element + // list.add(21); // Runtime error because the + // underlying array cannot be resized. + } +} ///:~ diff --git a/src/holding/ApplesAndOrangesWithGenerics.java b/src/holding/ApplesAndOrangesWithGenerics.java new file mode 100644 index 0000000..475ae59 --- /dev/null +++ b/src/holding/ApplesAndOrangesWithGenerics.java @@ -0,0 +1,27 @@ +package holding;//: holding/ApplesAndOrangesWithGenerics.java +import java.util.*; + +public class ApplesAndOrangesWithGenerics { + public static void main(String[] args) { + ArrayList apples = new ArrayList(); + for(int i = 0; i < 3; i++) { + apples.add(new Apple()); + } + // Compile-time error: + // apples.add(new Orange()); + for(int i = 0; i < apples.size(); i++) { + System.out.println(apples.get(i).id()); + } + // Using foreach: + for(Apple c : apples) { + System.out.println(c.id()); + } + } +} /* Output: +0 +1 +2 +0 +1 +2 +*///:~ diff --git a/src/holding/ApplesAndOrangesWithoutGenerics.java b/src/holding/ApplesAndOrangesWithoutGenerics.java new file mode 100644 index 0000000..567199f --- /dev/null +++ b/src/holding/ApplesAndOrangesWithoutGenerics.java @@ -0,0 +1,28 @@ +package holding;//: holding/ApplesAndOrangesWithoutGenerics.java +// Simple container example (produces compiler warnings). +// {ThrowsException} +import java.util.*; + +class Apple { + private static long counter; + private final long id = counter++; + public long id() { return id; } +} + +class Orange {} + +public class ApplesAndOrangesWithoutGenerics { + @SuppressWarnings("unchecked") + public static void main(String[] args) { + ArrayList apples = new ArrayList(); + for(int i = 0; i < 3; i++) { + apples.add(new Apple()); + } + // Not prevented from adding an Orange to apples: + apples.add(new Orange()); + for(int i = 0; i < apples.size(); i++) { + ((Apple)apples.get(i)).id(); + } + // Orange is detected only at run time + } +} /* (Execute to see output) *///:~ diff --git a/src/holding/ArrayIsNotIterable.java b/src/holding/ArrayIsNotIterable.java new file mode 100644 index 0000000..0a56c76 --- /dev/null +++ b/src/holding/ArrayIsNotIterable.java @@ -0,0 +1,20 @@ +package holding;//: holding/ArrayIsNotIterable.java +import java.util.*; + +public class ArrayIsNotIterable { + static void test(Iterable ib) { + for(T t : ib) { + System.out.print(t + " "); + } + } + public static void main(String[] args) { + test(Arrays.asList(1, 2, 3)); + String[] strings = { "A", "B", "C" }; + // An array works in foreach, but it's not Iterable: + //! test(strings); + // You must explicitly convert it to an Iterable: + test(Arrays.asList(strings)); + } +} /* Output: +1 2 3 A B C +*///:~ diff --git a/src/holding/AsListInference.java b/src/holding/AsListInference.java new file mode 100644 index 0000000..c2ae389 --- /dev/null +++ b/src/holding/AsListInference.java @@ -0,0 +1,33 @@ +package holding;//: holding/AsListInference.java +// Arrays.asList() makes its best guess about type. +import java.util.*; + +class Snow {} +class Powder extends Snow {} +class Light extends Powder {} +class Heavy extends Powder {} +class Crusty extends Snow {} +class Slush extends Snow {} + +public class AsListInference { + public static void main(String[] args) { + List snow1 = Arrays.asList( + new Crusty(), new Slush(), new Powder()); + + // Won't compile: + // List snow2 = Arrays.asList( + // new Light(), new Heavy()); + // Compiler says: + // found : java.util.List + // required: java.util.List + + // Collections.addAll() doesn't get confused: + List snow3 = new ArrayList(); + Collections.addAll(snow3, new Light(), new Heavy()); + + // Give a hint using an + // explicit type argument specification: + List snow4 = Arrays.asList( + new Light(), new Heavy()); + } +} ///:~ diff --git a/src/holding/CollectionSequence.java b/src/holding/CollectionSequence.java new file mode 100644 index 0000000..8561e56 --- /dev/null +++ b/src/holding/CollectionSequence.java @@ -0,0 +1,29 @@ +package holding;//: holding/CollectionSequence.java +import typeinfo.pets.*; +import java.util.*; + +public class CollectionSequence +extends AbstractCollection { + private Pet[] pets = Pets.createArray(8); + public int size() { return pets.length; } + public Iterator iterator() { + return new Iterator() { + private int index = 0; + public boolean hasNext() { + return index < pets.length; + } + public Pet next() { return pets[index++]; } + public void remove() { // Not implemented + throw new UnsupportedOperationException(); + } + }; + } + public static void main(String[] args) { + CollectionSequence c = new CollectionSequence(); + InterfaceVsIterator.display(c); + InterfaceVsIterator.display(c.iterator()); + } +} /* Output: +0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx +0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx +*///:~ diff --git a/src/holding/ContainerMethods.java b/src/holding/ContainerMethods.java new file mode 100644 index 0000000..e2772d9 --- /dev/null +++ b/src/holding/ContainerMethods.java @@ -0,0 +1,38 @@ +package holding;//: holding/ContainerMethods.java +import net.mindview.util.*; + +public class ContainerMethods { + public static void main(String[] args) { + ContainerMethodDifferences.main(args); + } +} /* Output: (Sample) +Collection: [add, addAll, clear, contains, containsAll, equals, hashCode, isEmpty, iterator, remove, removeAll, retainAll, size, toArray] +Interfaces in Collection: [Iterable] +Set extends Collection, adds: [] +Interfaces in Set: [Collection] +HashSet extends Set, adds: [] +Interfaces in HashSet: [Set, Cloneable, Serializable] +LinkedHashSet extends HashSet, adds: [] +Interfaces in LinkedHashSet: [Set, Cloneable, Serializable] +TreeSet extends Set, adds: [pollLast, navigableHeadSet, descendingIterator, lower, headSet, ceiling, pollFirst, subSet, navigableTailSet, comparator, first, floor, last, navigableSubSet, higher, tailSet] +Interfaces in TreeSet: [NavigableSet, Cloneable, Serializable] +List extends Collection, adds: [listIterator, indexOf, get, subList, set, lastIndexOf] +Interfaces in List: [Collection] +ArrayList extends List, adds: [ensureCapacity, trimToSize] +Interfaces in ArrayList: [List, RandomAccess, Cloneable, Serializable] +LinkedList extends List, adds: [pollLast, offer, descendingIterator, addFirst, peekLast, removeFirst, peekFirst, removeLast, getLast, pollFirst, pop, poll, addLast, removeFirstOccurrence, getFirst, element, peek, offerLast, push, offerFirst, removeLastOccurrence] +Interfaces in LinkedList: [List, Deque, Cloneable, Serializable] +Queue extends Collection, adds: [offer, element, peek, poll] +Interfaces in Queue: [Collection] +PriorityQueue extends Queue, adds: [comparator] +Interfaces in PriorityQueue: [Serializable] +Map: [clear, containsKey, containsValue, entrySet, equals, get, hashCode, isEmpty, keySet, put, putAll, remove, size, values] +HashMap extends Map, adds: [] +Interfaces in HashMap: [Map, Cloneable, Serializable] +LinkedHashMap extends HashMap, adds: [] +Interfaces in LinkedHashMap: [Map] +SortedMap extends Map, adds: [subMap, comparator, firstKey, lastKey, headMap, tailMap] +Interfaces in SortedMap: [Map] +TreeMap extends Map, adds: [descendingEntrySet, subMap, pollLastEntry, lastKey, floorEntry, lastEntry, lowerKey, navigableHeadMap, navigableTailMap, descendingKeySet, tailMap, ceilingEntry, higherKey, pollFirstEntry, comparator, firstKey, floorKey, higherEntry, firstEntry, navigableSubMap, headMap, lowerEntry, ceilingKey] +Interfaces in TreeMap: [NavigableMap, Cloneable, Serializable] +*///:~ diff --git a/src/holding/CrossContainerIteration.java b/src/holding/CrossContainerIteration.java new file mode 100644 index 0000000..5daca31 --- /dev/null +++ b/src/holding/CrossContainerIteration.java @@ -0,0 +1,28 @@ +package holding;//: holding/CrossContainerIteration.java +import typeinfo.pets.*; +import java.util.*; + +public class CrossContainerIteration { + public static void display(Iterator it) { + while(it.hasNext()) { + Pet p = it.next(); + System.out.print(p.id() + ":" + p + " "); + } + System.out.println(); + } + public static void main(String[] args) { + ArrayList pets = Pets.arrayList(8); + LinkedList petsLL = new LinkedList(pets); + HashSet petsHS = new HashSet(pets); + TreeSet petsTS = new TreeSet(pets); + display(pets.iterator()); + display(petsLL.iterator()); + display(petsHS.iterator()); + display(petsTS.iterator()); + } +} /* Output: +0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx +0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx +4:Pug 6:Pug 3:Mutt 1:Manx 5:Cymric 7:Manx 2:Cymric 0:Rat +5:Cymric 2:Cymric 7:Manx 1:Manx 3:Mutt 6:Pug 4:Pug 0:Rat +*///:~ diff --git a/src/holding/EnvironmentVariables.java b/src/holding/EnvironmentVariables.java new file mode 100644 index 0000000..b64e2d5 --- /dev/null +++ b/src/holding/EnvironmentVariables.java @@ -0,0 +1,11 @@ +package holding;//: holding/EnvironmentVariables.java +import java.util.*; + +public class EnvironmentVariables { + public static void main(String[] args) { + for(Map.Entry entry: System.getenv().entrySet()) { + System.out.println(entry.getKey() + ": " + + entry.getValue()); + } + } +} /* (Execute to see output) *///:~ diff --git a/src/holding/ForEachCollections.java b/src/holding/ForEachCollections.java new file mode 100644 index 0000000..dfbefba --- /dev/null +++ b/src/holding/ForEachCollections.java @@ -0,0 +1,16 @@ +package holding;//: holding/ForEachCollections.java +// All collections work with foreach. +import java.util.*; + +public class ForEachCollections { + public static void main(String[] args) { + Collection cs = new LinkedList(); + Collections.addAll(cs, + "Take the long way home".split(" ")); + for(String s : cs) { + System.out.print("'" + s + "' "); + } + } +} /* Output: +'Take' 'the' 'long' 'way' 'home' +*///:~ diff --git a/src/holding/GenericsAndUpcasting.java b/src/holding/GenericsAndUpcasting.java new file mode 100644 index 0000000..203a76e --- /dev/null +++ b/src/holding/GenericsAndUpcasting.java @@ -0,0 +1,25 @@ +package holding;//: holding/GenericsAndUpcasting.java +import java.util.*; + +class GrannySmith extends Apple {} +class Gala extends Apple {} +class Fuji extends Apple {} +class Braeburn extends Apple {} + +public class GenericsAndUpcasting { + public static void main(String[] args) { + ArrayList apples = new ArrayList(); + apples.add(new GrannySmith()); + apples.add(new Gala()); + apples.add(new Fuji()); + apples.add(new Braeburn()); + for(Apple c : apples) { + System.out.println(c); + } + } +} /* Output: (Sample) +GrannySmith@7d772e +Gala@11b86e7 +Fuji@35ce36 +Braeburn@757aef +*///:~ diff --git a/src/holding/InterfaceVsIterator.java b/src/holding/InterfaceVsIterator.java new file mode 100644 index 0000000..45cfb58 --- /dev/null +++ b/src/holding/InterfaceVsIterator.java @@ -0,0 +1,47 @@ +package holding;//: holding/InterfaceVsIterator.java +import typeinfo.pets.*; +import java.util.*; + +public class InterfaceVsIterator { + public static void display(Iterator it) { + while(it.hasNext()) { + Pet p = it.next(); + System.out.print(p.id() + ":" + p + " "); + } + System.out.println(); + } + public static void display(Collection pets) { + for(Pet p : pets) { + System.out.print(p.id() + ":" + p + " "); + } + System.out.println(); + } + public static void main(String[] args) { + List petList = Pets.arrayList(8); + Set petSet = new HashSet(petList); + Map petMap = + new LinkedHashMap(); + String[] names = ("Ralph, Eric, Robin, Lacey, " + + "Britney, Sam, Spot, Fluffy").split(", "); + for(int i = 0; i < names.length; i++) { + petMap.put(names[i], petList.get(i)); + } + display(petList); + display(petSet); + display(petList.iterator()); + display(petSet.iterator()); + System.out.println(petMap); + System.out.println(petMap.keySet()); + display(petMap.values()); + display(petMap.values().iterator()); + } +} /* Output: +0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx +4:Pug 6:Pug 3:Mutt 1:Manx 5:Cymric 7:Manx 2:Cymric 0:Rat +0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx +4:Pug 6:Pug 3:Mutt 1:Manx 5:Cymric 7:Manx 2:Cymric 0:Rat +{Ralph=Rat, Eric=Manx, Robin=Cymric, Lacey=Mutt, Britney=Pug, Sam=Cymric, Spot=Pug, Fluffy=Manx} +[Ralph, Eric, Robin, Lacey, Britney, Sam, Spot, Fluffy] +0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx +0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx +*///:~ diff --git a/src/holding/IterableClass.java b/src/holding/IterableClass.java new file mode 100644 index 0000000..950cdbf --- /dev/null +++ b/src/holding/IterableClass.java @@ -0,0 +1,27 @@ +package holding;//: holding/IterableClass.java +// Anything Iterable works with foreach. +import java.util.*; + +public class IterableClass implements Iterable { + protected String[] words = ("And that is how " + + "we know the Earth to be banana-shaped.").split(" "); + public Iterator iterator() { + return new Iterator() { + private int index = 0; + public boolean hasNext() { + return index < words.length; + } + public String next() { return words[index++]; } + public void remove() { // Not implemented + throw new UnsupportedOperationException(); + } + }; + } + public static void main(String[] args) { + for(String s : new IterableClass()) { + System.out.print(s + " "); + } + } +} /* Output: +And that is how we know the Earth to be banana-shaped. +*///:~ diff --git a/src/holding/LinkedListFeatures.java b/src/holding/LinkedListFeatures.java new file mode 100644 index 0000000..3629041 --- /dev/null +++ b/src/holding/LinkedListFeatures.java @@ -0,0 +1,49 @@ +package holding;//: holding/LinkedListFeatures.java +import typeinfo.pets.*; +import java.util.*; +import static net.mindview.util.Print.*; + +/** + * @author 11860 + */ +public class LinkedListFeatures { + public static void main(String[] args) { + LinkedList pets = + new LinkedList(Pets.arrayList(5)); + print(pets); + // Identical: + print("pets.getFirst(): " + pets.getFirst()); + print("pets.element(): " + pets.element()); + // Only differs in empty-list behavior: + print("pets.peek(): " + pets.peek()); + // Identical; remove and return the first element: + print("pets.remove(): " + pets.remove()); + print("pets.removeFirst(): " + pets.removeFirst()); + // Only differs in empty-list behavior: + print("pets.poll(): " + pets.poll()); + print(pets); + pets.addFirst(new Rat()); + print("After addFirst(): " + pets); + pets.offer(Pets.randomPet()); + print("After offer(): " + pets); + pets.add(Pets.randomPet()); + print("After add(): " + pets); + pets.addLast(new Hamster()); + print("After addLast(): " + pets); + print("pets.removeLast(): " + pets.removeLast()); + } +} /* Output: +[Rat, Manx, Cymric, Mutt, Pug] +pets.getFirst(): Rat +pets.element(): Rat +pets.peek(): Rat +pets.remove(): Rat +pets.removeFirst(): Manx +pets.poll(): Cymric +[Mutt, Pug] +After addFirst(): [Rat, Mutt, Pug] +After offer(): [Rat, Mutt, Pug, Cymric] +After add(): [Rat, Mutt, Pug, Cymric, Pug] +After addLast(): [Rat, Mutt, Pug, Cymric, Pug, Hamster] +pets.removeLast(): Hamster +*///:~ diff --git a/src/holding/ListFeatures.java b/src/holding/ListFeatures.java new file mode 100644 index 0000000..41a6427 --- /dev/null +++ b/src/holding/ListFeatures.java @@ -0,0 +1,89 @@ +package holding;//: holding/ListFeatures.java +import typeinfo.pets.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class ListFeatures { + public static void main(String[] args) { + Random rand = new Random(47); + List pets = Pets.arrayList(7); + print("1: " + pets); + Hamster h = new Hamster(); + pets.add(h); // Automatically resizes + print("2: " + pets); + print("3: " + pets.contains(h)); + pets.remove(h); // Remove by object + Pet p = pets.get(2); + print("4: " + p + " " + pets.indexOf(p)); + Pet cymric = new Cymric(); + print("5: " + pets.indexOf(cymric)); + print("6: " + pets.remove(cymric)); + // Must be the exact object: + print("7: " + pets.remove(p)); + print("8: " + pets); + pets.add(3, new Mouse()); // Insert at an index + print("9: " + pets); + List sub = pets.subList(1, 4); + print("subList: " + sub); + print("10: " + pets.containsAll(sub)); + Collections.sort(sub); // In-place sort + print("sorted subList: " + sub); + // Order is not important in containsAll(): + print("11: " + pets.containsAll(sub)); + Collections.shuffle(sub, rand); // Mix it up + print("shuffled subList: " + sub); + print("12: " + pets.containsAll(sub)); + List copy = new ArrayList(pets); + sub = Arrays.asList(pets.get(1), pets.get(4)); + print("sub: " + sub); + copy.retainAll(sub); + print("13: " + copy); + copy = new ArrayList(pets); // Get a fresh copy + copy.remove(2); // Remove by index + print("14: " + copy); + copy.removeAll(sub); // Only removes exact objects + print("15: " + copy); + copy.set(1, new Mouse()); // Replace an element + print("16: " + copy); + copy.addAll(2, sub); // Insert a list in the middle + print("17: " + copy); + print("18: " + pets.isEmpty()); + pets.clear(); // Remove all elements + print("19: " + pets); + print("20: " + pets.isEmpty()); + pets.addAll(Pets.arrayList(4)); + print("21: " + pets); + Object[] o = pets.toArray(); + print("22: " + o[3]); + Pet[] pa = pets.toArray(new Pet[0]); + print("23: " + pa[3].id()); + } +} /* Output: +1: [Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug] +2: [Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Hamster] +3: true +4: Cymric 2 +5: -1 +6: false +7: true +8: [Rat, Manx, Mutt, Pug, Cymric, Pug] +9: [Rat, Manx, Mutt, Mouse, Pug, Cymric, Pug] +subList: [Manx, Mutt, Mouse] +10: true +sorted subList: [Manx, Mouse, Mutt] +11: true +shuffled subList: [Mouse, Manx, Mutt] +12: true +sub: [Mouse, Pug] +13: [Mouse, Pug] +14: [Rat, Mouse, Mutt, Pug, Cymric, Pug] +15: [Rat, Mutt, Cymric, Pug] +16: [Rat, Mouse, Cymric, Pug] +17: [Rat, Mouse, Mouse, Pug, Cymric, Pug] +18: false +19: [] +20: true +21: [Manx, Cymric, Rat, EgyptianMau] +22: EgyptianMau +23: 14 +*///:~ diff --git a/src/holding/ListIteration.java b/src/holding/ListIteration.java new file mode 100644 index 0000000..f2bd7fd --- /dev/null +++ b/src/holding/ListIteration.java @@ -0,0 +1,32 @@ +package holding;//: holding/ListIteration.java +import typeinfo.pets.*; +import java.util.*; + +public class ListIteration { + public static void main(String[] args) { + List pets = Pets.arrayList(8); + ListIterator it = pets.listIterator(); + while(it.hasNext()) { + System.out.print(it.next() + ", " + it.nextIndex() + + ", " + it.previousIndex() + "; "); + } + System.out.println(); + // Backwards: + while(it.hasPrevious()) { + System.out.print(it.previous().id() + " "); + } + System.out.println(); + System.out.println(pets); + it = pets.listIterator(3); + while(it.hasNext()) { + it.next(); + it.set(Pets.randomPet()); + } + System.out.println(pets); + } +} /* Output: +Rat, 1, 0; Manx, 2, 1; Cymric, 3, 2; Mutt, 4, 3; Pug, 5, 4; Cymric, 6, 5; Pug, 7, 6; Manx, 8, 7; +7 6 5 4 3 2 1 0 +[Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx] +[Rat, Manx, Cymric, Cymric, Rat, EgyptianMau, Hamster, EgyptianMau] +*///:~ diff --git a/src/holding/MapOfList.java b/src/holding/MapOfList.java new file mode 100644 index 0000000..970a79c --- /dev/null +++ b/src/holding/MapOfList.java @@ -0,0 +1,55 @@ +//: holding/MapOfList.java +package holding; +import typeinfo.pets.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class MapOfList { + public static Map> + petPeople = new HashMap>(); + static { + petPeople.put(new Person("Dawn"), + Arrays.asList(new Cymric("Molly"),new Mutt("Spot"))); + petPeople.put(new Person("Kate"), + Arrays.asList(new Cat("Shackleton"), + new Cat("Elsie May"), new Dog("Margrett"))); + petPeople.put(new Person("Marilyn"), + Arrays.asList( + new Pug("Louie aka Louis Snorkelstein Dupree"), + new Cat("Stanford aka Stinky el Negro"), + new Cat("Pinkola"))); + petPeople.put(new Person("Luke"), + Arrays.asList(new Rat("Fuzzy"), new Rat("Fizzy"))); + petPeople.put(new Person("Isaac"), + Arrays.asList(new Rat("Freckly"))); + } + public static void main(String[] args) { + print("People: " + petPeople.keySet()); + print("Pets: " + petPeople.values()); + for(Person person : petPeople.keySet()) { + print(person + " has:"); + for(Pet pet : petPeople.get(person)) { + print(" " + pet); + } + } + } +} /* Output: +People: [Person Luke, Person Marilyn, Person Isaac, Person Dawn, Person Kate] +Pets: [[Rat Fuzzy, Rat Fizzy], [Pug Louie aka Louis Snorkelstein Dupree, Cat Stanford aka Stinky el Negro, Cat Pinkola], [Rat Freckly], [Cymric Molly, Mutt Spot], [Cat Shackleton, Cat Elsie May, Dog Margrett]] +Person Luke has: + Rat Fuzzy + Rat Fizzy +Person Marilyn has: + Pug Louie aka Louis Snorkelstein Dupree + Cat Stanford aka Stinky el Negro + Cat Pinkola +Person Isaac has: + Rat Freckly +Person Dawn has: + Cymric Molly + Mutt Spot +Person Kate has: + Cat Shackleton + Cat Elsie May + Dog Margrett +*///:~ diff --git a/src/holding/ModifyingArraysAsList.java b/src/holding/ModifyingArraysAsList.java new file mode 100644 index 0000000..b2bcfa4 --- /dev/null +++ b/src/holding/ModifyingArraysAsList.java @@ -0,0 +1,28 @@ +package holding;//: holding/ModifyingArraysAsList.java +import java.util.*; + +public class ModifyingArraysAsList { + public static void main(String[] args) { + Random rand = new Random(47); + Integer[] ia = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + List list1 = + new ArrayList(Arrays.asList(ia)); + System.out.println("Before shuffling: " + list1); + Collections.shuffle(list1, rand); + System.out.println("After shuffling: " + list1); + System.out.println("array: " + Arrays.toString(ia)); + + List list2 = Arrays.asList(ia); + System.out.println("Before shuffling: " + list2); + Collections.shuffle(list2, rand); + System.out.println("After shuffling: " + list2); + System.out.println("array: " + Arrays.toString(ia)); + } +} /* Output: +Before shuffling: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +After shuffling: [4, 6, 3, 1, 8, 7, 2, 5, 10, 9] +array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +Before shuffling: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +After shuffling: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8] +array: [9, 1, 6, 3, 7, 2, 5, 10, 4, 8] +*///:~ diff --git a/src/holding/MultiIterableClass.java b/src/holding/MultiIterableClass.java new file mode 100644 index 0000000..0733208 --- /dev/null +++ b/src/holding/MultiIterableClass.java @@ -0,0 +1,48 @@ +package holding;//: holding/MultiIterableClass.java +// Adding several Adapter Methods. +import java.util.*; + +public class MultiIterableClass extends IterableClass { + public Iterable reversed() { + return new Iterable() { + public Iterator iterator() { + return new Iterator() { + int current = words.length - 1; + public boolean hasNext() { return current > -1; } + public String next() { return words[current--]; } + public void remove() { // Not implemented + throw new UnsupportedOperationException(); + } + }; + } + }; + } + public Iterable randomized() { + return new Iterable() { + public Iterator iterator() { + List shuffled = + new ArrayList(Arrays.asList(words)); + Collections.shuffle(shuffled, new Random(47)); + return shuffled.iterator(); + } + }; + } + public static void main(String[] args) { + MultiIterableClass mic = new MultiIterableClass(); + for(String s : mic.reversed()) { + System.out.print(s + " "); + } + System.out.println(); + for(String s : mic.randomized()) { + System.out.print(s + " "); + } + System.out.println(); + for(String s : mic) { + System.out.print(s + " "); + } + } +} /* Output: +banana-shaped. be to Earth the know we how is that And +is banana-shaped. Earth that how the be And we know to +And that is how we know the Earth to be banana-shaped. +*///:~ diff --git a/src/holding/NonCollectionSequence.java b/src/holding/NonCollectionSequence.java new file mode 100644 index 0000000..95db6ff --- /dev/null +++ b/src/holding/NonCollectionSequence.java @@ -0,0 +1,28 @@ +package holding;//: holding/NonCollectionSequence.java +import typeinfo.pets.*; +import java.util.*; + +class PetSequence { + protected Pet[] pets = Pets.createArray(8); +} + +public class NonCollectionSequence extends PetSequence { + public Iterator iterator() { + return new Iterator() { + private int index = 0; + public boolean hasNext() { + return index < pets.length; + } + public Pet next() { return pets[index++]; } + public void remove() { // Not implemented + throw new UnsupportedOperationException(); + } + }; + } + public static void main(String[] args) { + NonCollectionSequence nc = new NonCollectionSequence(); + InterfaceVsIterator.display(nc.iterator()); + } +} /* Output: +0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx +*///:~ diff --git a/src/holding/PetMap.java b/src/holding/PetMap.java new file mode 100644 index 0000000..e76ef15 --- /dev/null +++ b/src/holding/PetMap.java @@ -0,0 +1,23 @@ +package holding;//: holding/PetMap.java +import typeinfo.pets.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class PetMap { + public static void main(String[] args) { + Map petMap = new HashMap(); + petMap.put("My Cat", new Cat("Molly")); + petMap.put("My Dog", new Dog("Ginger")); + petMap.put("My Hamster", new Hamster("Bosco")); + print(petMap); + Pet dog = petMap.get("My Dog"); + print(dog); + print(petMap.containsKey("My Dog")); + print(petMap.containsValue(dog)); + } +} /* Output: +{My Cat=Cat Molly, My Hamster=Hamster Bosco, My Dog=Dog Ginger} +Dog Ginger +true +true +*///:~ diff --git a/src/holding/PrintingContainers.java b/src/holding/PrintingContainers.java new file mode 100644 index 0000000..4576212 --- /dev/null +++ b/src/holding/PrintingContainers.java @@ -0,0 +1,40 @@ +package holding;//: holding/PrintingContainers.java +// Containers print themselves automatically. +import java.util.*; +import static net.mindview.util.Print.*; + +public class PrintingContainers { + static Collection fill(Collection collection) { + collection.add("rat"); + collection.add("cat"); + collection.add("dog"); + collection.add("dog"); + return collection; + } + static Map fill(Map map) { + map.put("rat", "Fuzzy"); + map.put("cat", "Rags"); + map.put("dog", "Bosco"); + map.put("dog", "Spot"); + return map; + } + public static void main(String[] args) { + print(fill(new ArrayList())); + print(fill(new LinkedList())); + print(fill(new HashSet())); + print(fill(new TreeSet())); + print(fill(new LinkedHashSet())); + print(fill(new HashMap())); + print(fill(new TreeMap())); + print(fill(new LinkedHashMap())); + } +} /* Output: +[rat, cat, dog, dog] +[rat, cat, dog, dog] +[dog, cat, rat] +[cat, dog, rat] +[rat, cat, dog] +{dog=Spot, cat=Rags, rat=Fuzzy} +{cat=Rags, dog=Spot, rat=Fuzzy} +{rat=Fuzzy, cat=Rags, dog=Spot} +*///:~ diff --git a/src/holding/PriorityQueueDemo.java b/src/holding/PriorityQueueDemo.java new file mode 100644 index 0000000..213baa9 --- /dev/null +++ b/src/holding/PriorityQueueDemo.java @@ -0,0 +1,48 @@ +package holding;//: holding/PriorityQueueDemo.java +import java.util.*; + +public class PriorityQueueDemo { + public static void main(String[] args) { + PriorityQueue priorityQueue = + new PriorityQueue(); + Random rand = new Random(47); + for(int i = 0; i < 10; i++) { + priorityQueue.offer(rand.nextInt(i + 10)); + } + QueueDemo.printQ(priorityQueue); + + List ints = Arrays.asList(25, 22, 20, + 18, 14, 9, 3, 1, 1, 2, 3, 9, 14, 18, 21, 23, 25); + priorityQueue = new PriorityQueue(ints); + QueueDemo.printQ(priorityQueue); + priorityQueue = new PriorityQueue( + ints.size(), Collections.reverseOrder()); + priorityQueue.addAll(ints); + QueueDemo.printQ(priorityQueue); + + String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION"; + List strings = Arrays.asList(fact.split("")); + PriorityQueue stringPQ = + new PriorityQueue(strings); + QueueDemo.printQ(stringPQ); + stringPQ = new PriorityQueue( + strings.size(), Collections.reverseOrder()); + stringPQ.addAll(strings); + QueueDemo.printQ(stringPQ); + + Set charSet = new HashSet(); + for(char c : fact.toCharArray()) { + charSet.add(c); // Autoboxing + } + PriorityQueue characterPQ = + new PriorityQueue(charSet); + QueueDemo.printQ(characterPQ); + } +} /* Output: +0 1 1 1 1 1 3 5 8 14 +1 1 2 3 3 9 9 14 14 18 18 20 21 22 23 25 25 +25 25 23 22 21 20 18 18 14 14 9 9 3 3 2 1 1 + A A B C C C D D E E E F H H I I L N N O O O O S S S T T U U U W +W U U U T T S S S O O O O N N L I I H H F E E E D D C C C B A A + A B C D E F H I L N O S T U W +*///:~ diff --git a/src/holding/QueueDemo.java b/src/holding/QueueDemo.java new file mode 100644 index 0000000..a788c9f --- /dev/null +++ b/src/holding/QueueDemo.java @@ -0,0 +1,28 @@ +package holding;//: holding/QueueDemo.java +// Upcasting to a Queue from a LinkedList. +import java.util.*; + +public class QueueDemo { + public static void printQ(Queue queue) { + while(queue.peek() != null) { + System.out.print(queue.remove() + " "); + } + System.out.println(); + } + public static void main(String[] args) { + Queue queue = new LinkedList(); + Random rand = new Random(47); + for(int i = 0; i < 10; i++) { + queue.offer(rand.nextInt(i + 10)); + } + printQ(queue); + Queue qc = new LinkedList(); + for(char c : "Brontosaurus".toCharArray()) { + qc.offer(c); + } + printQ(qc); + } +} /* Output: +8 1 1 1 5 14 3 1 0 1 +B r o n t o s a u r u s +*///:~ diff --git a/src/holding/SetOfInteger.java b/src/holding/SetOfInteger.java new file mode 100644 index 0000000..12e3867 --- /dev/null +++ b/src/holding/SetOfInteger.java @@ -0,0 +1,15 @@ +package holding;//: holding/SetOfInteger.java +import java.util.*; + +public class SetOfInteger { + public static void main(String[] args) { + Random rand = new Random(47); + Set intset = new HashSet(); + for(int i = 0; i < 10000; i++) { + intset.add(rand.nextInt(30)); + } + System.out.println(intset); + } +} /* Output: +[15, 8, 23, 16, 7, 22, 9, 21, 6, 1, 29, 14, 24, 4, 19, 26, 11, 18, 3, 12, 27, 17, 2, 13, 28, 20, 25, 10, 5, 0] +*///:~ diff --git a/src/holding/SetOperations.java b/src/holding/SetOperations.java new file mode 100644 index 0000000..808fde5 --- /dev/null +++ b/src/holding/SetOperations.java @@ -0,0 +1,32 @@ +package holding;//: holding/SetOperations.java +import java.util.*; +import static net.mindview.util.Print.*; + +public class SetOperations { + public static void main(String[] args) { + Set set1 = new HashSet(); + Collections.addAll(set1, + "A B C D E F G H I J K L".split(" ")); + set1.add("M"); + print("H: " + set1.contains("H")); + print("N: " + set1.contains("N")); + Set set2 = new HashSet(); + Collections.addAll(set2, "H I J K L".split(" ")); + print("set2 in set1: " + set1.containsAll(set2)); + set1.remove("H"); + print("set1: " + set1); + print("set2 in set1: " + set1.containsAll(set2)); + set1.removeAll(set2); + print("set2 removed from set1: " + set1); + Collections.addAll(set1, "X Y Z".split(" ")); + print("'X Y Z' added to set1: " + set1); + } +} /* Output: +H: true +N: false +set2 in set1: true +set1: [D, K, C, B, L, G, I, M, A, F, J, E] +set2 in set1: false +set2 removed from set1: [D, C, B, G, M, A, F, E] +'X Y Z' added to set1: [Z, D, C, B, G, M, A, F, Y, X, E] +*///:~ diff --git a/src/holding/SimpleCollection.java b/src/holding/SimpleCollection.java new file mode 100644 index 0000000..47fe374 --- /dev/null +++ b/src/holding/SimpleCollection.java @@ -0,0 +1,16 @@ +package holding;//: holding/SimpleCollection.java +import java.util.*; + +public class SimpleCollection { + public static void main(String[] args) { + Collection c = new ArrayList(); + for(int i = 0; i < 10; i++) { + c.add(i); // Autoboxing + } + for(Integer i : c) { + System.out.print(i + ", "); + } + } +} /* Output: +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +*///:~ diff --git a/src/holding/SimpleIteration.java b/src/holding/SimpleIteration.java new file mode 100644 index 0000000..e643c5a --- /dev/null +++ b/src/holding/SimpleIteration.java @@ -0,0 +1,31 @@ +package holding;//: holding/SimpleIteration.java +import typeinfo.pets.*; +import java.util.*; + +public class SimpleIteration { + public static void main(String[] args) { + List pets = Pets.arrayList(12); + Iterator it = pets.iterator(); + while(it.hasNext()) { + Pet p = it.next(); + System.out.print(p.id() + ":" + p + " "); + } + System.out.println(); + // A simpler approach, when possible: + for(Pet p : pets) { + System.out.print(p.id() + ":" + p + " "); + } + System.out.println(); + // An Iterator can also remove elements: + it = pets.iterator(); + for(int i = 0; i < 6; i++) { + it.next(); + it.remove(); + } + System.out.println(pets); + } +} /* Output: +0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 8:Cymric 9:Rat 10:EgyptianMau 11:Hamster +0:Rat 1:Manx 2:Cymric 3:Mutt 4:Pug 5:Cymric 6:Pug 7:Manx 8:Cymric 9:Rat 10:EgyptianMau 11:Hamster +[Pug, Manx, Cymric, Rat, EgyptianMau, Hamster] +*///:~ diff --git a/src/holding/SortedSetOfInteger.java b/src/holding/SortedSetOfInteger.java new file mode 100644 index 0000000..c9d1ceb --- /dev/null +++ b/src/holding/SortedSetOfInteger.java @@ -0,0 +1,15 @@ +package holding;//: holding/SortedSetOfInteger.java +import java.util.*; + +public class SortedSetOfInteger { + public static void main(String[] args) { + Random rand = new Random(47); + SortedSet intset = new TreeSet(); + for(int i = 0; i < 10000; i++) { + intset.add(rand.nextInt(30)); + } + System.out.println(intset); + } +} /* Output: +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] +*///:~ diff --git a/src/holding/StackCollision.java b/src/holding/StackCollision.java new file mode 100644 index 0000000..d2c0547 --- /dev/null +++ b/src/holding/StackCollision.java @@ -0,0 +1,27 @@ +package holding;//: holding/StackCollision.java +import net.mindview.util.*; + +public class StackCollision { + public static void main(String[] args) { + net.mindview.util.Stack stack = + new net.mindview.util.Stack(); + for(String s : "My dog has fleas".split(" ")) { + stack.push(s); + } + while(!stack.empty()) { + System.out.print(stack.pop() + " "); + } + System.out.println(); + java.util.Stack stack2 = + new java.util.Stack(); + for(String s : "My dog has fleas".split(" ")) { + stack2.push(s); + } + while(!stack2.empty()) { + System.out.print(stack2.pop() + " "); + } + } +} /* Output: +fleas has dog My +fleas has dog My +*///:~ diff --git a/src/holding/StackTest.java b/src/holding/StackTest.java new file mode 100644 index 0000000..162d995 --- /dev/null +++ b/src/holding/StackTest.java @@ -0,0 +1,16 @@ +package holding;//: holding/StackTest.java +import net.mindview.util.*; + +public class StackTest { + public static void main(String[] args) { + Stack stack = new Stack(); + for(String s : "My dog has fleas".split(" ")) { + stack.push(s); + } + while(!stack.empty()) { + System.out.print(stack.pop() + " "); + } + } +} /* Output: +fleas has dog My +*///:~ diff --git a/src/holding/Statistics.java b/src/holding/Statistics.java new file mode 100644 index 0000000..409392f --- /dev/null +++ b/src/holding/Statistics.java @@ -0,0 +1,20 @@ +package holding;//: holding/Statistics.java +// Simple demonstration of HashMap. +import java.util.*; + +public class Statistics { + public static void main(String[] args) { + Random rand = new Random(47); + Map m = + new HashMap(); + for(int i = 0; i < 10000; i++) { + // Produce a number between 0 and 20: + int r = rand.nextInt(20); + Integer freq = m.get(r); + m.put(r, freq == null ? 1 : freq + 1); + } + System.out.println(m); + } +} /* Output: +{15=497, 4=481, 19=464, 8=468, 11=531, 16=533, 18=478, 3=508, 7=471, 12=521, 17=509, 2=489, 13=506, 9=549, 6=519, 1=502, 14=477, 10=513, 5=503, 0=481} +*///:~ diff --git a/src/holding/UniqueWords.java b/src/holding/UniqueWords.java new file mode 100644 index 0000000..7e67958 --- /dev/null +++ b/src/holding/UniqueWords.java @@ -0,0 +1,13 @@ +package holding;//: holding/UniqueWords.java +import java.util.*; +import net.mindview.util.*; + +public class UniqueWords { + public static void main(String[] args) { + Set words = new TreeSet( + new TextFile("SetOperations.java", "\\W+")); + System.out.println(words); + } +} /* Output: +[A, B, C, Collections, D, E, F, G, H, HashSet, I, J, K, L, M, N, Output, Print, Set, SetOperations, String, X, Y, Z, add, addAll, added, args, class, contains, containsAll, false, from, holding, import, in, java, main, mindview, net, new, print, public, remove, removeAll, removed, set1, set2, split, static, to, true, util, void] +*///:~ diff --git a/src/holding/UniqueWordsAlphabetic.java b/src/holding/UniqueWordsAlphabetic.java new file mode 100644 index 0000000..38c62d6 --- /dev/null +++ b/src/holding/UniqueWordsAlphabetic.java @@ -0,0 +1,16 @@ +package holding;//: holding/UniqueWordsAlphabetic.java +// Producing an alphabetic listing. +import java.util.*; +import net.mindview.util.*; + +public class UniqueWordsAlphabetic { + public static void main(String[] args) { + Set words = + new TreeSet(String.CASE_INSENSITIVE_ORDER); + words.addAll( + new TextFile("SetOperations.java", "\\W+")); + System.out.println(words); + } +} /* Output: +[A, add, addAll, added, args, B, C, class, Collections, contains, containsAll, D, E, F, false, from, G, H, HashSet, holding, I, import, in, J, java, K, L, M, main, mindview, N, net, new, Output, Print, public, remove, removeAll, removed, Set, set1, set2, SetOperations, split, static, String, to, true, util, void, X, Y, Z] +*///:~ diff --git a/src/holding/build.xml b/src/holding/build.xml new file mode 100644 index 0000000..263cd74 --- /dev/null +++ b/src/holding/build.xml @@ -0,0 +1,430 @@ + + + + + + build.xml for the source code for the holding chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/initialization/Apricot.java b/src/initialization/Apricot.java new file mode 100644 index 0000000..40de192 --- /dev/null +++ b/src/initialization/Apricot.java @@ -0,0 +1,7 @@ +package initialization; + +//: initialization/Apricot.java +public class Apricot { + void pick() { /* ... */ } + void pit() { pick(); /* ... */ } +} ///:~ diff --git a/src/initialization/ArrayClassObj.java b/src/initialization/ArrayClassObj.java new file mode 100644 index 0000000..f50d8dc --- /dev/null +++ b/src/initialization/ArrayClassObj.java @@ -0,0 +1,19 @@ +package initialization;//: initialization/ArrayClassObj.java +// Creating an array of nonprimitive objects. +import java.util.*; +import static net.mindview.util.Print.*; + +public class ArrayClassObj { + public static void main(String[] args) { + Random rand = new Random(47); + Integer[] a = new Integer[rand.nextInt(20)]; + print("length of a = " + a.length); + for(int i = 0; i < a.length; i++) { + a[i] = rand.nextInt(500); // Autoboxing + } + print(Arrays.toString(a)); + } +} /* Output: (Sample) +length of a = 18 +[55, 193, 361, 461, 429, 368, 200, 22, 207, 288, 128, 51, 89, 309, 278, 498, 361, 20] +*///:~ diff --git a/src/initialization/ArrayInit.java b/src/initialization/ArrayInit.java new file mode 100644 index 0000000..aaa8450 --- /dev/null +++ b/src/initialization/ArrayInit.java @@ -0,0 +1,23 @@ +package initialization;//: initialization/ArrayInit.java +// Array initialization. +import java.util.*; + +public class ArrayInit { + public static void main(String[] args) { + Integer[] a = { + new Integer(1), + new Integer(2), + 3, // Autoboxing + }; + Integer[] b = new Integer[]{ + new Integer(1), + new Integer(2), + 3, // Autoboxing + }; + System.out.println(Arrays.toString(a)); + System.out.println(Arrays.toString(b)); + } +} /* Output: +[1, 2, 3] +[1, 2, 3] +*///:~ diff --git a/src/initialization/ArrayNew.java b/src/initialization/ArrayNew.java new file mode 100644 index 0000000..0b10437 --- /dev/null +++ b/src/initialization/ArrayNew.java @@ -0,0 +1,17 @@ +package initialization;//: initialization/ArrayNew.java +// Creating arrays with new. +import java.util.*; +import static net.mindview.util.Print.*; + +public class ArrayNew { + public static void main(String[] args) { + int[] a; + Random rand = new Random(47); + a = new int[rand.nextInt(20)]; + print("length of a = " + a.length); + print(Arrays.toString(a)); + } +} /* Output: +length of a = 18 +[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +*///:~ diff --git a/src/initialization/ArraysOfPrimitives.java b/src/initialization/ArraysOfPrimitives.java new file mode 100644 index 0000000..acfc163 --- /dev/null +++ b/src/initialization/ArraysOfPrimitives.java @@ -0,0 +1,22 @@ +package initialization;//: initialization/ArraysOfPrimitives.java +import static net.mindview.util.Print.*; + +public class ArraysOfPrimitives { + public static void main(String[] args) { + int[] a1 = { 1, 2, 3, 4, 5 }; + int[] a2; + a2 = a1; + for(int i = 0; i < a2.length; i++) { + a2[i] = a2[i] + 1; + } + for(int i = 0; i < a1.length; i++) { + print("a1[" + i + "] = " + a1[i]); + } + } +} /* Output: +a1[0] = 2 +a1[1] = 3 +a1[2] = 4 +a1[3] = 5 +a1[4] = 6 +*///:~ diff --git a/src/initialization/AutoboxingVarargs.java b/src/initialization/AutoboxingVarargs.java new file mode 100644 index 0000000..e74cc45 --- /dev/null +++ b/src/initialization/AutoboxingVarargs.java @@ -0,0 +1,19 @@ +package initialization;//: initialization/AutoboxingVarargs.java + +public class AutoboxingVarargs { + public static void f(Integer... args) { + for(Integer i : args) { + System.out.print(i + " "); + } + System.out.println(); + } + public static void main(String[] args) { + f(new Integer(1), new Integer(2)); + f(4, 5, 6, 7, 8, 9); + f(10, new Integer(11), 12); + } +} /* Output: +1 2 +4 5 6 7 8 9 +10 11 12 +*///:~ diff --git a/src/initialization/BananaPeel.java b/src/initialization/BananaPeel.java new file mode 100644 index 0000000..13f4171 --- /dev/null +++ b/src/initialization/BananaPeel.java @@ -0,0 +1,12 @@ +package initialization;//: initialization/BananaPeel.java + +class Banana { void peel(int i) { /* ... */ } } + +public class BananaPeel { + public static void main(String[] args) { + Banana a = new Banana(), + b = new Banana(); + a.peel(1); + b.peel(2); + } +} ///:~ diff --git a/src/initialization/Burrito.java b/src/initialization/Burrito.java new file mode 100644 index 0000000..e28371d --- /dev/null +++ b/src/initialization/Burrito.java @@ -0,0 +1,32 @@ +package initialization;//: initialization/Burrito.java + +public class Burrito { + Spiciness degree; + public Burrito(Spiciness degree) { this.degree = degree;} + public void describe() { + System.out.print("This burrito is "); + switch(degree) { + case NOT: System.out.println("not spicy at all."); + break; + case MILD: + case MEDIUM: System.out.println("a little hot."); + break; + case HOT: + case FLAMING: + default: System.out.println("maybe too hot."); + } + } + public static void main(String[] args) { + Burrito + plain = new Burrito(Spiciness.NOT), + greenChile = new Burrito(Spiciness.MEDIUM), + jalapeno = new Burrito(Spiciness.HOT); + plain.describe(); + greenChile.describe(); + jalapeno.describe(); + } +} /* Output: +This burrito is not spicy at all. +This burrito is a little hot. +This burrito is maybe too hot. +*///:~ diff --git a/src/initialization/Counter.java b/src/initialization/Counter.java new file mode 100644 index 0000000..f6a780d --- /dev/null +++ b/src/initialization/Counter.java @@ -0,0 +1,8 @@ +package initialization; + +//: initialization/Counter.java +public class Counter { + int i; + Counter() { i = 7; } + // ... +} ///:~ diff --git a/src/initialization/DefaultConstructor.java b/src/initialization/DefaultConstructor.java new file mode 100644 index 0000000..fdef5e4 --- /dev/null +++ b/src/initialization/DefaultConstructor.java @@ -0,0 +1,9 @@ +package initialization;//: initialization/DefaultConstructor.java + +class Bird {} + +public class DefaultConstructor { + public static void main(String[] args) { + Bird b = new Bird(); // Default! + } +} ///:~ diff --git a/src/initialization/Demotion.java b/src/initialization/Demotion.java new file mode 100644 index 0000000..eaaa4c3 --- /dev/null +++ b/src/initialization/Demotion.java @@ -0,0 +1,60 @@ +package initialization;//: initialization/Demotion.java +// Demotion of primitives and overloading. +import static net.mindview.util.Print.*; + +public class Demotion { + void f1(char x) { print("f1(char)"); } + void f1(byte x) { print("f1(byte)"); } + void f1(short x) { print("f1(short)"); } + void f1(int x) { print("f1(int)"); } + void f1(long x) { print("f1(long)"); } + void f1(float x) { print("f1(float)"); } + void f1(double x) { print("f1(double)"); } + + void f2(char x) { print("f2(char)"); } + void f2(byte x) { print("f2(byte)"); } + void f2(short x) { print("f2(short)"); } + void f2(int x) { print("f2(int)"); } + void f2(long x) { print("f2(long)"); } + void f2(float x) { print("f2(float)"); } + + void f3(char x) { print("f3(char)"); } + void f3(byte x) { print("f3(byte)"); } + void f3(short x) { print("f3(short)"); } + void f3(int x) { print("f3(int)"); } + void f3(long x) { print("f3(long)"); } + + void f4(char x) { print("f4(char)"); } + void f4(byte x) { print("f4(byte)"); } + void f4(short x) { print("f4(short)"); } + void f4(int x) { print("f4(int)"); } + + void f5(char x) { print("f5(char)"); } + void f5(byte x) { print("f5(byte)"); } + void f5(short x) { print("f5(short)"); } + + void f6(char x) { print("f6(char)"); } + void f6(byte x) { print("f6(byte)"); } + + void f7(char x) { print("f7(char)"); } + + void testDouble() { + double x = 0; + print("double argument:"); + f1(x);f2((float)x);f3((long)x);f4((int)x); + f5((short)x);f6((byte)x);f7((char)x); + } + public static void main(String[] args) { + Demotion p = new Demotion(); + p.testDouble(); + } +} /* Output: +double argument: +f1(double) +f2(float) +f3(long) +f4(int) +f5(short) +f6(byte) +f7(char) +*///:~ diff --git a/src/initialization/DynamicArray.java b/src/initialization/DynamicArray.java new file mode 100644 index 0000000..e20a445 --- /dev/null +++ b/src/initialization/DynamicArray.java @@ -0,0 +1,18 @@ +package initialization;//: initialization/DynamicArray.java +// Array initialization. + +public class DynamicArray { + public static void main(String[] args) { + Other.main(new String[]{ "fiddle", "de", "dum" }); + } +} + +class Other { + public static void main(String[] args) { + for(String s : args) { + System.out.print(s + " "); + } + } +} /* Output: +fiddle de dum +*///:~ diff --git a/src/initialization/EnumOrder.java b/src/initialization/EnumOrder.java new file mode 100644 index 0000000..7e2bf42 --- /dev/null +++ b/src/initialization/EnumOrder.java @@ -0,0 +1,15 @@ +package initialization;//: initialization/EnumOrder.java + +public class EnumOrder { + public static void main(String[] args) { + for(Spiciness s : Spiciness.values()) { + System.out.println(s + ", ordinal " + s.ordinal()); + } + } +} /* Output: +NOT, ordinal 0 +MILD, ordinal 1 +MEDIUM, ordinal 2 +HOT, ordinal 3 +FLAMING, ordinal 4 +*///:~ diff --git a/src/initialization/ExplicitStatic.java b/src/initialization/ExplicitStatic.java new file mode 100644 index 0000000..5025796 --- /dev/null +++ b/src/initialization/ExplicitStatic.java @@ -0,0 +1,38 @@ +package initialization;//: initialization/ExplicitStatic.java +// Explicit static initialization with the "static" clause. +import static net.mindview.util.Print.*; + +class Cup { + Cup(int marker) { + print("Cup(" + marker + ")"); + } + void f(int marker) { + print("f(" + marker + ")"); + } +} + +class Cups { + static Cup cup1; + static Cup cup2; + static { + cup1 = new Cup(1); + cup2 = new Cup(2); + } + Cups() { + print("Cups()"); + } +} + +public class ExplicitStatic { + public static void main(String[] args) { + print("Inside main()"); + Cups.cup1.f(99); // (1) + } + // static Cups cups1 = new Cups(); // (2) + // static Cups cups2 = new Cups(); // (2) +} /* Output: +Inside main() +Cup(1) +Cup(2) +f(99) +*///:~ diff --git a/src/initialization/Flower.java b/src/initialization/Flower.java new file mode 100644 index 0000000..daad073 --- /dev/null +++ b/src/initialization/Flower.java @@ -0,0 +1,40 @@ +package initialization;//: initialization/Flower.java +// Calling constructors with "this" +import static net.mindview.util.Print.*; + +public class Flower { + int petalCount = 0; + String s = "initial value"; + Flower(int petals) { + petalCount = petals; + print("Constructor w/ int arg only, petalCount= " + + petalCount); + } + Flower(String ss) { + print("Constructor w/ String arg only, s = " + ss); + s = ss; + } + Flower(String s, int petals) { + this(petals); +//! this(s); // Can't call two! + this.s = s; // Another use of "this" + print("String & int args"); + } + Flower() { + this("hi", 47); + print("default constructor (no args)"); + } + void printPetalCount() { +//! this(11); // Not inside non-constructor! + print("petalCount = " + petalCount + " s = "+ s); + } + public static void main(String[] args) { + Flower x = new Flower(); + x.printPetalCount(); + } +} /* Output: +Constructor w/ int arg only, petalCount= 47 +String & int args +default constructor (no args) +petalCount = 47 s = hi +*///:~ diff --git a/src/initialization/InitialValues.java b/src/initialization/InitialValues.java new file mode 100644 index 0000000..0e88364 --- /dev/null +++ b/src/initialization/InitialValues.java @@ -0,0 +1,45 @@ +package initialization;//: initialization/InitialValues.java +// Shows default initial values. +import static net.mindview.util.Print.*; + +public class InitialValues { + boolean t; + char c; + byte b; + short s; + int i; + long l; + float f; + double d; + InitialValues reference; + void printInitialValues() { + print("Data type Initial value"); + print("boolean " + t); + print("char [" + c + "]"); + print("byte " + b); + print("short " + s); + print("int " + i); + print("long " + l); + print("float " + f); + print("double " + d); + print("reference " + reference); + } + public static void main(String[] args) { + InitialValues iv = new InitialValues(); + iv.printInitialValues(); + /* You could also say: + new InitialValues().printInitialValues(); + */ + } +} /* Output: +Data type Initial value +boolean false +char [ ] +byte 0 +short 0 +int 0 +long 0 +float 0.0 +double 0.0 +reference null +*///:~ diff --git a/src/initialization/InitialValues2.java b/src/initialization/InitialValues2.java new file mode 100644 index 0000000..9365a0b --- /dev/null +++ b/src/initialization/InitialValues2.java @@ -0,0 +1,13 @@ +package initialization;//: initialization/InitialValues2.java +// Providing explicit initial values. + +public class InitialValues2 { + boolean bool = true; + char ch = 'x'; + byte b = 47; + short s = 0xff; + int i = 999; + long lng = 1; + float f = 3.14f; + double d = 3.14159; +} ///:~ diff --git a/src/initialization/Leaf.java b/src/initialization/Leaf.java new file mode 100644 index 0000000..4987136 --- /dev/null +++ b/src/initialization/Leaf.java @@ -0,0 +1,19 @@ +package initialization;//: initialization/Leaf.java +// Simple use of the "this" keyword. + +public class Leaf { + int i = 0; + Leaf increment() { + i++; + return this; + } + void print() { + System.out.println("i = " + i); + } + public static void main(String[] args) { + Leaf x = new Leaf(); + x.increment().increment().increment().print(); + } +} /* Output: +i = 3 +*///:~ diff --git a/src/initialization/Measurement.java b/src/initialization/Measurement.java new file mode 100644 index 0000000..1f65046 --- /dev/null +++ b/src/initialization/Measurement.java @@ -0,0 +1,9 @@ +package initialization; + +//: initialization/Measurement.java +class Depth {} + +public class Measurement { + Depth d = new Depth(); + // ... +} ///:~ diff --git a/src/initialization/MethodInit.java b/src/initialization/MethodInit.java new file mode 100644 index 0000000..ad22969 --- /dev/null +++ b/src/initialization/MethodInit.java @@ -0,0 +1,7 @@ +package initialization; + +//: initialization/MethodInit.java +public class MethodInit { + int i = f(); + int f() { return 11; } +} ///:~ diff --git a/src/initialization/MethodInit2.java b/src/initialization/MethodInit2.java new file mode 100644 index 0000000..2df0bbe --- /dev/null +++ b/src/initialization/MethodInit2.java @@ -0,0 +1,9 @@ +package initialization; + +//: initialization/MethodInit2.java +public class MethodInit2 { + int i = f(); + int j = g(i); + int f() { return 11; } + int g(int n) { return n * 10; } +} ///:~ diff --git a/src/initialization/MethodInit3.java b/src/initialization/MethodInit3.java new file mode 100644 index 0000000..74f655d --- /dev/null +++ b/src/initialization/MethodInit3.java @@ -0,0 +1,9 @@ +package initialization; + +//: initialization/MethodInit3.java +public class MethodInit3 { + //! int j = g(i); // Illegal forward reference + int i = f(); + int f() { return 11; } + int g(int n) { return n * 10; } +} ///:~ diff --git a/src/initialization/Mugs.java b/src/initialization/Mugs.java new file mode 100644 index 0000000..58daec7 --- /dev/null +++ b/src/initialization/Mugs.java @@ -0,0 +1,47 @@ +package initialization;//: initialization/Mugs.java +// Java "Instance Initialization." +import static net.mindview.util.Print.*; + +class Mug { + Mug(int marker) { + print("Mug(" + marker + ")"); + } + void f(int marker) { + print("f(" + marker + ")"); + } +} + +public class Mugs { + Mug mug1; + Mug mug2; + { + mug1 = new Mug(1); + mug2 = new Mug(2); + print("mug1 & mug2 initialized"); + } + Mugs() { + print("Mugs()"); + } + Mugs(int i) { + print("Mugs(int)"); + } + public static void main(String[] args) { + print("Inside main()"); + new Mugs(); + print("new Mugs() completed"); + new Mugs(1); + print("new Mugs(1) completed"); + } +} /* Output: +Inside main() +Mug(1) +Mug(2) +mug1 & mug2 initialized +Mugs() +new Mugs() completed +Mug(1) +Mug(2) +mug1 & mug2 initialized +Mugs(int) +new Mugs(1) completed +*///:~ diff --git a/src/initialization/NewVarArgs.java b/src/initialization/NewVarArgs.java new file mode 100644 index 0000000..256c0ae --- /dev/null +++ b/src/initialization/NewVarArgs.java @@ -0,0 +1,28 @@ +package initialization;//: initialization/NewVarArgs.java +// Using array syntax to create variable argument lists. + +public class NewVarArgs { + static void printArray(Object... args) { + for(Object obj : args) { + System.out.print(obj + " "); + } + System.out.println(); + } + public static void main(String[] args) { + // Can take individual elements: + printArray(new Integer(47), new Float(3.14), + new Double(11.11)); + printArray(47, 3.14F, 11.11); + printArray("one", "two", "three"); + printArray(new A(), new A(), new A()); + // Or an array: + printArray((Object[])new Integer[]{ 1, 2, 3, 4 }); + printArray(); // Empty list is OK + } +} /* Output: (75% match) +47 3.14 11.11 +47 3.14 11.11 +one two three +A@1bab50a A@c3c749 A@150bd4d +1 2 3 4 +*///:~ diff --git a/src/initialization/NoSynthesis.java b/src/initialization/NoSynthesis.java new file mode 100644 index 0000000..8093c0c --- /dev/null +++ b/src/initialization/NoSynthesis.java @@ -0,0 +1,14 @@ +package initialization;//: initialization/NoSynthesis.java + +class Bird2 { + Bird2(int i) {} + Bird2(double d) {} +} + +public class NoSynthesis { + public static void main(String[] args) { + //! Bird2 b = new Bird2(); // No default + Bird2 b2 = new Bird2(1); + Bird2 b3 = new Bird2(1.0); + } +} ///:~ diff --git a/src/initialization/OptionalTrailingArguments.java b/src/initialization/OptionalTrailingArguments.java new file mode 100644 index 0000000..b948bb1 --- /dev/null +++ b/src/initialization/OptionalTrailingArguments.java @@ -0,0 +1,20 @@ +package initialization;//: initialization/OptionalTrailingArguments.java + +public class OptionalTrailingArguments { + static void f(int required, String... trailing) { + System.out.print("required: " + required + " "); + for(String s : trailing) { + System.out.print(s + " "); + } + System.out.println(); + } + public static void main(String[] args) { + f(1, "one"); + f(2, "two", "three"); + f(0); + } +} /* Output: +required: 1 one +required: 2 two three +required: 0 +*///:~ diff --git a/src/initialization/OrderOfInitialization.java b/src/initialization/OrderOfInitialization.java new file mode 100644 index 0000000..f14ea4a --- /dev/null +++ b/src/initialization/OrderOfInitialization.java @@ -0,0 +1,35 @@ +package initialization;//: initialization/OrderOfInitialization.java +// Demonstrates initialization order. +import static net.mindview.util.Print.*; + +// When the constructor is called to create a +// Window object, you'll see a message: +class Window { + Window(int marker) { print("Window(" + marker + ")"); } +} + +class House { + Window w1 = new Window(1); // Before constructor + House() { + // Show that we're in the constructor: + print("House()"); + w3 = new Window(33); // Reinitialize w3 + } + Window w2 = new Window(2); // After constructor + void f() { print("f()"); } + Window w3 = new Window(3); // At end +} + +public class OrderOfInitialization { + public static void main(String[] args) { + House h = new House(); + h.f(); // Shows that construction is done + } +} /* Output: +Window(1) +Window(2) +Window(3) +House() +Window(33) +f() +*///:~ diff --git a/src/initialization/Overloading.java b/src/initialization/Overloading.java new file mode 100644 index 0000000..4558bdb --- /dev/null +++ b/src/initialization/Overloading.java @@ -0,0 +1,52 @@ +package initialization;//: initialization/Overloading.java +// Demonstration of both constructor +// and ordinary method overloading. +import static net.mindview.util.Print.*; + +class Tree { + int height; + Tree() { + print("Planting a seedling"); + height = 0; + } + Tree(int initialHeight) { + height = initialHeight; + print("Creating new Tree that is " + + height + " feet tall"); + } + void info() { + print("Tree is " + height + " feet tall"); + } + void info(String s) { + print(s + ": Tree is " + height + " feet tall"); + } +} + +public class Overloading { + public static void main(String[] args) { + for(int i = 0; i < 5; i++) { + Tree t = new Tree(i); + t.info(); + t.info("overloaded method"); + } + // Overloaded constructor: + new Tree(); + } +} /* Output: +Creating new Tree that is 0 feet tall +Tree is 0 feet tall +overloaded method: Tree is 0 feet tall +Creating new Tree that is 1 feet tall +Tree is 1 feet tall +overloaded method: Tree is 1 feet tall +Creating new Tree that is 2 feet tall +Tree is 2 feet tall +overloaded method: Tree is 2 feet tall +Creating new Tree that is 3 feet tall +Tree is 3 feet tall +overloaded method: Tree is 3 feet tall +Creating new Tree that is 4 feet tall +Tree is 4 feet tall +overloaded method: Tree is 4 feet tall +Planting a seedling +*///:~ diff --git a/src/initialization/OverloadingOrder.java b/src/initialization/OverloadingOrder.java new file mode 100644 index 0000000..dc22bd5 --- /dev/null +++ b/src/initialization/OverloadingOrder.java @@ -0,0 +1,19 @@ +package initialization;//: initialization/OverloadingOrder.java +// Overloading based on the order of the arguments. +import static net.mindview.util.Print.*; + +public class OverloadingOrder { + static void f(String s, int i) { + print("String: " + s + ", int: " + i); + } + static void f(int i, String s) { + print("int: " + i + ", String: " + s); + } + public static void main(String[] args) { + f("String first", 11); + f(99, "Int first"); + } +} /* Output: +String: String first, int: 11 +int: 99, String: Int first +*///:~ diff --git a/src/initialization/OverloadingVarargs.java b/src/initialization/OverloadingVarargs.java new file mode 100644 index 0000000..202b2bc --- /dev/null +++ b/src/initialization/OverloadingVarargs.java @@ -0,0 +1,35 @@ +package initialization;//: initialization/OverloadingVarargs.java + +public class OverloadingVarargs { + static void f(Character... args) { + System.out.print("first"); + for(Character c : args) { + System.out.print(" " + c); + } + System.out.println(); + } + static void f(Integer... args) { + System.out.print("second"); + for(Integer i : args) { + System.out.print(" " + i); + } + System.out.println(); + } + static void f(Long... args) { + System.out.println("third"); + } + public static void main(String[] args) { + f('a', 'b', 'c'); + f(1); + f(2, 1); + f(0); + f(0L); + //! f(); // Won't compile -- ambiguous + } +} /* Output: +first a b c +second 1 +second 2 1 +second 0 +third +*///:~ diff --git a/src/initialization/OverloadingVarargs2.java b/src/initialization/OverloadingVarargs2.java new file mode 100644 index 0000000..55e726b --- /dev/null +++ b/src/initialization/OverloadingVarargs2.java @@ -0,0 +1,15 @@ +package initialization;//: initialization/OverloadingVarargs2.java +// {CompileTimeError} (Won't compile) + +public class OverloadingVarargs2 { + static void f(float i, Character... args) { + System.out.println("first"); + } + static void f(Character... args) { + System.out.print("second"); + } + public static void main(String[] args) { + f(1, 'a'); + f('a', 'b'); + } +} ///:~ diff --git a/src/initialization/OverloadingVarargs3.java b/src/initialization/OverloadingVarargs3.java new file mode 100644 index 0000000..8b1b092 --- /dev/null +++ b/src/initialization/OverloadingVarargs3.java @@ -0,0 +1,17 @@ +package initialization;//: initialization/OverloadingVarargs3.java + +public class OverloadingVarargs3 { + static void f(float i, Character... args) { + System.out.println("first"); + } + static void f(char c, Character... args) { + System.out.println("second"); + } + public static void main(String[] args) { + f(1, 'a'); + f('a', 'b'); + } +} /* Output: +first +second +*///:~ diff --git a/src/initialization/PassingThis.java b/src/initialization/PassingThis.java new file mode 100644 index 0000000..457bdaa --- /dev/null +++ b/src/initialization/PassingThis.java @@ -0,0 +1,27 @@ +package initialization;//: initialization/PassingThis.java + +class Person { + public void eat(Apple apple) { + Apple peeled = apple.getPeeled(); + System.out.println("Yummy"); + } +} + +class Peeler { + static Apple peel(Apple apple) { + // ... remove peel + return apple; // Peeled + } +} + +class Apple { + Apple getPeeled() { return Peeler.peel(this); } +} + +public class PassingThis { + public static void main(String[] args) { + new Person().eat(new Apple()); + } +} /* Output: +Yummy +*///:~ diff --git a/src/initialization/PrimitiveOverloading.java b/src/initialization/PrimitiveOverloading.java new file mode 100644 index 0000000..94359c4 --- /dev/null +++ b/src/initialization/PrimitiveOverloading.java @@ -0,0 +1,101 @@ +package initialization;//: initialization/PrimitiveOverloading.java +// Promotion of primitives and overloading. +import static net.mindview.util.Print.*; + +public class PrimitiveOverloading { + void f1(char x) { printnb("f1(char) "); } + void f1(byte x) { printnb("f1(byte) "); } + void f1(short x) { printnb("f1(short) "); } + void f1(int x) { printnb("f1(int) "); } + void f1(long x) { printnb("f1(long) "); } + void f1(float x) { printnb("f1(float) "); } + void f1(double x) { printnb("f1(double) "); } + + void f2(byte x) { printnb("f2(byte) "); } + void f2(short x) { printnb("f2(short) "); } + void f2(int x) { printnb("f2(int) "); } + void f2(long x) { printnb("f2(long) "); } + void f2(float x) { printnb("f2(float) "); } + void f2(double x) { printnb("f2(double) "); } + + void f3(short x) { printnb("f3(short) "); } + void f3(int x) { printnb("f3(int) "); } + void f3(long x) { printnb("f3(long) "); } + void f3(float x) { printnb("f3(float) "); } + void f3(double x) { printnb("f3(double) "); } + + void f4(int x) { printnb("f4(int) "); } + void f4(long x) { printnb("f4(long) "); } + void f4(float x) { printnb("f4(float) "); } + void f4(double x) { printnb("f4(double) "); } + + void f5(long x) { printnb("f5(long) "); } + void f5(float x) { printnb("f5(float) "); } + void f5(double x) { printnb("f5(double) "); } + + void f6(float x) { printnb("f6(float) "); } + void f6(double x) { printnb("f6(double) "); } + + void f7(double x) { printnb("f7(double) "); } + + void testConstVal() { + printnb("5: "); + f1(5);f2(5);f3(5);f4(5);f5(5);f6(5);f7(5); print(); + } + void testChar() { + char x = 'x'; + printnb("char: "); + f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print(); + } + void testByte() { + byte x = 0; + printnb("byte: "); + f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print(); + } + void testShort() { + short x = 0; + printnb("short: "); + f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print(); + } + void testInt() { + int x = 0; + printnb("int: "); + f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print(); + } + void testLong() { + long x = 0; + printnb("long: "); + f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print(); + } + void testFloat() { + float x = 0; + printnb("float: "); + f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print(); + } + void testDouble() { + double x = 0; + printnb("double: "); + f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print(); + } + public static void main(String[] args) { + PrimitiveOverloading p = + new PrimitiveOverloading(); + p.testConstVal(); + p.testChar(); + p.testByte(); + p.testShort(); + p.testInt(); + p.testLong(); + p.testFloat(); + p.testDouble(); + } +} /* Output: +5: f1(int) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double) +char: f1(char) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double) +byte: f1(byte) f2(byte) f3(short) f4(int) f5(long) f6(float) f7(double) +short: f1(short) f2(short) f3(short) f4(int) f5(long) f6(float) f7(double) +int: f1(int) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double) +long: f1(long) f2(long) f3(long) f4(long) f5(long) f6(float) f7(double) +float: f1(float) f2(float) f3(float) f4(float) f5(float) f6(float) f7(double) +double: f1(double) f2(double) f3(double) f4(double) f5(double) f6(double) f7(double) +*///:~ diff --git a/src/initialization/SimpleConstructor.java b/src/initialization/SimpleConstructor.java new file mode 100644 index 0000000..ef07790 --- /dev/null +++ b/src/initialization/SimpleConstructor.java @@ -0,0 +1,18 @@ +package initialization;//: initialization/SimpleConstructor.java +// Demonstration of a simple constructor. + +class Rock { + Rock() { // This is the constructor + System.out.print("Rock "); + } +} + +public class SimpleConstructor { + public static void main(String[] args) { + for(int i = 0; i < 10; i++) { + new Rock(); + } + } +} /* Output: +Rock Rock Rock Rock Rock Rock Rock Rock Rock Rock +*///:~ diff --git a/src/initialization/SimpleConstructor2.java b/src/initialization/SimpleConstructor2.java new file mode 100644 index 0000000..d8ee840 --- /dev/null +++ b/src/initialization/SimpleConstructor2.java @@ -0,0 +1,18 @@ +package initialization;//: initialization/SimpleConstructor2.java +// Constructors can have arguments. + +class Rock2 { + Rock2(int i) { + System.out.print("Rock " + i + " "); + } +} + +public class SimpleConstructor2 { + public static void main(String[] args) { + for(int i = 0; i < 8; i++) { + new Rock2(i); + } + } +} /* Output: +Rock 0 Rock 1 Rock 2 Rock 3 Rock 4 Rock 5 Rock 6 Rock 7 +*///:~ diff --git a/src/initialization/SimpleEnumUse.java b/src/initialization/SimpleEnumUse.java new file mode 100644 index 0000000..83f5647 --- /dev/null +++ b/src/initialization/SimpleEnumUse.java @@ -0,0 +1,10 @@ +package initialization;//: initialization/SimpleEnumUse.java + +public class SimpleEnumUse { + public static void main(String[] args) { + Spiciness howHot = Spiciness.MEDIUM; + System.out.println(howHot); + } +} /* Output: +MEDIUM +*///:~ diff --git a/src/initialization/Spiciness.java b/src/initialization/Spiciness.java new file mode 100644 index 0000000..3eef78c --- /dev/null +++ b/src/initialization/Spiciness.java @@ -0,0 +1,5 @@ +package initialization;//: initialization/Spiciness.java + +public enum Spiciness { + NOT, MILD, MEDIUM, HOT, FLAMING +} ///:~ diff --git a/src/initialization/Spoon.java b/src/initialization/Spoon.java new file mode 100644 index 0000000..bf17dda --- /dev/null +++ b/src/initialization/Spoon.java @@ -0,0 +1,9 @@ +package initialization; + +//: initialization/Spoon.java +public class Spoon { + static int i; + static { + i = 47; + } +} ///:~ diff --git a/src/initialization/StaticInitialization.java b/src/initialization/StaticInitialization.java new file mode 100644 index 0000000..e9a905d --- /dev/null +++ b/src/initialization/StaticInitialization.java @@ -0,0 +1,70 @@ +package initialization;//: initialization/StaticInitialization.java +// Specifying initial values in a class definition. +import static net.mindview.util.Print.*; + +class Bowl { + Bowl(int marker) { + print("Bowl(" + marker + ")"); + } + void f1(int marker) { + print("f1(" + marker + ")"); + } +} + +class Table { + static Bowl bowl1 = new Bowl(1); + Table() { + print("Table()"); + bowl2.f1(1); + } + void f2(int marker) { + print("f2(" + marker + ")"); + } + static Bowl bowl2 = new Bowl(2); +} + +class Cupboard { + Bowl bowl3 = new Bowl(3); + static Bowl bowl4 = new Bowl(4); + Cupboard() { + print("Cupboard()"); + bowl4.f1(2); + } + void f3(int marker) { + print("f3(" + marker + ")"); + } + static Bowl bowl5 = new Bowl(5); +} + +public class StaticInitialization { + public static void main(String[] args) { + print("Creating new Cupboard() in main"); + new Cupboard(); + print("Creating new Cupboard() in main"); + new Cupboard(); + table.f2(1); + cupboard.f3(1); + } + static Table table = new Table(); + static Cupboard cupboard = new Cupboard(); +} /* Output: +Bowl(1) +Bowl(2) +Table() +f1(1) +Bowl(4) +Bowl(5) +Bowl(3) +Cupboard() +f1(2) +Creating new Cupboard() in main +Bowl(3) +Cupboard() +f1(2) +Creating new Cupboard() in main +Bowl(3) +Cupboard() +f1(2) +f2(1) +f3(1) +*///:~ diff --git a/src/initialization/TerminationCondition.java b/src/initialization/TerminationCondition.java new file mode 100644 index 0000000..f36aa66 --- /dev/null +++ b/src/initialization/TerminationCondition.java @@ -0,0 +1,34 @@ +package initialization;//: initialization/TerminationCondition.java +// Using finalize() to detect an object that +// hasn't been properly cleaned up. + +class Book { + boolean checkedOut = false; + Book(boolean checkOut) { + checkedOut = checkOut; + } + void checkIn() { + checkedOut = false; + } + protected void finalize() { + if(checkedOut) { + System.out.println("Error: checked out"); + } + // Normally, you'll also do this: + // super.finalize(); // Call the base-class version + } +} + +public class TerminationCondition { + public static void main(String[] args) { + Book novel = new Book(true); + // Proper cleanup: + novel.checkIn(); + // Drop the reference, forget to clean up: + new Book(true); + // Force garbage collection & finalization: + System.gc(); + } +} /* Output: +Error: checked out +*///:~ diff --git a/src/initialization/VarArgs.java b/src/initialization/VarArgs.java new file mode 100644 index 0000000..c3504d3 --- /dev/null +++ b/src/initialization/VarArgs.java @@ -0,0 +1,24 @@ +package initialization;//: initialization/VarArgs.java +// Using array syntax to create variable argument lists. + +class A {} + +public class VarArgs { + static void printArray(Object[] args) { + for(Object obj : args) { + System.out.print(obj + " "); + } + System.out.println(); + } + public static void main(String[] args) { + printArray(new Object[]{ + new Integer(47), new Float(3.14), new Double(11.11) + }); + printArray(new Object[]{"one", "two", "three" }); + printArray(new Object[]{new A(), new A(), new A()}); + } +} /* Output: (Sample) +47 3.14 11.11 +one two three +A@1a46e30 A@3e25a5 A@19821f +*///:~ diff --git a/src/initialization/VarargType.java b/src/initialization/VarargType.java new file mode 100644 index 0000000..a1cec0f --- /dev/null +++ b/src/initialization/VarargType.java @@ -0,0 +1,25 @@ +package initialization;//: initialization/VarargType.java + +public class VarargType { + static void f(Character... args) { + System.out.print(args.getClass()); + System.out.println(" length " + args.length); + } + static void g(int... args) { + System.out.print(args.getClass()); + System.out.println(" length " + args.length); + } + public static void main(String[] args) { + f('a'); + f(); + g(1); + g(); + System.out.println("int[]: " + new int[0].getClass()); + } +} /* Output: +class [Ljava.lang.Character; length 1 +class [Ljava.lang.Character; length 0 +class [I length 1 +class [I length 0 +int[]: class [I +*///:~ diff --git a/src/initialization/build.xml b/src/initialization/build.xml new file mode 100644 index 0000000..31fd42c --- /dev/null +++ b/src/initialization/build.xml @@ -0,0 +1,401 @@ + + + + + + build.xml for the source code for the initialization chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/innerclasses/AnonymousConstructor.java b/src/innerclasses/AnonymousConstructor.java new file mode 100644 index 0000000..49671d3 --- /dev/null +++ b/src/innerclasses/AnonymousConstructor.java @@ -0,0 +1,29 @@ +package innerclasses;//: innerclasses/AnonymousConstructor.java +// Creating a constructor for an anonymous inner class. +import static net.mindview.util.Print.*; + +abstract class Base { + public Base(int i) { + print("Base constructor, i = " + i); + } + public abstract void f(); +} + +public class AnonymousConstructor { + public static Base getBase(int i) { + return new Base(i) { + { print("Inside instance initializer"); } + public void f() { + print("In anonymous f()"); + } + }; + } + public static void main(String[] args) { + Base base = getBase(47); + base.f(); + } +} /* Output: +Base constructor, i = 47 +Inside instance initializer +In anonymous f() +*///:~ diff --git a/src/innerclasses/BigEgg.java b/src/innerclasses/BigEgg.java new file mode 100644 index 0000000..be235ad --- /dev/null +++ b/src/innerclasses/BigEgg.java @@ -0,0 +1,26 @@ +package innerclasses;//: innerclasses/BigEgg.java +// An inner class cannot be overriden like a method. +import static net.mindview.util.Print.*; + +class Egg { + private Yolk y; + protected class Yolk { + public Yolk() { print("Egg.Yolk()"); } + } + public Egg() { + print("New Egg()"); + y = new Yolk(); + } +} + +public class BigEgg extends Egg { + public class Yolk { + public Yolk() { print("BigEgg.Yolk()"); } + } + public static void main(String[] args) { + new BigEgg(); + } +} /* Output: +New Egg() +Egg.Yolk() +*///:~ diff --git a/src/innerclasses/BigEgg2.java b/src/innerclasses/BigEgg2.java new file mode 100644 index 0000000..3285512 --- /dev/null +++ b/src/innerclasses/BigEgg2.java @@ -0,0 +1,32 @@ +package innerclasses;//: innerclasses/BigEgg2.java +// Proper inheritance of an inner class. +import static net.mindview.util.Print.*; + +class Egg2 { + protected class Yolk { + public Yolk() { print("Egg2.Yolk()"); } + public void f() { print("Egg2.Yolk.f()");} + } + private Yolk y = new Yolk(); + public Egg2() { print("New Egg2()"); } + public void insertYolk(Yolk yy) { y = yy; } + public void g() { y.f(); } +} + +public class BigEgg2 extends Egg2 { + public class Yolk extends Egg2.Yolk { + public Yolk() { print("BigEgg2.Yolk()"); } + public void f() { print("BigEgg2.Yolk.f()"); } + } + public BigEgg2() { insertYolk(new Yolk()); } + public static void main(String[] args) { + Egg2 e2 = new BigEgg2(); + e2.g(); + } +} /* Output: +Egg2.Yolk() +New Egg2() +Egg2.Yolk() +BigEgg2.Yolk() +BigEgg2.Yolk.f() +*///:~ diff --git a/src/innerclasses/Callbacks.java b/src/innerclasses/Callbacks.java new file mode 100644 index 0000000..b982cd5 --- /dev/null +++ b/src/innerclasses/Callbacks.java @@ -0,0 +1,72 @@ +//: innerclasses/Callbacks.java +// Using inner classes for callbacks +package innerclasses; +import static net.mindview.util.Print.*; + +interface Incrementable { + void increment(); +} + +// Very simple to just implement the interface: +class Callee1 implements Incrementable { + private int i = 0; + public void increment() { + i++; + print(i); + } +} + +class MyIncrement { + public void increment() { print("Other operation"); } + static void f(MyIncrement mi) { mi.increment(); } +} + +// If your class must implement increment() in +// some other way, you must use an inner class: +class Callee2 extends MyIncrement { + private int i = 0; + public void increment() { + super.increment(); + i++; + print(i); + } + private class Closure implements Incrementable { + public void increment() { + // Specify outer-class method, otherwise + // you'd get an infinite recursion: + Callee2.this.increment(); + } + } + Incrementable getCallbackReference() { + return new Closure(); + } +} + +class Caller { + private Incrementable callbackReference; + Caller(Incrementable cbh) { callbackReference = cbh; } + void go() { callbackReference.increment(); } +} + +public class Callbacks { + public static void main(String[] args) { + Callee1 c1 = new Callee1(); + Callee2 c2 = new Callee2(); + MyIncrement.f(c2); + Caller caller1 = new Caller(c1); + Caller caller2 = new Caller(c2.getCallbackReference()); + caller1.go(); + caller1.go(); + caller2.go(); + caller2.go(); + } +} /* Output: +Other operation +1 +1 +2 +Other operation +2 +Other operation +3 +*///:~ diff --git a/src/innerclasses/ClassInInterface.java b/src/innerclasses/ClassInInterface.java new file mode 100644 index 0000000..e86f148 --- /dev/null +++ b/src/innerclasses/ClassInInterface.java @@ -0,0 +1,16 @@ +package innerclasses;//: innerclasses/ClassInInterface.java +// {main: ClassInInterface$Test} + +public interface ClassInInterface { + void howdy(); + class Test implements ClassInInterface { + public void howdy() { + System.out.println("Howdy!"); + } + public static void main(String[] args) { + new Test().howdy(); + } + } +} /* Output: +Howdy! +*///:~ diff --git a/src/innerclasses/Contents.java b/src/innerclasses/Contents.java new file mode 100644 index 0000000..8db0bd0 --- /dev/null +++ b/src/innerclasses/Contents.java @@ -0,0 +1,6 @@ +package innerclasses; + +//: innerclasses/Contents.java +public interface Contents { + int value(); +} ///:~ diff --git a/src/innerclasses/Destination.java b/src/innerclasses/Destination.java new file mode 100644 index 0000000..09f2836 --- /dev/null +++ b/src/innerclasses/Destination.java @@ -0,0 +1,6 @@ +package innerclasses; + +//: innerclasses/Destination.java +public interface Destination { + String readLabel(); +} ///:~ diff --git a/src/innerclasses/DotNew.java b/src/innerclasses/DotNew.java new file mode 100644 index 0000000..39d7910 --- /dev/null +++ b/src/innerclasses/DotNew.java @@ -0,0 +1,10 @@ +package innerclasses;//: innerclasses/DotNew.java +// Creating an inner class directly using the .new syntax. + +public class DotNew { + public class Inner {} + public static void main(String[] args) { + DotNew dn = new DotNew(); + DotNew.Inner dni = dn.new Inner(); + } +} ///:~ diff --git a/src/innerclasses/DotThis.java b/src/innerclasses/DotThis.java new file mode 100644 index 0000000..f74c937 --- /dev/null +++ b/src/innerclasses/DotThis.java @@ -0,0 +1,20 @@ +package innerclasses;//: innerclasses/DotThis.java +// Qualifying access to the outer-class object. + +public class DotThis { + void f() { System.out.println("DotThis.f()"); } + public class Inner { + public DotThis outer() { + return DotThis.this; + // A plain "this" would be Inner's "this" + } + } + public Inner inner() { return new Inner(); } + public static void main(String[] args) { + DotThis dt = new DotThis(); + DotThis.Inner dti = dt.inner(); + dti.outer().f(); + } +} /* Output: +DotThis.f() +*///:~ diff --git a/src/innerclasses/Factories.java b/src/innerclasses/Factories.java new file mode 100644 index 0000000..a61f46b --- /dev/null +++ b/src/innerclasses/Factories.java @@ -0,0 +1,53 @@ +package innerclasses;//: innerclasses/Factories.java +import static net.mindview.util.Print.*; + +interface Service { + void method1(); + void method2(); +} + +interface ServiceFactory { + Service getService(); +} + +class Implementation1 implements Service { + private Implementation1() {} + public void method1() {print("Implementation1 method1");} + public void method2() {print("Implementation1 method2");} + public static ServiceFactory factory = + new ServiceFactory() { + public Service getService() { + return new Implementation1(); + } + }; +} + +class Implementation2 implements Service { + private Implementation2() {} + public void method1() {print("Implementation2 method1");} + public void method2() {print("Implementation2 method2");} + public static ServiceFactory factory = + new ServiceFactory() { + public Service getService() { + return new Implementation2(); + } + }; +} + +public class Factories { + public static void serviceConsumer(ServiceFactory fact) { + Service s = fact.getService(); + s.method1(); + s.method2(); + } + public static void main(String[] args) { + serviceConsumer(Implementation1.factory); + // Implementations are completely interchangeable: + serviceConsumer(Implementation2.factory); + } +} /* Output: +Implementation1 method1 +Implementation1 method2 +Implementation2 method1 +Implementation2 method2 +*///:~ diff --git a/src/innerclasses/Games.java b/src/innerclasses/Games.java new file mode 100644 index 0000000..1ac9f91 --- /dev/null +++ b/src/innerclasses/Games.java @@ -0,0 +1,52 @@ +package innerclasses;//: innerclasses/Games.java +// Using anonymous inner classes with the Game framework. +import static net.mindview.util.Print.*; + +interface Game { boolean move(); } +interface GameFactory { Game getGame(); } + +class Checkers implements Game { + private Checkers() {} + private int moves = 0; + private static final int MOVES = 3; + public boolean move() { + print("Checkers move " + moves); + return ++moves != MOVES; + } + public static GameFactory factory = new GameFactory() { + public Game getGame() { return new Checkers(); } + }; +} + +class Chess implements Game { + private Chess() {} + private int moves = 0; + private static final int MOVES = 4; + public boolean move() { + print("Chess move " + moves); + return ++moves != MOVES; + } + public static GameFactory factory = new GameFactory() { + public Game getGame() { return new Chess(); } + }; +} + +public class Games { + public static void playGame(GameFactory factory) { + Game s = factory.getGame(); + while(s.move()) { + } + } + public static void main(String[] args) { + playGame(Checkers.factory); + playGame(Chess.factory); + } +} /* Output: +Checkers move 0 +Checkers move 1 +Checkers move 2 +Chess move 0 +Chess move 1 +Chess move 2 +Chess move 3 +*///:~ diff --git a/src/innerclasses/GreenhouseController.java b/src/innerclasses/GreenhouseController.java new file mode 100644 index 0000000..1adad69 --- /dev/null +++ b/src/innerclasses/GreenhouseController.java @@ -0,0 +1,38 @@ +package innerclasses;//: innerclasses/GreenhouseController.java +// Configure and execute the greenhouse system. +// {Args: 5000} +import innerclasses.controller.*; + +public class GreenhouseController { + public static void main(String[] args) { + GreenhouseControls gc = new GreenhouseControls(); + // Instead of hard-wiring, you could parse + // configuration information from a text file here: + gc.addEvent(gc.new Bell(900)); + Event[] eventList = { + gc.new ThermostatNight(0), + gc.new LightOn(200), + gc.new LightOff(400), + gc.new WaterOn(600), + gc.new WaterOff(800), + gc.new ThermostatDay(1400) + }; + gc.addEvent(gc.new Restart(2000, eventList)); + if(args.length == 1) { + gc.addEvent( + new GreenhouseControls.Terminate( + new Integer(args[0]))); + } + gc.run(); + } +} /* Output: +Bing! +Thermostat on night setting +Light is on +Light is off +Greenhouse water is on +Greenhouse water is off +Thermostat on day setting +Restarting system +Terminating +*///:~ diff --git a/src/innerclasses/GreenhouseControls.java b/src/innerclasses/GreenhouseControls.java new file mode 100644 index 0000000..386c6d6 --- /dev/null +++ b/src/innerclasses/GreenhouseControls.java @@ -0,0 +1,109 @@ +package innerclasses;//: innerclasses/GreenhouseControls.java +// This produces a specific application of the +// control system, all in a single class. Inner +// classes allow you to encapsulate different +// functionality for each type of event. +import innerclasses.controller.*; + +public class GreenhouseControls extends Controller { + private boolean light = false; + public class LightOn extends Event { + public LightOn(long delayTime) { super(delayTime); } + public void action() { + // Put hardware control code here to + // physically turn on the light. + light = true; + } + public String toString() { return "Light is on"; } + } + public class LightOff extends Event { + public LightOff(long delayTime) { super(delayTime); } + public void action() { + // Put hardware control code here to + // physically turn off the light. + light = false; + } + public String toString() { return "Light is off"; } + } + private boolean water = false; + public class WaterOn extends Event { + public WaterOn(long delayTime) { super(delayTime); } + public void action() { + // Put hardware control code here. + water = true; + } + public String toString() { + return "Greenhouse water is on"; + } + } + public class WaterOff extends Event { + public WaterOff(long delayTime) { super(delayTime); } + public void action() { + // Put hardware control code here. + water = false; + } + public String toString() { + return "Greenhouse water is off"; + } + } + private String thermostat = "Day"; + public class ThermostatNight extends Event { + public ThermostatNight(long delayTime) { + super(delayTime); + } + public void action() { + // Put hardware control code here. + thermostat = "Night"; + } + public String toString() { + return "Thermostat on night setting"; + } + } + public class ThermostatDay extends Event { + public ThermostatDay(long delayTime) { + super(delayTime); + } + public void action() { + // Put hardware control code here. + thermostat = "Day"; + } + public String toString() { + return "Thermostat on day setting"; + } + } + // An example of an action() that inserts a + // new one of itself into the event list: + public class Bell extends Event { + public Bell(long delayTime) { super(delayTime); } + public void action() { + addEvent(new Bell(delayTime)); + } + public String toString() { return "Bing!"; } + } + public class Restart extends Event { + private Event[] eventList; + public Restart(long delayTime, Event[] eventList) { + super(delayTime); + this.eventList = eventList; + for(Event e : eventList) { + addEvent(e); + } + } + public void action() { + for(Event e : eventList) { + e.start(); // Rerun each event + addEvent(e); + } + start(); // Rerun this Event + addEvent(this); + } + public String toString() { + return "Restarting system"; + } + } + public static class Terminate extends Event { + public Terminate(long delayTime) { super(delayTime); } + public void action() { System.exit(0); } + public String toString() { return "Terminating"; } + } +} ///:~ diff --git a/src/innerclasses/InheritInner.java b/src/innerclasses/InheritInner.java new file mode 100644 index 0000000..a8d35c9 --- /dev/null +++ b/src/innerclasses/InheritInner.java @@ -0,0 +1,17 @@ +package innerclasses;//: innerclasses/InheritInner.java +// Inheriting an inner class. + +class WithInner { + class Inner {} +} + +public class InheritInner extends WithInner.Inner { + //! InheritInner() {} // Won't compile + InheritInner(WithInner wi) { + wi.super(); + } + public static void main(String[] args) { + WithInner wi = new WithInner(); + InheritInner ii = new InheritInner(wi); + } +} ///:~ diff --git a/src/innerclasses/LocalInnerClass.java b/src/innerclasses/LocalInnerClass.java new file mode 100644 index 0000000..adc6e24 --- /dev/null +++ b/src/innerclasses/LocalInnerClass.java @@ -0,0 +1,64 @@ +package innerclasses;//: innerclasses/LocalInnerClass.java +// Holds a sequence of Objects. +import static net.mindview.util.Print.*; + +interface Counter { + int next(); +} + +public class LocalInnerClass { + private int count = 0; + Counter getCounter(final String name) { + // A local inner class: + class LocalCounter implements Counter { + public LocalCounter() { + // Local inner class can have a constructor + print("LocalCounter()"); + } + public int next() { + printnb(name); // Access local final + return count++; + } + } + return new LocalCounter(); + } + // The same thing with an anonymous inner class: + Counter getCounter2(final String name) { + return new Counter() { + // Anonymous inner class cannot have a named + // constructor, only an instance initializer: + { + print("Counter()"); + } + public int next() { + printnb(name); // Access local final + return count++; + } + }; + } + public static void main(String[] args) { + LocalInnerClass lic = new LocalInnerClass(); + Counter + c1 = lic.getCounter("Local inner "), + c2 = lic.getCounter2("Anonymous inner "); + for(int i = 0; i < 5; i++) { + print(c1.next()); + } + for(int i = 0; i < 5; i++) { + print(c2.next()); + } + } +} /* Output: +LocalCounter() +Counter() +Local inner 0 +Local inner 1 +Local inner 2 +Local inner 3 +Local inner 4 +Anonymous inner 5 +Anonymous inner 6 +Anonymous inner 7 +Anonymous inner 8 +Anonymous inner 9 +*///:~ diff --git a/src/innerclasses/MultiImplementation.java b/src/innerclasses/MultiImplementation.java new file mode 100644 index 0000000..fe067a9 --- /dev/null +++ b/src/innerclasses/MultiImplementation.java @@ -0,0 +1,22 @@ +//: innerclasses/MultiImplementation.java +// With concrete or abstract classes, inner +// classes are the only way to produce the effect +// of "multiple implementation inheritance." +package innerclasses; + +class D {} +abstract class E {} + +class Z extends D { + E makeE() { return new E() {}; } +} + +public class MultiImplementation { + static void takesD(D d) {} + static void takesE(E e) {} + public static void main(String[] args) { + Z z = new Z(); + takesD(z); + takesE(z.makeE()); + } +} ///:~ diff --git a/src/innerclasses/MultiInterfaces.java b/src/innerclasses/MultiInterfaces.java new file mode 100644 index 0000000..f6a170a --- /dev/null +++ b/src/innerclasses/MultiInterfaces.java @@ -0,0 +1,28 @@ +//: innerclasses/MultiInterfaces.java +// Two ways that a class can implement multiple interfaces. +package innerclasses; + +interface A {} +interface B {} + +class X implements A, B {} + +class Y implements A { + B makeB() { + // Anonymous inner class: + return new B() {}; + } +} + +public class MultiInterfaces { + static void takesA(A a) {} + static void takesB(B b) {} + public static void main(String[] args) { + X x = new X(); + Y y = new Y(); + takesA(x); + takesA(y); + takesB(x); + takesB(y.makeB()); + } +} ///:~ diff --git a/src/innerclasses/MultiNestingAccess.java b/src/innerclasses/MultiNestingAccess.java new file mode 100644 index 0000000..a802d58 --- /dev/null +++ b/src/innerclasses/MultiNestingAccess.java @@ -0,0 +1,25 @@ +package innerclasses;//: innerclasses/MultiNestingAccess.java +// Nested classes can access all members of all +// levels of the classes they are nested within. + +class MNA { + private void f() {} + class A { + private void g() {} + public class B { + void h() { + g(); + f(); + } + } + } +} + +public class MultiNestingAccess { + public static void main(String[] args) { + MNA mna = new MNA(); + MNA.A mnaa = mna.new A(); + MNA.A.B mnaab = mnaa.new B(); + mnaab.h(); + } +} ///:~ diff --git a/src/innerclasses/Parcel1.java b/src/innerclasses/Parcel1.java new file mode 100644 index 0000000..73d21b0 --- /dev/null +++ b/src/innerclasses/Parcel1.java @@ -0,0 +1,29 @@ +package innerclasses;//: innerclasses/Parcel1.java +// Creating inner classes. + +public class Parcel1 { + class Contents { + private int i = 11; + public int value() { return i; } + } + class Destination { + private String label; + Destination(String whereTo) { + label = whereTo; + } + String readLabel() { return label; } + } + // Using inner classes looks just like + // using any other class, within Parcel1: + public void ship(String dest) { + Contents c = new Contents(); + Destination d = new Destination(dest); + System.out.println(d.readLabel()); + } + public static void main(String[] args) { + Parcel1 p = new Parcel1(); + p.ship("Tasmania"); + } +} /* Output: +Tasmania +*///:~ diff --git a/src/innerclasses/Parcel10.java b/src/innerclasses/Parcel10.java new file mode 100644 index 0000000..a27c748 --- /dev/null +++ b/src/innerclasses/Parcel10.java @@ -0,0 +1,27 @@ +package innerclasses;//: innerclasses/Parcel10.java +// Using "instance initialization" to perform +// construction on an anonymous inner class. + +public class Parcel10 { + public Destination + destination(final String dest, final float price) { + return new Destination() { + private int cost; + // Instance initialization for each object: + { + cost = Math.round(price); + if(cost > 100) { + System.out.println("Over budget!"); + } + } + private String label = dest; + public String readLabel() { return label; } + }; + } + public static void main(String[] args) { + Parcel10 p = new Parcel10(); + Destination d = p.destination("Tasmania", 101.395F); + } +} /* Output: +Over budget! +*///:~ diff --git a/src/innerclasses/Parcel11.java b/src/innerclasses/Parcel11.java new file mode 100644 index 0000000..8c2fdb2 --- /dev/null +++ b/src/innerclasses/Parcel11.java @@ -0,0 +1,34 @@ +package innerclasses;//: innerclasses/Parcel11.java +// Nested classes (static inner classes). + +public class Parcel11 { + private static class ParcelContents implements Contents { + private int i = 11; + public int value() { return i; } + } + protected static class ParcelDestination + implements Destination { + private String label; + private ParcelDestination(String whereTo) { + label = whereTo; + } + public String readLabel() { return label; } + // Nested classes can contain other static elements: + public static void f() {} + static int x = 10; + static class AnotherLevel { + public static void f() {} + static int x = 10; + } + } + public static Destination destination(String s) { + return new ParcelDestination(s); + } + public static Contents contents() { + return new ParcelContents(); + } + public static void main(String[] args) { + Contents c = contents(); + Destination d = destination("Tasmania"); + } +} ///:~ diff --git a/src/innerclasses/Parcel2.java b/src/innerclasses/Parcel2.java new file mode 100644 index 0000000..6697376 --- /dev/null +++ b/src/innerclasses/Parcel2.java @@ -0,0 +1,37 @@ +package innerclasses;//: innerclasses/Parcel2.java +// Returning a reference to an inner class. + +public class Parcel2 { + class Contents { + private int i = 11; + public int value() { return i; } + } + class Destination { + private String label; + Destination(String whereTo) { + label = whereTo; + } + String readLabel() { return label; } + } + public Destination to(String s) { + return new Destination(s); + } + public Contents contents() { + return new Contents(); + } + public void ship(String dest) { + Contents c = contents(); + Destination d = to(dest); + System.out.println(d.readLabel()); + } + public static void main(String[] args) { + Parcel2 p = new Parcel2(); + p.ship("Tasmania"); + Parcel2 q = new Parcel2(); + // Defining references to inner classes: + Parcel2.Contents c = q.contents(); + Parcel2.Destination d = q.to("Borneo"); + } +} /* Output: +Tasmania +*///:~ diff --git a/src/innerclasses/Parcel3.java b/src/innerclasses/Parcel3.java new file mode 100644 index 0000000..2757c21 --- /dev/null +++ b/src/innerclasses/Parcel3.java @@ -0,0 +1,21 @@ +package innerclasses;//: innerclasses/Parcel3.java +// Using .new to create instances of inner classes. + +public class Parcel3 { + class Contents { + private int i = 11; + public int value() { return i; } + } + class Destination { + private String label; + Destination(String whereTo) { label = whereTo; } + String readLabel() { return label; } + } + public static void main(String[] args) { + Parcel3 p = new Parcel3(); + // Must use instance of outer class + // to create an instance of the inner class: + Parcel3.Contents c = p.new Contents(); + Parcel3.Destination d = p.new Destination("Tasmania"); + } +} ///:~ diff --git a/src/innerclasses/Parcel5.java b/src/innerclasses/Parcel5.java new file mode 100644 index 0000000..0669ba2 --- /dev/null +++ b/src/innerclasses/Parcel5.java @@ -0,0 +1,19 @@ +package innerclasses;//: innerclasses/Parcel5.java +// Nesting a class within a method. + +public class Parcel5 { + public Destination destination(String s) { + class PDestination implements Destination { + private String label; + private PDestination(String whereTo) { + label = whereTo; + } + public String readLabel() { return label; } + } + return new PDestination(s); + } + public static void main(String[] args) { + Parcel5 p = new Parcel5(); + Destination d = p.destination("Tasmania"); + } +} ///:~ diff --git a/src/innerclasses/Parcel6.java b/src/innerclasses/Parcel6.java new file mode 100644 index 0000000..77f41c4 --- /dev/null +++ b/src/innerclasses/Parcel6.java @@ -0,0 +1,25 @@ +package innerclasses;//: innerclasses/Parcel6.java +// Nesting a class within a scope. + +public class Parcel6 { + private void internalTracking(boolean b) { + if(b) { + class TrackingSlip { + private String id; + TrackingSlip(String s) { + id = s; + } + String getSlip() { return id; } + } + TrackingSlip ts = new TrackingSlip("slip"); + String s = ts.getSlip(); + } + // Can't use it here! Out of scope: + //! TrackingSlip ts = new TrackingSlip("x"); + } + public void track() { internalTracking(true); } + public static void main(String[] args) { + Parcel6 p = new Parcel6(); + p.track(); + } +} ///:~ diff --git a/src/innerclasses/Parcel7.java b/src/innerclasses/Parcel7.java new file mode 100644 index 0000000..43eed71 --- /dev/null +++ b/src/innerclasses/Parcel7.java @@ -0,0 +1,15 @@ +package innerclasses;//: innerclasses/Parcel7.java +// Returning an instance of an anonymous inner class. + +public class Parcel7 { + public Contents contents() { + return new Contents() { // Insert a class definition + private int i = 11; + public int value() { return i; } + }; // Semicolon required in this case + } + public static void main(String[] args) { + Parcel7 p = new Parcel7(); + Contents c = p.contents(); + } +} ///:~ diff --git a/src/innerclasses/Parcel7b.java b/src/innerclasses/Parcel7b.java new file mode 100644 index 0000000..2210741 --- /dev/null +++ b/src/innerclasses/Parcel7b.java @@ -0,0 +1,14 @@ +package innerclasses;//: innerclasses/Parcel7b.java +// Expanded version of Parcel7.java + +public class Parcel7b { + class MyContents implements Contents { + private int i = 11; + public int value() { return i; } + } + public Contents contents() { return new MyContents(); } + public static void main(String[] args) { + Parcel7b p = new Parcel7b(); + Contents c = p.contents(); + } +} ///:~ diff --git a/src/innerclasses/Parcel8.java b/src/innerclasses/Parcel8.java new file mode 100644 index 0000000..dad8ce2 --- /dev/null +++ b/src/innerclasses/Parcel8.java @@ -0,0 +1,17 @@ +package innerclasses;//: innerclasses/Parcel8.java +// Calling the base-class constructor. + +public class Parcel8 { + public Wrapping wrapping(int x) { + // Base constructor call: + return new Wrapping(x) { // Pass constructor argument. + public int value() { + return super.value() * 47; + } + }; // Semicolon required + } + public static void main(String[] args) { + Parcel8 p = new Parcel8(); + Wrapping w = p.wrapping(10); + } +} ///:~ diff --git a/src/innerclasses/Parcel9.java b/src/innerclasses/Parcel9.java new file mode 100644 index 0000000..fbcf6a0 --- /dev/null +++ b/src/innerclasses/Parcel9.java @@ -0,0 +1,18 @@ +package innerclasses;//: innerclasses/Parcel9.java +// An anonymous inner class that performs +// initialization. A briefer version of Parcel5.java. + +public class Parcel9 { + // Argument must be final to use inside + // anonymous inner class: + public Destination destination(final String dest) { + return new Destination() { + private String label = dest; + public String readLabel() { return label; } + }; + } + public static void main(String[] args) { + Parcel9 p = new Parcel9(); + Destination d = p.destination("Tasmania"); + } +} ///:~ diff --git a/src/innerclasses/Sequence.java b/src/innerclasses/Sequence.java new file mode 100644 index 0000000..f4a7bb3 --- /dev/null +++ b/src/innerclasses/Sequence.java @@ -0,0 +1,44 @@ +package innerclasses;//: innerclasses/Sequence.java +// Holds a sequence of Objects. + +interface Selector { + boolean end(); + Object current(); + void next(); +} + +public class Sequence { + private Object[] items; + private int next = 0; + public Sequence(int size) { items = new Object[size]; } + public void add(Object x) { + if(next < items.length) { + items[next++] = x; + } + } + private class SequenceSelector implements Selector { + private int i = 0; + public boolean end() { return i == items.length; } + public Object current() { return items[i]; } + public void next() { if(i < items.length) { + i++; + } + } + } + public Selector selector() { + return new SequenceSelector(); + } + public static void main(String[] args) { + Sequence sequence = new Sequence(10); + for(int i = 0; i < 10; i++) { + sequence.add(Integer.toString(i)); + } + Selector selector = sequence.selector(); + while(!selector.end()) { + System.out.print(selector.current() + " "); + selector.next(); + } + } +} /* Output: +0 1 2 3 4 5 6 7 8 9 +*///:~ diff --git a/src/innerclasses/TestBed.java b/src/innerclasses/TestBed.java new file mode 100644 index 0000000..a0fa163 --- /dev/null +++ b/src/innerclasses/TestBed.java @@ -0,0 +1,15 @@ +package innerclasses;//: innerclasses/TestBed.java +// Putting test code in a nested class. +// {main: TestBed$Tester} + +public class TestBed { + public void f() { System.out.println("f()"); } + public static class Tester { + public static void main(String[] args) { + TestBed t = new TestBed(); + t.f(); + } + } +} /* Output: +f() +*///:~ diff --git a/src/innerclasses/TestParcel.java b/src/innerclasses/TestParcel.java new file mode 100644 index 0000000..38e24d4 --- /dev/null +++ b/src/innerclasses/TestParcel.java @@ -0,0 +1,31 @@ +package innerclasses;//: innerclasses/TestParcel.java + +class Parcel4 { + private class PContents implements Contents { + private int i = 11; + public int value() { return i; } + } + protected class PDestination implements Destination { + private String label; + private PDestination(String whereTo) { + label = whereTo; + } + public String readLabel() { return label; } + } + public Destination destination(String s) { + return new PDestination(s); + } + public Contents contents() { + return new PContents(); + } +} + +public class TestParcel { + public static void main(String[] args) { + Parcel4 p = new Parcel4(); + Contents c = p.contents(); + Destination d = p.destination("Tasmania"); + // Illegal -- can't access private class: + //! Parcel4.PContents pc = p.new PContents(); + } +} ///:~ diff --git a/src/innerclasses/Wrapping.java b/src/innerclasses/Wrapping.java new file mode 100644 index 0000000..f16bef1 --- /dev/null +++ b/src/innerclasses/Wrapping.java @@ -0,0 +1,8 @@ +package innerclasses; + +//: innerclasses/Wrapping.java +public class Wrapping { + private int i; + public Wrapping(int x) { i = x; } + public int value() { return i; } +} ///:~ diff --git a/src/innerclasses/build.xml b/src/innerclasses/build.xml new file mode 100644 index 0000000..7a93d22 --- /dev/null +++ b/src/innerclasses/build.xml @@ -0,0 +1,362 @@ + + + + + + build.xml for the source code for the innerclasses chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/innerclasses/controller/Controller.java b/src/innerclasses/controller/Controller.java new file mode 100644 index 0000000..885235d --- /dev/null +++ b/src/innerclasses/controller/Controller.java @@ -0,0 +1,24 @@ +//: innerclasses/controller/Controller.java +// The reusable framework for control systems. +package innerclasses.controller; +import java.util.*; + +public class Controller { + // A class from java.util to hold Event objects: + private List eventList = new ArrayList(); + public void addEvent(Event c) { eventList.add(c); } + public void run() { + while(eventList.size() > 0) + // Make a copy so you're not modifying the list + // while you're selecting the elements in it: + { + for(Event e : new ArrayList(eventList)) { + if (e.ready()) { + System.out.println(e); + e.action(); + eventList.remove(e); + } + } + } + } +} ///:~ diff --git a/src/innerclasses/controller/Event.java b/src/innerclasses/controller/Event.java new file mode 100644 index 0000000..93a5ad9 --- /dev/null +++ b/src/innerclasses/controller/Event.java @@ -0,0 +1,19 @@ +//: innerclasses/controller/Event.java +// The common methods for any control event. +package innerclasses.controller; + +public abstract class Event { + private long eventTime; + protected final long delayTime; + public Event(long delayTime) { + this.delayTime = delayTime; + start(); + } + public void start() { // Allows restarting + eventTime = System.nanoTime() + delayTime; + } + public boolean ready() { + return System.nanoTime() >= eventTime; + } + public abstract void action(); +} ///:~ diff --git a/src/interfaces/AdaptedRandomDoubles.java b/src/interfaces/AdaptedRandomDoubles.java new file mode 100644 index 0000000..8429cd8 --- /dev/null +++ b/src/interfaces/AdaptedRandomDoubles.java @@ -0,0 +1,28 @@ +package interfaces;//: interfaces/AdaptedRandomDoubles.java +// Creating an adapter with inheritance. +import java.nio.*; +import java.util.*; + +public class AdaptedRandomDoubles extends RandomDoubles +implements Readable { + private int count; + public AdaptedRandomDoubles(int count) { + this.count = count; + } + public int read(CharBuffer cb) { + if(count-- == 0) { + return -1; + } + String result = Double.toString(next()) + " "; + cb.append(result); + return result.length(); + } + public static void main(String[] args) { + Scanner s = new Scanner(new AdaptedRandomDoubles(7)); + while(s.hasNextDouble()) { + System.out.print(s.nextDouble() + " "); + } + } +} /* Output: +0.7271157860730044 0.5309454508634242 0.16020656493302599 0.18847866977771732 0.5166020801268457 0.2678662084200585 0.2613610344283964 +*///:~ diff --git a/src/interfaces/Adventure.java b/src/interfaces/Adventure.java new file mode 100644 index 0000000..12d425c --- /dev/null +++ b/src/interfaces/Adventure.java @@ -0,0 +1,38 @@ +package interfaces;//: interfaces/Adventure.java +// Multiple interfaces. + +interface CanFight { + void fight(); +} + +interface CanSwim { + void swim(); +} + +interface CanFly { + void fly(); +} + +class ActionCharacter { + public void fight() {} +} + +class Hero extends ActionCharacter + implements CanFight, CanSwim, CanFly { + public void swim() {} + public void fly() {} +} + +public class Adventure { + public static void t(CanFight x) { x.fight(); } + public static void u(CanSwim x) { x.swim(); } + public static void v(CanFly x) { x.fly(); } + public static void w(ActionCharacter x) { x.fight(); } + public static void main(String[] args) { + Hero h = new Hero(); + t(h); // Treat it as a CanFight + u(h); // Treat it as a CanSwim + v(h); // Treat it as a CanFly + w(h); // Treat it as an ActionCharacter + } +} ///:~ diff --git a/src/interfaces/Factories.java b/src/interfaces/Factories.java new file mode 100644 index 0000000..a1d27d0 --- /dev/null +++ b/src/interfaces/Factories.java @@ -0,0 +1,53 @@ +package interfaces;//: interfaces/Factories.java +import static net.mindview.util.Print.*; + +interface Service { + void method1(); + void method2(); +} + +interface ServiceFactory { + Service getService(); +} + +class Implementation1 implements Service { + Implementation1() {} // Package access + public void method1() {print("Implementation1 method1");} + public void method2() {print("Implementation1 method2");} +} + +class Implementation1Factory implements ServiceFactory { + public Service getService() { + return new Implementation1(); + } +} + +class Implementation2 implements Service { + Implementation2() {} // Package access + public void method1() {print("Implementation2 method1");} + public void method2() {print("Implementation2 method2");} +} + +class Implementation2Factory implements ServiceFactory { + public Service getService() { + return new Implementation2(); + } +} + +public class Factories { + public static void serviceConsumer(ServiceFactory fact) { + Service s = fact.getService(); + s.method1(); + s.method2(); + } + public static void main(String[] args) { + serviceConsumer(new Implementation1Factory()); + // Implementations are completely interchangeable: + serviceConsumer(new Implementation2Factory()); + } +} /* Output: +Implementation1 method1 +Implementation1 method2 +Implementation2 method1 +Implementation2 method2 +*///:~ diff --git a/src/interfaces/Games.java b/src/interfaces/Games.java new file mode 100644 index 0000000..bfed80d --- /dev/null +++ b/src/interfaces/Games.java @@ -0,0 +1,52 @@ +package interfaces;//: interfaces/Games.java +// A Game framework using Factory Methods. +import static net.mindview.util.Print.*; + +interface Game { boolean move(); } +interface GameFactory { Game getGame(); } + +class Checkers implements Game { + private int moves = 0; + private static final int MOVES = 3; + public boolean move() { + print("Checkers move " + moves); + return ++moves != MOVES; + } +} + +class CheckersFactory implements GameFactory { + public Game getGame() { return new Checkers(); } +} + +class Chess implements Game { + private int moves = 0; + private static final int MOVES = 4; + public boolean move() { + print("Chess move " + moves); + return ++moves != MOVES; + } +} + +class ChessFactory implements GameFactory { + public Game getGame() { return new Chess(); } +} + +public class Games { + public static void playGame(GameFactory factory) { + Game s = factory.getGame(); + while(s.move()) { + } + } + public static void main(String[] args) { + playGame(new CheckersFactory()); + playGame(new ChessFactory()); + } +} /* Output: +Checkers move 0 +Checkers move 1 +Checkers move 2 +Chess move 0 +Chess move 1 +Chess move 2 +Chess move 3 +*///:~ diff --git a/src/interfaces/HorrorShow.java b/src/interfaces/HorrorShow.java new file mode 100644 index 0000000..fb0149e --- /dev/null +++ b/src/interfaces/HorrorShow.java @@ -0,0 +1,48 @@ +package interfaces;//: interfaces/HorrorShow.java +// Extending an interface with inheritance. + +interface Monster { + void menace(); +} + +interface DangerousMonster extends Monster { + void destroy(); +} + +interface Lethal { + void kill(); +} + +class DragonZilla implements DangerousMonster { + public void menace() {} + public void destroy() {} +} + +interface Vampire extends DangerousMonster, Lethal { + void drinkBlood(); +} + +class VeryBadVampire implements Vampire { + public void menace() {} + public void destroy() {} + public void kill() {} + public void drinkBlood() {} +} + +public class HorrorShow { + static void u(Monster b) { b.menace(); } + static void v(DangerousMonster d) { + d.menace(); + d.destroy(); + } + static void w(Lethal l) { l.kill(); } + public static void main(String[] args) { + DangerousMonster barney = new DragonZilla(); + u(barney); + v(barney); + Vampire vlad = new VeryBadVampire(); + u(vlad); + v(vlad); + w(vlad); + } +} ///:~ diff --git a/src/interfaces/InterfaceCollision.java b/src/interfaces/InterfaceCollision.java new file mode 100644 index 0000000..d9a9e1e --- /dev/null +++ b/src/interfaces/InterfaceCollision.java @@ -0,0 +1,25 @@ +//: interfaces/InterfaceCollision.java +package interfaces; + +interface I1 { void f(); } +interface I2 { int f(int i); } +interface I3 { int f(); } +class C { public int f() { return 1; } } + +class C2 implements I1, I2 { + public void f() {} + public int f(int i) { return 1; } // overloaded +} + +class C3 extends C implements I2 { + public int f(int i) { return 1; } // overloaded +} + +class C4 extends C implements I3 { + // Identical, no problem: + public int f() { return 1; } +} + +// Methods differ only by return type: +//! class C5 extends C implements I1 {} +//! interface I4 extends I1, I3 {} ///:~ diff --git a/src/interfaces/Months.java b/src/interfaces/Months.java new file mode 100644 index 0000000..ac53864 --- /dev/null +++ b/src/interfaces/Months.java @@ -0,0 +1,11 @@ +//: interfaces/Months.java +// Using interfaces to create groups of constants. +package interfaces; + +public interface Months { + int + JANUARY = 1, FEBRUARY = 2, MARCH = 3, + APRIL = 4, MAY = 5, JUNE = 6, JULY = 7, + AUGUST = 8, SEPTEMBER = 9, OCTOBER = 10, + NOVEMBER = 11, DECEMBER = 12; +} ///:~ diff --git a/src/interfaces/RandVals.java b/src/interfaces/RandVals.java new file mode 100644 index 0000000..aa480fc --- /dev/null +++ b/src/interfaces/RandVals.java @@ -0,0 +1,12 @@ +package interfaces;//: interfaces/RandVals.java +// Initializing interface fields with +// non-constant initializers. +import java.util.*; + +public interface RandVals { + Random RAND = new Random(47); + int RANDOM_INT = RAND.nextInt(10); + long RANDOM_LONG = RAND.nextLong() * 10; + float RANDOM_FLOAT = RAND.nextLong() * 10; + double RANDOM_DOUBLE = RAND.nextDouble() * 10; +} ///:~ diff --git a/src/interfaces/RandomDoubles.java b/src/interfaces/RandomDoubles.java new file mode 100644 index 0000000..d5bddf8 --- /dev/null +++ b/src/interfaces/RandomDoubles.java @@ -0,0 +1,15 @@ +package interfaces;//: interfaces/RandomDoubles.java +import java.util.*; + +public class RandomDoubles { + private static Random rand = new Random(47); + public double next() { return rand.nextDouble(); } + public static void main(String[] args) { + RandomDoubles rd = new RandomDoubles(); + for(int i = 0; i < 7; i ++) { + System.out.print(rd.next() + " "); + } + } +} /* Output: +0.7271157860730044 0.5309454508634242 0.16020656493302599 0.18847866977771732 0.5166020801268457 0.2678662084200585 0.2613610344283964 +*///:~ diff --git a/src/interfaces/RandomWords.java b/src/interfaces/RandomWords.java new file mode 100644 index 0000000..4dd71be --- /dev/null +++ b/src/interfaces/RandomWords.java @@ -0,0 +1,45 @@ +package interfaces;//: interfaces/RandomWords.java +// Implementing an interface to conform to a method. +import java.nio.*; +import java.util.*; + +public class RandomWords implements Readable { + private static Random rand = new Random(47); + private static final char[] capitals = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); + private static final char[] lowers = + "abcdefghijklmnopqrstuvwxyz".toCharArray(); + private static final char[] vowels = + "aeiou".toCharArray(); + private int count; + public RandomWords(int count) { this.count = count; } + public int read(CharBuffer cb) { + if(count-- == 0) { + return -1; // Indicates end of input + } + cb.append(capitals[rand.nextInt(capitals.length)]); + for(int i = 0; i < 4; i++) { + cb.append(vowels[rand.nextInt(vowels.length)]); + cb.append(lowers[rand.nextInt(lowers.length)]); + } + cb.append(" "); + return 10; // Number of characters appended + } + public static void main(String[] args) { + Scanner s = new Scanner(new RandomWords(10)); + while(s.hasNext()) { + System.out.println(s.next()); + } + } +} /* Output: +Yazeruyac +Fowenucor +Goeazimom +Raeuuacio +Nuoadesiw +Hageaikux +Ruqicibui +Numasetih +Kuuuuozog +Waqizeyoy +*///:~ diff --git a/src/interfaces/TestRandVals.java b/src/interfaces/TestRandVals.java new file mode 100644 index 0000000..e2f2fd8 --- /dev/null +++ b/src/interfaces/TestRandVals.java @@ -0,0 +1,16 @@ +package interfaces;//: interfaces/TestRandVals.java +import static net.mindview.util.Print.*; + +public class TestRandVals { + public static void main(String[] args) { + print(RandVals.RANDOM_INT); + print(RandVals.RANDOM_LONG); + print(RandVals.RANDOM_FLOAT); + print(RandVals.RANDOM_DOUBLE); + } +} /* Output: +8 +-32032247016559954 +-8.5939291E18 +5.779976127815049 +*///:~ diff --git a/src/interfaces/build.xml b/src/interfaces/build.xml new file mode 100644 index 0000000..2bd67a1 --- /dev/null +++ b/src/interfaces/build.xml @@ -0,0 +1,219 @@ + + + + + + build.xml for the source code for the interfaces chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/interfaces/classprocessor/Apply.java b/src/interfaces/classprocessor/Apply.java new file mode 100644 index 0000000..0b6609e --- /dev/null +++ b/src/interfaces/classprocessor/Apply.java @@ -0,0 +1,51 @@ +//: interfaces/classprocessor/Apply.java +package interfaces.classprocessor; +import java.util.*; +import static net.mindview.util.Print.*; + +class Processor { + public String name() { + return getClass().getSimpleName(); + } + Object process(Object input) { return input; } +} + +class Upcase extends Processor { + String process(Object input) { // Covariant return + return ((String)input).toUpperCase(); + } +} + +class Downcase extends Processor { + String process(Object input) { + return ((String)input).toLowerCase(); + } +} + +class Splitter extends Processor { + String process(Object input) { + // The split() argument divides a String into pieces: + return Arrays.toString(((String)input).split(" ")); + } +} + +public class Apply { + public static void process(Processor p, Object s) { + print("Using Processor " + p.name()); + print(p.process(s)); + } + public static String s = + "Disagreement with beliefs is by definition incorrect"; + public static void main(String[] args) { + process(new Upcase(), s); + process(new Downcase(), s); + process(new Splitter(), s); + } +} /* Output: +Using Processor Upcase +DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT +Using Processor Downcase +disagreement with beliefs is by definition incorrect +Using Processor Splitter +[Disagreement, with, beliefs, is, by, definition, incorrect] +*///:~ diff --git a/src/interfaces/filters/BandPass.java b/src/interfaces/filters/BandPass.java new file mode 100644 index 0000000..b949b1f --- /dev/null +++ b/src/interfaces/filters/BandPass.java @@ -0,0 +1,11 @@ +//: interfaces/filters/BandPass.java +package interfaces.filters; + +public class BandPass extends Filter { + double lowCutoff, highCutoff; + public BandPass(double lowCut, double highCut) { + lowCutoff = lowCut; + highCutoff = highCut; + } + public Waveform process(Waveform input) { return input; } +} ///:~ diff --git a/src/interfaces/filters/Filter.java b/src/interfaces/filters/Filter.java new file mode 100644 index 0000000..5a0bcba --- /dev/null +++ b/src/interfaces/filters/Filter.java @@ -0,0 +1,9 @@ +//: interfaces/filters/Filter.java +package interfaces.filters; + +public class Filter { + public String name() { + return getClass().getSimpleName(); + } + public Waveform process(Waveform input) { return input; } +} ///:~ diff --git a/src/interfaces/filters/HighPass.java b/src/interfaces/filters/HighPass.java new file mode 100644 index 0000000..b4983ae --- /dev/null +++ b/src/interfaces/filters/HighPass.java @@ -0,0 +1,8 @@ +//: interfaces/filters/HighPass.java +package interfaces.filters; + +public class HighPass extends Filter { + double cutoff; + public HighPass(double cutoff) { this.cutoff = cutoff; } + public Waveform process(Waveform input) { return input; } +} ///:~ diff --git a/src/interfaces/filters/LowPass.java b/src/interfaces/filters/LowPass.java new file mode 100644 index 0000000..00d671e --- /dev/null +++ b/src/interfaces/filters/LowPass.java @@ -0,0 +1,10 @@ +//: interfaces/filters/LowPass.java +package interfaces.filters; + +public class LowPass extends Filter { + double cutoff; + public LowPass(double cutoff) { this.cutoff = cutoff; } + public Waveform process(Waveform input) { + return input; // Dummy processing + } +} ///:~ diff --git a/src/interfaces/filters/Waveform.java b/src/interfaces/filters/Waveform.java new file mode 100644 index 0000000..adccc4d --- /dev/null +++ b/src/interfaces/filters/Waveform.java @@ -0,0 +1,8 @@ +//: interfaces/filters/Waveform.java +package interfaces.filters; + +public class Waveform { + private static long counter; + private final long id = counter++; + public String toString() { return "Waveform " + id; } +} ///:~ diff --git a/src/interfaces/interfaceprocessor/Apply.java b/src/interfaces/interfaceprocessor/Apply.java new file mode 100644 index 0000000..11fc922 --- /dev/null +++ b/src/interfaces/interfaceprocessor/Apply.java @@ -0,0 +1,10 @@ +//: interfaces/interfaceprocessor/Apply.java +package interfaces.interfaceprocessor; +import static net.mindview.util.Print.*; + +public class Apply { + public static void process(Processor p, Object s) { + print("Using Processor " + p.name()); + print(p.process(s)); + } +} ///:~ diff --git a/src/interfaces/interfaceprocessor/FilterProcessor.java b/src/interfaces/interfaceprocessor/FilterProcessor.java new file mode 100644 index 0000000..a2b6dc5 --- /dev/null +++ b/src/interfaces/interfaceprocessor/FilterProcessor.java @@ -0,0 +1,31 @@ +//: interfaces/interfaceprocessor/FilterProcessor.java +package interfaces.interfaceprocessor; +import interfaces.filters.*; + +class FilterAdapter implements Processor { + Filter filter; + public FilterAdapter(Filter filter) { + this.filter = filter; + } + public String name() { return filter.name(); } + public Waveform process(Object input) { + return filter.process((Waveform)input); + } +} + +public class FilterProcessor { + public static void main(String[] args) { + Waveform w = new Waveform(); + Apply.process(new FilterAdapter(new LowPass(1.0)), w); + Apply.process(new FilterAdapter(new HighPass(2.0)), w); + Apply.process( + new FilterAdapter(new BandPass(3.0, 4.0)), w); + } +} /* Output: +Using Processor LowPass +Waveform 0 +Using Processor HighPass +Waveform 0 +Using Processor BandPass +Waveform 0 +*///:~ diff --git a/src/interfaces/interfaceprocessor/Processor.java b/src/interfaces/interfaceprocessor/Processor.java new file mode 100644 index 0000000..8787ea4 --- /dev/null +++ b/src/interfaces/interfaceprocessor/Processor.java @@ -0,0 +1,7 @@ +//: interfaces/interfaceprocessor/Processor.java +package interfaces.interfaceprocessor; + +public interface Processor { + String name(); + Object process(Object input); +} ///:~ diff --git a/src/interfaces/interfaceprocessor/StringProcessor.java b/src/interfaces/interfaceprocessor/StringProcessor.java new file mode 100644 index 0000000..ff3ee08 --- /dev/null +++ b/src/interfaces/interfaceprocessor/StringProcessor.java @@ -0,0 +1,42 @@ +//: interfaces/interfaceprocessor/StringProcessor.java +package interfaces.interfaceprocessor; +import java.util.*; + +public abstract class StringProcessor implements Processor{ + public String name() { + return getClass().getSimpleName(); + } + public abstract String process(Object input); + public static String s = + "If she weighs the same as a duck, she's made of wood"; + public static void main(String[] args) { + Apply.process(new Upcase(), s); + Apply.process(new Downcase(), s); + Apply.process(new Splitter(), s); + } +} + +class Upcase extends StringProcessor { + public String process(Object input) { // Covariant return + return ((String)input).toUpperCase(); + } +} + +class Downcase extends StringProcessor { + public String process(Object input) { + return ((String)input).toLowerCase(); + } +} + +class Splitter extends StringProcessor { + public String process(Object input) { + return Arrays.toString(((String)input).split(" ")); + } +} /* Output: +Using Processor Upcase +IF SHE WEIGHS THE SAME AS A DUCK, SHE'S MADE OF WOOD +Using Processor Downcase +if she weighs the same as a duck, she's made of wood +Using Processor Splitter +[If, she, weighs, the, same, as, a, duck,, she's, made, of, wood] +*///:~ diff --git a/src/interfaces/music4/Music4.java b/src/interfaces/music4/Music4.java new file mode 100644 index 0000000..078c2df --- /dev/null +++ b/src/interfaces/music4/Music4.java @@ -0,0 +1,81 @@ +//: interfaces/music4/Music4.java +// Abstract classes and methods. +package interfaces.music4; +import polymorphism.music.Note; +import static net.mindview.util.Print.*; + +abstract class Instrument { + private int i; // Storage allocated for each + public abstract void play(Note n); + public String what() { return "Instrument"; } + public abstract void adjust(); +} + +class Wind extends Instrument { + public void play(Note n) { + print("Wind.play() " + n); + } + public String what() { return "Wind"; } + public void adjust() {} +} + +class Percussion extends Instrument { + public void play(Note n) { + print("Percussion.play() " + n); + } + public String what() { return "Percussion"; } + public void adjust() {} +} + +class Stringed extends Instrument { + public void play(Note n) { + print("Stringed.play() " + n); + } + public String what() { return "Stringed"; } + public void adjust() {} +} + +class Brass extends Wind { + public void play(Note n) { + print("Brass.play() " + n); + } + public void adjust() { print("Brass.adjust()"); } +} + +class Woodwind extends Wind { + public void play(Note n) { + print("Woodwind.play() " + n); + } + public String what() { return "Woodwind"; } +} + +public class Music4 { + // Doesn't care about type, so new types + // added to the system still work right: + static void tune(Instrument i) { + // ... + i.play(Note.MIDDLE_C); + } + static void tuneAll(Instrument[] e) { + for(Instrument i : e) { + tune(i); + } + } + public static void main(String[] args) { + // Upcasting during addition to the array: + Instrument[] orchestra = { + new Wind(), + new Percussion(), + new Stringed(), + new Brass(), + new Woodwind() + }; + tuneAll(orchestra); + } +} /* Output: +Wind.play() MIDDLE_C +Percussion.play() MIDDLE_C +Stringed.play() MIDDLE_C +Brass.play() MIDDLE_C +Woodwind.play() MIDDLE_C +*///:~ diff --git a/src/interfaces/music5/Music5.java b/src/interfaces/music5/Music5.java new file mode 100644 index 0000000..67a036f --- /dev/null +++ b/src/interfaces/music5/Music5.java @@ -0,0 +1,76 @@ +//: interfaces/music5/Music5.java +// Interfaces. +package interfaces.music5; +import polymorphism.music.Note; +import static net.mindview.util.Print.*; + +interface Instrument { + // Compile-time constant: + int VALUE = 5; // static & final + // Cannot have method definitions: + void play(Note n); // Automatically public + void adjust(); +} + +class Wind implements Instrument { + public void play(Note n) { + print(this + ".play() " + n); + } + public String toString() { return "Wind"; } + public void adjust() { print(this + ".adjust()"); } +} + +class Percussion implements Instrument { + public void play(Note n) { + print(this + ".play() " + n); + } + public String toString() { return "Percussion"; } + public void adjust() { print(this + ".adjust()"); } +} + +class Stringed implements Instrument { + public void play(Note n) { + print(this + ".play() " + n); + } + public String toString() { return "Stringed"; } + public void adjust() { print(this + ".adjust()"); } +} + +class Brass extends Wind { + public String toString() { return "Brass"; } +} + +class Woodwind extends Wind { + public String toString() { return "Woodwind"; } +} + +public class Music5 { + // Doesn't care about type, so new types + // added to the system still work right: + static void tune(Instrument i) { + // ... + i.play(Note.MIDDLE_C); + } + static void tuneAll(Instrument[] e) { + for(Instrument i : e) { + tune(i); + } + } + public static void main(String[] args) { + // Upcasting during addition to the array: + Instrument[] orchestra = { + new Wind(), + new Percussion(), + new Stringed(), + new Brass(), + new Woodwind() + }; + tuneAll(orchestra); + } +} /* Output: +Wind.play() MIDDLE_C +Percussion.play() MIDDLE_C +Stringed.play() MIDDLE_C +Brass.play() MIDDLE_C +Woodwind.play() MIDDLE_C +*///:~ diff --git a/src/interfaces/nesting/NestingInterfaces.java b/src/interfaces/nesting/NestingInterfaces.java new file mode 100644 index 0000000..121a9c9 --- /dev/null +++ b/src/interfaces/nesting/NestingInterfaces.java @@ -0,0 +1,89 @@ +//: interfaces/nesting/NestingInterfaces.java +package interfaces.nesting; + +class A { + interface B { + void f(); + } + public class BImp implements B { + public void f() {} + } + private class BImp2 implements B { + public void f() {} + } + public interface C { + void f(); + } + class CImp implements C { + public void f() {} + } + private class CImp2 implements C { + public void f() {} + } + private interface D { + void f(); + } + private class DImp implements D { + public void f() {} + } + public class DImp2 implements D { + public void f() {} + } + public D getD() { return new DImp2(); } + private D dRef; + public void receiveD(D d) { + dRef = d; + dRef.f(); + } +} + +interface E { + interface G { + void f(); + } + // Redundant "public": + public interface H { + void f(); + } + void g(); + // Cannot be private within an interface: + //! private interface I {} +} + +public class NestingInterfaces { + public class BImp implements A.B { + public void f() {} + } + class CImp implements A.C { + public void f() {} + } + // Cannot implement a private interface except + // within that interface's defining class: + //! class DImp implements A.D { + //! public void f() {} + //! } + class EImp implements E { + public void g() {} + } + class EGImp implements E.G { + public void f() {} + } + class EImp2 implements E { + public void g() {} + class EG implements E.G { + public void f() {} + } + } + public static void main(String[] args) { + A a = new A(); + // Can't access A.D: + //! A.D ad = a.getD(); + // Doesn't return anything but A.D: + //! A.DImp2 di2 = a.getD(); + // Cannot access a member of the interface: + //! a.getD().f(); + // Only another A can do anything with getD(): + A a2 = new A(); + a2.receiveD(a.getD()); + } +} ///:~ diff --git a/src/io/Alien.java b/src/io/Alien.java new file mode 100644 index 0000000..5a84044 --- /dev/null +++ b/src/io/Alien.java @@ -0,0 +1,4 @@ +package io;//: io/Alien.java +// A serializable class. +import java.io.*; +public class Alien implements Serializable {} ///:~ diff --git a/src/io/AvailableCharSets.java b/src/io/AvailableCharSets.java new file mode 100644 index 0000000..46aba7f --- /dev/null +++ b/src/io/AvailableCharSets.java @@ -0,0 +1,38 @@ +package io;//: io/AvailableCharSets.java +// Displays Charsets and aliases +import java.nio.charset.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class AvailableCharSets { + public static void main(String[] args) { + SortedMap charSets = + Charset.availableCharsets(); + Iterator it = charSets.keySet().iterator(); + while(it.hasNext()) { + String csName = it.next(); + printnb(csName); + Iterator aliases = + charSets.get(csName).aliases().iterator(); + if(aliases.hasNext()) { + printnb(": "); + } + while(aliases.hasNext()) { + printnb(aliases.next()); + if(aliases.hasNext()) { + printnb(", "); + } + } + print(); + } + } +} /* Output: +Big5: csBig5 +Big5-HKSCS: big5-hkscs, big5hk, big5-hkscs:unicode3.0, big5hkscs, Big5_HKSCS +EUC-JP: eucjis, x-eucjp, csEUCPkdFmtjapanese, eucjp, Extended_UNIX_Code_Packed_Format_for_Japanese, x-euc-jp, euc_jp +EUC-KR: ksc5601, 5601, ksc5601_1987, ksc_5601, ksc5601-1987, euc_kr, ks_c_5601-1987, euckr, csEUCKR +GB18030: gb18030-2000 +GB2312: gb2312-1980, gb2312, EUC_CN, gb2312-80, euc-cn, euccn, x-EUC-CN +GBK: windows-936, CP936 +... +*///:~ diff --git a/src/io/BasicFileOutput.java b/src/io/BasicFileOutput.java new file mode 100644 index 0000000..9971609 --- /dev/null +++ b/src/io/BasicFileOutput.java @@ -0,0 +1,22 @@ +package io;//: io/BasicFileOutput.java +import java.io.*; + +public class BasicFileOutput { + static String file = "BasicFileOutput.out"; + public static void main(String[] args) + throws IOException { + BufferedReader in = new BufferedReader( + new StringReader( + BufferedInputFile.read("BasicFileOutput.java"))); + PrintWriter out = new PrintWriter( + new BufferedWriter(new FileWriter(file))); + int lineCount = 1; + String s; + while((s = in.readLine()) != null ) { + out.println(lineCount++ + ": " + s); + } + out.close(); + // Show the stored file: + System.out.println(BufferedInputFile.read(file)); + } +} /* (Execute to see output) *///:~ diff --git a/src/io/Blip3.java b/src/io/Blip3.java new file mode 100644 index 0000000..ee0ffac --- /dev/null +++ b/src/io/Blip3.java @@ -0,0 +1,61 @@ +package io;//: io/Blip3.java +// Reconstructing an externalizable object. +import java.io.*; +import static net.mindview.util.Print.*; + +public class Blip3 implements Externalizable { + private int i; + private String s; // No initialization + public Blip3() { + print("Blip3 Constructor"); + // s, i not initialized + } + public Blip3(String x, int a) { + print("Blip3(String x, int a)"); + s = x; + i = a; + // s & i initialized only in non-default constructor. + } + public String toString() { return s + i; } + public void writeExternal(ObjectOutput out) + throws IOException { + print("Blip3.writeExternal"); + // You must do this: + out.writeObject(s); + out.writeInt(i); + } + public void readExternal(ObjectInput in) + throws IOException, ClassNotFoundException { + print("Blip3.readExternal"); + // You must do this: + s = (String)in.readObject(); + i = in.readInt(); + } + public static void main(String[] args) + throws IOException, ClassNotFoundException { + print("Constructing objects:"); + Blip3 b3 = new Blip3("A String ", 47); + print(b3); + ObjectOutputStream o = new ObjectOutputStream( + new FileOutputStream("Blip3.out")); + print("Saving object:"); + o.writeObject(b3); + o.close(); + // Now get it back: + ObjectInputStream in = new ObjectInputStream( + new FileInputStream("Blip3.out")); + print("Recovering b3:"); + b3 = (Blip3)in.readObject(); + print(b3); + } +} /* Output: +Constructing objects: +Blip3(String x, int a) +A String 47 +Saving object: +Blip3.writeExternal +Recovering b3: +Blip3 Constructor +Blip3.readExternal +A String 47 +*///:~ diff --git a/src/io/Blips.java b/src/io/Blips.java new file mode 100644 index 0000000..3e2fcb9 --- /dev/null +++ b/src/io/Blips.java @@ -0,0 +1,65 @@ +package io;//: io/Blips.java +// Simple use of Externalizable & a pitfall. +import java.io.*; +import static net.mindview.util.Print.*; + +class Blip1 implements Externalizable { + public Blip1() { + print("Blip1 Constructor"); + } + public void writeExternal(ObjectOutput out) + throws IOException { + print("Blip1.writeExternal"); + } + public void readExternal(ObjectInput in) + throws IOException, ClassNotFoundException { + print("Blip1.readExternal"); + } +} + +class Blip2 implements Externalizable { + Blip2() { + print("Blip2 Constructor"); + } + public void writeExternal(ObjectOutput out) + throws IOException { + print("Blip2.writeExternal"); + } + public void readExternal(ObjectInput in) + throws IOException, ClassNotFoundException { + print("Blip2.readExternal"); + } +} + +public class Blips { + public static void main(String[] args) + throws IOException, ClassNotFoundException { + print("Constructing objects:"); + Blip1 b1 = new Blip1(); + Blip2 b2 = new Blip2(); + ObjectOutputStream o = new ObjectOutputStream( + new FileOutputStream("Blips.out")); + print("Saving objects:"); + o.writeObject(b1); + o.writeObject(b2); + o.close(); + // Now get them back: + ObjectInputStream in = new ObjectInputStream( + new FileInputStream("Blips.out")); + print("Recovering b1:"); + b1 = (Blip1)in.readObject(); + // OOPS! Throws an exception: +//! print("Recovering b2:"); +//! b2 = (Blip2)in.readObject(); + } +} /* Output: +Constructing objects: +Blip1 Constructor +Blip2 Constructor +Saving objects: +Blip1.writeExternal +Blip2.writeExternal +Recovering b1: +Blip1 Constructor +Blip1.readExternal +*///:~ diff --git a/src/io/BufferToText.java b/src/io/BufferToText.java new file mode 100644 index 0000000..6adcd63 --- /dev/null +++ b/src/io/BufferToText.java @@ -0,0 +1,55 @@ +package io;//: io/BufferToText.java +// Converting text to and from ByteBuffers +import java.nio.*; +import java.nio.channels.*; +import java.nio.charset.*; +import java.io.*; + +public class BufferToText { + private static final int BSIZE = 1024; + public static void main(String[] args) throws Exception { + FileChannel fc = + new FileOutputStream("data2.txt").getChannel(); + fc.write(ByteBuffer.wrap("Some text".getBytes())); + fc.close(); + fc = new FileInputStream("data2.txt").getChannel(); + ByteBuffer buff = ByteBuffer.allocate(BSIZE); + fc.read(buff); + buff.flip(); + // Doesn't work: + System.out.println(buff.asCharBuffer()); + // Decode using this system's default Charset: + buff.rewind(); + String encoding = System.getProperty("file.encoding"); + System.out.println("Decoded using " + encoding + ": " + + Charset.forName(encoding).decode(buff)); + // Or, we could encode with something that will print: + fc = new FileOutputStream("data2.txt").getChannel(); + fc.write(ByteBuffer.wrap( + "Some text".getBytes("UTF-16BE"))); + fc.close(); + // Now try reading again: + fc = new FileInputStream("data2.txt").getChannel(); + buff.clear(); + fc.read(buff); + buff.flip(); + System.out.println(buff.asCharBuffer()); + // Use a CharBuffer to write through: + fc = new FileOutputStream("data2.txt").getChannel(); + buff = ByteBuffer.allocate(24); // More than needed + buff.asCharBuffer().put("Some text"); + fc.write(buff); + fc.close(); + // Read and display: + fc = new FileInputStream("data2.txt").getChannel(); + buff.clear(); + fc.read(buff); + buff.flip(); + System.out.println(buff.asCharBuffer()); + } +} /* Output: +???? +Decoded using Cp1252: Some text +Some text +Some text +*///:~ diff --git a/src/io/BufferedInputFile.java b/src/io/BufferedInputFile.java new file mode 100644 index 0000000..ce23341 --- /dev/null +++ b/src/io/BufferedInputFile.java @@ -0,0 +1,23 @@ +package io;//: io/BufferedInputFile.java +import java.io.*; + +public class BufferedInputFile { + // Throw exceptions to console: + public static String + read(String filename) throws IOException { + // Reading input by lines: + BufferedReader in = new BufferedReader( + new FileReader(filename)); + String s; + StringBuilder sb = new StringBuilder(); + while((s = in.readLine())!= null) { + sb.append(s + "\n"); + } + in.close(); + return sb.toString(); + } + public static void main(String[] args) + throws IOException { + System.out.print(read("BufferedInputFile.java")); + } +} /* (Execute to see output) *///:~ diff --git a/src/io/ChangeSystemOut.java b/src/io/ChangeSystemOut.java new file mode 100644 index 0000000..1eeec88 --- /dev/null +++ b/src/io/ChangeSystemOut.java @@ -0,0 +1,12 @@ +package io;//: io/ChangeSystemOut.java +// Turn System.out into a PrintWriter. +import java.io.*; + +public class ChangeSystemOut { + public static void main(String[] args) { + PrintWriter out = new PrintWriter(System.out, true); + out.println("Hello, world"); + } +} /* Output: +Hello, world +*///:~ diff --git a/src/io/ChannelCopy.java b/src/io/ChannelCopy.java new file mode 100644 index 0000000..c5519a1 --- /dev/null +++ b/src/io/ChannelCopy.java @@ -0,0 +1,25 @@ +package io;//: io/ChannelCopy.java +// Copying a file using channels and buffers +// {Args: ChannelCopy.java test.txt} +import java.nio.*; +import java.nio.channels.*; +import java.io.*; + +public class ChannelCopy { + private static final int BSIZE = 1024; + public static void main(String[] args) throws Exception { + if(args.length != 2) { + System.out.println("arguments: sourcefile destfile"); + System.exit(1); + } + FileChannel + in = new FileInputStream(args[0]).getChannel(), + out = new FileOutputStream(args[1]).getChannel(); + ByteBuffer buffer = ByteBuffer.allocate(BSIZE); + while(in.read(buffer) != -1) { + buffer.flip(); // Prepare for writing + out.write(buffer); + buffer.clear(); // Prepare for reading + } + } +} ///:~ diff --git a/src/io/DirList.java b/src/io/DirList.java new file mode 100644 index 0000000..0635323 --- /dev/null +++ b/src/io/DirList.java @@ -0,0 +1,37 @@ +package io;//: io/DirList.java +// Display a directory listing using regular expressions. +// {Args: "D.*\.java"} +import java.util.regex.*; +import java.io.*; +import java.util.*; + +public class DirList { + public static void main(String[] args) { + File path = new File("."); + String[] list; + if(args.length == 0) { + list = path.list(); + } else { + list = path.list(new DirFilter(args[0])); + } + Arrays.sort(list, String.CASE_INSENSITIVE_ORDER); + for(String dirItem : list) { + System.out.println(dirItem); + } + } +} + +class DirFilter implements FilenameFilter { + private Pattern pattern; + public DirFilter(String regex) { + pattern = Pattern.compile(regex); + } + public boolean accept(File dir, String name) { + return pattern.matcher(name).matches(); + } +} /* Output: +DirectoryDemo.java +DirList.java +DirList2.java +DirList3.java +*///:~ diff --git a/src/io/DirList2.java b/src/io/DirList2.java new file mode 100644 index 0000000..bc5b35c --- /dev/null +++ b/src/io/DirList2.java @@ -0,0 +1,36 @@ +package io;//: io/DirList2.java +// Uses anonymous inner classes. +// {Args: "D.*\.java"} +import java.util.regex.*; +import java.io.*; +import java.util.*; + +public class DirList2 { + public static FilenameFilter filter(final String regex) { + // Creation of anonymous inner class: + return new FilenameFilter() { + private Pattern pattern = Pattern.compile(regex); + public boolean accept(File dir, String name) { + return pattern.matcher(name).matches(); + } + }; // End of anonymous inner class + } + public static void main(String[] args) { + File path = new File("."); + String[] list; + if(args.length == 0) { + list = path.list(); + } else { + list = path.list(filter(args[0])); + } + Arrays.sort(list, String.CASE_INSENSITIVE_ORDER); + for(String dirItem : list) { + System.out.println(dirItem); + } + } +} /* Output: +DirectoryDemo.java +DirList.java +DirList2.java +DirList3.java +*///:~ diff --git a/src/io/DirList3.java b/src/io/DirList3.java new file mode 100644 index 0000000..bdba2bb --- /dev/null +++ b/src/io/DirList3.java @@ -0,0 +1,32 @@ +package io;//: io/DirList3.java +// Building the anonymous inner class "in-place." +// {Args: "D.*\.java"} +import java.util.regex.*; +import java.io.*; +import java.util.*; + +public class DirList3 { + public static void main(final String[] args) { + File path = new File("."); + String[] list; + if(args.length == 0) { + list = path.list(); + } else { + list = path.list(new FilenameFilter() { + private Pattern pattern = Pattern.compile(args[0]); + public boolean accept(File dir, String name) { + return pattern.matcher(name).matches(); + } + }); + } + Arrays.sort(list, String.CASE_INSENSITIVE_ORDER); + for(String dirItem : list) { + System.out.println(dirItem); + } + } +} /* Output: +DirectoryDemo.java +DirList.java +DirList2.java +DirList3.java +*///:~ diff --git a/src/io/DirectoryDemo.java b/src/io/DirectoryDemo.java new file mode 100644 index 0000000..d6b31fe --- /dev/null +++ b/src/io/DirectoryDemo.java @@ -0,0 +1,40 @@ +package io;//: io/DirectoryDemo.java +// Sample use of Directory utilities. +import java.io.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class DirectoryDemo { + public static void main(String[] args) { + // All directories: + PPrint.pprint(Directory.walk(".").dirs); + // All files beginning with 'T' + for(File file : Directory.local(".", "T.*")) { + print(file); + } + print("----------------------"); + // All Java files beginning with 'T': + for(File file : Directory.walk(".", "T.*\\.java")) { + print(file); + } + print("======================"); + // Class files containing "Z" or "z": + for(File file : Directory.walk(".",".*[Zz].*\\.class")) { + print(file); + } + } +} /* Output: (Sample) +[.\xfiles] +.\TestEOF.class +.\TestEOF.java +.\TransferTo.class +.\TransferTo.java +---------------------- +.\TestEOF.java +.\TransferTo.java +.\xfiles\ThawAlien.java +====================== +.\FreezeAlien.class +.\GZIPcompress.class +.\ZipCompress.class +*///:~ diff --git a/src/io/Echo.java b/src/io/Echo.java new file mode 100644 index 0000000..9f2f499 --- /dev/null +++ b/src/io/Echo.java @@ -0,0 +1,17 @@ +package io;//: io/Echo.java +// How to read from standard input. +// {RunByHand} +import java.io.*; + +public class Echo { + public static void main(String[] args) + throws IOException { + BufferedReader stdin = new BufferedReader( + new InputStreamReader(System.in)); + String s; + while((s = stdin.readLine()) != null && s.length()!= 0) { + System.out.println(s); + } + // An empty line or Ctrl-Z terminates the program + } +} ///:~ diff --git a/src/io/Endians.java b/src/io/Endians.java new file mode 100644 index 0000000..4523188 --- /dev/null +++ b/src/io/Endians.java @@ -0,0 +1,25 @@ +package io;//: io/Endians.java +// Endian differences and data storage. +import java.nio.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class Endians { + public static void main(String[] args) { + ByteBuffer bb = ByteBuffer.wrap(new byte[12]); + bb.asCharBuffer().put("abcdef"); + print(Arrays.toString(bb.array())); + bb.rewind(); + bb.order(ByteOrder.BIG_ENDIAN); + bb.asCharBuffer().put("abcdef"); + print(Arrays.toString(bb.array())); + bb.rewind(); + bb.order(ByteOrder.LITTLE_ENDIAN); + bb.asCharBuffer().put("abcdef"); + print(Arrays.toString(bb.array())); + } +} /* Output: +[0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102] +[0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102] +[97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0] +*///:~ diff --git a/src/io/FileLocking.java b/src/io/FileLocking.java new file mode 100644 index 0000000..6132767 --- /dev/null +++ b/src/io/FileLocking.java @@ -0,0 +1,21 @@ +package io;//: io/FileLocking.java +import java.nio.channels.*; +import java.util.concurrent.*; +import java.io.*; + +public class FileLocking { + public static void main(String[] args) throws Exception { + FileOutputStream fos= new FileOutputStream("file.txt"); + FileLock fl = fos.getChannel().tryLock(); + if(fl != null) { + System.out.println("Locked File"); + TimeUnit.MILLISECONDS.sleep(100); + fl.release(); + System.out.println("Released Lock"); + } + fos.close(); + } +} /* Output: +Locked File +Released Lock +*///:~ diff --git a/src/io/FileOutputShortcut.java b/src/io/FileOutputShortcut.java new file mode 100644 index 0000000..297e78b --- /dev/null +++ b/src/io/FileOutputShortcut.java @@ -0,0 +1,22 @@ +package io;//: io/FileOutputShortcut.java +import java.io.*; + +public class FileOutputShortcut { + static String file = "FileOutputShortcut.out"; + public static void main(String[] args) + throws IOException { + BufferedReader in = new BufferedReader( + new StringReader( + BufferedInputFile.read("FileOutputShortcut.java"))); + // Here's the shortcut: + PrintWriter out = new PrintWriter(file); + int lineCount = 1; + String s; + while((s = in.readLine()) != null ) { + out.println(lineCount++ + ": " + s); + } + out.close(); + // Show the stored file: + System.out.println(BufferedInputFile.read(file)); + } +} /* (Execute to see output) *///:~ diff --git a/src/io/FormattedMemoryInput.java b/src/io/FormattedMemoryInput.java new file mode 100644 index 0000000..38fcb85 --- /dev/null +++ b/src/io/FormattedMemoryInput.java @@ -0,0 +1,19 @@ +package io;//: io/FormattedMemoryInput.java +import java.io.*; + +public class FormattedMemoryInput { + public static void main(String[] args) + throws IOException { + try { + DataInputStream in = new DataInputStream( + new ByteArrayInputStream( + BufferedInputFile.read( + "FormattedMemoryInput.java").getBytes())); + while(true) { + System.out.print((char)in.readByte()); + } + } catch(EOFException e) { + System.err.println("End of stream"); + } + } +} /* (Execute to see output) *///:~ diff --git a/src/io/FreezeAlien.java b/src/io/FreezeAlien.java new file mode 100644 index 0000000..e09dee6 --- /dev/null +++ b/src/io/FreezeAlien.java @@ -0,0 +1,12 @@ +package io;//: io/FreezeAlien.java +// Create a serialized output file. +import java.io.*; + +public class FreezeAlien { + public static void main(String[] args) throws Exception { + ObjectOutput out = new ObjectOutputStream( + new FileOutputStream("X.file")); + Alien quellek = new Alien(); + out.writeObject(quellek); + } +} ///:~ diff --git a/src/io/GZIPcompress.java b/src/io/GZIPcompress.java new file mode 100644 index 0000000..2de64da --- /dev/null +++ b/src/io/GZIPcompress.java @@ -0,0 +1,37 @@ +package io;//: io/GZIPcompress.java +// {Args: GZIPcompress.java} +import java.util.zip.*; +import java.io.*; + +public class GZIPcompress { + public static void main(String[] args) + throws IOException { + if(args.length == 0) { + System.out.println( + "Usage: \nGZIPcompress file\n" + + "\tUses GZIP compression to compress " + + "the file to test.gz"); + System.exit(1); + } + BufferedReader in = new BufferedReader( + new FileReader(args[0])); + BufferedOutputStream out = new BufferedOutputStream( + new GZIPOutputStream( + new FileOutputStream("test.gz"))); + System.out.println("Writing file"); + int c; + while((c = in.read()) != -1) { + out.write(c); + } + in.close(); + out.close(); + System.out.println("Reading file"); + BufferedReader in2 = new BufferedReader( + new InputStreamReader(new GZIPInputStream( + new FileInputStream("test.gz")))); + String s; + while((s = in2.readLine()) != null) { + System.out.println(s); + } + } +} /* (Execute to see output) *///:~ diff --git a/src/io/GetChannel.java b/src/io/GetChannel.java new file mode 100644 index 0000000..1c072f9 --- /dev/null +++ b/src/io/GetChannel.java @@ -0,0 +1,32 @@ +package io;//: io/GetChannel.java +// Getting channels from streams +import java.nio.*; +import java.nio.channels.*; +import java.io.*; + +public class GetChannel { + private static final int BSIZE = 1024; + public static void main(String[] args) throws Exception { + // Write a file: + FileChannel fc = + new FileOutputStream("data.txt").getChannel(); + fc.write(ByteBuffer.wrap("Some text ".getBytes())); + fc.close(); + // Add to the end of the file: + fc = + new RandomAccessFile("data.txt", "rw").getChannel(); + fc.position(fc.size()); // Move to the end + fc.write(ByteBuffer.wrap("Some more".getBytes())); + fc.close(); + // Read the file: + fc = new FileInputStream("data.txt").getChannel(); + ByteBuffer buff = ByteBuffer.allocate(BSIZE); + fc.read(buff); + buff.flip(); + while(buff.hasRemaining()) { + System.out.print((char)buff.get()); + } + } +} /* Output: +Some text Some more +*///:~ diff --git a/src/io/GetData.java b/src/io/GetData.java new file mode 100644 index 0000000..1a94ee4 --- /dev/null +++ b/src/io/GetData.java @@ -0,0 +1,56 @@ +package io;//: io/GetData.java +// Getting different representations from a ByteBuffer +import java.nio.*; +import static net.mindview.util.Print.*; + +public class GetData { + private static final int BSIZE = 1024; + public static void main(String[] args) { + ByteBuffer bb = ByteBuffer.allocate(BSIZE); + // Allocation automatically zeroes the ByteBuffer: + int i = 0; + while(i++ < bb.limit()) { + if(bb.get() != 0) { + print("nonzero"); + } + } + print("i = " + i); + bb.rewind(); + // Store and read a char array: + bb.asCharBuffer().put("Howdy!"); + char c; + while((c = bb.getChar()) != 0) { + printnb(c + " "); + } + print(); + bb.rewind(); + // Store and read a short: + bb.asShortBuffer().put((short)471142); + print(bb.getShort()); + bb.rewind(); + // Store and read an int: + bb.asIntBuffer().put(99471142); + print(bb.getInt()); + bb.rewind(); + // Store and read a long: + bb.asLongBuffer().put(99471142); + print(bb.getLong()); + bb.rewind(); + // Store and read a float: + bb.asFloatBuffer().put(99471142); + print(bb.getFloat()); + bb.rewind(); + // Store and read a double: + bb.asDoubleBuffer().put(99471142); + print(bb.getDouble()); + bb.rewind(); + } +} /* Output: +i = 1025 +H o w d y ! +12390 +99471142 +99471142 +9.9471144E7 +9.9471142E7 +*///:~ diff --git a/src/io/IntBufferDemo.java b/src/io/IntBufferDemo.java new file mode 100644 index 0000000..f95d5ea --- /dev/null +++ b/src/io/IntBufferDemo.java @@ -0,0 +1,31 @@ +package io;//: io/IntBufferDemo.java +// Manipulating ints in a ByteBuffer with an IntBuffer +import java.nio.*; + +public class IntBufferDemo { + private static final int BSIZE = 1024; + public static void main(String[] args) { + ByteBuffer bb = ByteBuffer.allocate(BSIZE); + IntBuffer ib = bb.asIntBuffer(); + // Store an array of int: + ib.put(new int[]{ 11, 42, 47, 99, 143, 811, 1016 }); + // Absolute location read and write: + System.out.println(ib.get(3)); + ib.put(3, 1811); + // Setting a new limit before rewinding the buffer. + ib.flip(); + while(ib.hasRemaining()) { + int i = ib.get(); + System.out.println(i); + } + } +} /* Output: +99 +11 +42 +47 +1811 +143 +811 +1016 +*///:~ diff --git a/src/io/LargeMappedFiles.java b/src/io/LargeMappedFiles.java new file mode 100644 index 0000000..527fcf8 --- /dev/null +++ b/src/io/LargeMappedFiles.java @@ -0,0 +1,23 @@ +package io;//: io/LargeMappedFiles.java +// Creating a very large file using mapping. +// {RunByHand} +import java.nio.*; +import java.nio.channels.*; +import java.io.*; +import static net.mindview.util.Print.*; + +public class LargeMappedFiles { + static int length = 0x8FFFFFF; // 128 MB + public static void main(String[] args) throws Exception { + MappedByteBuffer out = + new RandomAccessFile("test.dat", "rw").getChannel() + .map(FileChannel.MapMode.READ_WRITE, 0, length); + for(int i = 0; i < length; i++) { + out.put((byte)'x'); + } + print("Finished writing"); + for(int i = length/2; i < length/2 + 6; i++) { + printnb((char)out.get(i)); + } + } +} ///:~ diff --git a/src/io/LockingMappedFiles.java b/src/io/LockingMappedFiles.java new file mode 100644 index 0000000..cc14804 --- /dev/null +++ b/src/io/LockingMappedFiles.java @@ -0,0 +1,49 @@ +package io;//: io/LockingMappedFiles.java +// Locking portions of a mapped file. +// {RunByHand} +import java.nio.*; +import java.nio.channels.*; +import java.io.*; + +public class LockingMappedFiles { + static final int LENGTH = 0x8FFFFFF; // 128 MB + static FileChannel fc; + public static void main(String[] args) throws Exception { + fc = + new RandomAccessFile("test.dat", "rw").getChannel(); + MappedByteBuffer out = + fc.map(FileChannel.MapMode.READ_WRITE, 0, LENGTH); + for(int i = 0; i < LENGTH; i++) { + out.put((byte)'x'); + } + new LockAndModify(out, 0, 0 + LENGTH/3); + new LockAndModify(out, LENGTH/2, LENGTH/2 + LENGTH/4); + } + private static class LockAndModify extends Thread { + private ByteBuffer buff; + private int start, end; + LockAndModify(ByteBuffer mbb, int start, int end) { + this.start = start; + this.end = end; + mbb.limit(end); + mbb.position(start); + buff = mbb.slice(); + start(); + } + public void run() { + try { + // Exclusive lock with no overlap: + FileLock fl = fc.lock(start, end, false); + System.out.println("Locked: "+ start +" to "+ end); + // Perform modification: + while(buff.position() < buff.limit() - 1) { + buff.put((byte)(buff.get() + 1)); + } + fl.release(); + System.out.println("Released: "+start+" to "+ end); + } catch(IOException e) { + throw new RuntimeException(e); + } + } + } +} ///:~ diff --git a/src/io/Logon.java b/src/io/Logon.java new file mode 100644 index 0000000..234ffea --- /dev/null +++ b/src/io/Logon.java @@ -0,0 +1,45 @@ +package io;//: io/Logon.java +// Demonstrates the "transient" keyword. +import java.util.concurrent.*; +import java.io.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class Logon implements Serializable { + private Date date = new Date(); + private String username; + private transient String password; + public Logon(String name, String pwd) { + username = name; + password = pwd; + } + public String toString() { + return "logon info: \n username: " + username + + "\n date: " + date + "\n password: " + password; + } + public static void main(String[] args) throws Exception { + Logon a = new Logon("Hulk", "myLittlePony"); + print("logon a = " + a); + ObjectOutputStream o = new ObjectOutputStream( + new FileOutputStream("Logon.out")); + o.writeObject(a); + o.close(); + TimeUnit.SECONDS.sleep(1); // Delay + // Now get them back: + ObjectInputStream in = new ObjectInputStream( + new FileInputStream("Logon.out")); + print("Recovering object at " + new Date()); + a = (Logon)in.readObject(); + print("logon a = " + a); + } +} /* Output: (Sample) +logon a = logon info: + username: Hulk + date: Sat Nov 19 15:03:26 MST 2005 + password: myLittlePony +Recovering object at Sat Nov 19 15:03:28 MST 2005 +logon a = logon info: + username: Hulk + date: Sat Nov 19 15:03:26 MST 2005 + password: null +*///:~ diff --git a/src/io/MakeDirectories.java b/src/io/MakeDirectories.java new file mode 100644 index 0000000..db8f0bb --- /dev/null +++ b/src/io/MakeDirectories.java @@ -0,0 +1,86 @@ +package io;//: io/MakeDirectories.java +// Demonstrates the use of the File class to +// create directories and manipulate files. +// {Args: MakeDirectoriesTest} +import java.io.*; + +public class MakeDirectories { + private static void usage() { + System.err.println( + "Usage:MakeDirectories path1 ...\n" + + "Creates each path\n" + + "Usage:MakeDirectories -d path1 ...\n" + + "Deletes each path\n" + + "Usage:MakeDirectories -r path1 path2\n" + + "Renames from path1 to path2"); + System.exit(1); + } + private static void fileData(File f) { + System.out.println( + "Absolute path: " + f.getAbsolutePath() + + "\n Can read: " + f.canRead() + + "\n Can write: " + f.canWrite() + + "\n getName: " + f.getName() + + "\n getParent: " + f.getParent() + + "\n getPath: " + f.getPath() + + "\n length: " + f.length() + + "\n lastModified: " + f.lastModified()); + if(f.isFile()) { + System.out.println("It's a file"); + } else if(f.isDirectory()) { + System.out.println("It's a directory"); + } + } + public static void main(String[] args) { + if(args.length < 1) { + usage(); + } + if(args[0].equals("-r")) { + if(args.length != 3) { + usage(); + } + File + old = new File(args[1]), + rname = new File(args[2]); + old.renameTo(rname); + fileData(old); + fileData(rname); + return; // Exit main + } + int count = 0; + boolean del = false; + if(args[0].equals("-d")) { + count++; + del = true; + } + count--; + while(++count < args.length) { + File f = new File(args[count]); + if(f.exists()) { + System.out.println(f + " exists"); + if(del) { + System.out.println("deleting..." + f); + f.delete(); + } + } + else { // Doesn't exist + if(!del) { + f.mkdirs(); + System.out.println("created " + f); + } + } + fileData(f); + } + } +} /* Output: (80% match) +created MakeDirectoriesTest +Absolute path: d:\aaa-TIJ4\code\io\MakeDirectoriesTest + Can read: true + Can write: true + getName: MakeDirectoriesTest + getParent: null + getPath: MakeDirectoriesTest + length: 0 + lastModified: 1101690308831 +It's a directory +*///:~ diff --git a/src/io/MappedIO.java b/src/io/MappedIO.java new file mode 100644 index 0000000..e5296f8 --- /dev/null +++ b/src/io/MappedIO.java @@ -0,0 +1,114 @@ +package io;//: io/MappedIO.java +import java.nio.*; +import java.nio.channels.*; +import java.io.*; + +public class MappedIO { + private static int numOfInts = 4000000; + private static int numOfUbuffInts = 200000; + private abstract static class Tester { + private String name; + public Tester(String name) { this.name = name; } + public void runTest() { + System.out.print(name + ": "); + try { + long start = System.nanoTime(); + test(); + double duration = System.nanoTime() - start; + System.out.format("%.2f\n", duration/1.0e9); + } catch(IOException e) { + throw new RuntimeException(e); + } + } + public abstract void test() throws IOException; + } + private static Tester[] tests = { + new Tester("Stream Write") { + public void test() throws IOException { + DataOutputStream dos = new DataOutputStream( + new BufferedOutputStream( + new FileOutputStream(new File("temp.tmp")))); + for(int i = 0; i < numOfInts; i++) { + dos.writeInt(i); + } + dos.close(); + } + }, + new Tester("Mapped Write") { + public void test() throws IOException { + FileChannel fc = + new RandomAccessFile("temp.tmp", "rw") + .getChannel(); + IntBuffer ib = fc.map( + FileChannel.MapMode.READ_WRITE, 0, fc.size()) + .asIntBuffer(); + for(int i = 0; i < numOfInts; i++) { + ib.put(i); + } + fc.close(); + } + }, + new Tester("Stream Read") { + public void test() throws IOException { + DataInputStream dis = new DataInputStream( + new BufferedInputStream( + new FileInputStream("temp.tmp"))); + for(int i = 0; i < numOfInts; i++) { + dis.readInt(); + } + dis.close(); + } + }, + new Tester("Mapped Read") { + public void test() throws IOException { + FileChannel fc = new FileInputStream( + new File("temp.tmp")).getChannel(); + IntBuffer ib = fc.map( + FileChannel.MapMode.READ_ONLY, 0, fc.size()) + .asIntBuffer(); + while(ib.hasRemaining()) { + ib.get(); + } + fc.close(); + } + }, + new Tester("Stream Read/Write") { + public void test() throws IOException { + RandomAccessFile raf = new RandomAccessFile( + new File("temp.tmp"), "rw"); + raf.writeInt(1); + for(int i = 0; i < numOfUbuffInts; i++) { + raf.seek(raf.length() - 4); + raf.writeInt(raf.readInt()); + } + raf.close(); + } + }, + new Tester("Mapped Read/Write") { + public void test() throws IOException { + FileChannel fc = new RandomAccessFile( + new File("temp.tmp"), "rw").getChannel(); + IntBuffer ib = fc.map( + FileChannel.MapMode.READ_WRITE, 0, fc.size()) + .asIntBuffer(); + ib.put(0); + for(int i = 1; i < numOfUbuffInts; i++) { + ib.put(ib.get(i - 1)); + } + fc.close(); + } + } + }; + public static void main(String[] args) { + for(Tester test : tests) { + test.runTest(); + } + } +} /* Output: (90% match) +Stream Write: 0.56 +Mapped Write: 0.12 +Stream Read: 0.80 +Mapped Read: 0.07 +Stream Read/Write: 5.32 +Mapped Read/Write: 0.02 +*///:~ diff --git a/src/io/MemoryInput.java b/src/io/MemoryInput.java new file mode 100644 index 0000000..a9e7687 --- /dev/null +++ b/src/io/MemoryInput.java @@ -0,0 +1,14 @@ +package io;//: io/MemoryInput.java +import java.io.*; + +public class MemoryInput { + public static void main(String[] args) + throws IOException { + StringReader in = new StringReader( + BufferedInputFile.read("MemoryInput.java")); + int c; + while((c = in.read()) != -1) { + System.out.print((char)c); + } + } +} /* (Execute to see output) *///:~ diff --git a/src/io/MyWorld.java b/src/io/MyWorld.java new file mode 100644 index 0000000..9925f65 --- /dev/null +++ b/src/io/MyWorld.java @@ -0,0 +1,70 @@ +package io;//: io/MyWorld.java +import java.io.*; +import java.util.*; +import static net.mindview.util.Print.*; + +class House implements Serializable {} + +class Animal implements Serializable { + private String name; + private House preferredHouse; + Animal(String nm, House h) { + name = nm; + preferredHouse = h; + } + public String toString() { + return name + "[" + super.toString() + + "], " + preferredHouse + "\n"; + } +} + +public class MyWorld { + public static void main(String[] args) + throws IOException, ClassNotFoundException { + House house = new House(); + List animals = new ArrayList(); + animals.add(new Animal("Bosco the dog", house)); + animals.add(new Animal("Ralph the hamster", house)); + animals.add(new Animal("Molly the cat", house)); + print("animals: " + animals); + ByteArrayOutputStream buf1 = + new ByteArrayOutputStream(); + ObjectOutputStream o1 = new ObjectOutputStream(buf1); + o1.writeObject(animals); + o1.writeObject(animals); // Write a 2nd set + // Write to a different stream: + ByteArrayOutputStream buf2 = + new ByteArrayOutputStream(); + ObjectOutputStream o2 = new ObjectOutputStream(buf2); + o2.writeObject(animals); + // Now get them back: + ObjectInputStream in1 = new ObjectInputStream( + new ByteArrayInputStream(buf1.toByteArray())); + ObjectInputStream in2 = new ObjectInputStream( + new ByteArrayInputStream(buf2.toByteArray())); + List + animals1 = (List)in1.readObject(), + animals2 = (List)in1.readObject(), + animals3 = (List)in2.readObject(); + print("animals1: " + animals1); + print("animals2: " + animals2); + print("animals3: " + animals3); + } +} /* Output: (Sample) +animals: [Bosco the dog[Animal@addbf1], House@42e816 +, Ralph the hamster[Animal@9304b1], House@42e816 +, Molly the cat[Animal@190d11], House@42e816 +] +animals1: [Bosco the dog[Animal@de6f34], House@156ee8e +, Ralph the hamster[Animal@47b480], House@156ee8e +, Molly the cat[Animal@19b49e6], House@156ee8e +] +animals2: [Bosco the dog[Animal@de6f34], House@156ee8e +, Ralph the hamster[Animal@47b480], House@156ee8e +, Molly the cat[Animal@19b49e6], House@156ee8e +] +animals3: [Bosco the dog[Animal@10d448], House@e0e1c6 +, Ralph the hamster[Animal@6ca1c], House@e0e1c6 +, Molly the cat[Animal@1bf216a], House@e0e1c6 +] +*///:~ diff --git a/src/io/OSExecuteDemo.java b/src/io/OSExecuteDemo.java new file mode 100644 index 0000000..bb33217 --- /dev/null +++ b/src/io/OSExecuteDemo.java @@ -0,0 +1,15 @@ +package io;//: io/OSExecuteDemo.java +// Demonstrates standard I/O redirection. +import net.mindview.util.*; + +public class OSExecuteDemo { + public static void main(String[] args) { + OSExecute.command("javap OSExecuteDemo"); + } +} /* Output: +Compiled from "OSExecuteDemo.java" +public class OSExecuteDemo extends java.lang.Object{ + public OSExecuteDemo(); + public static void main(java.lang.String[]); +} +*///:~ diff --git a/src/io/PreferencesDemo.java b/src/io/PreferencesDemo.java new file mode 100644 index 0000000..9a9fa61 --- /dev/null +++ b/src/io/PreferencesDemo.java @@ -0,0 +1,30 @@ +package io;//: io/PreferencesDemo.java +import java.util.prefs.*; +import static net.mindview.util.Print.*; + +public class PreferencesDemo { + public static void main(String[] args) throws Exception { + Preferences prefs = Preferences + .userNodeForPackage(PreferencesDemo.class); + prefs.put("Location", "Oz"); + prefs.put("Footwear", "Ruby Slippers"); + prefs.putInt("Companions", 4); + prefs.putBoolean("Are there witches?", true); + int usageCount = prefs.getInt("UsageCount", 0); + usageCount++; + prefs.putInt("UsageCount", usageCount); + for(String key : prefs.keys()) { + print(key + ": "+ prefs.get(key, null)); + } + // You must always provide a default value: + print("How many companions does Dorothy have? " + + prefs.getInt("Companions", 0)); + } +} /* Output: (Sample) +Location: Oz +Footwear: Ruby Slippers +Companions: 4 +Are there witches?: true +UsageCount: 53 +How many companions does Dorothy have? 4 +*///:~ diff --git a/src/io/RecoverCADState.java b/src/io/RecoverCADState.java new file mode 100644 index 0000000..199bd57 --- /dev/null +++ b/src/io/RecoverCADState.java @@ -0,0 +1,31 @@ +package io;//: io/RecoverCADState.java +// Restoring the state of the pretend CAD system. +// {RunFirst: StoreCADState} +import java.io.*; +import java.util.*; + +public class RecoverCADState { + @SuppressWarnings("unchecked") + public static void main(String[] args) throws Exception { + ObjectInputStream in = new ObjectInputStream( + new FileInputStream("CADState.out")); + // Read in the same order they were written: + List> shapeTypes = + (List>)in.readObject(); + Line.deserializeStaticState(in); + List shapes = (List)in.readObject(); + System.out.println(shapes); + } +} /* Output: +[class Circlecolor[1] xPos[58] yPos[55] dim[93] +, class Squarecolor[0] xPos[61] yPos[61] dim[29] +, class Linecolor[3] xPos[68] yPos[0] dim[22] +, class Circlecolor[1] xPos[7] yPos[88] dim[28] +, class Squarecolor[0] xPos[51] yPos[89] dim[9] +, class Linecolor[3] xPos[78] yPos[98] dim[61] +, class Circlecolor[1] xPos[20] yPos[58] dim[16] +, class Squarecolor[0] xPos[40] yPos[11] dim[22] +, class Linecolor[3] xPos[4] yPos[83] dim[6] +, class Circlecolor[1] xPos[75] yPos[10] dim[42] +] +*///:~ diff --git a/src/io/Redirecting.java b/src/io/Redirecting.java new file mode 100644 index 0000000..800109f --- /dev/null +++ b/src/io/Redirecting.java @@ -0,0 +1,26 @@ +package io;//: io/Redirecting.java +// Demonstrates standard I/O redirection. +import java.io.*; + +public class Redirecting { + public static void main(String[] args) + throws IOException { + PrintStream console = System.out; + BufferedInputStream in = new BufferedInputStream( + new FileInputStream("Redirecting.java")); + PrintStream out = new PrintStream( + new BufferedOutputStream( + new FileOutputStream("test.out"))); + System.setIn(in); + System.setOut(out); + System.setErr(out); + BufferedReader br = new BufferedReader( + new InputStreamReader(System.in)); + String s; + while((s = br.readLine()) != null) { + System.out.println(s); + } + out.close(); // Remember this! + System.setOut(console); + } +} ///:~ diff --git a/src/io/SerialCtl.java b/src/io/SerialCtl.java new file mode 100644 index 0000000..a45149e --- /dev/null +++ b/src/io/SerialCtl.java @@ -0,0 +1,44 @@ +package io;//: io/SerialCtl.java +// Controlling serialization by adding your own +// writeObject() and readObject() methods. +import java.io.*; + +public class SerialCtl implements Serializable { + private String a; + private transient String b; + public SerialCtl(String aa, String bb) { + a = "Not Transient: " + aa; + b = "Transient: " + bb; + } + public String toString() { return a + "\n" + b; } + private void writeObject(ObjectOutputStream stream) + throws IOException { + stream.defaultWriteObject(); + stream.writeObject(b); + } + private void readObject(ObjectInputStream stream) + throws IOException, ClassNotFoundException { + stream.defaultReadObject(); + b = (String)stream.readObject(); + } + public static void main(String[] args) + throws IOException, ClassNotFoundException { + SerialCtl sc = new SerialCtl("Test1", "Test2"); + System.out.println("Before:\n" + sc); + ByteArrayOutputStream buf= new ByteArrayOutputStream(); + ObjectOutputStream o = new ObjectOutputStream(buf); + o.writeObject(sc); + // Now get it back: + ObjectInputStream in = new ObjectInputStream( + new ByteArrayInputStream(buf.toByteArray())); + SerialCtl sc2 = (SerialCtl)in.readObject(); + System.out.println("After:\n" + sc2); + } +} /* Output: +Before: +Not Transient: Test1 +Transient: Test2 +After: +Not Transient: Test1 +Transient: Test2 +*///:~ diff --git a/src/io/StoreCADState.java b/src/io/StoreCADState.java new file mode 100644 index 0000000..86831ba --- /dev/null +++ b/src/io/StoreCADState.java @@ -0,0 +1,108 @@ +package io;//: io/StoreCADState.java +// Saving the state of a pretend CAD system. +import java.io.*; +import java.util.*; + +abstract class Shape implements Serializable { + public static final int RED = 1, BLUE = 2, GREEN = 3; + private int xPos, yPos, dimension; + private static Random rand = new Random(47); + private static int counter = 0; + public abstract void setColor(int newColor); + public abstract int getColor(); + public Shape(int xVal, int yVal, int dim) { + xPos = xVal; + yPos = yVal; + dimension = dim; + } + public String toString() { + return getClass() + + "color[" + getColor() + "] xPos[" + xPos + + "] yPos[" + yPos + "] dim[" + dimension + "]\n"; + } + public static Shape randomFactory() { + int xVal = rand.nextInt(100); + int yVal = rand.nextInt(100); + int dim = rand.nextInt(100); + switch(counter++ % 3) { + default: + case 0: return new Circle(xVal, yVal, dim); + case 1: return new Square(xVal, yVal, dim); + case 2: return new Line(xVal, yVal, dim); + } + } +} + +class Circle extends Shape { + private static int color = RED; + public Circle(int xVal, int yVal, int dim) { + super(xVal, yVal, dim); + } + public void setColor(int newColor) { color = newColor; } + public int getColor() { return color; } +} + +class Square extends Shape { + private static int color; + public Square(int xVal, int yVal, int dim) { + super(xVal, yVal, dim); + color = RED; + } + public void setColor(int newColor) { color = newColor; } + public int getColor() { return color; } +} + +class Line extends Shape { + private static int color = RED; + public static void + serializeStaticState(ObjectOutputStream os) + throws IOException { os.writeInt(color); } + public static void + deserializeStaticState(ObjectInputStream os) + throws IOException { color = os.readInt(); } + public Line(int xVal, int yVal, int dim) { + super(xVal, yVal, dim); + } + public void setColor(int newColor) { color = newColor; } + public int getColor() { return color; } +} + +public class StoreCADState { + public static void main(String[] args) throws Exception { + List> shapeTypes = + new ArrayList>(); + // Add references to the class objects: + shapeTypes.add(Circle.class); + shapeTypes.add(Square.class); + shapeTypes.add(Line.class); + List shapes = new ArrayList(); + // Make some shapes: + for(int i = 0; i < 10; i++) { + shapes.add(Shape.randomFactory()); + } + // Set all the static colors to GREEN: + for(int i = 0; i < 10; i++) { + ((Shape)shapes.get(i)).setColor(Shape.GREEN); + } + // Save the state vector: + ObjectOutputStream out = new ObjectOutputStream( + new FileOutputStream("CADState.out")); + out.writeObject(shapeTypes); + Line.serializeStaticState(out); + out.writeObject(shapes); + // Display the shapes: + System.out.println(shapes); + } +} /* Output: +[class Circlecolor[3] xPos[58] yPos[55] dim[93] +, class Squarecolor[3] xPos[61] yPos[61] dim[29] +, class Linecolor[3] xPos[68] yPos[0] dim[22] +, class Circlecolor[3] xPos[7] yPos[88] dim[28] +, class Squarecolor[3] xPos[51] yPos[89] dim[9] +, class Linecolor[3] xPos[78] yPos[98] dim[61] +, class Circlecolor[3] xPos[20] yPos[58] dim[16] +, class Squarecolor[3] xPos[40] yPos[11] dim[22] +, class Linecolor[3] xPos[4] yPos[83] dim[6] +, class Circlecolor[3] xPos[75] yPos[10] dim[42] +] +*///:~ diff --git a/src/io/StoringAndRecoveringData.java b/src/io/StoringAndRecoveringData.java new file mode 100644 index 0000000..c68a63e --- /dev/null +++ b/src/io/StoringAndRecoveringData.java @@ -0,0 +1,30 @@ +package io;//: io/StoringAndRecoveringData.java +import java.io.*; + +public class StoringAndRecoveringData { + public static void main(String[] args) + throws IOException { + DataOutputStream out = new DataOutputStream( + new BufferedOutputStream( + new FileOutputStream("Data.txt"))); + out.writeDouble(3.14159); + out.writeUTF("That was pi"); + out.writeDouble(1.41413); + out.writeUTF("Square root of 2"); + out.close(); + DataInputStream in = new DataInputStream( + new BufferedInputStream( + new FileInputStream("Data.txt"))); + System.out.println(in.readDouble()); + // Only readUTF() will recover the + // Java-UTF String properly: + System.out.println(in.readUTF()); + System.out.println(in.readDouble()); + System.out.println(in.readUTF()); + } +} /* Output: +3.14159 +That was pi +1.41413 +Square root of 2 +*///:~ diff --git a/src/io/TestEOF.java b/src/io/TestEOF.java new file mode 100644 index 0000000..86f366c --- /dev/null +++ b/src/io/TestEOF.java @@ -0,0 +1,15 @@ +package io;//: io/TestEOF.java +// Testing for end of file while reading a byte at a time. +import java.io.*; + +public class TestEOF { + public static void main(String[] args) + throws IOException { + DataInputStream in = new DataInputStream( + new BufferedInputStream( + new FileInputStream("TestEOF.java"))); + while(in.available() != 0) { + System.out.print((char)in.readByte()); + } + } +} /* (Execute to see output) *///:~ diff --git a/src/io/TransferTo.java b/src/io/TransferTo.java new file mode 100644 index 0000000..9e3a40c --- /dev/null +++ b/src/io/TransferTo.java @@ -0,0 +1,20 @@ +package io;//: io/TransferTo.java +// Using transferTo() between channels +// {Args: TransferTo.java TransferTo.txt} +import java.nio.channels.*; +import java.io.*; + +public class TransferTo { + public static void main(String[] args) throws Exception { + if(args.length != 2) { + System.out.println("arguments: sourcefile destfile"); + System.exit(1); + } + FileChannel + in = new FileInputStream(args[0]).getChannel(), + out = new FileOutputStream(args[1]).getChannel(); + in.transferTo(0, in.size(), out); + // Or: + // out.transferFrom(in, 0, in.size()); + } +} ///:~ diff --git a/src/io/UsingBuffers.java b/src/io/UsingBuffers.java new file mode 100644 index 0000000..af9da28 --- /dev/null +++ b/src/io/UsingBuffers.java @@ -0,0 +1,30 @@ +package io;//: io/UsingBuffers.java +import java.nio.*; +import static net.mindview.util.Print.*; + +public class UsingBuffers { + private static void symmetricScramble(CharBuffer buffer){ + while(buffer.hasRemaining()) { + buffer.mark(); + char c1 = buffer.get(); + char c2 = buffer.get(); + buffer.reset(); + buffer.put(c2).put(c1); + } + } + public static void main(String[] args) { + char[] data = "UsingBuffers".toCharArray(); + ByteBuffer bb = ByteBuffer.allocate(data.length * 2); + CharBuffer cb = bb.asCharBuffer(); + cb.put(data); + print(cb.rewind()); + symmetricScramble(cb); + print(cb.rewind()); + symmetricScramble(cb); + print(cb.rewind()); + } +} /* Output: +UsingBuffers +sUniBgfuefsr +UsingBuffers +*///:~ diff --git a/src/io/UsingRandomAccessFile.java b/src/io/UsingRandomAccessFile.java new file mode 100644 index 0000000..27d2996 --- /dev/null +++ b/src/io/UsingRandomAccessFile.java @@ -0,0 +1,47 @@ +package io;//: io/UsingRandomAccessFile.java +import java.io.*; + +public class UsingRandomAccessFile { + static String file = "rtest.dat"; + static void display() throws IOException { + RandomAccessFile rf = new RandomAccessFile(file, "r"); + for(int i = 0; i < 7; i++) { + System.out.println( + "Value " + i + ": " + rf.readDouble()); + } + System.out.println(rf.readUTF()); + rf.close(); + } + public static void main(String[] args) + throws IOException { + RandomAccessFile rf = new RandomAccessFile(file, "rw"); + for(int i = 0; i < 7; i++) { + rf.writeDouble(i*1.414); + } + rf.writeUTF("The end of the file"); + rf.close(); + display(); + rf = new RandomAccessFile(file, "rw"); + rf.seek(5*8); + rf.writeDouble(47.0001); + rf.close(); + display(); + } +} /* Output: +Value 0: 0.0 +Value 1: 1.414 +Value 2: 2.828 +Value 3: 4.242 +Value 4: 5.656 +Value 5: 7.069999999999999 +Value 6: 8.484 +The end of the file +Value 0: 0.0 +Value 1: 1.414 +Value 2: 2.828 +Value 3: 4.242 +Value 4: 5.656 +Value 5: 47.0001 +Value 6: 8.484 +The end of the file +*///:~ diff --git a/src/io/ViewBuffers.java b/src/io/ViewBuffers.java new file mode 100644 index 0000000..d7430a2 --- /dev/null +++ b/src/io/ViewBuffers.java @@ -0,0 +1,65 @@ +package io;//: io/ViewBuffers.java +import java.nio.*; +import static net.mindview.util.Print.*; + +public class ViewBuffers { + public static void main(String[] args) { + ByteBuffer bb = ByteBuffer.wrap( + new byte[]{ 0, 0, 0, 0, 0, 0, 0, 'a' }); + bb.rewind(); + printnb("Byte Buffer "); + while(bb.hasRemaining()) { + printnb(bb.position()+ " -> " + bb.get() + ", "); + } + print(); + CharBuffer cb = + ((ByteBuffer)bb.rewind()).asCharBuffer(); + printnb("Char Buffer "); + while(cb.hasRemaining()) { + printnb(cb.position() + " -> " + cb.get() + ", "); + } + print(); + FloatBuffer fb = + ((ByteBuffer)bb.rewind()).asFloatBuffer(); + printnb("Float Buffer "); + while(fb.hasRemaining()) { + printnb(fb.position()+ " -> " + fb.get() + ", "); + } + print(); + IntBuffer ib = + ((ByteBuffer)bb.rewind()).asIntBuffer(); + printnb("Int Buffer "); + while(ib.hasRemaining()) { + printnb(ib.position()+ " -> " + ib.get() + ", "); + } + print(); + LongBuffer lb = + ((ByteBuffer)bb.rewind()).asLongBuffer(); + printnb("Long Buffer "); + while(lb.hasRemaining()) { + printnb(lb.position()+ " -> " + lb.get() + ", "); + } + print(); + ShortBuffer sb = + ((ByteBuffer)bb.rewind()).asShortBuffer(); + printnb("Short Buffer "); + while(sb.hasRemaining()) { + printnb(sb.position()+ " -> " + sb.get() + ", "); + } + print(); + DoubleBuffer db = + ((ByteBuffer)bb.rewind()).asDoubleBuffer(); + printnb("Double Buffer "); + while(db.hasRemaining()) { + printnb(db.position()+ " -> " + db.get() + ", "); + } + } +} /* Output: +Byte Buffer 0 -> 0, 1 -> 0, 2 -> 0, 3 -> 0, 4 -> 0, 5 -> 0, 6 -> 0, 7 -> 97, +Char Buffer 0 -> , 1 -> , 2 -> , 3 -> a, +Float Buffer 0 -> 0.0, 1 -> 1.36E-43, +Int Buffer 0 -> 0, 1 -> 97, +Long Buffer 0 -> 97, +Short Buffer 0 -> 0, 1 -> 0, 2 -> 0, 3 -> 97, +Double Buffer 0 -> 4.8E-322, +*///:~ diff --git a/src/io/Worm.java b/src/io/Worm.java new file mode 100644 index 0000000..28cf6ee --- /dev/null +++ b/src/io/Worm.java @@ -0,0 +1,84 @@ +package io;//: io/Worm.java +// Demonstrates object serialization. +import java.io.*; +import java.util.*; +import static net.mindview.util.Print.*; + +class Data implements Serializable { + private int n; + public Data(int n) { this.n = n; } + public String toString() { return Integer.toString(n); } +} + +public class Worm implements Serializable { + private static Random rand = new Random(47); + private Data[] d = { + new Data(rand.nextInt(10)), + new Data(rand.nextInt(10)), + new Data(rand.nextInt(10)) + }; + private Worm next; + private char c; + // Value of i == number of segments + public Worm(int i, char x) { + print("Worm constructor: " + i); + c = x; + if(--i > 0) { + next = new Worm(i, (char)(x + 1)); + } + } + public Worm() { + print("Default constructor"); + } + public String toString() { + StringBuilder result = new StringBuilder(":"); + result.append(c); + result.append("("); + for(Data dat : d) { + result.append(dat); + } + result.append(")"); + if(next != null) { + result.append(next); + } + return result.toString(); + } + public static void main(String[] args) + throws ClassNotFoundException, IOException { + Worm w = new Worm(6, 'a'); + print("w = " + w); + ObjectOutputStream out = new ObjectOutputStream( + new FileOutputStream("worm.out")); + out.writeObject("Worm storage\n"); + out.writeObject(w); + out.close(); // Also flushes output + ObjectInputStream in = new ObjectInputStream( + new FileInputStream("worm.out")); + String s = (String)in.readObject(); + Worm w2 = (Worm)in.readObject(); + print(s + "w2 = " + w2); + ByteArrayOutputStream bout = + new ByteArrayOutputStream(); + ObjectOutputStream out2 = new ObjectOutputStream(bout); + out2.writeObject("Worm storage\n"); + out2.writeObject(w); + out2.flush(); + ObjectInputStream in2 = new ObjectInputStream( + new ByteArrayInputStream(bout.toByteArray())); + s = (String)in2.readObject(); + Worm w3 = (Worm)in2.readObject(); + print(s + "w3 = " + w3); + } +} /* Output: +Worm constructor: 6 +Worm constructor: 5 +Worm constructor: 4 +Worm constructor: 3 +Worm constructor: 2 +Worm constructor: 1 +w = :a(853):b(119):c(802):d(788):e(199):f(881) +Worm storage +w2 = :a(853):b(119):c(802):d(788):e(199):f(881) +Worm storage +w3 = :a(853):b(119):c(802):d(788):e(199):f(881) +*///:~ diff --git a/src/io/ZipCompress.java b/src/io/ZipCompress.java new file mode 100644 index 0000000..1ae03cd --- /dev/null +++ b/src/io/ZipCompress.java @@ -0,0 +1,65 @@ +package io;//: io/ZipCompress.java +// Uses Zip compression to compress any +// number of files given on the command line. +// {Args: ZipCompress.java} +import java.util.zip.*; +import java.io.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class ZipCompress { + public static void main(String[] args) + throws IOException { + FileOutputStream f = new FileOutputStream("test.zip"); + CheckedOutputStream csum = + new CheckedOutputStream(f, new Adler32()); + ZipOutputStream zos = new ZipOutputStream(csum); + BufferedOutputStream out = + new BufferedOutputStream(zos); + zos.setComment("A test of Java Zipping"); + // No corresponding getComment(), though. + for(String arg : args) { + print("Writing file " + arg); + BufferedReader in = + new BufferedReader(new FileReader(arg)); + zos.putNextEntry(new ZipEntry(arg)); + int c; + while((c = in.read()) != -1) { + out.write(c); + } + in.close(); + out.flush(); + } + out.close(); + // Checksum valid only after the file has been closed! + print("Checksum: " + csum.getChecksum().getValue()); + // Now extract the files: + print("Reading file"); + FileInputStream fi = new FileInputStream("test.zip"); + CheckedInputStream csumi = + new CheckedInputStream(fi, new Adler32()); + ZipInputStream in2 = new ZipInputStream(csumi); + BufferedInputStream bis = new BufferedInputStream(in2); + ZipEntry ze; + while((ze = in2.getNextEntry()) != null) { + print("Reading file " + ze); + int x; + while((x = bis.read()) != -1) { + System.out.write(x); + } + } + if(args.length == 1) { + print("Checksum: " + csumi.getChecksum().getValue()); + } + bis.close(); + // Alternative way to open and read Zip files: + ZipFile zf = new ZipFile("test.zip"); + Enumeration e = zf.entries(); + while(e.hasMoreElements()) { + ZipEntry ze2 = (ZipEntry)e.nextElement(); + print("File: " + ze2); + // ... and extract the data as before + } + /* if(args.length == 1) */ + } +} /* (Execute to see output) *///:~ diff --git a/src/io/build.xml b/src/io/build.xml new file mode 100644 index 0000000..fe1ac48 --- /dev/null +++ b/src/io/build.xml @@ -0,0 +1,529 @@ + + + + + + build.xml for the source code for the io chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/io/xfiles/ThawAlien.java b/src/io/xfiles/ThawAlien.java new file mode 100644 index 0000000..11ef3b1 --- /dev/null +++ b/src/io/xfiles/ThawAlien.java @@ -0,0 +1,16 @@ +package io.xfiles;//: io/xfiles/ThawAlien.java +// Try to recover a serialized file without the +// class of object that's stored in that file. +// {RunByHand} +import java.io.*; + +public class ThawAlien { + public static void main(String[] args) throws Exception { + ObjectInputStream in = new ObjectInputStream( + new FileInputStream(new File("..", "X.file"))); + Object mystery = in.readObject(); + System.out.println(mystery.getClass()); + } +} /* Output: +class Alien +*///:~ diff --git a/src/net/build.xml b/src/net/build.xml new file mode 100644 index 0000000..2cb4142 --- /dev/null +++ b/src/net/build.xml @@ -0,0 +1,200 @@ + + + + + + build.xml for the source code for the net chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/net/mindview/atunit/AtUnit.java b/src/net/mindview/atunit/AtUnit.java new file mode 100644 index 0000000..6fa5b3b --- /dev/null +++ b/src/net/mindview/atunit/AtUnit.java @@ -0,0 +1,168 @@ +//: net/mindview/atunit/AtUnit.java +// An annotation-based unit-test framework. +// {RunByHand} +package net.mindview.atunit; +import java.lang.reflect.*; +import java.io.*; +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class AtUnit implements ProcessFiles.Strategy { + static Class testClass; + static List failedTests= new ArrayList(); + static long testsRun = 0; + static long failures = 0; + public static void main(String[] args) throws Exception { + ClassLoader.getSystemClassLoader() + .setDefaultAssertionStatus(true); // Enable asserts + new ProcessFiles(new AtUnit(), "class").start(args); + if(failures == 0) { + print("OK (" + testsRun + " tests)"); + } else { + print("(" + testsRun + " tests)"); + print("\n>>> " + failures + " FAILURE" + + (failures > 1 ? "S" : "") + " <<<"); + for(String failed : failedTests) { + print(" " + failed); + } + } + } + public void process(File cFile) { + try { + String cName = ClassNameFinder.thisClass( + BinaryFile.read(cFile)); + if(!cName.contains(".")) { + return; // Ignore unpackaged classes + } + testClass = Class.forName(cName); + } catch(Exception e) { + throw new RuntimeException(e); + } + TestMethods testMethods = new TestMethods(); + Method creator = null; + Method cleanup = null; + for(Method m : testClass.getDeclaredMethods()) { + testMethods.addIfTestMethod(m); + if(creator == null) { + creator = checkForCreatorMethod(m); + } + if(cleanup == null) { + cleanup = checkForCleanupMethod(m); + } + } + if(testMethods.size() > 0) { + if(creator == null) { + try { + if(!Modifier.isPublic(testClass + .getDeclaredConstructor().getModifiers())) { + print("Error: " + testClass + + " default constructor must be public"); + System.exit(1); + } + } catch(NoSuchMethodException e) { + // Synthesized default constructor; OK + } + } + print(testClass.getName()); + } + for(Method m : testMethods) { + printnb(" . " + m.getName() + " "); + try { + Object testObject = createTestObject(creator); + boolean success = false; + try { + if(m.getReturnType().equals(boolean.class)) { + success = (Boolean)m.invoke(testObject); + } else { + m.invoke(testObject); + success = true; // If no assert fails + } + } catch(InvocationTargetException e) { + // Actual exception is inside e: + print(e.getCause()); + } + print(success ? "" : "(failed)"); + testsRun++; + if(!success) { + failures++; + failedTests.add(testClass.getName() + + ": " + m.getName()); + } + if(cleanup != null) { + cleanup.invoke(testObject, testObject); + } + } catch(Exception e) { + throw new RuntimeException(e); + } + } + } + static class TestMethods extends ArrayList { + void addIfTestMethod(Method m) { + if(m.getAnnotation(Test.class) == null) { + return; + } + if(!(m.getReturnType().equals(boolean.class) || + m.getReturnType().equals(void.class))) { + throw new RuntimeException("@Test method" + + " must return boolean or void"); + } + m.setAccessible(true); // In case it's private, etc. + add(m); + } + } + private static Method checkForCreatorMethod(Method m) { + if(m.getAnnotation(TestObjectCreate.class) == null) { + return null; + } + if(!m.getReturnType().equals(testClass)) { + throw new RuntimeException("@TestObjectCreate " + + "must return instance of Class to be tested"); + } + if((m.getModifiers() & + java.lang.reflect.Modifier.STATIC) < 1) { + throw new RuntimeException("@TestObjectCreate " + + "must be static."); + } + m.setAccessible(true); + return m; + } + private static Method checkForCleanupMethod(Method m) { + if(m.getAnnotation(TestObjectCleanup.class) == null) { + return null; + } + if(!m.getReturnType().equals(void.class)) { + throw new RuntimeException("@TestObjectCleanup " + + "must return void"); + } + if((m.getModifiers() & + java.lang.reflect.Modifier.STATIC) < 1) { + throw new RuntimeException("@TestObjectCleanup " + + "must be static."); + } + if(m.getParameterTypes().length == 0 || + m.getParameterTypes()[0] != testClass) { + throw new RuntimeException("@TestObjectCleanup " + + "must take an argument of the tested type."); + } + m.setAccessible(true); + return m; + } + private static Object createTestObject(Method creator) { + if(creator != null) { + try { + return creator.invoke(testClass); + } catch(Exception e) { + throw new RuntimeException("Couldn't run " + + "@TestObject (creator) method."); + } + } else { // Use the default constructor: + try { + return testClass.newInstance(); + } catch(Exception e) { + throw new RuntimeException("Couldn't create a " + + "test object. Try using a @TestObject method."); + } + } + } +} ///:~ diff --git a/src/net/mindview/atunit/AtUnitRemover.java b/src/net/mindview/atunit/AtUnitRemover.java new file mode 100644 index 0000000..0ba131b --- /dev/null +++ b/src/net/mindview/atunit/AtUnitRemover.java @@ -0,0 +1,70 @@ +//: net/mindview/atunit/AtUnitRemover.java +// Displays @Unit annotations in compiled class files. If +// first argument is "-r", @Unit annotations are removed. +// {Args: ..} +// {Requires: javassist.bytecode.ClassFile; +// You must install the Javassist library from +// http://sourceforge.net/projects/jboss/ } +package net.mindview.atunit; +import javassist.*; +import javassist.expr.*; +import javassist.bytecode.*; +import javassist.bytecode.annotation.*; +import java.io.*; +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class AtUnitRemover +implements ProcessFiles.Strategy { + private static boolean remove = false; + public static void main(String[] args) throws Exception { + if(args.length > 0 && args[0].equals("-r")) { + remove = true; + String[] nargs = new String[args.length - 1]; + System.arraycopy(args, 1, nargs, 0, nargs.length); + args = nargs; + } + new ProcessFiles( + new AtUnitRemover(), "class").start(args); + } + public void process(File cFile) { + boolean modified = false; + try { + String cName = ClassNameFinder.thisClass( + BinaryFile.read(cFile)); + if(!cName.contains(".")) { + return; // Ignore unpackaged classes + } + ClassPool cPool = ClassPool.getDefault(); + CtClass ctClass = cPool.get(cName); + for(CtMethod method : ctClass.getDeclaredMethods()) { + MethodInfo mi = method.getMethodInfo(); + AnnotationsAttribute attr = (AnnotationsAttribute) + mi.getAttribute(AnnotationsAttribute.visibleTag); + if(attr == null) { + continue; + } + for(Annotation ann : attr.getAnnotations()) { + if(ann.getTypeName() + .startsWith("net.mindview.atunit")) { + print(ctClass.getName() + " Method: " + + mi.getName() + " " + ann); + if(remove) { + ctClass.removeMethod(method); + modified = true; + } + } + } + } + // Fields are not removed in this version (see text). + if(modified) { + ctClass.toBytecode(new DataOutputStream( + new FileOutputStream(cFile))); + } + ctClass.detach(); + } catch(Exception e) { + throw new RuntimeException(e); + } + } +} ///:~ diff --git a/src/net/mindview/atunit/ClassNameFinder.java b/src/net/mindview/atunit/ClassNameFinder.java new file mode 100644 index 0000000..5721c1c --- /dev/null +++ b/src/net/mindview/atunit/ClassNameFinder.java @@ -0,0 +1,82 @@ +//: net/mindview/atunit/ClassNameFinder.java +package net.mindview.atunit; +import java.io.*; +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class ClassNameFinder { + public static String thisClass(byte[] classBytes) { + Map offsetTable = + new HashMap(); + Map classNameTable = + new HashMap(); + try { + DataInputStream data = new DataInputStream( + new ByteArrayInputStream(classBytes)); + int magic = data.readInt(); // 0xcafebabe + int minorVersion = data.readShort(); + int majorVersion = data.readShort(); + int constant_pool_count = data.readShort(); + int[] constant_pool = new int[constant_pool_count]; + for(int i = 1; i < constant_pool_count; i++) { + int tag = data.read(); + int tableSize; + switch(tag) { + case 1: // UTF + int length = data.readShort(); + char[] bytes = new char[length]; + for(int k = 0; k < bytes.length; k++) { + bytes[k] = (char)data.read(); + } + String className = new String(bytes); + classNameTable.put(i, className); + break; + case 5: // LONG + case 6: // DOUBLE + data.readLong(); // discard 8 bytes + i++; // Special skip necessary + break; + case 7: // CLASS + int offset = data.readShort(); + offsetTable.put(i, offset); + break; + case 8: // STRING + data.readShort(); // discard 2 bytes + break; + case 3: // INTEGER + case 4: // FLOAT + case 9: // FIELD_REF + case 10: // METHOD_REF + case 11: // INTERFACE_METHOD_REF + case 12: // NAME_AND_TYPE + data.readInt(); // discard 4 bytes; + break; + default: + throw new RuntimeException("Bad tag " + tag); + } + } + short access_flags = data.readShort(); + int this_class = data.readShort(); + int super_class = data.readShort(); + return classNameTable.get( + offsetTable.get(this_class)).replace('/', '.'); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + // Demonstration: + public static void main(String[] args) throws Exception { + if(args.length > 0) { + for(String arg : args) { + print(thisClass(BinaryFile.read(new File(arg)))); + } + } else + // Walk the entire tree: + { + for(File klass : Directory.walk(".", ".*\\.class")) { + print(thisClass(BinaryFile.read(klass))); + } + } + } +} ///:~ diff --git a/src/net/mindview/atunit/Test.java b/src/net/mindview/atunit/Test.java new file mode 100644 index 0000000..8fe76ff --- /dev/null +++ b/src/net/mindview/atunit/Test.java @@ -0,0 +1,8 @@ +//: net/mindview/atunit/Test.java +// The @Test tag. +package net.mindview.atunit; +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Test {} ///:~ diff --git a/src/net/mindview/atunit/TestObjectCleanup.java b/src/net/mindview/atunit/TestObjectCleanup.java new file mode 100644 index 0000000..96ec997 --- /dev/null +++ b/src/net/mindview/atunit/TestObjectCleanup.java @@ -0,0 +1,8 @@ +//: net/mindview/atunit/TestObjectCleanup.java +// The @Unit @TestObjectCleanup tag. +package net.mindview.atunit; +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface TestObjectCleanup {} ///:~ diff --git a/src/net/mindview/atunit/TestObjectCreate.java b/src/net/mindview/atunit/TestObjectCreate.java new file mode 100644 index 0000000..d9951d8 --- /dev/null +++ b/src/net/mindview/atunit/TestObjectCreate.java @@ -0,0 +1,8 @@ +//: net/mindview/atunit/TestObjectCreate.java +// The @Unit @TestObjectCreate tag. +package net.mindview.atunit; +import java.lang.annotation.*; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface TestObjectCreate {} ///:~ diff --git a/src/net/mindview/atunit/TestProperty.java b/src/net/mindview/atunit/TestProperty.java new file mode 100644 index 0000000..274da11 --- /dev/null +++ b/src/net/mindview/atunit/TestProperty.java @@ -0,0 +1,9 @@ +//: net/mindview/atunit/TestProperty.java +// The @Unit @TestProperty tag. +package net.mindview.atunit; +import java.lang.annotation.*; + +// Both fields and methods may be tagged as properties: +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface TestProperty {} ///:~ diff --git a/src/net/mindview/simple/List.java b/src/net/mindview/simple/List.java new file mode 100644 index 0000000..751797e --- /dev/null +++ b/src/net/mindview/simple/List.java @@ -0,0 +1,9 @@ +//: net/mindview/simple/List.java +// Creating a package. +package net.mindview.simple; + +public class List { + public List() { + System.out.println("net.mindview.simple.List"); + } +} ///:~ diff --git a/src/net/mindview/simple/Vector.java b/src/net/mindview/simple/Vector.java new file mode 100644 index 0000000..38b3993 --- /dev/null +++ b/src/net/mindview/simple/Vector.java @@ -0,0 +1,9 @@ +//: net/mindview/simple/Vector.java +// Creating a package. +package net.mindview.simple; + +public class Vector { + public Vector() { + System.out.println("net.mindview.simple.Vector"); + } +} ///:~ diff --git a/src/net/mindview/util/BasicGenerator.java b/src/net/mindview/util/BasicGenerator.java new file mode 100644 index 0000000..7c588eb --- /dev/null +++ b/src/net/mindview/util/BasicGenerator.java @@ -0,0 +1,21 @@ +//: net/mindview/util/BasicGenerator.java +// Automatically create a Generator, given a class +// with a default (no-arg) constructor. +package net.mindview.util; + +public class BasicGenerator implements Generator { + private Class type; + public BasicGenerator(Class type){ this.type = type; } + public T next() { + try { + // Assumes type is a public class: + return type.newInstance(); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + // Produce a Default generator given a type token: + public static Generator create(Class type) { + return new BasicGenerator(type); + } +} ///:~ diff --git a/src/net/mindview/util/BinaryFile.java b/src/net/mindview/util/BinaryFile.java new file mode 100644 index 0000000..fa8a623 --- /dev/null +++ b/src/net/mindview/util/BinaryFile.java @@ -0,0 +1,22 @@ +//: net/mindview/util/BinaryFile.java +// Utility for reading files in binary form. +package net.mindview.util; +import java.io.*; + +public class BinaryFile { + public static byte[] read(File bFile) throws IOException{ + BufferedInputStream bf = new BufferedInputStream( + new FileInputStream(bFile)); + try { + byte[] data = new byte[bf.available()]; + bf.read(data); + return data; + } finally { + bf.close(); + } + } + public static byte[] + read(String bFile) throws IOException { + return read(new File(bFile).getAbsoluteFile()); + } +} ///:~ diff --git a/src/net/mindview/util/CollectionData.java b/src/net/mindview/util/CollectionData.java new file mode 100644 index 0000000..b92fbf6 --- /dev/null +++ b/src/net/mindview/util/CollectionData.java @@ -0,0 +1,17 @@ +//: net/mindview/util/CollectionData.java +// A Collection filled with data using a generator object. +package net.mindview.util; +import java.util.*; + +public class CollectionData extends ArrayList { + public CollectionData(Generator gen, int quantity) { + for(int i = 0; i < quantity; i++) { + add(gen.next()); + } + } + // A generic convenience method: + public static CollectionData + list(Generator gen, int quantity) { + return new CollectionData(gen, quantity); + } +} ///:~ diff --git a/src/net/mindview/util/ContainerMethodDifferences.java b/src/net/mindview/util/ContainerMethodDifferences.java new file mode 100644 index 0000000..0132fdc --- /dev/null +++ b/src/net/mindview/util/ContainerMethodDifferences.java @@ -0,0 +1,54 @@ +//: net/mindview/util/ContainerMethodDifferences.java +package net.mindview.util; +import java.lang.reflect.*; +import java.util.*; + +public class ContainerMethodDifferences { + static Set methodSet(Class type) { + Set result = new TreeSet(); + for(Method m : type.getMethods()) { + result.add(m.getName()); + } + return result; + } + static void interfaces(Class type) { + System.out.print("Interfaces in " + + type.getSimpleName() + ": "); + List result = new ArrayList(); + for(Class c : type.getInterfaces()) { + result.add(c.getSimpleName()); + } + System.out.println(result); + } + static Set object = methodSet(Object.class); + static { object.add("clone"); } + static void + difference(Class superset, Class subset) { + System.out.print(superset.getSimpleName() + + " extends " + subset.getSimpleName() + ", adds: "); + Set comp = Sets.difference( + methodSet(superset), methodSet(subset)); + comp.removeAll(object); // Don't show 'Object' methods + System.out.println(comp); + interfaces(superset); + } + public static void main(String[] args) { + System.out.println("Collection: " + + methodSet(Collection.class)); + interfaces(Collection.class); + difference(Set.class, Collection.class); + difference(HashSet.class, Set.class); + difference(LinkedHashSet.class, HashSet.class); + difference(TreeSet.class, Set.class); + difference(List.class, Collection.class); + difference(ArrayList.class, List.class); + difference(LinkedList.class, List.class); + difference(Queue.class, Collection.class); + difference(PriorityQueue.class, Queue.class); + System.out.println("Map: " + methodSet(Map.class)); + difference(HashMap.class, Map.class); + difference(LinkedHashMap.class, HashMap.class); + difference(SortedMap.class, Map.class); + difference(TreeMap.class, Map.class); + } +} ///:~ diff --git a/src/net/mindview/util/ConvertTo.java b/src/net/mindview/util/ConvertTo.java new file mode 100644 index 0000000..3c810ec --- /dev/null +++ b/src/net/mindview/util/ConvertTo.java @@ -0,0 +1,61 @@ +//: net/mindview/util/ConvertTo.java +package net.mindview.util; + +public class ConvertTo { + public static boolean[] primitive(Boolean[] in) { + boolean[] result = new boolean[in.length]; + for(int i = 0; i < in.length; i++) { + result[i] = in[i]; // Autounboxing + } + return result; + } + public static char[] primitive(Character[] in) { + char[] result = new char[in.length]; + for(int i = 0; i < in.length; i++) { + result[i] = in[i]; + } + return result; + } + public static byte[] primitive(Byte[] in) { + byte[] result = new byte[in.length]; + for(int i = 0; i < in.length; i++) { + result[i] = in[i]; + } + return result; + } + public static short[] primitive(Short[] in) { + short[] result = new short[in.length]; + for(int i = 0; i < in.length; i++) { + result[i] = in[i]; + } + return result; + } + public static int[] primitive(Integer[] in) { + int[] result = new int[in.length]; + for(int i = 0; i < in.length; i++) { + result[i] = in[i]; + } + return result; + } + public static long[] primitive(Long[] in) { + long[] result = new long[in.length]; + for(int i = 0; i < in.length; i++) { + result[i] = in[i]; + } + return result; + } + public static float[] primitive(Float[] in) { + float[] result = new float[in.length]; + for(int i = 0; i < in.length; i++) { + result[i] = in[i]; + } + return result; + } + public static double[] primitive(Double[] in) { + double[] result = new double[in.length]; + for(int i = 0; i < in.length; i++) { + result[i] = in[i]; + } + return result; + } +} ///:~ diff --git a/src/net/mindview/util/CountingGenerator.java b/src/net/mindview/util/CountingGenerator.java new file mode 100644 index 0000000..9be89a6 --- /dev/null +++ b/src/net/mindview/util/CountingGenerator.java @@ -0,0 +1,76 @@ +//: net/mindview/util/CountingGenerator.java +// Simple generator implementations. +package net.mindview.util; + +public class CountingGenerator { + public static class + Boolean implements Generator { + private boolean value = false; + public java.lang.Boolean next() { + value = !value; // Just flips back and forth + return value; + } + } + public static class + Byte implements Generator { + private byte value = 0; + public java.lang.Byte next() { return value++; } + } + static char[] chars = ("abcdefghijklmnopqrstuvwxyz" + + "ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray(); + public static class + Character implements Generator { + int index = -1; + public java.lang.Character next() { + index = (index + 1) % chars.length; + return chars[index]; + } + } + public static class + String implements Generator { + private int length = 7; + Generator cg = new Character(); + public String() {} + public String(int length) { this.length = length; } + public java.lang.String next() { + char[] buf = new char[length]; + for(int i = 0; i < length; i++) { + buf[i] = cg.next(); + } + return new java.lang.String(buf); + } + } + public static class + Short implements Generator { + private short value = 0; + public java.lang.Short next() { return value++; } + } + public static class + Integer implements Generator { + private int value = 0; + public java.lang.Integer next() { return value++; } + } + public static class + Long implements Generator { + private long value = 0; + public java.lang.Long next() { return value++; } + } + public static class + Float implements Generator { + private float value = 0; + public java.lang.Float next() { + float result = value; + value += 1.0; + return result; + } + } + public static class + Double implements Generator { + private double value = 0.0; + public java.lang.Double next() { + double result = value; + value += 1.0; + return result; + } + } +} ///:~ diff --git a/src/net/mindview/util/CountingIntegerList.java b/src/net/mindview/util/CountingIntegerList.java new file mode 100644 index 0000000..e6c9788 --- /dev/null +++ b/src/net/mindview/util/CountingIntegerList.java @@ -0,0 +1,21 @@ +//: net/mindview/util/CountingIntegerList.java +// List of any length, containing sample data. +package net.mindview.util; +import java.util.*; + +public class CountingIntegerList +extends AbstractList { + private int size; + public CountingIntegerList(int size) { + this.size = size < 0 ? 0 : size; + } + public Integer get(int index) { + return Integer.valueOf(index); + } + public int size() { return size; } + public static void main(String[] args) { + System.out.println(new CountingIntegerList(30)); + } +} /* Output: +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] +*///:~ diff --git a/src/net/mindview/util/CountingMapData.java b/src/net/mindview/util/CountingMapData.java new file mode 100644 index 0000000..3310edd --- /dev/null +++ b/src/net/mindview/util/CountingMapData.java @@ -0,0 +1,52 @@ +//: net/mindview/util/CountingMapData.java +// Unlimited-length Map containing sample data. +package net.mindview.util; +import java.util.*; + +public class CountingMapData +extends AbstractMap { + private int size; + private static String[] chars = + "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" + .split(" "); + public CountingMapData(int size) { + if(size < 0) { + this.size = 0; + } + this.size = size; + } + private static class Entry + implements Map.Entry { + int index; + Entry(int index) { this.index = index; } + public boolean equals(Object o) { + return Integer.valueOf(index).equals(o); + } + public Integer getKey() { return index; } + public String getValue() { + return + chars[index % chars.length] + + Integer.toString(index / chars.length); + } + public String setValue(String value) { + throw new UnsupportedOperationException(); + } + public int hashCode() { + return Integer.valueOf(index).hashCode(); + } + } + public Set> entrySet() { + // LinkedHashSet retains initialization order: + Set> entries = + new LinkedHashSet>(); + for(int i = 0; i < size; i++) { + entries.add(new Entry(i)); + } + return entries; + } + public static void main(String[] args) { + System.out.println(new CountingMapData(60)); + } +} /* Output: +{0=A0, 1=B0, 2=C0, 3=D0, 4=E0, 5=F0, 6=G0, 7=H0, 8=I0, 9=J0, 10=K0, 11=L0, 12=M0, 13=N0, 14=O0, 15=P0, 16=Q0, 17=R0, 18=S0, 19=T0, 20=U0, 21=V0, 22=W0, 23=X0, 24=Y0, 25=Z0, 26=A1, 27=B1, 28=C1, 29=D1, 30=E1, 31=F1, 32=G1, 33=H1, 34=I1, 35=J1, 36=K1, 37=L1, 38=M1, 39=N1, 40=O1, 41=P1, 42=Q1, 43=R1, 44=S1, 45=T1, 46=U1, 47=V1, 48=W1, 49=X1, 50=Y1, 51=Z1, 52=A2, 53=B2, 54=C2, 55=D2, 56=E2, 57=F2, 58=G2, 59=H2} +*///:~ diff --git a/src/net/mindview/util/Countries.java b/src/net/mindview/util/Countries.java new file mode 100644 index 0000000..a5ad2a2 --- /dev/null +++ b/src/net/mindview/util/Countries.java @@ -0,0 +1,244 @@ +//: net/mindview/util/Countries.java +// "Flyweight" Maps and Lists of sample data. +package net.mindview.util; +import java.util.*; +import static net.mindview.util.Print.*; + +public class Countries { + public static final String[][] DATA = { + // Africa + {"ALGERIA","Algiers"}, {"ANGOLA","Luanda"}, + {"BENIN","Porto-Novo"}, {"BOTSWANA","Gaberone"}, + {"BURKINA FASO","Ouagadougou"}, + {"BURUNDI","Bujumbura"}, + {"CAMEROON","Yaounde"}, {"CAPE VERDE","Praia"}, + {"CENTRAL AFRICAN REPUBLIC","Bangui"}, + {"CHAD","N'djamena"}, {"COMOROS","Moroni"}, + {"CONGO","Brazzaville"}, {"DJIBOUTI","Dijibouti"}, + {"EGYPT","Cairo"}, {"EQUATORIAL GUINEA","Malabo"}, + {"ERITREA","Asmara"}, {"ETHIOPIA","Addis Ababa"}, + {"GABON","Libreville"}, {"THE GAMBIA","Banjul"}, + {"GHANA","Accra"}, {"GUINEA","Conakry"}, + {"BISSAU","Bissau"}, + {"COTE D'IVOIR (IVORY COAST)","Yamoussoukro"}, + {"KENYA","Nairobi"}, {"LESOTHO","Maseru"}, + {"LIBERIA","Monrovia"}, {"LIBYA","Tripoli"}, + {"MADAGASCAR","Antananarivo"}, {"MALAWI","Lilongwe"}, + {"MALI","Bamako"}, {"MAURITANIA","Nouakchott"}, + {"MAURITIUS","Port Louis"}, {"MOROCCO","Rabat"}, + {"MOZAMBIQUE","Maputo"}, {"NAMIBIA","Windhoek"}, + {"NIGER","Niamey"}, {"NIGERIA","Abuja"}, + {"RWANDA","Kigali"}, + {"SAO TOME E PRINCIPE","Sao Tome"}, + {"SENEGAL","Dakar"}, {"SEYCHELLES","Victoria"}, + {"SIERRA LEONE","Freetown"}, {"SOMALIA","Mogadishu"}, + {"SOUTH AFRICA","Pretoria/Cape Town"}, + {"SUDAN","Khartoum"}, + {"SWAZILAND","Mbabane"}, {"TANZANIA","Dodoma"}, + {"TOGO","Lome"}, {"TUNISIA","Tunis"}, + {"UGANDA","Kampala"}, + {"DEMOCRATIC REPUBLIC OF THE CONGO (ZAIRE)", + "Kinshasa"}, + {"ZAMBIA","Lusaka"}, {"ZIMBABWE","Harare"}, + // Asia + {"AFGHANISTAN","Kabul"}, {"BAHRAIN","Manama"}, + {"BANGLADESH","Dhaka"}, {"BHUTAN","Thimphu"}, + {"BRUNEI","Bandar Seri Begawan"}, + {"CAMBODIA","Phnom Penh"}, + {"CHINA","Beijing"}, {"CYPRUS","Nicosia"}, + {"INDIA","New Delhi"}, {"INDONESIA","Jakarta"}, + {"IRAN","Tehran"}, {"IRAQ","Baghdad"}, + {"ISRAEL","Jerusalem"}, {"JAPAN","Tokyo"}, + {"JORDAN","Amman"}, {"KUWAIT","Kuwait City"}, + {"LAOS","Vientiane"}, {"LEBANON","Beirut"}, + {"MALAYSIA","Kuala Lumpur"}, {"THE MALDIVES","Male"}, + {"MONGOLIA","Ulan Bator"}, + {"MYANMAR (BURMA)","Rangoon"}, + {"NEPAL","Katmandu"}, {"NORTH KOREA","P'yongyang"}, + {"OMAN","Muscat"}, {"PAKISTAN","Islamabad"}, + {"PHILIPPINES","Manila"}, {"QATAR","Doha"}, + {"SAUDI ARABIA","Riyadh"}, {"SINGAPORE","Singapore"}, + {"SOUTH KOREA","Seoul"}, {"SRI LANKA","Colombo"}, + {"SYRIA","Damascus"}, + {"TAIWAN (REPUBLIC OF CHINA)","Taipei"}, + {"THAILAND","Bangkok"}, {"TURKEY","Ankara"}, + {"UNITED ARAB EMIRATES","Abu Dhabi"}, + {"VIETNAM","Hanoi"}, {"YEMEN","Sana'a"}, + // Australia and Oceania + {"AUSTRALIA","Canberra"}, {"FIJI","Suva"}, + {"KIRIBATI","Bairiki"}, + {"MARSHALL ISLANDS","Dalap-Uliga-Darrit"}, + {"MICRONESIA","Palikir"}, {"NAURU","Yaren"}, + {"NEW ZEALAND","Wellington"}, {"PALAU","Koror"}, + {"PAPUA NEW GUINEA","Port Moresby"}, + {"SOLOMON ISLANDS","Honaira"}, {"TONGA","Nuku'alofa"}, + {"TUVALU","Fongafale"}, {"VANUATU","< Port-Vila"}, + {"WESTERN SAMOA","Apia"}, + // Eastern Europe and former USSR + {"ARMENIA","Yerevan"}, {"AZERBAIJAN","Baku"}, + {"BELARUS (BYELORUSSIA)","Minsk"}, + {"BULGARIA","Sofia"}, {"GEORGIA","Tbilisi"}, + {"KAZAKSTAN","Almaty"}, {"KYRGYZSTAN","Alma-Ata"}, + {"MOLDOVA","Chisinau"}, {"RUSSIA","Moscow"}, + {"TAJIKISTAN","Dushanbe"}, {"TURKMENISTAN","Ashkabad"}, + {"UKRAINE","Kyiv"}, {"UZBEKISTAN","Tashkent"}, + // Europe + {"ALBANIA","Tirana"}, {"ANDORRA","Andorra la Vella"}, + {"AUSTRIA","Vienna"}, {"BELGIUM","Brussels"}, + {"BOSNIA","-"}, {"HERZEGOVINA","Sarajevo"}, + {"CROATIA","Zagreb"}, {"CZECH REPUBLIC","Prague"}, + {"DENMARK","Copenhagen"}, {"ESTONIA","Tallinn"}, + {"FINLAND","Helsinki"}, {"FRANCE","Paris"}, + {"GERMANY","Berlin"}, {"GREECE","Athens"}, + {"HUNGARY","Budapest"}, {"ICELAND","Reykjavik"}, + {"IRELAND","Dublin"}, {"ITALY","Rome"}, + {"LATVIA","Riga"}, {"LIECHTENSTEIN","Vaduz"}, + {"LITHUANIA","Vilnius"}, {"LUXEMBOURG","Luxembourg"}, + {"MACEDONIA","Skopje"}, {"MALTA","Valletta"}, + {"MONACO","Monaco"}, {"MONTENEGRO","Podgorica"}, + {"THE NETHERLANDS","Amsterdam"}, {"NORWAY","Oslo"}, + {"POLAND","Warsaw"}, {"PORTUGAL","Lisbon"}, + {"ROMANIA","Bucharest"}, {"SAN MARINO","San Marino"}, + {"SERBIA","Belgrade"}, {"SLOVAKIA","Bratislava"}, + {"SLOVENIA","Ljuijana"}, {"SPAIN","Madrid"}, + {"SWEDEN","Stockholm"}, {"SWITZERLAND","Berne"}, + {"UNITED KINGDOM","London"}, {"VATICAN CITY","---"}, + // North and Central America + {"ANTIGUA AND BARBUDA","Saint John's"}, + {"BAHAMAS","Nassau"}, + {"BARBADOS","Bridgetown"}, {"BELIZE","Belmopan"}, + {"CANADA","Ottawa"}, {"COSTA RICA","San Jose"}, + {"CUBA","Havana"}, {"DOMINICA","Roseau"}, + {"DOMINICAN REPUBLIC","Santo Domingo"}, + {"EL SALVADOR","San Salvador"}, + {"GRENADA","Saint George's"}, + {"GUATEMALA","Guatemala City"}, + {"HAITI","Port-au-Prince"}, + {"HONDURAS","Tegucigalpa"}, {"JAMAICA","Kingston"}, + {"MEXICO","Mexico City"}, {"NICARAGUA","Managua"}, + {"PANAMA","Panama City"}, {"ST. KITTS","-"}, + {"NEVIS","Basseterre"}, {"ST. LUCIA","Castries"}, + {"ST. VINCENT AND THE GRENADINES","Kingstown"}, + {"UNITED STATES OF AMERICA","Washington, D.C."}, + // South America + {"ARGENTINA","Buenos Aires"}, + {"BOLIVIA","Sucre (legal)/La Paz(administrative)"}, + {"BRAZIL","Brasilia"}, {"CHILE","Santiago"}, + {"COLOMBIA","Bogota"}, {"ECUADOR","Quito"}, + {"GUYANA","Georgetown"}, {"PARAGUAY","Asuncion"}, + {"PERU","Lima"}, {"SURINAME","Paramaribo"}, + {"TRINIDAD AND TOBAGO","Port of Spain"}, + {"URUGUAY","Montevideo"}, {"VENEZUELA","Caracas"}, + }; + // Use AbstractMap by implementing entrySet() + private static class FlyweightMap + extends AbstractMap { + private static class Entry + implements Map.Entry { + int index; + Entry(int index) { this.index = index; } + public boolean equals(Object o) { + return DATA[index][0].equals(o); + } + public String getKey() { return DATA[index][0]; } + public String getValue() { return DATA[index][1]; } + public String setValue(String value) { + throw new UnsupportedOperationException(); + } + public int hashCode() { + return DATA[index][0].hashCode(); + } + } + // Use AbstractSet by implementing size() & iterator() + static class EntrySet + extends AbstractSet> { + private int size; + EntrySet(int size) { + if(size < 0) { + this.size = 0; + } + // Can't be any bigger than the array: + else if(size > DATA.length) { + this.size = DATA.length; + } else { + this.size = size; + } + } + public int size() { return size; } + private class Iter + implements Iterator> { + // Only one Entry object per Iterator: + private Entry entry = new Entry(-1); + public boolean hasNext() { + return entry.index < size - 1; + } + public Map.Entry next() { + entry.index++; + return entry; + } + public void remove() { + throw new UnsupportedOperationException(); + } + } + public + Iterator> iterator() { + return new Iter(); + } + } + private static Set> entries = + new EntrySet(DATA.length); + public Set> entrySet() { + return entries; + } + } + // Create a partial map of 'size' countries: + static Map select(final int size) { + return new FlyweightMap() { + public Set> entrySet() { + return new EntrySet(size); + } + }; + } + static Map map = new FlyweightMap(); + public static Map capitals() { + return map; // The entire map + } + public static Map capitals(int size) { + return select(size); // A partial map + } + static List names = + new ArrayList(map.keySet()); + // All the names: + public static List names() { return names; } + // A partial list: + public static List names(int size) { + return new ArrayList(select(size).keySet()); + } + public static void main(String[] args) { + print(capitals(10)); + print(names(10)); + print(new HashMap(capitals(3))); + print(new LinkedHashMap(capitals(3))); + print(new TreeMap(capitals(3))); + print(new Hashtable(capitals(3))); + print(new HashSet(names(6))); + print(new LinkedHashSet(names(6))); + print(new TreeSet(names(6))); + print(new ArrayList(names(6))); + print(new LinkedList(names(6))); + print(capitals().get("BRAZIL")); + } +} /* Output: +{ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo, BOTSWANA=Gaberone, BULGARIA=Sofia, BURKINA FASO=Ouagadougou, BURUNDI=Bujumbura, CAMEROON=Yaounde, CAPE VERDE=Praia, CENTRAL AFRICAN REPUBLIC=Bangui} +[ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO, BURUNDI, CAMEROON, CAPE VERDE, CENTRAL AFRICAN REPUBLIC] +{BENIN=Porto-Novo, ANGOLA=Luanda, ALGERIA=Algiers} +{ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo} +{ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo} +{ALGERIA=Algiers, ANGOLA=Luanda, BENIN=Porto-Novo} +[BULGARIA, BURKINA FASO, BOTSWANA, BENIN, ANGOLA, ALGERIA] +[ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO] +[ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO] +[ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO] +[ALGERIA, ANGOLA, BENIN, BOTSWANA, BULGARIA, BURKINA FASO] +Brasilia +*///:~ diff --git a/src/net/mindview/util/DaemonThreadFactory.java b/src/net/mindview/util/DaemonThreadFactory.java new file mode 100644 index 0000000..635ee78 --- /dev/null +++ b/src/net/mindview/util/DaemonThreadFactory.java @@ -0,0 +1,11 @@ +//: net/mindview/util/DaemonThreadFactory.java +package net.mindview.util; +import java.util.concurrent.*; + +public class DaemonThreadFactory implements ThreadFactory { + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + return t; + } +} ///:~ diff --git a/src/net/mindview/util/DaemonThreadPoolExecutor.java b/src/net/mindview/util/DaemonThreadPoolExecutor.java new file mode 100644 index 0000000..03dec85 --- /dev/null +++ b/src/net/mindview/util/DaemonThreadPoolExecutor.java @@ -0,0 +1,12 @@ +//: net/mindview/util/DaemonThreadPoolExecutor.java +package net.mindview.util; +import java.util.concurrent.*; + +public class DaemonThreadPoolExecutor +extends ThreadPoolExecutor { + public DaemonThreadPoolExecutor() { + super(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, + new SynchronousQueue(), + new DaemonThreadFactory()); + } +} ///:~ diff --git a/src/net/mindview/util/Deque.java b/src/net/mindview/util/Deque.java new file mode 100644 index 0000000..1973942 --- /dev/null +++ b/src/net/mindview/util/Deque.java @@ -0,0 +1,17 @@ +//: net/mindview/util/Deque.java +// Creating a Deque from a LinkedList. +package net.mindview.util; +import java.util.*; + +public class Deque { + private LinkedList deque = new LinkedList(); + public void addFirst(T e) { deque.addFirst(e); } + public void addLast(T e) { deque.addLast(e); } + public T getFirst() { return deque.getFirst(); } + public T getLast() { return deque.getLast(); } + public T removeFirst() { return deque.removeFirst(); } + public T removeLast() { return deque.removeLast(); } + public int size() { return deque.size(); } + public String toString() { return deque.toString(); } + // And other methods as necessary... +} ///:~ diff --git a/src/net/mindview/util/Directory.java b/src/net/mindview/util/Directory.java new file mode 100644 index 0000000..f411de0 --- /dev/null +++ b/src/net/mindview/util/Directory.java @@ -0,0 +1,79 @@ +//: net/mindview/util/Directory.java +// Produce a sequence of File objects that match a +// regular expression in either a local directory, +// or by walking a directory tree. +package net.mindview.util; +import java.util.regex.*; +import java.io.*; +import java.util.*; + +public final class Directory { + public static File[] + local(File dir, final String regex) { + return dir.listFiles(new FilenameFilter() { + private Pattern pattern = Pattern.compile(regex); + public boolean accept(File dir, String name) { + return pattern.matcher( + new File(name).getName()).matches(); + } + }); + } + public static File[] + local(String path, final String regex) { // Overloaded + return local(new File(path), regex); + } + // A two-tuple for returning a pair of objects: + public static class TreeInfo implements Iterable { + public List files = new ArrayList(); + public List dirs = new ArrayList(); + // The default iterable element is the file list: + public Iterator iterator() { + return files.iterator(); + } + void addAll(TreeInfo other) { + files.addAll(other.files); + dirs.addAll(other.dirs); + } + public String toString() { + return "dirs: " + PPrint.pformat(dirs) + + "\n\nfiles: " + PPrint.pformat(files); + } + } + public static TreeInfo + walk(String start, String regex) { // Begin recursion + return recurseDirs(new File(start), regex); + } + public static TreeInfo + walk(File start, String regex) { // Overloaded + return recurseDirs(start, regex); + } + public static TreeInfo walk(File start) { // Everything + return recurseDirs(start, ".*"); + } + public static TreeInfo walk(String start) { + return recurseDirs(new File(start), ".*"); + } + static TreeInfo recurseDirs(File startDir, String regex){ + TreeInfo result = new TreeInfo(); + for(File item : startDir.listFiles()) { + if(item.isDirectory()) { + result.dirs.add(item); + result.addAll(recurseDirs(item, regex)); + } else // Regular file + if(item.getName().matches(regex)) { + result.files.add(item); + } + } + return result; + } + // Simple validation test: + public static void main(String[] args) { + if(args.length == 0) { + System.out.println(walk(".")); + } else { + for(String arg : args) { + System.out.println(walk(arg)); + } + } + } +} ///:~ diff --git a/src/net/mindview/util/Enums.java b/src/net/mindview/util/Enums.java new file mode 100644 index 0000000..d01a2bd --- /dev/null +++ b/src/net/mindview/util/Enums.java @@ -0,0 +1,13 @@ +//: net/mindview/util/Enums.java +package net.mindview.util; +import java.util.*; + +public class Enums { + private static Random rand = new Random(47); + public static > T random(Class ec) { + return random(ec.getEnumConstants()); + } + public static T random(T[] values) { + return values[rand.nextInt(values.length)]; + } +} ///:~ diff --git a/src/net/mindview/util/FiveTuple.java b/src/net/mindview/util/FiveTuple.java new file mode 100644 index 0000000..f2f3e39 --- /dev/null +++ b/src/net/mindview/util/FiveTuple.java @@ -0,0 +1,15 @@ +//: net/mindview/util/FiveTuple.java +package net.mindview.util; + +public class FiveTuple +extends FourTuple { + public final E fifth; + public FiveTuple(A a, B b, C c, D d, E e) { + super(a, b, c, d); + fifth = e; + } + public String toString() { + return "(" + first + ", " + second + ", " + + third + ", " + fourth + ", " + fifth + ")"; + } +} ///:~ diff --git a/src/net/mindview/util/FourTuple.java b/src/net/mindview/util/FourTuple.java new file mode 100644 index 0000000..4eee0e9 --- /dev/null +++ b/src/net/mindview/util/FourTuple.java @@ -0,0 +1,14 @@ +//: net/mindview/util/FourTuple.java +package net.mindview.util; + +public class FourTuple extends ThreeTuple { + public final D fourth; + public FourTuple(A a, B b, C c, D d) { + super(a, b, c); + fourth = d; + } + public String toString() { + return "(" + first + ", " + second + ", " + + third + ", " + fourth + ")"; + } +} ///:~ diff --git a/src/net/mindview/util/Generated.java b/src/net/mindview/util/Generated.java new file mode 100644 index 0000000..4eb56f4 --- /dev/null +++ b/src/net/mindview/util/Generated.java @@ -0,0 +1,18 @@ +//: net/mindview/util/Generated.java +package net.mindview.util; +import java.util.*; + +public class Generated { + // Fill an existing array: + public static T[] array(T[] a, Generator gen) { + return new CollectionData(gen, a.length).toArray(a); + } + // Create a new array: + @SuppressWarnings("unchecked") + public static T[] array(Class type, + Generator gen, int size) { + T[] a = + (T[])java.lang.reflect.Array.newInstance(type, size); + return new CollectionData(gen, size).toArray(a); + } +} ///:~ diff --git a/src/net/mindview/util/Generator.java b/src/net/mindview/util/Generator.java new file mode 100644 index 0000000..c753dd8 --- /dev/null +++ b/src/net/mindview/util/Generator.java @@ -0,0 +1,4 @@ +//: net/mindview/util/Generator.java +// A generic interface. +package net.mindview.util; +public interface Generator { T next(); } ///:~ diff --git a/src/net/mindview/util/Hex.java b/src/net/mindview/util/Hex.java new file mode 100644 index 0000000..a99fdc3 --- /dev/null +++ b/src/net/mindview/util/Hex.java @@ -0,0 +1,41 @@ +//: net/mindview/util/Hex.java +package net.mindview.util; +import java.io.*; + +public class Hex { + public static String format(byte[] data) { + StringBuilder result = new StringBuilder(); + int n = 0; + for(byte b : data) { + if(n % 16 == 0) { + result.append(String.format("%05X: ", n)); + } + result.append(String.format("%02X ", b)); + n++; + if(n % 16 == 0) { + result.append("\n"); + } + } + result.append("\n"); + return result.toString(); + } + public static void main(String[] args) throws Exception { + if(args.length == 0) + // Test by displaying this class file: + { + System.out.println( + format(BinaryFile.read("Hex.class"))); + } else { + System.out.println( + format(BinaryFile.read(new File(args[0])))); + } + } +} /* Output: (Sample) +00000: CA FE BA BE 00 00 00 31 00 52 0A 00 05 00 22 07 +00010: 00 23 0A 00 02 00 22 08 00 24 07 00 25 0A 00 26 +00020: 00 27 0A 00 28 00 29 0A 00 02 00 2A 08 00 2B 0A +00030: 00 2C 00 2D 08 00 2E 0A 00 02 00 2F 09 00 30 00 +00040: 31 08 00 32 0A 00 33 00 34 0A 00 15 00 35 0A 00 +00050: 36 00 37 07 00 38 0A 00 12 00 39 0A 00 33 00 3A +... +*///:~ diff --git a/src/net/mindview/util/MapData.java b/src/net/mindview/util/MapData.java new file mode 100644 index 0000000..2d923a3 --- /dev/null +++ b/src/net/mindview/util/MapData.java @@ -0,0 +1,60 @@ +//: net/mindview/util/MapData.java +// A Map filled with data using a generator object. +package net.mindview.util; +import java.util.*; + +public class MapData extends LinkedHashMap { + // A single Pair Generator: + public MapData(Generator> gen, int quantity) { + for(int i = 0; i < quantity; i++) { + Pair p = gen.next(); + put(p.key, p.value); + } + } + // Two separate Generators: + public MapData(Generator genK, Generator genV, + int quantity) { + for(int i = 0; i < quantity; i++) { + put(genK.next(), genV.next()); + } + } + // A key Generator and a single value: + public MapData(Generator genK, V value, int quantity){ + for(int i = 0; i < quantity; i++) { + put(genK.next(), value); + } + } + // An Iterable and a value Generator: + public MapData(Iterable genK, Generator genV) { + for(K key : genK) { + put(key, genV.next()); + } + } + // An Iterable and a single value: + public MapData(Iterable genK, V value) { + for(K key : genK) { + put(key, value); + } + } + // Generic convenience methods: + public static MapData + map(Generator> gen, int quantity) { + return new MapData(gen, quantity); + } + public static MapData + map(Generator genK, Generator genV, int quantity) { + return new MapData(genK, genV, quantity); + } + public static MapData + map(Generator genK, V value, int quantity) { + return new MapData(genK, value, quantity); + } + public static MapData + map(Iterable genK, Generator genV) { + return new MapData(genK, genV); + } + public static MapData + map(Iterable genK, V value) { + return new MapData(genK, value); + } +} ///:~ diff --git a/src/net/mindview/util/New.java b/src/net/mindview/util/New.java new file mode 100644 index 0000000..16ed4be --- /dev/null +++ b/src/net/mindview/util/New.java @@ -0,0 +1,31 @@ +//: net/mindview/util/New.java +// Utilities to simplify generic container creation +// by using type argument inference. +package net.mindview.util; +import java.util.*; + +public class New { + public static Map map() { + return new HashMap(); + } + public static List list() { + return new ArrayList(); + } + public static LinkedList lList() { + return new LinkedList(); + } + public static Set set() { + return new HashSet(); + } + public static Queue queue() { + return new LinkedList(); + } + // Examples: + public static void main(String[] args) { + Map> sls = New.map(); + List ls = New.list(); + LinkedList lls = New.lList(); + Set ss = New.set(); + Queue qs = New.queue(); + } +} ///:~ diff --git a/src/net/mindview/util/Null.java b/src/net/mindview/util/Null.java new file mode 100644 index 0000000..ec217b9 --- /dev/null +++ b/src/net/mindview/util/Null.java @@ -0,0 +1,3 @@ +//: net/mindview/util/Null.java +package net.mindview.util; +public interface Null {} ///:~ diff --git a/src/net/mindview/util/OSExecute.java b/src/net/mindview/util/OSExecute.java new file mode 100644 index 0000000..f213397 --- /dev/null +++ b/src/net/mindview/util/OSExecute.java @@ -0,0 +1,41 @@ +//: net/mindview/util/OSExecute.java +// Run an operating system command +// and send the output to the console. +package net.mindview.util; +import java.io.*; + +public class OSExecute { + public static void command(String command) { + boolean err = false; + try { + Process process = + new ProcessBuilder(command.split(" ")).start(); + BufferedReader results = new BufferedReader( + new InputStreamReader(process.getInputStream())); + String s; + while((s = results.readLine())!= null) { + System.out.println(s); + } + BufferedReader errors = new BufferedReader( + new InputStreamReader(process.getErrorStream())); + // Report errors and return nonzero value + // to calling process if there are problems: + while((s = errors.readLine())!= null) { + System.err.println(s); + err = true; + } + } catch(Exception e) { + // Compensate for Windows 2000, which throws an + // exception for the default command line: + if(!command.startsWith("CMD /C")) { + command("CMD /C " + command); + } else { + throw new RuntimeException(e); + } + } + if(err) { + throw new OSExecuteException("Errors executing " + + command); + } + } +} ///:~ diff --git a/src/net/mindview/util/OSExecuteException.java b/src/net/mindview/util/OSExecuteException.java new file mode 100644 index 0000000..766e0f2 --- /dev/null +++ b/src/net/mindview/util/OSExecuteException.java @@ -0,0 +1,6 @@ +//: net/mindview/util/OSExecuteException.java +package net.mindview.util; + +public class OSExecuteException extends RuntimeException { + public OSExecuteException(String why) { super(why); } +} ///:~ diff --git a/src/net/mindview/util/PPrint.java b/src/net/mindview/util/PPrint.java new file mode 100644 index 0000000..3d9ff1f --- /dev/null +++ b/src/net/mindview/util/PPrint.java @@ -0,0 +1,30 @@ +//: net/mindview/util/PPrint.java +// Pretty-printer for collections +package net.mindview.util; +import java.util.*; + +public class PPrint { + public static String pformat(Collection c) { + if(c.size() == 0) { + return "[]"; + } + StringBuilder result = new StringBuilder("["); + for(Object elem : c) { + if(c.size() != 1) { + result.append("\n "); + } + result.append(elem); + } + if(c.size() != 1) { + result.append("\n"); + } + result.append("]"); + return result.toString(); + } + public static void pprint(Collection c) { + System.out.println(pformat(c)); + } + public static void pprint(Object[] c) { + System.out.println(pformat(Arrays.asList(c))); + } +} ///:~ diff --git a/src/net/mindview/util/Pair.java b/src/net/mindview/util/Pair.java new file mode 100644 index 0000000..e346003 --- /dev/null +++ b/src/net/mindview/util/Pair.java @@ -0,0 +1,11 @@ +//: net/mindview/util/Pair.java +package net.mindview.util; + +public class Pair { + public final K key; + public final V value; + public Pair(K k, V v) { + key = k; + value = v; + } +} ///:~ diff --git a/src/net/mindview/util/Print.java b/src/net/mindview/util/Print.java new file mode 100644 index 0000000..0147ec6 --- /dev/null +++ b/src/net/mindview/util/Print.java @@ -0,0 +1,25 @@ +//: net/mindview/util/Print.java +// Print methods that can be used without +// qualifiers, using Java SE5 static imports: +package net.mindview.util; +import java.io.*; + +public class Print { + // Print with a newline: + public static void print(Object obj) { + System.out.println(obj); + } + // Print a newline by itself: + public static void print() { + System.out.println(); + } + // Print with no line break: + public static void printnb(Object obj) { + System.out.print(obj); + } + // The new Java SE5 printf() (from C): + public static PrintStream + printf(String format, Object... args) { + return System.out.printf(format, args); + } +} ///:~ diff --git a/src/net/mindview/util/ProcessFiles.java b/src/net/mindview/util/ProcessFiles.java new file mode 100644 index 0000000..0c1e523 --- /dev/null +++ b/src/net/mindview/util/ProcessFiles.java @@ -0,0 +1,53 @@ +//: net/mindview/util/ProcessFiles.java +package net.mindview.util; +import java.io.*; + +public class ProcessFiles { + public interface Strategy { + void process(File file); + } + private Strategy strategy; + private String ext; + public ProcessFiles(Strategy strategy, String ext) { + this.strategy = strategy; + this.ext = ext; + } + public void start(String[] args) { + try { + if(args.length == 0) { + processDirectoryTree(new File(".")); + } else { + for(String arg : args) { + File fileArg = new File(arg); + if(fileArg.isDirectory()) { + processDirectoryTree(fileArg); + } else { + // Allow user to leave off extension: + if(!arg.endsWith("." + ext)) { + arg += "." + ext; + } + strategy.process( + new File(arg).getCanonicalFile()); + } + } + } + } catch(IOException e) { + throw new RuntimeException(e); + } + } + public void + processDirectoryTree(File root) throws IOException { + for(File file : Directory.walk( + root.getAbsolutePath(), ".*\\." + ext)) { + strategy.process(file.getCanonicalFile()); + } + } + // Demonstration of how to use it: + public static void main(String[] args) { + new ProcessFiles(new ProcessFiles.Strategy() { + public void process(File file) { + System.out.println(file); + } + }, "java").start(args); + } +} /* (Execute to see output) *///:~ diff --git a/src/net/mindview/util/RandomGenerator.java b/src/net/mindview/util/RandomGenerator.java new file mode 100644 index 0000000..1aee91a --- /dev/null +++ b/src/net/mindview/util/RandomGenerator.java @@ -0,0 +1,73 @@ +//: net/mindview/util/RandomGenerator.java +// Generators that produce random values. +package net.mindview.util; +import java.util.*; + +public class RandomGenerator { + private static Random r = new Random(47); + public static class + Boolean implements Generator { + public java.lang.Boolean next() { + return r.nextBoolean(); + } + } + public static class + Byte implements Generator { + public java.lang.Byte next() { + return (byte)r.nextInt(); + } + } + public static class + Character implements Generator { + public java.lang.Character next() { + return CountingGenerator.chars[ + r.nextInt(CountingGenerator.chars.length)]; + } + } + public static class + String extends CountingGenerator.String { + // Plug in the random Character generator: + { cg = new Character(); } // Instance initializer + public String() {} + public String(int length) { super(length); } + } + public static class + Short implements Generator { + public java.lang.Short next() { + return (short)r.nextInt(); + } + } + public static class + Integer implements Generator { + private int mod = 10000; + public Integer() {} + public Integer(int modulo) { mod = modulo; } + public java.lang.Integer next() { + return r.nextInt(mod); + } + } + public static class + Long implements Generator { + private int mod = 10000; + public Long() {} + public Long(int modulo) { mod = modulo; } + public java.lang.Long next() { + return new java.lang.Long(r.nextInt(mod)); + } + } + public static class + Float implements Generator { + public java.lang.Float next() { + // Trim all but the first two decimal places: + int trimmed = Math.round(r.nextFloat() * 100); + return ((float)trimmed) / 100; + } + } + public static class + Double implements Generator { + public java.lang.Double next() { + long trimmed = Math.round(r.nextDouble() * 100); + return ((double)trimmed) / 100; + } + } +} ///:~ diff --git a/src/net/mindview/util/Range.java b/src/net/mindview/util/Range.java new file mode 100644 index 0000000..dc0e34c --- /dev/null +++ b/src/net/mindview/util/Range.java @@ -0,0 +1,33 @@ +//: net/mindview/util/Range.java +// Array creation methods that can be used without +// qualifiers, using Java SE5 static imports: +package net.mindview.util; + +public class Range { + // Produce a sequence [0..n) + public static int[] range(int n) { + int[] result = new int[n]; + for(int i = 0; i < n; i++) { + result[i] = i; + } + return result; + } + // Produce a sequence [start..end) + public static int[] range(int start, int end) { + int sz = end - start; + int[] result = new int[sz]; + for(int i = 0; i < sz; i++) { + result[i] = start + i; + } + return result; + } + // Produce a sequence [start..end) incrementing by step + public static int[] range(int start, int end, int step) { + int sz = (end - start)/step; + int[] result = new int[sz]; + for(int i = 0; i < sz; i++) { + result[i] = start + (i * step); + } + return result; + } +} ///:~ diff --git a/src/net/mindview/util/Sets.java b/src/net/mindview/util/Sets.java new file mode 100644 index 0000000..d04afc1 --- /dev/null +++ b/src/net/mindview/util/Sets.java @@ -0,0 +1,28 @@ +//: net/mindview/util/Sets.java +package net.mindview.util; +import java.util.*; + +public class Sets { + public static Set union(Set a, Set b) { + Set result = new HashSet(a); + result.addAll(b); + return result; + } + public static + Set intersection(Set a, Set b) { + Set result = new HashSet(a); + result.retainAll(b); + return result; + } + // Subtract subset from superset: + public static Set + difference(Set superset, Set subset) { + Set result = new HashSet(superset); + result.removeAll(subset); + return result; + } + // Reflexive--everything not in the intersection: + public static Set complement(Set a, Set b) { + return difference(union(a, b), intersection(a, b)); + } +} ///:~ diff --git a/src/net/mindview/util/Stack.java b/src/net/mindview/util/Stack.java new file mode 100644 index 0000000..bdcb42e --- /dev/null +++ b/src/net/mindview/util/Stack.java @@ -0,0 +1,30 @@ +//: net/mindview/util/Stack.java +// Making a stack from a LinkedList. +package net.mindview.util; + +import java.util.LinkedList; + +public class Stack { + private LinkedList storage = new LinkedList(); + + public void push(T v) { + storage.addFirst(v); + } + + public T peek() { + return storage.getFirst(); + } + + public T pop() { + return storage.removeFirst(); + } + + public boolean empty() { + return storage.isEmpty(); + } + + @Override + public String toString() { + return storage.toString(); + } +} ///:~ diff --git a/src/net/mindview/util/SwingConsole.java b/src/net/mindview/util/SwingConsole.java new file mode 100644 index 0000000..279e013 --- /dev/null +++ b/src/net/mindview/util/SwingConsole.java @@ -0,0 +1,19 @@ +//: net/mindview/util/SwingConsole.java +// Tool for running Swing demos from the +// console, both applets and JFrames. +package net.mindview.util; +import javax.swing.*; + +public class SwingConsole { + public static void + run(final JFrame f, final int width, final int height) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + f.setTitle(f.getClass().getSimpleName()); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.setSize(width, height); + f.setVisible(true); + } + }); + } +} ///:~ diff --git a/src/net/mindview/util/TaskItem.java b/src/net/mindview/util/TaskItem.java new file mode 100644 index 0000000..a7827a4 --- /dev/null +++ b/src/net/mindview/util/TaskItem.java @@ -0,0 +1,13 @@ +//: net/mindview/util/TaskItem.java +// A Future and the Callable that produced it. +package net.mindview.util; +import java.util.concurrent.*; + +public class TaskItem> { + public final Future future; + public final C task; + public TaskItem(Future future, C task) { + this.future = future; + this.task = task; + } +} ///:~ diff --git a/src/net/mindview/util/TaskManager.java b/src/net/mindview/util/TaskManager.java new file mode 100644 index 0000000..b96f65e --- /dev/null +++ b/src/net/mindview/util/TaskManager.java @@ -0,0 +1,44 @@ +//: net/mindview/util/TaskManager.java +// Managing and executing a queue of tasks. +package net.mindview.util; +import java.util.concurrent.*; +import java.util.*; + +public class TaskManager> +extends ArrayList> { + private ExecutorService exec = + Executors.newSingleThreadExecutor(); + public void add(C task) { + add(new TaskItem(exec.submit(task),task)); + } + public List getResults() { + Iterator> items = iterator(); + List results = new ArrayList(); + while(items.hasNext()) { + TaskItem item = items.next(); + if(item.future.isDone()) { + try { + results.add(item.future.get()); + } catch(Exception e) { + throw new RuntimeException(e); + } + items.remove(); + } + } + return results; + } + public List purge() { + Iterator> items = iterator(); + List results = new ArrayList(); + while(items.hasNext()) { + TaskItem item = items.next(); + // Leave completed tasks for results reporting: + if(!item.future.isDone()) { + results.add("Cancelling " + item.task); + item.future.cancel(true); // May interrupt + items.remove(); + } + } + return results; + } +} ///:~ diff --git a/src/net/mindview/util/TextFile.java b/src/net/mindview/util/TextFile.java new file mode 100644 index 0000000..8703cee --- /dev/null +++ b/src/net/mindview/util/TextFile.java @@ -0,0 +1,85 @@ +//: net/mindview/util/TextFile.java +// Static functions for reading and writing text files as +// a single string, and treating a file as an ArrayList. +package net.mindview.util; +import java.io.*; +import java.util.*; + +public class TextFile extends ArrayList { + // Read a file as a single string: + public static String read(String fileName) { + StringBuilder sb = new StringBuilder(); + try { + BufferedReader in= new BufferedReader(new FileReader( + new File(fileName).getAbsoluteFile())); + try { + String s; + while((s = in.readLine()) != null) { + sb.append(s); + sb.append("\n"); + } + } finally { + in.close(); + } + } catch(IOException e) { + throw new RuntimeException(e); + } + return sb.toString(); + } + // Write a single file in one method call: + public static void write(String fileName, String text) { + try { + PrintWriter out = new PrintWriter( + new File(fileName).getAbsoluteFile()); + try { + out.print(text); + } finally { + out.close(); + } + } catch(IOException e) { + throw new RuntimeException(e); + } + } + // Read a file, split by any regular expression: + public TextFile(String fileName, String splitter) { + super(Arrays.asList(read(fileName).split(splitter))); + // Regular expression split() often leaves an empty + // String at the first position: + if(get(0).equals("")) { + remove(0); + } + } + // Normally read by lines: + public TextFile(String fileName) { + this(fileName, "\n"); + } + public void write(String fileName) { + try { + PrintWriter out = new PrintWriter( + new File(fileName).getAbsoluteFile()); + try { + for(String item : this) { + out.println(item); + } + } finally { + out.close(); + } + } catch(IOException e) { + throw new RuntimeException(e); + } + } + // Simple test: + public static void main(String[] args) { + String file = read("TextFile.java"); + write("test.txt", file); + TextFile text = new TextFile("test.txt"); + text.write("test2.txt"); + // Break into unique sorted list of words: + TreeSet words = new TreeSet( + new TextFile("TextFile.java", "\\W+")); + // Display the capitalized words: + System.out.println(words.headSet("a")); + } +} /* Output: +[0, ArrayList, Arrays, Break, BufferedReader, BufferedWriter, Clean, Display, File, FileReader, FileWriter, IOException, Normally, Output, PrintWriter, Read, Regular, RuntimeException, Simple, Static, String, StringBuilder, System, TextFile, Tools, TreeSet, W, Write] +*///:~ diff --git a/src/net/mindview/util/ThreeTuple.java b/src/net/mindview/util/ThreeTuple.java new file mode 100644 index 0000000..aed60a0 --- /dev/null +++ b/src/net/mindview/util/ThreeTuple.java @@ -0,0 +1,13 @@ +//: net/mindview/util/ThreeTuple.java +package net.mindview.util; + +public class ThreeTuple extends TwoTuple { + public final C third; + public ThreeTuple(A a, B b, C c) { + super(a, b); + third = c; + } + public String toString() { + return "(" + first + ", " + second + ", " + third +")"; + } +} ///:~ diff --git a/src/net/mindview/util/Tuple.java b/src/net/mindview/util/Tuple.java new file mode 100644 index 0000000..8fe2c8f --- /dev/null +++ b/src/net/mindview/util/Tuple.java @@ -0,0 +1,21 @@ +//: net/mindview/util/Tuple.java +// Tuple library using type argument inference. +package net.mindview.util; + +public class Tuple { + public static TwoTuple tuple(A a, B b) { + return new TwoTuple(a, b); + } + public static ThreeTuple + tuple(A a, B b, C c) { + return new ThreeTuple(a, b, c); + } + public static FourTuple + tuple(A a, B b, C c, D d) { + return new FourTuple(a, b, c, d); + } + public static + FiveTuple tuple(A a, B b, C c, D d, E e) { + return new FiveTuple(a, b, c, d, e); + } +} ///:~ diff --git a/src/net/mindview/util/TwoTuple.java b/src/net/mindview/util/TwoTuple.java new file mode 100644 index 0000000..02e6f7d --- /dev/null +++ b/src/net/mindview/util/TwoTuple.java @@ -0,0 +1,11 @@ +//: net/mindview/util/TwoTuple.java +package net.mindview.util; + +public class TwoTuple { + public final A first; + public final B second; + public TwoTuple(A a, B b) { first = a; second = b; } + public String toString() { + return "(" + first + ", " + second + ")"; + } +} ///:~ diff --git a/src/net/mindview/util/TypeCounter.java b/src/net/mindview/util/TypeCounter.java new file mode 100644 index 0000000..6362e0e --- /dev/null +++ b/src/net/mindview/util/TypeCounter.java @@ -0,0 +1,41 @@ +//: net/mindview/util/TypeCounter.java +// Counts instances of a type family. +package net.mindview.util; +import java.util.*; + +public class TypeCounter extends HashMap,Integer>{ + private Class baseType; + public TypeCounter(Class baseType) { + this.baseType = baseType; + } + public void count(Object obj) { + Class type = obj.getClass(); + if(!baseType.isAssignableFrom(type)) { + throw new RuntimeException(obj + " incorrect type: " + + type + ", should be type or subtype of " + + baseType); + } + countClass(type); + } + private void countClass(Class type) { + Integer quantity = get(type); + put(type, quantity == null ? 1 : quantity + 1); + Class superClass = type.getSuperclass(); + if(superClass != null && + baseType.isAssignableFrom(superClass)) { + countClass(superClass); + } + } + public String toString() { + StringBuilder result = new StringBuilder("{"); + for(Map.Entry,Integer> pair : entrySet()) { + result.append(pair.getKey().getSimpleName()); + result.append("="); + result.append(pair.getValue()); + result.append(", "); + } + result.delete(result.length()-2, result.length()); + result.append("}"); + return result.toString(); + } +} ///:~ diff --git a/src/object/Documentation1.java b/src/object/Documentation1.java new file mode 100644 index 0000000..d90d72a --- /dev/null +++ b/src/object/Documentation1.java @@ -0,0 +1,8 @@ +package object;//: object/Documentation1.java +/** A class comment */ +public class Documentation1 { + /** A field comment */ + public int i; + /** A method comment */ + public void f() {} +} ///:~ diff --git a/src/object/Documentation2.java b/src/object/Documentation2.java new file mode 100644 index 0000000..fa263d5 --- /dev/null +++ b/src/object/Documentation2.java @@ -0,0 +1,8 @@ +package object;//: object/Documentation2.java +/** +*
+* System.out.println(new Date());
+* 
+*/ +public class Documentation2 {} +///:~ diff --git a/src/object/Documentation3.java b/src/object/Documentation3.java new file mode 100644 index 0000000..565a6b8 --- /dev/null +++ b/src/object/Documentation3.java @@ -0,0 +1,11 @@ +package object;//: object/Documentation3.java +/** +* You can even insert a list: +*
    +*
  1. Item one +*
  2. Item two +*
  3. Item three +*
+*/ +public class Documentation3 {} +///:~ diff --git a/src/object/HelloDate.java b/src/object/HelloDate.java new file mode 100644 index 0000000..9e76b71 --- /dev/null +++ b/src/object/HelloDate.java @@ -0,0 +1,22 @@ +package object;//: object/HelloDate.java +import java.util.*; + +/** The first Thinking in Java example program. + * Displays a string and today's date. + * @author Bruce Eckel + * @author www.MindView.net + * @version 4.0 +*/ +public class HelloDate { + /** Entry point to class & application. + * @param args array of string arguments + * @throws exceptions No exceptions thrown + */ + public static void main(String[] args) { + System.out.println("Hello, it's: "); + System.out.println(new Date()); + } +} /* Output: (55% match) +Hello, it's: +Wed Oct 05 14:39:36 MDT 2005 +*///:~ diff --git a/src/object/ShowProperties.java b/src/object/ShowProperties.java new file mode 100644 index 0000000..7da2bfd --- /dev/null +++ b/src/object/ShowProperties.java @@ -0,0 +1,10 @@ +package object;//: object/ShowProperties.java + +public class ShowProperties { + public static void main(String[] args) { + System.getProperties().list(System.out); + System.out.println(System.getProperty("user.name")); + System.out.println( + System.getProperty("java.library.path")); + } +} ///:~ diff --git a/src/object/build.xml b/src/object/build.xml new file mode 100644 index 0000000..d884e4e --- /dev/null +++ b/src/object/build.xml @@ -0,0 +1,81 @@ + + + + + + build.xml for the source code for the object chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/operators/AllOps.java b/src/operators/AllOps.java new file mode 100644 index 0000000..ada9d2d --- /dev/null +++ b/src/operators/AllOps.java @@ -0,0 +1,408 @@ +package operators;//: operators/AllOps.java +// Tests all the operators on all the primitive data types +// to show which ones are accepted by the Java compiler. + +public class AllOps { + // To accept the results of a boolean test: + void f(boolean b) {} + void boolTest(boolean x, boolean y) { + // Arithmetic operators: + //! x = x * y; + //! x = x / y; + //! x = x % y; + //! x = x + y; + //! x = x - y; + //! x++; + //! x--; + //! x = +y; + //! x = -y; + // Relational and logical: + //! f(x > y); + //! f(x >= y); + //! f(x < y); + //! f(x <= y); + f(x == y); + f(x != y); + f(!y); + x = x && y; + x = x || y; + // Bitwise operators: + //! x = ~y; + x = x & y; + x = x | y; + x = x ^ y; + //! x = x << 1; + //! x = x >> 1; + //! x = x >>> 1; + // Compound assignment: + //! x += y; + //! x -= y; + //! x *= y; + //! x /= y; + //! x %= y; + //! x <<= 1; + //! x >>= 1; + //! x >>>= 1; + x &= y; + x ^= y; + x |= y; + // Casting: + //! char c = (char)x; + //! byte b = (byte)x; + //! short s = (short)x; + //! int i = (int)x; + //! long l = (long)x; + //! float f = (float)x; + //! double d = (double)x; + } + void charTest(char x, char y) { + // Arithmetic operators: + x = (char)(x * y); + x = (char)(x / y); + x = (char)(x % y); + x = (char)(x + y); + x = (char)(x - y); + x++; + x--; + x = (char)+y; + x = (char)-y; + // Relational and logical: + f(x > y); + f(x >= y); + f(x < y); + f(x <= y); + f(x == y); + f(x != y); + //! f(!x); + //! f(x && y); + //! f(x || y); + // Bitwise operators: + x= (char)~y; + x = (char)(x & y); + x = (char)(x | y); + x = (char)(x ^ y); + x = (char)(x << 1); + x = (char)(x >> 1); + x = (char)(x >>> 1); + // Compound assignment: + x += y; + x -= y; + x *= y; + x /= y; + x %= y; + x <<= 1; + x >>= 1; + x >>>= 1; + x &= y; + x ^= y; + x |= y; + // Casting: + //! boolean bl = (boolean)x; + byte b = (byte)x; + short s = (short)x; + int i = (int)x; + long l = (long)x; + float f = (float)x; + double d = (double)x; + } + void byteTest(byte x, byte y) { + // Arithmetic operators: + x = (byte)(x* y); + x = (byte)(x / y); + x = (byte)(x % y); + x = (byte)(x + y); + x = (byte)(x - y); + x++; + x--; + x = (byte)+ y; + x = (byte)- y; + // Relational and logical: + f(x > y); + f(x >= y); + f(x < y); + f(x <= y); + f(x == y); + f(x != y); + //! f(!x); + //! f(x && y); + //! f(x || y); + // Bitwise operators: + x = (byte)~y; + x = (byte)(x & y); + x = (byte)(x | y); + x = (byte)(x ^ y); + x = (byte)(x << 1); + x = (byte)(x >> 1); + x = (byte)(x >>> 1); + // Compound assignment: + x += y; + x -= y; + x *= y; + x /= y; + x %= y; + x <<= 1; + x >>= 1; + x >>>= 1; + x &= y; + x ^= y; + x |= y; + // Casting: + //! boolean bl = (boolean)x; + char c = (char)x; + short s = (short)x; + int i = (int)x; + long l = (long)x; + float f = (float)x; + double d = (double)x; + } + void shortTest(short x, short y) { + // Arithmetic operators: + x = (short)(x * y); + x = (short)(x / y); + x = (short)(x % y); + x = (short)(x + y); + x = (short)(x - y); + x++; + x--; + x = (short)+y; + x = (short)-y; + // Relational and logical: + f(x > y); + f(x >= y); + f(x < y); + f(x <= y); + f(x == y); + f(x != y); + //! f(!x); + //! f(x && y); + //! f(x || y); + // Bitwise operators: + x = (short)~y; + x = (short)(x & y); + x = (short)(x | y); + x = (short)(x ^ y); + x = (short)(x << 1); + x = (short)(x >> 1); + x = (short)(x >>> 1); + // Compound assignment: + x += y; + x -= y; + x *= y; + x /= y; + x %= y; + x <<= 1; + x >>= 1; + x >>>= 1; + x &= y; + x ^= y; + x |= y; + // Casting: + //! boolean bl = (boolean)x; + char c = (char)x; + byte b = (byte)x; + int i = (int)x; + long l = (long)x; + float f = (float)x; + double d = (double)x; + } + void intTest(int x, int y) { + // Arithmetic operators: + x = x * y; + x = x / y; + x = x % y; + x = x + y; + x = x - y; + x++; + x--; + x = +y; + x = -y; + // Relational and logical: + f(x > y); + f(x >= y); + f(x < y); + f(x <= y); + f(x == y); + f(x != y); + //! f(!x); + //! f(x && y); + //! f(x || y); + // Bitwise operators: + x = ~y; + x = x & y; + x = x | y; + x = x ^ y; + x = x << 1; + x = x >> 1; + x = x >>> 1; + // Compound assignment: + x += y; + x -= y; + x *= y; + x /= y; + x %= y; + x <<= 1; + x >>= 1; + x >>>= 1; + x &= y; + x ^= y; + x |= y; + // Casting: + //! boolean bl = (boolean)x; + char c = (char)x; + byte b = (byte)x; + short s = (short)x; + long l = (long)x; + float f = (float)x; + double d = (double)x; + } + void longTest(long x, long y) { + // Arithmetic operators: + x = x * y; + x = x / y; + x = x % y; + x = x + y; + x = x - y; + x++; + x--; + x = +y; + x = -y; + // Relational and logical: + f(x > y); + f(x >= y); + f(x < y); + f(x <= y); + f(x == y); + f(x != y); + //! f(!x); + //! f(x && y); + //! f(x || y); + // Bitwise operators: + x = ~y; + x = x & y; + x = x | y; + x = x ^ y; + x = x << 1; + x = x >> 1; + x = x >>> 1; + // Compound assignment: + x += y; + x -= y; + x *= y; + x /= y; + x %= y; + x <<= 1; + x >>= 1; + x >>>= 1; + x &= y; + x ^= y; + x |= y; + // Casting: + //! boolean bl = (boolean)x; + char c = (char)x; + byte b = (byte)x; + short s = (short)x; + int i = (int)x; + float f = (float)x; + double d = (double)x; + } + void floatTest(float x, float y) { + // Arithmetic operators: + x = x * y; + x = x / y; + x = x % y; + x = x + y; + x = x - y; + x++; + x--; + x = +y; + x = -y; + // Relational and logical: + f(x > y); + f(x >= y); + f(x < y); + f(x <= y); + f(x == y); + f(x != y); + //! f(!x); + //! f(x && y); + //! f(x || y); + // Bitwise operators: + //! x = ~y; + //! x = x & y; + //! x = x | y; + //! x = x ^ y; + //! x = x << 1; + //! x = x >> 1; + //! x = x >>> 1; + // Compound assignment: + x += y; + x -= y; + x *= y; + x /= y; + x %= y; + //! x <<= 1; + //! x >>= 1; + //! x >>>= 1; + //! x &= y; + //! x ^= y; + //! x |= y; + // Casting: + //! boolean bl = (boolean)x; + char c = (char)x; + byte b = (byte)x; + short s = (short)x; + int i = (int)x; + long l = (long)x; + double d = (double)x; + } + void doubleTest(double x, double y) { + // Arithmetic operators: + x = x * y; + x = x / y; + x = x % y; + x = x + y; + x = x - y; + x++; + x--; + x = +y; + x = -y; + // Relational and logical: + f(x > y); + f(x >= y); + f(x < y); + f(x <= y); + f(x == y); + f(x != y); + //! f(!x); + //! f(x && y); + //! f(x || y); + // Bitwise operators: + //! x = ~y; + //! x = x & y; + //! x = x | y; + //! x = x ^ y; + //! x = x << 1; + //! x = x >> 1; + //! x = x >>> 1; + // Compound assignment: + x += y; + x -= y; + x *= y; + x /= y; + x %= y; + //! x <<= 1; + //! x >>= 1; + //! x >>>= 1; + //! x &= y; + //! x ^= y; + //! x |= y; + // Casting: + //! boolean bl = (boolean)x; + char c = (char)x; + byte b = (byte)x; + short s = (short)x; + int i = (int)x; + long l = (long)x; + float f = (float)x; + } +} ///:~ diff --git a/src/operators/Assignment.java b/src/operators/Assignment.java new file mode 100644 index 0000000..7e3be53 --- /dev/null +++ b/src/operators/Assignment.java @@ -0,0 +1,28 @@ +package operators;//: operators/Assignment.java +// Assignment with objects is a bit tricky. +import static net.mindview.util.Print.*; + +class Tank { + int level; +} + +public class Assignment { + public static void main(String[] args) { + Tank t1 = new Tank(); + Tank t2 = new Tank(); + t1.level = 9; + t2.level = 47; + print("1: t1.level: " + t1.level + + ", t2.level: " + t2.level); + t1 = t2; + print("2: t1.level: " + t1.level + + ", t2.level: " + t2.level); + t1.level = 27; + print("3: t1.level: " + t1.level + + ", t2.level: " + t2.level); + } +} /* Output: +1: t1.level: 9, t2.level: 47 +2: t1.level: 47, t2.level: 47 +3: t1.level: 27, t2.level: 27 +*///:~ diff --git a/src/operators/AutoInc.java b/src/operators/AutoInc.java new file mode 100644 index 0000000..a7231c0 --- /dev/null +++ b/src/operators/AutoInc.java @@ -0,0 +1,24 @@ +package operators;//: operators/AutoInc.java +// Demonstrates the ++ and -- operators. +import static net.mindview.util.Print.*; + +public class AutoInc { + public static void main(String[] args) { + int i = 1; + print("i : " + i); + print("++i : " + ++i); // Pre-increment + print("i++ : " + i++); // Post-increment + print("i : " + i); + print("--i : " + --i); // Pre-decrement + print("i-- : " + i--); // Post-decrement + print("i : " + i); + } +} /* Output: +i : 1 +++i : 2 +i++ : 2 +i : 3 +--i : 2 +i-- : 2 +i : 1 +*///:~ diff --git a/src/operators/BitManipulation.java b/src/operators/BitManipulation.java new file mode 100644 index 0000000..92b23c7 --- /dev/null +++ b/src/operators/BitManipulation.java @@ -0,0 +1,93 @@ +package operators;//: operators/BitManipulation.java +// Using the bitwise operators. +import java.util.*; +import static net.mindview.util.Print.*; + +public class BitManipulation { + public static void main(String[] args) { + Random rand = new Random(47); + int i = rand.nextInt(); + int j = rand.nextInt(); + printBinaryInt("-1", -1); + printBinaryInt("+1", +1); + int maxpos = 2147483647; + printBinaryInt("maxpos", maxpos); + int maxneg = -2147483648; + printBinaryInt("maxneg", maxneg); + printBinaryInt("i", i); + printBinaryInt("~i", ~i); + printBinaryInt("-i", -i); + printBinaryInt("j", j); + printBinaryInt("i & j", i & j); + printBinaryInt("i | j", i | j); + printBinaryInt("i ^ j", i ^ j); + printBinaryInt("i << 5", i << 5); + printBinaryInt("i >> 5", i >> 5); + printBinaryInt("(~i) >> 5", (~i) >> 5); + printBinaryInt("i >>> 5", i >>> 5); + printBinaryInt("(~i) >>> 5", (~i) >>> 5); + + long l = rand.nextLong(); + long m = rand.nextLong(); + printBinaryLong("-1L", -1L); + printBinaryLong("+1L", +1L); + long ll = 9223372036854775807L; + printBinaryLong("maxpos", ll); + long lln = -9223372036854775808L; + printBinaryLong("maxneg", lln); + printBinaryLong("l", l); + printBinaryLong("~l", ~l); + printBinaryLong("-l", -l); + printBinaryLong("m", m); + printBinaryLong("l & m", l & m); + printBinaryLong("l | m", l | m); + printBinaryLong("l ^ m", l ^ m); + printBinaryLong("l << 5", l << 5); + printBinaryLong("l >> 5", l >> 5); + printBinaryLong("(~l) >> 5", (~l) >> 5); + printBinaryLong("l >>> 5", l >>> 5); + printBinaryLong("(~l) >>> 5", (~l) >>> 5); + } + static void printBinaryInt(String s, int i) { + print(s + ", int: " + i + ", binary:\n " + + Integer.toBinaryString(i)); + } + static void printBinaryLong(String s, long l) { + print(s + ", long: " + l + ", binary:\n " + + Long.toBinaryString(l)); + } +} /* Output: +-1, int: -1, binary: + 11111111111111111111111111111111 ++1, int: 1, binary: + 1 +maxpos, int: 2147483647, binary: + 1111111111111111111111111111111 +maxneg, int: -2147483648, binary: + 10000000000000000000000000000000 +i, int: -1172028779, binary: + 10111010001001000100001010010101 +~i, int: 1172028778, binary: + 1000101110110111011110101101010 +-i, int: 1172028779, binary: + 1000101110110111011110101101011 +j, int: 1717241110, binary: + 1100110010110110000010100010110 +i & j, int: 570425364, binary: + 100010000000000000000000010100 +i | j, int: -25213033, binary: + 11111110011111110100011110010111 +i ^ j, int: -595638397, binary: + 11011100011111110100011110000011 +i << 5, int: 1149784736, binary: + 1000100100010000101001010100000 +i >> 5, int: -36625900, binary: + 11111101110100010010001000010100 +(~i) >> 5, int: 36625899, binary: + 10001011101101110111101011 +i >>> 5, int: 97591828, binary: + 101110100010010001000010100 +(~i) >>> 5, int: 36625899, binary: + 10001011101101110111101011 +... +*///:~ diff --git a/src/operators/Bool.java b/src/operators/Bool.java new file mode 100644 index 0000000..b6fb6da --- /dev/null +++ b/src/operators/Bool.java @@ -0,0 +1,39 @@ +package operators;//: operators/Bool.java +// Relational and logical operators. +import java.util.*; +import static net.mindview.util.Print.*; + +public class Bool { + public static void main(String[] args) { + Random rand = new Random(47); + int i = rand.nextInt(100); + int j = rand.nextInt(100); + print("i = " + i); + print("j = " + j); + print("i > j is " + (i > j)); + print("i < j is " + (i < j)); + print("i >= j is " + (i >= j)); + print("i <= j is " + (i <= j)); + print("i == j is " + (i == j)); + print("i != j is " + (i != j)); + // Treating an int as a boolean is not legal Java: +//! print("i && j is " + (i && j)); +//! print("i || j is " + (i || j)); +//! print("!i is " + !i); + print("(i < 10) && (j < 10) is " + + ((i < 10) && (j < 10)) ); + print("(i < 10) || (j < 10) is " + + ((i < 10) || (j < 10)) ); + } +} /* Output: +i = 58 +j = 55 +i > j is true +i < j is false +i >= j is true +i <= j is false +i == j is false +i != j is true +(i < 10) && (j < 10) is false +(i < 10) || (j < 10) is false +*///:~ diff --git a/src/operators/Casting.java b/src/operators/Casting.java new file mode 100644 index 0000000..0413a97 --- /dev/null +++ b/src/operators/Casting.java @@ -0,0 +1,13 @@ +package operators;//: operators/Casting.java + +public class Casting { + public static void main(String[] args) { + int i = 200; + long lng = (long)i; + lng = i; // "Widening," so cast not really required + long lng2 = (long)200; + lng2 = 200; + // A "narrowing conversion": + i = (int)lng2; // Cast required + } +} ///:~ diff --git a/src/operators/CastingNumbers.java b/src/operators/CastingNumbers.java new file mode 100644 index 0000000..2bc9249 --- /dev/null +++ b/src/operators/CastingNumbers.java @@ -0,0 +1,20 @@ +package operators;//: operators/CastingNumbers.java +// What happens when you cast a float +// or double to an integral value? +import static net.mindview.util.Print.*; + +public class CastingNumbers { + public static void main(String[] args) { + double above = 0.7, below = 0.4; + float fabove = 0.7f, fbelow = 0.4f; + print("(int)above: " + (int)above); + print("(int)below: " + (int)below); + print("(int)fabove: " + (int)fabove); + print("(int)fbelow: " + (int)fbelow); + } +} /* Output: +(int)above: 0 +(int)below: 0 +(int)fabove: 0 +(int)fbelow: 0 +*///:~ diff --git a/src/operators/EqualsMethod.java b/src/operators/EqualsMethod.java new file mode 100644 index 0000000..1a21cd5 --- /dev/null +++ b/src/operators/EqualsMethod.java @@ -0,0 +1,11 @@ +package operators;//: operators/EqualsMethod.java + +public class EqualsMethod { + public static void main(String[] args) { + Integer n1 = new Integer(47); + Integer n2 = new Integer(47); + System.out.println(n1.equals(n2)); + } +} /* Output: +true +*///:~ diff --git a/src/operators/EqualsMethod2.java b/src/operators/EqualsMethod2.java new file mode 100644 index 0000000..9ea2d86 --- /dev/null +++ b/src/operators/EqualsMethod2.java @@ -0,0 +1,17 @@ +package operators;//: operators/EqualsMethod2.java +// Default equals() does not compare contents. + +class Value { + int i; +} + +public class EqualsMethod2 { + public static void main(String[] args) { + Value v1 = new Value(); + Value v2 = new Value(); + v1.i = v2.i = 100; + System.out.println(v1.equals(v2)); + } +} /* Output: +false +*///:~ diff --git a/src/operators/Equivalence.java b/src/operators/Equivalence.java new file mode 100644 index 0000000..c9285f4 --- /dev/null +++ b/src/operators/Equivalence.java @@ -0,0 +1,13 @@ +package operators;//: operators/Equivalence.java + +public class Equivalence { + public static void main(String[] args) { + Integer n1 = new Integer(47); + Integer n2 = new Integer(47); + System.out.println(n1 == n2); + System.out.println(n1 != n2); + } +} /* Output: +false +true +*///:~ diff --git a/src/operators/Exponents.java b/src/operators/Exponents.java new file mode 100644 index 0000000..8523378 --- /dev/null +++ b/src/operators/Exponents.java @@ -0,0 +1,17 @@ +package operators;//: operators/Exponents.java +// "e" means "10 to the power." + +public class Exponents { + public static void main(String[] args) { + // Uppercase and lowercase 'e' are the same: + float expFloat = 1.39e-43f; + expFloat = 1.39E-43f; + System.out.println(expFloat); + double expDouble = 47e47d; // 'd' is optional + double expDouble2 = 47e47; // Automatically double + System.out.println(expDouble); + } +} /* Output: +1.39E-43 +4.7E48 +*///:~ diff --git a/src/operators/HelloDate.java b/src/operators/HelloDate.java new file mode 100644 index 0000000..a43c15e --- /dev/null +++ b/src/operators/HelloDate.java @@ -0,0 +1,13 @@ +package operators;//: operators/HelloDate.java +import java.util.*; +import static net.mindview.util.Print.*; + +public class HelloDate { + public static void main(String[] args) { + print("Hello, it's: "); + print(new Date()); + } +} /* Output: (55% match) +Hello, it's: +Wed Oct 05 14:39:05 MDT 2005 +*///:~ diff --git a/src/operators/Literals.java b/src/operators/Literals.java new file mode 100644 index 0000000..ee96413 --- /dev/null +++ b/src/operators/Literals.java @@ -0,0 +1,35 @@ +package operators;//: operators/Literals.java +import static net.mindview.util.Print.*; + +public class Literals { + public static void main(String[] args) { + int i1 = 0x2f; // Hexadecimal (lowercase) + print("i1: " + Integer.toBinaryString(i1)); + int i2 = 0X2F; // Hexadecimal (uppercase) + print("i2: " + Integer.toBinaryString(i2)); + int i3 = 0177; // Octal (leading zero) + print("i3: " + Integer.toBinaryString(i3)); + char c = 0xffff; // max char hex value + print("c: " + Integer.toBinaryString(c)); + byte b = 0x7f; // max byte hex value + print("b: " + Integer.toBinaryString(b)); + short s = 0x7fff; // max short hex value + print("s: " + Integer.toBinaryString(s)); + long n1 = 200L; // long suffix + long n2 = 200L; // long suffix (but can be confusing) + long n3 = 200; + float f1 = 1; + float f2 = 1F; // float suffix + float f3 = 1f; // float suffix + double d1 = 1d; // double suffix + double d2 = 1D; // double suffix + // (Hex and Octal also work with long) + } +} /* Output: +i1: 101111 +i2: 101111 +i3: 1111111 +c: 1111111111111111 +b: 1111111 +s: 111111111111111 +*///:~ diff --git a/src/operators/MathOps.java b/src/operators/MathOps.java new file mode 100644 index 0000000..f8c82d3 --- /dev/null +++ b/src/operators/MathOps.java @@ -0,0 +1,72 @@ +package operators;//: operators/MathOps.java +// Demonstrates the mathematical operators. +import java.util.*; +import static net.mindview.util.Print.*; + +public class MathOps { + public static void main(String[] args) { + // Create a seeded random number generator: + Random rand = new Random(47); + int i, j, k; + // Choose value from 1 to 100: + j = rand.nextInt(100) + 1; + print("j : " + j); + k = rand.nextInt(100) + 1; + print("k : " + k); + i = j + k; + print("j + k : " + i); + i = j - k; + print("j - k : " + i); + i = k / j; + print("k / j : " + i); + i = k * j; + print("k * j : " + i); + i = k % j; + print("k % j : " + i); + j %= k; + print("j %= k : " + j); + // Floating-point number tests: + float u, v, w; // Applies to doubles, too + v = rand.nextFloat(); + print("v : " + v); + w = rand.nextFloat(); + print("w : " + w); + u = v + w; + print("v + w : " + u); + u = v - w; + print("v - w : " + u); + u = v * w; + print("v * w : " + u); + u = v / w; + print("v / w : " + u); + // The following also works for char, + // byte, short, int, long, and double: + u += v; + print("u += v : " + u); + u -= v; + print("u -= v : " + u); + u *= v; + print("u *= v : " + u); + u /= v; + print("u /= v : " + u); + } +} /* Output: +j : 59 +k : 56 +j + k : 115 +j - k : 3 +k / j : 0 +k * j : 3304 +k % j : 56 +j %= k : 3 +v : 0.5309454 +w : 0.0534122 +v + w : 0.5843576 +v - w : 0.47753322 +v * w : 0.028358962 +v / w : 9.940527 +u += v : 10.471473 +u -= v : 9.940527 +u *= v : 5.2778773 +u /= v : 9.940527 +*///:~ diff --git a/src/operators/Overflow.java b/src/operators/Overflow.java new file mode 100644 index 0000000..e3206e2 --- /dev/null +++ b/src/operators/Overflow.java @@ -0,0 +1,14 @@ +package operators;//: operators/Overflow.java +// Surprise! Java lets you overflow. + +public class Overflow { + public static void main(String[] args) { + int big = Integer.MAX_VALUE; + System.out.println("big = " + big); + int bigger = big * 4; + System.out.println("bigger = " + bigger); + } +} /* Output: +big = 2147483647 +bigger = -4 +*///:~ diff --git a/src/operators/PassObject.java b/src/operators/PassObject.java new file mode 100644 index 0000000..730bbeb --- /dev/null +++ b/src/operators/PassObject.java @@ -0,0 +1,24 @@ +package operators;//: operators/PassObject.java +// Passing objects to methods may not be +// what you're used to. +import static net.mindview.util.Print.*; + +class Letter { + char c; +} + +public class PassObject { + static void f(Letter y) { + y.c = 'z'; + } + public static void main(String[] args) { + Letter x = new Letter(); + x.c = 'a'; + print("1: x.c: " + x.c); + f(x); + print("2: x.c: " + x.c); + } +} /* Output: +1: x.c: a +2: x.c: z +*///:~ diff --git a/src/operators/Precedence.java b/src/operators/Precedence.java new file mode 100644 index 0000000..893faad --- /dev/null +++ b/src/operators/Precedence.java @@ -0,0 +1,12 @@ +package operators;//: operators/Precedence.java + +public class Precedence { + public static void main(String[] args) { + int x = 1, y = 2, z = 3; + int a = x + y - 2/2 + z; // (1) + int b = x + (y - 2)/(2 + z); // (2) + System.out.println("a = " + a + " b = " + b); + } +} /* Output: +a = 5 b = 1 +*///:~ diff --git a/src/operators/RoundingNumbers.java b/src/operators/RoundingNumbers.java new file mode 100644 index 0000000..ce151e2 --- /dev/null +++ b/src/operators/RoundingNumbers.java @@ -0,0 +1,19 @@ +package operators;//: operators/RoundingNumbers.java +// Rounding floats and doubles. +import static net.mindview.util.Print.*; + +public class RoundingNumbers { + public static void main(String[] args) { + double above = 0.7, below = 0.4; + float fabove = 0.7f, fbelow = 0.4f; + print("Math.round(above): " + Math.round(above)); + print("Math.round(below): " + Math.round(below)); + print("Math.round(fabove): " + Math.round(fabove)); + print("Math.round(fbelow): " + Math.round(fbelow)); + } +} /* Output: +Math.round(above): 1 +Math.round(below): 0 +Math.round(fabove): 1 +Math.round(fbelow): 0 +*///:~ diff --git a/src/operators/ShortCircuit.java b/src/operators/ShortCircuit.java new file mode 100644 index 0000000..f285f20 --- /dev/null +++ b/src/operators/ShortCircuit.java @@ -0,0 +1,32 @@ +package operators;//: operators/ShortCircuit.java +// Demonstrates short-circuiting behavior +// with logical operators. +import static net.mindview.util.Print.*; + +public class ShortCircuit { + static boolean test1(int val) { + print("test1(" + val + ")"); + print("result: " + (val < 1)); + return val < 1; + } + static boolean test2(int val) { + print("test2(" + val + ")"); + print("result: " + (val < 2)); + return val < 2; + } + static boolean test3(int val) { + print("test3(" + val + ")"); + print("result: " + (val < 3)); + return val < 3; + } + public static void main(String[] args) { + boolean b = test1(0) && test2(2) && test3(2); + print("expression is " + b); + } +} /* Output: +test1(0) +result: true +test2(2) +result: false +expression is false +*///:~ diff --git a/src/operators/StringOperators.java b/src/operators/StringOperators.java new file mode 100644 index 0000000..e059a3a --- /dev/null +++ b/src/operators/StringOperators.java @@ -0,0 +1,19 @@ +package operators;//: operators/StringOperators.java +import static net.mindview.util.Print.*; + +public class StringOperators { + public static void main(String[] args) { + int x = 0, y = 1, z = 2; + String s = "x, y, z "; + print(s + x + y + z); + print(x + " " + s); // Converts x to a String + s += "(summed) = "; // Concatenation operator + print(s + (x + y + z)); + print("" + x); // Shorthand for Integer.toString() + } +} /* Output: +x, y, z 012 +0 x, y, z +x, y, z (summed) = 3 +0 +*///:~ diff --git a/src/operators/TernaryIfElse.java b/src/operators/TernaryIfElse.java new file mode 100644 index 0000000..69ecd1d --- /dev/null +++ b/src/operators/TernaryIfElse.java @@ -0,0 +1,26 @@ +package operators;//: operators/TernaryIfElse.java +import static net.mindview.util.Print.*; + +public class TernaryIfElse { + static int ternary(int i) { + return i < 10 ? i * 100 : i * 10; + } + static int standardIfElse(int i) { + if(i < 10) { + return i * 100; + } else { + return i * 10; + } + } + public static void main(String[] args) { + print(ternary(9)); + print(ternary(10)); + print(standardIfElse(9)); + print(standardIfElse(10)); + } +} /* Output: +900 +100 +900 +100 +*///:~ diff --git a/src/operators/URShift.java b/src/operators/URShift.java new file mode 100644 index 0000000..da55af4 --- /dev/null +++ b/src/operators/URShift.java @@ -0,0 +1,38 @@ +package operators;//: operators/URShift.java +// Test of unsigned right shift. +import static net.mindview.util.Print.*; + +public class URShift { + public static void main(String[] args) { + int i = -1; + print(Integer.toBinaryString(i)); + i >>>= 10; + print(Integer.toBinaryString(i)); + long l = -1; + print(Long.toBinaryString(l)); + l >>>= 10; + print(Long.toBinaryString(l)); + short s = -1; + print(Integer.toBinaryString(s)); + s >>>= 10; + print(Integer.toBinaryString(s)); + byte b = -1; + print(Integer.toBinaryString(b)); + b >>>= 10; + print(Integer.toBinaryString(b)); + b = -1; + print(Integer.toBinaryString(b)); + print(Integer.toBinaryString(b>>>10)); + } +} /* Output: +11111111111111111111111111111111 +1111111111111111111111 +1111111111111111111111111111111111111111111111111111111111111111 +111111111111111111111111111111111111111111111111111111 +11111111111111111111111111111111 +11111111111111111111111111111111 +11111111111111111111111111111111 +11111111111111111111111111111111 +11111111111111111111111111111111 +1111111111111111111111 +*///:~ diff --git a/src/operators/build.xml b/src/operators/build.xml new file mode 100644 index 0000000..edae86f --- /dev/null +++ b/src/operators/build.xml @@ -0,0 +1,280 @@ + + + + + + build.xml for the source code for the operators chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/polymorphism/CovariantReturn.java b/src/polymorphism/CovariantReturn.java new file mode 100644 index 0000000..4c449bf --- /dev/null +++ b/src/polymorphism/CovariantReturn.java @@ -0,0 +1,31 @@ +package polymorphism;//: polymorphism/CovariantReturn.java + +class Grain { + public String toString() { return "Grain"; } +} + +class Wheat extends Grain { + public String toString() { return "Wheat"; } +} + +class Mill { + Grain process() { return new Grain(); } +} + +class WheatMill extends Mill { + Wheat process() { return new Wheat(); } +} + +public class CovariantReturn { + public static void main(String[] args) { + Mill m = new Mill(); + Grain g = m.process(); + System.out.println(g); + m = new WheatMill(); + g = m.process(); + System.out.println(g); + } +} /* Output: +Grain +Wheat +*///:~ diff --git a/src/polymorphism/FieldAccess.java b/src/polymorphism/FieldAccess.java new file mode 100644 index 0000000..b2e8d7f --- /dev/null +++ b/src/polymorphism/FieldAccess.java @@ -0,0 +1,30 @@ +package polymorphism;//: polymorphism/FieldAccess.java +// Direct field access is determined at compile time. + +class Super { + public int field = 0; + public int getField() { return field; } +} + +class Sub extends Super { + public int field = 1; + public int getField() { return field; } + public int getSuperField() { return super.field; } +} + +public class FieldAccess { + public static void main(String[] args) { + Super sup = new Sub(); // Upcast + System.out.println("sup.field = " + sup.field + + ", sup.getField() = " + sup.getField()); + Sub sub = new Sub(); + System.out.println("sub.field = " + + sub.field + ", sub.getField() = " + + sub.getField() + + ", sub.getSuperField() = " + + sub.getSuperField()); + } +} /* Output: +sup.field = 0, sup.getField() = 1 +sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0 +*///:~ diff --git a/src/polymorphism/Frog.java b/src/polymorphism/Frog.java new file mode 100644 index 0000000..da614bc --- /dev/null +++ b/src/polymorphism/Frog.java @@ -0,0 +1,114 @@ +//: polymorphism/Frog.java +// Cleanup and inheritance. +package polymorphism; +import static net.mindview.util.Print.*; + +class Characteristic { + private String s; + Characteristic(String s) { + this.s = s; + print("Creating Characteristic " + s); + } + protected void dispose() { + print("disposing Characteristic " + s); + } +} + +class Description { + private String s; + Description(String s) { + this.s = s; + print("Creating Description " + s); + } + protected void dispose() { + print("disposing Description " + s); + } +} + +class LivingCreature { + private Characteristic p = + new Characteristic("is alive"); + private Description t = + new Description("Basic Living Creature"); + LivingCreature() { + print("LivingCreature()"); + } + protected void dispose() { + print("LivingCreature dispose"); + t.dispose(); + p.dispose(); + } +} + +class Animal extends LivingCreature { + private Characteristic p = + new Characteristic("has heart"); + private Description t = + new Description("Animal not Vegetable"); + Animal() { print("Animal()"); } + protected void dispose() { + print("Animal dispose"); + t.dispose(); + p.dispose(); + super.dispose(); + } +} + +class Amphibian extends Animal { + private Characteristic p = + new Characteristic("can live in water"); + private Description t = + new Description("Both water and land"); + Amphibian() { + print("Amphibian()"); + } + protected void dispose() { + print("Amphibian dispose"); + t.dispose(); + p.dispose(); + super.dispose(); + } +} + +public class Frog extends Amphibian { + private Characteristic p = new Characteristic("Croaks"); + private Description t = new Description("Eats Bugs"); + public Frog() { print("Frog()"); } + protected void dispose() { + print("Frog dispose"); + t.dispose(); + p.dispose(); + super.dispose(); + } + public static void main(String[] args) { + Frog frog = new Frog(); + print("Bye!"); + frog.dispose(); + } +} /* Output: +Creating Characteristic is alive +Creating Description Basic Living Creature +LivingCreature() +Creating Characteristic has heart +Creating Description Animal not Vegetable +Animal() +Creating Characteristic can live in water +Creating Description Both water and land +Amphibian() +Creating Characteristic Croaks +Creating Description Eats Bugs +Frog() +Bye! +Frog dispose +disposing Description Eats Bugs +disposing Characteristic Croaks +Amphibian dispose +disposing Description Both water and land +disposing Characteristic can live in water +Animal dispose +disposing Description Animal not Vegetable +disposing Characteristic has heart +LivingCreature dispose +disposing Description Basic Living Creature +disposing Characteristic is alive +*///:~ diff --git a/src/polymorphism/PolyConstructors.java b/src/polymorphism/PolyConstructors.java new file mode 100644 index 0000000..62cae55 --- /dev/null +++ b/src/polymorphism/PolyConstructors.java @@ -0,0 +1,35 @@ +package polymorphism;//: polymorphism/PolyConstructors.java +// Constructors and polymorphism +// don't produce what you might expect. +import static net.mindview.util.Print.*; + +class Glyph { + void draw() { print("Glyph.draw()"); } + Glyph() { + print("Glyph() before draw()"); + draw(); + print("Glyph() after draw()"); + } +} + +class RoundGlyph extends Glyph { + private int radius = 1; + RoundGlyph(int r) { + radius = r; + print("RoundGlyph.RoundGlyph(), radius = " + radius); + } + void draw() { + print("RoundGlyph.draw(), radius = " + radius); + } +} + +public class PolyConstructors { + public static void main(String[] args) { + new RoundGlyph(5); + } +} /* Output: +Glyph() before draw() +RoundGlyph.draw(), radius = 0 +Glyph() after draw() +RoundGlyph.RoundGlyph(), radius = 5 +*///:~ diff --git a/src/polymorphism/PrivateOverride.java b/src/polymorphism/PrivateOverride.java new file mode 100644 index 0000000..560317e --- /dev/null +++ b/src/polymorphism/PrivateOverride.java @@ -0,0 +1,18 @@ +//: polymorphism/PrivateOverride.java +// Trying to override a private method. +package polymorphism; +import static net.mindview.util.Print.*; + +public class PrivateOverride { + private void f() { print("private f()"); } + public static void main(String[] args) { + PrivateOverride po = new Derived(); + po.f(); + } +} + +class Derived extends PrivateOverride { + public void f() { print("public f()"); } +} /* Output: +private f() +*///:~ diff --git a/src/polymorphism/RTTI.java b/src/polymorphism/RTTI.java new file mode 100644 index 0000000..1c3fa45 --- /dev/null +++ b/src/polymorphism/RTTI.java @@ -0,0 +1,31 @@ +package polymorphism;//: polymorphism/RTTI.java +// Downcasting & Runtime type information (RTTI). +// {ThrowsException} + +class Useful { + public void f() {} + public void g() {} +} + +class MoreUseful extends Useful { + public void f() {} + public void g() {} + public void u() {} + public void v() {} + public void w() {} +} + +public class RTTI { + public static void main(String[] args) { + Useful[] x = { + new Useful(), + new MoreUseful() + }; + x[0].f(); + x[1].g(); + // Compile time: method not found in Useful: + //! x[1].u(); + ((MoreUseful)x[1]).u(); // Downcast/RTTI + ((MoreUseful)x[0]).u(); // Exception thrown + } +} ///:~ diff --git a/src/polymorphism/ReferenceCounting.java b/src/polymorphism/ReferenceCounting.java new file mode 100644 index 0000000..64b77b8 --- /dev/null +++ b/src/polymorphism/ReferenceCounting.java @@ -0,0 +1,60 @@ +package polymorphism;//: polymorphism/ReferenceCounting.java +// Cleaning up shared member objects. +import static net.mindview.util.Print.*; + +class Shared { + private int refcount = 0; + private static long counter = 0; + private final long id = counter++; + public Shared() { + print("Creating " + this); + } + public void addRef() { refcount++; } + protected void dispose() { + if(--refcount == 0) { + print("Disposing " + this); + } + } + public String toString() { return "Shared " + id; } +} + +class Composing { + private Shared shared; + private static long counter = 0; + private final long id = counter++; + public Composing(Shared shared) { + print("Creating " + this); + this.shared = shared; + this.shared.addRef(); + } + protected void dispose() { + print("disposing " + this); + shared.dispose(); + } + public String toString() { return "Composing " + id; } +} + +public class ReferenceCounting { + public static void main(String[] args) { + Shared shared = new Shared(); + Composing[] composing = { new Composing(shared), + new Composing(shared), new Composing(shared), + new Composing(shared), new Composing(shared) }; + for(Composing c : composing) { + c.dispose(); + } + } +} /* Output: +Creating Shared 0 +Creating Composing 0 +Creating Composing 1 +Creating Composing 2 +Creating Composing 3 +Creating Composing 4 +disposing Composing 0 +disposing Composing 1 +disposing Composing 2 +disposing Composing 3 +disposing Composing 4 +Disposing Shared 0 +*///:~ diff --git a/src/polymorphism/Sandwich.java b/src/polymorphism/Sandwich.java new file mode 100644 index 0000000..466241c --- /dev/null +++ b/src/polymorphism/Sandwich.java @@ -0,0 +1,46 @@ +//: polymorphism/Sandwich.java +// Order of constructor calls. +package polymorphism; +import static net.mindview.util.Print.*; + +class Meal { + Meal() { print("Meal()"); } +} + +class Bread { + Bread() { print("Bread()"); } +} + +class Cheese { + Cheese() { print("Cheese()"); } +} + +class Lettuce { + Lettuce() { print("Lettuce()"); } +} + +class Lunch extends Meal { + Lunch() { print("Lunch()"); } +} + +class PortableLunch extends Lunch { + PortableLunch() { print("PortableLunch()");} +} + +public class Sandwich extends PortableLunch { + private Bread b = new Bread(); + private Cheese c = new Cheese(); + private Lettuce l = new Lettuce(); + public Sandwich() { print("Sandwich()"); } + public static void main(String[] args) { + new Sandwich(); + } +} /* Output: +Meal() +Lunch() +PortableLunch() +Bread() +Cheese() +Lettuce() +Sandwich() +*///:~ diff --git a/src/polymorphism/Shapes.java b/src/polymorphism/Shapes.java new file mode 100644 index 0000000..81f7f60 --- /dev/null +++ b/src/polymorphism/Shapes.java @@ -0,0 +1,29 @@ +package polymorphism;//: polymorphism/Shapes.java +// Polymorphism in Java. +import polymorphism.shape.*; + +public class Shapes { + private static RandomShapeGenerator gen = + new RandomShapeGenerator(); + public static void main(String[] args) { + Shape[] s = new Shape[9]; + // Fill up the array with shapes: + for(int i = 0; i < s.length; i++) { + s[i] = gen.next(); + } + // Make polymorphic method calls: + for(Shape shp : s) { + shp.draw(); + } + } +} /* Output: +Triangle.draw() +Triangle.draw() +Square.draw() +Triangle.draw() +Square.draw() +Triangle.draw() +Square.draw() +Triangle.draw() +Circle.draw() +*///:~ diff --git a/src/polymorphism/StaticPolymorphism.java b/src/polymorphism/StaticPolymorphism.java new file mode 100644 index 0000000..284553e --- /dev/null +++ b/src/polymorphism/StaticPolymorphism.java @@ -0,0 +1,31 @@ +package polymorphism;//: polymorphism/StaticPolymorphism.java +// Static methods are not polymorphic. + +class StaticSuper { + public static String staticGet() { + return "Base staticGet()"; + } + public String dynamicGet() { + return "Base dynamicGet()"; + } +} + +class StaticSub extends StaticSuper { + public static String staticGet() { + return "Derived staticGet()"; + } + public String dynamicGet() { + return "Derived dynamicGet()"; + } +} + +public class StaticPolymorphism { + public static void main(String[] args) { + StaticSuper sup = new StaticSub(); // Upcast + System.out.println(sup.staticGet()); + System.out.println(sup.dynamicGet()); + } +} /* Output: +Base staticGet() +Derived dynamicGet() +*///:~ diff --git a/src/polymorphism/Transmogrify.java b/src/polymorphism/Transmogrify.java new file mode 100644 index 0000000..1e9311d --- /dev/null +++ b/src/polymorphism/Transmogrify.java @@ -0,0 +1,34 @@ +package polymorphism;//: polymorphism/Transmogrify.java +// Dynamically changing the behavior of an object +// via composition (the "State" design pattern). +import static net.mindview.util.Print.*; + +class Actor { + public void act() {} +} + +class HappyActor extends Actor { + public void act() { print("HappyActor"); } +} + +class SadActor extends Actor { + public void act() { print("SadActor"); } +} + +class Stage { + private Actor actor = new HappyActor(); + public void change() { actor = new SadActor(); } + public void performPlay() { actor.act(); } +} + +public class Transmogrify { + public static void main(String[] args) { + Stage stage = new Stage(); + stage.performPlay(); + stage.change(); + stage.performPlay(); + } +} /* Output: +HappyActor +SadActor +*///:~ diff --git a/src/polymorphism/build.xml b/src/polymorphism/build.xml new file mode 100644 index 0000000..699fe72 --- /dev/null +++ b/src/polymorphism/build.xml @@ -0,0 +1,220 @@ + + + + + + build.xml for the source code for the polymorphism chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/polymorphism/music/Instrument.java b/src/polymorphism/music/Instrument.java new file mode 100644 index 0000000..96181e6 --- /dev/null +++ b/src/polymorphism/music/Instrument.java @@ -0,0 +1,10 @@ +//: polymorphism/music/Instrument.java +package polymorphism.music; +import static net.mindview.util.Print.*; + +class Instrument { + public void play(Note n) { + print("Instrument.play()"); + } +} + ///:~ diff --git a/src/polymorphism/music/Music.java b/src/polymorphism/music/Music.java new file mode 100644 index 0000000..c5344b9 --- /dev/null +++ b/src/polymorphism/music/Music.java @@ -0,0 +1,16 @@ +//: polymorphism/music/Music.java +// Inheritance & upcasting. +package polymorphism.music; + +public class Music { + public static void tune(Instrument i) { + // ... + i.play(Note.MIDDLE_C); + } + public static void main(String[] args) { + Wind flute = new Wind(); + tune(flute); // Upcasting + } +} /* Output: +Wind.play() MIDDLE_C +*///:~ diff --git a/src/polymorphism/music/Music2.java b/src/polymorphism/music/Music2.java new file mode 100644 index 0000000..955c203 --- /dev/null +++ b/src/polymorphism/music/Music2.java @@ -0,0 +1,40 @@ +//: polymorphism/music/Music2.java +// Overloading instead of upcasting. +package polymorphism.music; +import static net.mindview.util.Print.*; + +class Stringed extends Instrument { + public void play(Note n) { + print("Stringed.play() " + n); + } +} + +class Brass extends Instrument { + public void play(Note n) { + print("Brass.play() " + n); + } +} + +public class Music2 { + public static void tune(Wind i) { + i.play(Note.MIDDLE_C); + } + public static void tune(Stringed i) { + i.play(Note.MIDDLE_C); + } + public static void tune(Brass i) { + i.play(Note.MIDDLE_C); + } + public static void main(String[] args) { + Wind flute = new Wind(); + Stringed violin = new Stringed(); + Brass frenchHorn = new Brass(); + tune(flute); // No upcasting + tune(violin); + tune(frenchHorn); + } +} /* Output: +Wind.play() MIDDLE_C +Stringed.play() MIDDLE_C +Brass.play() MIDDLE_C +*///:~ diff --git a/src/polymorphism/music/Note.java b/src/polymorphism/music/Note.java new file mode 100644 index 0000000..0595dbd --- /dev/null +++ b/src/polymorphism/music/Note.java @@ -0,0 +1,7 @@ +//: polymorphism/music/Note.java +// Notes to play on musical instruments. +package polymorphism.music; + +public enum Note { + MIDDLE_C, C_SHARP, B_FLAT; // Etc. +} ///:~ diff --git a/src/polymorphism/music/Wind.java b/src/polymorphism/music/Wind.java new file mode 100644 index 0000000..77847d4 --- /dev/null +++ b/src/polymorphism/music/Wind.java @@ -0,0 +1,11 @@ +//: polymorphism/music/Wind.java +package polymorphism.music; + +// Wind objects are instruments +// because they have the same interface: +public class Wind extends Instrument { + // Redefine interface method: + public void play(Note n) { + System.out.println("Wind.play() " + n); + } +} ///:~ diff --git a/src/polymorphism/music3/Music3.java b/src/polymorphism/music3/Music3.java new file mode 100644 index 0000000..5043475 --- /dev/null +++ b/src/polymorphism/music3/Music3.java @@ -0,0 +1,70 @@ +//: polymorphism/music3/Music3.java +// An extensible program. +package polymorphism.music3; +import polymorphism.music.Note; +import static net.mindview.util.Print.*; + +class Instrument { + void play(Note n) { print("Instrument.play() " + n); } + String what() { return "Instrument"; } + void adjust() { print("Adjusting Instrument"); } +} + +class Wind extends Instrument { + void play(Note n) { print("Wind.play() " + n); } + String what() { return "Wind"; } + void adjust() { print("Adjusting Wind"); } +} + +class Percussion extends Instrument { + void play(Note n) { print("Percussion.play() " + n); } + String what() { return "Percussion"; } + void adjust() { print("Adjusting Percussion"); } +} + +class Stringed extends Instrument { + void play(Note n) { print("Stringed.play() " + n); } + String what() { return "Stringed"; } + void adjust() { print("Adjusting Stringed"); } +} + +class Brass extends Wind { + void play(Note n) { print("Brass.play() " + n); } + void adjust() { print("Adjusting Brass"); } +} + +class Woodwind extends Wind { + void play(Note n) { print("Woodwind.play() " + n); } + String what() { return "Woodwind"; } +} + +public class Music3 { + // Doesn't care about type, so new types + // added to the system still work right: + public static void tune(Instrument i) { + // ... + i.play(Note.MIDDLE_C); + } + public static void tuneAll(Instrument[] e) { + for(Instrument i : e) { + tune(i); + } + } + public static void main(String[] args) { + // Upcasting during addition to the array: + Instrument[] orchestra = { + new Wind(), + new Percussion(), + new Stringed(), + new Brass(), + new Woodwind() + }; + tuneAll(orchestra); + } +} /* Output: +Wind.play() MIDDLE_C +Percussion.play() MIDDLE_C +Stringed.play() MIDDLE_C +Brass.play() MIDDLE_C +Woodwind.play() MIDDLE_C +*///:~ diff --git a/src/polymorphism/shape/Circle.java b/src/polymorphism/shape/Circle.java new file mode 100644 index 0000000..ff54632 --- /dev/null +++ b/src/polymorphism/shape/Circle.java @@ -0,0 +1,8 @@ +//: polymorphism/shape/Circle.java +package polymorphism.shape; +import static net.mindview.util.Print.*; + +public class Circle extends Shape { + public void draw() { print("Circle.draw()"); } + public void erase() { print("Circle.erase()"); } +} ///:~ diff --git a/src/polymorphism/shape/RandomShapeGenerator.java b/src/polymorphism/shape/RandomShapeGenerator.java new file mode 100644 index 0000000..e474997 --- /dev/null +++ b/src/polymorphism/shape/RandomShapeGenerator.java @@ -0,0 +1,16 @@ +//: polymorphism/shape/RandomShapeGenerator.java +// A "factory" that randomly creates shapes. +package polymorphism.shape; +import java.util.*; + +public class RandomShapeGenerator { + private Random rand = new Random(47); + public Shape next() { + switch(rand.nextInt(3)) { + default: + case 0: return new Circle(); + case 1: return new Square(); + case 2: return new Triangle(); + } + } +} ///:~ diff --git a/src/polymorphism/shape/Shape.java b/src/polymorphism/shape/Shape.java new file mode 100644 index 0000000..13188a0 --- /dev/null +++ b/src/polymorphism/shape/Shape.java @@ -0,0 +1,7 @@ +//: polymorphism/shape/Shape.java +package polymorphism.shape; + +public class Shape { + public void draw() {} + public void erase() {} +} ///:~ diff --git a/src/polymorphism/shape/Square.java b/src/polymorphism/shape/Square.java new file mode 100644 index 0000000..1b5c50b --- /dev/null +++ b/src/polymorphism/shape/Square.java @@ -0,0 +1,8 @@ +//: polymorphism/shape/Square.java +package polymorphism.shape; +import static net.mindview.util.Print.*; + +public class Square extends Shape { + public void draw() { print("Square.draw()"); } + public void erase() { print("Square.erase()"); } +} ///:~ diff --git a/src/polymorphism/shape/Triangle.java b/src/polymorphism/shape/Triangle.java new file mode 100644 index 0000000..58ec072 --- /dev/null +++ b/src/polymorphism/shape/Triangle.java @@ -0,0 +1,8 @@ +//: polymorphism/shape/Triangle.java +package polymorphism.shape; +import static net.mindview.util.Print.*; + +public class Triangle extends Shape { + public void draw() { print("Triangle.draw()"); } + public void erase() { print("Triangle.erase()"); } +} ///:~ diff --git a/src/reusing/Bath.java b/src/reusing/Bath.java new file mode 100644 index 0000000..fccabcd --- /dev/null +++ b/src/reusing/Bath.java @@ -0,0 +1,58 @@ +package reusing;//: reusing/Bath.java +// Constructor initialization with composition. +import static net.mindview.util.Print.*; + +class Soap { + private String s; + Soap() { + print("Soap()"); + s = "Constructed"; + } + public String toString() { return s; } +} + +public class Bath { + private String // Initializing at point of definition: + s1 = "Happy", + s2 = "Happy", + s3, s4; + private Soap castille; + private int i; + private float toy; + public Bath() { + print("Inside Bath()"); + s3 = "Joy"; + toy = 3.14f; + castille = new Soap(); + } + // Instance initialization: + { i = 47; } + public String toString() { + if(s4 == null) // Delayed initialization: + { + s4 = "Joy"; + } + return + "s1 = " + s1 + "\n" + + "s2 = " + s2 + "\n" + + "s3 = " + s3 + "\n" + + "s4 = " + s4 + "\n" + + "i = " + i + "\n" + + "toy = " + toy + "\n" + + "castille = " + castille; + } + public static void main(String[] args) { + Bath b = new Bath(); + print(b); + } +} /* Output: +Inside Bath() +Soap() +s1 = Happy +s2 = Happy +s3 = Joy +s4 = Joy +i = 47 +toy = 3.14 +castille = Constructed +*///:~ diff --git a/src/reusing/Beetle.java b/src/reusing/Beetle.java new file mode 100644 index 0000000..023fe15 --- /dev/null +++ b/src/reusing/Beetle.java @@ -0,0 +1,40 @@ +package reusing;//: reusing/Beetle.java +// The full process of initialization. +import static net.mindview.util.Print.*; + +class Insect { + private int i = 9; + protected int j; + Insect() { + print("i = " + i + ", j = " + j); + j = 39; + } + private static int x1 = + printInit("static Insect.x1 initialized"); + static int printInit(String s) { + print(s); + return 47; + } +} + +public class Beetle extends Insect { + private int k = printInit("Beetle.k initialized"); + public Beetle() { + print("k = " + k); + print("j = " + j); + } + private static int x2 = + printInit("static Beetle.x2 initialized"); + public static void main(String[] args) { + print("Beetle constructor"); + Beetle b = new Beetle(); + } +} /* Output: +static Insect.x1 initialized +static Beetle.x2 initialized +Beetle constructor +i = 9, j = 0 +Beetle.k initialized +k = 47 +j = 39 +*///:~ diff --git a/src/reusing/BlankFinal.java b/src/reusing/BlankFinal.java new file mode 100644 index 0000000..7c50582 --- /dev/null +++ b/src/reusing/BlankFinal.java @@ -0,0 +1,26 @@ +package reusing;//: reusing/BlankFinal.java +// "Blank" final fields. + +class Poppet { + private int i; + Poppet(int ii) { i = ii; } +} + +public class BlankFinal { + private final int i = 0; // Initialized final + private final int j; // Blank final + private final Poppet p; // Blank final reference + // Blank finals MUST be initialized in the constructor: + public BlankFinal() { + j = 1; // Initialize blank final + p = new Poppet(1); // Initialize blank final reference + } + public BlankFinal(int x) { + j = x; // Initialize blank final + p = new Poppet(x); // Initialize blank final reference + } + public static void main(String[] args) { + new BlankFinal(); + new BlankFinal(47); + } +} ///:~ diff --git a/src/reusing/CADSystem.java b/src/reusing/CADSystem.java new file mode 100644 index 0000000..ea7d222 --- /dev/null +++ b/src/reusing/CADSystem.java @@ -0,0 +1,104 @@ +//: reusing/CADSystem.java +// Ensuring proper cleanup. +package reusing; +import static net.mindview.util.Print.*; + +class Shape { + Shape(int i) { print("Shape constructor"); } + void dispose() { print("Shape dispose"); } +} + +class Circle extends Shape { + Circle(int i) { + super(i); + print("Drawing Circle"); + } + void dispose() { + print("Erasing Circle"); + super.dispose(); + } +} + +class Triangle extends Shape { + Triangle(int i) { + super(i); + print("Drawing Triangle"); + } + void dispose() { + print("Erasing Triangle"); + super.dispose(); + } +} + +class Line extends Shape { + private int start, end; + Line(int start, int end) { + super(start); + this.start = start; + this.end = end; + print("Drawing Line: " + start + ", " + end); + } + void dispose() { + print("Erasing Line: " + start + ", " + end); + super.dispose(); + } +} + +public class CADSystem extends Shape { + private Circle c; + private Triangle t; + private Line[] lines = new Line[3]; + public CADSystem(int i) { + super(i + 1); + for(int j = 0; j < lines.length; j++) { + lines[j] = new Line(j, j*j); + } + c = new Circle(1); + t = new Triangle(1); + print("Combined constructor"); + } + public void dispose() { + print("CADSystem.dispose()"); + // The order of cleanup is the reverse + // of the order of initialization: + t.dispose(); + c.dispose(); + for(int i = lines.length - 1; i >= 0; i--) { + lines[i].dispose(); + } + super.dispose(); + } + public static void main(String[] args) { + CADSystem x = new CADSystem(47); + try { + // Code and exception handling... + } finally { + x.dispose(); + } + } +} /* Output: +Shape constructor +Shape constructor +Drawing Line: 0, 0 +Shape constructor +Drawing Line: 1, 1 +Shape constructor +Drawing Line: 2, 4 +Shape constructor +Drawing Circle +Shape constructor +Drawing Triangle +Combined constructor +CADSystem.dispose() +Erasing Triangle +Shape dispose +Erasing Circle +Shape dispose +Erasing Line: 2, 4 +Shape dispose +Erasing Line: 1, 1 +Shape dispose +Erasing Line: 0, 0 +Shape dispose +Shape dispose +*///:~ diff --git a/src/reusing/Car.java b/src/reusing/Car.java new file mode 100644 index 0000000..3d310f1 --- /dev/null +++ b/src/reusing/Car.java @@ -0,0 +1,41 @@ +package reusing;//: reusing/Car.java +// Composition with public objects. + +class Engine { + public void start() {} + public void rev() {} + public void stop() {} +} + +class Wheel { + public void inflate(int psi) {} +} + +class Window { + public void rollup() {} + public void rolldown() {} +} + +class Door { + public Window window = new Window(); + public void open() {} + public void close() {} +} + +public class Car { + public Engine engine = new Engine(); + public Wheel[] wheel = new Wheel[4]; + public Door + left = new Door(), + right = new Door(); // 2-door + public Car() { + for(int i = 0; i < 4; i++) { + wheel[i] = new Wheel(); + } + } + public static void main(String[] args) { + Car car = new Car(); + car.left.window.rollup(); + car.wheel[0].inflate(72); + } +} ///:~ diff --git a/src/reusing/Cartoon.java b/src/reusing/Cartoon.java new file mode 100644 index 0000000..f009030 --- /dev/null +++ b/src/reusing/Cartoon.java @@ -0,0 +1,22 @@ +package reusing;//: reusing/Cartoon.java +// Constructor calls during inheritance. +import static net.mindview.util.Print.*; + +class Art { + Art() { print("Art constructor"); } +} + +class Drawing extends Art { + Drawing() { print("Drawing constructor"); } +} + +public class Cartoon extends Drawing { + public Cartoon() { print("Cartoon constructor"); } + public static void main(String[] args) { + Cartoon x = new Cartoon(); + } +} /* Output: +Art constructor +Drawing constructor +Cartoon constructor +*///:~ diff --git a/src/reusing/Chess.java b/src/reusing/Chess.java new file mode 100644 index 0000000..028115e --- /dev/null +++ b/src/reusing/Chess.java @@ -0,0 +1,30 @@ +package reusing;//: reusing/Chess.java +// Inheritance, constructors and arguments. +import static net.mindview.util.Print.*; + +class Game { + Game(int i) { + print("Game constructor"); + } +} + +class BoardGame extends Game { + BoardGame(int i) { + super(i); + print("BoardGame constructor"); + } +} + +public class Chess extends BoardGame { + Chess() { + super(11); + print("Chess constructor"); + } + public static void main(String[] args) { + Chess x = new Chess(); + } +} /* Output: +Game constructor +BoardGame constructor +Chess constructor +*///:~ diff --git a/src/reusing/Detergent.java b/src/reusing/Detergent.java new file mode 100644 index 0000000..5462eac --- /dev/null +++ b/src/reusing/Detergent.java @@ -0,0 +1,42 @@ +package reusing;//: reusing/Detergent.java +// Inheritance syntax & properties. +import static net.mindview.util.Print.*; + +class Cleanser { + private String s = "Cleanser"; + public void append(String a) { s += a; } + public void dilute() { append(" dilute()"); } + public void apply() { append(" apply()"); } + public void scrub() { append(" scrub()"); } + public String toString() { return s; } + public static void main(String[] args) { + Cleanser x = new Cleanser(); + x.dilute(); x.apply(); x.scrub(); + print(x); + } +} + +public class Detergent extends Cleanser { + // Change a method: + public void scrub() { + append(" Detergent.scrub()"); + super.scrub(); // Call base-class version + } + // Add methods to the interface: + public void foam() { append(" foam()"); } + // Test the new class: + public static void main(String[] args) { + Detergent x = new Detergent(); + x.dilute(); + x.apply(); + x.scrub(); + x.foam(); + print(x); + print("Testing base class:"); + Cleanser.main(args); + } +} /* Output: +Cleanser dilute() apply() Detergent.scrub() scrub() foam() +Testing base class: +Cleanser dilute() apply() scrub() +*///:~ diff --git a/src/reusing/FinalArguments.java b/src/reusing/FinalArguments.java new file mode 100644 index 0000000..722c7d0 --- /dev/null +++ b/src/reusing/FinalArguments.java @@ -0,0 +1,24 @@ +package reusing;//: reusing/FinalArguments.java +// Using "final" with method arguments. + +class Gizmo { + public void spin() {} +} + +public class FinalArguments { + void with(final Gizmo g) { + //! g = new Gizmo(); // Illegal -- g is final + } + void without(Gizmo g) { + g = new Gizmo(); // OK -- g not final + g.spin(); + } + // void f(final int i) { i++; } // Can't change + // You can only read from a final primitive: + int g(final int i) { return i + 1; } + public static void main(String[] args) { + FinalArguments bf = new FinalArguments(); + bf.without(null); + bf.with(null); + } +} ///:~ diff --git a/src/reusing/FinalData.java b/src/reusing/FinalData.java new file mode 100644 index 0000000..9e65390 --- /dev/null +++ b/src/reusing/FinalData.java @@ -0,0 +1,53 @@ +package reusing;//: reusing/FinalData.java +// The effect of final on fields. +import java.util.*; +import static net.mindview.util.Print.*; + +class Value { + int i; // Package access + public Value(int i) { this.i = i; } +} + +public class FinalData { + private static Random rand = new Random(47); + private String id; + public FinalData(String id) { this.id = id; } + // Can be compile-time constants: + private final int valueOne = 9; + private static final int VALUE_TWO = 99; + // Typical public constant: + public static final int VALUE_THREE = 39; + // Cannot be compile-time constants: + private final int i4 = rand.nextInt(20); + static final int INT_5 = rand.nextInt(20); + private Value v1 = new Value(11); + private final Value v2 = new Value(22); + private static final Value VAL_3 = new Value(33); + // Arrays: + private final int[] a = { 1, 2, 3, 4, 5, 6 }; + public String toString() { + return id + ": " + "i4 = " + i4 + ", INT_5 = " + INT_5; + } + public static void main(String[] args) { + FinalData fd1 = new FinalData("fd1"); + //! fd1.valueOne++; // Error: can't change value + fd1.v2.i++; // Object isn't constant! + fd1.v1 = new Value(9); // OK -- not final + for(int i = 0; i < fd1.a.length; i++) { + fd1.a[i]++; // Object isn't constant! + } + //! fd1.v2 = new Value(0); // Error: Can't + //! fd1.VAL_3 = new Value(1); // change reference + //! fd1.a = new int[3]; + print(fd1); + print("Creating new FinalData"); + FinalData fd2 = new FinalData("fd2"); + print(fd1); + print(fd2); + } +} /* Output: +fd1: i4 = 15, INT_5 = 18 +Creating new FinalData +fd1: i4 = 15, INT_5 = 18 +fd2: i4 = 13, INT_5 = 18 +*///:~ diff --git a/src/reusing/FinalOverridingIllusion.java b/src/reusing/FinalOverridingIllusion.java new file mode 100644 index 0000000..95b1adc --- /dev/null +++ b/src/reusing/FinalOverridingIllusion.java @@ -0,0 +1,49 @@ +package reusing;//: reusing/FinalOverridingIllusion.java +// It only looks like you can override +// a private or private final method. +import static net.mindview.util.Print.*; + +class WithFinals { + // Identical to "private" alone: + private final void f() { print("WithFinals.f()"); } + // Also automatically "final": + private void g() { print("WithFinals.g()"); } +} + +class OverridingPrivate extends WithFinals { + private final void f() { + print("OverridingPrivate.f()"); + } + private void g() { + print("OverridingPrivate.g()"); + } +} + +class OverridingPrivate2 extends OverridingPrivate { + public final void f() { + print("OverridingPrivate2.f()"); + } + public void g() { + print("OverridingPrivate2.g()"); + } +} + +public class FinalOverridingIllusion { + public static void main(String[] args) { + OverridingPrivate2 op2 = new OverridingPrivate2(); + op2.f(); + op2.g(); + // You can upcast: + OverridingPrivate op = op2; + // But you can't call the methods: + //! op.f(); + //! op.g(); + // Same here: + WithFinals wf = op2; + //! wf.f(); + //! wf.g(); + } +} /* Output: +OverridingPrivate2.f() +OverridingPrivate2.g() +*///:~ diff --git a/src/reusing/Hide.java b/src/reusing/Hide.java new file mode 100644 index 0000000..709a08b --- /dev/null +++ b/src/reusing/Hide.java @@ -0,0 +1,38 @@ +package reusing;//: reusing/Hide.java +// Overloading a base-class method name in a derived +// class does not hide the base-class versions. +import static net.mindview.util.Print.*; + +class Homer { + char doh(char c) { + print("doh(char)"); + return 'd'; + } + float doh(float f) { + print("doh(float)"); + return 1.0f; + } +} + +class Milhouse {} + +class Bart extends Homer { + void doh(Milhouse m) { + print("doh(Milhouse)"); + } +} + +public class Hide { + public static void main(String[] args) { + Bart b = new Bart(); + b.doh(1); + b.doh('x'); + b.doh(1.0f); + b.doh(new Milhouse()); + } +} /* Output: +doh(float) +doh(char) +doh(float) +doh(Milhouse) +*///:~ diff --git a/src/reusing/Jurassic.java b/src/reusing/Jurassic.java new file mode 100644 index 0000000..baccd2a --- /dev/null +++ b/src/reusing/Jurassic.java @@ -0,0 +1,23 @@ +package reusing;//: reusing/Jurassic.java +// Making an entire class final. + +class SmallBrain {} + +final class Dinosaur { + int i = 7; + int j = 1; + SmallBrain x = new SmallBrain(); + void f() {} +} + +//! class Further extends Dinosaur {} +// error: Cannot extend final class 'Dinosaur' + +public class Jurassic { + public static void main(String[] args) { + Dinosaur n = new Dinosaur(); + n.f(); + n.i = 40; + n.j++; + } +} ///:~ diff --git a/src/reusing/Lisa.java b/src/reusing/Lisa.java new file mode 100644 index 0000000..625a170 --- /dev/null +++ b/src/reusing/Lisa.java @@ -0,0 +1,8 @@ +package reusing;//: reusing/Lisa.java +// {CompileTimeError} (Won't compile) + +class Lisa extends Homer { + @Override void doh(Milhouse m) { + System.out.println("doh(Milhouse)"); + } +} ///:~ diff --git a/src/reusing/Orc.java b/src/reusing/Orc.java new file mode 100644 index 0000000..c5ab10f --- /dev/null +++ b/src/reusing/Orc.java @@ -0,0 +1,36 @@ +package reusing;//: reusing/Orc.java +// The protected keyword. +import static net.mindview.util.Print.*; + +class Villain { + private String name; + protected void set(String nm) { name = nm; } + public Villain(String name) { this.name = name; } + public String toString() { + return "I'm a Villain and my name is " + name; + } +} + +public class Orc extends Villain { + private int orcNumber; + public Orc(String name, int orcNumber) { + super(name); + this.orcNumber = orcNumber; + } + public void change(String name, int orcNumber) { + set(name); // Available because it's protected + this.orcNumber = orcNumber; + } + public String toString() { + return "Orc " + orcNumber + ": " + super.toString(); + } + public static void main(String[] args) { + Orc orc = new Orc("Limburger", 12); + print(orc); + orc.change("Bob", 19); + print(orc); + } +} /* Output: +Orc 12: I'm a Villain and my name is Limburger +Orc 19: I'm a Villain and my name is Bob +*///:~ diff --git a/src/reusing/PlaceSetting.java b/src/reusing/PlaceSetting.java new file mode 100644 index 0000000..188a608 --- /dev/null +++ b/src/reusing/PlaceSetting.java @@ -0,0 +1,79 @@ +package reusing;//: reusing/PlaceSetting.java +// Combining composition & inheritance. +import static net.mindview.util.Print.*; + +class Plate { + Plate(int i) { + print("Plate constructor"); + } +} + +class DinnerPlate extends Plate { + DinnerPlate(int i) { + super(i); + print("DinnerPlate constructor"); + } +} + +class Utensil { + Utensil(int i) { + print("Utensil constructor"); + } +} + +class Spoon extends Utensil { + Spoon(int i) { + super(i); + print("Spoon constructor"); + } +} + +class Fork extends Utensil { + Fork(int i) { + super(i); + print("Fork constructor"); + } +} + +class Knife extends Utensil { + Knife(int i) { + super(i); + print("Knife constructor"); + } +} + +// A cultural way of doing something: +class Custom { + Custom(int i) { + print("Custom constructor"); + } +} + +public class PlaceSetting extends Custom { + private Spoon sp; + private Fork frk; + private Knife kn; + private DinnerPlate pl; + public PlaceSetting(int i) { + super(i + 1); + sp = new Spoon(i + 2); + frk = new Fork(i + 3); + kn = new Knife(i + 4); + pl = new DinnerPlate(i + 5); + print("PlaceSetting constructor"); + } + public static void main(String[] args) { + PlaceSetting x = new PlaceSetting(9); + } +} /* Output: +Custom constructor +Utensil constructor +Spoon constructor +Utensil constructor +Fork constructor +Utensil constructor +Knife constructor +Plate constructor +DinnerPlate constructor +PlaceSetting constructor +*///:~ diff --git a/src/reusing/SpaceShip.java b/src/reusing/SpaceShip.java new file mode 100644 index 0000000..d77344e --- /dev/null +++ b/src/reusing/SpaceShip.java @@ -0,0 +1,11 @@ +package reusing;//: reusing/SpaceShip.java + +public class SpaceShip extends SpaceShipControls { + private String name; + public SpaceShip(String name) { this.name = name; } + public String toString() { return name; } + public static void main(String[] args) { + SpaceShip protector = new SpaceShip("NSEA Protector"); + protector.forward(100); + } +} ///:~ diff --git a/src/reusing/SpaceShipControls.java b/src/reusing/SpaceShipControls.java new file mode 100644 index 0000000..8077eac --- /dev/null +++ b/src/reusing/SpaceShipControls.java @@ -0,0 +1,11 @@ +package reusing;//: reusing/SpaceShipControls.java + +public class SpaceShipControls { + void up(int velocity) {} + void down(int velocity) {} + void left(int velocity) {} + void right(int velocity) {} + void forward(int velocity) {} + void back(int velocity) {} + void turboBoost() {} +} ///:~ diff --git a/src/reusing/SpaceShipDelegation.java b/src/reusing/SpaceShipDelegation.java new file mode 100644 index 0000000..fa30691 --- /dev/null +++ b/src/reusing/SpaceShipDelegation.java @@ -0,0 +1,37 @@ +package reusing;//: reusing/SpaceShipDelegation.java + +public class SpaceShipDelegation { + private String name; + private SpaceShipControls controls = + new SpaceShipControls(); + public SpaceShipDelegation(String name) { + this.name = name; + } + // Delegated methods: + public void back(int velocity) { + controls.back(velocity); + } + public void down(int velocity) { + controls.down(velocity); + } + public void forward(int velocity) { + controls.forward(velocity); + } + public void left(int velocity) { + controls.left(velocity); + } + public void right(int velocity) { + controls.right(velocity); + } + public void turboBoost() { + controls.turboBoost(); + } + public void up(int velocity) { + controls.up(velocity); + } + public static void main(String[] args) { + SpaceShipDelegation protector = + new SpaceShipDelegation("NSEA Protector"); + protector.forward(100); + } +} ///:~ diff --git a/src/reusing/SprinklerSystem.java b/src/reusing/SprinklerSystem.java new file mode 100644 index 0000000..ef0f7a0 --- /dev/null +++ b/src/reusing/SprinklerSystem.java @@ -0,0 +1,35 @@ +package reusing;//: reusing/SprinklerSystem.java +// Composition for code reuse. + +class WaterSource { + private String s; + WaterSource() { + System.out.println("WaterSource()"); + s = "Constructed"; + } + public String toString() { return s; } +} + +public class SprinklerSystem { + private String valve1, valve2, valve3, valve4; + private WaterSource source = new WaterSource(); + private int i; + private float f; + public String toString() { + return + "valve1 = " + valve1 + " " + + "valve2 = " + valve2 + " " + + "valve3 = " + valve3 + " " + + "valve4 = " + valve4 + "\n" + + "i = " + i + " " + "f = " + f + " " + + "source = " + source; + } + public static void main(String[] args) { + SprinklerSystem sprinklers = new SprinklerSystem(); + System.out.println(sprinklers); + } +} /* Output: +WaterSource() +valve1 = null valve2 = null valve3 = null valve4 = null +i = 0 f = 0.0 source = Constructed +*///:~ diff --git a/src/reusing/Wind.java b/src/reusing/Wind.java new file mode 100644 index 0000000..eac9394 --- /dev/null +++ b/src/reusing/Wind.java @@ -0,0 +1,19 @@ +package reusing;//: reusing/Wind.java +// Inheritance & upcasting. + +class Instrument { + public void play() {} + static void tune(Instrument i) { + // ... + i.play(); + } +} + +// Wind objects are instruments +// because they have the same interface: +public class Wind extends Instrument { + public static void main(String[] args) { + Wind flute = new Wind(); + Instrument.tune(flute); // Upcasting + } +} ///:~ diff --git a/src/reusing/build.xml b/src/reusing/build.xml new file mode 100644 index 0000000..d77af80 --- /dev/null +++ b/src/reusing/build.xml @@ -0,0 +1,261 @@ + + + + + + build.xml for the source code for the reusing chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/strings/ArrayListDisplay.java b/src/strings/ArrayListDisplay.java new file mode 100644 index 0000000..428bfb5 --- /dev/null +++ b/src/strings/ArrayListDisplay.java @@ -0,0 +1,15 @@ +package strings;//: strings/ArrayListDisplay.java +import generics.coffee.*; +import java.util.*; + +public class ArrayListDisplay { + public static void main(String[] args) { + ArrayList coffees = new ArrayList(); + for(Coffee c : new CoffeeGenerator(10)) { + coffees.add(c); + } + System.out.println(coffees); + } +} /* Output: +[Americano 0, Latte 1, Americano 2, Mocha 3, Mocha 4, Breve 5, Americano 6, Latte 7, Cappuccino 8, Cappuccino 9] +*///:~ diff --git a/src/strings/BetterRead.java b/src/strings/BetterRead.java new file mode 100644 index 0000000..d96f470 --- /dev/null +++ b/src/strings/BetterRead.java @@ -0,0 +1,33 @@ +package strings;//: strings/BetterRead.java +import java.util.*; + +public class BetterRead { + public static void main(String[] args) { + Scanner stdin = new Scanner(SimpleRead.input); + System.out.println("What is your name?"); + String name = stdin.nextLine(); + System.out.println(name); + System.out.println( + "How old are you? What is your favorite double?"); + System.out.println("(input: )"); + int age = stdin.nextInt(); + double favorite = stdin.nextDouble(); + System.out.println(age); + System.out.println(favorite); + System.out.format("Hi %s.\n", name); + System.out.format("In 5 years you will be %d.\n", + age + 5); + System.out.format("My favorite double is %f.", + favorite / 2); + } +} /* Output: +What is your name? +Sir Robin of Camelot +How old are you? What is your favorite double? +(input: ) +22 +1.61803 +Hi Sir Robin of Camelot. +In 5 years you will be 27. +My favorite double is 0.809015. +*///:~ diff --git a/src/strings/Concatenation.java b/src/strings/Concatenation.java new file mode 100644 index 0000000..c1bb9a4 --- /dev/null +++ b/src/strings/Concatenation.java @@ -0,0 +1,11 @@ +package strings;//: strings/Concatenation.java + +public class Concatenation { + public static void main(String[] args) { + String mango = "mango"; + String s = "abc" + mango + "def" + 47; + System.out.println(s); + } +} /* Output: +abcmangodef47 +*///:~ diff --git a/src/strings/Conversion.java b/src/strings/Conversion.java new file mode 100644 index 0000000..5a61662 --- /dev/null +++ b/src/strings/Conversion.java @@ -0,0 +1,109 @@ +package strings;//: strings/Conversion.java +import java.math.*; +import java.util.*; + +public class Conversion { + public static void main(String[] args) { + Formatter f = new Formatter(System.out); + + char u = 'a'; + System.out.println("u = 'a'"); + f.format("s: %s\n", u); + // f.format("d: %d\n", u); + f.format("c: %c\n", u); + f.format("b: %b\n", u); + // f.format("f: %f\n", u); + // f.format("e: %e\n", u); + // f.format("x: %x\n", u); + f.format("h: %h\n", u); + + int v = 121; + System.out.println("v = 121"); + f.format("d: %d\n", v); + f.format("c: %c\n", v); + f.format("b: %b\n", v); + f.format("s: %s\n", v); + // f.format("f: %f\n", v); + // f.format("e: %e\n", v); + f.format("x: %x\n", v); + f.format("h: %h\n", v); + + BigInteger w = new BigInteger("50000000000000"); + System.out.println( + "w = new BigInteger(\"50000000000000\")"); + f.format("d: %d\n", w); + // f.format("c: %c\n", w); + f.format("b: %b\n", w); + f.format("s: %s\n", w); + // f.format("f: %f\n", w); + // f.format("e: %e\n", w); + f.format("x: %x\n", w); + f.format("h: %h\n", w); + + double x = 179.543; + System.out.println("x = 179.543"); + // f.format("d: %d\n", x); + // f.format("c: %c\n", x); + f.format("b: %b\n", x); + f.format("s: %s\n", x); + f.format("f: %f\n", x); + f.format("e: %e\n", x); + // f.format("x: %x\n", x); + f.format("h: %h\n", x); + + Conversion y = new Conversion(); + System.out.println("y = new Conversion()"); + // f.format("d: %d\n", y); + // f.format("c: %c\n", y); + f.format("b: %b\n", y); + f.format("s: %s\n", y); + // f.format("f: %f\n", y); + // f.format("e: %e\n", y); + // f.format("x: %x\n", y); + f.format("h: %h\n", y); + + boolean z = false; + System.out.println("z = false"); + // f.format("d: %d\n", z); + // f.format("c: %c\n", z); + f.format("b: %b\n", z); + f.format("s: %s\n", z); + // f.format("f: %f\n", z); + // f.format("e: %e\n", z); + // f.format("x: %x\n", z); + f.format("h: %h\n", z); + } +} /* Output: (Sample) +u = 'a' +s: a +c: a +b: true +h: 61 +v = 121 +d: 121 +c: y +b: true +s: 121 +x: 79 +h: 79 +w = new BigInteger("50000000000000") +d: 50000000000000 +b: true +s: 50000000000000 +x: 2d79883d2000 +h: 8842a1a7 +x = 179.543 +b: true +s: 179.543 +f: 179.543000 +e: 1.795430e+02 +h: 1ef462c +y = new Conversion() +b: true +s: Conversion@9cab16 +h: 9cab16 +z = false +b: false +s: false +h: 4d5 +*///:~ diff --git a/src/strings/DatabaseException.java b/src/strings/DatabaseException.java new file mode 100644 index 0000000..8bca0ee --- /dev/null +++ b/src/strings/DatabaseException.java @@ -0,0 +1,18 @@ +package strings;//: strings/DatabaseException.java + +public class DatabaseException extends Exception { + public DatabaseException(int transactionID, int queryID, + String message) { + super(String.format("(t%d, q%d) %s", transactionID, + queryID, message)); + } + public static void main(String[] args) { + try { + throw new DatabaseException(3, 7, "Write failed"); + } catch(Exception e) { + System.out.println(e); + } + } +} /* Output: +DatabaseException: (t3, q7) Write failed +*///:~ diff --git a/src/strings/Finding.java b/src/strings/Finding.java new file mode 100644 index 0000000..436e0a8 --- /dev/null +++ b/src/strings/Finding.java @@ -0,0 +1,22 @@ +package strings;//: strings/Finding.java +import java.util.regex.*; +import static net.mindview.util.Print.*; + +public class Finding { + public static void main(String[] args) { + Matcher m = Pattern.compile("\\w+") + .matcher("Evening is full of the linnet's wings"); + while(m.find()) { + printnb(m.group() + " "); + } + print(); + int i = 0; + while(m.find(i)) { + printnb(m.group() + " "); + i++; + } + } +} /* Output: +Evening is full of the linnet s wings +Evening vening ening ning ing ng g is is s full full ull ll l of of f the the he e linnet linnet innet nnet net et t s s wings wings ings ngs gs s +*///:~ diff --git a/src/strings/Groups.java b/src/strings/Groups.java new file mode 100644 index 0000000..7a88207 --- /dev/null +++ b/src/strings/Groups.java @@ -0,0 +1,35 @@ +package strings;//: strings/Groups.java +import java.util.regex.*; +import static net.mindview.util.Print.*; + +public class Groups { + static public final String POEM = + "Twas brillig, and the slithy toves\n" + + "Did gyre and gimble in the wabe.\n" + + "All mimsy were the borogoves,\n" + + "And the mome raths outgrabe.\n\n" + + "Beware the Jabberwock, my son,\n" + + "The jaws that bite, the claws that catch.\n" + + "Beware the Jubjub bird, and shun\n" + + "The frumious Bandersnatch."; + public static void main(String[] args) { + Matcher m = + Pattern.compile("(?m)(\\S+)\\s+((\\S+)\\s+(\\S+))$") + .matcher(POEM); + while(m.find()) { + for(int j = 0; j <= m.groupCount(); j++) { + printnb("[" + m.group(j) + "]"); + } + print(); + } + } +} /* Output: +[the slithy toves][the][slithy toves][slithy][toves] +[in the wabe.][in][the wabe.][the][wabe.] +[were the borogoves,][were][the borogoves,][the][borogoves,] +[mome raths outgrabe.][mome][raths outgrabe.][raths][outgrabe.] +[Jabberwock, my son,][Jabberwock,][my son,][my][son,] +[claws that catch.][claws][that catch.][that][catch.] +[bird, and shun][bird,][and shun][and][shun] +[The frumious Bandersnatch.][The][frumious Bandersnatch.][frumious][Bandersnatch.] +*///:~ diff --git a/src/strings/Immutable.java b/src/strings/Immutable.java new file mode 100644 index 0000000..42e5f1b --- /dev/null +++ b/src/strings/Immutable.java @@ -0,0 +1,19 @@ +package strings;//: strings/Immutable.java +import static net.mindview.util.Print.*; + +public class Immutable { + public static String upcase(String s) { + return s.toUpperCase(); + } + public static void main(String[] args) { + String q = "howdy"; + print(q); // howdy + String qq = upcase(q); + print(qq); // HOWDY + print(q); // howdy + } +} /* Output: +howdy +HOWDY +howdy +*///:~ diff --git a/src/strings/InfiniteRecursion.java b/src/strings/InfiniteRecursion.java new file mode 100644 index 0000000..41c13c0 --- /dev/null +++ b/src/strings/InfiniteRecursion.java @@ -0,0 +1,18 @@ +package strings;//: strings/InfiniteRecursion.java +// Accidental recursion. +// {RunByHand} +import java.util.*; + +public class InfiniteRecursion { + public String toString() { + return " InfiniteRecursion address: " + this + "\n"; + } + public static void main(String[] args) { + List v = + new ArrayList(); + for(int i = 0; i < 10; i++) { + v.add(new InfiniteRecursion()); + } + System.out.println(v); + } +} ///:~ diff --git a/src/strings/IntegerMatch.java b/src/strings/IntegerMatch.java new file mode 100644 index 0000000..b96edee --- /dev/null +++ b/src/strings/IntegerMatch.java @@ -0,0 +1,15 @@ +package strings;//: strings/IntegerMatch.java + +public class IntegerMatch { + public static void main(String[] args) { + System.out.println("-1234".matches("-?\\d+")); + System.out.println("5678".matches("-?\\d+")); + System.out.println("+911".matches("-?\\d+")); + System.out.println("+911".matches("(-|\\+)?\\d+")); + } +} /* Output: +true +true +false +true +*///:~ diff --git a/src/strings/JGrep.java b/src/strings/JGrep.java new file mode 100644 index 0000000..e493ac6 --- /dev/null +++ b/src/strings/JGrep.java @@ -0,0 +1,43 @@ +package strings;//: strings/JGrep.java +// A very simple version of the "grep" program. +// {Args: JGrep.java "\\b[Ssct]\\w+"} +import java.util.regex.*; +import net.mindview.util.*; + +public class JGrep { + public static void main(String[] args) throws Exception { + if(args.length < 2) { + System.out.println("Usage: java JGrep file regex"); + System.exit(0); + } + Pattern p = Pattern.compile(args[1]); + // Iterate through the lines of the input file: + int index = 0; + Matcher m = p.matcher(""); + for(String line : new TextFile(args[0])) { + m.reset(line); + while(m.find()) { + System.out.println(index++ + ": " + + m.group() + ": " + m.start()); + } + } + } +} /* Output: (Sample) +0: strings: 4 +1: simple: 10 +2: the: 28 +3: Ssct: 26 +4: class: 7 +5: static: 9 +6: String: 26 +7: throws: 41 +8: System: 6 +9: System: 6 +10: compile: 24 +11: through: 15 +12: the: 23 +13: the: 36 +14: String: 8 +15: System: 8 +16: start: 31 +*///:~ diff --git a/src/strings/ReFlags.java b/src/strings/ReFlags.java new file mode 100644 index 0000000..4550444 --- /dev/null +++ b/src/strings/ReFlags.java @@ -0,0 +1,20 @@ +package strings;//: strings/ReFlags.java +import java.util.regex.*; + +public class ReFlags { + public static void main(String[] args) { + Pattern p = Pattern.compile("^java", + Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + Matcher m = p.matcher( + "java has regex\nJava has regex\n" + + "JAVA has pretty good regular expressions\n" + + "Regular expressions are in Java"); + while(m.find()) { + System.out.println(m.group()); + } + } +} /* Output: +java +Java +JAVA +*///:~ diff --git a/src/strings/Receipt.java b/src/strings/Receipt.java new file mode 100644 index 0000000..968852d --- /dev/null +++ b/src/strings/Receipt.java @@ -0,0 +1,38 @@ +package strings;//: strings/Receipt.java +import java.util.*; + +public class Receipt { + private double total = 0; + private Formatter f = new Formatter(System.out); + public void printTitle() { + f.format("%-15s %5s %10s\n", "Item", "Qty", "Price"); + f.format("%-15s %5s %10s\n", "----", "---", "-----"); + } + public void print(String name, int qty, double price) { + f.format("%-15.15s %5d %10.2f\n", name, qty, price); + total += price; + } + public void printTotal() { + f.format("%-15s %5s %10.2f\n", "Tax", "", total*0.06); + f.format("%-15s %5s %10s\n", "", "", "-----"); + f.format("%-15s %5s %10.2f\n", "Total", "", + total * 1.06); + } + public static void main(String[] args) { + Receipt receipt = new Receipt(); + receipt.printTitle(); + receipt.print("Jack's Magic Beans", 4, 4.25); + receipt.print("Princess Peas", 3, 5.1); + receipt.print("Three Bears Porridge", 1, 14.29); + receipt.printTotal(); + } +} /* Output: +Item Qty Price +---- --- ----- +Jack's Magic Be 4 4.25 +Princess Peas 3 5.10 +Three Bears Por 1 14.29 +Tax 1.42 + ----- +Total 25.06 +*///:~ diff --git a/src/strings/Replacing.java b/src/strings/Replacing.java new file mode 100644 index 0000000..c085e1e --- /dev/null +++ b/src/strings/Replacing.java @@ -0,0 +1,13 @@ +package strings;//: strings/Replacing.java +import static net.mindview.util.Print.*; + +public class Replacing { + static String s = Splitting.knights; + public static void main(String[] args) { + print(s.replaceFirst("f\\w+", "located")); + print(s.replaceAll("shrubbery|tree|herring","banana")); + } +} /* Output: +Then, when you have located the shrubbery, you must cut down the mightiest tree in the forest... with... a herring! +Then, when you have found the banana, you must cut down the mightiest banana in the forest... with... a banana! +*///:~ diff --git a/src/strings/ReplacingStringTokenizer.java b/src/strings/ReplacingStringTokenizer.java new file mode 100644 index 0000000..e7aaf63 --- /dev/null +++ b/src/strings/ReplacingStringTokenizer.java @@ -0,0 +1,22 @@ +package strings;//: strings/ReplacingStringTokenizer.java +import java.util.*; + +public class ReplacingStringTokenizer { + public static void main(String[] args) { + String input = "But I'm not dead yet! I feel happy!"; + StringTokenizer stoke = new StringTokenizer(input); + while(stoke.hasMoreElements()) { + System.out.print(stoke.nextToken() + " "); + } + System.out.println(); + System.out.println(Arrays.toString(input.split(" "))); + Scanner scanner = new Scanner(input); + while(scanner.hasNext()) { + System.out.print(scanner.next() + " "); + } + } +} /* Output: +But I'm not dead yet! I feel happy! +[But, I'm, not, dead, yet!, I, feel, happy!] +But I'm not dead yet! I feel happy! +*///:~ diff --git a/src/strings/Resetting.java b/src/strings/Resetting.java new file mode 100644 index 0000000..356d187 --- /dev/null +++ b/src/strings/Resetting.java @@ -0,0 +1,20 @@ +package strings;//: strings/Resetting.java +import java.util.regex.*; + +public class Resetting { + public static void main(String[] args) throws Exception { + Matcher m = Pattern.compile("[frb][aiu][gx]") + .matcher("fix the rug with bags"); + while(m.find()) { + System.out.print(m.group() + " "); + } + System.out.println(); + m.reset("fix the rig with rags"); + while(m.find()) { + System.out.print(m.group() + " "); + } + } +} /* Output: +fix rug bag +fix rig rag +*///:~ diff --git a/src/strings/Rudolph.java b/src/strings/Rudolph.java new file mode 100644 index 0000000..ba3eaff --- /dev/null +++ b/src/strings/Rudolph.java @@ -0,0 +1,15 @@ +package strings;//: strings/Rudolph.java + +public class Rudolph { + public static void main(String[] args) { + for(String pattern : new String[]{ "Rudolph", + "[rR]udolph", "[rR][aeiou][a-z]ol.*", "R.*" }) { + System.out.println("Rudolph".matches(pattern)); + } + } +} /* Output: +true +true +true +true +*///:~ diff --git a/src/strings/ScannerDelimiter.java b/src/strings/ScannerDelimiter.java new file mode 100644 index 0000000..d4bf87b --- /dev/null +++ b/src/strings/ScannerDelimiter.java @@ -0,0 +1,18 @@ +package strings;//: strings/ScannerDelimiter.java +import java.util.*; + +public class ScannerDelimiter { + public static void main(String[] args) { + Scanner scanner = new Scanner("12, 42, 78, 99, 42"); + scanner.useDelimiter("\\s*,\\s*"); + while(scanner.hasNextInt()) { + System.out.println(scanner.nextInt()); + } + } +} /* Output: +12 +42 +78 +99 +42 +*///:~ diff --git a/src/strings/SimpleFormat.java b/src/strings/SimpleFormat.java new file mode 100644 index 0000000..1360785 --- /dev/null +++ b/src/strings/SimpleFormat.java @@ -0,0 +1,18 @@ +package strings;//: strings/SimpleFormat.java + +public class SimpleFormat { + public static void main(String[] args) { + int x = 5; + double y = 5.332542; + // The old way: + System.out.println("Row 1: [" + x + " " + y + "]"); + // The new way: + System.out.format("Row 1: [%d %f]\n", x, y); + // or + System.out.printf("Row 1: [%d %f]\n", x, y); + } +} /* Output: +Row 1: [5 5.332542] +Row 1: [5 5.332542] +Row 1: [5 5.332542] +*///:~ diff --git a/src/strings/SimpleRead.java b/src/strings/SimpleRead.java new file mode 100644 index 0000000..064c54c --- /dev/null +++ b/src/strings/SimpleRead.java @@ -0,0 +1,38 @@ +package strings;//: strings/SimpleRead.java +import java.io.*; + +public class SimpleRead { + public static BufferedReader input = new BufferedReader( + new StringReader("Sir Robin of Camelot\n22 1.61803")); + public static void main(String[] args) { + try { + System.out.println("What is your name?"); + String name = input.readLine(); + System.out.println(name); + System.out.println( + "How old are you? What is your favorite double?"); + System.out.println("(input: )"); + String numbers = input.readLine(); + System.out.println(numbers); + String[] numArray = numbers.split(" "); + int age = Integer.parseInt(numArray[0]); + double favorite = Double.parseDouble(numArray[1]); + System.out.format("Hi %s.\n", name); + System.out.format("In 5 years you will be %d.\n", + age + 5); + System.out.format("My favorite double is %f.", + favorite / 2); + } catch(IOException e) { + System.err.println("I/O exception"); + } + } +} /* Output: +What is your name? +Sir Robin of Camelot +How old are you? What is your favorite double? +(input: ) +22 1.61803 +Hi Sir Robin of Camelot. +In 5 years you will be 27. +My favorite double is 0.809015. +*///:~ diff --git a/src/strings/SplitDemo.java b/src/strings/SplitDemo.java new file mode 100644 index 0000000..e74ce36 --- /dev/null +++ b/src/strings/SplitDemo.java @@ -0,0 +1,19 @@ +package strings;//: strings/SplitDemo.java +import java.util.regex.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class SplitDemo { + public static void main(String[] args) { + String input = + "This!!unusual use!!of exclamation!!points"; + print(Arrays.toString( + Pattern.compile("!!").split(input))); + // Only do the first three: + print(Arrays.toString( + Pattern.compile("!!").split(input, 3))); + } +} /* Output: +[This, unusual use, of exclamation, points] +[This, unusual use, of exclamation!!points] +*///:~ diff --git a/src/strings/Splitting.java b/src/strings/Splitting.java new file mode 100644 index 0000000..393781b --- /dev/null +++ b/src/strings/Splitting.java @@ -0,0 +1,22 @@ +package strings;//: strings/Splitting.java +import java.util.*; + +public class Splitting { + public static String knights = + "Then, when you have found the shrubbery, you must " + + "cut down the mightiest tree in the forest... " + + "with... a herring!"; + public static void split(String regex) { + System.out.println( + Arrays.toString(knights.split(regex))); + } + public static void main(String[] args) { + split(" "); // Doesn't have to contain regex chars + split("\\W+"); // Non-word characters + split("n\\W+"); // 'n' followed by non-word characters + } +} /* Output: +[Then,, when, you, have, found, the, shrubbery,, you, must, cut, down, the, mightiest, tree, in, the, forest..., with..., a, herring!] +[Then, when, you, have, found, the, shrubbery, you, must, cut, down, the, mightiest, tree, in, the, forest, with, a, herring] +[The, whe, you have found the shrubbery, you must cut dow, the mightiest tree i, the forest... with... a herring!] +*///:~ diff --git a/src/strings/StartEnd.java b/src/strings/StartEnd.java new file mode 100644 index 0000000..91d6ea5 --- /dev/null +++ b/src/strings/StartEnd.java @@ -0,0 +1,83 @@ +package strings;//: strings/StartEnd.java +import java.util.regex.*; +import static net.mindview.util.Print.*; + +public class StartEnd { + public static String input = + "As long as there is injustice, whenever a\n" + + "Targathian baby cries out, wherever a distress\n" + + "signal sounds among the stars ... We'll be there.\n" + + "This fine ship, and this fine crew ...\n" + + "Never give up! Never surrender!"; + private static class Display { + private boolean regexPrinted = false; + private String regex; + Display(String regex) { this.regex = regex; } + void display(String message) { + if(!regexPrinted) { + print(regex); + regexPrinted = true; + } + print(message); + } + } + static void examine(String s, String regex) { + Display d = new Display(regex); + Pattern p = Pattern.compile(regex); + Matcher m = p.matcher(s); + while(m.find()) { + d.display("find() '" + m.group() + + "' start = "+ m.start() + " end = " + m.end()); + } + if(m.lookingAt()) // No reset() necessary + { + d.display("lookingAt() start = " + + m.start() + " end = " + m.end()); + } + if(m.matches()) // No reset() necessary + { + d.display("matches() start = " + + m.start() + " end = " + m.end()); + } + } + public static void main(String[] args) { + for(String in : input.split("\n")) { + print("input : " + in); + for(String regex : new String[]{"\\w*ere\\w*", + "\\w*ever", "T\\w+", "Never.*?!"}) { + examine(in, regex); + } + } + } +} /* Output: +input : As long as there is injustice, whenever a +\w*ere\w* +find() 'there' start = 11 end = 16 +\w*ever +find() 'whenever' start = 31 end = 39 +input : Targathian baby cries out, wherever a distress +\w*ere\w* +find() 'wherever' start = 27 end = 35 +\w*ever +find() 'wherever' start = 27 end = 35 +T\w+ +find() 'Targathian' start = 0 end = 10 +lookingAt() start = 0 end = 10 +input : signal sounds among the stars ... We'll be there. +\w*ere\w* +find() 'there' start = 43 end = 48 +input : This fine ship, and this fine crew ... +T\w+ +find() 'This' start = 0 end = 4 +lookingAt() start = 0 end = 4 +input : Never give up! Never surrender! +\w*ever +find() 'Never' start = 0 end = 5 +find() 'Never' start = 15 end = 20 +lookingAt() start = 0 end = 5 +Never.*?! +find() 'Never give up!' start = 0 end = 14 +find() 'Never surrender!' start = 15 end = 31 +lookingAt() start = 0 end = 14 +matches() start = 0 end = 31 +*///:~ diff --git a/src/strings/TestRegularExpression.java b/src/strings/TestRegularExpression.java new file mode 100644 index 0000000..3f65a53 --- /dev/null +++ b/src/strings/TestRegularExpression.java @@ -0,0 +1,39 @@ +package strings;//: strings/TestRegularExpression.java +// Allows you to easily try out regular expressions. +// {Args: abcabcabcdefabc "abc+" "(abc)+" "(abc){2,}" } +import java.util.regex.*; +import static net.mindview.util.Print.*; + +public class TestRegularExpression { + public static void main(String[] args) { + if(args.length < 2) { + print("Usage:\njava TestRegularExpression " + + "characterSequence regularExpression+"); + System.exit(0); + } + print("Input: \"" + args[0] + "\""); + for(String arg : args) { + print("Regular expression: \"" + arg + "\""); + Pattern p = Pattern.compile(arg); + Matcher m = p.matcher(args[0]); + while(m.find()) { + print("Match \"" + m.group() + "\" at positions " + + m.start() + "-" + (m.end() - 1)); + } + } + } +} /* Output: +Input: "abcabcabcdefabc" +Regular expression: "abcabcabcdefabc" +Match "abcabcabcdefabc" at positions 0-14 +Regular expression: "abc+" +Match "abc" at positions 0-2 +Match "abc" at positions 3-5 +Match "abc" at positions 6-8 +Match "abc" at positions 12-14 +Regular expression: "(abc)+" +Match "abcabcabc" at positions 0-8 +Match "abc" at positions 12-14 +Regular expression: "(abc){2,}" +Match "abcabcabc" at positions 0-8 +*///:~ diff --git a/src/strings/TheReplacements.java b/src/strings/TheReplacements.java new file mode 100644 index 0000000..826ad65 --- /dev/null +++ b/src/strings/TheReplacements.java @@ -0,0 +1,52 @@ +package strings;//: strings/TheReplacements.java +import java.util.regex.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +/*! Here's a block of text to use as input to + the regular expression matcher. Note that we'll + first extract the block of text by looking for + the special delimiters, then process the + extracted block. !*/ + +public class TheReplacements { + public static void main(String[] args) throws Exception { + String s = TextFile.read("TheReplacements.java"); + // Match the specially commented block of text above: + Matcher mInput = + Pattern.compile("/\\*!(.*)!\\*/", Pattern.DOTALL) + .matcher(s); + if(mInput.find()) { + s = mInput.group(1); // Captured by parentheses + } + // Replace two or more spaces with a single space: + s = s.replaceAll(" {2,}", " "); + // Replace one or more spaces at the beginning of each + // line with no spaces. Must enable MULTILINE mode: + s = s.replaceAll("(?m)^ +", ""); + print(s); + s = s.replaceFirst("[aeiou]", "(VOWEL1)"); + StringBuffer sbuf = new StringBuffer(); + Pattern p = Pattern.compile("[aeiou]"); + Matcher m = p.matcher(s); + // Process the find information as you + // perform the replacements: + while(m.find()) { + m.appendReplacement(sbuf, m.group().toUpperCase()); + } + // Put in the remainder of the text: + m.appendTail(sbuf); + print(sbuf); + } +} /* Output: +Here's a block of text to use as input to +the regular expression matcher. Note that we'll +first extract the block of text by looking for +the special delimiters, then process the +extracted block. +H(VOWEL1)rE's A blOck Of tExt tO UsE As InpUt tO +thE rEgUlAr ExprEssIOn mAtchEr. NOtE thAt wE'll +fIrst ExtrAct thE blOck Of tExt by lOOkIng fOr +thE spEcIAl dElImItErs, thEn prOcEss thE +ExtrActEd blOck. +*///:~ diff --git a/src/strings/ThreatAnalyzer.java b/src/strings/ThreatAnalyzer.java new file mode 100644 index 0000000..f450140 --- /dev/null +++ b/src/strings/ThreatAnalyzer.java @@ -0,0 +1,31 @@ +package strings;//: strings/ThreatAnalyzer.java +import java.util.regex.*; +import java.util.*; + +public class ThreatAnalyzer { + static String threatData = + "58.27.82.161@02/10/2005\n" + + "204.45.234.40@02/11/2005\n" + + "58.27.82.161@02/11/2005\n" + + "58.27.82.161@02/12/2005\n" + + "58.27.82.161@02/12/2005\n" + + "[Next log section with different data format]"; + public static void main(String[] args) { + Scanner scanner = new Scanner(threatData); + String pattern = "(\\d+[.]\\d+[.]\\d+[.]\\d+)@" + + "(\\d{2}/\\d{2}/\\d{4})"; + while(scanner.hasNext(pattern)) { + scanner.next(pattern); + MatchResult match = scanner.match(); + String ip = match.group(1); + String date = match.group(2); + System.out.format("Threat on %s from %s\n", date,ip); + } + } +} /* Output: +Threat on 02/10/2005 from 58.27.82.161 +Threat on 02/11/2005 from 204.45.234.40 +Threat on 02/11/2005 from 58.27.82.161 +Threat on 02/12/2005 from 58.27.82.161 +Threat on 02/12/2005 from 58.27.82.161 +*///:~ diff --git a/src/strings/Turtle.java b/src/strings/Turtle.java new file mode 100644 index 0000000..4bff404 --- /dev/null +++ b/src/strings/Turtle.java @@ -0,0 +1,35 @@ +package strings;//: strings/Turtle.java +import java.io.*; +import java.util.*; + +public class Turtle { + private String name; + private Formatter f; + public Turtle(String name, Formatter f) { + this.name = name; + this.f = f; + } + public void move(int x, int y) { + f.format("%s The Turtle is at (%d,%d)\n", name, x, y); + } + public static void main(String[] args) { + PrintStream outAlias = System.out; + Turtle tommy = new Turtle("Tommy", + new Formatter(System.out)); + Turtle terry = new Turtle("Terry", + new Formatter(outAlias)); + tommy.move(0,0); + terry.move(4,8); + tommy.move(3,4); + terry.move(2,5); + tommy.move(3,3); + terry.move(3,3); + } +} /* Output: +Tommy The Turtle is at (0,0) +Terry The Turtle is at (4,8) +Tommy The Turtle is at (3,4) +Terry The Turtle is at (2,5) +Tommy The Turtle is at (3,3) +Terry The Turtle is at (3,3) +*///:~ diff --git a/src/strings/UsingStringBuilder.java b/src/strings/UsingStringBuilder.java new file mode 100644 index 0000000..b4d79db --- /dev/null +++ b/src/strings/UsingStringBuilder.java @@ -0,0 +1,22 @@ +package strings;//: strings/UsingStringBuilder.java +import java.util.*; + +public class UsingStringBuilder { + public static Random rand = new Random(47); + public String toString() { + StringBuilder result = new StringBuilder("["); + for(int i = 0; i < 25; i++) { + result.append(rand.nextInt(100)); + result.append(", "); + } + result.delete(result.length()-2, result.length()); + result.append("]"); + return result.toString(); + } + public static void main(String[] args) { + UsingStringBuilder usb = new UsingStringBuilder(); + System.out.println(usb); + } +} /* Output: +[58, 55, 93, 61, 61, 29, 68, 0, 22, 7, 88, 28, 51, 89, 9, 78, 98, 61, 20, 58, 16, 40, 11, 22, 4] +*///:~ diff --git a/src/strings/WhitherStringBuilder.java b/src/strings/WhitherStringBuilder.java new file mode 100644 index 0000000..4cd2c55 --- /dev/null +++ b/src/strings/WhitherStringBuilder.java @@ -0,0 +1,18 @@ +package strings;//: strings/WhitherStringBuilder.java + +public class WhitherStringBuilder { + public String implicit(String[] fields) { + String result = ""; + for(int i = 0; i < fields.length; i++) { + result += fields[i]; + } + return result; + } + public String explicit(String[] fields) { + StringBuilder result = new StringBuilder(); + for(int i = 0; i < fields.length; i++) { + result.append(fields[i]); + } + return result.toString(); + } +} ///:~ diff --git a/src/strings/build.xml b/src/strings/build.xml new file mode 100644 index 0000000..7e4f840 --- /dev/null +++ b/src/strings/build.xml @@ -0,0 +1,366 @@ + + + + + + build.xml for the source code for the strings chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/swt/ColorBoxes.java b/src/swt/ColorBoxes.java new file mode 100644 index 0000000..04ee7c0 --- /dev/null +++ b/src/swt/ColorBoxes.java @@ -0,0 +1,82 @@ +package swt;//: swt/ColorBoxes.java +// SWT translation of Swing ColorBoxes.java. +import swt.util.*; +import org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.layout.*; +import java.util.concurrent.*; +import java.util.*; +import net.mindview.util.*; + +class CBox extends Canvas implements Runnable { + class CBoxPaintListener implements PaintListener { + public void paintControl(PaintEvent e) { + Color color = new Color(e.display, cColor); + e.gc.setBackground(color); + Point size = getSize(); + e.gc.fillRectangle(0, 0, size.x, size.y); + color.dispose(); + } + } + private static Random rand = new Random(); + private static RGB newColor() { + return new RGB(rand.nextInt(255), + rand.nextInt(255), rand.nextInt(255)); + } + private int pause; + private RGB cColor = newColor(); + public CBox(Composite parent, int pause) { + super(parent, SWT.NONE); + this.pause = pause; + addPaintListener(new CBoxPaintListener()); + } + public void run() { + try { + while(!Thread.interrupted()) { + cColor = newColor(); + getDisplay().asyncExec(new Runnable() { + public void run() { + try { redraw(); } catch(SWTException e) {} + // SWTException is OK when the parent + // is terminated from under us. + } + }); + TimeUnit.MILLISECONDS.sleep(pause); + } + } catch(InterruptedException e) { + // Acceptable way to exit + } catch(SWTException e) { + // Acceptable way to exit: our parent + // was terminated from under us. + } + } +} + +public class ColorBoxes implements SWTApplication { + private int grid = 12; + private int pause = 50; + public void createContents(Composite parent) { + GridLayout gridLayout = new GridLayout(grid, true); + gridLayout.horizontalSpacing = 0; + gridLayout.verticalSpacing = 0; + parent.setLayout(gridLayout); + ExecutorService exec = new DaemonThreadPoolExecutor(); + for(int i = 0; i < (grid * grid); i++) { + final CBox cb = new CBox(parent, pause); + cb.setLayoutData(new GridData(GridData.FILL_BOTH)); + exec.execute(cb); + } + } + public static void main(String[] args) { + ColorBoxes boxes = new ColorBoxes(); + if(args.length > 0) { + boxes.grid = new Integer(args[0]); + } + if(args.length > 1) { + boxes.pause = new Integer(args[1]); + } + SWTConsole.run(boxes, 500, 400); + } +} ///:~ diff --git a/src/swt/DisplayEnvironment.java b/src/swt/DisplayEnvironment.java new file mode 100644 index 0000000..832fdd1 --- /dev/null +++ b/src/swt/DisplayEnvironment.java @@ -0,0 +1,20 @@ +package swt;//: swt/DisplayEnvironment.java +import swt.util.*; +import org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.swt.layout.*; +import java.util.*; + +public class DisplayEnvironment implements SWTApplication { + public void createContents(Composite parent) { + parent.setLayout(new FillLayout()); + Text text = new Text(parent, SWT.WRAP | SWT.V_SCROLL); + for(Map.Entry entry: System.getenv().entrySet()) { + text.append(entry.getKey() + ": " + + entry.getValue() + "\n"); + } + } + public static void main(String [] args) { + SWTConsole.run(new DisplayEnvironment(), 800, 600); + } +} ///:~ diff --git a/src/swt/DisplayProperties.java b/src/swt/DisplayProperties.java new file mode 100644 index 0000000..68dc614 --- /dev/null +++ b/src/swt/DisplayProperties.java @@ -0,0 +1,25 @@ +package swt;//: swt/DisplayProperties.java +import org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.swt.layout.*; +import java.io.*; + +public class DisplayProperties { + public static void main(String [] args) { + Display display = new Display(); + Shell shell = new Shell(display); + shell.setText("Display Properties"); + shell.setLayout(new FillLayout()); + Text text = new Text(shell, SWT.WRAP | SWT.V_SCROLL); + StringWriter props = new StringWriter(); + System.getProperties().list(new PrintWriter(props)); + text.setText(props.toString()); + shell.open(); + while(!shell.isDisposed()) { + if(!display.readAndDispatch()) { + display.sleep(); + } + } + display.dispose(); + } +} ///:~ diff --git a/src/swt/HelloSWT.java b/src/swt/HelloSWT.java new file mode 100644 index 0000000..a57f75f --- /dev/null +++ b/src/swt/HelloSWT.java @@ -0,0 +1,19 @@ +package swt;//: swt/HelloSWT.java +// {Requires: org.eclipse.swt.widgets.Display; You must +// install the SWT library from http://www.eclipse.org } +import org.eclipse.swt.widgets.*; + +public class HelloSWT { + public static void main(String [] args) { + Display display = new Display(); + Shell shell = new Shell(display); + shell.setText("Hi there, SWT!"); // Title bar + shell.open(); + while(!shell.isDisposed()) { + if(!display.readAndDispatch()) { + display.sleep(); + } + } + display.dispose(); + } +} ///:~ diff --git a/src/swt/Menus.java b/src/swt/Menus.java new file mode 100644 index 0000000..e31b373 --- /dev/null +++ b/src/swt/Menus.java @@ -0,0 +1,47 @@ +package swt;//: swt/Menus.java +// Fun with menus. +import swt.util.*; +import org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; +import java.util.*; +import net.mindview.util.*; + +public class Menus implements SWTApplication { + private static Shell shell; + public void createContents(Composite parent) { + shell = parent.getShell(); + Menu bar = new Menu(shell, SWT.BAR); + shell.setMenuBar(bar); + Set words = new TreeSet( + new TextFile("Menus.java", "\\W+")); + Iterator it = words.iterator(); + while(it.next().matches("[0-9]+")) { + } + MenuItem[] mItem = new MenuItem[7]; + for(int i = 0; i < mItem.length; i++) { + mItem[i] = new MenuItem(bar, SWT.CASCADE); + mItem[i].setText(it.next()); + Menu submenu = new Menu(shell, SWT.DROP_DOWN); + mItem[i].setMenu(submenu); + } + int i = 0; + while(it.hasNext()) { + addItem(bar, it, mItem[i]); + i = (i + 1) % mItem.length; + } + } + static Listener listener = new Listener() { + public void handleEvent(Event e) { + System.out.println(e.toString()); + } + }; + void + addItem(Menu bar, Iterator it, MenuItem mItem) { + MenuItem item = new MenuItem(mItem.getMenu(),SWT.PUSH); + item.addListener(SWT.Selection, listener); + item.setText(it.next()); + } + public static void main(String[] args) { + SWTConsole.run(new Menus(), 600, 200); + } +} ///:~ diff --git a/src/swt/ShellsAreMainWindows.java b/src/swt/ShellsAreMainWindows.java new file mode 100644 index 0000000..43ff74b --- /dev/null +++ b/src/swt/ShellsAreMainWindows.java @@ -0,0 +1,28 @@ +package swt;//: swt/ShellsAreMainWindows.java +import org.eclipse.swt.widgets.*; + +public class ShellsAreMainWindows { + static Shell[] shells = new Shell[10]; + public static void main(String [] args) { + Display display = new Display(); + for(int i = 0; i < shells.length; i++) { + shells[i] = new Shell(display); + shells[i].setText("Shell #" + i); + shells[i].open(); + } + while(!shellsDisposed()) { + if(!display.readAndDispatch()) { + display.sleep(); + } + } + display.dispose(); + } + static boolean shellsDisposed() { + for(int i = 0; i < shells.length; i++) { + if(shells[i].isDisposed()) { + return true; + } + } + return false; + } +} ///:~ diff --git a/src/swt/SineWave.java b/src/swt/SineWave.java new file mode 100644 index 0000000..a86b8f6 --- /dev/null +++ b/src/swt/SineWave.java @@ -0,0 +1,74 @@ +package swt;//: swt/SineWave.java +// SWT translation of Swing SineWave.java. +import swt.util.*; +import org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.layout.*; + +class SineDraw extends Canvas { + private static final int SCALEFACTOR = 200; + private int cycles; + private int points; + private double[] sines; + private int[] pts; + public SineDraw(Composite parent, int style) { + super(parent, style); + addPaintListener(new PaintListener() { + public void paintControl(PaintEvent e) { + int maxWidth = getSize().x; + double hstep = (double)maxWidth / (double)points; + int maxHeight = getSize().y; + pts = new int[points]; + for(int i = 0; i < points; i++) { + pts[i] = (int)((sines[i] * maxHeight / 2 * .95) + + (maxHeight / 2)); + } + e.gc.setForeground( + e.display.getSystemColor(SWT.COLOR_RED)); + for(int i = 1; i < points; i++) { + int x1 = (int)((i - 1) * hstep); + int x2 = (int)(i * hstep); + int y1 = pts[i - 1]; + int y2 = pts[i]; + e.gc.drawLine(x1, y1, x2, y2); + } + } + }); + setCycles(5); + } + public void setCycles(int newCycles) { + cycles = newCycles; + points = SCALEFACTOR * cycles * 2; + sines = new double[points]; + for(int i = 0; i < points; i++) { + double radians = (Math.PI / SCALEFACTOR) * i; + sines[i] = Math.sin(radians); + } + redraw(); + } +} + +public class SineWave implements SWTApplication { + private SineDraw sines; + private Slider slider; + public void createContents(Composite parent) { + parent.setLayout(new GridLayout(1, true)); + sines = new SineDraw(parent, SWT.NONE); + sines.setLayoutData( + new GridData(SWT.FILL, SWT.FILL, true, true)); + sines.setFocus(); + slider = new Slider(parent, SWT.HORIZONTAL); + slider.setValues(5, 1, 30, 1, 1, 1); + slider.setLayoutData( + new GridData(SWT.FILL, SWT.DEFAULT, true, false)); + slider.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent event) { + sines.setCycles(slider.getSelection()); + } + }); + } + public static void main(String[] args) { + SWTConsole.run(new SineWave(), 700, 400); + } +} ///:~ diff --git a/src/swt/TabbedPane.java b/src/swt/TabbedPane.java new file mode 100644 index 0000000..d28af59 --- /dev/null +++ b/src/swt/TabbedPane.java @@ -0,0 +1,147 @@ +package swt;//: swt/TabbedPane.java +// Placing SWT components in tabbed panes. +import swt.util.*; +import org.eclipse.swt.*; +import org.eclipse.swt.widgets.*; +import org.eclipse.swt.events.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.browser.*; + +public class TabbedPane implements SWTApplication { + private static TabFolder folder; + private static Shell shell; + public void createContents(Composite parent) { + shell = parent.getShell(); + parent.setLayout(new FillLayout()); + folder = new TabFolder(shell, SWT.BORDER); + labelTab(); + directoryDialogTab(); + buttonTab(); + sliderTab(); + scribbleTab(); + browserTab(); + } + public static void labelTab() { + TabItem tab = new TabItem(folder, SWT.CLOSE); + tab.setText("A Label"); // Text on the tab + tab.setToolTipText("A simple label"); + Label label = new Label(folder, SWT.CENTER); + label.setText("Label text"); + tab.setControl(label); + } + public static void directoryDialogTab() { + TabItem tab = new TabItem(folder, SWT.CLOSE); + tab.setText("Directory Dialog"); + tab.setToolTipText("Select a directory"); + final Button b = new Button(folder, SWT.PUSH); + b.setText("Select a Directory"); + b.addListener(SWT.MouseDown, new Listener() { + public void handleEvent(Event e) { + DirectoryDialog dd = new DirectoryDialog(shell); + String path = dd.open(); + if(path != null) { + b.setText(path); + } + } + }); + tab.setControl(b); + } + public static void buttonTab() { + TabItem tab = new TabItem(folder, SWT.CLOSE); + tab.setText("Buttons"); + tab.setToolTipText("Different kinds of Buttons"); + Composite composite = new Composite(folder, SWT.NONE); + composite.setLayout(new GridLayout(4, true)); + for(int dir : new int[]{ + SWT.UP, SWT.RIGHT, SWT.LEFT, SWT.DOWN + }) { + Button b = new Button(composite, SWT.ARROW | dir); + b.addListener(SWT.MouseDown, listener); + } + newButton(composite, SWT.CHECK, "Check button"); + newButton(composite, SWT.PUSH, "Push button"); + newButton(composite, SWT.RADIO, "Radio button"); + newButton(composite, SWT.TOGGLE, "Toggle button"); + newButton(composite, SWT.FLAT, "Flat button"); + tab.setControl(composite); + } + private static Listener listener = new Listener() { + public void handleEvent(Event e) { + MessageBox m = new MessageBox(shell, SWT.OK); + m.setMessage(e.toString()); + m.open(); + } + }; + private static void newButton(Composite composite, + int type, String label) { + Button b = new Button(composite, type); + b.setText(label); + b.addListener(SWT.MouseDown, listener); + } + public static void sliderTab() { + TabItem tab = new TabItem(folder, SWT.CLOSE); + tab.setText("Sliders and Progress bars"); + tab.setToolTipText("Tied Slider to ProgressBar"); + Composite composite = new Composite(folder, SWT.NONE); + composite.setLayout(new GridLayout(2, true)); + final Slider slider = + new Slider(composite, SWT.HORIZONTAL); + final ProgressBar progress = + new ProgressBar(composite, SWT.HORIZONTAL); + slider.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent event) { + progress.setSelection(slider.getSelection()); + } + }); + tab.setControl(composite); + } + public static void scribbleTab() { + TabItem tab = new TabItem(folder, SWT.CLOSE); + tab.setText("Scribble"); + tab.setToolTipText("Simple graphics: drawing"); + final Canvas canvas = new Canvas(folder, SWT.NONE); + ScribbleMouseListener sml= new ScribbleMouseListener(); + canvas.addMouseListener(sml); + canvas.addMouseMoveListener(sml); + tab.setControl(canvas); + } + private static class ScribbleMouseListener + extends MouseAdapter implements MouseMoveListener { + private Point p = new Point(0, 0); + public void mouseMove(MouseEvent e) { + if((e.stateMask & SWT.BUTTON1) == 0) { + return; + } + GC gc = new GC((Canvas)e.widget); + gc.drawLine(p.x, p.y, e.x, e.y); + gc.dispose(); + updatePoint(e); + } + public void mouseDown(MouseEvent e) { updatePoint(e); } + private void updatePoint(MouseEvent e) { + p.x = e.x; + p.y = e.y; + } + } + public static void browserTab() { + TabItem tab = new TabItem(folder, SWT.CLOSE); + tab.setText("A Browser"); + tab.setToolTipText("A Web browser"); + Browser browser = null; + try { + browser = new Browser(folder, SWT.NONE); + } catch(SWTError e) { + Label label = new Label(folder, SWT.BORDER); + label.setText("Could not initialize browser"); + tab.setControl(label); + } + if(browser != null) { + browser.setUrl("http://www.mindview.net"); + tab.setControl(browser); + } + } + public static void main(String[] args) { + SWTConsole.run(new TabbedPane(), 800, 600); + } +} ///:~ diff --git a/src/swt/build.xml b/src/swt/build.xml new file mode 100644 index 0000000..5a71a19 --- /dev/null +++ b/src/swt/build.xml @@ -0,0 +1,173 @@ + + + + + + build.xml for the source code for the swt chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/swt/util/SWTApplication.java b/src/swt/util/SWTApplication.java new file mode 100644 index 0000000..9e3847f --- /dev/null +++ b/src/swt/util/SWTApplication.java @@ -0,0 +1,7 @@ +//: swt/util/SWTApplication.java +package swt.util; +import org.eclipse.swt.widgets.*; + +public interface SWTApplication { + void createContents(Composite parent); +} ///:~ diff --git a/src/swt/util/SWTConsole.java b/src/swt/util/SWTConsole.java new file mode 100644 index 0000000..563d643 --- /dev/null +++ b/src/swt/util/SWTConsole.java @@ -0,0 +1,21 @@ +//: swt/util/SWTConsole.java +package swt.util; +import org.eclipse.swt.widgets.*; + +public class SWTConsole { + public static void + run(SWTApplication swtApp, int width, int height) { + Display display = new Display(); + Shell shell = new Shell(display); + shell.setText(swtApp.getClass().getSimpleName()); + swtApp.createContents(shell); + shell.setSize(width, height); + shell.open(); + while(!shell.isDisposed()) { + if(!display.readAndDispatch()) { + display.sleep(); + } + } + display.dispose(); + } +} ///:~ diff --git a/src/typeinfo/AnonymousImplementation.java b/src/typeinfo/AnonymousImplementation.java new file mode 100644 index 0000000..4bfb7ed --- /dev/null +++ b/src/typeinfo/AnonymousImplementation.java @@ -0,0 +1,36 @@ +package typeinfo;//: typeinfo/AnonymousImplementation.java +// Anonymous inner classes can't hide from reflection. +import typeinfo.interfacea.*; +import static net.mindview.util.Print.*; + +class AnonymousA { + public static A makeA() { + return new A() { + public void f() { print("public C.f()"); } + public void g() { print("public C.g()"); } + void u() { print("package C.u()"); } + protected void v() { print("protected C.v()"); } + private void w() { print("private C.w()"); } + }; + } +} + +public class AnonymousImplementation { + public static void main(String[] args) throws Exception { + A a = AnonymousA.makeA(); + a.f(); + System.out.println(a.getClass().getName()); + // Reflection still gets into the anonymous class: + HiddenImplementation.callHiddenMethod(a, "g"); + HiddenImplementation.callHiddenMethod(a, "u"); + HiddenImplementation.callHiddenMethod(a, "v"); + HiddenImplementation.callHiddenMethod(a, "w"); + } +} /* Output: +public C.f() +AnonymousA$1 +public C.g() +package C.u() +protected C.v() +private C.w() +*///:~ diff --git a/src/typeinfo/BoundedClassReferences.java b/src/typeinfo/BoundedClassReferences.java new file mode 100644 index 0000000..f0adee0 --- /dev/null +++ b/src/typeinfo/BoundedClassReferences.java @@ -0,0 +1,10 @@ +package typeinfo;//: typeinfo/BoundedClassReferences.java + +public class BoundedClassReferences { + public static void main(String[] args) { + Class bounded = int.class; + bounded = double.class; + bounded = Number.class; + // Or anything else derived from Number. + } +} ///:~ diff --git a/src/typeinfo/ClassCasts.java b/src/typeinfo/ClassCasts.java new file mode 100644 index 0000000..91df895 --- /dev/null +++ b/src/typeinfo/ClassCasts.java @@ -0,0 +1,13 @@ +package typeinfo;//: typeinfo/ClassCasts.java + +class Building {} +class House extends Building {} + +public class ClassCasts { + public static void main(String[] args) { + Building b = new House(); + Class houseType = House.class; + House h = houseType.cast(b); + h = (House)b; // ... or just do this. + } +} ///:~ diff --git a/src/typeinfo/ClassInitialization.java b/src/typeinfo/ClassInitialization.java new file mode 100644 index 0000000..a9de5f2 --- /dev/null +++ b/src/typeinfo/ClassInitialization.java @@ -0,0 +1,52 @@ +package typeinfo;//: typeinfo/ClassInitialization.java +import java.util.*; + +class Initable { + static final int staticFinal = 47; + static final int staticFinal2 = + ClassInitialization.rand.nextInt(1000); + static { + System.out.println("Initializing Initable"); + } +} + +class Initable2 { + static int staticNonFinal = 147; + static { + System.out.println("Initializing Initable2"); + } +} + +class Initable3 { + static int staticNonFinal = 74; + static { + System.out.println("Initializing Initable3"); + } +} + +public class ClassInitialization { + public static Random rand = new Random(47); + public static void main(String[] args) throws Exception { + Class initable = Initable.class; + System.out.println("After creating Initable ref"); + // Does not trigger initialization: + System.out.println(Initable.staticFinal); + // Does trigger initialization: + System.out.println(Initable.staticFinal2); + // Does trigger initialization: + System.out.println(Initable2.staticNonFinal); + Class initable3 = Class.forName("Initable3"); + System.out.println("After creating Initable3 ref"); + System.out.println(Initable3.staticNonFinal); + } +} /* Output: +After creating Initable ref +47 +Initializing Initable +258 +Initializing Initable2 +147 +Initializing Initable3 +After creating Initable3 ref +74 +*///:~ diff --git a/src/typeinfo/FamilyVsExactType.java b/src/typeinfo/FamilyVsExactType.java new file mode 100644 index 0000000..1cbe2e1 --- /dev/null +++ b/src/typeinfo/FamilyVsExactType.java @@ -0,0 +1,49 @@ +//: typeinfo/FamilyVsExactType.java +// The difference between instanceof and class +package typeinfo; +import static net.mindview.util.Print.*; + +class Base {} +class Derived extends Base {} + +public class FamilyVsExactType { + static void test(Object x) { + print("Testing x of type " + x.getClass()); + print("x instanceof Base " + (x instanceof Base)); + print("x instanceof Derived "+ (x instanceof Derived)); + print("Base.isInstance(x) "+ Base.class.isInstance(x)); + print("Derived.isInstance(x) " + + Derived.class.isInstance(x)); + print("x.getClass() == Base.class " + + (x.getClass() == Base.class)); + print("x.getClass() == Derived.class " + + (x.getClass() == Derived.class)); + print("x.getClass().equals(Base.class)) "+ + (x.getClass().equals(Base.class))); + print("x.getClass().equals(Derived.class)) " + + (x.getClass().equals(Derived.class))); + } + public static void main(String[] args) { + test(new Base()); + test(new Derived()); + } +} /* Output: +Testing x of type class typeinfo.Base +x instanceof Base true +x instanceof Derived false +Base.isInstance(x) true +Derived.isInstance(x) false +x.getClass() == Base.class true +x.getClass() == Derived.class false +x.getClass().equals(Base.class)) true +x.getClass().equals(Derived.class)) false +Testing x of type class typeinfo.Derived +x instanceof Base true +x instanceof Derived true +Base.isInstance(x) true +Derived.isInstance(x) true +x.getClass() == Base.class false +x.getClass() == Derived.class true +x.getClass().equals(Base.class)) false +x.getClass().equals(Derived.class)) true +*///:~ diff --git a/src/typeinfo/FilledList.java b/src/typeinfo/FilledList.java new file mode 100644 index 0000000..1d026cf --- /dev/null +++ b/src/typeinfo/FilledList.java @@ -0,0 +1,31 @@ +package typeinfo;//: typeinfo/FilledList.java +import java.util.*; + +class CountedInteger { + private static long counter; + private final long id = counter++; + public String toString() { return Long.toString(id); } +} + +public class FilledList { + private Class type; + public FilledList(Class type) { this.type = type; } + public List create(int nElements) { + List result = new ArrayList(); + try { + for(int i = 0; i < nElements; i++) { + result.add(type.newInstance()); + } + } catch(Exception e) { + throw new RuntimeException(e); + } + return result; + } + public static void main(String[] args) { + FilledList fl = + new FilledList(CountedInteger.class); + System.out.println(fl.create(15)); + } +} /* Output: +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] +*///:~ diff --git a/src/typeinfo/GenericClassReferences.java b/src/typeinfo/GenericClassReferences.java new file mode 100644 index 0000000..54772e6 --- /dev/null +++ b/src/typeinfo/GenericClassReferences.java @@ -0,0 +1,11 @@ +package typeinfo;//: typeinfo/GenericClassReferences.java + +public class GenericClassReferences { + public static void main(String[] args) { + Class intClass = int.class; + Class genericIntClass = int.class; + genericIntClass = Integer.class; // Same thing + intClass = double.class; + // genericIntClass = double.class; // Illegal + } +} ///:~ diff --git a/src/typeinfo/HiddenImplementation.java b/src/typeinfo/HiddenImplementation.java new file mode 100644 index 0000000..35f4ecf --- /dev/null +++ b/src/typeinfo/HiddenImplementation.java @@ -0,0 +1,37 @@ +package typeinfo;//: typeinfo/HiddenImplementation.java +// Sneaking around package access. +import typeinfo.interfacea.*; +import typeinfo.packageaccess.*; +import java.lang.reflect.*; + +public class HiddenImplementation { + public static void main(String[] args) throws Exception { + A a = HiddenC.makeA(); + a.f(); + System.out.println(a.getClass().getName()); + // Compile error: cannot find symbol 'C': + /* if(a instanceof C) { + C c = (C)a; + c.g(); + } */ + // Oops! Reflection still allows us to call g(): + callHiddenMethod(a, "g"); + // And even methods that are less accessible! + callHiddenMethod(a, "u"); + callHiddenMethod(a, "v"); + callHiddenMethod(a, "w"); + } + static void callHiddenMethod(Object a, String methodName) + throws Exception { + Method g = a.getClass().getDeclaredMethod(methodName); + g.setAccessible(true); + g.invoke(a); + } +} /* Output: +public C.f() +typeinfo.packageaccess.C +public C.g() +package C.u() +protected C.v() +private C.w() +*///:~ diff --git a/src/typeinfo/InnerImplementation.java b/src/typeinfo/InnerImplementation.java new file mode 100644 index 0000000..f978f58 --- /dev/null +++ b/src/typeinfo/InnerImplementation.java @@ -0,0 +1,35 @@ +package typeinfo;//: typeinfo/InnerImplementation.java +// Private inner classes can't hide from reflection. +import typeinfo.interfacea.*; +import static net.mindview.util.Print.*; + +class InnerA { + private static class C implements A { + public void f() { print("public C.f()"); } + public void g() { print("public C.g()"); } + void u() { print("package C.u()"); } + protected void v() { print("protected C.v()"); } + private void w() { print("private C.w()"); } + } + public static A makeA() { return new C(); } +} + +public class InnerImplementation { + public static void main(String[] args) throws Exception { + A a = InnerA.makeA(); + a.f(); + System.out.println(a.getClass().getName()); + // Reflection still gets into the private class: + HiddenImplementation.callHiddenMethod(a, "g"); + HiddenImplementation.callHiddenMethod(a, "u"); + HiddenImplementation.callHiddenMethod(a, "v"); + HiddenImplementation.callHiddenMethod(a, "w"); + } +} /* Output: +public C.f() +InnerA$C +public C.g() +package C.u() +protected C.v() +private C.w() +*///:~ diff --git a/src/typeinfo/InterfaceViolation.java b/src/typeinfo/InterfaceViolation.java new file mode 100644 index 0000000..d5163a0 --- /dev/null +++ b/src/typeinfo/InterfaceViolation.java @@ -0,0 +1,23 @@ +package typeinfo;//: typeinfo/InterfaceViolation.java +// Sneaking around an interface. +import typeinfo.interfacea.*; + +class B implements A { + public void f() {} + public void g() {} +} + +public class InterfaceViolation { + public static void main(String[] args) { + A a = new B(); + a.f(); + // a.g(); // Compile error + System.out.println(a.getClass().getName()); + if(a instanceof B) { + B b = (B)a; + b.g(); + } + } +} /* Output: +B +*///:~ diff --git a/src/typeinfo/ModifyingPrivateFields.java b/src/typeinfo/ModifyingPrivateFields.java new file mode 100644 index 0000000..cfc1fc7 --- /dev/null +++ b/src/typeinfo/ModifyingPrivateFields.java @@ -0,0 +1,41 @@ +package typeinfo;//: typeinfo/ModifyingPrivateFields.java +import java.lang.reflect.*; + +class WithPrivateFinalField { + private int i = 1; + private final String s = "I'm totally safe"; + private String s2 = "Am I safe?"; + public String toString() { + return "i = " + i + ", " + s + ", " + s2; + } +} + +public class ModifyingPrivateFields { + public static void main(String[] args) throws Exception { + WithPrivateFinalField pf = new WithPrivateFinalField(); + System.out.println(pf); + Field f = pf.getClass().getDeclaredField("i"); + f.setAccessible(true); + System.out.println("f.getInt(pf): " + f.getInt(pf)); + f.setInt(pf, 47); + System.out.println(pf); + f = pf.getClass().getDeclaredField("s"); + f.setAccessible(true); + System.out.println("f.get(pf): " + f.get(pf)); + f.set(pf, "No, you're not!"); + System.out.println(pf); + f = pf.getClass().getDeclaredField("s2"); + f.setAccessible(true); + System.out.println("f.get(pf): " + f.get(pf)); + f.set(pf, "No, you're not!"); + System.out.println(pf); + } +} /* Output: +i = 1, I'm totally safe, Am I safe? +f.getInt(pf): 1 +i = 47, I'm totally safe, Am I safe? +f.get(pf): I'm totally safe +i = 47, I'm totally safe, Am I safe? +f.get(pf): Am I safe? +i = 47, I'm totally safe, No, you're not! +*///:~ diff --git a/src/typeinfo/NullRobot.java b/src/typeinfo/NullRobot.java new file mode 100644 index 0000000..23d2bd0 --- /dev/null +++ b/src/typeinfo/NullRobot.java @@ -0,0 +1,56 @@ +package typeinfo;//: typeinfo/NullRobot.java +// Using a dynamic proxy to create a Null Object. +import java.lang.reflect.*; +import java.util.*; +import net.mindview.util.*; + +class NullRobotProxyHandler implements InvocationHandler { + private String nullName; + private Robot proxied = new NRobot(); + NullRobotProxyHandler(Class type) { + nullName = type.getSimpleName() + " NullRobot"; + } + private class NRobot implements Null, Robot { + public String name() { return nullName; } + public String model() { return nullName; } + public List operations() { + return Collections.emptyList(); + } + } + public Object + invoke(Object proxy, Method method, Object[] args) + throws Throwable { + return method.invoke(proxied, args); + } +} + +public class NullRobot { + public static Robot + newNullRobot(Class type) { + return (Robot)Proxy.newProxyInstance( + NullRobot.class.getClassLoader(), + new Class[]{ Null.class, Robot.class }, + new NullRobotProxyHandler(type)); + } + public static void main(String[] args) { + Robot[] bots = { + new SnowRemovalRobot("SnowBee"), + newNullRobot(SnowRemovalRobot.class) + }; + for(Robot bot : bots) { + Robot.Test.test(bot); + } + } +} /* Output: +Robot name: SnowBee +Robot model: SnowBot Series 11 +SnowBee can shovel snow +SnowBee shoveling snow +SnowBee can chip ice +SnowBee chipping ice +SnowBee can clear the roof +SnowBee clearing roof +[Null Robot] +Robot name: SnowRemovalRobot NullRobot +Robot model: SnowRemovalRobot NullRobot +*///:~ diff --git a/src/typeinfo/Operation.java b/src/typeinfo/Operation.java new file mode 100644 index 0000000..b39f899 --- /dev/null +++ b/src/typeinfo/Operation.java @@ -0,0 +1,6 @@ +package typeinfo;//: typeinfo/Operation.java + +public interface Operation { + String description(); + void command(); +} ///:~ diff --git a/src/typeinfo/Person.java b/src/typeinfo/Person.java new file mode 100644 index 0000000..52816c5 --- /dev/null +++ b/src/typeinfo/Person.java @@ -0,0 +1,24 @@ +package typeinfo;//: typeinfo/Person.java +// A class with a Null Object. +import net.mindview.util.*; + +class Person { + public final String first; + public final String last; + public final String address; + // etc. + public Person(String first, String last, String address){ + this.first = first; + this.last = last; + this.address = address; + } + public String toString() { + return "Person: " + first + " " + last + " " + address; + } + public static class NullPerson + extends Person implements Null { + private NullPerson() { super("None", "None", "None"); } + public String toString() { return "NullPerson"; } + } + public static final Person NULL = new NullPerson(); +} ///:~ diff --git a/src/typeinfo/PetCount.java b/src/typeinfo/PetCount.java new file mode 100644 index 0000000..b19ad16 --- /dev/null +++ b/src/typeinfo/PetCount.java @@ -0,0 +1,71 @@ +package typeinfo;//: typeinfo/PetCount.java +// Using instanceof. +import typeinfo.pets.*; +import java.util.*; +import static net.mindview.util.Print.*; + +public class PetCount { + static class PetCounter extends HashMap { + public void count(String type) { + Integer quantity = get(type); + if(quantity == null) { + put(type, 1); + } else { + put(type, quantity + 1); + } + } + } + public static void + countPets(PetCreator creator) { + PetCounter counter= new PetCounter(); + for(Pet pet : creator.createArray(20)) { + // List each individual pet: + printnb(pet.getClass().getSimpleName() + " "); + if(pet instanceof Pet) { + counter.count("Pet"); + } + if(pet instanceof Dog) { + counter.count("Dog"); + } + if(pet instanceof Mutt) { + counter.count("Mutt"); + } + if(pet instanceof Pug) { + counter.count("Pug"); + } + if(pet instanceof Cat) { + counter.count("Cat"); + } + if(pet instanceof Manx) { + counter.count("EgyptianMau"); + } + if(pet instanceof Manx) { + counter.count("Manx"); + } + if(pet instanceof Manx) { + counter.count("Cymric"); + } + if(pet instanceof Rodent) { + counter.count("Rodent"); + } + if(pet instanceof Rat) { + counter.count("Rat"); + } + if(pet instanceof Mouse) { + counter.count("Mouse"); + } + if(pet instanceof Hamster) { + counter.count("Hamster"); + } + } + // Show the counts: + print(); + print(counter); + } + public static void main(String[] args) { + countPets(new ForNameCreator()); + } +} /* Output: +Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat EgyptianMau Hamster EgyptianMau Mutt Mutt Cymric Mouse Pug Mouse Cymric +{Pug=3, Cat=9, Hamster=1, Cymric=7, Mouse=2, Mutt=3, Rodent=5, Pet=20, Manx=7, EgyptianMau=7, Dog=6, Rat=2} +*///:~ diff --git a/src/typeinfo/PetCount2.java b/src/typeinfo/PetCount2.java new file mode 100644 index 0000000..4a6f2b7 --- /dev/null +++ b/src/typeinfo/PetCount2.java @@ -0,0 +1,8 @@ +package typeinfo;//: typeinfo/PetCount2.java +import typeinfo.pets.*; + +public class PetCount2 { + public static void main(String[] args) { + PetCount.countPets(Pets.creator); + } +} /* (Execute to see output) *///:~ diff --git a/src/typeinfo/PetCount3.java b/src/typeinfo/PetCount3.java new file mode 100644 index 0000000..f331659 --- /dev/null +++ b/src/typeinfo/PetCount3.java @@ -0,0 +1,49 @@ +package typeinfo;//: typeinfo/PetCount3.java +// Using isInstance() +import typeinfo.pets.*; +import java.util.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class PetCount3 { + static class PetCounter + extends LinkedHashMap,Integer> { + public PetCounter() { + super(MapData.map(LiteralPetCreator.allTypes, 0)); + } + public void count(Pet pet) { + // Class.isInstance() eliminates instanceofs: + for(Map.Entry,Integer> pair + : entrySet()) { + if(pair.getKey().isInstance(pet)) { + put(pair.getKey(), pair.getValue() + 1); + } + } + } + public String toString() { + StringBuilder result = new StringBuilder("{"); + for(Map.Entry,Integer> pair + : entrySet()) { + result.append(pair.getKey().getSimpleName()); + result.append("="); + result.append(pair.getValue()); + result.append(", "); + } + result.delete(result.length()-2, result.length()); + result.append("}"); + return result.toString(); + } + } + public static void main(String[] args) { + PetCounter petCount = new PetCounter(); + for(Pet pet : Pets.createArray(20)) { + printnb(pet.getClass().getSimpleName() + " "); + petCount.count(pet); + } + print(); + print(petCount); + } +} /* Output: +Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat EgyptianMau Hamster EgyptianMau Mutt Mutt Cymric Mouse Pug Mouse Cymric +{Pet=20, Dog=6, Cat=9, Rodent=5, Mutt=3, Pug=3, EgyptianMau=2, Manx=7, Cymric=5, Rat=2, Mouse=2, Hamster=1} +*///:~ diff --git a/src/typeinfo/PetCount4.java b/src/typeinfo/PetCount4.java new file mode 100644 index 0000000..4ac44e3 --- /dev/null +++ b/src/typeinfo/PetCount4.java @@ -0,0 +1,19 @@ +package typeinfo;//: typeinfo/PetCount4.java +import typeinfo.pets.*; +import net.mindview.util.*; +import static net.mindview.util.Print.*; + +public class PetCount4 { + public static void main(String[] args) { + TypeCounter counter = new TypeCounter(Pet.class); + for(Pet pet : Pets.createArray(20)) { + printnb(pet.getClass().getSimpleName() + " "); + counter.count(pet); + } + print(); + print(counter); + } +} /* Output: (Sample) +Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat EgyptianMau Hamster EgyptianMau Mutt Mutt Cymric Mouse Pug Mouse Cymric +{Mouse=2, Dog=6, Manx=7, EgyptianMau=2, Rodent=5, Pug=3, Mutt=3, Cymric=5, Cat=9, Hamster=1, Pet=20, Rat=2} +*///:~ diff --git a/src/typeinfo/Position.java b/src/typeinfo/Position.java new file mode 100644 index 0000000..4ea36b2 --- /dev/null +++ b/src/typeinfo/Position.java @@ -0,0 +1,31 @@ +package typeinfo;//: typeinfo/Position.java + +class Position { + private String title; + private Person person; + public Position(String jobTitle, Person employee) { + title = jobTitle; + person = employee; + if(person == null) { + person = Person.NULL; + } + } + public Position(String jobTitle) { + title = jobTitle; + person = Person.NULL; + } + public String getTitle() { return title; } + public void setTitle(String newTitle) { + title = newTitle; + } + public Person getPerson() { return person; } + public void setPerson(Person newPerson) { + person = newPerson; + if(person == null) { + person = Person.NULL; + } + } + public String toString() { + return "Position: " + title + " " + person; + } +} ///:~ diff --git a/src/typeinfo/RegisteredFactories.java b/src/typeinfo/RegisteredFactories.java new file mode 100644 index 0000000..536987b --- /dev/null +++ b/src/typeinfo/RegisteredFactories.java @@ -0,0 +1,107 @@ +package typeinfo;//: typeinfo/RegisteredFactories.java +// Registering Class Factories in the base class. +import typeinfo.factory.*; +import java.util.*; + +class Part { + public String toString() { + return getClass().getSimpleName(); + } + static List> partFactories = + new ArrayList>(); + static { + // Collections.addAll() gives an "unchecked generic + // array creation ... for varargs parameter" warning. + partFactories.add(new FuelFilter.Factory()); + partFactories.add(new AirFilter.Factory()); + partFactories.add(new CabinAirFilter.Factory()); + partFactories.add(new OilFilter.Factory()); + partFactories.add(new FanBelt.Factory()); + partFactories.add(new PowerSteeringBelt.Factory()); + partFactories.add(new GeneratorBelt.Factory()); + } + private static Random rand = new Random(47); + public static Part createRandom() { + int n = rand.nextInt(partFactories.size()); + return partFactories.get(n).create(); + } +} + +class Filter extends Part {} + +class FuelFilter extends Filter { + // Create a Class Factory for each specific type: + public static class Factory + implements typeinfo.factory.Factory { + public FuelFilter create() { return new FuelFilter(); } + } +} + +class AirFilter extends Filter { + public static class Factory + implements typeinfo.factory.Factory { + public AirFilter create() { return new AirFilter(); } + } +} + +class CabinAirFilter extends Filter { + public static class Factory + implements typeinfo.factory.Factory { + public CabinAirFilter create() { + return new CabinAirFilter(); + } + } +} + +class OilFilter extends Filter { + public static class Factory + implements typeinfo.factory.Factory { + public OilFilter create() { return new OilFilter(); } + } +} + +class Belt extends Part {} + +class FanBelt extends Belt { + public static class Factory + implements typeinfo.factory.Factory { + public FanBelt create() { return new FanBelt(); } + } +} + +class GeneratorBelt extends Belt { + public static class Factory + implements typeinfo.factory.Factory { + public GeneratorBelt create() { + return new GeneratorBelt(); + } + } +} + +class PowerSteeringBelt extends Belt { + public static class Factory + implements typeinfo.factory.Factory { + public PowerSteeringBelt create() { + return new PowerSteeringBelt(); + } + } +} + +public class RegisteredFactories { + public static void main(String[] args) { + for(int i = 0; i < 10; i++) { + System.out.println(Part.createRandom()); + } + } +} /* Output: +GeneratorBelt +CabinAirFilter +GeneratorBelt +AirFilter +PowerSteeringBelt +CabinAirFilter +FuelFilter +PowerSteeringBelt +PowerSteeringBelt +FuelFilter +*///:~ diff --git a/src/typeinfo/Robot.java b/src/typeinfo/Robot.java new file mode 100644 index 0000000..aca8946 --- /dev/null +++ b/src/typeinfo/Robot.java @@ -0,0 +1,22 @@ +package typeinfo;//: typeinfo/Robot.java +import java.util.*; +import net.mindview.util.*; + +public interface Robot { + String name(); + String model(); + List operations(); + class Test { + public static void test(Robot r) { + if(r instanceof Null) { + System.out.println("[Null Robot]"); + } + System.out.println("Robot name: " + r.name()); + System.out.println("Robot model: " + r.model()); + for(Operation operation : r.operations()) { + System.out.println(operation.description()); + operation.command(); + } + } + } +} ///:~ diff --git a/src/typeinfo/SelectingMethods.java b/src/typeinfo/SelectingMethods.java new file mode 100644 index 0000000..39fd5e1 --- /dev/null +++ b/src/typeinfo/SelectingMethods.java @@ -0,0 +1,54 @@ +package typeinfo;//: typeinfo/SelectingMethods.java +// Looking for particular methods in a dynamic proxy. +import java.lang.reflect.*; +import static net.mindview.util.Print.*; + +class MethodSelector implements InvocationHandler { + private Object proxied; + public MethodSelector(Object proxied) { + this.proxied = proxied; + } + public Object + invoke(Object proxy, Method method, Object[] args) + throws Throwable { + if(method.getName().equals("interesting")) { + print("Proxy detected the interesting method"); + } + return method.invoke(proxied, args); + } +} + +interface SomeMethods { + void boring1(); + void boring2(); + void interesting(String arg); + void boring3(); +} + +class Implementation implements SomeMethods { + public void boring1() { print("boring1"); } + public void boring2() { print("boring2"); } + public void interesting(String arg) { + print("interesting " + arg); + } + public void boring3() { print("boring3"); } +} + +class SelectingMethods { + public static void main(String[] args) { + SomeMethods proxy= (SomeMethods)Proxy.newProxyInstance( + SomeMethods.class.getClassLoader(), + new Class[]{ SomeMethods.class }, + new MethodSelector(new Implementation())); + proxy.boring1(); + proxy.boring2(); + proxy.interesting("bonobo"); + proxy.boring3(); + } +} /* Output: +boring1 +boring2 +Proxy detected the interesting method +interesting bonobo +boring3 +*///:~ diff --git a/src/typeinfo/Shapes.java b/src/typeinfo/Shapes.java new file mode 100644 index 0000000..d64365a --- /dev/null +++ b/src/typeinfo/Shapes.java @@ -0,0 +1,34 @@ +package typeinfo;//: typeinfo/Shapes.java +import java.util.*; + +abstract class Shape { + void draw() { System.out.println(this + ".draw()"); } + abstract public String toString(); +} + +class Circle extends Shape { + public String toString() { return "Circle"; } +} + +class Square extends Shape { + public String toString() { return "Square"; } +} + +class Triangle extends Shape { + public String toString() { return "Triangle"; } +} + +public class Shapes { + public static void main(String[] args) { + List shapeList = Arrays.asList( + new Circle(), new Square(), new Triangle() + ); + for(Shape shape : shapeList) { + shape.draw(); + } + } +} /* Output: +Circle.draw() +Square.draw() +Triangle.draw() +*///:~ diff --git a/src/typeinfo/ShowMethods.java b/src/typeinfo/ShowMethods.java new file mode 100644 index 0000000..78e0161 --- /dev/null +++ b/src/typeinfo/ShowMethods.java @@ -0,0 +1,68 @@ +package typeinfo;//: typeinfo/ShowMethods.java +// Using reflection to show all the methods of a class, +// even if the methods are defined in the base class. +// {Args: ShowMethods} +import java.lang.reflect.*; +import java.util.regex.*; +import static net.mindview.util.Print.*; + +public class ShowMethods { + private static String usage = + "usage:\n" + + "ShowMethods qualified.class.name\n" + + "To show all methods in class or:\n" + + "ShowMethods qualified.class.name word\n" + + "To search for methods involving 'word'"; + private static Pattern p = Pattern.compile("\\w+\\."); + public static void main(String[] args) { + if(args.length < 1) { + print(usage); + System.exit(0); + } + int lines = 0; + try { + Class c = Class.forName(args[0]); + Method[] methods = c.getMethods(); + Constructor[] ctors = c.getConstructors(); + if(args.length == 1) { + for(Method method : methods) { + print( + p.matcher(method.toString()).replaceAll("")); + } + for(Constructor ctor : ctors) { + print(p.matcher(ctor.toString()).replaceAll("")); + } + lines = methods.length + ctors.length; + } else { + for(Method method : methods) { + if(method.toString().indexOf(args[1]) != -1) { + print( + p.matcher(method.toString()).replaceAll("")); + lines++; + } + } + for(Constructor ctor : ctors) { + if(ctor.toString().indexOf(args[1]) != -1) { + print(p.matcher( + ctor.toString()).replaceAll("")); + lines++; + } + } + } + } catch(ClassNotFoundException e) { + print("No such class: " + e); + } + } +} /* Output: +public static void main(String[]) +public native int hashCode() +public final native Class getClass() +public final void wait(long,int) throws InterruptedException +public final void wait() throws InterruptedException +public final native void wait(long) throws InterruptedException +public boolean equals(Object) +public String toString() +public final native void notify() +public final native void notifyAll() +public ShowMethods() +*///:~ diff --git a/src/typeinfo/SimpleDynamicProxy.java b/src/typeinfo/SimpleDynamicProxy.java new file mode 100644 index 0000000..5f58bee --- /dev/null +++ b/src/typeinfo/SimpleDynamicProxy.java @@ -0,0 +1,46 @@ +package typeinfo;//: typeinfo/SimpleDynamicProxy.java +import java.lang.reflect.*; + +class DynamicProxyHandler implements InvocationHandler { + private Object proxied; + public DynamicProxyHandler(Object proxied) { + this.proxied = proxied; + } + public Object + invoke(Object proxy, Method method, Object[] args) + throws Throwable { + System.out.println("**** proxy: " + proxy.getClass() + + ", method: " + method + ", args: " + args); + if(args != null) { + for(Object arg : args) { + System.out.println(" " + arg); + } + } + return method.invoke(proxied, args); + } +} + +class SimpleDynamicProxy { + public static void consumer(Interface iface) { + iface.doSomething(); + iface.somethingElse("bonobo"); + } + public static void main(String[] args) { + RealObject real = new RealObject(); + consumer(real); + // Insert a proxy and call again: + Interface proxy = (Interface)Proxy.newProxyInstance( + Interface.class.getClassLoader(), + new Class[]{ Interface.class }, + new DynamicProxyHandler(real)); + consumer(proxy); + } +} /* Output: (95% match) +doSomething +somethingElse bonobo +**** proxy: class $Proxy0, method: public abstract void Interface.doSomething(), args: null +doSomething +**** proxy: class $Proxy0, method: public abstract void Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@42e816 + bonobo +somethingElse bonobo +*///:~ diff --git a/src/typeinfo/SimpleProxyDemo.java b/src/typeinfo/SimpleProxyDemo.java new file mode 100644 index 0000000..bee7101 --- /dev/null +++ b/src/typeinfo/SimpleProxyDemo.java @@ -0,0 +1,47 @@ +package typeinfo;//: typeinfo/SimpleProxyDemo.java +import static net.mindview.util.Print.*; + +interface Interface { + void doSomething(); + void somethingElse(String arg); +} + +class RealObject implements Interface { + public void doSomething() { print("doSomething"); } + public void somethingElse(String arg) { + print("somethingElse " + arg); + } +} + +class SimpleProxy implements Interface { + private Interface proxied; + public SimpleProxy(Interface proxied) { + this.proxied = proxied; + } + public void doSomething() { + print("SimpleProxy doSomething"); + proxied.doSomething(); + } + public void somethingElse(String arg) { + print("SimpleProxy somethingElse " + arg); + proxied.somethingElse(arg); + } +} + +class SimpleProxyDemo { + public static void consumer(Interface iface) { + iface.doSomething(); + iface.somethingElse("bonobo"); + } + public static void main(String[] args) { + consumer(new RealObject()); + consumer(new SimpleProxy(new RealObject())); + } +} /* Output: +doSomething +somethingElse bonobo +SimpleProxy doSomething +doSomething +SimpleProxy somethingElse bonobo +somethingElse bonobo +*///:~ diff --git a/src/typeinfo/SnowRemovalRobot.java b/src/typeinfo/SnowRemovalRobot.java new file mode 100644 index 0000000..83947b8 --- /dev/null +++ b/src/typeinfo/SnowRemovalRobot.java @@ -0,0 +1,49 @@ +package typeinfo;//: typeinfo/SnowRemovalRobot.java +import java.util.*; + +public class SnowRemovalRobot implements Robot { + private String name; + public SnowRemovalRobot(String name) {this.name = name;} + public String name() { return name; } + public String model() { return "SnowBot Series 11"; } + public List operations() { + return Arrays.asList( + new Operation() { + public String description() { + return name + " can shovel snow"; + } + public void command() { + System.out.println(name + " shoveling snow"); + } + }, + new Operation() { + public String description() { + return name + " can chip ice"; + } + public void command() { + System.out.println(name + " chipping ice"); + } + }, + new Operation() { + public String description() { + return name + " can clear the roof"; + } + public void command() { + System.out.println(name + " clearing roof"); + } + } + ); + } + public static void main(String[] args) { + Robot.Test.test(new SnowRemovalRobot("Slusher")); + } +} /* Output: +Robot name: Slusher +Robot model: SnowBot Series 11 +Slusher can shovel snow +Slusher shoveling snow +Slusher can chip ice +Slusher chipping ice +Slusher can clear the roof +Slusher clearing roof +*///:~ diff --git a/src/typeinfo/Staff.java b/src/typeinfo/Staff.java new file mode 100644 index 0000000..0922d66 --- /dev/null +++ b/src/typeinfo/Staff.java @@ -0,0 +1,53 @@ +package typeinfo;//: typeinfo/Staff.java +import java.util.*; + +public class Staff extends ArrayList { + public void add(String title, Person person) { + add(new Position(title, person)); + } + public void add(String... titles) { + for(String title : titles) { + add(new Position(title)); + } + } + public Staff(String... titles) { add(titles); } + public boolean positionAvailable(String title) { + for(Position position : this) { + if(position.getTitle().equals(title) && + position.getPerson() == Person.NULL) { + return true; + } + } + return false; + } + public void fillPosition(String title, Person hire) { + for(Position position : this) { + if(position.getTitle().equals(title) && + position.getPerson() == Person.NULL) { + position.setPerson(hire); + return; + } + } + throw new RuntimeException( + "Position " + title + " not available"); + } + public static void main(String[] args) { + Staff staff = new Staff("President", "CTO", + "Marketing Manager", "Product Manager", + "Project Lead", "Software Engineer", + "Software Engineer", "Software Engineer", + "Software Engineer", "Test Engineer", + "Technical Writer"); + staff.fillPosition("President", + new Person("Me", "Last", "The Top, Lonely At")); + staff.fillPosition("Project Lead", + new Person("Janet", "Planner", "The Burbs")); + if(staff.positionAvailable("Software Engineer")) { + staff.fillPosition("Software Engineer", + new Person("Bob", "Coder", "Bright Light City")); + } + System.out.println(staff); + } +} /* Output: +[Position: President Person: Me Last The Top, Lonely At, Position: CTO NullPerson, Position: Marketing Manager NullPerson, Position: Product Manager NullPerson, Position: Project Lead Person: Janet Planner The Burbs, Position: Software Engineer Person: Bob Coder Bright Light City, Position: Software Engineer NullPerson, Position: Software Engineer NullPerson, Position: Software Engineer NullPerson, Position: Test Engineer NullPerson, Position: Technical Writer NullPerson] +*///:~ diff --git a/src/typeinfo/SweetShop.java b/src/typeinfo/SweetShop.java new file mode 100644 index 0000000..d163c7e --- /dev/null +++ b/src/typeinfo/SweetShop.java @@ -0,0 +1,39 @@ +package typeinfo;//: typeinfo/SweetShop.java +// Examination of the way the class loader works. +import static net.mindview.util.Print.*; + +class Candy { + static { print("Loading Candy"); } +} + +class Gum { + static { print("Loading Gum"); } +} + +class Cookie { + static { print("Loading Cookie"); } +} + +public class SweetShop { + public static void main(String[] args) { + print("inside main"); + new Candy(); + print("After creating Candy"); + try { + Class.forName("Gum"); + } catch(ClassNotFoundException e) { + print("Couldn't find Gum"); + } + print("After Class.forName(\"Gum\")"); + new Cookie(); + print("After creating Cookie"); + } +} /* Output: +inside main +Loading Candy +After creating Candy +Loading Gum +After Class.forName("Gum") +Loading Cookie +After creating Cookie +*///:~ diff --git a/src/typeinfo/WildcardClassReferences.java b/src/typeinfo/WildcardClassReferences.java new file mode 100644 index 0000000..04dfc5f --- /dev/null +++ b/src/typeinfo/WildcardClassReferences.java @@ -0,0 +1,8 @@ +package typeinfo;//: typeinfo/WildcardClassReferences.java + +public class WildcardClassReferences { + public static void main(String[] args) { + Class intClass = int.class; + intClass = double.class; + } +} ///:~ diff --git a/src/typeinfo/build.xml b/src/typeinfo/build.xml new file mode 100644 index 0000000..bef1704 --- /dev/null +++ b/src/typeinfo/build.xml @@ -0,0 +1,371 @@ + + + + + + build.xml for the source code for the typeinfo chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/typeinfo/factory/Factory.java b/src/typeinfo/factory/Factory.java new file mode 100644 index 0000000..cfd268f --- /dev/null +++ b/src/typeinfo/factory/Factory.java @@ -0,0 +1,3 @@ +//: typeinfo/factory/Factory.java +package typeinfo.factory; +public interface Factory { T create(); } ///:~ diff --git a/src/typeinfo/interfacea/A.java b/src/typeinfo/interfacea/A.java new file mode 100644 index 0000000..d1eb6c6 --- /dev/null +++ b/src/typeinfo/interfacea/A.java @@ -0,0 +1,6 @@ +//: typeinfo/interfacea/A.java +package typeinfo.interfacea; + +public interface A { + void f(); +} ///:~ diff --git a/src/typeinfo/packageaccess/HiddenC.java b/src/typeinfo/packageaccess/HiddenC.java new file mode 100644 index 0000000..2505e50 --- /dev/null +++ b/src/typeinfo/packageaccess/HiddenC.java @@ -0,0 +1,16 @@ +//: typeinfo/packageaccess/HiddenC.java +package typeinfo.packageaccess; +import typeinfo.interfacea.*; +import static net.mindview.util.Print.*; + +class C implements A { + public void f() { print("public C.f()"); } + public void g() { print("public C.g()"); } + void u() { print("package C.u()"); } + protected void v() { print("protected C.v()"); } + private void w() { print("private C.w()"); } +} + +public class HiddenC { + public static A makeA() { return new C(); } +} ///:~ diff --git a/src/typeinfo/pets/Cat.java b/src/typeinfo/pets/Cat.java new file mode 100644 index 0000000..eba7fe4 --- /dev/null +++ b/src/typeinfo/pets/Cat.java @@ -0,0 +1,7 @@ +//: typeinfo/pets/Cat.java +package typeinfo.pets; + +public class Cat extends Pet { + public Cat(String name) { super(name); } + public Cat() { super(); } +} ///:~ diff --git a/src/typeinfo/pets/Cymric.java b/src/typeinfo/pets/Cymric.java new file mode 100644 index 0000000..60d5a95 --- /dev/null +++ b/src/typeinfo/pets/Cymric.java @@ -0,0 +1,7 @@ +//: typeinfo/pets/Cymric.java +package typeinfo.pets; + +public class Cymric extends Manx { + public Cymric(String name) { super(name); } + public Cymric() { super(); } +} ///:~ diff --git a/src/typeinfo/pets/Dog.java b/src/typeinfo/pets/Dog.java new file mode 100644 index 0000000..1cd2e44 --- /dev/null +++ b/src/typeinfo/pets/Dog.java @@ -0,0 +1,7 @@ +//: typeinfo/pets/Dog.java +package typeinfo.pets; + +public class Dog extends Pet { + public Dog(String name) { super(name); } + public Dog() { super(); } +} ///:~ diff --git a/src/typeinfo/pets/EgyptianMau.java b/src/typeinfo/pets/EgyptianMau.java new file mode 100644 index 0000000..68480d1 --- /dev/null +++ b/src/typeinfo/pets/EgyptianMau.java @@ -0,0 +1,7 @@ +//: typeinfo/pets/EgyptianMau.java +package typeinfo.pets; + +public class EgyptianMau extends Cat { + public EgyptianMau(String name) { super(name); } + public EgyptianMau() { super(); } +} ///:~ diff --git a/src/typeinfo/pets/ForNameCreator.java b/src/typeinfo/pets/ForNameCreator.java new file mode 100644 index 0000000..54d57ea --- /dev/null +++ b/src/typeinfo/pets/ForNameCreator.java @@ -0,0 +1,32 @@ +//: typeinfo/pets/ForNameCreator.java +package typeinfo.pets; +import java.util.*; + +public class ForNameCreator extends PetCreator { + private static List> types = + new ArrayList>(); + // Types that you want to be randomly created: + private static String[] typeNames = { + "typeinfo.pets.Mutt", + "typeinfo.pets.Pug", + "typeinfo.pets.EgyptianMau", + "typeinfo.pets.Manx", + "typeinfo.pets.Cymric", + "typeinfo.pets.Rat", + "typeinfo.pets.Mouse", + "typeinfo.pets.Hamster" + }; + @SuppressWarnings("unchecked") + private static void loader() { + try { + for(String name : typeNames) { + types.add( + (Class)Class.forName(name)); + } + } catch(ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + static { loader(); } + public List> types() {return types;} +} ///:~ diff --git a/src/typeinfo/pets/Hamster.java b/src/typeinfo/pets/Hamster.java new file mode 100644 index 0000000..a797677 --- /dev/null +++ b/src/typeinfo/pets/Hamster.java @@ -0,0 +1,7 @@ +//: typeinfo/pets/Hamster.java +package typeinfo.pets; + +public class Hamster extends Rodent { + public Hamster(String name) { super(name); } + public Hamster() { super(); } +} ///:~ diff --git a/src/typeinfo/pets/Individual.java b/src/typeinfo/pets/Individual.java new file mode 100644 index 0000000..8cf7727 --- /dev/null +++ b/src/typeinfo/pets/Individual.java @@ -0,0 +1,44 @@ +//: typeinfo/pets/Individual.java +package typeinfo.pets; + +public class Individual implements Comparable { + private static long counter = 0; + private final long id = counter++; + private String name; + public Individual(String name) { this.name = name; } + // 'name' is optional: + public Individual() {} + public String toString() { + return getClass().getSimpleName() + + (name == null ? "" : " " + name); + } + public long id() { return id; } + public boolean equals(Object o) { + return o instanceof Individual && + id == ((Individual)o).id; + } + public int hashCode() { + int result = 17; + if(name != null) { + result = 37 * result + name.hashCode(); + } + result = 37 * result + (int)id; + return result; + } + public int compareTo(Individual arg) { + // Compare by class name first: + String first = getClass().getSimpleName(); + String argFirst = arg.getClass().getSimpleName(); + int firstCompare = first.compareTo(argFirst); + if(firstCompare != 0) { + return firstCompare; + } + if(name != null && arg.name != null) { + int secondCompare = name.compareTo(arg.name); + if(secondCompare != 0) { + return secondCompare; + } + } + return (arg.id < id ? -1 : (arg.id == id ? 0 : 1)); + } +} ///:~ diff --git a/src/typeinfo/pets/LiteralPetCreator.java b/src/typeinfo/pets/LiteralPetCreator.java new file mode 100644 index 0000000..5b853e0 --- /dev/null +++ b/src/typeinfo/pets/LiteralPetCreator.java @@ -0,0 +1,26 @@ +//: typeinfo/pets/LiteralPetCreator.java +// Using class literals. +package typeinfo.pets; +import java.util.*; + +public class LiteralPetCreator extends PetCreator { + // No try block needed. + @SuppressWarnings("unchecked") + public static final List> allTypes = + Collections.unmodifiableList(Arrays.asList( + Pet.class, Dog.class, Cat.class, Rodent.class, + Mutt.class, Pug.class, EgyptianMau.class, Manx.class, + Cymric.class, Rat.class, Mouse.class,Hamster.class)); + // Types for random creation: + private static final List> types = + allTypes.subList(allTypes.indexOf(Mutt.class), + allTypes.size()); + public List> types() { + return types; + } + public static void main(String[] args) { + System.out.println(types); + } +} /* Output: +[class typeinfo.pets.Mutt, class typeinfo.pets.Pug, class typeinfo.pets.EgyptianMau, class typeinfo.pets.Manx, class typeinfo.pets.Cymric, class typeinfo.pets.Rat, class typeinfo.pets.Mouse, class typeinfo.pets.Hamster] +*///:~ diff --git a/src/typeinfo/pets/Manx.java b/src/typeinfo/pets/Manx.java new file mode 100644 index 0000000..939b11d --- /dev/null +++ b/src/typeinfo/pets/Manx.java @@ -0,0 +1,7 @@ +//: typeinfo/pets/Manx.java +package typeinfo.pets; + +public class Manx extends Cat { + public Manx(String name) { super(name); } + public Manx() { super(); } +} ///:~ diff --git a/src/typeinfo/pets/Mouse.java b/src/typeinfo/pets/Mouse.java new file mode 100644 index 0000000..d4795b5 --- /dev/null +++ b/src/typeinfo/pets/Mouse.java @@ -0,0 +1,7 @@ +//: typeinfo/pets/Mouse.java +package typeinfo.pets; + +public class Mouse extends Rodent { + public Mouse(String name) { super(name); } + public Mouse() { super(); } +} ///:~ diff --git a/src/typeinfo/pets/Mutt.java b/src/typeinfo/pets/Mutt.java new file mode 100644 index 0000000..4646967 --- /dev/null +++ b/src/typeinfo/pets/Mutt.java @@ -0,0 +1,7 @@ +//: typeinfo/pets/Mutt.java +package typeinfo.pets; + +public class Mutt extends Dog { + public Mutt(String name) { super(name); } + public Mutt() { super(); } +} ///:~ diff --git a/src/typeinfo/pets/Person.java b/src/typeinfo/pets/Person.java new file mode 100644 index 0000000..83ac004 --- /dev/null +++ b/src/typeinfo/pets/Person.java @@ -0,0 +1,6 @@ +//: typeinfo/pets/Person.java +package typeinfo.pets; + +public class Person extends Individual { + public Person(String name) { super(name); } +} ///:~ diff --git a/src/typeinfo/pets/Pet.java b/src/typeinfo/pets/Pet.java new file mode 100644 index 0000000..fb390c9 --- /dev/null +++ b/src/typeinfo/pets/Pet.java @@ -0,0 +1,7 @@ +//: typeinfo/pets/Pet.java +package typeinfo.pets; + +public class Pet extends Individual { + public Pet(String name) { super(name); } + public Pet() { super(); } +} ///:~ diff --git a/src/typeinfo/pets/PetCreator.java b/src/typeinfo/pets/PetCreator.java new file mode 100644 index 0000000..a62ccd9 --- /dev/null +++ b/src/typeinfo/pets/PetCreator.java @@ -0,0 +1,32 @@ +//: typeinfo/pets/PetCreator.java +// Creates random sequences of Pets. +package typeinfo.pets; +import java.util.*; + +public abstract class PetCreator { + private Random rand = new Random(47); + // The List of the different types of Pet to create: + public abstract List> types(); + public Pet randomPet() { // Create one random Pet + int n = rand.nextInt(types().size()); + try { + return types().get(n).newInstance(); + } catch(InstantiationException e) { + throw new RuntimeException(e); + } catch(IllegalAccessException e) { + throw new RuntimeException(e); + } + } + public Pet[] createArray(int size) { + Pet[] result = new Pet[size]; + for(int i = 0; i < size; i++) { + result[i] = randomPet(); + } + return result; + } + public ArrayList arrayList(int size) { + ArrayList result = new ArrayList(); + Collections.addAll(result, createArray(size)); + return result; + } +} ///:~ diff --git a/src/typeinfo/pets/Pets.java b/src/typeinfo/pets/Pets.java new file mode 100644 index 0000000..bedf658 --- /dev/null +++ b/src/typeinfo/pets/Pets.java @@ -0,0 +1,18 @@ +//: typeinfo/pets/Pets.java +// Facade to produce a default PetCreator. +package typeinfo.pets; +import java.util.*; + +public class Pets { + public static final PetCreator creator = + new LiteralPetCreator(); + public static Pet randomPet() { + return creator.randomPet(); + } + public static Pet[] createArray(int size) { + return creator.createArray(size); + } + public static ArrayList arrayList(int size) { + return creator.arrayList(size); + } +} ///:~ diff --git a/src/typeinfo/pets/Pug.java b/src/typeinfo/pets/Pug.java new file mode 100644 index 0000000..853b969 --- /dev/null +++ b/src/typeinfo/pets/Pug.java @@ -0,0 +1,7 @@ +//: typeinfo/pets/Pug.java +package typeinfo.pets; + +public class Pug extends Dog { + public Pug(String name) { super(name); } + public Pug() { super(); } +} ///:~ diff --git a/src/typeinfo/pets/Rat.java b/src/typeinfo/pets/Rat.java new file mode 100644 index 0000000..7e89c14 --- /dev/null +++ b/src/typeinfo/pets/Rat.java @@ -0,0 +1,7 @@ +//: typeinfo/pets/Rat.java +package typeinfo.pets; + +public class Rat extends Rodent { + public Rat(String name) { super(name); } + public Rat() { super(); } +} ///:~ diff --git a/src/typeinfo/pets/Rodent.java b/src/typeinfo/pets/Rodent.java new file mode 100644 index 0000000..6ed2c14 --- /dev/null +++ b/src/typeinfo/pets/Rodent.java @@ -0,0 +1,7 @@ +//: typeinfo/pets/Rodent.java +package typeinfo.pets; + +public class Rodent extends Pet { + public Rodent(String name) { super(name); } + public Rodent() { super(); } +} ///:~ diff --git a/src/typeinfo/toys/GenericToyTest.java b/src/typeinfo/toys/GenericToyTest.java new file mode 100644 index 0000000..a07425a --- /dev/null +++ b/src/typeinfo/toys/GenericToyTest.java @@ -0,0 +1,16 @@ +//: typeinfo/toys/GenericToyTest.java +// Testing class Class. +package typeinfo.toys; + +public class GenericToyTest { + public static void main(String[] args) throws Exception { + Class ftClass = FancyToy.class; + // Produces exact type: + FancyToy fancyToy = ftClass.newInstance(); + Class up = ftClass.getSuperclass(); + // This won't compile: + // Class up2 = ftClass.getSuperclass(); + // Only produces Object: + Object obj = up.newInstance(); + } +} ///:~ diff --git a/src/typeinfo/toys/ToyTest.java b/src/typeinfo/toys/ToyTest.java new file mode 100644 index 0000000..d52ab47 --- /dev/null +++ b/src/typeinfo/toys/ToyTest.java @@ -0,0 +1,71 @@ +//: typeinfo/toys/ToyTest.java +// Testing class Class. +package typeinfo.toys; +import static net.mindview.util.Print.*; + +interface HasBatteries {} +interface Waterproof {} +interface Shoots {} + +class Toy { + // Comment out the following default constructor + // to see NoSuchMethodError from (*1*) + Toy() {} + Toy(int i) {} +} + +class FancyToy extends Toy +implements HasBatteries, Waterproof, Shoots { + FancyToy() { super(1); } +} + +public class ToyTest { + static void printInfo(Class cc) { + print("Class name: " + cc.getName() + + " is interface? [" + cc.isInterface() + "]"); + print("Simple name: " + cc.getSimpleName()); + print("Canonical name : " + cc.getCanonicalName()); + } + public static void main(String[] args) { + Class c = null; + try { + c = Class.forName("typeinfo.toys.FancyToy"); + } catch(ClassNotFoundException e) { + print("Can't find FancyToy"); + System.exit(1); + } + printInfo(c); + for(Class face : c.getInterfaces()) { + printInfo(face); + } + Class up = c.getSuperclass(); + Object obj = null; + try { + // Requires default constructor: + obj = up.newInstance(); + } catch(InstantiationException e) { + print("Cannot instantiate"); + System.exit(1); + } catch(IllegalAccessException e) { + print("Cannot access"); + System.exit(1); + } + printInfo(obj.getClass()); + } +} /* Output: +Class name: typeinfo.toys.FancyToy is interface? [false] +Simple name: FancyToy +Canonical name : typeinfo.toys.FancyToy +Class name: typeinfo.toys.HasBatteries is interface? [true] +Simple name: HasBatteries +Canonical name : typeinfo.toys.HasBatteries +Class name: typeinfo.toys.Waterproof is interface? [true] +Simple name: Waterproof +Canonical name : typeinfo.toys.Waterproof +Class name: typeinfo.toys.Shoots is interface? [true] +Simple name: Shoots +Canonical name : typeinfo.toys.Shoots +Class name: typeinfo.toys.Toy is interface? [false] +Simple name: Toy +Canonical name : typeinfo.toys.Toy +*///:~ diff --git a/xml/People.java b/xml/People.java new file mode 100644 index 0000000..4ad6690 --- /dev/null +++ b/xml/People.java @@ -0,0 +1,22 @@ +//: xml/People.java +// {Requires: nu.xom.Node; You must install +// the XOM library from http://www.xom.nu } +// {RunFirst: Person} +import nu.xom.*; +import java.util.*; + +public class People extends ArrayList { + public People(String fileName) throws Exception { + Document doc = new Builder().build(fileName); + Elements elements = + doc.getRootElement().getChildElements(); + for(int i = 0; i < elements.size(); i++) + add(new Person(elements.get(i))); + } + public static void main(String[] args) throws Exception { + People p = new People("People.xml"); + System.out.println(p); + } +} /* Output: +[Dr. Bunsen Honeydew, Gonzo The Great, Phillip J. Fry] +*///:~ diff --git a/xml/Person.java b/xml/Person.java new file mode 100644 index 0000000..1167140 --- /dev/null +++ b/xml/Person.java @@ -0,0 +1,72 @@ +//: xml/Person.java +// Use the XOM library to write and read XML +// {Requires: nu.xom.Node; You must install +// the XOM library from http://www.xom.nu } +import nu.xom.*; +import java.io.*; +import java.util.*; + +public class Person { + private String first, last; + public Person(String first, String last) { + this.first = first; + this.last = last; + } + // Produce an XML Element from this Person object: + public Element getXML() { + Element person = new Element("person"); + Element firstName = new Element("first"); + firstName.appendChild(first); + Element lastName = new Element("last"); + lastName.appendChild(last); + person.appendChild(firstName); + person.appendChild(lastName); + return person; + } + // Constructor to restore a Person from an XML Element: + public Person(Element person) { + first= person.getFirstChildElement("first").getValue(); + last = person.getFirstChildElement("last").getValue(); + } + public String toString() { return first + " " + last; } + // Make it human-readable: + public static void + format(OutputStream os, Document doc) throws Exception { + Serializer serializer= new Serializer(os,"ISO-8859-1"); + serializer.setIndent(4); + serializer.setMaxLength(60); + serializer.write(doc); + serializer.flush(); + } + public static void main(String[] args) throws Exception { + List people = Arrays.asList( + new Person("Dr. Bunsen", "Honeydew"), + new Person("Gonzo", "The Great"), + new Person("Phillip J.", "Fry")); + System.out.println(people); + Element root = new Element("people"); + for(Person p : people) + root.appendChild(p.getXML()); + Document doc = new Document(root); + format(System.out, doc); + format(new BufferedOutputStream(new FileOutputStream( + "People.xml")), doc); + } +} /* Output: +[Dr. Bunsen Honeydew, Gonzo The Great, Phillip J. Fry] + + + + Dr. Bunsen + Honeydew + + + Gonzo + The Great + + + Phillip J. + Fry + + +*///:~ diff --git a/xml/build.xml b/xml/build.xml new file mode 100644 index 0000000..04c70f1 --- /dev/null +++ b/xml/build.xml @@ -0,0 +1,86 @@ + + + + + + build.xml for the source code for the xml chapter of + Thinking in Java, 4th Edition by Bruce Eckel + Source code available at http://www.MindView.net + See copyright notice in CopyRight.txt + + Ant available from: http://jakarta.apache.org/ant + + To see options, type: ant -p + + This file was automatically generated by AntBuilder + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +