diff --git a/book.toml b/book.toml index dafff0a5..fbcd4d59 100644 --- a/book.toml +++ b/book.toml @@ -1,7 +1,11 @@ [book] title = "Modern Java" description = "Book teaching how to write modern and effective Java." -authors = ["Ethan McCue", "Together Java", "Contributions from the Java Community"] +authors = [ + "Ethan McCue", + "Together Java", + "Contributions from the Java Community", +] language = "en" [output.html] @@ -10,10 +14,11 @@ edit-url-template = "https://github.com/Together-Java/ModernJava/edit/develop/{p mathjax-support = true additional-css = ["ferris.css"] additional-js = ["ferris.js"] +heading-split-level = 2 [output.html.fold] -enable = true # whether or not to enable section folding -level = 0 # the depth to start folding +enable = false # whether or not to enable section folding +level = 0 # the depth to start folding [output.html.playground] editable = true diff --git a/src/SUMMARY.md b/src/SUMMARY.md index a2b6fb85..c045bc79 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -15,12 +15,9 @@ - [w3schools](./examples/w3schools.md) Project ideas: - calorie tracker tic tac toe chess go - CSV - image stuff with the PPM format swing game - maybe have a simplified game engine @@ -37,12 +34,71 @@ airplane physics program ball throw physics program chemical stuff -(after bytes) Make an audio file. Play hot cross buns. snake 2048 colors battleship + +CLI Hangman +Tic Tac Toe AI + +TODO App + +Task Description - data formats + +Approximate PI + +Pomodoro timer () +Terminal ANKI (might require threads) + +Simple REST API +HTML + +- [Tic-Tac-Toe]() +- [Chicken Nugget Numbers]() + +* Argument Objects (InputArgs) +* Cursor Objects (sql) +* Exception Hierarchies +* Interior Iteration + +Make your own iterator + +# Projects II + + https://michaelxing.com/UltimateTTT/v3/ +- [Ultimate Tic-Tac-Toe]() + +Circuit Builder Resistance Solver + +Draw a circuit. + +Adjacency Matrix + +BFS + +Have a project that is just klarna but the your goal is "never write this program." + +- [Journal Entries]() + +sealed interfaces +lambdas + +easy vs. simple + +enshittification state machine example (Acquire -> Make good for business bad for users -> Bad for everyone) + +Zombie Game + +try to trick someone into making that. +LegBeanProviderFactory + + +stuff in mooc i do not have +Streams +Intro to testing +Gui stuff --> # Modern Java @@ -51,6 +107,8 @@ battleship - [Asking for Help](./prelude/asking_for_help.md) - [Toy Problems](./prelude/toy_problems.md) - [Lies](./prelude/lies.md) + - [AI](./prelude/ai.md) + - [Java](./prelude/java.md) - [Getting Started](./getting_started.md) - [First Steps](./first_steps.md) - [Comments](./first_steps/comments.md) @@ -123,6 +181,16 @@ battleship - [Access Individual Characters](./strings/access_individual_characters.md) - [Challenges](./strings/challenges.md) +# Interactive Programs + +- [Standard Input](./standard_input.md) + - [Prompting](./standard_input/prompting.md) + - [Interpreting Input](./standard_input/interpreting_input.md) + - [Integers](./standard_input/integers.md) + - [Floating Point Numbers](./standard_input/floating_point_numbers.md) + - [Other Types](./standard_input/other_types.md) + - [Challenges](./standard_input/challenges.md) + # Control Flow I - [Branching Paths](./branching_paths.md) @@ -153,6 +221,12 @@ battleship - [Iterate over a String](./loops/iterate_over_a_string.md) - [Challenges](./loops/challenges.md) +# Projects + + +- [Prelude](./projects/prelude.md) +- [Calorie Tracker](./projects/calorie_tracker.md) + # Data Types II - [Arrays](./arrays.md) @@ -169,10 +243,6 @@ battleship - [Initialization with new](./arrays/initialization_with_new.md) - [Challenges](./arrays/challenges.md) -# Projects - -- [The Boston Molasses Disaster Game]() - # Control Flow II - [Loops II](./loops_ii.md) @@ -226,11 +296,26 @@ battleship - [Unreachable Statements](./return_values/unreachable_statements.md) - [Challenges](./return_values/challenges.md) + +# Data Types III + +- [Multi-Dimensional Arrays](./multi_dimensional_arrays.md) + - [Declaration](./multi_dimensional_arrays/declaration.md) + - [Array Initializers](./multi_dimensional_arrays/array_initializers.md) + - [Initialization with new](./multi_dimensional_arrays/initialization_with_new.md) + - [Access Individual Elements](./multi_dimensional_arrays/access_individual_elements.md) + - [Set Individual Elements](./multi_dimensional_arrays/set_individual_elements.md) + - [Initialization with Size](./multi_dimensional_arrays/initialize_with_size.md) + - [Default Values](./multi_dimensional_arrays/default_values.md) + - [Populate Values](./multi_dimensional_arrays/populate_values.md) + - [Ragged Arrays](./multi_dimensional_arrays/ragged_arrays.md) + - [Challenges](./multi_dimensional_arrays/challenges.md) + # Projects -- [Tic-Tac-Toe]() +- [ASCII Art Generator](./projects/ascii_art.md) -# Data Types III +# Data Types IV - [null](./null.md) - [Null as Absence](./null/null_as_absence.md) @@ -253,10 +338,6 @@ battleship - [Populate Arrays](./arrays_ii/populate_arrays.md) - [Challenges](./arrays_ii/challenges.md) -# Projects II - - -- [Ultimate Tic-Tac-Toe]() # Code Structure II @@ -285,7 +366,15 @@ battleship - [Clarity](./instance_methods/clarity.md) - [Challenges](./instance_methods/challenges.md) -# Data Types IV + + +# Projects + + +- [Point of Sale System](./projects/point_of_sale_system.md) + + +# Data Types V - [Enums](./enums.md) - [Declaration](./enums/declaration.md) @@ -318,11 +407,23 @@ battleship - [ints](./switch/ints.md) - [Enums](./switch/enums.md) - [Omitted Default](./switch/omitted_default.md) - - [Exhaustiveness](./switch/exhaustiveness.md) - [Combining Cases](./switch/combining_cases.md) - [null](./switch/null.md) + - [Exhaustiveness](./switch/exhaustiveness.md) - [Challenges](./switch/challenges.md) + +# Interactive Programs II + +- [Standard Input II](./standard_input_ii.md) + - [Reprompting](./standard_input_ii/reprompting.md) + - [Enums](./standard_input_ii/enums.md) + - [Delayed Assignment](./standard_input_ii/delayed_assignment.md) + - [Leniency](./standard_input_ii/leniency.md) + - [Aggregating Data](./standard_input_ii/aggregating_data.md) + - [Challenges](./standard_input_ii/challenges.md) + + # Code Structure III - [Constructors](./constructors.md) @@ -341,6 +442,12 @@ battleship - [Inferred Types](./global_fields/inferred_types.md) - [Challenges](./global_fields/challenges.md) +# Projects + + +- [Tic-Tac-Toe]() + + # Concepts - [Code is Read more than Written](./code_is_read_more_than_written.md) @@ -350,15 +457,7 @@ battleship - [Audience](./code_is_read_more_than_written/audience.md) - [Practice](./code_is_read_more_than_written/practice.md) -# Interactive Programs -- [Standard Input](./standard_input.md) - - [Prompting](./standard_input/prompting.md) - - [Interpreting Input](./standard_input/interpreting_input.md) - - [Reprompting](./standard_input/reprompting.md) - - [Leniency](./standard_input/leniency.md) - - [Delayed Assignment](./standard_input/delayed_assignment.md) - - [Transporting Data](./standard_input/transporting_data.md) # The Computing Environment @@ -395,15 +494,17 @@ battleship - [Unchecked Exceptions](./exceptions_ii/unchecked_exceptions.md) - [throws](./exceptions_ii/throws.md) - [Propagating Exceptions](./exceptions_ii/propagating_exceptions.md) - - [Rethrowing Exceptions](./exceptions_ii/rethrowing_exceptions.md) - [Exception](./exceptions_ii/exception.md) - [RuntimeException](./exceptions_ii/runtime_exception.md) + - [Rethrowing Exceptions](./exceptions_ii/rethrowing_exceptions.md) - [main](./exceptions_ii/main.md) + - [Challenges](./exceptions_ii/challenges.md) - [Switch II](./switch_ii.md) - [Yield](./switch_ii/yield.md) - [Omitted Yield](./switch_ii/omitted_yield.md) - [Exhaustiveness](./switch_ii/exhaustiveness.md) - [Return a Switch](./switch_ii/return_a_switch.md) + - [Challenges](./switch_ii/challenges.md) # Code Structure IV @@ -414,12 +515,14 @@ battleship - [File names](./multi_file_programs/file_names.md) - [The Anonymous Main Class](./multi_file_programs/the_anonymous_main_class.md) - [Global Fields](./multi_file_programs/global_fields.md) + - [Challenges](./multi_file_programs/challenges.md) - [Visibility](./visibility.md) - [Private Methods](./visibility/private_methods.md) - [Private Fields](./visibility/private_fields.md) - [Invariants](./visibility/invariants.md) - [Accessors](./visibility/accessors.md) - [Getters and Setters](./visibility/getter_and_setters.md) + - [Challenges](./visibility/challenges.md) - [Static Fields](./static_fields.md) - [Declaration](./static_fields/declaration.md) - [Initialization](./static_fields/initialization.md) @@ -427,6 +530,7 @@ battleship - [Constants](./static_fields/constants.md) - [Controversy](./static_fields/controversy.md) - [Naming](./static_fields/naming.md) + - [Challenges](./static_fields/challenges.md) - [Static Methods](./static_methods.md) - [Declaration](./static_methods/declaration.md) - [Scope](./static_methods/scope.md) @@ -434,6 +538,7 @@ battleship - [Usage](./static_methods/usage.md) - [Math](./static_methods/math.md) - [Factories](./static_methods/factories.md) + - [Challenges](./static_methods/challenges.md) # Data Structures & Algorithms @@ -444,13 +549,14 @@ battleship - [Performance Problems](./growable_arrays/performance_problems.md) - [Performance Solutions](./growable_arrays/performance_solutions.md) - [Optimized Implementation](./growable_arrays/optimized_implementation.md) + - [Challenges](./growable_arrays/challenges.md) # Interactive Programs II - [Command Line Arguments](./command_line_arguments.md) - [Accessing Arguments](./command_line_arguments/accessing_arguments.md) - [Conventions](./command_line_arguments/conventions.md) - + - [Challenges](./command_line_arguments/challenges.md) # Code Structure V @@ -463,12 +569,14 @@ battleship - [The anonymous main class](./inner_classes/the_anonymous_main_class.md) - [Static Inner Classes](./inner_classes/static_inner_classes.md) - [Private Inner Classes](./inner_classes/private_inner_classes.md) + - [Challenges](./inner_classes/challenges.md) - [Packages](./packages.md) - [Declaration](./packages/declaration.md) - [Visibility](./packages/visibility.md) - [Public Classes](./packages/public_classes.md) - [Fully Qualified Class Name](./packages/fully_qualified_class_name.md) - [Import](./packages/import.md) + - [Package Imports](./packages/package_imports.md) - [The Default Package](./packages/the_default_package.md) - [The Anonymous Main Class](./packages/the_anonymous_main_class.md) - [Public Methods](./packages/public_methods.md) @@ -480,8 +588,9 @@ battleship - [Package-Private Constructors](./packages/package_private_constructors.md) - [Subpackages](./packages/subpackages.md) - [Reverse Domain Name Notation](./packages/reverse_domain_name_notation.md) + - [Challenges](./packages/challenges.md) -# Data Types IV +# Data Types VI - [Records](./records.md) - [Declaration](./records/declaration.md) @@ -492,6 +601,7 @@ battleship - [Check for Equality](./records/check_for_equality.md) - [Return Multiple Values](./records/return_multiple_values.md) - [Shorthand](./records/shorthand.md) + - [Challenges](./records/challenges.md) - [Integers II](./integers_ii.md) - [Integer from a String](./integers_ii/integer_from_a_string.md) @@ -500,6 +610,7 @@ battleship - [Integer from a Base 16 String](./integers_ii/integer_from_a_base_16_string.md) - [Integer to a Base 16 String](./integers_ii/integer_to_a_base_16_string.md) - [Underscores in Integer Literals](./integers_ii/underscores_in_integer_literals.md) + - [Challenges](./integers_ii/challenges.md) # Interactive Programs III @@ -514,7 +625,13 @@ battleship - [Challenges](./files/challenges.md) +# Projects + +- [Data Visualization](./projects/data_visualization.md) + + + @@ -541,6 +659,7 @@ battleship - [Inference](./generics/inference.md) - [Soundness](./generics/soundness.md) - [Raw Types](./generics/raw_types.md) + - [Challenges](./generics/challenges.md) - [Interfaces](./interfaces.md) - [Interface Declaration](./interfaces/interface_declaration.md) - [Implementation](./interfaces/implementation.md) @@ -548,8 +667,8 @@ battleship - [Naming](./interfaces/naming.md) - [Subtypes](./interfaces/subtypes.md) - [Multiple Implementations](./interfaces/multiple_implementations.md) - -# Data Types V + - [Challenges](./interfaces/challenges.md) +# Data Types VII @@ -572,6 +691,7 @@ Make them do one. --> - [Loop over items](./array_list/loop_over_items.md) - [Set an item](./array_list/set_an_item.md) - [Remove an item](./array_list/remove_an_item.md) + - [Challenges](./array_list/challenges.md) - [HashMap](./hash_maps.md) - [Filing Cabinets](./hash_maps/filing_cabinets.md) - [Keys and Values](./hash_maps/keys_and_values.md) @@ -584,6 +704,7 @@ Make them do one. --> - [Value Based Identity](./hash_maps/value_based_identity.md) - [Appropriate Keys](./hash_maps/appropriate_keys.md) - [Ubiquity](./hash_maps/ubiquity.md) + - [Challenges](./hash_maps/challenges.md) @@ -602,6 +723,7 @@ Make them do one. --> - [return](./switch_iii/return.md) - [default](./switch_iii/default.md) - [yield](./switch_iii/yield.md) + - [Challenges](./switch_iii/challenges.md) - [Recursion](./recursion.md) - [Disclaimer](./recursion/disclaimer.md) - [Base Case](./recursion/base_case.md) @@ -611,6 +733,7 @@ Make them do one. --> - [Accumulators](./recursion/accumulators.md) - [Recurse Over a String](./recursion/recursing_over_strings.md) - [Recurse Over an Array](./recursion/recursing_over_arrays.md) + - [Challenges](./recursion/challenges.md) - [Loops III](./loops_iii.md) - [For-each loops](./loops_iii/for_each_loops.md) @@ -620,8 +743,9 @@ Make them do one. --> - [String](./loops_iii/string.md) - [Concurrent Modifications](./loops_iii/concurrent_modifications.md) - [Inferred Types](./loops_iii/inferred_types.md) + - [Challenges](./loops_iii/challenges.md) -# Concepts II +# Concepts III - [Encapsulation](./encapsulation.md) - [Implementation Details](./encapsulation/implementation_details.md) @@ -634,7 +758,7 @@ Make them do one. --> - [Information Hiding](./encapsulation/information_hiding.md) -# Data Types VI +# Data Types VIII - [Collections](./collections.md) - [List](./collections/list.md) @@ -644,19 +768,9 @@ Make them do one. --> - [UnsupportedOperationException](./collections/unsupported_operation_exception.md) - [Factories](./collections/factories.md) - [Specificity](./collections/specificity.md) + - [Challenges](./collections/challenges.md) -- [Multi-Dimensional Arrays](./multi_dimensional_arrays.md) - - [Declaration](./multi_dimensional_arrays/declaration.md) - - [Array Initializers](./multi_dimensional_arrays/array_initializers.md) - - [Initialization with new](./multi_dimensional_arrays/initialization_with_new.md) - - [Access Individual Elements](./multi_dimensional_arrays/access_individual_elements.md) - - [Set Individual Elements](./multi_dimensional_arrays/set_individual_elements.md) - - [Initialization with Size](./multi_dimensional_arrays/initialize_with_size.md) - - [Default Values](./multi_dimensional_arrays/default_values.md) - - [Populate Values](./multi_dimensional_arrays/populate_values.md) - - [Ragged Arrays](./multi_dimensional_arrays/ragged_arrays.md) - # Metaprogramming - [Reflection](./reflection.md) @@ -671,7 +785,7 @@ Make them do one. --> - [Get a Constructor](./reflection/get_a_constructor.md) - [Get all Constructors](./reflection/get_all_constructors.md) - [Invoke a Constructor](./reflection/invoke_a_constructor.md) - + - [Challenges](./reflection/challenges.md) - [Annotations](./annotations.md) - [Declaration](./annotations/declaration.md) - [Usage](./annotations/usage.md) @@ -682,6 +796,7 @@ Make them do one. --> - [@Retention](./annotations/retention.md) - [Reflective Access](./annotations/reflective_access.md) - [@Override](./annotations/override.md) + - [Challenges](./annotations/challenges.md) @@ -702,6 +817,7 @@ Make them do one. --> - [Interface Extension](./interfaces_ii/interface_extension.md) - [Static Methods](./interfaces_ii/static_methods.md) - [Static Fields](./interfaces_ii/static_fields.md) + - [Challenges](./interfaces_ii/challenges.md) - [Class Extension](./class_extension.md) - [Extend a Class](./class_extension/extend_a_class.md) - [Inheritance](./class_extension/inheritance.md) @@ -712,9 +828,17 @@ Make them do one. --> - [Relation to Interfaces](./class_extension/relation_to_interfaces.md) - [Relation to Encapsulation](./class_extension/relation_to_encapsulation.md) - [Final Classes](./class_extension/final_classes.md) + - [Challenges](./class_extension/challenges.md) + -# Data Types VII + +# Data Types IX - [Niche Numerics](./niche_numerics.md) - [byte](./niche_numerics/byte.md) @@ -722,47 +846,176 @@ Make them do one. --> - [long](./niche_numerics/long.md) - [Unsigned Operations](./niche_numerics/unsigned_operations.md) - [float](./niche_numerics/float.md) + - [Challenges](./niche_numerics/challenges.md) + +# Projects + + +- [Music Maker](./projects/music_maker.md) # Code Structure VIII - [Modules](./modules.md) - [Declaration](./modules/declaration.md) - [Restrictions](./modules/restrictions.md) - - [Exports]() - - [Integrity]() + - [Exports](./modules/exports.md) + - [Requires](./modules/requires.md) + - [Module Imports](./modules/module_imports.md) - [java.base](./modules/java.base.md) - [The Unnamed Module](./modules/the_unnamed_module.md) - - [Module Imports](./modules/module_imports.md) - - [Multi-Module Directory Layout]() + - [Multi-Module Directory Layout](./modules/multi_module_directory_layout.md) + - [Purpose](./modules/purpose.md) + - [Challenges](./modules/challenges.md) + +- [Lambdas](./lambdas.md) + - [Functional Interfaces](./lambdas/functional_interfaces.md) + - [@FunctionalInterface](./lambdas/functional_interface_annotation.md) + - [Lambda Expressions](./lambdas/lambda_expressions.md) + - [Arguments](./lambdas/arguments.md) + - [Return](./lambdas/return.md) + - [Method References](./lambdas/method_references.md) + - [Inference](./lambdas/inference.md) + - [Built-In Functional Interfaces](./lambdas/built_in_functional_interfaces.md) + - [Runnable](./lambdas/runnable.md) + - [Function](./lambdas/function.md) + - [Checked Exceptions](./lambdas/checked_exceptions.md) + - [Challenges](./lambdas/challenges.md) + + # Sharing Code - [Compilation](./compilation.md) - - [javac]() - - [--release]() -- [Packaging]() - - [jar]() - - [--main-class]() -- [Documentation]() - - [javadoc]() + - [javac](./compilation/javac.md) + - [Class Files](./compilation/class_files.md) + - [Modules](./compilation/modules.md) + - [Compile Multiple Files](./compilation/multiple_files.md) + - [Clean](./compilation/clean.md) + - [-g](./compilation/g.md) + - [Running Compiled Code](./compilation/running_compiled_code.md) + - [Challenges](./compilation/challenges.md) +- [Packaging](./packaging.md) + - [jar](./packaging/jar.md) + - [Jar Files](./packaging/jar_files.md) + - [--module-path](./packaging/module_path.md) + - [--main-class](./packaging/main_class.md) + - [Libraries](./packaging/libraries.md) + - [Challenges](./packaging/challenges.md) + +# Tools + +- [just](./just.md) + - [Installation](./just/installation.md) + - [Justfile](./just/justfile.md) + - [Recipes](./just/recipes.md) + - [Dependencies](./just/dependencies.md) + - [Documentation Comments](./just/documentation_comments.md) + - [Further Reading](./just/further_reading.md) + - [Challenges](./just/challenges.md) + +# Sharing Code II + + +- [Documentation](./documentation.md) - [Documentation Comments](./documentation/documentation_comments.md) -- [Distribution]() - - [jlink]() + - [Format](./documentation/format.md) + - [javadoc](./documentation/javadoc.md) + - [Challenges](./documentation/challenges.md) + +# Data Types X + +- [Streams](./streams.md) + - [stream](./streams/stream.md) + - [map](./streams/map.md) + - [filter](./streams/filter.md) + - [Terminal Operations](./streams/terminal_operations.md) + - [Collectors](./streams/collectors.md) + - [Purpose](./streams/purpose.md) + - [Challenges](./streams/challenges.md) + + +# Conclusion + +- [What Now?](./conclusion/what_now.md) + + + javac -------------\ + ----> Class Files --> Java Virtual Machine +Kotlin Source Code --> kotlinc ---------/ | + / +Lombok Source Code --> lombokc ----------/ + +... and more +``` + +This lets those languages make use of the Java Virtual Machine and all the millions +of dollars and decades of work put into making it run code fast.[^bringup] + +[^bringup]: I bring this up because if you are only thinking about Java it might seem like a +pointless extra step. It somewhat is, but at the same time it lets other languages "compete" +on more or less even ground. The JVM is a labor of fill-in-the-blank, that is for sure. \ No newline at end of file diff --git a/src/compilation/clean.md b/src/compilation/clean.md new file mode 100644 index 00000000..1afa87f6 --- /dev/null +++ b/src/compilation/clean.md @@ -0,0 +1,22 @@ +# Clean + +If you compile code multiple times you should "clean" +your working directory before each compilation. + +This is because when you run `javac -d output ...` the compiler +will simply dump class files into the destination folder. +It will not remove any "stale" class files from earlier compiler runs. + +This can lead you to be in a state where you think your code is working +but if you ever compiled it again, from scratch, you would get errors. + +The simplest way to handle this in bash is to use the `rm` tool. `rm` **r**e**m**oves +files. Before you run the compiler clear out any old output with `rm -rf output`. +`-rf` stands for "**r**ecursive" and "**f**orce." This is what you need to delete whole +folders.[^simple] + +[^simple]: This technique - just deleting folders and doing the work from scratch again - can be slow. +The upside of this approach is that it is simple to understand. +It also probably doesn't matter since your computer is fast. When you need to really efficiently compile +huge codebases you turn to a special kind of program called a build tool. These are complex beasts but can +avoid unneccesary work. \ No newline at end of file diff --git a/src/compilation/g.md b/src/compilation/g.md new file mode 100644 index 00000000..6b4aa4a6 --- /dev/null +++ b/src/compilation/g.md @@ -0,0 +1,39 @@ +# -g + +There are a lot of options you can pass to `javac` to alter its behavior. +One of the ones that I find useful is `-g`. The `g` stands for "**g**enerate debug info."[^obvious] + +This makes sure the generated class files have information about things like what variables were named +what. This, in turn, helps Java give better error messages + +For example, if you have a program like the following: + +```java +class Main { + void main() { + String nocturne = null; + IO.println(nocturne.length()); + } +} +``` + +While in all situations it will ultimately crash with a `NullPointerException`, if +you did not compile with `-g` you might get a stack trace like the following. +You will probably need to scroll to the right to see the relevant bit. + +```text,no_run +Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "" is null + at Main.main(Main.java:4) +``` + +Whereas using `-g` will get you an exception that includes the variable name of what was `null`. + +```text,no_run +Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "nocturne" is null + at Main.main(Main.java:4) +``` + +So, with rare exceptions, you should always use `-g`. + + +[^obvious]: Obviously. \ No newline at end of file diff --git a/src/compilation/header.png b/src/compilation/header.png new file mode 100644 index 00000000..117bacc2 Binary files /dev/null and b/src/compilation/header.png differ diff --git a/src/compilation/javac.md b/src/compilation/javac.md new file mode 100644 index 00000000..04703364 --- /dev/null +++ b/src/compilation/javac.md @@ -0,0 +1,36 @@ +# javac + +The compiler used for compiling Java code is called `javac`. +This stands for "**Java** **C**ompiler". + +Its job is to take a list of `.java` files and compile +them into `.class` files. + +For a single file Java program you can do this by running +a command similar to the following. + +```bash +javac -d output src/Main.java +``` + +The `-d output` in that example means "put the compiled class files in a folder called `output`." +After running the command above you would expect to see something like the following. + +```text +src/ + Main.java +output/ + Main.class +``` + +You aren't guarenteed that any given `.java` file will produce only one `.class` file +as output. Inner classes are one reason for this, but there are others. + +```text +src/ + Main.java +output/ + Main.class + Main$1.class + Main$Thing.class +``` diff --git a/src/compilation/modules.md b/src/compilation/modules.md new file mode 100644 index 00000000..5619095f --- /dev/null +++ b/src/compilation/modules.md @@ -0,0 +1,11 @@ +# Modules + +For reasons that will become apparent as we proceed, +it is best if all all the code you intend to share +is contained within a named module. + +This means at minimum your classes would need to be in packages +and would need to provide a `module-info.java`. + +Many things will work even without that but some important ones +will not. \ No newline at end of file diff --git a/src/compilation/multiple_files.md b/src/compilation/multiple_files.md new file mode 100644 index 00000000..7c08c069 --- /dev/null +++ b/src/compilation/multiple_files.md @@ -0,0 +1,43 @@ +# Compile Multiple Files + +To have `javac` compile multiple files you have a few options. + +The first is to simply list every file you want to compile one after +the other when calling `javac`. + +```bash +javac -d output src/Main.java src/Other.java src/Another.java +``` + +This has the obvious downside of needing you to add new files to what can +become a large list over time. + +If you get far enough in learning bash you can paper over this +with commonly available tools like `find`. + +```bash +javac -d output $(find ./src -name "*.java" -type f) +``` + +Where the `$()` is bash syntax that runs the command in the parentheses and uses its +output as arguments to the command being run. `find` is a tool that lists all files that +match some criteria. In this case all files (as opposed to folders) that have a name that +ends with `.java`. + +The other option is to structure your project using the multi-module directory layout. + +``` +your.project/ + src/ + code/ + Main.java + Other.java + module-info.java +``` + +If you do this then you can compile all the code in a module by using the `--module-source-path` and `--module` options. + +```bash +javac -d output --module-source-path "./*/src" --module your.project +``` + diff --git a/src/compilation/running_compiled_code.md b/src/compilation/running_compiled_code.md new file mode 100644 index 00000000..107ded53 --- /dev/null +++ b/src/compilation/running_compiled_code.md @@ -0,0 +1,44 @@ +# Running Compiled Code + +The way you ultimately run your compiled code depends on +whether or not all your code is in packages. + +If you have any classes in the unnamed package - which +will only be the case when said code is also not in a named +module - you should run your code like so: + +```bash,no_run +java --class-path output Main +``` + +Where you can substitute `Main` for whatever the class you want to run is. +So if you want to run a class named `Impromptu` in the `chopin` package +you would run `java --class-path output chopin.Impromptu`. + +`--class-path` should be self-explanatory. It is the path where `java` +will look for class files. + +But if you do not have any classes in the unnamed package - which will +be hopefully be the case when you share code with others[^conflicts] - +you instead want to run your code like this. + +``` +java \ + --module-path output \ + --add-modules ALL-MODULE-PATH + composers.Main +``` + +The `--module-path` option is very similar to the `--class-path` option. The difference +is that all the code on the `--module-path` will be loaded with more strict rules.[^before] + +The `--add-modules ALL-MODULE-PATH` bit just means "load all the code on the module path."[^auth] + +[^conflicts]: Remember the social convention of reverse domain name notation (`com.google`, etc) + +[^before]: You can think of `--class-path` as sort of a "compatibility mode" for libraries +and code written before Java had a concept of modules. The difference isn't super important +other than the very specific case of wanting to compile and run classes in the unnamed package. +Well that and "troublesome" libraries, but we will get to that later. + +[^auth]: Chances are Java will make this the default in the future. For now you must write it though. \ No newline at end of file diff --git "a/src/conclusion/Screenshot 2025-08-25 at 8.09.39\342\200\257PM.png" "b/src/conclusion/Screenshot 2025-08-25 at 8.09.39\342\200\257PM.png" new file mode 100644 index 00000000..c56ff811 Binary files /dev/null and "b/src/conclusion/Screenshot 2025-08-25 at 8.09.39\342\200\257PM.png" differ diff --git a/src/conclusion/cici_c_rat.png b/src/conclusion/cici_c_rat.png new file mode 100644 index 00000000..8ebb036d Binary files /dev/null and b/src/conclusion/cici_c_rat.png differ diff --git a/src/conclusion/csharp_mascot.png b/src/conclusion/csharp_mascot.png new file mode 100644 index 00000000..8e407be1 Binary files /dev/null and b/src/conclusion/csharp_mascot.png differ diff --git a/src/conclusion/go_mascot.png b/src/conclusion/go_mascot.png new file mode 100644 index 00000000..f0f80ac0 Binary files /dev/null and b/src/conclusion/go_mascot.png differ diff --git a/src/conclusion/java_mascot.png b/src/conclusion/java_mascot.png new file mode 100644 index 00000000..9047082e Binary files /dev/null and b/src/conclusion/java_mascot.png differ diff --git a/src/conclusion/javascript_mascot.png b/src/conclusion/javascript_mascot.png new file mode 100644 index 00000000..0ce0ee23 Binary files /dev/null and b/src/conclusion/javascript_mascot.png differ diff --git a/src/conclusion/kotlin_mascot.png b/src/conclusion/kotlin_mascot.png new file mode 100644 index 00000000..ba2b3a09 Binary files /dev/null and b/src/conclusion/kotlin_mascot.png differ diff --git a/src/conclusion/minecraft_duke.png b/src/conclusion/minecraft_duke.png new file mode 100644 index 00000000..6e85fb60 Binary files /dev/null and b/src/conclusion/minecraft_duke.png differ diff --git a/src/conclusion/python_logo.png b/src/conclusion/python_logo.png new file mode 100644 index 00000000..f80ddb58 Binary files /dev/null and b/src/conclusion/python_logo.png differ diff --git a/src/conclusion/ruby_logo.png b/src/conclusion/ruby_logo.png new file mode 100644 index 00000000..bcca018f Binary files /dev/null and b/src/conclusion/ruby_logo.png differ diff --git a/src/conclusion/what_now.md b/src/conclusion/what_now.md new file mode 100644 index 00000000..77e2c9ff --- /dev/null +++ b/src/conclusion/what_now.md @@ -0,0 +1,264 @@ +# What Now? + +If you have made it this far: + +1. Congratulations, you have now learned Java! +2. You are probably wondering what you should do next. + +## Go Deeper + +No matter how much I write there is no chance I will have covered all of the Java +language nor all of what you might want to know to write software in Java. + +With what you've learned so far you should have a solid enough foundation +to go off and learn from other sources. I'll try and paint a picture of the landscape for you +before you run off though. + +### Build Tools + +First you probably are going to want to learn a build tool. +I haven't covered how to get dependencies yet and that is on purpose. + +In the Java world - due to the ability to launch a program with `java src/Main.java` being +a pretty recent development - all the tools that help you automatically download +libraries written by other people are married with tools that "build" - i.e. run `javac`, `jar`, etc. +for you - your code. + +There are two major build tools (meaning widely used) in the Java world (Maven and Gradle) +as well as many more niche ones (bld, mill, etc.). + +If you want a gentle introduction to this world you can start with bld, though be aware this +will be a road less travelled. + +([bld tutorial here](https://github.com/rife2/bld/wiki)) + +If you want to learn the one that will probably be the most useful to you +in a professional setting you should learn Maven first. + +(["Maven By Example" book here](https://books.sonatype.com/mvnex-book/reference/index.html)) + +If you are angling to get into Android development you should learn Gradle. +Hop ahead and check out the resources for Kotlin too because Kotlin is the +language you will use for Gradle build scripts. + +([Gradle Documentation Here](https://docs.gradle.org/current/userguide/userguide.html)) + +### Minecraft + + + +If your age begins with the number `1` you are either near death or statistically +very interested in Minecraft. + +A lot of people who learn Java do so in order to be able to write Minecraft +mods or plugins for Minecraft servers, it is normal. + +Just a few words of caution: + +* The world of Minecraft development can be deeply exploitative. If you are not +a full adult please do not try to "work" for anyone. Be careful. Talk to a +parent or an adult you trust when people online seem to want something from you. +* The kinds of code you write to make mods work will be pretty different than the +kind of code you would write for most other kinds of software. This is partially +because of what modding is (adjusting software whose evolution you do not control) +and partially because of peculiarities around Minecraft in particular. + +With that all said: there are two basic "mod compatibility layers." These are +"Fabric" and "Forge." + +The point of these is to give you code to program against that isn't the direct Minecraft source +code, which can change very frequently, and to give your mod a fighting chance of being compatible +across multiple Minecraft versions. + +Of the two: this book probably prepared you the best for Forge. + +Forge requires you to use Gradle which in turn will require at least a little knowledge of Kotlin. +You don't need to take a full detour through that to get started, but you should put both of +those on your list of things to learn. + +[Forge Getting Started here](https://docs.minecraftforge.net/en/latest/gettingstarted/) + +["Modded Minecraft" Discord here](https://discord.gg/moddedmc) + +["Minecraft Mod Development" Discord here](https://discord.com/invite/wpMz4AtAhn) + +Fabric pretty quickly requires you to interact with a concept called a "Mixin." +This is a mechanism the Minecraft modding world made for magically editing the code inside Minecraft +among other things. If you go this path just be ready for that. + +[Fabric Getting Started here](https://docs.fabricmc.net/develop/getting-started/introduction-to-fabric-and-modding) + +[Fabric community Discord here]([https://discord.gg/v6v4pMv](https://discord.gg/v6v4pMv)) + +For making plugins that run on a custom Minecraft server - so things that handle custom +chat commands and things of that nature - you have to use the plugin required by whatever +server you are using. I am not the most up to date with Minecraft, but I know there +is both Spigot and PaperMC. I have been told that Spigot is the preferred option +as it allows for Bedrock players to play as well. + +[Docs for Spigot here](https://www.spigotmc.org/wiki/spigot-plugin-development/) + +[Spigot community Discord here](https://discord.gg/spigotmc) + +[Docs for PaperMC here](https://docs.papermc.io/) + +[PaperMC community Discord here](https://discord.gg/papermc) + + +### Websites + +Making websites is a profitable career path. At least it is at the time of writing. +There are a few essential things you will need to learn about to get started with that +path. + +The first is how to make an HTTP Server. HTTP Servers are what web browsers talk +to in order to render websites. + +There are a lot of tools for this in Java. A lot a lot. I would recommend +starting with the one that comes built-in: the `jdk.httpserver` module. + +[Docs for `jdk.httpserver` here](https://docs.oracle.com/en/java/javase/24/docs/api/jdk.httpserver/module-summary.html) + +Then you will need to learn about SQL databases and how to query from/insert into them from Java. + +A good start for that is SQLite - it is a database that runs self-contained in a single file. + +[SQLite tutorial here](https://www.sqlitetutorial.net/) + +After that - or as part of that - you should learn about JDBC. This is the way you interact with +a database from Java. + +[I have a primer for that here](https://mccue.dev/pages/1-17-24-java-sql) + +Then from there you should learn about Dependency Injection + +[Good video on that here](https://www.youtube.com/watch?v=J1f5b4vcxCQ) + +And finally you can dive into the world of Spring - which is likely the +most employable one of the many HTTP server options out there. + +[Spring Academy Courses here - the basic ones are free](https://spring.academy/) + +### Desktop Applications + +If you want to learn how to make desktop applications in Java you have basically +three paths. + +Path #1 is to learn Java Swing. This is an old crusty GUI framework that is kinda difficult to use +but has the pro of coming with Java and being able to run on every potato in existence. + +[Docs for `java.desktop` (Swing) here](https://docs.oracle.com/en/java/javase/24/docs/api/jdk.httpserver/module-summary.html) + +Path #2 is the learn JavaFX. By all accounts JavaFX is better software than Swing, but it was cursed +by coming out at a point in history where desktop apps were no longer big business to develop. It +was eventually removed from the JDK and you will need to procure it like any other dependency. + +[Docs for JavaFX here](https://openjfx.io/openjfx-docs/) + +## Learn a New Language + +An unfortunate truth is that you cannot use Java for everything. + +Sometimes this is intrinsic - Java would not be a good choice for code +driving a pacemaker - and some of it is just how the ecosystems around languages +are right now. + +Even if you could get away with only using Java, there is significant value in knowing multiple +languages. Particuarly languages that are different than Java. + +### JavaScript + + + +To make highly interactive programs that run inside a browser you +will need JavaScript. JavaScript and languages that compile to JavaScript +are basically the only language it is practical +to use for the frontend of a website[^wasm]. + +It is probably worth your time to learn how to write JavaScript. + +[Mozilla Developer Network's JavaScript Guide here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide) + +The most popular language that compiles to JavaScript is TypeScript. After you've +learned JavaScript that is a good one to touch next. + +[TypeScript tutorial here](https://www.typescripttutorial.net/) + + +There are languages out there like TypeScript that compile to JavaScript - +and you can find some projects out there that do much the same for Java - +but just practically speaking learning JavaScript + +### C# + + + +C# is a language broadly similar to Java. It has a lot of features Java doesn't - for better or worse - +but the basics are comparable. + +C# is very prevalent in the game development world. The Unity game engine, +[corporate blunders notwithstanding](https://www.theverge.com/2024/9/12/24242937/unity-runtime-fee-cancelled-subscription-pricing), is still very big and still has you use C# for game scripts. Competitors like Godot [have C# Scripting as well](https://docs.godotengine.org/en/stable/tutorials/scripting/c_sharp/index.html). + +You will also find C# being used to make websites and desktop apps but it doesn't have +as much unique pull there as it does in game development. + +[Microsoft's C# tutorial here](https://learn.microsoft.com/en-us/training/paths/get-started-c-sharp-part-1/) + +### Kotlin + + + +Kotlin is one of a family of languages that try to be a "better Java." +Better is relative, but you are likely to learn _something_ when diving into it. + +Regardless, Kotlin is what you nowadays use to write Gradle build scripts in and +Kotlin is the de-facto language for writing Android apps. + +This means that, while Java in some form is still technically an option, +all the documentation for Android will be using Kotlin for their examples +and most new frameworks will assume you are using Kotlin in preference to Java. + +And Gradle is a build tool that many Java projects choose to use. You probably don't need +as deep of an understanding of Kotlin to work with Gradle build scripts as +you do to make a full app in it, but it can't hurt. + +[Getting Started with Kotlin tutorial here](https://kotlinlang.org/docs/getting-started.html) + +[Kotlin Android Tutorial here](https://kotlinlang.org/docs/android-overview.html) + +If you want to make Desktop or Mobile apps in Kotlin it is probably also worth checking out +Jetpack Compose. + +[Getting Started for Jetpack Compose here](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-create-first-app.html) + + +### Others + +Other languages you might want to learn that I haven't written up +context for quite yet: + +* C + + + +* C++ +* Clojure +* Elm +* Haskell +* Python + + + +* Ruby + + + + +* Go + + + + +* Rust +* Zig +[^wasm]: Fight me, WebAssembly fans. \ No newline at end of file diff --git a/src/constructors.md b/src/constructors.md index c4cbb0a9..98a28e0b 100644 --- a/src/constructors.md +++ b/src/constructors.md @@ -1,5 +1,8 @@ # Constructors + + + When defining a class, you are allowed to make a special kind of method called a constructor. diff --git a/src/constructors/challenges.md b/src/constructors/challenges.md index 8d2295ae..2974181c 100644 --- a/src/constructors/challenges.md +++ b/src/constructors/challenges.md @@ -1,5 +1,7 @@ # Challenges +Remember the rules for this are + - Try to use only the information given up to this point in this book. - Try not to give up until you've given it a solid attempt diff --git a/src/constructors/header.png b/src/constructors/header.png new file mode 100644 index 00000000..69142a5d Binary files /dev/null and b/src/constructors/header.png differ diff --git a/src/distribution.md b/src/distribution.md new file mode 100644 index 00000000..4a44067a --- /dev/null +++ b/src/distribution.md @@ -0,0 +1,12 @@ +# Distribution + +If you are intending to run code on your own machine there isn't much reason to +compile or package it. Compiling and packaging become useful when sharing +code with other developers or when "deploying" code to a machine you (in some manner) control. + +But if you are making code intended to be used by a layperson it is insane to expect them +to install Java special just for your program. It can also become burden on you +since if you do get them to install Java, they won't keep it up to date. + +The solution is to bundle your code with the Java runtime itself. This gives you +an "artifact" you can freely distribute and have someone "just run." diff --git a/src/distribution/jars.md b/src/distribution/jars.md new file mode 100644 index 00000000..f38d94b9 --- /dev/null +++ b/src/distribution/jars.md @@ -0,0 +1 @@ +# jars diff --git a/src/documentation.md b/src/documentation.md new file mode 100644 index 00000000..81a0fb42 --- /dev/null +++ b/src/documentation.md @@ -0,0 +1,16 @@ +# Documentation + + + +When sharing code that is intended to be used as a library it is important to explain +how exactly your code should be used. + +If you can physically or digitally converse with the people you are sharing with this is a solved problem. +They can ask you questions and you can answer them. Rinse and repeat until they no longer need to ask you +questions. + +But seeing as there are millions of developers in the world that isn't always practical.[^grouptext] + +One tool we use to combat this reality is documentation. Documentation is where we write down what things are, what they do and why. + +[^grouptext]: Would make for one hell of a hectic group text. \ No newline at end of file diff --git a/src/documentation/challenges.md b/src/documentation/challenges.md new file mode 100644 index 00000000..6f805751 --- /dev/null +++ b/src/documentation/challenges.md @@ -0,0 +1,24 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Run `javadoc` on some of your code. Add the command for doing so to a `Justfile`. +Make sure to include cleaning stale of output. + +## Challenge 2. + +When you ran `javadoc` on your code you were almost certainly hit by a deluge of `warning: no comment` +messages. + +Fix these by documenting that code enough that `javadoc` no longer gives you warnings. + +## Challenge 3. + +Read the [documentation for java.util.StringJoiner](https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/util/StringJoiner.html). + +If you can, alter one of your projects to use it. \ No newline at end of file diff --git a/src/documentation/documentation_comments.md b/src/documentation/documentation_comments.md index d16a9815..dc7ccd4a 100644 --- a/src/documentation/documentation_comments.md +++ b/src/documentation/documentation_comments.md @@ -1 +1,21 @@ # Documentation Comments + +To document our code we can use "documentation comments." + +These work the same as regular comments but instead of using two slashes (`//`) +we use three slashes (`///`).[^old] + +You put these documentation comments above different elements of your program +with text describing what the purpose of those elements are. + +```java +/// This class represents a ninja +public class Ninja { + /// Says a catchphrase + public void forsooth() { + // .. + } +} +``` + +[^old]: There is an older way to make documentation comments using `/**` and `*/` (note the extra `*` in `/**`), but we prefer this one because the way you write the actual content of the comment is easier. You are sure to see these around though. \ No newline at end of file diff --git a/src/documentation/format.md b/src/documentation/format.md new file mode 100644 index 00000000..7249b2e0 --- /dev/null +++ b/src/documentation/format.md @@ -0,0 +1,80 @@ +# Format + +```java,no_run +import java.io.IOException; + +/// This class serves as a place to put examples +/// of the different kinds of documentation as well +/// as how to write documentation properly. +/// +/// When specified after the three slashes +/// you can write documentation using [Markdown](https://en.wikipedia.org/wiki/Markdown). +/// +/// In Markdown you can just write text as you would in any file, +/// line after line. +/// +/// # For headings you can use a single hash +/// +/// ## For subheadings you can use two +/// +/// ### And so on +/// +/// You can make unordered lists using hyphens +/// +/// - A +/// - B +/// - C +/// +/// And numbered lists like so +/// +/// 1. One +/// 2. Two +/// 3. Thre +/// +/// And so on. Definitely peruse up [tutorial on markdown](https://www.markdownguide.org/getting-started/) +/// when you have the time. +/// +/// There are some additions specific to Java though. +/// We call these additions "tags." +/// +/// One notable tag is the author tag. It lets you mark who worked +/// on a given unit of code +/// +/// @author Ethan McCue +/// @see [https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html](https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html) +public class DocumentationExample { + /// You can document the purpose of parameters + /// to constructors and methods with the param tag + /// + /// @param o Demonstrates a param tag on a constructor + public DocumentationExample(Object o) { + + } + + /// Generic parameters can also be documented using the param tag + /// as well. + /// + /// @param item The item to just return back + /// @param The type of the item. + public static T identity(T item) { + return item; + } + + /// The exceptions thrown by a method can also be documented + /// to explain under what conditions the exceptions might be thrown + /// + /// @param s A parameter to show that throws can be used alongside params + /// @throws IOException whenever the method is called, no matter what + public void crash(String s) throws IOException { + throw new IOException(); + } + + /// You can reference other classes and methods on those classes with the + /// link tag. + /// + /// For instance {@link String} and {@link String#length()}. + public void seeMore() { + + } +} +``` \ No newline at end of file diff --git a/src/documentation/header.png b/src/documentation/header.png new file mode 100644 index 00000000..3ba43300 Binary files /dev/null and b/src/documentation/header.png differ diff --git a/src/documentation/javadoc.md b/src/documentation/javadoc.md new file mode 100644 index 00000000..981ad8a9 --- /dev/null +++ b/src/documentation/javadoc.md @@ -0,0 +1,22 @@ +# javadoc + +The way to take code with documentation comments and produce a documentation website +is with the `javadoc` tool. + +There are multiple ways to run this[^theme], but if you have your code in the +multi-module directory layout you can use `--module-path` and `--module` the +same as `javac` does. + +```bash,no_run +javadoc \ + -d output/javadoc \ + --module-source-path "./*/src" \ + --module one.piece +``` + +This will produce a directory full of HTML files that contain all +the documentation from the specified modules. + +This is what is used to make [the official Java documenation](https://docs.oracle.com/en/java/javase/24/docs/api/index.html) as well as at least part of the documentation for most Java libraries. + +[^theme]: As is a theme with command-line tools \ No newline at end of file diff --git a/src/encapsulation.md b/src/encapsulation.md index de517e63..350ed27b 100644 --- a/src/encapsulation.md +++ b/src/encapsulation.md @@ -1,5 +1,7 @@ # Encapsulation + + One way of combatting the effects of Hyrum's Law is encapsulation. diff --git a/src/encapsulation/header.png b/src/encapsulation/header.png new file mode 100644 index 00000000..d9faaf2d Binary files /dev/null and b/src/encapsulation/header.png differ diff --git a/src/encapsulation/implicit_interfaces.md b/src/encapsulation/implicit_interfaces.md index 98660db2..62fec3bb 100644 --- a/src/encapsulation/implicit_interfaces.md +++ b/src/encapsulation/implicit_interfaces.md @@ -15,6 +15,6 @@ Then back within Java there are things which form "an interface" which may or may not use the `interface` keyword. The static methods available on a class, the constructors that are public, the set of classes that come with a library, etc. -I think a more general concept "the implicit interface." Everything you can observe about a thing forms its "implicit interface."[^implicit] +I think a more general concept is "the implicit interface." Everything you can observe about a thing forms its "implicit interface."[^implicit] [^implicit]: I call it "implicit" because there is no place where you write down "all the properties of a thing" that is not the thing itself. And by thing I mean field, method, class, group of classes, package names - everything. \ No newline at end of file diff --git a/src/enums.md b/src/enums.md index 58a03fae..feead6cf 100644 --- a/src/enums.md +++ b/src/enums.md @@ -1,5 +1,8 @@ # Enums + + + While you can use `String`, `int`, `boolean`, and friends alongside your own custom classes to represent many situations, that is not always enough. diff --git a/src/enums/header.png b/src/enums/header.png new file mode 100644 index 00000000..f91a992e Binary files /dev/null and b/src/enums/header.png differ diff --git a/src/exceptions.md b/src/exceptions.md index 90557894..73e86bc4 100644 --- a/src/exceptions.md +++ b/src/exceptions.md @@ -1,5 +1,8 @@ # Exceptions + + + When you do something that Java doesn't know how to handle, like dividing a number by zero, your program will fail. diff --git a/src/exceptions/header.png b/src/exceptions/header.png new file mode 100644 index 00000000..ba60a784 Binary files /dev/null and b/src/exceptions/header.png differ diff --git a/src/exceptions_ii.md b/src/exceptions_ii.md index 60e3ae09..fd862df7 100644 --- a/src/exceptions_ii.md +++ b/src/exceptions_ii.md @@ -1,5 +1,7 @@ # Exceptions II + + Perhaps unsurprisingly, `RuntimeException` is not the only kind of exception that can be thrown. diff --git a/src/exceptions_ii/challenges.md b/src/exceptions_ii/challenges.md new file mode 100644 index 00000000..f2c2d898 --- /dev/null +++ b/src/exceptions_ii/challenges.md @@ -0,0 +1,76 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1 + +Make a method named `parseIntChecked` that works the same as `Integer.parseInt` +but throws a checked exception instead of an unchecked one. + +```java,editable +// CODE HERE + +void main() throws Exception { + int x = parseIntChecked("45"); + IO.println(x); + + parseIntChecked("abc"); +} +``` + +## Challenge 2 + +The following program will not compile. Make it compile by propagating the checked exception +thrown by the `doesSomethingDangerous` method. + +```java,no_run +int doesSomethingDangerous(int value) { + if (value == 1) { + throw new Exception("1 does not work"); + } + return 3 * value + 2; +} + +int compute(int start) { + return square(doesSomethingDangerous(start)); +} + +int square(int x) { + return x * x; +} + +void main() { + int x = Integer.parseInt(IO.readln("Give a starting number: ")); + IO.println(compute(x)); +} +``` + +## Challenge 3 + +The following program is the same as the last one. Instead of propagating the exception, make it so that +`compute` catches and rethrows the checked exception as an unchecked one. + +```java,no_run +int doesSomethingDangerous(int value) throws Exception { + if (value == 1) { + throw new Exception("1 does not work"); + } + return 3 * value + 2; +} + +int compute(int start) { + return square(doesSomethingDangerous(start)); +} + +int square(int x) { + return x * x; +} + +void main() { + int x = Integer.parseInt(IO.readln("Give a starting number: ")); + IO.println(compute(x)); +} +``` \ No newline at end of file diff --git a/src/exceptions_ii/header.png b/src/exceptions_ii/header.png new file mode 100644 index 00000000..50e15a95 Binary files /dev/null and b/src/exceptions_ii/header.png differ diff --git a/src/exceptions_ii/runtime_exception.md b/src/exceptions_ii/runtime_exception.md index bc10f41a..74b094c9 100644 --- a/src/exceptions_ii/runtime_exception.md +++ b/src/exceptions_ii/runtime_exception.md @@ -1 +1,17 @@ # RuntimeException + +`RuntimeException` is an unchecked exception.[^naming] + +If you want a unchecked exception and do not know of a better one, `RuntimeException` will do. + +```java,panics +void main() { + throw new RuntimeException("Crash!"); +} +``` + +[^naming]: You may be wondering why the names "`Exception`" and "`RuntimeException`" instead of the +probably easier to understand "`CheckedException`" and "`UncheckedException`." Java was made by humans +and initially - as I understand it - "normal" exceptions were expected to be checked. It was only +exceptions that came from "The Java Runtime" that would be unchecked. So that is where the name "`RuntimeException`" comes from. It just very quickly became a choice that was too late to change +without breaking all the Java code in the world. \ No newline at end of file diff --git a/src/files.md b/src/files.md index d00bbf4c..f9a011ee 100644 --- a/src/files.md +++ b/src/files.md @@ -1,5 +1,7 @@ # Files + + Files are how you store information on a[^normal] computer so that it can still be there when your program is done running. diff --git a/src/files/header.png b/src/files/header.png new file mode 100644 index 00000000..fd815711 Binary files /dev/null and b/src/files/header.png differ diff --git a/src/first_steps.md b/src/first_steps.md index fb4f0468..1292470d 100644 --- a/src/first_steps.md +++ b/src/first_steps.md @@ -1,5 +1,9 @@ # First Steps + + + + If you made it through the [Getting Started section](./getting_started/hello_world.md) you've successfully run this program. ```java diff --git a/src/first_steps/header.png b/src/first_steps/header.png new file mode 100644 index 00000000..b899f11c Binary files /dev/null and b/src/first_steps/header.png differ diff --git a/src/first_steps/semicolon.md b/src/first_steps/semicolon.md index c52b918f..4099b4b2 100644 --- a/src/first_steps/semicolon.md +++ b/src/first_steps/semicolon.md @@ -5,7 +5,7 @@ The `;` at the end of each of those lines is a "semicolon". ```java ~void main(){ IO.print("Hello, "); // <-- this thing - // ^ + // ^ ~} ``` diff --git a/src/floating_point_numbers.md b/src/floating_point_numbers.md index 39d365fb..6ecaf08a 100644 --- a/src/floating_point_numbers.md +++ b/src/floating_point_numbers.md @@ -1,5 +1,8 @@ # Floating Point Numbers + + + Floating point numbers are used to represent numbers which cannot be stored as Integers like `2.5` or `3.14`. diff --git a/src/floating_point_numbers/equality.md b/src/floating_point_numbers/equality.md index fbaf95b7..81876162 100644 --- a/src/floating_point_numbers/equality.md +++ b/src/floating_point_numbers/equality.md @@ -29,6 +29,26 @@ IO.println(doesWhatYouExpect); ~} ``` +To account for that you can check if a number is "close enough" to another one +by seeing if the "absolute value" of the difference is a small number. + +```java +~void main() { +double x = 0.1; +double y = 0.2; +// z will be 0.30000000000000004 +double z = x + y; + +double compare = 0.3; + +// this will be true. +boolean doesWhatYouExpect = + Math.abs(z - 0.3) < 0.00001; + +IO.println(doesWhatYouExpect); +~} +``` + A `double` can also be compared to an `int` and, if they represent the same value, they will be reported as equal. ```java diff --git a/src/floating_point_numbers/header.png b/src/floating_point_numbers/header.png new file mode 100644 index 00000000..678252b1 Binary files /dev/null and b/src/floating_point_numbers/header.png differ diff --git a/src/generics.md b/src/generics.md index d428f73d..14ca196f 100644 --- a/src/generics.md +++ b/src/generics.md @@ -1,5 +1,7 @@ # Generics + + Certain types of classes, like growable arrays, are simply holders of data. That is, almost none of how they work has to change to diff --git a/src/generics/challenges.md b/src/generics/challenges.md new file mode 100644 index 00000000..f1659abb --- /dev/null +++ b/src/generics/challenges.md @@ -0,0 +1,89 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + + + +## Challenge 1. + + +Will the following code work? Why or why not? + +```java +class Thing { + T value; +} + +class Cool { +} +class NotCool { +} + +class Main { + void main() { + int n = Integer.parseInt( + IO.readln("Give a number: ") + ); + + Thing o = (n % 2 == 0) + ? new Thing() + : new Thing(); + + } +} +``` + +## Challenge 2. + +Make a class that holds a `NotNull` value. This class should be generic over the kind of +data that it holds but it should throw an exception if the provided `value` is `null`. + +```java,editable +// CODE HERE + +class Main { + void main() { + NotNull s = new NotNull<>("abc"); + IO.println(s.value); + + NotNull i = new NotNull<>(123); + IO.println(i.value); + + // This should throw an exception + // NotNull d = new NotNull<>(null); + } +} +``` + + +## Challenge 3. + +The following class has 5 generic parameters. + +Correct the ones which do not follow expected naming conventions + + +```java,editable +class Organism { + name name; + h h; + T t; + Cat cat; + inch_worm inchWorm; +} + +class Main { + void main() { + var o = new Organism(); + o.name = "abc"; + o.h = 123; + o.t = 5; + o.cat = "..."; + o.inchWorm = "\\_/-\\--(*)"; + } +} +``` + diff --git a/src/generics/header.png b/src/generics/header.png new file mode 100644 index 00000000..a6a567b1 Binary files /dev/null and b/src/generics/header.png differ diff --git a/src/getting_started.md b/src/getting_started.md index f2b4e682..01d9a773 100644 --- a/src/getting_started.md +++ b/src/getting_started.md @@ -17,13 +17,13 @@ void main() { ## Windows -Download the "JDK MSI" from [adoptium.net](https://adoptium.net/temurin/releases/?version=22&os=windows). +Download the "JDK MSI" from [adoptium.net](https://adoptium.net/temurin/releases/?version=25&os=windows). Run the installer, selecting all the default options. ## Mac OS -Download the "JDK .pkg" from [adoptium.net](https://adoptium.net/temurin/releases/?version=22&os=mac). +Download the "JDK .pkg" from [adoptium.net](https://adoptium.net/temurin/releases/?version=25&os=mac). Run the installer, selecting all the default options. @@ -31,74 +31,8 @@ Run the installer, selecting all the default options. ## Linux Linux is a little annoying. If you are using it you are likely used to it -by now, but you can use [adoptium.net](https://adoptium.net/temurin/releases/?version=22&os=linux) like everyone else, but there is no universal installer there. +by now, but you can use [adoptium.net](https://adoptium.net/temurin/releases/?version=25&os=linux) like everyone else, but there is no universal installer there. You can either download the `.tar.gz` file that matches your machine, extract it, and add the `bin` folder to your `PATH`, or you can try to find an installer for your specific linux distribution. - - -## repl.it - -[replit.com](https://replit.com) is a pretty common choice for teachers because -they will be able to give you assignments and have you share back your results. -It is also a decent option if your school only provides you with Chromebooks -or similar. - -It requires an internet connection and you will have to make an account, but -otherwise it is fairly convenient. - -If you are in school and your teacher has helped you get set up in some other -way it is okay to skip this section and just do it the way you were shown. - - -### Step 1. Make an account - -Go to [replit.com](https://replit.com) and find the "Sign Up" button. -Websites change every now and then so these screenshots might be out of date. - -Picture of the sign up button on replit's website - -Click it and sign up for an account. - -Picture of the sign up form on replit's website - -### Step 2. Create a Java REPL - -Find the "Create REPL" button and click it. - -Picture of the create repl button on replit's website - -Then you should be presented with a menu that lets you search for the type of REPL to create. -Find the Java template and click "Create". - -Unfilled in create from template menu on replit - -Filled in create from template menu on replit - -### Step 3. Run code - -You should land on a screen with a big green run button, an open file called -"Main.java", and a blank window labeled "console". - - - -Click that run button, and you should see the text `Hello, world!` appear under the console window. - - diff --git a/src/getting_started/repl_1.png b/src/getting_started/repl_1.png deleted file mode 100644 index 6ebb1222..00000000 Binary files a/src/getting_started/repl_1.png and /dev/null differ diff --git a/src/getting_started/repl_2.png b/src/getting_started/repl_2.png deleted file mode 100644 index d8d9e5b9..00000000 Binary files a/src/getting_started/repl_2.png and /dev/null differ diff --git a/src/getting_started/repl_3.png b/src/getting_started/repl_3.png deleted file mode 100644 index 4c3a36cd..00000000 Binary files a/src/getting_started/repl_3.png and /dev/null differ diff --git a/src/getting_started/repl_4.png b/src/getting_started/repl_4.png deleted file mode 100644 index 01817426..00000000 Binary files a/src/getting_started/repl_4.png and /dev/null differ diff --git a/src/getting_started/repl_4_voidmain.png b/src/getting_started/repl_4_voidmain.png deleted file mode 100644 index 10b12687..00000000 Binary files a/src/getting_started/repl_4_voidmain.png and /dev/null differ diff --git a/src/getting_started/repl_5.png b/src/getting_started/repl_5.png deleted file mode 100644 index d6d2c030..00000000 Binary files a/src/getting_started/repl_5.png and /dev/null differ diff --git a/src/getting_started/repl_5_voidmain.png b/src/getting_started/repl_5_voidmain.png deleted file mode 100644 index 598700ec..00000000 Binary files a/src/getting_started/repl_5_voidmain.png and /dev/null differ diff --git a/src/getting_started/repl_signup_0.png b/src/getting_started/repl_signup_0.png deleted file mode 100644 index cc0fec9d..00000000 Binary files a/src/getting_started/repl_signup_0.png and /dev/null differ diff --git a/src/getting_started/repl_signup_1.png b/src/getting_started/repl_signup_1.png deleted file mode 100644 index 1c0a0ec9..00000000 Binary files a/src/getting_started/repl_signup_1.png and /dev/null differ diff --git a/src/global_fields.md b/src/global_fields.md index 0da8b025..15b55a2b 100644 --- a/src/global_fields.md +++ b/src/global_fields.md @@ -1,5 +1,8 @@ # Global Fields + + + You can make field declarations outside of a class. This makes these fields "global" to the program. diff --git a/src/global_fields/challenges.md b/src/global_fields/challenges.md index 9358534c..bd60e726 100644 --- a/src/global_fields/challenges.md +++ b/src/global_fields/challenges.md @@ -1 +1,83 @@ # Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1 + +Write a method named `anothaOne`. Each time this method is +called it should return a number one larger than the last time +it was called. + +`anothaOne` should take no arguments. + +```java,editable +// CODE HERE + +int anothaOne() { + // CODE HERE +} + +void main() { + IO.println(anothaOne()); // 1 + IO.println(anothaOne()); // 2 + IO.println(anothaOne()); // 3 + IO.println(anothaOne()); // 4 + IO.println(anothaOne()); // 5 +} +``` + +## Challenge 2 + +Make a class named `DJKhaled`. If you ask DJ Khaled to `produceMusic` +some lyrics should be printed out but also `anothaOne` should be called at least 7 times.[^drake] + + +```java,editable +// CODE FROM LAST SECTION + +class DJKhaled { + void produceMusic() { + // CODE HERE + } +} + +void main() { + IO.println(anothaOne()); // 1 + var dj = new DJKhaled(); + dj.produceMusic(); + IO.println(anothaOne()); // 8+, at least +} +``` + +## Challenge 3 + +Make a method named `nextLyric`. It should take no arguments and return a `String`. +Each time it is called it should return a subsequent line from [Kendrick Lamar's 2025 Superbowl +Halftime Show](https://www.youtube.com/watch?v=KDorKy-13ak). + +You should be able to find a transcription of the lyrics [here](https://genius.com/Kendrick-lamar-and-nfl-super-bowl-lix-halftime-show-lyrics). + +Once all the lyrics are exhausted `nextLyric` should start returning `null`. + +```java,editable +// CODE HERE + +String nextLyric() { + // CODE HERE +} + +void main() { + for ( + String lyric = nextLyric(); + lyric != null; + lyric = nextLyric() + ) { + IO.println(lyric); + } +} +``` + +[^drake]: We are just going to have fun with the "anotha one" meme here, lets not think too hard about DJ Khaled's relationship with Drake. \ No newline at end of file diff --git a/src/global_fields/header.png b/src/global_fields/header.png new file mode 100644 index 00000000..248799a4 Binary files /dev/null and b/src/global_fields/header.png differ diff --git a/src/growable_arrays.md b/src/growable_arrays.md index 6f63844e..ad736d3b 100644 --- a/src/growable_arrays.md +++ b/src/growable_arrays.md @@ -1,5 +1,7 @@ # Growable Arrays + + Arrays are fixed size collections of elements. This means when we make an array that is 5 elements big it will always be 5 elements big. diff --git a/src/growable_arrays/challenges.md b/src/growable_arrays/challenges.md new file mode 100644 index 00000000..b7bef59d --- /dev/null +++ b/src/growable_arrays/challenges.md @@ -0,0 +1,27 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Make your own implementation of a growable array that holds `String`s and not +`int`s. + +Validate that `.size`, `.add`, `.get`, `.set`, etc. all work as you would expect. + +## Challenge 2. + +Go back to your calorie tracker program from that project. +Make use of a growable array of some kind to keep the full history +of calorie amounts entered by the user. + +## Challenge 3. + +When implementing the point of sale system project you may or may not have +gone down the path of discovering growable arrays for yourself. Either way +refactor that project to make use of a growable array to store the items +ordered for the purpose of making a receipt. + diff --git a/src/growable_arrays/header.png b/src/growable_arrays/header.png new file mode 100644 index 00000000..9a41a189 Binary files /dev/null and b/src/growable_arrays/header.png differ diff --git a/src/hardware.md b/src/hardware.md index a50384e7..0bfef985 100644 --- a/src/hardware.md +++ b/src/hardware.md @@ -1,4 +1,5 @@ # Hardware + That was a long stretch of Java, so I think you've earned a bit of a detour into general computing knowledge. diff --git a/src/hardware/header.png b/src/hardware/header.png new file mode 100644 index 00000000..d69100c5 Binary files /dev/null and b/src/hardware/header.png differ diff --git a/src/hash_maps.md b/src/hash_maps.md index ab36962f..013da259 100644 --- a/src/hash_maps.md +++ b/src/hash_maps.md @@ -1,5 +1,8 @@ # HashMap + + + Arrays and their growable cousin `ArrayList` store a sequence of elements. This is often enough, but if you want to find an element in an array you usually need to check every element one by one. diff --git a/src/hash_maps/challenges.md b/src/hash_maps/challenges.md new file mode 100644 index 00000000..077ffe74 --- /dev/null +++ b/src/hash_maps/challenges.md @@ -0,0 +1,145 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + + +Make a `Library` class. It should have four exposed methods + +- `add` which takes a `Book` and adds it to the library. +- `list` which returns an `ArrayList` of all the `Book`s in the library. +The `String`s in this list should be the [ISBN](https://en.wikipedia.org/wiki/ISBN) +of the book. +- `find` which takes a `String` representing the ISBN and returns a full `Book` record. +- `remove` which takes a `String` representing the ISBN and removes that `Book` from the +library. + +Use a `HashMap` for storing this info in the `Library`. + +```java,editable +record Book(String isbn, String title, String author) {} + +class Library { + // CODE HERE +} + +class Main { + void main() { + Library publicLibrary = new Library(); + publicLibrary.add(new Book("978-0-8041-3902-1", "The Martian", "Andy Weir")); + publicLibrary.add(new Book("978-0062060624", "The Song of Achilles", "Madeline Miller")); + + IO.println(publicLibrary.list()); + + Book b1 = publicLibrary.find("978-0062060624"); + IO.println(b1); // The Song of Achilles + + Book b2 = publicLibrary.find("123"); + IO.println(b2); // null + + publicLibrary.remove("978-0062060624"); + + Book b3 = publicLibrary.find("978-0062060624"); + IO.println(b3); // null + + IO.println(publicLibrary.list()); + } +} +``` + +## Challenge 2. + +Write a method which takes as an argument an `ArrayList` +and returns a `HashMap` where each key is +a `String` from the original `ArrayList` and each value is the number +of times it appeared in that `ArrayList`. + +```java,editable +class Main { + HashMap count(ArrayList words) { + // CODE HERE + } + + void main() { + ArrayList w = new ArrayList<>(); + w.add("duck"); + w.add("duck"); + w.add("duck"); + w.add("goose"); + w.add("duck"); + w.add("duck"); + w.add("duck"); + w.add("duck"); + w.add("duck"); + w.add("duck"); + w.add("duck"); + w.add("duck"); + w.add("goose"); + w.add("zebra"); + + // {duck=11,goose=2,zebra=1} + IO.println( + count(w) + ); + } +} +``` + +If you feel like this is hard to do with just `.put` and `.get`, you can +check for [other methods on HashMap that can help you](https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/util/HashMap.html). + +## Challenge 3. + +Without calling any methods on the `HashMap`, make it so that `map.get(person)` returns `null`. + +```java +class Person { + int age; + + Person(int age) { + this.age = age; + } + + @Override + public String toString() { + return "Person[age=" + age + "]"; + } + + @Override + public boolean equals(Object o) { + if (o instanceof Person p) { + return age == p.age; + } + else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash(age); + } +} + +class Main { + void main() { + var person = new Person("Patrocolus"); + var map = new HashMap(); + map.put(person, "Achilles"); + + // ------------- + + // CODE HERE + // Do not directly touch "map" + + // ------------- + + // Should output `null` + IO.println(map.get(person)); + } +} +``` \ No newline at end of file diff --git a/src/hash_maps/header.png b/src/hash_maps/header.png new file mode 100644 index 00000000..894dff91 Binary files /dev/null and b/src/hash_maps/header.png differ diff --git a/src/hash_maps/reference_based_identity.md b/src/hash_maps/reference_based_identity.md index c99d0dc5..a851e4eb 100644 --- a/src/hash_maps/reference_based_identity.md +++ b/src/hash_maps/reference_based_identity.md @@ -8,7 +8,8 @@ Instead of making buckets like `A-G` and `H-Z`, it will use ranges of numbers. T same though For classes you make yourself, their `hashCode` will be based on what we call an object's -"identity." This means that while different instances of a class might give the same (incomplete sentence) +"identity." This means that every individual instance of a class is likely to give you a different +value, regardless of if they hold identical fields. ```java diff --git a/src/inner_classes.md b/src/inner_classes.md index 7047c616..2132d922 100644 --- a/src/inner_classes.md +++ b/src/inner_classes.md @@ -1,5 +1,7 @@ # Inner Classes + + You can declare a class within another class. ```java,no_run diff --git a/src/inner_classes/challenges.md b/src/inner_classes/challenges.md new file mode 100644 index 00000000..d1f7f264 --- /dev/null +++ b/src/inner_classes/challenges.md @@ -0,0 +1,108 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +As written the code in `GameConsole.Controller` +looks at its own `isPoweredOn` field. + +Disambiguate the usage so that it is uses the `isPoweredOn` field from +the `GameConsole` instance wrapping it in its `status` method. + +```java,editable +class GameConsole { + boolean isPoweredOn; + + GameConsole() { + this.isPoweredOn = false; + } + + class Controller { + boolean isPoweredOn; + + Controller() { + this.isPoweredOn = false; + } + + String status() { + "Controller[" + + isPoweredOn ? "ON" : "OFF" + "] - GameConsole[" + + isPoweredOn ? "ON" : "OFF" + "]"; + } + } +} + +class Main { + void main() { + var console = new GameConsole(); + var controller = console.new Controller(); + + IO.println(controller.status()); + + console.isPoweredOn = true; + IO.println(controller.status()); + + controller.isPoweredOn = true; + IO.println(controller.status()); + + console.isPoweredOn = false; + IO.println(controller.status()); + } +} +``` + +## Challenge 2. + +Make the `Controller` class from the previous example a `static` inner class. +The `status` method should keep the same behavior. This means you will need +to explicitly pass an instance of `GameConsole` to the constructor of `Controller` +and store it in a field. + +## Challenge 3. + +Successfully make an instance of `Fly` from the `Main` class. + +```java,editable +class OldLady { + class Horse { + class Cow { + class Goat { + class Dog { + class Cat { + class Bird { + class Spider { + class Fly { + Fly() { + IO.println("She's dead, of course"); + } + } + } + } + } + } + } + } + } +} + +class Main { + Fly f = /* CODE HERE */; + IO.println(f); +} +``` + +## Challenge 4. + +Go back to some of the programs you wrote entirely within the anonymous +main class. Turn that main class into a named class. + +First make sure your program works the same as it did. Then +go through all the inner classes in your program and mark them +as many of them as you can `static`. + +Which ones can't be trivially made `static`? Note what state they +access in the enclosing instance. \ No newline at end of file diff --git a/src/inner_classes/header.png b/src/inner_classes/header.png new file mode 100644 index 00000000..2888653e Binary files /dev/null and b/src/inner_classes/header.png differ diff --git a/src/instance_methods.md b/src/instance_methods.md index bd247cb0..08dc2c1c 100644 --- a/src/instance_methods.md +++ b/src/instance_methods.md @@ -1,5 +1,8 @@ # Instance Methods + + + In addition to having fields, classes can also have their own method definitions. diff --git a/src/instance_methods/derived_values.md b/src/instance_methods/derived_values.md index 71143169..41b869e0 100644 --- a/src/instance_methods/derived_values.md +++ b/src/instance_methods/derived_values.md @@ -21,4 +21,24 @@ void main() { } ``` -Which is useful for situations like where you store someones first and last name \ No newline at end of file +Which is useful for situations like where you store someones first and last name +but need to ask "what is their full name?" + +```java +class Elmo { + String firstName; + String lastName; + + String fullName() { + return firstName + " " + lastName; + } +} + +void main() { + Elmo elmo = new Elmo(); + elmo.firstName = "Elmo"; + elmo.lastName = "Furchester"; + + IO.println("Elmo's full name is " + elmo.fullName()); +} +``` \ No newline at end of file diff --git a/src/instance_methods/header.png b/src/instance_methods/header.png new file mode 100644 index 00000000..90623014 Binary files /dev/null and b/src/instance_methods/header.png differ diff --git a/src/integer_ii/integer_to_a_base_16_string.md b/src/integer_ii/integer_to_a_base_16_string.md deleted file mode 100644 index f609cba6..00000000 --- a/src/integer_ii/integer_to_a_base_16_string.md +++ /dev/null @@ -1 +0,0 @@ -# Integer to a Base 16 String diff --git a/src/integers.md b/src/integers.md index 92087ebd..4f274145 100644 --- a/src/integers.md +++ b/src/integers.md @@ -1,5 +1,8 @@ # Integers + + + [An integer is any number in the set `{ ..., -2, -1, 0, 1, 2, ... }.`](https://www.khanacademy.org/math/cc-sixth-grade-math/cc-6th-expressions-and-variables/whole-numbers-integers/a/whole-numbers-integers) ```java @@ -7,3 +10,4 @@ int x = 1; int y = 8; int z = -4; ``` + diff --git a/src/integers/chained_comparisons.md b/src/integers/chained_comparisons.md index 334e0104..f73d44ee 100644 --- a/src/integers/chained_comparisons.md +++ b/src/integers/chained_comparisons.md @@ -8,7 +8,7 @@ in the middle of both operators like so. 0 < x < 5 ``` -This does not work in Java. In order to "chain" comparisons like this, you should combine +This does not work in Java. In order to "chain" comparisons like this, you have to combine the results of comparisons using the `&&` operator. ```java,no_run diff --git a/src/integers/header.png b/src/integers/header.png new file mode 100644 index 00000000..c2380181 Binary files /dev/null and b/src/integers/header.png differ diff --git a/src/integers/limits.md b/src/integers/limits.md index 8ce95567..413130a8 100644 --- a/src/integers/limits.md +++ b/src/integers/limits.md @@ -32,5 +32,8 @@ IO.println(beyondLimit); ~} ``` +When a value loops around because it got too big we call that "overflow." When it +loops around because it got too small we call that "underflow." + There are other types which can represent a larger range of integers, as well as types which do not have any limits, but for now `int` is the only one you will need. diff --git a/src/integers_ii.md b/src/integers_ii.md index 47bc3fdf..2bc833e8 100644 --- a/src/integers_ii.md +++ b/src/integers_ii.md @@ -1,5 +1,7 @@ # Integers II + + Integers never stop being relevant and, now that we have covered static methods, we are ready to cover some of the more useful ones that exist for working with integers. \ No newline at end of file diff --git a/src/integers_ii/challenges.md b/src/integers_ii/challenges.md new file mode 100644 index 00000000..0361a932 --- /dev/null +++ b/src/integers_ii/challenges.md @@ -0,0 +1,52 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +RGB Colors can be represented as base 16 numbers. + +The following numbers represent colors. Print them out in base 16 +to figure out what colors they represent. Rename the variables to be +the color names. + +You should be able to just look up the hex string. + +```java,editable +class Main { + void main() { + int colorA = 255; + int colorB = 65280; + int colorC = 16711935; + int colorD = 16776960; + + // CODE HERE + } +} +``` + +## Challenge 2. + +Rewrite the integer literals in the code from the previous challenge +to be base 16 integer literals. + +## Challenge 3. + +Add underscores in the following integer literals in the same places +you would write commas in regular math (every 3 digits 13,550,145). + +```java,editable +class Main { + void main() { + int a = 515326326; + int b = 32523522; + int c = 1221415133; + IO.println(a); + IO.println(b); + IO.println(c); + } +} +``` \ No newline at end of file diff --git a/src/integers_ii/header.png b/src/integers_ii/header.png new file mode 100644 index 00000000..9ea7e3b7 Binary files /dev/null and b/src/integers_ii/header.png differ diff --git a/src/integers_ii/integer_from_a_string.md b/src/integers_ii/integer_from_a_string.md index 39b40281..f76e3a9b 100644 --- a/src/integers_ii/integer_from_a_string.md +++ b/src/integers_ii/integer_from_a_string.md @@ -2,7 +2,7 @@ If you have a `String` which contains text that can be interpreted as an integer you can convert it to an `int` using the `parseInt` -static method on `Integer`. +static method on `Integer`.[^already] ```java ~void main() { @@ -41,3 +41,5 @@ try { ~} ``` +[^already]: You should actually already know this. I just never explained explicitly that parseInt was a static method or showed that you could catch _only_ the `NumberFormatException`. In my defense, `IO.readln` +at one point came far later in the book. \ No newline at end of file diff --git a/src/interfaces.md b/src/interfaces.md index 456e2230..5ad33d76 100644 --- a/src/interfaces.md +++ b/src/interfaces.md @@ -1,5 +1,8 @@ # Interfaces + + + Generics let you write code that doesn't care what is different between different things - you would accept a `String` or an `Integer`, doesn't matter what is diferent between them. @@ -8,7 +11,7 @@ Interfaces do a related thing. They let you write code that takes advantage of commonalities. ```java -inteface Dog { +interface Dog { void bark(); } ``` diff --git a/src/interfaces/challenges.md b/src/interfaces/challenges.md new file mode 100644 index 00000000..1ebf6503 --- /dev/null +++ b/src/interfaces/challenges.md @@ -0,0 +1,111 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Declare an interface named `DoubleArray` which requires +two methods of classes that implementing classes: `length` and `get`. + +These methods should be specified to work the same as how `[]` and `.length` +work on a `double[]`. + +```java,no_run +interface DoubleArray { + // CODE HERE +} +``` + +## Challenge 2. + +Make a class that implements your `DoubleArray` interface +using a `double[]` in a field to perform all the operations. + +```java,editable +interface DoubleArray { + // CODE FROM PREVIOUS CHALLENGE +} + +class RealDoubleArray /* CODE HERE */ { + // CODE HERE +} + +class Main { + void main() { + DoubleArray arr = new RealDoubleArray(new double[] { + 1.0, 1.5, 2.0, 2.5, 3.0 + }); + + for (int i = 0; i < arr.length(); i++) { + IO.println("Got double value: " + arr.get(i)); + } + } +} +``` +## Challenge 3. + +Make a second class that implements `DoubleArray` but have this one +be backed by an `int[]` and perform widening conversions when returning values. + +```java,editable +interface DoubleArray { + // CODE FROM PREVIOUS CHALLENGE +} + +class FauxDoubleArray /* CODE HERE */ { + // CODE HERE +} + +class Main { + void main() { + DoubleArray arr = new FauxDoubleArray(new int[] { + 1, 2, 3, 4, 5 + }); + + for (int i = 0; i < arr.length(); i++) { + IO.println("Got double value: " + arr.get(i)); + } + } +} +``` + +## Challenge 4. + +Make an implementation of the following `Tarot` interface for [each tarot card +featured in JoJo's Bizzare Adventure](https://jojowiki.com/Tarot_Cards). + +```java +interface Tarot { + String symbolism(); + + String standUser(); +} +``` + +## Challenge 5. + +Make a method named `promptGeneric` which can prompt the user for information +but, based on if what they typed is properly interpretable, can reprompt them. + +As part of this make a `Parser` interface and at least two implementations: `IntParser` +and `DoubleParser`. + +```java +// CODE HERE + +class Main { + // CODE HERE + + void main() { + int x = promptGeneric( + "Give me an x: ", new IntParser() + ); + double y = promptGeneric( + "Give me a floating point y: ", new DoubleParser() + ); + } +} +``` \ No newline at end of file diff --git a/src/interfaces/header.png b/src/interfaces/header.png new file mode 100644 index 00000000..0b46b5aa Binary files /dev/null and b/src/interfaces/header.png differ diff --git a/src/interfaces_ii.md b/src/interfaces_ii.md index c38bc225..d153bf43 100644 --- a/src/interfaces_ii.md +++ b/src/interfaces_ii.md @@ -1,5 +1,7 @@ # Interfaces II + + Interfaces let you describe a common set of methods shared by different implementing classes. diff --git a/src/interfaces_ii/challenges.md b/src/interfaces_ii/challenges.md new file mode 100644 index 00000000..dd4294c4 --- /dev/null +++ b/src/interfaces_ii/challenges.md @@ -0,0 +1,100 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Create an interface named `TabbyCat`. It should +extend the provided `Cat` interface and provide +a `lounge` method along with a default implementation of that +method. + +```java +interface Cat { + void purr(); +} + +// CODE HERE + +class Garfield implements TabbyCat { + @Override + public void purr() { + IO.println("mmmm lasagna"); + } +} + +class Main { + void main() { + TabbyCat c = new Garfield(); + // Should come in via a default method. + c.lounge(); + } +} +``` + +## Challenge 2. + +Add a static field to the `Cat` interface +which indicates the healthy amount of lasagna for a cat to +consume.[^zero] + +```java +interface Cat { + void purr(); +} + +class Main { + void main() { + // 0 + IO.println(Cat.HEALTHY_AMOUNT_OF_LASAGNA); + } +} +``` + +## Challenge 3. + +Put a static method on the `Cat` interface named `garfield" +which returns an instance of `TabbyCat`. + +```java,no_run +public interface Cat { + // CODE HERE +} +``` + +```java,no_run +public interface TabbyCat extends Cat { + // CODE FROM PREVIOUS CHALLENGES +} +``` + +```java,no_run +class Garfield implements TabbyCat { + @Override + public void purr() { + IO.println("mmmm lasagna"); + } +} +``` + +```java +public class Main { + void main() { + TabbyCat tc = Cat.garfield(); + tc.lounge(); + } +} +``` + +Note that this gives you a way to expose a `Garfield` instance +to other packages, even if the `Garfield` class itself +is `non-public`.[^unrunnable] + +[^zero]: Zero, it can literally kill them. + +[^unrunnable]: Apologies for the inconvenience. To make the point about +package visibility I had to make the code in browser non runnable +or editable. You should be able to manage at this point though. \ No newline at end of file diff --git a/src/interfaces_ii/header.png b/src/interfaces_ii/header.png new file mode 100644 index 00000000..5368221d Binary files /dev/null and b/src/interfaces_ii/header.png differ diff --git a/src/just.md b/src/just.md new file mode 100644 index 00000000..63830999 --- /dev/null +++ b/src/just.md @@ -0,0 +1,36 @@ +# just + + + + +As you might be noticing, commands in the terminal can get quite long. + +Not only are you liable to make a mistake typing out `java --module-path a:bunch:of:files --add-modules ALL-MODULE-PATH src/Main.java` for the 20th time, you are also just going to get annoyed doing so.[^nature] + +To remember what commands to run to do certain tasks I recommend using a tool called "[just](https://github.com/casey/just)." + +```justfile,no_run +help: + just --list + +clean: + rm -rf output + +compile: clean + javac \ + -d output/javac \ + --module-source-path "./*/src" \ + --module dan.da.dan + +package: compile + jar \ + --create \ + --file output/jar/dan.da.dan.jar \ + -C output/javac/dan.da.dan . +``` + +This section is ultimately optional, but you will quickly see a need for _something_ +that helps you remember commands. + +[^nature]: Generally when something is "your job" or "the right way" you do need to just suck it up +and do the manual labor. But there are limits to this, human nature being what it is. \ No newline at end of file diff --git a/src/just/challenges.md b/src/just/challenges.md new file mode 100644 index 00000000..2d92f1dc --- /dev/null +++ b/src/just/challenges.md @@ -0,0 +1,34 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Make a Justfile with a recipe named `hello` that runs `echo "hello"`. + +Test it by running `just hello`. + +## Challenge 2. + +Make a `run` recipe that launches one of your programs. + +## Challenge 3. + +Take the commands you used to compile one of your projects +and put them into a `Justfile`. + +Ensure that `just compile` will compile your code and `just clean` will +delete any output directories. + +## Challenge 4. + +Expand the `Justfile` from the previous challenge to also package +your code into a JAR. + +## Challenge 5. + +Expand the `Justfile` from the previous challenge to compile +and package multiple modules, not just one. \ No newline at end of file diff --git a/src/just/dependencies.md b/src/just/dependencies.md new file mode 100644 index 00000000..6d89d977 --- /dev/null +++ b/src/just/dependencies.md @@ -0,0 +1,34 @@ +# Dependencies + +If there are recipes you want to run before other recipies +you specify them after the `:` of the recipe declaration. + +This can be useful for things like always removing an output +directory before compiling or packaging. + +```justfile,no_run +help: + just --list + +clean: + rm -rf output + +compile: clean + javac \ + -d output/javac \ + --module-source-path "./*/src" \ + --module dan.da.dan + +package: compile + jar \ + --create \ + --file output/jar/dan.da.dan.jar \ + -C output/javac/dan.da.dan . +``` + +So in the example above, all the commands in the "`compile`" recipe +run before the commands in the "`package`" recipe. Before "`compile`" +the commands in the "`clean`" recipe run. + +This is useful for making these sorts of "chains" of commands, but isn't strictly required. + diff --git a/src/just/documentation_comments.md b/src/just/documentation_comments.md new file mode 100644 index 00000000..41f6f11a --- /dev/null +++ b/src/just/documentation_comments.md @@ -0,0 +1,40 @@ +# Documentation Comments + +You can describe what a recipe does by putting a `#` followed by the description +above the recipe + +```justfile,no_run +# Lists available recipes +help: + just --list + +# Cleans build output +clean: + rm -rf output + +# Compiles the code +compile: clean + javac \ + -d output/javac \ + --module-source-path "./*/src" \ + --module dan.da.dan + +# Packages the code +package: compile + jar \ + --create \ + --file output/jar/dan.da.dan.jar \ + -C output/javac/dan.da.dan . +``` + +These documentation comments will appear alongside the recipe name with `just --list`, which +is the command our `help` recipe runs. + +```text,no_run +$ just +Available recipes: + clean # Cleans build output + compile # Compiles the code + help # Lists available recipes + package # Packages the code +``` \ No newline at end of file diff --git a/src/just/further_reading.md b/src/just/further_reading.md new file mode 100644 index 00000000..6ec51755 --- /dev/null +++ b/src/just/further_reading.md @@ -0,0 +1,9 @@ +# Further Reading + +What has been covered so far should be enough for our stated goal of +remembering what commands to run to do stuff. + +But there is more to `just` than that. If you are interested, you should start +by reading the documentation put out by the people who develop it. + +[https://just.systems/man/en/](https://just.systems/man/en/) \ No newline at end of file diff --git a/src/just/header.png b/src/just/header.png new file mode 100644 index 00000000..abbfe973 Binary files /dev/null and b/src/just/header.png differ diff --git a/src/just/installation.md b/src/just/installation.md new file mode 100644 index 00000000..fa60c05d --- /dev/null +++ b/src/just/installation.md @@ -0,0 +1,9 @@ +# Installation + +As you might have noted, `just` is not a tool that comes with Java. + +This means that you will need to install it yourself. The process +for doing so differs based on what kind of computer you have but, +if you've made it this far, you should have it in you to figure it out. + +You can find instructions from the authors starting at [https://just.systems/man/en/prerequisites.html](https://just.systems/man/en/prerequisites.html). \ No newline at end of file diff --git a/src/just/justfile.md b/src/just/justfile.md new file mode 100644 index 00000000..cef47437 --- /dev/null +++ b/src/just/justfile.md @@ -0,0 +1,24 @@ +# Justfile + +To use `just`, first make a file named `Justfile` and put it at the top of your project. + +```text,no_run +project/ + dan.da.dan/ + src/ + module-info.java + characters/ + MoMo.java + JiJi.java + Justfile +``` + +In this file put the following contents. + +```justfile,no_run +help: + just --list +``` + +This makes it so that if you run `just` or `just help` +it will list the "recipes" available for the project. \ No newline at end of file diff --git a/src/just/recipes.md b/src/just/recipes.md new file mode 100644 index 00000000..226e6216 --- /dev/null +++ b/src/just/recipes.md @@ -0,0 +1,37 @@ +# Recipes + +A Justfile contains multiple "recipes." +These are lists of commands to run when you type `just `. + +```justfile,no_run +help: + just --list + +compile: + rm -rf output + javac \ + -d output \ + --module-source-path "./*/src" \ + --module dan.da.dan +``` + +So in the example above `just compile` will first run `rm -rf output` followed by `javac <...>`. + +The first recipe in a file is run by default when you type `just`. This is why we put +the `help` recipe first in the file - it is convenient. + +```text,no_run +$ just +just --list +Available recipes: + compile + help +$ just help +just --list +Available recipes: + compile + help +``` + + + diff --git a/src/lambdas.md b/src/lambdas.md index fa354f9d..4a5d3aaa 100644 --- a/src/lambdas.md +++ b/src/lambdas.md @@ -1,2 +1,37 @@ # Lambdas + + + +Making an implementation of `interface`s that +only require one method turns out to be a common +task for a Java developer. + +```java,no_run +interface Band { + void playHitSong(); +} + +class Starcadian implements Bank { + @Override + public void playHitSong() { + IO.println("ultralove"); + } +} +``` + +As such there is a mechanism called "lambdas" +which lowers the effort required to do so. + +```java +interface Band { + void playHitSong(); +} + +class Main { + void main() { + Band starcadian = () -> IO.println("ultralove"); + starcadian.playHitSong(); + } +} +``` diff --git "a/src/lambdas/Screenshot 2025-08-25 at 7.04.29\342\200\257AM.png" "b/src/lambdas/Screenshot 2025-08-25 at 7.04.29\342\200\257AM.png" new file mode 100644 index 00000000..4f8cffe1 Binary files /dev/null and "b/src/lambdas/Screenshot 2025-08-25 at 7.04.29\342\200\257AM.png" differ diff --git a/src/lambdas/arguments.md b/src/lambdas/arguments.md new file mode 100644 index 00000000..ffcdba21 --- /dev/null +++ b/src/lambdas/arguments.md @@ -0,0 +1,70 @@ +# Arguments + +If the method on the functional interface takes arguments +you can include those in the parentheses before the `->`. + +```java +@FunctionalInterface +interface Singer { + void sing(String title, double volume); +} + +class Main { + void main() { + Singer woodkid = (String title, double volume) -> { + IO.println( + title + + " by woodkid is now " + + (volume > 10 ? "BLASTING" : "playing" + ) + ); + }; + + // AC Revelations wasn't the best game + // but that trailer was awesome. + woodkid.sing("Iron", 11); + } +} +``` + +If it won't lead to any ambiguity Java also allows you to omit the types +from the argument list. + +```java +@FunctionalInterface +interface Singer { + void sing(String title, double volume); +} + +class Main { + void main() { + Singer twrp = (title, volume) -> { + IO.println( + title + + " by twrp is now " + + (volume > 10 ? "BLASTING" : "playing" + ) + ); + }; + + woodkid.sing("Pets", 7); + } +} +``` + +Further, if there is only one argument you may omit even the `()`. + +```java +@FunctionalInterface +interface Conductor { + void conduct(String tempo); +} + +class Main { + void main() { + Conductor jkSimmons = tempo -> IO.println("Not my tempo.") + + jkSimmons.conduct("4/4"); + } +} +``` \ No newline at end of file diff --git a/src/lambdas/built_in_functional_interfaces.md b/src/lambdas/built_in_functional_interfaces.md new file mode 100644 index 00000000..0a89e277 --- /dev/null +++ b/src/lambdas/built_in_functional_interfaces.md @@ -0,0 +1,58 @@ +# Built-In Functional Interfaces + +Java comes with many functional interfaces as part of its standard library. + +You are under no obligation to use any of these. They are convenient because +they are already at hand, but they tend to have very "generic" +names. It is often better to make your own. That being said: + +One functional interface that comes with Java is `Runnable`[^javaone]. +This describes a method that takes no arguments and returns no values. + +```java,no_run +@FunctionalInterface +public interface Runnable { + void run(); +} +``` + +Another is `Function`. This is a generic interface that describes +a function taking only one argument. `Function` is notable +in that it is an example of a functional interface that contains +both default methods and static methods. + +```java,no_run +@FunctionalInterface +public interface Function { + R apply(T t); + + default Function compose(Function before) { + Objects.requireNonNull(before); + return (V v) -> apply(before.apply(v)); + } + + default Function andThen(Function after) { + Objects.requireNonNull(after); + return (T t) -> after.apply(apply(t)); + } + + static Function identity() { + return t -> t; + } +} +``` + +Then there is `Consumer` for something that takes an argument and returns something. `BiFunction` +and `BiConsumer` for things that take two arguments, `Supplier` for something +that takes nothing and returns a value... and so on. + +Really the important thing is just to know that some of these exist. There are only +slim benefits to using them over interfaces you make yourself. Lambdas and method +references work the same for everyone. + + +[^javaone]: I think it is really neat that Runnable came with Java 1.0 - nearly two +decades before lambdas were in the language - and yet works perfectly fine with them. +That's design bay-bee[^powers]. + +[^powers]: Read that in the Austin Powers voice. \ No newline at end of file diff --git a/src/lambdas/challenges.md b/src/lambdas/challenges.md new file mode 100644 index 00000000..4af22ee0 --- /dev/null +++ b/src/lambdas/challenges.md @@ -0,0 +1,125 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Replace the `Sunflower` class with a lambda expression. + +```java,editable +@FunctionalInterface +interface Flower { + void bloom(); +} + +class Sunflower implements Flower { + @Override + public void bloom() { + IO.println("bright yellow flower."); + } +} + +class Main { + void bloomField(Flower flower) { + for (int i = 0; i < 15; i++) { + flower.bloom(); + } + } + + void main() { + Flower sunflower = new Sunflower(); + bloomField(sunflower); + } +} +``` + +## Challenge 2. + +Add a new method to the `Flower` interface above while +keeping the `@FunctionalInterface` annotation. What error does Java +give you? + +What errors do you see if you remove the annotation but still have +lambdas in your code? + +## Challenge 3. + +Change the lambda expression you used for the `Flower` interface +to a method reference. + +## Challenge 4. + +One of these snippets of code will throw a `NullPointerException`. The other +will not. Which do you think will do which? Why do you think that is? + +```java +@FunctionalInterface +interface Screamer { + void scream(); +} + +class Lemon { + void grab() { + IO.println("UNACCEPTABLE!"); + } +} + +class Main { + void main() { + Lemon lemon = null; + Screamer s = lemon::grab; + } +} +``` + +```java +@FunctionalInterface +interface Screamer { + void scream(); +} + +class Lemon { + void grab() { + IO.println("UNACCEPTABLE!"); + } +} + +class Main { + void main() { + Lemon lemon = null; + Screamer s = () -> lemon.grab(); + } +} +``` +## Challenge 4. + +You should have done this challenge before: + +> Make a method named `promptGeneric` which can prompt the user for information +> but, based on if what they typed is properly interpretable, can reprompt them. +> +> As part of this make a `Parser` interface and at least two implementations: `IntParser` +> and `DoubleParser`. + +If not, do so now. Then in either case replace the usages of `IntParser` and `DoubleParser` +with method references. + +```java +// CODE HERE + +class Main { + // CODE HERE + + void main() { + int x = promptGeneric( + "Give me an x: ", new IntParser() + ); + double y = promptGeneric( + "Give me a floating point y: ", new DoubleParser() + ); + } +} +``` \ No newline at end of file diff --git a/src/lambdas/checked_exceptions.md b/src/lambdas/checked_exceptions.md new file mode 100644 index 00000000..9f645363 --- /dev/null +++ b/src/lambdas/checked_exceptions.md @@ -0,0 +1,99 @@ +# Checked Exceptions + +One thing that might catch you off guard is how lambdas +and method references interact with checked exceptions. + +Normally if the implementation of a functional interface throws +an exception that is allowed. + +```java +class Main { + void main() { + Function parseObject + = Integer::parseInt; + + int x = parseObject.apply("1657"); + + IO.println(x); + } +} +``` + +But if the implementation throws a checked exception Java +will complain. + +This is because checked exceptions need to be declared in a method signature. +If the functional interface you are making an implementation +does not declare a checked exception is thrown in its single method + +```java,does_not_compile +import module java.base; + +@FunctionalInterface +interface Loader { + T load(); +} + +class Main { + void main() throws IOException { + Files.writeString(Path.of("one.txt"), "One and only one"); + // Files.readString can throw an IOException and that is checked + Loader loader = () -> Files.readString(Path.of("one.txt")); + IO.println(loader.load()); + } +} +``` + +One solution is to edit the functional interface such that it declares +the thrown exception. + + +```java +import module java.base; + +@FunctionalInterface +interface Loader { + // If we add this throws it will be fine. + T load() throws IOException; +} + +class Main { + void main() throws IOException { + Files.writeString(Path.of("one.txt"), "One and only one"); + Loader loader = () -> Files.readString(Path.of("one.txt")); + IO.println(loader.load()); + } +} +``` + +The other solution is to catch and rethrow any exceptions as unchecked. + + +```java,does_not_compile +import module java.base; + +@FunctionalInterface +interface Loader { + T load(); +} + +class Main { + void main() throws IOException { + Files.writeString(Path.of("one.txt"), "One and only one"); + Loader loader = () -> { + try { + return Files.readString(Path.of("one.txt")); + } catch (IOException e) { + // No issue throwing unchecked exceptions + throw new UncheckedIOException(e); + } + }; + IO.println(loader.load()); + } +} +``` + +Which of those two solutions you pick will be context dependent. +If you don't control the implementation of the functional interface +(such as with the built-in `Runnable`, `Function`, `Supplier`, etc.) +your best option is the second one. \ No newline at end of file diff --git a/src/lambdas/function.md b/src/lambdas/function.md new file mode 100644 index 00000000..331a5644 --- /dev/null +++ b/src/lambdas/function.md @@ -0,0 +1 @@ +# Function diff --git a/src/lambdas/functional_interfaces.md b/src/lambdas/functional_interfaces.md index 0b109c77..23641d78 100644 --- a/src/lambdas/functional_interfaces.md +++ b/src/lambdas/functional_interfaces.md @@ -3,10 +3,10 @@ If an interface has only a one method that needs to be implemented we would call that a "functional interface."[^SAM] ```java -interface Runner { +interface Band { // Only one method to implement // "single abstract method" - void run(); + void playHitSong(); } ``` @@ -14,17 +14,22 @@ interface Runner { Any other methods on the interface must be `default` or `static`. ```java -interface Runner { - void run(); +interface Band { + void playHitSong(); // Neither the default or static method are considered - default void runTwice() { - this.run(); - this.run(); + default void encore() { + this.playHitSong(); + this.playHitSong(); } - static double milesToKilometers(double distance) { - return distance * 1.609; + static int turnDial(int level) { + if (level == 10) { + return 11; + } + else { + return level; + } } } ``` diff --git a/src/lambdas/header.png b/src/lambdas/header.png new file mode 100644 index 00000000..066402cb Binary files /dev/null and b/src/lambdas/header.png differ diff --git a/src/lambdas/inference.md b/src/lambdas/inference.md new file mode 100644 index 00000000..209db3b9 --- /dev/null +++ b/src/lambdas/inference.md @@ -0,0 +1,33 @@ +# Inference + +If Java cannot figure out exactly what functional interface you want +to use it will not allow you to use a lambda or a method reference. + +```java,does_not_compile +class Main { + void main() { + // Does not know what type the "var" should be + var ride = () -> IO.println("cruisin'"); + } +} +``` + +To resolve this you need to give Java a hint as to what +interface it should resolve to. In addition to simply +giving explicit types to variables and fields +you can do this by "casting" the expression +to a functional interface. + +```java +@FunctionalInterface +interface Trip { + void takeOff(); +} + +class Main { + void main() { + var ride = (Trip) () -> IO.println("cruisin'"); + ride.takeOff(); + } +} +``` \ No newline at end of file diff --git a/src/lambdas/lambda_expressions.md b/src/lambdas/lambda_expressions.md index b2f3cf36..39b47a42 100644 --- a/src/lambdas/lambda_expressions.md +++ b/src/lambdas/lambda_expressions.md @@ -1 +1,39 @@ # Lambda Expressions + +To make an implementation of a functional interface +you can use a "lambda expression." + +If the method on the functional interface +takes no arguments, you write `() ->` followed +by the code to run when that method is called. + +```java +@FunctionalInterface +interface Band { + void playHitSong(); +} + +class Main { + void main() { + Band twrp = () -> { + IO.println("Got no commitments today"); + IO.println("No work all play"); + } + } +} +``` + +Just like with `switch` if there is only one line to run you may omit the `{}`s. + +```java +@FunctionalInterface +interface Band { + void playHitSong(); +} + +class Main { + void main() { + Band theProtomen = () -> IO.println("Light up the Night!"); + } +} +``` \ No newline at end of file diff --git a/src/lambdas/method_references.md b/src/lambdas/method_references.md index 987e7b43..25d0914b 100644 --- a/src/lambdas/method_references.md +++ b/src/lambdas/method_references.md @@ -1 +1,96 @@ # Method References + +Often it will make sense to separate the code +for a lambda into its own method. + +```java +interface Drummer { + void drum(); +} + +class Main { + void doDrum() { + IO.println("ratatatatat"); + IO.println("crash!"); + IO.println("crash!"); + IO.println("ratatatatat"); + } + + void main() { + Drummer drummer = () -> drum(); + drummer.drum(); + } +} +``` + +When you have a method that matches[^exactly] the method required by a functional interface +you can use a "method reference." + +For instance methods this will look like `instance::methodName`. So inside a method +this can be `this::methodName` but you can also have a `variableName::methodName` + +```java +interface Drummer { + void drum(); +} + +class Main { + void doDrum() { + IO.println("ratatatatat"); + IO.println("crash!"); + IO.println("crash!"); + IO.println("ratatatatat"); + } + + void main() { + Drummer drummer = this::doDrum; + drummer.drum(); + + Runnable run = drummer::drum; + } +} +``` + +For static methods it will look like `ClassName::doDrum`. + +```java +interface Drummer { + void drum(); +} + +class Main { + static void doDrum() { + IO.println("ratatatatat"); + IO.println("crash!"); + IO.println("crash!"); + IO.println("ratatatatat"); + } + + void main() { + Drummer drummer = Drummer::doDrum; + drummer.drum(); + } +} +``` + +And finally for instance methods where the the functional interface's method takes the instance +explicitly as an argument it will look the same as a static method reference. + +```java +interface StringTransformer { + String transform(String s); +} + +class Main { + void main() { + // Instance method, but looks like a reference + // to a static method that just so happens to + // take a String as a first argument + StringTransformer t = String::toUpperCase; + IO.println(t.transform("ratatatatat")); + } +} +``` + + +[^exactly]: It doesn't have to match _exactly_ but describing the exact matching mechanism isn't that important. Play it by ear. \ No newline at end of file diff --git a/src/lambdas/return.md b/src/lambdas/return.md new file mode 100644 index 00000000..75c5a70b --- /dev/null +++ b/src/lambdas/return.md @@ -0,0 +1,56 @@ +# Return + + +If the method on the functional interface has a return value +that is not `void` you can return a value from a lambda +the same as from a method. + +This will not return from any enclosing method. + +```java +@FunctionalInterface +interface Farmer { + String farm(String item); +} + +class Main { + void main() { + Farmer jeremyClarkson = item -> { + if (Math.random() < 0.1) { + return "failure"; + } + else { + return item; + } + }; + + IO.println(jeremyClarkson.farm("potato")); + IO.println(jeremyClarkson.farm("sheep")); + } +} +``` + +If the value is returned by a single expression you +do not need `return` or `{}`. + +```java +@FunctionalInterface +interface Farmer { + String farm(String item); +} + +class Main { + void main() { + Farmer caleb = item -> item; + + IO.println(caleb.farm("potato")); + IO.println(caleb.farm("sheep")); + + caleb = item -> item.toUpperCase(); + + IO.println(caleb.farm("potato")); + IO.println(caleb.farm("sheep")); + } +} +``` + diff --git a/src/lambdas/runnable.md b/src/lambdas/runnable.md new file mode 100644 index 00000000..69f7c256 --- /dev/null +++ b/src/lambdas/runnable.md @@ -0,0 +1 @@ +# Runnable diff --git a/src/loops.md b/src/loops.md index 69e42d6f..51e198a9 100644 --- a/src/loops.md +++ b/src/loops.md @@ -1,5 +1,8 @@ # Loops + + + `if` and `else` let you write programs which can take branching paths, but they will still run from the beginning to the end. diff --git a/src/loops/header.png b/src/loops/header.png new file mode 100644 index 00000000..a2463545 Binary files /dev/null and b/src/loops/header.png differ diff --git a/src/loops_ii.md b/src/loops_ii.md index 7557a903..be296f85 100644 --- a/src/loops_ii.md +++ b/src/loops_ii.md @@ -1,5 +1,8 @@ # Loops II + + + `while` loops are enough to make any looping logic that you might want, but they aren't the only kind of loops you will see. diff --git a/src/loops_ii/header.png b/src/loops_ii/header.png new file mode 100644 index 00000000..c630b54c Binary files /dev/null and b/src/loops_ii/header.png differ diff --git a/src/loops_iii.md b/src/loops_iii.md index 0c573cce..335c7b9c 100644 --- a/src/loops_iii.md +++ b/src/loops_iii.md @@ -1,5 +1,8 @@ # Loops III + + + Counting indexes of elements is, while effective, a tad exhausting to do every time you want to loop through something. diff --git a/src/loops_iii/challenges.md b/src/loops_iii/challenges.md new file mode 100644 index 00000000..6f25d12d --- /dev/null +++ b/src/loops_iii/challenges.md @@ -0,0 +1,103 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Make your own class named `OneToTen` which implements `Iterable` +and will yield the numbers from `1` to `10`. + +This will require making a class which implements `Iterator` as well. + +```java,editable +// CODE HERE + +class OneToTen implements Iterable { + // CODE HERE +} + +class Main { + void main() { + var oneToTen = new OneToTen(); + + // Should output + // 1 2 3 4 5 6 7 8 9 10 + // twice + for (int i : oneToTen) { + IO.print(i); + IO.print(" ") + } + IO.println(); + + // If it only happens once, you might be + // mistaken about the difference between + // Iterable and Iterator + for (int i : oneToTen) { + IO.print(i); + IO.print(" ") + } + IO.println(); + } +} +``` + +## Challenge 2. + +Make a class named `StringIterable` which implements `Iterable`. + +Its constructor should take a `String` and it should let you iterate over +each character. + +```java,editable +// CODE HERE + +class Main { + void main() { + var stringIterable = new StringIterable("abc"); + + // Should output + // + // a + // b + // c + // ------- + // a + // b + // c + for (char c : stringIterable) { + IO.println(c); + } + IO.println("-------"); + for (char c : stringIterable) { + IO.println(c); + } + } +} +``` + +## Challenge 3. + +The following code will crash with a `ConcurrentModificationException`. +Rewrite it so that it does not. + +```java,editable +import java.util.ArrayList; + +class Main { + void main() { + var trash = new ArrayList(); + trash.add("gloves"); + trash.add("staff"); + trash.add("glasses"); + + for (var item : trash) { + if (item.equals("glasses")) { + trash.remove(item); + } + } + } +} +``` \ No newline at end of file diff --git a/src/loops_iii/header.png b/src/loops_iii/header.png new file mode 100644 index 00000000..0a4384bf Binary files /dev/null and b/src/loops_iii/header.png differ diff --git a/src/methods.md b/src/methods.md index 37819626..e49a717e 100644 --- a/src/methods.md +++ b/src/methods.md @@ -1,5 +1,8 @@ # Methods + + + All the code you have seen up until this point has lived inside of `void main() {}`. ```java diff --git a/src/methods/header.png b/src/methods/header.png new file mode 100644 index 00000000..b5bb4d1f Binary files /dev/null and b/src/methods/header.png differ diff --git a/src/methods/main.md b/src/methods/main.md index b718f05b..c140373a 100644 --- a/src/methods/main.md +++ b/src/methods/main.md @@ -9,16 +9,4 @@ void main() { } ``` -This means you can do anything in your `main` method you can do in any other method, including returning early. - -```java -void main() { - int x = 5; - - if (x == 5) { - return; - } - - IO.println("WONT RUN"); -} -``` +This means you can do anything in your `main` method you can do in any other method. diff --git a/src/modules.md b/src/modules.md index 8c1581b8..d189885f 100644 --- a/src/modules.md +++ b/src/modules.md @@ -1,5 +1,7 @@ # Modules + + Every package in Java "lives" in a module. Just as classes may be grouped into packages, packages may be grouped into modules. diff --git a/src/modules/challenges.md b/src/modules/challenges.md new file mode 100644 index 00000000..6ed0ffcf --- /dev/null +++ b/src/modules/challenges.md @@ -0,0 +1,24 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Add a `module-info.java` to one of your projects. + +## Challenge 2. + +Move some of your code to use the multi-module directory layout. +If you need to split one of your projects into multiple modules +to pull this off, do so. + +## Challenge 3. + +Intentionally create a split package situation. What error does Java give you? + +## Challenge 4. + +Intentionally create a cycle in the "module graph." What error does Java give you? diff --git a/src/modules/exports.md b/src/modules/exports.md new file mode 100644 index 00000000..05a3b66a --- /dev/null +++ b/src/modules/exports.md @@ -0,0 +1,41 @@ +# Exports + +Inside your `module-info.java` file you declare which packages are +"exported" from that module. + +```java +module reality { + exports earth; + exports sea; + exports sky; +} +``` + +Any packages that do not have an `exports` declaration are "unexported packages." + +The classes in unexported packages will not be visible to other modules +even if those classes are `public`. + +```java,no_run +// Because the "backrooms" package is not exported +// from the "reality" module, other modules cannot +// observe classes within it. +package backrooms; + +public class YellowCarpet { +} +``` + +```java,no_run +package sky; + + +// But classes within other packages of +// the "reality" module can see it just fine. +public class Cloud { + private final YelloCarpet fabricOfExistence + = new YellowCarpet(); + + // ... +} +``` \ No newline at end of file diff --git a/src/modules/header.png b/src/modules/header.png new file mode 100644 index 00000000..80250445 Binary files /dev/null and b/src/modules/header.png differ diff --git a/src/modules/module_imports.md b/src/modules/module_imports.md index 826b10d7..24f6c6da 100644 --- a/src/modules/module_imports.md +++ b/src/modules/module_imports.md @@ -1,3 +1,32 @@ # Module Imports -A special kind of import \ No newline at end of file +A special kind of import is the "module import." + +Just like a package import (`import package_name.*`) will +import all the classes in that package, an `import module` +declaration will import all the classes in all the packages +contained within that module. + +```java +// Same as writing +// +// import java.util.*; +// import java.lang.annotation.*; +// import java.lang.reflect.*; +// ... and so on for every package in the java.base module +import module java.base; + +class Main { + void main() { + // You can now freely use any of the classes from the imported module + Collection c = List.of("do", "re", "mi"); + + IO.println(c); + } +} +``` + +This has much the same upsides and downsides as a package import. It is +much easier to write this and get all the classes you want, but in exchange +it might be harder to see where a particular class comes from when you are +reading code later. \ No newline at end of file diff --git a/src/modules/multi_module_directory_layout.md b/src/modules/multi_module_directory_layout.md new file mode 100644 index 00000000..0f7ff543 --- /dev/null +++ b/src/modules/multi_module_directory_layout.md @@ -0,0 +1,59 @@ +# Multi-Module Directory Layout + +If you yourself want to develop a project using multiple of your own modules +there is a special way to layout the files to do so. + +First, make folders with the name of each module. If a module name +has a `.` in it the folder should too. + +```text,no_run +reality/ +backrooms/ +horrible.monsters/ +``` + +Then in those folders under a `src` folder put the `module-info.java` files.[^src] + +```text,no_run +reality/ + src/ + module-info.java +backrooms/ + src/ + module-info.java +horrible.monsters/ + src/ + module-info.java +``` + +From there you can put all the classes you want into each module, so long as they don't conflict +or create split packages. + +```text,no_run +reality/ + src/ + earth/ + Dirt.java + Worm.java + sea/ + Starfish.java + sky/ + Cloud.java + module-info.java +backrooms/ + src/ + backrooms/ + YellowCarpet.java + module-info.java +horrible.monsters/ + src/ + horrible/ + monsters/ + Slime.java + Skeleton.java + module-info.java +``` + +This can be helpful in structuring larger projects. + +[^src]: The `src` folder isn't technically required. You will see what a `--module-source-path` looks like in a bit. I think its a good idea anyways. \ No newline at end of file diff --git a/src/modules/purpose.md b/src/modules/purpose.md new file mode 100644 index 00000000..1b0081fa --- /dev/null +++ b/src/modules/purpose.md @@ -0,0 +1,22 @@ +# Purpose + +It might be a little unclear at first what reason +modules have for existing. + +After all, don't packages already group code? Why would you +want groupings of a grouping? + +The answer is that[^more] when you share code with other people +you will often want to have that code contain multiple packages. +If you couldn't "hide" packages of code from the people using +the code you are sharing it would be a bummer. + +Just as private fields help you encapsulate state in a class +and package-private classes help you encapsulate whole classes, +unexported packages are your mechanism for encapsulating whole package. + + +[^andby]: And by "you" I mean you and whoever else is directly working with you. + +[^more]: There are more answers, but they mostly involve tales of Java's past +and mistakes that were made there. \ No newline at end of file diff --git a/src/modules/requires.md b/src/modules/requires.md new file mode 100644 index 00000000..f3b37579 --- /dev/null +++ b/src/modules/requires.md @@ -0,0 +1,51 @@ +# Requires + +Code within modules can only use classes defined in packages +that are part of its module or are in exported packages of modules it +"requires." + +```java,no_run +module reality { + exports earth; + exports sea; + exports sky; +} +``` + +```java +module human { + // Code within this "human" module + // will have access to classes in + // the "earth," "sea," and "sky" packages. + requires reality; +} +``` + +Modules can both require other modules and +export packages for other modules to use. + +```java +module human { + requires reality; + + exports sadness; +} +``` + +These `requires` are not allowed form "cycles." +This means that `cat` cannot require `dog` if `dog` also +requires `cat`, indirectly or otherwise. + +```java,no_run +module dog { + requires cat; +} +``` + +```java,no_run +// Apologies to any hit Nickelodeon shows +// but this is unacceptable. +module cat { + requires dog; +} +``` \ No newline at end of file diff --git a/src/modules/restrictions.md b/src/modules/restrictions.md index e7569de5..01ef7212 100644 --- a/src/modules/restrictions.md +++ b/src/modules/restrictions.md @@ -2,9 +2,11 @@ All classes within a module must be in named packages -The packages within one module must only be in that module. This -means that you cannot have a class in the `java.lang` package defined -in any of your modules. The `java.lang` - Unlike packages, where two packages can have classes of the same name, -two modules cannot contain the same package. \ No newline at end of file +two modules cannot contain the same package. + +This means that you cannot have a class in the `java.lang` package defined +in any of your modules, as that already comes with Java. + +If you accidentally make a situation where one package is in multiple modules +we call that having a "split package" and Java will raise an error. \ No newline at end of file diff --git a/src/modules/the_unnamed_module.md b/src/modules/the_unnamed_module.md index 8777a8f8..909d2be5 100644 --- a/src/modules/the_unnamed_module.md +++ b/src/modules/the_unnamed_module.md @@ -4,8 +4,15 @@ All packages that are not in a named module are placed in "the unnamed module." This includes all the code you have written thus far and all code written without a `module-info.java` alongside it. -This is generally okay. Modules as an organizational tool are most useful for sharing -code across "maintenance boundaries." Explicitly - The unnamed module is special in that code within it `requires` every other module and therefore can see every exported package and every class within. + +It is fine to have your code in the unnamed module, but code you get from other people +should generally be in named modules. This is because modules are mostly +useful for sharing code across "maintenance boundaries." + +If you are not going to be sharing your code with another person then +the ability to have "unexported packages" matters less. If you are +the person who code is shared with, the group sharing it with you +will expect you to not touch classes in packages they did not export. + diff --git a/src/multi_dimensional_arrays.md b/src/multi_dimensional_arrays.md index f01263eb..208f44a5 100644 --- a/src/multi_dimensional_arrays.md +++ b/src/multi_dimensional_arrays.md @@ -1,5 +1,8 @@ # Multi-Dimensional Arrays + + + When you make an array in which each element is itself an array we call that array of arrays a "multi-dimensional array." diff --git a/src/multi_dimensional_arrays/challenges.md b/src/multi_dimensional_arrays/challenges.md new file mode 100644 index 00000000..342456de --- /dev/null +++ b/src/multi_dimensional_arrays/challenges.md @@ -0,0 +1,101 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + + + + +## Challenge 1. + +Initialize 10x10 2D array of `int`s by size. +Print every element of this new 2D array. + +Will any elements of the outermost array be `null`? +Write down your guess before making the program. + +```java,editable +void main() { + int[][] xs = /* CODE HERE */ + // CODE HERE +} +``` + +## Challenge 2. + +Print out every character in the 2D array of `char`s. After each +row print a new line. + +```java,editable +void main() { + char[][] picture = new char[][] { + new char[] { ' ', ' ', ' ', ' ' }, + new char[] { ' ', '*', '*', ' ' }, + new char[] { '\\', ' ', ' ', '/' }, + new char[] { ' ', '-', '-', ' ' } + }; + + // CODE HERE +} +``` + +## Challenge 3. + +"Draw" a smiley face on the "canvas" provided by a 5x5 2D `char` array. + +Use similar code as the previous challenge to print it out. + +```java,editable +void main() { + final char[][] picture = new char[5][5]; + + // CODE HERE +} +``` + + + +## Challenge 4. + +Write a method named `winner`. It should take in a 2-dimensional +array of `String`s where each character is either `X`, `O`, or an empty string. +This 2D array represents a [game of Tic-Tac-Toe](https://en.wikipedia.org/wiki/Tic-tac-toe). + +If there is a winner of the Tic-Tac-Toe game, it should return `X` or `O` depending on who the winner is. +It should return `null` if nobody won or the game is a tie. + +```java +String winner(String[][] ticTacToe) { + // CODE HERE +} + +void main() { + var winnerA = winner(new String[][] { + new String[] { "X", "X", "" }, + new String[] { "O", "", "" }, + new String[] { "O", "", "" } + }); + + IO.println(winnerA); + + var winnerB = winner(new String[][] { + new String[] { "X", "X", "X" }, + new String[] { "O", "O", "X" }, + new String[] { "O", "", "O" } + }); + + IO.println(winnerB); + + var winnerC = winner(new String[][] { + new String[] { "O", "X", "O" }, + new String[] { "O", "O", "X" }, + new String[] { "O", "X", "O" } + }); + + IO.println(winnerC); +} +``` + + diff --git a/src/multi_dimensional_arrays/header.png b/src/multi_dimensional_arrays/header.png new file mode 100644 index 00000000..13f45b6c Binary files /dev/null and b/src/multi_dimensional_arrays/header.png differ diff --git a/src/multi_file_programs.md b/src/multi_file_programs.md index 78accc19..d3337a16 100644 --- a/src/multi_file_programs.md +++ b/src/multi_file_programs.md @@ -1,5 +1,7 @@ # Multi-File Programs + + For many reasons, it makes sense to split programs into multiple files. This is one of those things that is easier to show than tell, so to follow along diff --git a/src/multi_file_programs/challenges.md b/src/multi_file_programs/challenges.md new file mode 100644 index 00000000..c7062ee1 --- /dev/null +++ b/src/multi_file_programs/challenges.md @@ -0,0 +1,105 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1 + +Put this code in a file named `Pickle.java` + +```java +class Pickle { + boolean dill; +} +``` + +Put this code in a file named `Ramen.java` + +```java +class Ramen { + void mixWith(Pickle pickle) { + IO.println("This is gross."); + if (pickle.dill) { + IO.println("Extra super gross."); + } + } +} +``` + +And finally put this code in `Main.java` + +```java +class Main { + void main() { + var pickle = new Pickle(); + pickle.dill = true; + + var ramen = new Ramen(); + + ramen.mixWith(pickle); + } +} +``` + +Then run the entire program from your terminal by using `java Main.java` +in the directory where you made these files. + +## Challenge 2 + +Move `Ramen.java`, `Pickle.java`, and `Main.java` into a folder named `src`. + +Make sure you can still run the program by typing `java src/Main.java`. + +## Challenge 3 + +The following code uses an anonymous main class. Make it instead use a named class. + +```java,editable +int dogs = 99; + +void main() { + while (dogs > 0) { + IO.println(dogs + " dogs on the floor."); + IO.println("Wake one up, take it for a walk"); + dogs--; + } + IO.println("No more dogs on the floor."); +} +``` + +## Challenge 4 + + +Combine the following programs while keeping them in separate files. +This will require you to give names to both classes as well as +parameterize the "dogs on the floor" program in some way. + +```java +int dogs = 99; + +void main() { + while (dogs > 0) { + IO.println(dogs + " dogs on the floor."); + IO.println("Wake one up, take it for a walk"); + dogs--; + } + IO.println("No more dogs on the floor."); +} +``` + + +```java +void main() { + while (true) { + try { + int dogs = Integer.parseInt(IO.readln("How many dogs are on the floor? ")); + IO.println("TODO"); // Call the first program here somehow. + } catch (RuntimeException e) { + IO.println("Goodbye!"); + } + } + +} +``` \ No newline at end of file diff --git a/src/multi_file_programs/header.png b/src/multi_file_programs/header.png new file mode 100644 index 00000000..bd302681 Binary files /dev/null and b/src/multi_file_programs/header.png differ diff --git a/src/niche_numerics.md b/src/niche_numerics.md index a41e9df7..27c24e0d 100644 --- a/src/niche_numerics.md +++ b/src/niche_numerics.md @@ -1,5 +1,7 @@ # Niche Numerics + + For a surprisingly wide range of programs `int` and `double` (along with their boxed counterparts `Integer` and `Double`) are the only numeric types you will need. diff --git a/src/niche_numerics/challenges.md b/src/niche_numerics/challenges.md new file mode 100644 index 00000000..081c2170 --- /dev/null +++ b/src/niche_numerics/challenges.md @@ -0,0 +1,103 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Write two static methods in the following `Bytes` class. + +The first should take an `int` and return an array of 4 +bytes. + +The second should take that array of 4 bytes and give back the original int. + +There are a few ways to do this, but you only pass the test if +you can do a "full round trip." + +```java,editable +class Bytes { + static int toInt(byte[] bytes) { + // CODE HERE + } + static byte[] toBytes(int value) { + // CODE HERE + } +} + +class Main { + void main() { + int i = 172089379; + + byte[] bs = Bytes.toBytes(i); + + // 172089379 + IO.println(Bytes.fromInt(bs)); + } +} +``` + +## Challenge 2. + +Turn the `String` `"hello world"` into a `short[]`. Print the contents +of this `short[]` and then turn it back into a `String`. + +```java +class Main { + void main() { + String s = "hello world"; + + short[] shorts; + + // CODE HERE + + String roundTripped; + + // CODE HERE + } +} +``` + +## Challenge 3. + +Make a class named `UnsignedInteger`. It should wrap up an `int` such that +all operations on it are performed using the appropriate unsigned specific methods +when needed. + +At a minimum: + +- Implement `toString` so that the unsigned representation is used +- Implement `equals` and `hashCode` +- Implement `plus` and `minus` +- Implement the `Comparable` interface. + +```java +// CODE HERE + +class Main { + void main() { + var i = new UnsignedInteger(0); + IO.println(i.minus(new UnsignedInteger(1))); + } +} +``` + +## Challenge 4. + +Convert this program to use `float` instead of `double`. + +```java +class Main { + void main() { + double radius = Double.parseDouble( + IO.readln("What is the radius of the circle? ") + ); + double pi = 3.14; + double circumference = 2 * pi * radius; + + IO.println("Circumference: " + circumference); + } +} +``` \ No newline at end of file diff --git a/src/niche_numerics/header.png b/src/niche_numerics/header.png new file mode 100644 index 00000000..ac717c1a Binary files /dev/null and b/src/niche_numerics/header.png differ diff --git a/src/null.md b/src/null.md index f5ebb226..9b376113 100644 --- a/src/null.md +++ b/src/null.md @@ -1,5 +1,8 @@ # null + + + There is a special value called `null` which is assignable to most types. ```java diff --git a/src/null/header.png b/src/null/header.png new file mode 100644 index 00000000..777882c9 Binary files /dev/null and b/src/null/header.png differ diff --git a/src/objects.md b/src/objects.md index 6f1ec49f..fb7f172d 100644 --- a/src/objects.md +++ b/src/objects.md @@ -1,5 +1,8 @@ # Object + + + You may have heard the phrase "everything is an object" parroted about. diff --git a/src/objects/challenges.md b/src/objects/challenges.md new file mode 100644 index 00000000..00f327b2 --- /dev/null +++ b/src/objects/challenges.md @@ -0,0 +1,129 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +The `Witch` class has a method named `pullFromHat` +that returns an `Object` that is sometimes a `Spell` and sometimes a `Broom`. + +If it is a `Broom` call the `.fly()` method on it. If it is a `Spell` call `.cast`. + +```java +class Spell { + private final String name; + + Spell(String name) { + this.name = name; + } + + void cast() { + IO.println("Casting " + name + "..."); + } +} + +class Broom { + void fly() { + IO.println("Flying!"); + } +} + +class Witch { + Object pullFromHat() { + double r = Math.random(); + if (Math.random() < 0.25) { + return new Spell("Ensmallen"); + } + else if (r < 0.5) { + return new Spell("Embiggen"); + } + else if (r < 0.75) { + return new Spell("Enlongen"); + } + else { + return new Broom(); + } + } +} + +class Main { + void main() { + var witch = new Witch(); + + Object item = witch.pullFromHat(); + + // CODE HERE + } +} +``` + +## Challenge 2. + +Will the following code work? Why or why not? + +```java,editable +class Main { + void main() { + String s = "abc"; + Object o = s; + + IO.println(o.length()); + } +} +``` + +## Challenge 3. + +Will the following code work? Why or why not? + +```java,editable +class Main { + void main() { + Object o = "abc"; + String s = o; + + IO.println(s.length()); + } +} +``` + +## Challenge 4. + +Implement `toString`, `equals`, and `hashCode` +on the following `Ogre` class such that it behaves +the same as an `Ogre` record. + +```java +record Ogre(String name, double strength) {} +``` + +```java,editable +class Ogre { + final String name; + final double strength; + + Ogre(String name, double strength) { + this.name = name; + this.strength = strength; + } + + // CODE HERE +} + +class Main { + void main() { + Ogre o1 = new Ogre("Morihito", 100); + Ogre o2 = new Ogre("Morihito", 100); + + IO.println(o1); // Ogre[name=Morihito, strength=100] + IO.println(o1.equals(o2)); // True + IO.println(o1.hashCode() == o2.hashCode()); // True + IO.println(o1.equals(new Ogre("Reiji", 5))); // False + IO.println(o1.hashCode() == new Ogre("Reiji", 5).hashCode()); // False + } +} +``` + diff --git a/src/objects/header.png b/src/objects/header.png new file mode 100644 index 00000000..18326ca4 Binary files /dev/null and b/src/objects/header.png differ diff --git a/src/packages.md b/src/packages.md index a951fc9d..ca826574 100644 --- a/src/packages.md +++ b/src/packages.md @@ -1,5 +1,7 @@ # Packages + + Every class in Java "lives" in a package. A package is a way to group individual classes. diff --git a/src/packages/challenges.md b/src/packages/challenges.md new file mode 100644 index 00000000..e7996b24 --- /dev/null +++ b/src/packages/challenges.md @@ -0,0 +1,34 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Take all the code you have written so far and put into one project. + +To do this put everything under one `src` folder just in different packages. +You will need to convert any usages of the anonymous main class into +named classes, but other than that it should be doable. + +If you have already been doing that, just start making use of packages. +They are, first and foremost, an organizational tool. Use them to organize +your code as you see fit. + +## Challenge 2. + +In the packages above have all the growable array implementations you were +asked to make in one package named `collections`. Import these collections +wherever you want to use them with `import collections.GrowableIntArray;` +or similar declarations. + +This will require marking the classes and many of the methods within `public`. + +## Challenge 3. + +Read through [the list of packages that come with Java](https://docs.oracle.com/en/java/javase/24/docs/api/java.base/module-summary.html). You don't need to understand everything you are looking at; just +browse and try to find at least one class that interests you. + +Import that class and try to use it. diff --git a/src/packages/header.png b/src/packages/header.png new file mode 100644 index 00000000..57cfaaf3 Binary files /dev/null and b/src/packages/header.png differ diff --git a/src/packages/package_imports.md b/src/packages/package_imports.md new file mode 100644 index 00000000..d7250012 --- /dev/null +++ b/src/packages/package_imports.md @@ -0,0 +1,41 @@ +# Package Imports + +If you want to import all of the classes in a package +all at once you can use a "package import." + +To do this write `import` followed by the name of the +package and `.*`. + +```java,no_run +package village; + +public class Villager {} +``` + +```java,no_run +package village; + +public class Dog {} +``` + +```java,no_run +package dungeon; + +// Imports Village, Dog, and any other classes +// in the "village" package. +import village.*; + +class Dwarf { + Villager meet() { + return new Villager(); + } + + Dog pet() { + return new Dog(); + } +} +``` + +This has the upside of being only one line of code. This also has the +downside of being only one line of code - it is harder to figure out +from what package any particular class might be coming from. \ No newline at end of file diff --git a/src/packaging.md b/src/packaging.md new file mode 100644 index 00000000..1bb078e0 --- /dev/null +++ b/src/packaging.md @@ -0,0 +1,15 @@ +# Packaging + + + +Once you have a folder full of class files you technically have enough +to share your code. + +You can take that folder, put it on a flash drive, and give it to your friend. +They can then run your code, presuming they have Java installed as well. + +But for several reasons sharing a folder of "loose" classes is unideal. +What you really want is to package those classes up into a single file. + +This way you never accidentally forget one of the files and its easier to +share. \ No newline at end of file diff --git a/src/packaging/challenges.md b/src/packaging/challenges.md new file mode 100644 index 00000000..bf5a0ed1 --- /dev/null +++ b/src/packaging/challenges.md @@ -0,0 +1,26 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Package up the code from the previous section's challenges into JAR files. + +Run those JAR files using `--module-path ...` and `--module`. + +## Challenge 2. + +Inspect the contents of the JARs you just created with `jar --list ` +and `jar --describe-module `. + +Ensure that the module names you picked match up with the file names for the jars. + + +## Challenge 3. + +Use `--main-class` to specify the main classes for the previous modules. + +You can make sure you did it right by trying to run them directly. \ No newline at end of file diff --git a/src/packaging/header.png b/src/packaging/header.png new file mode 100644 index 00000000..fb4bcbd4 Binary files /dev/null and b/src/packaging/header.png differ diff --git a/src/packaging/jar.md b/src/packaging/jar.md new file mode 100644 index 00000000..9bf3a635 --- /dev/null +++ b/src/packaging/jar.md @@ -0,0 +1,79 @@ +# jar + +The tool you use to package up class files in Java is called `jar`. +The output from this tool is a "JAR file." + +To make a JAR file, first compile your code into a folder. + +If you do this by listing all the files out one-by-one, your output +folder will be structured something like the following. + +```text,no_run +output/ + some_pkg/ + A.class + nested/ + B.class +``` + +If you did it using the `--module-source-path` your class files will be nested +under a folder with the name of the module. + +```text,no_run +output/ + ex.mod/ + module-info.class + some_pkg/ + A.class + nested/ + B.class +``` + +Then use a command like the following: + +```bash,no_run +jar \ + --create \ + --file ex.mod.jar \ + -C output/ex.mod . +``` + +The `--create` part means "we are creating a new jar file" +and `--file` says what file to put the jar in. For the file name +you should generally use the name of the module followed by `.jar`.[^mods] +This is technically optional though. + +The `-C` flag is where it gets interesting. The `jar` tool works somewhat like how your terminal +does when you `cd` into directories. + +The tool starts at the directory you are running it at. + +```text,no_run +project/ <--- jar "is here" + output/ + ex.mod/ + module-info.class + some_pkg/ + A.class + nested/ + B.class +``` + +Then with `-C` you "**C**hange" into a directory. + +```text,no_run +project/ + output/ + ex.mod/ <--- jar is here after "-C output/ex.mod" + module-info.class + some_pkg/ + A.class + nested/ + B.class +``` + +After that you are meant to specify what files you want to add into the jar. Just putting one period (`.`) +means "take everything in this folder." Hence `-C output/ex.mod .` puts all those files into the final JAR. + +[^mods]: And, like I've mentioned before, you should ideally be packaging up code +in modules. \ No newline at end of file diff --git a/src/packaging/jar_files.md b/src/packaging/jar_files.md new file mode 100644 index 00000000..b8000fd3 --- /dev/null +++ b/src/packaging/jar_files.md @@ -0,0 +1,15 @@ +# Jar Files + +JAR files - short for **J**ava **Ar**chive - +are just ZIP files with a few special bits of "metadata." + +ZIP files are a common way of bundling a bunch of files up into one file.[^compression] + +You don't need to know exactly where this metadata goes or what all of it is for yet, +just that at a high level its all just files in a ZIP. + +[^compression]: This bundling up also generally includes "compression," where +the single file might be smaller than the combined sizes of its components. Most people don't +need to think about this nowadays. Class files are small and hard drives are big. +I say that knowing full well that 145GB of the 500GB hard drive my work laptop is Baldurs Gate 3. +I have 4GB left. \ No newline at end of file diff --git a/src/packaging/javac.md b/src/packaging/javac.md new file mode 100644 index 00000000..0a44124c --- /dev/null +++ b/src/packaging/javac.md @@ -0,0 +1 @@ +# javac diff --git a/src/packaging/libraries.md b/src/packaging/libraries.md new file mode 100644 index 00000000..39d3c1b7 --- /dev/null +++ b/src/packaging/libraries.md @@ -0,0 +1,46 @@ +# Libraries + +It is often the case that it isn't practical to produce a program +using only the modules that come with Java. + +To cope with this reality you can use code written by others +as "libraries" of functionality.[^procuring] + +When running a Java program from source you can provide the libraries +using `--module-path --add-modules ALL-MODULE-PATH`. +So if you had a library named `tangerine.jar` you could run something like the following +command. + +```bash,no_run +java \ + --module-path tangerine.jar \ + --add-modules ALL-MODULE-PATH \ + src/Main.java +``` + +You do not need the `--add-modules ALL-MODULE-PATH` if your code is itself inside of a named +module. The `requires` in the module take care of telling Java what to include. + +```bash,no_run +java \ + --module-path tangerine.jar \ + ex.mod/src/Main.java +``` + +If you use libraries you need to provide the same flags to `javac` when compiling +your own code to share. + +```bash,no_run +java \ + -g \ + -d output \ + --module-path tangerine.jar \ + --module-source-path "./*/src" \ + --module example.module +``` + +Now that you can package your own code into JARs you can share code with others +for this purpose. + +[^procuring]: Libraries can depend on libraries which depend on other libraries in sprawling nightmare graphs. For now let us assume that the libraries you want to use are relatively self-contained +and reasonable to download manually. Dependency resolution and procurement can be a topic for a later day. diff --git a/src/packaging/main_class.md b/src/packaging/main_class.md new file mode 100644 index 00000000..b29c3222 --- /dev/null +++ b/src/packaging/main_class.md @@ -0,0 +1,23 @@ +# --main-class + +When making a JAR you can specify a "main class" +for that JAR using the `--main-class` flag. + +```bash,no_run +jar \ + --create \ + --file ex.mod.jar \ + --main-class some.pkg.Main \ + -C output/ex.mod . +``` + +This makes it so that you can run the code in a JAR without knowing which class +in particular you intend to run. You only need to specify the module name. + + +```bash,no_run +java \ + --module-path ex.mod.jar \ + --module ex.mod +``` + diff --git a/src/packaging/module_path.md b/src/packaging/module_path.md new file mode 100644 index 00000000..b12b8aa5 --- /dev/null +++ b/src/packaging/module_path.md @@ -0,0 +1,34 @@ +# --module-path + +After packaging code into a JAR file you can put that +JAR onto the `--module-path` in the same way you would a folder +of classes. + +```bash +java \ + --module-path ex.mod.jar \ + --add-modules ALL-MODULE-PATH + some.pkg.Main +``` + +If you have multiple JARs to put on the module path +you can do so by separating them with a `:`.[^windows] + +```bash +java \ + --module-path ex.mod.jar:other.thing.jar \ + --add-modules ALL-MODULE-PATH + some.pkg.Main +``` + +Instead of `--add-modules ALL-MODULE-PATH` followed by a class name you +can use `--module` followed by `/` + +```bash,no_run +java \ + --module-path ex.mod.jar \ + --module ex.mod/some.pkg.Main +``` + +[^windows]: On Windows instead of a `:` you use a `;`. I am assuming when making these examples +that you are using Windows Subsystem for Linux. If you are not, just adjust as needed. \ No newline at end of file diff --git a/src/prelude/ai.md b/src/prelude/ai.md new file mode 100644 index 00000000..33ff48c3 --- /dev/null +++ b/src/prelude/ai.md @@ -0,0 +1,13 @@ +# AI + +Do not use any form of AI when you are learning. + +I could go into exactly why or shadowbox against why you might think its a good idea, +but instead I am just going to ask that you trust me. + +Don't do it. + +If you find it hard to resist the temptation, install something like [this](https://getcoldturkey.com/) +on your machine to block yourself from accessing AI websites. + +When you get stuck reach out to a person for help. \ No newline at end of file diff --git a/src/prelude/java.md b/src/prelude/java.md new file mode 100644 index 00000000..1c3c321f --- /dev/null +++ b/src/prelude/java.md @@ -0,0 +1,30 @@ +# Java + + + +The bulk of this book will be, as the title suggests, +covering the Java programming language. + +To clear up some common misconceptions: + +* No, Java was not made by or for Minecraft. Minecraft came into existence in 2010. Java has been around since 1996. +* No, you do not _have to_ pay Oracle if you use Java. You can download Java from other organizations +with non-litigious track records like [Adoptium](https://adoptium.net/) if you are nervous. +* No, Java is not the same thing as JavaScript. JavaScript is a very different beast. The naming is very confusing yes. The [history is wild](https://javascript.tm/). + + +The drawing at the top is Java's official[^coffee] mascot.[^what] The [drawing below is JavaScript's](https://javascript-mascot.github.io/). + + + + +The differences between these mascots are representative of the differences between the two languages. + + + +[^coffee]: There is another logo people use for Java - a coffee cup with steam coming off of it - +that is a trademarked symbol of Oracle. If you use it there is a high likelyhood of Oracle +juicero-ing your first-born. + +[^what]: What is that mascot? Well it is named "Duke," but beyond that your guess is as good as mine. +I'll be using that ambiguity to its fullest. \ No newline at end of file diff --git a/src/prelude/java_mascot.png b/src/prelude/java_mascot.png new file mode 100644 index 00000000..9047082e Binary files /dev/null and b/src/prelude/java_mascot.png differ diff --git a/src/prelude/javascript_mascot.png b/src/prelude/javascript_mascot.png new file mode 100644 index 00000000..0ce0ee23 Binary files /dev/null and b/src/prelude/javascript_mascot.png differ diff --git a/src/projects/ascii_art.md b/src/projects/ascii_art.md new file mode 100644 index 00000000..4bb2f878 --- /dev/null +++ b/src/projects/ascii_art.md @@ -0,0 +1,164 @@ +# ASCII Art Generator + +## Problem Statement + +Humans like to draw stuff and to look at drawings of stuff. + +We know this is in some manner intrinisic to us as a species +because we've found drawings in caves [dating back at least 51,200 years.](https://en.wikipedia.org/wiki/Cave_painting) + +As such it is a normal impulse to use pictures, drawings, iconography, and other forms +of art as a tool for communication. + +In the early days of the internet the amount of data you could send between computers was extremely limited. +This meant that, in practice, most people would communicate using solely text. + +Instead of making it so that people couldn't send images to eachother, that restriction birthed a +new form of art. Using only the characters available to send as text, people would make and send pictures. + +For example, here is a bat from [a website that archives examples of this form of art](https://www.asciiart.eu/animals/bats). + +``` + /'. .'\ + \( \__/ )/ + ___ / (.)(.) \ ___ + _.-"`_ `-.| ____ |.-` _`"-._ + .-'.-'//||`'-.\ V--V /.-'`||\\'-.'-. +`'-'-.// || / .___. \ || \\.-'-'` + `-.||_.._| |_.._||.-' + \ (( )) / + jgs '. .' + `\/` +``` + +[Here are some cubes](https://www.asciiart.eu/art-and-design/geometries). + +``` ++------+. +------+ +------+ +------+ .+------+ +|`. | `. |\ |\ | | /| /| .' | .'| +| `+--+---+ | +----+-+ +------+ +-+----+ | +---+--+' | +| | | | | | | | | | | | | | | | | | ++---+--+. | +-+----+ | +------+ | +----+-+ | .+--+---+ + `. | `.| \| \| | | |/ |/ |.' | .' + `+------+ +------+ +------+ +------+ +------+' +``` + +[And this one is a dog](https://www.asciiart.eu/animals/dogs). + +``` + __ + \ ______/ V`-, + } /~~ + /_)^ --,r' +|b |b +``` + +We call these drawings "ASCII Art" after the "American Standard Code for Information Interchange" - ASCII. +ASCII defined an English-centric set of characters and how to represent them in a computer. Much of this early +art was made solely using that character set, hence the name. + +Even though sending images is now practical to do over the internet, ASCII art is still +a valid form of expression. Either as a deliberate choice or because of using a text only medium +(they still exist. Think of in-game chats.) ASCII art can be useful. + +If you want to see how far this can be taken +check out this [entirely ASCII art rendition of the Star Wars IV: A New Hope](https://www.asciimation.co.nz/index.php) + + + +## Your Goal + +Make a program that asks a user for a "height" and then prints out an ASCII art christmas tree +that is that many characters tall. + +Here is an example tree you can use as a starting point. You can print this when asked for a height of `3`. + +``` + * + *** +***** + | +``` + +Here is another example, but with a height of `5`. + +``` + * + *** + ***** + ******* +********* + | +``` + +And here it is with a height of `1` + +``` +* +| +``` + + + + +## Future Goals + +When you learn enough to do the following, come back to this project and expand it. + +* Draw something with more varied parts, like a snowman. You might find it convenient to not directly print to the screen, but instead "draw" what you want to print into an array first then print the contents of that array. + +``` + --- + | | + ----- + / _ ^ \ + | * * | + | V | + \^___^/ + ------- + / \ + * | | * + * | | * + * | | * +* \-------/ * + --------- + / \ + | | + | | + | | + | | + \---------/ +``` +* Make sure that if the person using your program gives you a negative number, zero, or something that is +not a number you don't just crash. This means you might need to reprompt them for information. +* Make the christmas tree prettier. This will require "finding the pattern" in a more interesting piece of art, like [this example](https://www.asciiart.eu/holiday-and-events/christmas/trees). + +``` + /\ + /\*\ + /\O\*\ + /*/\/\/\ + /\O\/\*\/\ + /\*\/\*\/\/\ +/\O\/\/*/\/O/\ + || + || + || +``` + +* Draw both the snowman and a tree. Let the user select which goes on the left and which goes on the right. + +* Turn this into a command-line program that works similarly to the [cowsay](https://en.wikipedia.org/wiki/Cowsay#:~:text=cowsay%20is%20a%20program%20that,a%20cow%20with%20a%20message.) tool. + +``` + _______ +< Hello > + ------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +``` + +* Expand or refocus into drawing other kinds of things. \ No newline at end of file diff --git a/src/projects/buy_now_pay_later.md b/src/projects/buy_now_pay_later.md new file mode 100644 index 00000000..85f6f184 --- /dev/null +++ b/src/projects/buy_now_pay_later.md @@ -0,0 +1,101 @@ +# Buy Now, Pay Later + +## Problem Statement + +People exchange money for goods and/or services. + +Some of these purchases are non-essential. This means that they are, +strictly speaking, optional. Ordering extra guacamole at a restraunt, +buying a brand new pair of shoes, and buying a diamond ring are all +examples of non-essential spending. + +Other purchases are essential. If you are having a heart attack (within +the United States) medical care is a mandatory expense. You either make that +purchase or you die. If one does not have some minimum level of food, water, and shelter +those are also essential expenses. + +It comes down to needs and wants. If you **need** to have something that is an essential purchase. +If you **want** to have something that is a non-essential purchase. + +Many people do not have enough money for their essential expenses. +This can be due to any number of reasons, but in the United States minimum wage has +not kept up with overall inflation since 1968. Add to that rising costs of housing, +transportation, education, etc. and times are tough. + +Some people have much more money than they could ever need. These people, +sometimes pooling funds via corporations, often choose to loan this money +to those that do not have enough. + +The reason they do this is because they can charge fees. If someone can't afford +a particular expense of ~$100 they could get the money for that expense right away in exchange for agreeing +to a 5% monthly interest rate. This would mean that the amount they owe goes up by 5% every month that they +do not pay the loan issuer back. After 5 months they would owe $127.63.[^compound] + +Banks work this way. You deposit money in the bank and, while the bank and the government +guarentee you that you can go at any time and get your money out, they are also turning around +and lending that money to any number of businesses and individuals. + +So long as you assume most people will be able to eventually pay off their debts, loan issuance +is a very profitable business to be in. It is also something of a public good.[^christmas] But if a large number of people are unable to pay off their debts +you can get financial collapses. This can be at the level of an individual loan issuer up to entire countries. + +Because it is bad when this happens governments generally have laws that restrict the "level of risk" +loan issuers are allowed to take on. If they "take on too much risk" - meaning loan enough money +to people who aren't able to pay it back - there will eventually be a collapse. + +The level of scrutiny that applies to normal debts does not seem to, at least in the United States, +apply when you do it via payment plans and call it "Buy Now, Pay Later." This allows for businesses +to make loans they otherwise wouldn't be allowed to to people they might not otherwise be allowed to loan to. + +This certainly helps people who are struggling to get enough to affort their essential expenses. +It can also help people make non-essential expenses which they deem essential. Human's are imperfect +creatures. Many will take on a payment plan with an interest rate for a burrito. + +In all cases the ability to take on debt can either help someone stabilize their financial situation +or it can ultimately make things worse. The higher the interest rate the more likely the second situation +comes to pass. + +In the modern world computers are required to track both the issuance of these debts as +well as compute interest and facilitate the collection process. This is in part because payments +are often made electronically but also in part because the overhead of paying a person +to manually keep track of all this info would eat in to the profits from interest and flat fees. + +"Buy Now, Pay Later" programs also want to be embedded into online storefronts. People are much more likely +to accept higher interest rates or take on the debt at all the closer the offer is made to "the point of sale." This requires not only business deals but a non-trivial amount of code. + +Never make a program for a "Buy Now, Pay Later" company. + +The only reason they would be using that term would be to dodge regulations +set up to protect both parties in the arrangement. + +"Buy Now, Pay Later" is just another word for debt. If you make a program that +enables desperate people go deeper and deeper into debt to enrich yourself +you are a bad person. + +I put this here to tell you that building software is not +a neutral act. You need to critically consider how what you decide to spend +your time on will affect the world around you. + +Facebook is one of the largest websites in the world. It makes a lot of +people a lot of money. The world would be a better place +if it did not exist. + +There are real companies - at the time of writing the biggest one is called [Klarna](https://klarna.com/) - +which are more than happy to offer predatory loans to the desperate and short-sighted. Their existence makes the +world a worse place. They also could not exist without the efforts of people who write software. + +But loans are not *intrinsically* a bad thing. What makes a loan bad comes down to the terms of the +loan and the circumstances under which it is taken out. + +## Your Goal + +Make a program to track the debts owed by + +## Future Goals + +None. + +[^compound]: Compound interest adds up fast. + +[^christmas]: Watch "It's A Wonderful Life" sometimes. Banking is not intrinsically evil. Loaning people +money so they can buy a home to live in can be a great thing. \ No newline at end of file diff --git a/src/projects/calorie_tracker.md b/src/projects/calorie_tracker.md new file mode 100644 index 00000000..7d478983 --- /dev/null +++ b/src/projects/calorie_tracker.md @@ -0,0 +1,58 @@ +# Calorie Tracker + +## Problem Statement + +A calorie is the amount of energy needed to raise the temperature of +1 gram of water by 1 degree celcius. A kilo-calorie, often shortened to kcal, +is one thousand calories. + +Food consumed by humans provides a certain amount of calories. Somewhat confusingly +when people talk about "calories," as might be reported on food labels, they really mean +kilo-calories. + +For most of the history of the human species food was not an abundant resource. As such +when food is abundant our brains are predisposed to consuming as much of it as possible. +This is a behavior likely evolved in order that one best withstand periods of famine.[^biology] + +The problem is that, while many places in the world still experience regular food shortages, +many people have an extreme abundance of food available to them at all times. Not only that, +the food they do have access to is often designed to be as addictive to consume as possible. + +As such many of these people gain extreme amounts of weight. +This, in turn, lead to many health problems. + +Whether someone gains or loses weight over a given period of time comes down to "CiCo" - Calories In, Calories Out. +If someone eats more in calories than they burn they are at a calorie surplus and will gain weight. +If someone eats fewer calories than they burn they are at a calorie deficit and will lose weight. + +There are also people out there in the world with the opposite problem. Not eating enough leads to starvation. +Whether because of trauma, upbringing, or some other circumstance: some people will not naturally eat enough even +when food is available to them. + +## Your Goal + +Your goal is to make a program that helps someone track the number of calories they have consumed +in a given day. + +The intent is to help them be intentional about the number of calories they are consuming. + +We will count it as a success if the program you produce at least helps them track the total. + +Hint: You will need to use `IO.readln` alongside `Integer.parseInt` and/or `Double.parseDouble`. + +## Future Goals + +When you learn enough to do the following, come back to this project and expand it. + +* Make it so that they can also track a "calorie goal" and see how they are doing with respect to that goal. +* Make it so that they can also track the name of the food. +* Make it so that if the computer running the program is turned off they do not lose information. +* Expand the program to also help them record the macro-nutritional value of the food they ate. +* Make it so that they can more easily track foods they eat often. +* Expand the program to also let them track their weight over time. +* Make it so that they can track their progress over multiple days, months, or years. +* Anything else you can think of. + + + +[^biology]: I am not an evolutionary biologist, but this is my understanding. \ No newline at end of file diff --git a/src/projects/data_visualization.md b/src/projects/data_visualization.md new file mode 100644 index 00000000..a1046ffe --- /dev/null +++ b/src/projects/data_visualization.md @@ -0,0 +1,92 @@ +# Data Visualization + +## Problem Statement + +In 1854 there was a major cholera outbreak on Broad Street in London. + +At the time it was thought that the outbreak was caused by "bad air." +This was in line with the [miasma theory of disease](https://en.wikipedia.org/wiki/Miasma_theory) +which held that plauges were literally caused by bad air coming from rotting +organic matter. + +If you've ever seen a drawing of [a plague doctor](https://en.wikipedia.org/wiki/Plague_doctor) +this is part of why they had that long nosed mask. They would fill it with nice smelling herbs +in order to counter the miasma they thought was causing disease. + +This was basically entirely wrong in terms of the mechanics of disease spread. But it wasn't all bad. Staying +away from dead bodies and wearing a mask around the sick aren't the worst ideas in the world. + +The actual cause of this cholera outbreak was not bad air but instead contaminated water. +The person who figured this out was [Dr. John Snow](https://pmc.ncbi.nlm.nih.gov/articles/PMC11416802/). + +He recorded information about where the people who were getting sick lived. Using this he was able to +show that the people who were getting sick were the ones drinking from a particular water pump +in town. He did this by [overlaying the number of people who got sick onto a map of the town](https://cdn.ncbi.nlm.nih.gov/pmc/blobs/917c/11416802/c06be2237467/cureus-0016-00000067602-i02.jpg). + +This visualization was key to determining the actual cause of the outbreak. Unfortunately it took quite +a bit longer for the germ theory of disease to be widely accepted, but Dr. Snow did at least get that +one contaminated water pump removed. + +One moral of the story - and there are a few - is that making a visual representation of +data can be key to the interpretation of that data. If you have a file with a million +numbers in it that is not exactly interpretable. If you took the data in that file, +plotted it, and saw a straight line then that is useful information. + +Computers are uniquely suited to making visualizations. Modern data sets +are often quite large and already stored on a computer. It is easier for a computer +to turn that data into a chart or diagram of some kind than it is +for a human to sit down and labor with a sheet of paper and a ruler. + +## Your Goal + + +Fill up a file named `data.txt` with a bunch of numbers you type randomly on your keyboard. So something like the following + +``` +124124523950159219359858327587324573258723458342756734 +``` + + +Write a program which reads that file and writes a new file containing a series of [bar charts](https://en.wikipedia.org/wiki/Bar_chart) representing how many times a given digit appears in the file. People aren't that good at picking random numbers so you are bound to +see one bar be higher than the others. + + +This will require you to learn how to create an image from code. The good news is that this is easier than +you would expect. There is a file format called "[PPM](https://en.wikipedia.org/wiki/Netpbm)" which represents images like this. + +```no_run +P3 +# "P3" means this is a RGB color image in ASCII +# "3 2" is the width and height of the image in pixels +# "255" is the maximum value for each color +# This, up through the "255" line below are the header. +# Everything after that is the image data: RGB triplets. +# In order: red, green, blue, yellow, white, and black. +3 2 +255 +255 0 0 + 0 255 0 + 0 0 255 +255 255 0 +255 255 255 + 0 0 0 +``` + +So if you write text like this into a file and give it a `.ppm` extension your computer should be +able to show it to you as an image. + +You will still need to figure out how you are going to represent image data in your program, +how to produce a file like this, and how to arrange the pixels. That should all be within your +ability at this point though.[^believe] + +## Future Goals + +* Expand this program to support other kinds of visualizations such as scatterplots, pie charts, and line graphs. +* Visualize some data that comes from a [CSV](https://en.wikipedia.org/wiki/Comma-separated_values) file. +* Make the program use a different method of data visualization based on a command-line flag. +* For scatterplots, allow adding a "line of best fit." Look up "linear regression" for one way to do this. +* Add labels and axes to your charts. This will require figuring out how to render text in your images. +* Try to make a spacial map similar to the one that Dr. Snow made. This is likely pretty involved to do +in a program but it is definitely possible. + +[^believe]: I believe in you \ No newline at end of file diff --git a/src/projects/music_maker.md b/src/projects/music_maker.md new file mode 100644 index 00000000..aaad45b3 --- /dev/null +++ b/src/projects/music_maker.md @@ -0,0 +1,57 @@ +# Music Maker + +## Problem Statement + +Sounds are formed by waves propagating through the air +at various frequencies. When sound enters your ear [it vibrates your eardrum](https://www.nidcd.nih.gov/health/how-do-we-hear) which in turn vibrates other parts of your ear ultimately culminating +in your brain perceiving a sound. The frequencies +of sound determine in what way your eardrum will vibrate and therefore are what determine how you +will percieve any given sound. + +If you want to recreate a sound you need to in some way record what that sound was. +The oldest example we have of someone doing this dates back [to a thousands of years old stone tablet.](https://www.youtube.com/watch?v=KElPnD-dbkk) The idea being that if someone could read that tablet +and knew what the symbols on it meant, they could use an instrument of some kind to reproduce the song. + +While stone tablets were awesome and paper acceptable for the task, writing down what someone else +should play only lets you record music. Writing down words similarly doesn't help record +the sound of someone's voice.[^lincoln] + +The first of recording of someone's voice was done [in 1860 in Paris by Édouard-Léon Scott de Martinville](https://www.firstsounds.org/sounds/scott.php). He would later have his glory stolen by Thomas Edison; a common occurrence for the time. + +Nowadays computers are generally more convenient for sharing music and recordings than other +methods, if sometimes of lower quality than something like a [vinyl record](https://victrola.com/blogs/articles/does-music-really-sound-better-on-vinyl)[^debate]. + +Storing audio on a computer requires translating audio information into a form +that a computer can store. We call this translation step "digitization." +It requires in some manner storing what frequencies of sounds +and in what proportion need to be produced to "play back" a sound. + + +## Your Goal + +Make a program that produces a WAV file that, when played, +will sound like an instrumental version of [Three Blind Mice](https://en.wikipedia.org/wiki/Three_Blind_Mice)[^haloreach]. + +As a hint: `byte` and `short` can be helpful when representing "binary formats" like WAV. +Reading comprehension as well as reading stamina will also be useful for figuring out +how a WAV file works. You will need to learn a lot about audio. + +## Future Goals + +When you learn enough to do the following, come back to this project and expand it. + +* Make the program produce other songs. +* Expand the program to take as input a text file that in some way describes a song and produce a WAV file +in turn. +* Make a "virtual keyboard" where somebody can play notes by typing and whatever they played can +be "exported" as a WAV file. +* Support a file format other than WAV as the output. + +[^lincoln]: Abraham Lincoln had a really high pitched trill voice apparently. It is a real bummer we don't have recordings. + +[^debate]: This is a subject of much debate. + +[^haloreach]: When Halo Reach came out there was a lot of internet fighting about the "reticle bloom" +on a weapon called the DMR. This meant that if you fired it too quickly it would get +less and less accurate. The advice I heard around that time was to pull the trigger to the tune +of "three blind mice" and that would be about the right timing. \ No newline at end of file diff --git a/src/projects/point_of_sale_system.md b/src/projects/point_of_sale_system.md new file mode 100644 index 00000000..553b449d --- /dev/null +++ b/src/projects/point_of_sale_system.md @@ -0,0 +1,81 @@ +# Point of Sale System + +## Problem Statement + +Consider the operations of a grocery store. + +Prospective customers walk in the store, perhaps acquiring a cart or a basket, +and gather the items they want to purchase. They then walk to the front of the +store, purchase those items using money, and leave. + +Some items are priced by quantity. For example, one [avacado](https://en.wikipedia.org/wiki/Avocado) might cost $0.77. +If they purchased 5 avacados they would have to pay $0.77 five times or $3.85 in total. + +Other items are priced by weight. Bananas are [really cheap for some reason](https://en.wikipedia.org/wiki/Banana_republic) +and so 1 pound of bananas costs $0.39. If they purchase 1.5 pounds of bananas they would have to pay 1.5 times $0.39 or $0.585 in total. Currencies like the dollar do not have a "tenth of a penny" so after this math the amount will be rounded +either to $0.58 or $0.59 depending on the policy of the store in question. + +Either way it is uncommon for someone to have exactly $3.85 or $0.59 on their person. So to facilitate purchases +using physical money they must accept larger bills and offer change for the difference. If someone tries to use a $10 bill +to buy $3.85 of avacados the store will provide $6.15 back to the customer in change. + +While a business can operate this way without any sort of computer involved there are several benefits to having one. + +The price a store wants to charge for any given item is bound to change over time. When training a cashier it can make +more sense to teach them short codes for different items and have the computer system figure out the price to use[^plu]. This would generally include telling them to weigh an item or count the quantity of said item. + +In addition to the price, calculating change is easy for computers to do. While it is not hard for a human to do either, +a computer will make fewer mistakes calculating the difference between $45 and $13.53. This is especially true over the course of an 8 hour shift where mental fatigue can start to set in. + +It is also helpful for a business to know how much they sold in a given day. Having computers in the mix makes it more practical to get these "end of day reports." They might use these reports to know that they need to order more onions because those are selling well recently.[^viral] They can also be used to catch cashiers who are stealing from the register +to support their families.[^common] + +We call these systems that are used at the point products are sold Point of Sale Systems. + +## Your Goal + +Make a program that can be used as a point of sale system for a hypothetical produce stand. + +The products the produce stand sells are as follows: + +| Item | Cost | +|------|------ | +| Banana | $0.39 per pound | +| Avacado | $0.66 each | +| Plantains | $0.99 each | +| Watermelon | $6.99 each | +| Onion | $0.62 each | +| Celery |$0.31 per ounce| +| Carrot |$0.06 per ounce| +| Cabbage |$2.72 each| + +Customers may purchase any amount of any items in any combination. + +Make sure to at least: + +* Track the total for an order. +* Prompt the cashier for the right quantities and weights. +* At the end of an order give the cashier the total and have them enter the amount given to them by the customer. +* Tell the cashier how much in change they need to provide. + +## Future Goals + +When you learn enough to do the following, come back to this project and expand it. + +* Grow your program to support double or triple the number of available items. +* Make it so that the cashier has to "log in" to use the system +* Account for a manager whose job it is to record how much money is in the register before and after a day of operations +and note any unaccounted for funds. +* Make the program not lose information if the computer running it turns off then on again. +* Make the system work for "self check out," where the person entering the items is also the customer. Theft in +these cases is monitored via security cameras and punished via the might of the police state. +* Use a camera attached to the computer to scan a bar code which contains the product number. +* Optionally print a receipt for each order to a physical printer.. + + + +[^plu]: Here is a list of "[PLU](https://www.fsproduce.com/wp-content/uploads/2015/05/2011-PLU-Listing1.pdf)" codes. 4011 is banana. + +[^viral]: Due to a viral onion tart recipie or something. + +[^common]: This is common in no small part because cashiers, like many modern professions, are not paid a livable wage. \ No newline at end of file diff --git a/src/projects/prelude.md b/src/projects/prelude.md new file mode 100644 index 00000000..674bb717 --- /dev/null +++ b/src/projects/prelude.md @@ -0,0 +1,22 @@ +# Prelude + +Separate from the challenges at the end of each section I will be putting some projects +sections throughout the book. + +While the purpose of the challenges is to make you practice what you just read, +the purpose of the projects is for you to put that knowledge into action. + +This is for two reasons + +1. If you don't practice for real, it is very difficult to learn things. +2. I want to drive home that software is an interdisciplinary field. + +The first reason means that, while be some early hand-holding, I largely expect +you to put together projects on your own using what you have been shown. +This means you will struggle. The hope is that in that struggle you are forced to learn, +but you can always ask for help. + +By that second point I mean we make software to do things in the real world. +To do this requires understanding who would use the software, why, and for what purpose. +I am going to try my best to have projects that make you interact with the world outside +of software construction. diff --git a/src/records.md b/src/records.md index 8fb0e3cd..8591c9ed 100644 --- a/src/records.md +++ b/src/records.md @@ -1,5 +1,7 @@ # Records + + If you have a class whose only purpose is to ferry data around, you can instead use a `record`. diff --git a/src/records/challenges.md b/src/records/challenges.md new file mode 100644 index 00000000..04e0ec6c --- /dev/null +++ b/src/records/challenges.md @@ -0,0 +1,105 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Replace the usage of a normal class in the following code with a record. + +```java,editable +import java.util.Arrays; + +class InventoryItem { + String name; + + InventoryItem(String name) { + this.name = name; + } +} + +class Main { + void main() { + var item1 = new InventoryItem("sword"); + var item2 = new InventoryItem("shield"); + var item3 = new InventoryItem("armor"); + + InventoryItem[] items = { + item1, + item2, + item3 + }; + + IO.println(Arrays.toString(items)); + } +} +``` + +## Challenge 2. + +Make an instance of the `Tien` record and print it out. + +```java,editable +enum TienMove { + DODON_RAY, + TRI_BEAM, + EXPLODE +} + +record Chiaotzu( + Move memorableMove +) {} + +record Tien( + Chiaotzu onlyFriend, + Move firstMove, + Move secondMove +) {} + +class Main { + void main() { + Tien tien; + // CODE HERE + IO.println(tien); + } +} +``` + +## Challenge 3. + +What will this program output when run? + +1. `true` then `true` +2. `true` then `false` +3. `false` then `true` +4. `false` then `false` + +Write down your guess and then try running it. + +```java +class Position { + int x; + int y; + + Position(int x, int y) { + this.x = x; + this.y = y; + } +} + +record Location(int x, int y) {} + +class Main { + void main() { + var p1 = new Position(7, 1); + var p2 = new Position(7, 1); + IO.println(p1.equals(p2)); + + var l1 = new Location(8, 3); + var l2 = new Location(8, 3); + IO.println(l1.equals(l2)); + } +} +``` \ No newline at end of file diff --git a/src/records/header.png b/src/records/header.png new file mode 100644 index 00000000..53089a62 Binary files /dev/null and b/src/records/header.png differ diff --git a/src/records/return_multiple_values.md b/src/records/return_multiple_values.md index 3be89ed3..5937f097 100644 --- a/src/records/return_multiple_values.md +++ b/src/records/return_multiple_values.md @@ -9,7 +9,7 @@ A record is likely better for that purpose than a regular class. record Location(double latitude, double longitude) {} Location findTreasureIsland() { - return new Location(40.2085, -3.713); + return new Location(51.4075, 0.4636); } void main() { diff --git a/src/recur b/src/recur deleted file mode 100644 index 9ceb45de..00000000 --- a/src/recur +++ /dev/null @@ -1 +0,0 @@ -# Accumulators diff --git a/src/recursion.md b/src/recursion.md index 71cd54eb..c015b920 100644 --- a/src/recursion.md +++ b/src/recursion.md @@ -1,5 +1,8 @@ # Recursion + + + In a method you can call another method. ```java diff --git a/src/recursion/challenges.md b/src/recursion/challenges.md new file mode 100644 index 00000000..50e23395 --- /dev/null +++ b/src/recursion/challenges.md @@ -0,0 +1,124 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Write code that outputs every number from `1` to an arbitrary number + +You are not allowed to use `while` or `for` loops. + +```java,editable +// CODE HERE + +void main() { + int n = 30; + + // CODE HERE +} +``` + +## Challenge 2. + +Write code that will output each character of `name` on its own line. + +So if name is equal to "Riyo", I would expect the following as output. + +```text +R +i +y +o +``` + +You are not allowed to use `while` or `for` loops. + +```java,editable +// CODE HERE + +void main() { + String name = "Rudo"; + + // CODE HERE +} +``` + +## Challenge 3. + +Write code that will take a number and if it is divisible by two, divides it by two. If it is not, multiplies it by three and adds one. + +Keep doing this until the number equals one. Output it each time. + +If the initial number is 6 you should have this as output. + +```text +6 +3 +10 +5 +16 +8 +4 +2 +1 +``` + +You are not allowed to use `while` or `for` loops. + +```java,editable +// CODE HERE + +void main() { + // Change this value to test your code. + int n = 15; + + // CODE HERE +} +``` + +## Challenge 4. + +Write code that outputs the number of vowels in name. Treat y as a vowel. + +Treat the characters a, A, e, E, i, I, o, O, u, U, y, and Y as vowels. + +You are not allowed to use `while` or `for` loops. + +```java,editable +// CODE HERE + +void main() { + // Change this value to test your code. + String name = "Zanka"; + + // CODE HERE +} +``` + +## Challenge 5. + +Draw a square. + +Make it so that you can make the square bigger or smaller by changing a variable at the start of the program. + +``` +***** +***** +***** +***** +``` + +You are not allowed to use `while` or `for` loops. + +```java,editable +// CODE HERE + +void main() { + int size = 5; + + // CODE HERE +} +``` \ No newline at end of file diff --git a/src/recursion/header.png b/src/recursion/header.png new file mode 100644 index 00000000..4f481660 Binary files /dev/null and b/src/recursion/header.png differ diff --git a/src/reflection.md b/src/reflection.md index 48f13784..7899770c 100644 --- a/src/reflection.md +++ b/src/reflection.md @@ -1,5 +1,7 @@ # Reflection + + Reflection is what we call it when a program uses information about how it - the program - is structured in order to do things while running. diff --git a/src/reflection/challenges.md b/src/reflection/challenges.md new file mode 100644 index 00000000..3d598a09 --- /dev/null +++ b/src/reflection/challenges.md @@ -0,0 +1,253 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Write a method `startsWithVowel` that takes an `Object` +as input and returns if the name of the underlying +class for that `Object` starts with a vowel. + +You might need to consult the documentation +for [Class](https://docs.oracle.com/en/java/javase/24/docs/api/index.html). + +```java,editable +class Apple {} +class Banana {} + +class Main { + boolean startsWithVowel(Object o) { + // CODE HERE + } + void main() { + // Integer -> i -> true + IO.println(startsWithVowel(4)); + + + // String -> s -> false + IO.println(startsWithVowel("abc")); + + + // Apple -> a -> true + IO.println(startsWithVowel(new Apple())); + + // Banana -> b -> false + IO.println(startsWithVowel(new Banana())); + } +} +``` + +## Challenge 2. + +Write a method `toMap` that takes an `Object` +as input and returns a `Map` +with all the object's field names as keys and field values +as the value. + +```java,editable +class HankHill { + public double loveForPropane = 100; + public String shock = "bwhaaha"; +} + +class BobbyHill { + public boolean hasCulinaryAcumen = true; +} + +class CottonHill { + public boolean bitter = true; + public boolean angry = true; + public boolean short = true; + public boolean dead = true; +} + +class Main { + Map toMap(Object o) { + // CODE HERE + } + + void main() { + // {loveForPropane=100, shock=bwhaaha} + IO.println(toMap(new HankHill())); + + // {hasCulinaryAcumen=true} + IO.println(toMap(new BobbyHill())); + + // {bitter=true, angry=true, short=true, dead=true} + IO.println(toMap(new CottonHill())); + + } +} +``` + +## Challenge 3. + +Write a method `fromMap` that takes a `Map` +and a `Class` +and returns an `Object` whose fields are all filled in using +the values in the map. + +Assume that the given `Class` has a zero argument constructor you can +call to get an "empty" instance of the class. + +Add your own `toString` methods to the example classes to debug your work. + +```java,editable +class HankHill { + public double loveForPropane; + public String shock; + + // CODE HERE +} + +class BobbyHill { + public boolean hasCulinaryAcumen; + + // CODE HERE +} + +class CottonHill { + public boolean bitter; + public boolean angry; + public boolean short; + public boolean dead; + + // CODE HERE +} + +class Main { + Object fromMap(Map o, Class klass) { + // CODE HERE + } + + void main() { + IO.println(fromMap(Map.of( + "loveForPropane", 100, + "shock", "bwhaaha" + ), HankHill.class)); + + IO.println(fromMap(Map.of( + "hasCulinaryAccumen", true + ), BobbyHill.class)); + + IO.println(fromMap(Map.of( + "bitter", true, + "angry", true, + "short", true, + "dead", true + ), CottonHill.class)); + } +} +``` + +## Challenge 4. + +Call all the methods declared on the `Dale` +class in alphabetical order using reflection.[^speech] + +Make sure not to call methods inherited from `Object` +such as `toString`, `equals`, and `hashCode`. + +```java +class Dale { + public static void u() { + IO.println("and get yourself out of that tunnel and into some strange woman's bed!"); + } + + public static void t() { + IO.println("wash off some of that cologne,"); + } + + public static void d() { + IO.println("and the only way out is through a long dark tunnel."); + } + + public static void f() { + IO.println("carrying a boxcar full of heartbreak."); + } + + public static void a() { + IO.println("I know how dark it is for you right now"); + } + + public static void k() { + IO.println("I'm fat and I'm old and every day I'm just going to wake up fatter and older."); + } + + public static void q() { + IO.println("Will I be out there next month? If I'm alive, you'd better believe it."); + } + + public static void r() { + IO.println("You've got to get up off that tanning bed,"); + } + + + public static void g() { + IO.println("Well let me tell you something:"); + } + + public static void h() { + IO.println("all you can do is let it hit you and then try to find your legs."); + } + + public static void i() { + IO.println("I know - I've taken that hit more times than I can remember"); + } + + + public static void e() { + IO.println("And you're afraid to go in because there is a train coming at ya"); + } + + public static void j() { + IO.println("Look at me Boomhauer."); + } + + + public static void m() { + IO.println("I'm out there digging holes, falling into 'em, climbing out, trying again"); + } + + public static void c() { + IO.println("You're in Hell now Boomhauer"); + } + + public static void n() { + IO.println("And tomorrow I'm going to hang outside at a ladies' prison,"); + } + + public static void o() { + IO.println("and the first thing those lady cons are going to see after twenty years is me."); + } + + public static void l() { + IO.println("Yet somehow I managed to drag this fat old bald bastard into the alley every day."); + } + + public static void p() { + IO.println("Will I get one? Experience says no."); + } + + public static void s() { + IO.println("slip into a tight T-shirt,"); + } + + public static void b() { + IO.println("curled up lying in your own emotional vomit."); + } +} + +class Main { + void main() { + var dale = Dale.class; + + // CODE HERE + } +} +``` + +[^speech]: https://www.youtube.com/watch?v=7nkrzI9GwNk \ No newline at end of file diff --git a/src/reflection/header.png b/src/reflection/header.png new file mode 100644 index 00000000..f8644f92 Binary files /dev/null and b/src/reflection/header.png differ diff --git a/src/regular_expressions.md b/src/regular_expressions.md new file mode 100644 index 00000000..920bbd2a --- /dev/null +++ b/src/regular_expressions.md @@ -0,0 +1,42 @@ +# Regular Expressions 🚧 + + + +Phone numbers in the US mostly look like `123-456-7890`. Three numbers, +a dash, three numbers, a dash, then four numbers.[^simple] + +Writing the code to check if a `String` matches this pattern is tricky. + +```java,no_run +boolean matchesPhoneNumberPattern(String s) { + return s.length() == 12 + && Character.isDigit(s.charAt(0)) + && Character.isDigit(s.charAt(1)) + && Character.isDigit(s.charAt(2)) + && s.charAt(3) == '-' + && Character.isDigit(s.charAt(4)) + && Character.isDigit(s.charAt(5)) + && Character.isDigit(s.charAt(6)) + && s.charAt(7) == '-' + && Character.isDigit(s.charAt(8)) + && Character.isDigit(s.charAt(9)) + && Character.isDigit(s.charAt(10)) + && Character.isDigit(s.charAt(11)) +} +``` + +For situations like this a useful tool is something called "regular expressions" +and the `Pattern` class. + +```java,no_run +boolean matchesPhoneNumberPattern(String s) { + return Pattern.compile("(\\d\\d\\d)-(\\d\\d\\d)-(\\d\\d\\d\\d)") + .asMatchPredicate() + .test(s); +} +``` + + +[^simple]: A common, but understandable, mistake is assuming that formats +like phone numbers are actually this simple. Phone numbers and emails are always a little +bit of a nightmare to validate. \ No newline at end of file diff --git "a/src/regular_expressions/Screenshot 2025-08-25 at 3.28.13\342\200\257PM.png" "b/src/regular_expressions/Screenshot 2025-08-25 at 3.28.13\342\200\257PM.png" new file mode 100644 index 00000000..604e6b8f Binary files /dev/null and "b/src/regular_expressions/Screenshot 2025-08-25 at 3.28.13\342\200\257PM.png" differ diff --git a/src/regular_expressions/character_classes.md b/src/regular_expressions/character_classes.md new file mode 100644 index 00000000..52783db6 --- /dev/null +++ b/src/regular_expressions/character_classes.md @@ -0,0 +1 @@ +# Character Classes diff --git a/src/regular_expressions/exact_matches.md b/src/regular_expressions/exact_matches.md new file mode 100644 index 00000000..ad090f05 --- /dev/null +++ b/src/regular_expressions/exact_matches.md @@ -0,0 +1 @@ +# Exact Matches diff --git a/src/regular_expressions/header.png b/src/regular_expressions/header.png new file mode 100644 index 00000000..4d2458d0 Binary files /dev/null and b/src/regular_expressions/header.png differ diff --git a/src/regular_expressions/pattern.md b/src/regular_expressions/pattern.md new file mode 100644 index 00000000..662d3a5f --- /dev/null +++ b/src/regular_expressions/pattern.md @@ -0,0 +1 @@ +# Pattern diff --git a/src/regular_expressions/theoretical_basis.md b/src/regular_expressions/theoretical_basis.md new file mode 100644 index 00000000..61ecbc43 --- /dev/null +++ b/src/regular_expressions/theoretical_basis.md @@ -0,0 +1,17 @@ +# Theoretical Basis + +There are basically two levels you can engage the concept of regular expressions +at. + +The first is as a practical tool for matching patterns in text. You will learn +how to construct special strings that act as a shorthand for code that can +recognize if text meets a certain shape. + +The second is around their place in the theory of computation. A regular expression +is one step above a "deterministic finite automata" and one step below a "turing machine." + +I tend to find that diving into the theoretical makes the practical part a lot less intimidating. +It will be a giant tangent, but i suggest [diving in to a theory of computation course](https://www.youtube.com/watch?v=rJhfQp1jgGI&list=PLRgsEjJNLnh7yx0AAQMrdJssDySazeJzZ)[^random]. When you come out +of it regular expressions will be much easier to understand. + +[^random]: I just searched online and this one popped up. It looks fine, but maybe its not or maybe there is something better. \ No newline at end of file diff --git a/src/return_values.md b/src/return_values.md index 127b6f88..1a636d37 100644 --- a/src/return_values.md +++ b/src/return_values.md @@ -1,5 +1,8 @@ # Return + + + If the only thing you could do with methods was to call them, they would have limited uses. diff --git a/src/return_values/challenges.md b/src/return_values/challenges.md index 0bf53fd9..314585dd 100644 --- a/src/return_values/challenges.md +++ b/src/return_values/challenges.md @@ -83,6 +83,3 @@ void main() { } ``` -## Challenge 5. - - diff --git a/src/return_values/header.png b/src/return_values/header.png new file mode 100644 index 00000000..a09743f9 Binary files /dev/null and b/src/return_values/header.png differ diff --git a/src/return_values/return_in_void_methods.md b/src/return_values/return_in_void_methods.md index 77e30bfe..aecce210 100644 --- a/src/return_values/return_in_void_methods.md +++ b/src/return_values/return_in_void_methods.md @@ -14,5 +14,5 @@ void doStuff() { IO.println(i); } } -``` - +~void main() {doStuff();} +``` \ No newline at end of file diff --git a/src/standard_input.md b/src/standard_input.md index 64a59fc7..e528fd38 100644 --- a/src/standard_input.md +++ b/src/standard_input.md @@ -1,5 +1,8 @@ # Standard Input + + + Programs are pretty boring if they just make a machine warm and do some math. There are a lot of ways to make a program interactive, but the easiest is to read diff --git a/src/standard_input/booleans.md b/src/standard_input/booleans.md new file mode 100644 index 00000000..3d7bb6cf --- /dev/null +++ b/src/standard_input/booleans.md @@ -0,0 +1 @@ +# Booleans diff --git a/src/standard_input/challenges.md b/src/standard_input/challenges.md new file mode 100644 index 00000000..ceb85eaf --- /dev/null +++ b/src/standard_input/challenges.md @@ -0,0 +1,70 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1 + +Write a program that asks a person for their name and then says +"Hello \" back to them. + +```java,no_run +void main() { + // 1. Call IO.readln to get their name + // 2. Call IO.print/IO.println to say hello to them +} +``` + +## Challenge 2 + +Write a program that asks a person their age and tells them +what age they will be this time next year. + +```java,no_run +void main() { + // 1. Call IO.readln to get their age + // 2. Interpret their age as an int + // 3. Add one to that age + // 4. Call IO.print/IO.println to say what age they will be next year +} +``` + +## Challenge 3 + +Write a program that asks a person for two floating point numbers and tells them +what the sum of those two numbers is + +```java,no_run +void main() { + // 1. Call IO.readln to get the first number + // 2. Interpret that first number as a double + // 3. Call IO.readln to get the second number + // 4. Interpret that second number as a double + // 5. Add the two numbers together + // 6. Call IO.print/IO.println to say what the sum is +} +``` + +## Challenge 4 + +["Mad Libs"](https://en.wikipedia.org/wiki/Mad_Libs) are a word game where you ask people for nouns, verbs, adjectives, etc. absent any context +and then fill them in to a template. + +For example + +``` +I saw a today and . +Unfortunately the stopped me at the . +``` + +Can become + +``` +I saw a dog today and flew. +Unfortunately the clown stopped me at the elephant. +``` + +Make a program that asks a user for some nouns, verbs, etc. and prints +a Mad Lib using those words. \ No newline at end of file diff --git a/src/standard_input/floating_point_numbers.md b/src/standard_input/floating_point_numbers.md new file mode 100644 index 00000000..7780a990 --- /dev/null +++ b/src/standard_input/floating_point_numbers.md @@ -0,0 +1,15 @@ +# Floating Point Numbers + +If you expect someone to type a floating point value you can turn the `String` +you get from `IO.readln` into a `double` using `Double.parseDouble`. + +```java,no_run +void main() { + String gpaString = IO.readln("What is your GPA? "); + double gpa = Double.parseDouble(gpaString); + IO.println("You're GPA is " + gpa); +} +``` + +So long as they type something which can be interpreted as a `double` (like `123` or `14.5`) +you will get a value for the `double` variable. Otherwise the program will crash. \ No newline at end of file diff --git a/src/standard_input/header.png b/src/standard_input/header.png new file mode 100644 index 00000000..50b7a879 Binary files /dev/null and b/src/standard_input/header.png differ diff --git a/src/standard_input/integers.md b/src/standard_input/integers.md new file mode 100644 index 00000000..4638268a --- /dev/null +++ b/src/standard_input/integers.md @@ -0,0 +1,16 @@ +# Integers + +If you expect someone to type an integer value you can turn the `String` +you get from `IO.readln` into an `int` using `Integer.parseInt`. + +```java,no_run +void main() { + String ageString = IO.readln("How old are you? "); + int age = Integer.parseInt(ageString); + IO.println("You are " + age + " years old!"); +} +``` + +So long as they type something which can be interpreted as an `int` (like `123`) +you will get a value for the `int` variable. Otherwise +the program will crash. \ No newline at end of file diff --git a/src/standard_input/interpreting_input.md b/src/standard_input/interpreting_input.md index 65743573..edeef944 100644 --- a/src/standard_input/interpreting_input.md +++ b/src/standard_input/interpreting_input.md @@ -3,17 +3,18 @@ When you call `IO.readln` the human on the other side can type whatever they want. This means that, depending on the question you asked, you might need to interpret -what they typed as something other than a generic `String`. +what they typed as something other than a `String`. In its most basic form this will look like seeing if one `String` equals another `String`. ```java,no_run void main() { - while (true) { - String shouldExit = IO.readln("Exit the program? (y/n)"); - if (shouldExit.equals("y")) { - break; - } + String color = IO.readln("What is your favorite color? "); + if (color.equals("green")) { + IO.println("Me too!"); + } + else { + IO.println("neat.") } } ``` \ No newline at end of file diff --git a/src/standard_input/other_types.md b/src/standard_input/other_types.md new file mode 100644 index 00000000..cabdbd22 --- /dev/null +++ b/src/standard_input/other_types.md @@ -0,0 +1,24 @@ +# Other Types + +Just as you can turn a `String` into an `int` using `Integer.parseInt` and you can +turn a `String` into a `double` using `Double.parseDouble`, you can use `Boolean.parseBoolean` to turn a `String` into a `boolean`. + +```java,no_run +void main() { + String happyString = IO.readln("Are you happy? "); + boolean happy = Boolean.parseBoolean(happyString); + IO.println("You are happy? " + happy); +} +``` + +But lest you become too comfortable, know that there is no `Character.parseCharacter` to turn a `String` into a character. +It is not always going to be the case that there is just one way to convert a `String` to any given type. + +```java,no_run,panics +void main() { + String gradeString = IO.readln("What is your letter grade? "); + // Does not exist! + char grade = Character.parseCharacter(gradeString); + IO.println("You have a " + grade + " in the class."); +} +``` \ No newline at end of file diff --git a/src/standard_input/prompting.md b/src/standard_input/prompting.md index b096a57a..5a7c91ae 100644 --- a/src/standard_input/prompting.md +++ b/src/standard_input/prompting.md @@ -1,4 +1,4 @@ -# input +# Prompting To prompt a user for information you use the `IO.readln` function. @@ -15,4 +15,4 @@ void main() { } ``` -`readln` stands for "read line." It reads the next line a person types. \ No newline at end of file +`readln` stands for "read line." It reads the next line a person types on "standard input". \ No newline at end of file diff --git a/src/standard_input/reprompting.md b/src/standard_input/reprompting.md deleted file mode 100644 index a1ce2c80..00000000 --- a/src/standard_input/reprompting.md +++ /dev/null @@ -1,29 +0,0 @@ -# Reprompting - -If you ask someone a yes or no question and they respond with "huh?" you might want to ask them again. - -This is a good use case for loops. You ask a question and, if the answer you get is acceptable, -you proceed as normal. If it is not then you loop back and ask again. - -```java,no_run -void main() { - while (true) { - String response = IO.readln("Answer me: yes or no"); - if (response.equals("yes")) { - IO.println("okay then!"); - } - else if (response.equals("no")) { - IO.println("also fine!"); - } - else { - IO.println("Not a valid response"); - // Will go back to the top of the loop - continue; - } - - // If a "continue" is not hit, exit the loop - break; - } -} -``` - diff --git a/src/standard_input/strings.md b/src/standard_input/strings.md new file mode 100644 index 00000000..78bf9a7e --- /dev/null +++ b/src/standard_input/strings.md @@ -0,0 +1,2 @@ +# Strings + diff --git a/src/standard_input_ii.md b/src/standard_input_ii.md new file mode 100644 index 00000000..63250810 --- /dev/null +++ b/src/standard_input_ii.md @@ -0,0 +1,10 @@ +# Standard Input II + + + + +If you are using a program and you type something slightly wrong +it does not feel good if the program then immediately crashes. + +As such it often makes sense to not only request your users type something +and then interpret what they typed, but to also give feedback and perhaps ask them again. \ No newline at end of file diff --git a/src/standard_input/transporting_data.md b/src/standard_input_ii/aggregating_data.md similarity index 94% rename from src/standard_input/transporting_data.md rename to src/standard_input_ii/aggregating_data.md index a911f30e..f94abebc 100644 --- a/src/standard_input/transporting_data.md +++ b/src/standard_input_ii/aggregating_data.md @@ -1,4 +1,5 @@ -# Transporting Data +# Aggregating Data + If you ask someone multiple questions you likely will get multiple variables worth of information. @@ -99,4 +100,4 @@ void main() { [^dto]: When you make a class just to make objects which transfer data between different parts of your program we -call those DTOs - data transfer objects. You will learn better ways to make DTOs in the future. \ No newline at end of file +sometimes call those DTOs - data transfer objects. You will learn better ways to make DTOs in the future. \ No newline at end of file diff --git a/src/standard_input_ii/challenges.md b/src/standard_input_ii/challenges.md new file mode 100644 index 00000000..bc32e647 --- /dev/null +++ b/src/standard_input_ii/challenges.md @@ -0,0 +1,86 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1 + +Write a program that asks which Twilight character Bella should have ended up with. + +It should keep asking this question until the user types the magic words "i do not care" + +```java,no_run +void main() { + // CODE HERE +} +``` + +## Challenge 2 + +Update the program above to also accept "I DO NOT CARE", "I Do Not Care", and +any other mix of capital and lower-case letters + + +## Challenge 3 + +Write a method called `askForBirthday` that asks for the year, month, and day that someone was born. +This method should return all three pieces of information with a `Birthday` class. + +```java,no_run +class Birthday { + // CODE HERE +} + +Birthday askForBirthday() { + // CODE ALSO HERE +} + +void main() { + Birthday b = askForBirthday(); + IO.println(b.year + "-" + b.month + "-" + b.day) +} +``` + +## Challenge 4 + +Update the program above to reprompt the user if they enter any nonsensical information +for the year, month, or day. This includes negative numbers for the year, month, or day. + +## Challenge 5 + +Update the birthday program to represent the month with an enum named `Month`. + +Accept both the name of the month with any capitalization and the number of the month (starting with `1` for January) as valid ways to specify the month. + +Make sure to still reprompt on unexpected inputs. + +```java,no_run +enum Month { + JANUARY, + FEBRUARY, + MARCH, + APRIL, + MAY, + JUNE, + JULY, + AUGUST, + SEPTEMBER, + NOVEMBER, + DECEMBER +} + +class Birthday { + // CODE HERE +} + +Birthday askForBirthday() { + // CODE ALSO HERE +} + +void main() { + Birthday b = askForBirthday(); + IO.println(b.year + "-" + b.month + "-" + b.day) +} +``` diff --git a/src/standard_input/delayed_assignment.md b/src/standard_input_ii/delayed_assignment.md similarity index 100% rename from src/standard_input/delayed_assignment.md rename to src/standard_input_ii/delayed_assignment.md diff --git a/src/standard_input_ii/enums.md b/src/standard_input_ii/enums.md new file mode 100644 index 00000000..5db12e27 --- /dev/null +++ b/src/standard_input_ii/enums.md @@ -0,0 +1,82 @@ +# Enums + +Just as you might want to interpret what someone typed as an `int` or `double` +there are times you will want to interpret input as an `enum` value. + +To do this you can write `.valueOf` after the name of the enum. So for a `StopLight` enum `StopLight.valueOf` +can interpret a `String` as a `StopLight`. + +```java,no_run +enum StopLight { + RED, + YELLOW, + GREEN +} + +void main() { + String colorString = IO.readln("What color was the stoplight? "); + StopLight color = StopLight.valueOf(colorString); + IO.println("The stop light was " + color); +} +``` + +This will throw an exception if the `String` does not match, so you can reprompt using the same `try`/`catch` structure as you would use with `Integer.parseInt`. + +```java,no_run +enum StopLight { + RED, + YELLOW, + GREEN +} + +void main() { + StopLight color; + while (true) { + String colorString = IO.readln("What color was the stoplight? "); + try { + color = StopLight.valueOf(colorString); + } catch (RuntimeException e) { + continue; + } + + break; + } + + IO.println("The stop light was " + color); +} +``` + +Unfortunately, this only works if what they typed is exactly the name of an enum variant. So +in the example above they need to type `RED` in all capital letters. + +To have a different mapping of strings to enum values you need to write code yourself. + +```java,no_run +enum StopLight { + RED, + YELLOW, + GREEN +} + +StopLight stringToStopLight(String s) { + if (s.equals("r")) { + return StopLight.RED; + } + else if (s.equals("y")) { + return StopLight.YELLOW; + } + else if (s.equals("g")) { + return StopLight.GREEN; + } + else { + throw new RuntimeException("Unknown color.") + } +} + +void main() { + String colorString = IO.readln("What color was the stoplight? "); + StopLight color = stringToStopLight(colorString); + IO.println("The stop light was " + color); +} +``` + diff --git a/src/standard_input_ii/header.png b/src/standard_input_ii/header.png new file mode 100644 index 00000000..d0109d8e Binary files /dev/null and b/src/standard_input_ii/header.png differ diff --git a/src/standard_input/leniency.md b/src/standard_input_ii/leniency.md similarity index 100% rename from src/standard_input/leniency.md rename to src/standard_input_ii/leniency.md diff --git a/src/standard_input_ii/reprompting.md b/src/standard_input_ii/reprompting.md new file mode 100644 index 00000000..79783dd8 --- /dev/null +++ b/src/standard_input_ii/reprompting.md @@ -0,0 +1,55 @@ +# Reprompting + +If you ask someone a yes or no question and they respond with "huh?" you might want to ask them again. + +This is a good use case for loops. You ask a question and, if the answer you get is acceptable, +you proceed as normal. If it is not then you loop back and ask again. + +```java,no_run +void main() { + while (true) { + String response = IO.readln("Answer me: yes or no"); + if (response.equals("yes")) { + IO.println("okay then!"); + } + else if (response.equals("no")) { + IO.println("also fine!"); + } + else { + IO.println("Not a valid response"); + // Will go back to the top of the loop + continue; + } + + // If a "continue" is not hit, exit the loop + break; + } +} +``` + +If the program would normally crash on unexpected input you can use `try` and `catch` to recover +from this and reprompt the user. + +This is applicable to `Integer.parseInt`, `Double.parseDouble`, and any other method that would +throw an exception on unexpected inputs. + +```java,no_run +void main() { + int number; + while (true) { + String response = IO.readln("What is your least favorite number? "); + try { + // Here Integer.parseInt might throw an exception, + number = Integer.parseInt(response); + } catch (RuntimeException e) { + // If that happens, go up to the top and reprompt + continue; + } + + // If a "continue" is not hit, exit the loop + break; + } + + IO.println("Your least favorite number is " + number); +} +``` \ No newline at end of file diff --git a/src/standard_input_iii.md b/src/standard_input_iii.md new file mode 100644 index 00000000..2cfb4bda --- /dev/null +++ b/src/standard_input_iii.md @@ -0,0 +1 @@ +# Standard Input III diff --git a/src/static_fields.md b/src/static_fields.md index 3e05fc14..8961067c 100644 --- a/src/static_fields.md +++ b/src/static_fields.md @@ -1,5 +1,7 @@ # Static Fields + + To have a field be truly global for your program you can mark it as static. ```java diff --git a/src/static_fields/challenges.md b/src/static_fields/challenges.md new file mode 100644 index 00000000..173aec75 --- /dev/null +++ b/src/static_fields/challenges.md @@ -0,0 +1,90 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1 + +Add a static field to this `Ogre` class that keeps track of +how many Ogres have been made thus far in the program.[^threads] + +```java,editable +class Ogre { + // CODE HERE + + Ogre() { + // CODE HERE + } +} + +class Main { + void main() { + // 0 + IO.println(Ogre.NUMBER_OF_OGRES_MADE); + + // 1 + Ogre o1 = new Ogre(); + IO.println(Ogre.NUMBER_OF_OGRES_MADE); + + // 2 + Ogre o2 = new Ogre(); + IO.println(Ogre.NUMBER_OF_OGRES_MADE); + + // 3 + Ogre o3 = new Ogre(); + IO.println(Ogre.NUMBER_OF_OGRES_MADE); + + // 4 + Ogre o4 = new Ogre(); + IO.println(Ogre.NUMBER_OF_OGRES_MADE); + + // 5 + Ogre o5 = new Ogre(); + IO.println(Ogre.NUMBER_OF_OGRES_MADE); + } +} +``` + +## Challenge 2 + +Initialize the `PI` and `TAU` static final fields inside of a static initializer block. +`TAU` should have twice the value of `PI`. + +```java +class Maths { + static final double PI; + static final double TAU; + + static { + // CODE HERE + } +} + +class Main { + void main() { + IO.println(Maths.PI); + IO.println(Maths.TAU); + } +} +``` + +## Challenge 3 + +Rename the constants in the `Doug` class in the way that would +be expected of you by others. + +```java +class Doug { + static final String pattyMayonnaise = "Patty Mayonnaise"; + static final String sKeEtEr = "Mosquito 'Skeeter' Valentine"; + static final String mosquito_valentine = sKeEtEr; + static final String rodgerMKlotz = "Rodger M. Klotz"; + static final String DOUG = "Douglas Yancy Funnie"; +} +``` + + +[^threads]: Part of why mutable static fields are such a nightmare is that code like this would +not work when you have to write "multi-threaded" Java code. There are things you can do with normal fields to sort of "make unsafe stuff safe in a way," but static fields are a lot harder to wrangle. \ No newline at end of file diff --git a/src/static_fields/header.png b/src/static_fields/header.png new file mode 100644 index 00000000..517baaa4 Binary files /dev/null and b/src/static_fields/header.png differ diff --git a/src/static_methods.md b/src/static_methods.md index c779f2ad..6e375863 100644 --- a/src/static_methods.md +++ b/src/static_methods.md @@ -1,5 +1,7 @@ # Static Methods + + If you want to be able to call a method from anywhere in your program you can use a `static` method. diff --git a/src/static_methods/challenges.md b/src/static_methods/challenges.md new file mode 100644 index 00000000..4c988239 --- /dev/null +++ b/src/static_methods/challenges.md @@ -0,0 +1,131 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1 + +Change this position class such that it doesn't only have an `x` and a `y`. It should +also have a `z`. + +Then add the following static methods. `fromZ`, `fromXY`, `fromXZ`, and `fromYZ`. + +```java,editable +class Position { + int x; + int y; + + Position(int x, int y) { + this.x = x; + this.y = y; + } + + static Position fromX(int x) { + return new Position(x, 0); + } + + static Position fromY(int y) { + return new Position(0, y); + } +} + +class Main { + void main() { + var p1 = Position.fromXY(1, 2); + IO.println("(" + p1.x + ", " + p1.y + ", " + p1.z + ")"); + + var p2 = Position.fromYZ(1, 2); + IO.println("(" + p2.x + ", " + p2.y + ", " + p2.z + ")"); + + var p3 = Position.fromXZ(1, 2); + IO.println("(" + p3.x + ", " + p3.y + ", " + p3.z + ")"); + + var p4 = Position.fromZ(4); + IO.println("(" + p4.x + ", " + p4.y + ", " + p4.z + ")"); + } +} +``` + +## Challenge 2 + +Make a static method in the `Maths` class called `quadraticFormula`. +It should take in an `a`, `b`, and `c` and run the [quadradic formula](https://en.wikipedia.org/wiki/Quadratic_formula) on those values. + +This formula will give you either one "real" solution, two real solutions, +or two imaginary solutions. + +For now just throw an exception if the solutions are imaginary +and treat having one solution as having two solutions with the same value. + +```java,editable +class Maths { + // CODE HERE +} + +class Main { + void main() { + var result = Maths.quadraticFormula(6, -17, 12); + IO.println(result.solutionOne); + IO.println(result.solutionTwo); + } +} +``` + +## Challenge 3 + +Update your code above to also communicate if a solution is imaginary. +Use a boolean or an enum field on whatever class you wrote to hold both solutions +for this.[^other] + +[^other]: You will learn other ways later. + +## Challenge 4 + +Make the following code compile. Do so first by changing one of the fields to `static` +then by instead passing an extra argument to the `static` method. + +```java,no_run +class Keychain { + final String[] keys; + + Keychain(String[] keys) { + this.keys = keys; + } + + boolean hasKey(String key) { + for (int i = 0; i < keys.length; i++) { + if (keys[i].equals(key)) { + return true; + } + } + return false; + } +} + + +class Main { + Keychain keychain = new Keychain(new String[] { + "house", + "car", + "shed" + }); + + static void unlock(String thing) { + if (keychain.hasKey(thing)) { + IO.println("You have unlocked my " + thing); + } + else { + IO.println("You don't have a key for my " + thing); + } + } + + void main() { + unlock("house"); + unlock("car"); + unlock("shed"); + unlock("heart"); + } +} +``` \ No newline at end of file diff --git a/src/static_methods/header.png b/src/static_methods/header.png new file mode 100644 index 00000000..8b18b032 Binary files /dev/null and b/src/static_methods/header.png differ diff --git a/src/streams.md b/src/streams.md index 80eeccb1..7c93b92e 100644 --- a/src/streams.md +++ b/src/streams.md @@ -1 +1,60 @@ # Streams + + + +Programs often have to transform all of the elements in a collection +in order to produce a new collection. + +For instance: take all the people in a `List`, remove any who are +older than 65, and extract their names into a set. + +```java +import module java.base; + +record Person(String name, int age) {} + +class Main { + void main() { + List people = List.of( + new Person("Jess", 29), + new Person("Sally", 72), + new Person("Bess", 41) + ); + + Set names = new HashSet<>(); + for (var person : people) { + if (person.age() < 65) { + names.add(person.name()); + } + } + + IO.println(names); + } +} +``` + +While such transformations can always be done in "normal code," +it can be preferable to use "streams." + +```java +import module java.base; + +record Person(String name, int age) {} + +class Main { + void main() { + List people = List.of( + new Person("Jess", 29), + new Person("Sally", 72), + new Person("Bess", 41) + ); + + Set names = people.stream() + .filter(person -> person.age() < 65) + .map(Person::name) + .collect(Collectors.toSet()); + + IO.println(names); + } +} +``` \ No newline at end of file diff --git a/src/streams/challenges.md b/src/streams/challenges.md new file mode 100644 index 00000000..a9b8bac7 --- /dev/null +++ b/src/streams/challenges.md @@ -0,0 +1,65 @@ +# Challenges + + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Translate the following code using a for-loop to code using streams. + +```java,editable +import module java.base; + +class Main { + void main() { + for (int i = 0; i < 10; i++) { + IO.println(i); + } + } +} +``` + +## Challenge 2. + +Translate the following code using a for-loop to code using streams. + +```java +import module java.base; + +class Main { + void main() { + List timestamps = List.of(1, 1756137441); + + Set instants = new HashSet<>(); + for (int timestamp : timestamps) { + instants.add(Instant.ofEpochSecond(timestamp)); + } + + IO.println(instants); + } +} +``` + +## Challenge 3. + +Read the documentation on [`Collector`](https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/util/stream/Collector.html) and [`Collectors`](https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/util/stream/Collector.html). + +Make an implementation of `Collector` that can collect elements into `MySpecialList`. + +```java +import module java.base; + +class MySpecialList extends ArrayList {} + +class Main { + // CODE HERE + + void main() { + MySpecialList l = Stream.of(1, 2, 3) + .collect(/* CODE HERE */); + } +} +``` \ No newline at end of file diff --git a/src/streams/collectors.md b/src/streams/collectors.md index 06840d46..a018d943 100644 --- a/src/streams/collectors.md +++ b/src/streams/collectors.md @@ -1 +1,80 @@ # Collectors + +The most common kind of terminal operation you will perform on a stream +is "collecting" elements into a new collection. + +For this you use the `.collect` method along with an implemention of the +`Collector` interface. + +```java +~void main() { +List roles = List.of("seer", "clown", "nightmare"); + +Function countVowels = s -> { + int vowels = 0; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') { + vowels++; + } + } + return vowels; +}; + +List vowelCounts = roles.stream() + .map(countVowels) + .collect(Collectors.toList()); + +IO.println(vowelCounts); +~} +``` + +There are implementations available as static methods on the `Collectors` class for +collecting into `List`s, `Set`s, and even `Map`s. + +Because collecting into specifically a `List` is so common, there is +also a `.toList()` method directly on `Stream` that serves as a shortcut +for `.collect(Collectors.toUnmodifiableList())`. + +```java +~void main() { +List roles = List.of("seer", "clown", "nightmare"); + +Function countVowels = s -> { + int vowels = 0; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') { + vowels++; + } + } + return vowels; +}; + +// There is also Collectors.toUnmodifiableList +List vowelCountsList = roles.stream() + .map(countVowels) + .collect(Collectors.toList()); + +IO.println(vowelCountsList); + +vowelCountsList = roles.stream() + .map(countVowels) + .toList(); +IO.println(vowelCountsList); + +// ...and Collectors.toUnmodifiableSet() +Set vowelCountsSet = roles.stream() + .map(countVowels) + .collect(Collectors.toSet()); +IO.println(vowelCountsSet); + +// ...and Collectors.toUnmodifiableMap +Map vowelCountsMap = roles.stream() + .collect(Collectors.toMap( + s -> s, + s -> countVowels.apply(s) + )); +IO.println(vowelCountsMap); +~} +``` \ No newline at end of file diff --git a/src/streams/filter.md b/src/streams/filter.md index e816ea14..abbc91bf 100644 --- a/src/streams/filter.md +++ b/src/streams/filter.md @@ -1 +1,20 @@ # filter + +With a stream of elements you can also drop the +elements of the stream as they flow by[^metaphor] with `.filter`. + +`.filter` will test each element with a predicate. If the +predicate returns `true` the element will be retained. +If it returns `false` the element will be dropped. + + +```java,no_run +var numbers = List.of("1", "2", "3"); + +Stream numberStream = numbers.stream() + .map(Integer::parseInt) // 1, 2, 3 + .filter(x -> x % 2 == 1); // 1, 3 +``` + +[^metaphor]: In the real life stream metaphor, this is akin to rocks stuck along the way and not +continuing to go with the flow of water. diff --git a/src/streams/header.png b/src/streams/header.png new file mode 100644 index 00000000..6b8cda4d Binary files /dev/null and b/src/streams/header.png differ diff --git a/src/streams/map.md b/src/streams/map.md index d4ab730f..e9b58cdd 100644 --- a/src/streams/map.md +++ b/src/streams/map.md @@ -1 +1,18 @@ # map + +Once you have a stream of elements you can transform the +elements of the stream as they flow by[^metaphor] with `.map`. + +`.map` applies a `Function` to the elements of the stream +one by one and returns you a new `Stream` containing the new elements. + +```java,no_run +var numbers = List.of("1", "2", "3"); + +Stream numberStream = numbers.stream() + .map(Integer::parseInt); // 1, 2, 3 +``` + +[^metaphor]: In the real life stream metaphor, this is akin to rocks getting polished +by sand as they flow. + diff --git a/src/streams/purpose.md b/src/streams/purpose.md new file mode 100644 index 00000000..d6aa1fe0 --- /dev/null +++ b/src/streams/purpose.md @@ -0,0 +1,43 @@ +# Purpose + +The purpose of streams is to "de-couple" +the source of data, the transformations to apply on that +data, and the final shape you want that data in. + +This serves one somewhat holistic goal and one practical one. + +The holistic goal is that you are "declaring what you want done" +as opposed to "expressing how you want something done." +This is often referred to as "declarative" vs "imperative" programming. + +Compare these two bits of code. They both add one to every number in a list. + +```java,no_run +List doubles = List.of(1.5, 2.5, 3.9); + +List newDoubles = new ArrayList<>(); +for (double d : doubles) { + newDoubles.add(d + 1); +} +``` + +```java +List doubles = List.of(1.5, 2.5, 3.9); + +List newDoubles = doubles.stream() + .map(d -> d + 1) + .toList(); +``` + +You can argue that the second code snippet more transparently "reflects the intent" +than the first one. The mechanics of looping and adding to a new list are +hidden and the only things on screen are `doubles.stream()` (using this collection as a source), +`.map(d -> d + 1)` (apply this transformation), and `.toList()` (put the results in a list.) + +This has its upsides and downsides. Part of the trouble is that there isn't a good rule of +thumb as to when you should use a regular loop or use a stream - it often comes down to +personal taste and other "soft" factors. + +The mechanical reason for streams is that, because the operations to perform are separated somewhat +from how to perform them, there are opportunities for Java to be "smart" about how it does them. +The usual example given for this is "parallel streams," which we can get into eventually. \ No newline at end of file diff --git a/src/streams/stream.md b/src/streams/stream.md new file mode 100644 index 00000000..a2cfa3d3 --- /dev/null +++ b/src/streams/stream.md @@ -0,0 +1,48 @@ +# stream + +A Java `Stream` represents a "stream[^flow]" +of elements coming from some "source." + +You can stream the elements of a `List` +or `Set` by using the `.stream()` instance method +on each. + +```java,no_run +Stream heroes = List.of( + "Deku", + "Explosive Hero: Great Explosion Murder God Dynamight", + "Froppy" +).stream(); + +Stream villains = Set.of( + "All for One", + "Muscular" +).stream(); +``` + +For arrays there is a static method on `Arrays` which +can stream their contents. You could also first convert the +array to a collection with `Arrays.asList`. + +```java,no_run +Stream heroes = Arrays.stream(new String[] { + "Lin Ling", + "Lucky Cyan" +}); + +Stream villains = Arrays.asList(new String[] { + "E-Soul" +}).stream(); +``` + +If you legitimately do not have a source collection +you can also create a stream directly with `Stream.of`. + +```java,no_run +Stream letterStream = Stream.of('a', 'b', 'c'); +``` + +[^flow]: In real life a stream flows from some source of water +to some destination. The water can carry rocks and other things +along with it. So that is where the metaphor comes from. Real life streams +carry rocks, Java streams carry chunks of data. \ No newline at end of file diff --git a/src/streams/terminal_operations.md b/src/streams/terminal_operations.md new file mode 100644 index 00000000..4e30d038 --- /dev/null +++ b/src/streams/terminal_operations.md @@ -0,0 +1,42 @@ +# Terminal Operations + +We call `.map`, `.filter`, and methods like them "intermediate operations." +This is because they run "in the middle" of the entire process. + +For consuming a stream you use "terminal operations." Terminal operations +"consume" the stream and produce some result. + +The simplest terminal operation is `.forEach`. It consumes the entire stream and does +something for each element in the flow. + +```java,no_run +~void main() { +List cities = List.of( + "St. Louis", "Dallas", "London", "Tokyo" +); + +cities.stream() + .filter(city -> !city.startsWith("S")) + .forEach(IO::println); // Dallas, London, Tokyo +~} +``` + +Once a terminal operation has been performed the stream is no longer +usable. + +```java +~void main() { +List cities = List.of( + "St. Louis", "Dallas", "London", "Tokyo" +); + +Stream citiesStream = cities.stream() + .filter(city -> !city.startsWith("S")); + +// Dallas, London, Tokyo +citiesStream.forEach(IO::println); + +// java.lang.IllegalStateException: stream has already been operated upon or closed +citiesStream.forEach(IO::println); +~} +``` \ No newline at end of file diff --git a/src/streams/toList.md b/src/streams/toList.md deleted file mode 100644 index fe773bbf..00000000 --- a/src/streams/toList.md +++ /dev/null @@ -1 +0,0 @@ -# toList diff --git a/src/strings.md b/src/strings.md index 0a64c462..83e3b730 100644 --- a/src/strings.md +++ b/src/strings.md @@ -1,5 +1,8 @@ # Strings + + + The `String` data type is used to represent text. ```java,no_run diff --git a/src/strings/equality.md b/src/strings/equality.md index 6e5c5b97..034188ec 100644 --- a/src/strings/equality.md +++ b/src/strings/equality.md @@ -31,3 +31,7 @@ boolean areNotSame = !bow.equals(wow); IO.println(areNotSame); ~} ``` + +Note that you should **not** use `==`. Java will let you do it but you won't get what you expect.[^inaway] + +[^inaway]: It is confusing in a way that we aren't ready to explain yet. Just remember for `int`, `double`, `char`, etc. you can use `==`. For `String` use `.equals`. diff --git a/src/strings/header.png b/src/strings/header.png new file mode 100644 index 00000000..988d4136 Binary files /dev/null and b/src/strings/header.png differ diff --git a/src/strings_ii.md b/src/strings_ii.md index 2fb9ac7f..bc002fab 100644 --- a/src/strings_ii.md +++ b/src/strings_ii.md @@ -1,5 +1,8 @@ # Strings II + + + If you haven't already, you will eventually realize that `String`s are one of the most common data types you will use. diff --git a/src/strings_ii/header.png b/src/strings_ii/header.png new file mode 100644 index 00000000..1f22864b Binary files /dev/null and b/src/strings_ii/header.png differ diff --git a/src/switch.md b/src/switch.md index 2a413762..a1827681 100644 --- a/src/switch.md +++ b/src/switch.md @@ -1,5 +1,8 @@ # Switch + + + `if` and `else` let you branch logic based on whether any arbitrary expression that evaluates to a boolean. diff --git a/src/switch/header.png b/src/switch/header.png new file mode 100644 index 00000000..93a39b67 Binary files /dev/null and b/src/switch/header.png differ diff --git a/src/switch_ii.md b/src/switch_ii.md index 78acc887..d96264e9 100644 --- a/src/switch_ii.md +++ b/src/switch_ii.md @@ -1,5 +1,7 @@ # Switch II + + A common thing to do is have a `switch` statement which assigns a value to a variable in each branch. diff --git a/src/switch_ii/challenges.md b/src/switch_ii/challenges.md new file mode 100644 index 00000000..79e3f4f5 --- /dev/null +++ b/src/switch_ii/challenges.md @@ -0,0 +1,52 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1 + +Complete the switch expression such that it yields +the appropriate value for each case. + +In the surfer case, make sure to print out "Radical!" before yielding +the value. + +```java,editable +enum Profession { + FIREFIGHTER, + PLUMBER, + SURFER +} + +enum NaturalEnemy { + FIRE, + LEAKY_PIPES, + BODACIOUS_WAVES +} + +NaturalEnemy enemy(Profession p) { + switch (p) { + case FIREFIGHTER -> { + // CODE HERE + }; + case PLUMBER -> { + // CODE HERE + } + case SURFER -> { + // CODE HERE + } + } +} + +void main() { + IO.println(enemy(Profession.FIREFIGHTER)); + IO.println(enemy(Profession.PLUMBER)); + IO.println(enemy(Profession.SURFER)); +} +``` + +## Challenge 2 + +Update your program above to omit `yield` in the two cases where it is not needed. \ No newline at end of file diff --git a/src/switch_ii/exhaustive_switches_with_enums.md b/src/switch_ii/exhaustive_switches_with_enums.md deleted file mode 100644 index 47969766..00000000 --- a/src/switch_ii/exhaustive_switches_with_enums.md +++ /dev/null @@ -1 +0,0 @@ -# Exhaustive Switches with Enums diff --git a/src/switch_ii/exhaustive_switches_with_strings.md b/src/switch_ii/exhaustive_switches_with_strings.md deleted file mode 100644 index 87fa738f..00000000 --- a/src/switch_ii/exhaustive_switches_with_strings.md +++ /dev/null @@ -1 +0,0 @@ -# Exhaustive Switches with Strings diff --git a/src/switch_ii/exhaustiveness.md b/src/switch_ii/exhaustiveness.md index cef3c887..f78fae27 100644 --- a/src/switch_ii/exhaustiveness.md +++ b/src/switch_ii/exhaustiveness.md @@ -28,3 +28,34 @@ boolean cool = switch (name) { IO.println(cool); ~} ``` + +Unlike in a switch statement, covering all the enum values in a switch expression +is enough for that switch to be considered exhaustive. You do not need an +extra `default` or `case null`[^reason]. + +```java,no_run +enum Species { + ALIEN, + PREDATOR, + HUMAN +} + +void main() { + Species species = Species.valueOf( + IO.readln("What do you see? ").toUpperCase() + ); + String message = switch (species) { + case ALIEN -> "Run"; + case PREDATOR -> "Fight Back"; + case HUMAN -> "RUN" + }; + + IO.println(message); +} +``` + +[^reason]: The nitty gritty of why this is comes down to what Java will do if the code is +run with an unexpected enum variant. With a switch statement the code will just move on and +run none of the switch cases. With a switch expression Java will crash on an unexpected value. +This difference is partially due to the fact that switch statements came first in the language +and switch expressions came later. What a world, huh? \ No newline at end of file diff --git a/src/switch_ii/header.png b/src/switch_ii/header.png new file mode 100644 index 00000000..9e943138 Binary files /dev/null and b/src/switch_ii/header.png differ diff --git a/src/switch_iii.md b/src/switch_iii.md index bf2ce228..627911ef 100644 --- a/src/switch_iii.md +++ b/src/switch_iii.md @@ -1,5 +1,8 @@ # Switch III + + + The switch statement in Java originally came from a little language you might know about called `C`. diff --git a/src/switch_iii/challenges.md b/src/switch_iii/challenges.md new file mode 100644 index 00000000..5b72e126 --- /dev/null +++ b/src/switch_iii/challenges.md @@ -0,0 +1,95 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Convert this program which uses a switch without fallthrough +to instead use fallthrough for all the common cases. + +```java,editable +enum Character { + GOKU, + PICCOLO, + FRIEZA, + VEGETA, + PILAF, + GOHAN, + BEERUS +} + +class Main { + String isEvil(Character c) { + switch (c) { + case GOKU -> { + // Man puts the multiverse in danger + // on account of wanting to fight! + return "debatable"; + } + case PICCOLO -> { + return "not_anymore"; + } + case FRIEZA -> { + return "yes"; + } + case VEGETA -> { + return "not_anymore"; + } + case PILAF -> { + return "not_anymore"; + } + case GOHAN -> { + return "no"; + } + case BEERUS -> { + // He's more a force of nature, + // but its relative. + return "debatable"; + } + default -> { + return "unknown"; + } + } + } + + void main() { + IO.println("Goku: " + isEvil(Character.GOKU)); + IO.println("Piccolo: " + isEvil(Character.PICCOLO)); + IO.println("Frieza: " + isEvil(Character.FRIEZA)); + IO.println("Vegeta: " + isEvil(Character.VEGETA)); + IO.println("Pilaf: " + isEvil(Character.PILAF)); + IO.println("Gohan: " + isEvil(Character.GOHAN)); + IO.println("Beerus: " + isEvil(Character.BEERUS)); + } +} +``` + +## Challenge 2. + +Convert the previous program using fallthrough to also +be directly returned as a switch expression using `yield`. + +## Challenge 3. + +Write a method named `aToZ` which, given a character +between `a` and `z`, will print all the letters +from that letter on until `z`. + +Make use of a switch with fallthrough instead of loops or any other mechanism. + +```java,editable +class Main { + void aToZ(char c) { + // CODE HERE + } + + void main() { + aToZ('z'); // z + aToZ('x'); // x y z + aToZ('a'); // a b c d ... x y z + } +} +``` diff --git a/src/switch_iii/header.png b/src/switch_iii/header.png new file mode 100644 index 00000000..c853b5f2 Binary files /dev/null and b/src/switch_iii/header.png differ diff --git a/src/the_terminal.md b/src/the_terminal.md index 89321f3e..cb86296b 100644 --- a/src/the_terminal.md +++ b/src/the_terminal.md @@ -1,4 +1,5 @@ # The Terminal + Early computers didn't have graphical interfaces with windows or buttons you could "click." Instead, they offered a text based interface. diff --git a/src/the_terminal/chromebooks_and_school_computers.md b/src/the_terminal/chromebooks_and_school_computers.md index 7a20a515..7015579f 100644 --- a/src/the_terminal/chromebooks_and_school_computers.md +++ b/src/the_terminal/chromebooks_and_school_computers.md @@ -7,6 +7,6 @@ This is a good thing if your goal is to prevent high schoolers from committing s There are also computer types, like chromebooks, which aren't really built with programming in mind. While you could access a terminal, the sorts of programs you would generally install to work with Java will be out of reach. It's a similar case for things like iPads. -If you are in one of these situations I would reccomend getting a "normal" computer if at all possible. You can make do for a time, but eventually it will be required to continue in this field.[^thems] +If you are in one of these situations I would recommend getting a "normal" computer if at all possible. You can make do for a time, but eventually it will be required to continue in this field.[^thems] [^thems]: Thems the ropes, kid. 🤷 \ No newline at end of file diff --git a/src/the_terminal/header.png b/src/the_terminal/header.png new file mode 100644 index 00000000..e680f42e Binary files /dev/null and b/src/the_terminal/header.png differ diff --git a/src/time.md b/src/time.md index 814b6cc6..759a310b 100644 --- a/src/time.md +++ b/src/time.md @@ -1,5 +1,7 @@ # Time + + Wikipedia defines "time" as "The continued sequence of existence and events that occurs in an apparently irreversible succession from the past, through the present, and into the future."[^source] Most everything that interacts with the real world needs to understand information about time. diff --git a/src/time/challenges.md b/src/time/challenges.md index 9358534c..89c5b840 100644 --- a/src/time/challenges.md +++ b/src/time/challenges.md @@ -1 +1,83 @@ # Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1. + +Print out every day from January 1st to December 31st. When the program reaches your birthday +make it print out "Happy Birthday \" instead of the date. + +```java,editable +class Main { + void main() { + // CODE HERE + } +} +``` + +## Challenge 2. + +Make a `Poison` class which has a `Duration` field which stores how long +the poison will be potent for as well as an `Instant` at which the +poison was brewed. + +Implement a method that takes an `Instant` and returns if the `Poison` +will be expired by that point. + +```java,editable +import java.time.Duration; +import java.time.Instant; + +class Poison { + // CODE HERE +} + +class Main { + void main() { + var hemlock = new Poison(Instant.now(), Duration.ofDays(365 * 3)); + + IO.println(hemlock.isPotentAt(Instant.now())); // true + IO.println(hemlock.isPotentAt(Instant.now().plus(Duration.ofDays(5))); // true + IO.println(hemlock.isPotentAt(Instant.now().plus(Duration.ofDays(365 * 10)))); // false + } +} +``` + +## Challenge 3. + +Get as input using `IO.readln` a `day`, `month`, `year`, and UTC offset. + +Interpret that input as an `OffsetDateTime` then print how many seconds will have +passed between that offset date time and midnight of January 1st 1983 GMT. + +```java,editable +class Main { + void main() { + // CODE HERE + } +} +``` + +## Challenge 4. + +A train leaves Boston at 12:50pm EDT on August 23rd 2025 and arrives in Chicago +at 10:12am CDT August 24th 2025. + +How many minutes long was that train ride? Use the Java's time classes to figure out the answer. + +```java,editable +class Main { + void main() { + // CODE HERE + } +} +``` + +As a small hint, you will first want to represent those events as `ZonedDateTime`s, convert the `ZonedDateTime`s to `Instant`s, and then get the `Duration` between those `Instant`s. Then get the number +of minutes in that `Duration`. + + + diff --git a/src/time/header.png b/src/time/header.png new file mode 100644 index 00000000..78904413 Binary files /dev/null and b/src/time/header.png differ diff --git a/src/variables.md b/src/variables.md index 5a0a1486..672544e0 100644 --- a/src/variables.md +++ b/src/variables.md @@ -1,5 +1,7 @@ # Local Variables + + Mechanically, the next thing to cover is "variables". diff --git a/src/variables/header.png b/src/variables/header.png new file mode 100644 index 00000000..b7038a83 Binary files /dev/null and b/src/variables/header.png differ diff --git a/src/visibility.md b/src/visibility.md index 91a4f340..e299a597 100644 --- a/src/visibility.md +++ b/src/visibility.md @@ -1,5 +1,7 @@ # Visibility + + When code is all in one file, everything is "visible." This means that if there is a method you are always allowed to call it. diff --git a/src/visibility/challenges.md b/src/visibility/challenges.md new file mode 100644 index 00000000..ec4a65b9 --- /dev/null +++ b/src/visibility/challenges.md @@ -0,0 +1,92 @@ +# Challenges + +Remember the rules for this are + +- Try to use only the information given up to this point in this book. +- Try not to give up until you've given it a solid attempt + +## Challenge 1 + +The following `Ratio` class should have an invariant where `denominator` +cannot be `0`. + +```java,no_run +class Ratio { + int numerator; + int denominator; + + Ratio(int numerator, int denominator) { + if (denominator == 0) { + throw new RuntimeException("Denominator cannot be zero"); + } + } + + double value() { + return ((double) numerator) / denominator; + } +} +``` + +Unfortunately other classes can directly access the `denominator` field. + +Make the denominator field private and add an accessor method for other classes to use. +You can name this accessor `.denominator()` or `.getDenominator()`. + +## Challenge 2 + +Alter the code from the last challenge such that numerator is also private and only +usable via an accessor method. + +## Challenge 3 + +Alter the code from the last challenge such that you can not only access but also +mutate the `numerator` and `denominator` fields via exposed methods. +The method for mutating the `denominator` should throw an exception +if someone tries to set it to zero. + +## Challenge 4 + +Given this `Pineapple` class make every method except `digest` private. + +```java,no_run +class Pineapple { + void digestProtein(String name) { + IO.println("Pineapple juice is digesting the proteins in " + name); + } + + void eatPork() { + digestProtein("pork"); + } + + void eatChicken() { + digestProtein("chicken"); + } + + void eatBeef() { + digestProtein("beef"); + } + + void eatYou() { + IO.println("Pineapple juice is eating you from the inside."); + } + + void digest(String thing) { + switch (thing) { + case "pork" -> eatPork(); + case "chicken" -> eatChicken(); + case "beef" -> eatBeef(); + default -> eatYou(); + } + } +} +``` + +## Challenge 5 + +Rewrite the code from the previous challenge such that the `digest` +method on `Pineapple` does the same thing but no other private methods +exist. + +Note when doing this that you wouldn't need to consider other code. +The behavior of the only exposed method does not change so those private +methods would only be "implementation details." diff --git a/src/visibility/getter_and_setters.md b/src/visibility/getter_and_setters.md index c25639bf..f825d5eb 100644 --- a/src/visibility/getter_and_setters.md +++ b/src/visibility/getter_and_setters.md @@ -38,6 +38,6 @@ class Person { } ``` -The answer to that is...annoying. We'll get to it, but the short story is that its a bit of a holdover from a very weird period in the 1990s. +The answer to that is interesting. We'll get to it, but the short story is that its a bit of a holdover from a very weird period in the early 2000s. I mention it specifically so that you know that there isn't any important information you are missing and you are not crazy. \ No newline at end of file diff --git a/src/visibility/header.png b/src/visibility/header.png new file mode 100644 index 00000000..4ae46581 Binary files /dev/null and b/src/visibility/header.png differ