diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 0371702..d86c825 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -38,9 +38,9 @@ # Data Types I - [Booleans](./boolean.md) + - [Not](./boolean/not.md) - [And](./boolean/and.md) - [Or](./boolean/or.md) - - [Not](./boolean/not.md) - [Operator Precedence](./boolean/operator_precedence.md) - [Challenges](./boolean/challenges.md) - [Integers](./integers.md) @@ -95,7 +95,6 @@ # Control Flow I - [Branching Paths](./branching_paths.md) - - [If](./branching_logic/if.md) - [Nested Ifs](./branching_logic/nested_ifs.md) - [Else](./branching_logic/else.md) @@ -136,6 +135,7 @@ - [Printing the Contents of an Array](./arrays/printing_the_contents_of_an_array.md) - [Empty Array](./arrays/empty_array.md) - [Difference between Initializer and Literal](./arrays/difference_between_initializer_and_literal.md) + - [Initialization with new](./arrays/initialization_with_new.md) - [Challenges](./arrays/challenges.md) # Control Flow II @@ -190,6 +190,7 @@ - [Return in void methods](./return_values/return_in_void_methods.md) - [Conversion](./return_values/conversion.md) - [Unreachable Statements](./return_values/unreachable_statements.md) + - [Challenges](./return_values/challenges.md) # Data Types III @@ -198,6 +199,7 @@ - [Null as Unknown](./null/null_as_unknown.md) - [Checking for null](./null/checking_for_null.md) - [NullPointerException](./null/null_pointer_exception.md) + - [Challenges](./null/challenges.md) - [Boxed Primitives](./boxed_primitives.md) - [Integer](./boxed_primitives/integer.md) - [Double](./boxed_primitives/double.md) @@ -211,6 +213,7 @@ - [Initializion with Size](./arrays_ii/initialization_with_size.md) - [Default Values](./arrays_ii/default_values.md) - [Populate Arrays](./arrays_ii/populate_arrays.md) + - [Challenges](./arrays_ii/challenges.md) # Code Structure II @@ -225,6 +228,7 @@ - [Field Default Values](./classes/field_default_values.md) - [Aliasing](./classes/aliasing.md) - [Return Multiple Values](./classes/return_multiple_values.md) + - [Challenges](./classes/challenges.md) - [Instance Methods](./instance_methods.md) - [Invocation](./instance_methods/invocation.md) @@ -236,6 +240,7 @@ - [this](./instance_methods/this.md) - [Disambiguation](./instance_methods/disambiguation.md) - [Clarity](./instance_methods/clarity.md) + - [Challenges](./instance_methods/challenges.md) # Data Types IV @@ -246,6 +251,7 @@ - [Usage](./enums/usage.md) - [Equality](./enums/equality.md) - [Comparison to boolean](./enums/comparison_to_boolean.md) + - [Challenges](./enums/challenges.md) - [Strings II](./strings_ii.md) - [lowercase](./strings_ii/lowercase.md) - [UPPERCASE](./strings_ii/UPPERCASE.md) @@ -253,6 +259,7 @@ - [Check if empty](./strings_ii/check_if_empty.md) - [Check if blank](./strings_ii/check_if_blank.md) - [Strip extra whitespace](./strings_ii/strip_extra_whitespace.md) + - [Challenges](./strings_ii/challenges.md) # Control Flow III @@ -261,6 +268,7 @@ - [Messages](./exceptions/messages.md) - [Stack Traces](./exceptions/stack_traces.md) - [try/catch](./exceptions/try_catch.md) + - [Challenges](./exceptions/challenges.md) - [Switch](./switch.md) - [Case and Default](./switch/case_and_default.md) - [Strings](./switch/strings.md) @@ -270,6 +278,7 @@ - [Exhaustiveness](./switch/exhaustiveness.md) - [Combining Cases](./switch/combining_cases.md) - [null](./switch/null.md) + - [Challenges](./switch/challenges.md) # Code Structure III @@ -281,11 +290,13 @@ - [Invariants](./constructors/invariants.md) - [Overloads](./constructors/overloads.md) - [Delegation](./constructors/delegation.md) + - [Challenges](./constructors/challenges.md) - [Global Fields](./global_fields.md) - [Default Values](./global_fields/default_values.md) - [Final Fields](./global_fields/final_fields.md) - [Field Access](./global_fields/field_access.md) - [Inferred Types](./global_fields/inferred_types.md) + - [Challenges](./global_fields/challenges.md) # Concepts @@ -334,7 +345,7 @@ - [Getting Used to it](./the_terminal/getting_used_to_it.md) -# Control Flow III +# Control Flow IV - [Exceptions II](./exceptions_ii.md) - [Checked Exceptions](./exceptions_ii/checked_exceptions.md) @@ -447,7 +458,6 @@ - [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) -- [🚧 Construction Zone 🚧]() # Interactive Programs III @@ -458,9 +468,11 @@ - [Write to a File](./files/write_to_a_file.md) - [Read from a File](./files/read_from_a_file.md) - [Create a Folder](./files/creating_a_folder.md) - - [Delete a Folder](./files/deleting_a_folder.md) - - [Delete a File](./files/deleting_a_file.md) + - [Challenges](./files/challenges.md) + + + # Code Structure IV @@ -486,10 +498,23 @@ - [@Override](./objects/override.md) - [equals and hashCode](./objects/equals_and_hashCode.md) - [Override equals and hashCode](./objects/override_equals_and_hashCode.md) - - [Variance and Casting](./objects/variance_and_casting.md) - - [Subclasses](./objects/subclasses.md) + + - [Generics](./generics.md) + - [Type Variables](./generics/type_variables.md) + - [Naming](./generics/naming.md) + - [Instantiation](./generics/instantiation.md) + - [Inference](./generics/inference.md) + - [Soundness](./generics/soundness.md) + - [Raw Types](./generics/raw_types.md) - [Interfaces](./interfaces.md) + - [Interface Declaration](./interfaces/interface_declaration.md) + - [Implementation](./interfaces/implementation.md) + - [@Override](./interfaces/override.md) + - [Naming](./interfaces/naming.md) + - [Subtypes](./interfaces/subtypes.md) + - [Multiple Implementations](./interfaces/multiple_implementations.md) # Data Types V @@ -501,6 +526,7 @@ Make them do one. --> - [LocalDate](./time/local_date.md) - [LocalTime](./time/local_time.md) - [LocalDateTime](./time/local_date_time.md) + - [Time Zones](./time/time_zones.md) - [ZonedDateTime](./time/zoned_date_time.md) - [OffsetDateTime](./time/offset_date_time.md) - [ArrayList](./array_list.md) @@ -511,16 +537,32 @@ 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) -- [HashMap](./hash_map.md) + + - [Concurrent Modifications](./loops_iii/concurrent_modifications.md) + - [Reasons to go back]() - [Base Case](./recursion/base_case.md) - [Comparison to Delegation](./recursion/comparison_to_delegation.md) - [Comparison to Loops](./recursion/comparison_to_loops.md) + - [Counting Down](./recursion/counting_down.md) + - [Accumulators](./recursion/accumulators.md) + - [Recurse Over a String](./recursion/recursing_over_strings.md) + - [Recurse Over an Array](./recursion/recursing_over_arrays.md) # Code Structure V +- [Interface Extension](./interface_extension.md) - [Class Extension](./class_extension.md) - [Abstract Classes](./abstract_classes.md) -# Concepts II -- [Encapsulation](./encapsulation.md) - - [Methods]() - - [Classes]() - - [Information Hiding]() # Building @@ -551,18 +593,70 @@ Make them do one. --> - [Compile a List of Files]() - [Specify Where to Find Sources]() - [Enable Debug Info]() +- [Package Java Code]() +- [Consume External Libraries]() # Data Types VI + - [StringBuilder]() -- [Pattern]() + - [Why]() +- [Regular Expressions]() + - [Pattern]() + +# Communication + - [Paths]() - [Iterator and Iterable]() - [Exceptions II]() -- [Lists]() - - [ArrayList]() - - [Add an item]() - - [Get an item]() - - [Set an item]() # Control Flow IV @@ -610,12 +699,6 @@ Make them do one. --> - [Sets]() - [HashSet]() -# Metaprogramming - -- [Reflection]() -- [Annotations]() --> - - ## Data Types IV - -- [Documentation]() - - [Documentation Comments]() - [Testing]() @@ -794,3 +874,7 @@ multi dimensional arrays System.console() over Scanner? --> + + + +- [🚧 (More Chapters Planned) 🚧]() \ No newline at end of file diff --git a/src/acronyms.md b/src/acronyms.md new file mode 100644 index 0000000..802eb0a --- /dev/null +++ b/src/acronyms.md @@ -0,0 +1,3 @@ +# Acronyms + +In the 1940s, Franklin Delano Rosevelt \ No newline at end of file diff --git a/src/acronyms/ambiguity.md b/src/acronyms/ambiguity.md new file mode 100644 index 0000000..b1116ba --- /dev/null +++ b/src/acronyms/ambiguity.md @@ -0,0 +1 @@ +# Ambiguity diff --git a/src/acronyms/elaboration.md b/src/acronyms/elaboration.md new file mode 100644 index 0000000..84f4a9d --- /dev/null +++ b/src/acronyms/elaboration.md @@ -0,0 +1 @@ +# Elaboration diff --git a/src/acronyms/familiarity.md b/src/acronyms/familiarity.md new file mode 100644 index 0000000..c59e312 --- /dev/null +++ b/src/acronyms/familiarity.md @@ -0,0 +1 @@ +# Familiarity diff --git a/src/acronyms/niches.md b/src/acronyms/niches.md new file mode 100644 index 0000000..60b4487 --- /dev/null +++ b/src/acronyms/niches.md @@ -0,0 +1 @@ +# Niches diff --git a/src/acronyms/usage_contexts.md b/src/acronyms/usage_contexts.md new file mode 100644 index 0000000..3e334e9 --- /dev/null +++ b/src/acronyms/usage_contexts.md @@ -0,0 +1 @@ +# Usage Contexts diff --git a/src/arguments/challenges.md b/src/arguments/challenges.md index cc83488..e7c1229 100644 --- a/src/arguments/challenges.md +++ b/src/arguments/challenges.md @@ -53,3 +53,39 @@ void main() { ``` ## Challenge 3. + +Write a method with four overloads such that +the code in `main` can run unchanged. + +```java,editable +// CODE HERE + +void main() { + f(2); + f("b"); + f('9'); + f(new String[] { "s" }); +} +``` + +## Challenge 4. + +Call the defined methods in a way that outputs "I did it!" + +```java,editable +void i() { + System.out.print("I"); +} + +void did(String what) { + System.out.println("did " + what); +} + +void space() { + System.out.print(" "); +} + +void main() { + // Code here +} +``` \ No newline at end of file diff --git a/src/arguments/reassignment.md b/src/arguments/reassignment.md index df04c0d..7d71fd7 100644 --- a/src/arguments/reassignment.md +++ b/src/arguments/reassignment.md @@ -23,7 +23,7 @@ any variables passed to the method by the caller. void eat(String food) { System.out.println("I ate " + food); food = "nothing"; - System.out.println("Now I have " + food) + System.out.println("Now I have " + food); } void main() { diff --git a/src/arrays/difference_between_initializer_and_literal.md b/src/arrays/difference_between_initializer_and_literal.md index b3c71b0..9e01433 100644 --- a/src/arrays/difference_between_initializer_and_literal.md +++ b/src/arrays/difference_between_initializer_and_literal.md @@ -7,30 +7,38 @@ When you have a literal, like a `String` literal, you can assign that to a varia then use that `String` afterwards. ```java +~void main() { String name = "Alana"; // l System.out.println(name.charAt(1)); +~} ``` But you can also perform those operations using the literal itself, without an intermediate variable. ```java +~void main() { // l System.out.println("Alana".charAt(1)); +~} ``` Array initializers work in the case where you first assign them to a variable before using the array. ```java +~void main() { char[] name = { 'A', 'm', 'a', 'n', 'd', 'a' }; // m System.out.println(name[1]); +~} ``` But they do not work to perform operations on directly. ```java +~void main() { // Will not run System.out.println({ 'A', 'm', 'a', 'n', 'd', 'a' }[1]); +~} ``` diff --git a/src/arrays/empty_array.md b/src/arrays/empty_array.md index 12f8da9..6f3465f 100644 --- a/src/arrays/empty_array.md +++ b/src/arrays/empty_array.md @@ -3,7 +3,7 @@ If you use an array initializer that has no elements between the `{` and `}` you can create an empty array. -```java +```java,no_run char[] emptyCharArray = {}; ``` @@ -11,7 +11,8 @@ An empty array is very similar to an empty `String`. It has a length of 0, it ha and it is generally useful only as a placeholder value for when you have no data yet but will be able to reassign the variable holding it when you get some. -```java +```java,panics +~void main() { char[] emptyCharArray = {}; // 0 @@ -19,4 +20,5 @@ System.out.println(emptyCharArray.length); // Crash System.out.println(emptyCharArray[0]); +~} ``` \ No newline at end of file diff --git a/src/arrays/initialization_with_new.md b/src/arrays/initialization_with_new.md new file mode 100644 index 0000000..b916804 --- /dev/null +++ b/src/arrays/initialization_with_new.md @@ -0,0 +1,39 @@ +# Initialization with new + +Before the initializer for an array, you are allowed to write +`new` followed by a space, the type of thing in the array, +and an empty `[]`. + +```java +~void main() { +char[] mainCharacter = { 'A', 'a', 'n', 'g' }; +System.out.println(mainCharacter); + +char[] sideCharacter = new char[] { 'A', 'a', 'n', 'g' }; +System.out.println(sideCharacter); +~} +``` + +This is required for performing delayed initialization of a variable +holding an array. + +```java +~void main() { +char[] element; + +element = new char[] { 'f', 'i', 'r', 'e' }; +System.out.println(element); + +// This would not work +// element = { 'f', 'i', 'r', 'e' }; +~} +``` + +One ability this gives you is to use an array in an expression. I.E. +the initializer coupled with the `new char[]` is akin to an "array expression." + +```java +~void main() { +System.out.println(new char[]{ 'K', 'a', 't', 'a', 'r', 'a' }[1]); +~} +``` \ No newline at end of file diff --git a/src/arrays/initialization_without_initializer b/src/arrays/initialization_without_initializer deleted file mode 100644 index 90f0f13..0000000 --- a/src/arrays/initialization_without_initializer +++ /dev/null @@ -1 +0,0 @@ -# Initializion without Initializer diff --git a/src/arrays/initialization_without_initializer.md b/src/arrays/initialization_without_initializer.md deleted file mode 100644 index e69de29..0000000 diff --git a/src/arrays/printing_the_contents_of_an_array.md b/src/arrays/printing_the_contents_of_an_array.md index 24e5112..cae4796 100644 --- a/src/arrays/printing_the_contents_of_an_array.md +++ b/src/arrays/printing_the_contents_of_an_array.md @@ -45,6 +45,7 @@ If you want to actually see the contents of an array, you should use a loop.[^future] ```java +~void main() { String[] factions = { "empire", "stormcloaks", "dragons" }; int index = 0; @@ -52,7 +53,9 @@ while (index < factions.length) { System.out.println(factions[index]); index++; } +~} ``` [^gibberish]: What `[I@5a07e868` and co. mean isn't really important. Try not to get too distracted by it. + [^future]: Later on, there will be easier ways to do this sort of inspection. This is just the one I can demonstrate now. diff --git a/src/arrays/set_individual_elements.md b/src/arrays/set_individual_elements.md index 3b5661a..ecd9243 100644 --- a/src/arrays/set_individual_elements.md +++ b/src/arrays/set_individual_elements.md @@ -9,10 +9,26 @@ the new value.[^strings] ```java ~void main() { String[] sentence = { "you", "are", "found", "guilty" }; -System.out.println(sentence); +System.out.println( + sentence[0] + + " " + + sentence[1] + + " " + + sentence[2] + + " " + + sentence[3] +); sentence[1] = "aren't"; -System.out.println(sentence); +System.out.println( + sentence[0] + + " " + + sentence[1] + + " " + + sentence[2] + + " " + + sentence[3] +); ~} ``` @@ -22,16 +38,36 @@ The index of the element to set can also come from a variable. ~void main() { int index = 2; String[] response = { "and", "it", "isn't", "opposite", "day" }; -System.out.println(response); +System.out.println( + response[0] + + " " + + response[1] + + " " + + response[2] + + " " + + response[3] + + " " + + response[4] +); response[2] = "is"; -System.out.println(response); +System.out.println( + response[0] + + " " + + response[1] + + " " + + response[2] + + " " + + response[3] + + " " + + response[4] +); ~} ``` If you give a number equal to or greater than the length of the array or a number less than zero, you will get an error. -```java +```java,panics ~void main() { String[] response = { "objection" }; // Crash diff --git a/src/arrays_ii/challenges.md b/src/arrays_ii/challenges.md new file mode 100644 index 0000000..fe24ea1 --- /dev/null +++ b/src/arrays_ii/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. + +Make an empty `String` array without using an empty initializer. + +This means you cannot write `String[] empty = {}` or `String[] empty = new String[] {}`. + +```java,editable +void main() { + String[] empty = ???; + + // Should be 0 + System.out.println(empty.length); +} +``` + +## Challenge 2. + +What will the following code output? Change the code +between the lines so it instead outputs the following. + +``` +1.0 +2.0 +3.0 +4.0 +5.0 +``` + + +```java,editable +void main() { + Double[] prices = new Double[5]; + + // ---------- + // CODE HERE + // ---------- + + for (int i = 0; i < prices.length; i++) { + double price = prices[i]; + System.out.println(price); + } +} +``` + +## Challenge 3. + +Only writing code between the lines and without reassigning the `sandwich` variable, +make the following code output `egg and cheese`. + +```java,editable +void main() { + char[] sandwich = new char[14]; + + // ---------- + // CODE HERE + // ---------- + + System.out.println(sandwich); +} +``` + +## Challenge 4. + +Populate the `triangle` array such that the code +prints a right triangle that looks like the following. + +``` +* +** +*** +``` + +```java,editable +void main() { + char[] triangle = new char[8]; + + // ---------- + // CODE HERE + // ---------- + + System.out.println(triangle); +} +``` + +## Challenge 5. + +Make a method named `buildTriangle` which returns a `char[]` +that can be printed out to display a right triangle of any height. + +You can ignore the possibility that a negative or zero height is given. + +```java,editable +char[] buildTriangle(int height) { + // CODE HERE +} + +void main() { + System.out.println(buildTriangle(3)); + System.out.println("--------------"); + System.out.println(buildTriangle(5)); + System.out.println("--------------"); + System.out.println(buildTriangle(2)); +} +``` \ No newline at end of file diff --git a/src/arrays_ii/initialization_with_size.md b/src/arrays_ii/initialization_with_size.md index 5535226..e076ff4 100644 --- a/src/arrays_ii/initialization_with_size.md +++ b/src/arrays_ii/initialization_with_size.md @@ -13,4 +13,5 @@ boolean[] pixels = new boolean[23040]; So you have to say `new` followed by the type of element in the array, `[`, the size of the array and `]`. -[^bw]: The original GameBoy was technically capable of seven shades of gray per pixel, so in this example `boolean` implies a black and white image. + +[^bw]: The original GameBoy wasn't actually just black and white. It supported 7 shades of gray, so a `boolean` wouldn't technically to be enough to represent a pixel's state. You'd have to use something with at least 8 states, not just 2. diff --git a/src/arrays_ii/populate_arrays.md b/src/arrays_ii/populate_arrays.md index c494e07..393056b 100644 --- a/src/arrays_ii/populate_arrays.md +++ b/src/arrays_ii/populate_arrays.md @@ -14,20 +14,3 @@ for (int i = 0; i < letters.length; i++) { System.out.println(letters); ~} ``` - -But if you are just writing some value to every index -without any other interesting logic, you can use `Arrays.fill`. - -```java -~void main() { -int[] allNines = new char[123]; -Arrays.fill(allNines, 9); - -for (int i = 0; i < allNines.length; i++) { - System.out.println(allNines[i]); -} -~} -``` - -You give that the array and the value to fill it with. In the example above, the array starts -out with everything defaulted to `0` and is then filled with `9`s. diff --git a/src/boxed_primitives/arrays_of_boxed_primitives.md b/src/boxed_primitives/arrays_of_boxed_primitives.md index 5cece93..6e3509e 100644 --- a/src/boxed_primitives/arrays_of_boxed_primitives.md +++ b/src/boxed_primitives/arrays_of_boxed_primitives.md @@ -13,5 +13,31 @@ numbersOne = numbersTwo; numbersTwo = numbersOne; ``` +This means that to turn something like a `boolean[]` into a `Boolean[]` or vice-versa, +you must manually make a new array and copy over elements. Doing this in either +direction will work because boxing and unboxing conversions exist between the primitives +and their boxed variants. + +```java +~void main() { +boolean[] yesAndNo = new boolean[] { true, false }; + +Boolean[] yesAndNoCopy = new Boolean[] { false, false }; +for (int i = 0; i < yesAndNo.length; i++) { + // Here a boxing conversion takes place + yesAndNoCopy[i] = yesAndNo[i]; +} + +boolean[] yesAndNoCopyCopy = new boolean[] { false, false }; +for (int i = 0; i < yesAndNoCopy.length; i++) { + // And here an unboxing conversion + yesAndNoCopyCopy[i] = yesAndNoCopy[i]; +} +~} +``` + + + + [^interesting]: The reasons for this are deeply interesting and have to do with the nitty gritty of how Java is actually implemented. It might also change in the future. \ No newline at end of file diff --git a/src/boxed_primitives/challenges.md b/src/boxed_primitives/challenges.md index ff0b882..e44312c 100644 --- a/src/boxed_primitives/challenges.md +++ b/src/boxed_primitives/challenges.md @@ -28,3 +28,110 @@ void main() { ## Challenge 2. +Write a method which takes in a `Integer[]` representing +a series of distances and prints out every distance +followed by ` kilometers`. + +So if the array has `1`, `2`, and `3` you should output + +``` +1 kilometers +2 kilometers +3 kilometers +``` + +If this method is given `null`, it should act as if it +was given an empty array. + + +```java,editable +void printDistances(Integer[] distances) { + +} + +void main() { + printNames(new String[] { + 45, + 99, + 23 + }); +} +``` + +## Challenge 3. + +Write a method called `onlyPositive` which takes in an `int` and returns +the same value out if the number is greater than zero. + +If the number is less than or equal to zero, return `null`. + +```java,editable +// Write onlyPositive here + +void main() { + // 45 + System.out.println( + onlyPositive(45) + ); + + // 46 + System.out.println( + onlyPositive(45) + 1 + ); + + // null + System.out.println( + onlyPositive(0) + ); + + // null + System.out.println( + onlyPositive(-1) + ); +} +``` + +## Challenge 4. + +Will the following code work? Why or why not? + +```java +void main() { + int ducks = 5; + Integer sparrows = 3; + + int birds = ducks + sparrows; + + System.out.println(birds); +} +``` + +## Challenge 4. + +Will the following code work? Why or why not? + +```java +void main() { + char[] face = new char[] { ':', ')' }; + Character[] smile = face; + + System.out.println(smile); +} +``` + +## Challenge 5. + +Will the following code work? Why or why not? + +```java +void main() { + char[] face = new char[] { ':', ')' }; + + Character[] smile = new Character[face.length]; + for (int i = 0; i < face.length; i++) { + smile[i] = face[i]; + } + + System.out.println(smile); +} +``` \ No newline at end of file diff --git a/src/boxed_primitives/character.md b/src/boxed_primitives/character.md index 7ec17ce..75c8493 100644 --- a/src/boxed_primitives/character.md +++ b/src/boxed_primitives/character.md @@ -8,4 +8,18 @@ Character c = null; System.out.println(c); c = '%'; System.out.println(c); -~} \ No newline at end of file +~} +``` + +Unlike a `char[]`, a `Character[]` will not be have its contents +shown when printed. + +```java +~void main() { +char[] c1 = new char[] { 'a', 'b', 'c' }; +System.out.println(c1); + +Character[] c2 = new Character[] { 'a', 'b', 'c' }; +System.out.println(c2); +~} +``` \ No newline at end of file diff --git a/src/boxed_primitives/double.md b/src/boxed_primitives/double.md index 6655c41..97402b7 100644 --- a/src/boxed_primitives/double.md +++ b/src/boxed_primitives/double.md @@ -8,4 +8,15 @@ Double d = null; System.out.println(d); d = 3.14; System.out.println(d); -~} \ No newline at end of file +~} +``` + +If you try to do any math on a `Double` which holds `null` you will +get a `NullPointerException`. + +```java,panics +~void main() { +Double d = null; +System.out.println(d + 1); +~} +``` \ No newline at end of file diff --git a/src/boxed_primitives/integer.md b/src/boxed_primitives/integer.md index 95f8cf0..dc2c83b 100644 --- a/src/boxed_primitives/integer.md +++ b/src/boxed_primitives/integer.md @@ -9,4 +9,14 @@ System.out.println(i); i = 5; System.out.println(i); ~} +``` + +If you try to do any math on an `Integer` which holds `null` you will +get a `NullPointerException`. + +```java,panics +~void main() { +Integer i = null; +System.out.println(i * 5); +~} ``` \ No newline at end of file diff --git a/src/classes/challenges.md b/src/classes/challenges.md new file mode 100644 index 0000000..d7bf220 --- /dev/null +++ b/src/classes/challenges.md @@ -0,0 +1,150 @@ +# 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 classes are named "wrong." Name them correctly. + +```java,editable +class gonzo {} + +class fozzie_the_bear {} + +class MSPIGGY {} + +void main() { + System.out.println(new gonzo()); + System.out.println(new fozzie_the_bear()); + System.out.println(new MSPIGGY()); +} +``` + +## Challenge 2. + +Make a variable named `movie` which is an instance of the `Movie` class. +Set the value of its `title` field to `Muppets in Space`. + +```java,editable +class Movie { + String title; +} + +void main() { + // --------------- + // CODE HERE + // --------------- + + System.out.println( + movie.title + ); +} +``` + +## Challenge 3. + +Alter the `ThemePark` class so that the default value +for its `entranceFee` field is `35.24`. + +```java,editable +class ThemePark { + double entranceFee; +} + +void main() { + ThemePark themePark = new ThemePark(); + + System.out.println( + themePark.entranceFee + ); +} +``` + +## Challenge 4. + +Changing only the indicated line, +make it so the word `Kermit` only appears twice in the +program. + +Hint: Remember that inferred types exist. + +```java,editable +class Kermit { + boolean angry = true; +} + +void main() { + // ------------------------ + // CHANGE ONLY THIS LINE v + Kermit kermit = new Kermit(); + // ------------------------ + + System.out.println(kermit.angry); +} +``` + +## Challenge 5. + +Make a method named `squareRoot` which returns an +instance of the `SquareRoot` class containing both +the `positiveRoot` and `negativeRoot` of the given `double`. + +So if you are given `4` you should return a positive root +of `2` and a negative root of `-2`. + +You do not have to account for the possibility of being given a negative +number. You should use `Math.sqrt` to find the positive root and common +sense to find the negative root. + +```java,editable +class SquareRoot { + double positiveRoot; + double negativeRoot; +} + +void squareRoot(double value) { + // ----------- + // CODE HERE + // ----------- +} + +void main() { + SquareRoot sqrtOfFour = squareRoot(4); + // 2 + System.out.println(sqrtOfFour.positiveRoot); + // -2 + System.out.println(sqrtOfFour.negativeRoot); + + SquareRoot sqrtOfFifteen = squareRoot(15); + // 3.872983346207417 + System.out.println(sqrtOfFifteen.positiveRoot); + // -3.872983346207417 + System.out.println(sqrtOfFifteen.negativeRoot); +} +``` + +## Challenge 6. + +Only writing code between the lines and without directly accessing any fields on or reassigning the `actor` variable, make the program output `Tim Curry`. + +Hint: The key word is "directly." + +```java,editable +class Actor { + String name; +} + +void main() { + Actor actor = new Actor(); + actor.name = "Frog, Kermit the"; + + // -------------------------- + // CODE HERE + // -------------------------- + + System.out.println(actor.name); +} +``` \ No newline at end of file diff --git a/src/classes/instances.md b/src/classes/instances.md index 0546560..2131840 100644 --- a/src/classes/instances.md +++ b/src/classes/instances.md @@ -19,4 +19,4 @@ Very similarly to arrays, the output from printing an instance of a class might You will learn how to make it nicer later. [^var]: I haven't used it in many code samples thus far, but if you remember `var` this is one of the times -where it can be aethetically convenient. `var kermit = new Muppet();` +where it can be aesthetically convenient. `var kermit = new Muppet();` diff --git a/src/classes/return_multiple_values.md b/src/classes/return_multiple_values.md index a78b9d4..7db4f01 100644 --- a/src/classes/return_multiple_values.md +++ b/src/classes/return_multiple_values.md @@ -24,9 +24,9 @@ void main() { Location treasureIsland = findTreasureIsland(); System.out.println( "Treasure island is located at " + - location.latitude + + treasureIsland.latitude + " " + - location.longitude + + treasureIsland.longitude + "." ); } diff --git a/src/classes/user_defined_types.md b/src/classes/user_defined_types.md deleted file mode 100644 index 10d5977..0000000 --- a/src/classes/user_defined_types.md +++ /dev/null @@ -1,3 +0,0 @@ -# User Defined Types - -Declaring a class diff --git a/src/classes/zero_values.md b/src/classes/zero_values.md deleted file mode 100644 index fd09e2a..0000000 --- a/src/classes/zero_values.md +++ /dev/null @@ -1 +0,0 @@ -# Zero Values diff --git a/src/constructors/challenges.md b/src/constructors/challenges.md new file mode 100644 index 0000000..9a8c32d --- /dev/null +++ b/src/constructors/challenges.md @@ -0,0 +1,147 @@ +# Challenges + +- 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 `Shoe` class. Give each shoe a `name` field and a `quality` field. +Have these fields be initialized inside of a constructor. + +```java,editable +enum Quality { + SUPA_FINE, + FINE, + SUB_FINE +} + +// CODE HERE + +void main() { + Shoe nike = new Shoe("Nikes", Quality.SUB_FINE); + System.out.println( + "SHOE: " + nike.name + ", " + nike.quality + ); + + + Shoe moccasin = new Shoe("Moccasins", Quality.SUPA_FINE); + System.out.println( + "SHOE: " + moccasin.name + ", " + moccasin.quality + ); +} +``` + +## Challenge 2. + +Add a new `price` field to the `Shoe` class you wrote above. + +Add a new constructor which accepts a third argument to set the `price`. +Keep the old two argument constructor around as well. When that one is +used set `price` to `null`. + +Hint: Use `Double` to represent a nullable price. + +```java,editable +enum Quality { + SUPA_FINE, + FINE, + SUB_FINE +} + +// CODE HERE + +void main() { + Shoe jays = new Shoe("Air Jordans", Quality.FINE, 130.0); + System.out.println( + "SHOE: " + jays.name + ", " + jays.quality + ", $" + jays.price + ); + + Shoe nike = new Shoe("Nikes", Quality.SUB_FINE, 25); + System.out.println( + "SHOE: " + nike.name + ", " + nike.quality + ", $" + jays.price + ); + + + Shoe moccasin = new Shoe("Moccasins", Quality.SUPA_FINE); + System.out.println( + "SHOE: " + moccasin.name + ", " + moccasin.quality + ", $" + jays.price + ); +} +``` + +## Challenge 3. + +Alter the `Shoe` class so that the `price` field is final. +Alter its constructors so that if they are given a negative +value they throw an exception instead of finishing normally. + +Keep in mind that while `null` is allowed (you might not know the price) +a negative number wouldn't be. Nobody is paying you to take their shoes.[^holes] + +```java,editable +enum Quality { + SUPA_FINE, + FINE, + SUB_FINE +} + +// CODE HERE + +void main() { + Shoe jays = new Shoe("Air Jordans", Quality.FINE, 130.0); + System.out.println( + "SHOE: " + jays.name + ", " + jays.quality + ", $" + jays.price + ); + + Shoe nike = new Shoe("Nikes", Quality.SUB_FINE, 25); + System.out.println( + "SHOE: " + nike.name + ", " + nike.quality + ", $" + jays.price + ); + + + Shoe moccasin = new Shoe("Moccasins", Quality.SUPA_FINE); + System.out.println( + "SHOE: " + moccasin.name + ", " + moccasin.quality + ", $" + jays.price + ); + + Shoe shouldCrash = new Shoe("Base Ball Cleats", Quality.SUPA_FINE, -10); +} +``` + +[^holes]: If they do, you are going to dig holes in the desert in search of treasure that rightfully belongs +to you. + +## Challenge 4. + +If you haven't yet, rewrite your `Shoe` constructors so only one of them actually sets fields +and the other just delegates to that one. + +```java,editable +enum Quality { + SUPA_FINE, + FINE, + SUB_FINE +} + +// CODE HERE + +void main() { + Shoe jays = new Shoe("Air Jordans", Quality.FINE, 130.0); + System.out.println( + "SHOE: " + jays.name + ", " + jays.quality + ", $" + jays.price + ); + + Shoe nike = new Shoe("Nikes", Quality.SUB_FINE, 25); + System.out.println( + "SHOE: " + nike.name + ", " + nike.quality + ", $" + jays.price + ); + + + Shoe moccasin = new Shoe("Moccasins", Quality.SUPA_FINE); + System.out.println( + "SHOE: " + moccasin.name + ", " + moccasin.quality + ", $" + jays.price + ); + + Shoe shouldCrash = new Shoe("Base Ball Cleats", Quality.SUPA_FINE, -10); +} +``` \ No newline at end of file diff --git a/src/encapsulation.md b/src/encapsulation.md index 3bd7237..de517e6 100644 --- a/src/encapsulation.md +++ b/src/encapsulation.md @@ -1 +1,9 @@ # Encapsulation + +One way of combatting the effects of Hyrum's Law +is encapsulation. + +To encapsulate something is to hide it from the larger world. By doing +so you lower the number of people who are able to observe what we would call +"implementation details." + diff --git a/src/encapsulation/abstractions.md b/src/encapsulation/abstractions.md new file mode 100644 index 0000000..fa05de0 --- /dev/null +++ b/src/encapsulation/abstractions.md @@ -0,0 +1,8 @@ +# Abstractions + +A term closely related to encapsulation is abstraction. + +When you encapsulate something you have made an abstraction on top of it. + +You could say that "abstraction" is the noun and "encapsulation" is the verb. +You encapsulate things, thereby making abstractions. \ No newline at end of file diff --git a/src/encapsulation/classes.md b/src/encapsulation/classes.md new file mode 100644 index 0000000..87846ef --- /dev/null +++ b/src/encapsulation/classes.md @@ -0,0 +1 @@ +# Classes diff --git a/src/encapsulation/implementation_details.md b/src/encapsulation/implementation_details.md new file mode 100644 index 0000000..1d6a88d --- /dev/null +++ b/src/encapsulation/implementation_details.md @@ -0,0 +1,21 @@ +# Implementation Details + +Just as there is almost always more than one way +to remove the epidermis of a feline, +there is often more than one way to implement +a program. + +We call properties and choices made when writing a program +that are not related to what that program ultimitely needs +to do "implementation details." + +As an example, imagine you are about to type +a query into Microsoft Bing.[^bingit] The website you go to +and the search box you see are intended parts of the program. +The fact that if you click search you will see results is also +just what the program needs to do. + +But all the mechanics of *how* that query is answered are +implementation details. + +[^bingit]: You know, when you have a question and just have to "bing it?" \ No newline at end of file diff --git a/src/encapsulation/implicit_interfaces.md b/src/encapsulation/implicit_interfaces.md new file mode 100644 index 0000000..837c75e --- /dev/null +++ b/src/encapsulation/implicit_interfaces.md @@ -0,0 +1 @@ +# Implicit Interfaces diff --git a/src/encapsulation/information_hiding.md b/src/encapsulation/information_hiding.md new file mode 100644 index 0000000..e01dcd9 --- /dev/null +++ b/src/encapsulation/information_hiding.md @@ -0,0 +1,18 @@ +# Information Hiding + +When encapsulating, your fundamental activity is finding +ways to hide information that you consider to be +implementation details. + +If you did not hide this information and +you have a large number of consumers[^if], you would never +be able to change those implementation details. + +Something to be careful of with respect to this is "side channels." +If you use the mechanisms Java gives you to hide a field from people, +.. + +[^if]: Again, if. These concerns do not apply as much to +programs written at smaller scales or programs written +_within_ some encapsulation boundary. Don't get too paranoid +about needing to hide things. \ No newline at end of file diff --git a/src/encapsulation/leaky_abstractions.md b/src/encapsulation/leaky_abstractions.md new file mode 100644 index 0000000..f00d449 --- /dev/null +++ b/src/encapsulation/leaky_abstractions.md @@ -0,0 +1 @@ +# Leaky Abstractions diff --git a/src/encapsulation/methods.md b/src/encapsulation/methods.md new file mode 100644 index 0000000..1b3b663 --- /dev/null +++ b/src/encapsulation/methods.md @@ -0,0 +1,43 @@ +# Methods + +The simplest mechanism for encapsulating implementation details +is a method. + +The `Math.sin` function that comes with Java has a definition that looks +something like this.[^liberties] + +```java,no_run +double sin(double x) { + double[] y = new double[2]; + double z = 0.0; + int n, ix; + + // High word of x. + ix = __HI(x); + + // |x| ~< pi/4 + ix &= EXP_SIGNIF_BITS; + if (ix <= 0x3fe9_21fb) { + return __kernel_sin(x, z, 0); + } else if (ix >= EXP_BITS) { // sin(Inf or NaN) is NaN + return x - x; + } else { // argument reduction needed + n = RemPio2.__ieee754_rem_pio2(x, y); + // ... + } +} +``` + +People who write programs that depend on the `sin` function do not generally +understand how this works. I have a math minor[^notimpressive] and I certainly don't. + +All they need to know to use `sin` effectively is what it does, not how it does it. +In this way methods are one way to provide encapsulation. You reduce +a potentially complicated body of code to inputs required and outputs produced.[^news] + +[^liberties]: I took some creative liberties here, roll with it. + +[^notimpressive]: Laugh it up. + +[^news]: This shouldn't be news to you at this point, but I think its helpful to point +out that property in the context of this topic. \ No newline at end of file diff --git a/src/enums.md b/src/enums.md index 6d5b3a8..58a03fa 100644 --- a/src/enums.md +++ b/src/enums.md @@ -17,4 +17,4 @@ enum StopLight { } ``` -We call them enums because they enumerate multiple different possibilities. \ No newline at end of file +Enums are types with fixed sets of allowed values. We call them enums because they enumerate multiple different possibilities. \ No newline at end of file diff --git a/src/enums/challenges.md b/src/enums/challenges.md new file mode 100644 index 0000000..34fd123 --- /dev/null +++ b/src/enums/challenges.md @@ -0,0 +1,107 @@ +# 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 an enum named `Response` which +has three variants. `YES`, `NO`, and `MAYBE_SO`. + +```java,editable +// ------------- +// CODE HERE +// ------------- + +void main() { + System.out.println( + Response.YES + ); + + System.out.println( + Response.NO + ); + + System.out.println( + Response.MAYBE_SO + ); +} +``` + +## Challenge 2. + +Write a method named `goodPerformer` which takes +in a `String` representing the name of an artist. + +If that `String` is equal to `Pitbull` or `Billy Joel` +return `YES`. If it is equal to `Shaggy` return `NO`. +Otherwise return `MAYBE_SO`. + +Use the enum you defined above. + +```java,editable +// ------------ +// CODE HERE +// ------------ + +void main() { + Response pitbull = goodPerformer("Pitbull"); + System.out.println(pitbull); + + Response billyJoel = goodPerformer("Billy Joel"); + System.out.println(billyJoel); + + Response shaggy = goodPerformer("Shaggy"); + System.out.println(shaggy); + + Response chappelRoan = goodPerformer("Chappell Roan"); + System.out.println(chappelRoan); +} +``` + +## Challenge 3. + +Make a method named `transition` which takes in a `StopLight` +and returns the next light it will transition to. + +For those who don't drive cars: red lights go to green, +green lights go to yellow, and yellow lights go to red. + +```java,editable +enum StopLight { + RED, + YELLOW, + GREEN +} + +StopLight transition(StopLight current) { + // ------------ + // CODE HERE + // ------------ +} + +void main() { + var light = StopLight.RED; + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); +} +``` \ No newline at end of file diff --git a/src/exceptions/challenges.md b/src/exceptions/challenges.md new file mode 100644 index 0000000..017c57b --- /dev/null +++ b/src/exceptions/challenges.md @@ -0,0 +1,166 @@ +# 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 `arise` which accepts a `String` parameter +representing someone's name and prints `Awake `. + +If this function is given an empty string throw a `RuntimeException`. + +```java,editable +// ------------ +// CODE HERE +// ------------ + +void main() { + arise("Lion El'Jonson"); + arise("Roboute Guilliman"); + + arise(""); +} +``` + +## Challenge 2. + +Starting with the code you wrote above, make the thrown runtime exception +include a message saying why it was thrown ("given an empty string" or something like that.) + +```java,editable +// ------------ +// CODE HERE +// ------------ + +void main() { + arise("Lion El'Jonson"); + arise("Roboute Guilliman"); + + arise(""); +} +``` + +## Challenge 3. + +The following code is written in an intentionally confusing way. +Instead of reading the code and trying to figure it out that way, +run the code and read the stack trace to figure out which method originally +throws an exception. + +```java,panics +void a(int x) { + if (x == 0) { + throw new RuntimeException(); + } + else { + b(x / 2); + } +} + +void b(int x) { + if (x == 0) { + throw new RuntimeException(); + } + else { + c(x * 3 + 5); + } +} + +void c(int x) { + if (x == 0) { + throw new RuntimeException(); + } + else { + d(x / 4); + } +} + +void d(int x) { + if (x == 0) { + throw new RuntimeException(); + } + else { + e(x / 3); + } +} + +void e(int x) { + if (x == 0) { + throw new RuntimeException(); + } + else { + a(x / 10); + } +} + +void main() { + a(1215135236); +} +``` + +## Challenge 4. + +Write a method named `command` which takes in a `SpaceMarine`. +If the space marine is corrupted throw a `RuntimeException`. +Otherwise print out their name. + +```java,editable +class SpaceMarine { + boolean corrupted; + String name; +} + +// --------------------- +// CODE HERE +// --------------------- + +void main() { + SpaceMarine titus = new SpaceMarine(); + titus.corrupted = false; + titus.name = "Demetrian Titus"; + + command(titus); + + SpaceMarine imurah = new SpaceMarine(); + imurah.corrupted = true; + imurah.name = "Imurah"; + + command(imurah); +} +``` + +## Challenge 5. + +Alter your code above by adding a new method named `safeCommand`. It should +call `command` in a `try/catch` block. If a `RuntimeException` is thrown +it should print `Unable to command`. + +```java,editable +class SpaceMarine { + boolean corrupted; + String name; +} + +// --------------------- +// CODE HERE +// --------------------- + +void main() { + SpaceMarine titus = new SpaceMarine(); + titus.corrupted = false; + titus.name = "Demetrian Titus"; + + command(titus); + safeCommand(titus); + + SpaceMarine imurah = new SpaceMarine(); + imurah.corrupted = true; + imurah.name = "Imurah"; + + safeCommand(imurah); +} +``` + diff --git a/src/exceptions/exception_messages b/src/exceptions/exception_messages deleted file mode 100644 index f0aebb4..0000000 --- a/src/exceptions/exception_messages +++ /dev/null @@ -1 +0,0 @@ -# Exception Messages diff --git a/src/exceptions/throw.md b/src/exceptions/throw.md index ba0bf63..7dabe91 100644 --- a/src/exceptions/throw.md +++ b/src/exceptions/throw.md @@ -1,6 +1,6 @@ # throw -In order to throw an exception from your own code, you say `throw` then the name +In order to throw an exception from your own code, you say `throw`, `new`, then the name of the exception and `()`. `RuntimeException` is one of many kinds of exceptions, but you can make do with only diff --git a/src/files/challenges.md b/src/files/challenges.md new file mode 100644 index 0000000..d255025 --- /dev/null +++ b/src/files/challenges.md @@ -0,0 +1,82 @@ +# 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 file named `hello.txt` and give it `Hello, world` +as contents. + +## Challenge 2. + +Write a program that asks the user for a number and writes it into +a file named `numbers.txt`. + +## Challenge 3. + +Update the previous program so that the list of numbers entered by a user +is stored in the file. So if they give `1`, `2`, and `3` the file should contain +something like the following. + +``` +1 +2 +3 +``` + +Hint: `\n` is how you embed a newline character in `String`. You might find it useful. + +## Challenge 4. + +Update the previous program to also display the biggest number given so far +if the user types `biggest` instead of a number. + + +## Challenge 5. + +Make the previous program behave sensibly if the file contains data that is not numbers. + +## Challenge 6. + +Complete this program. + +```java +import java.nio.file.Path; +import java.io.IOException; + +class Main { + record Person(String name, int age) {} + + void main() throws IOException { + var people = new Person[] { + new Person("Steve Smith", 15), + new Person("Stan Smith", 42), + new Person("Rodger", 1601) + }; + + var path = Path.of("people.txt"); + + save(path, people); + + people = load(path); + + System.out.println(people[0]); + System.out.println(people[1]); + System.out.println(people[2]); + + } + + void save(Path path, Person[] people) throws IOException { + // Save to a file + } + + Person[] load(Path path) throws IOException { + return null; // Make actually return an array + } +} +``` + + diff --git a/src/files/creating_a_folder.md b/src/files/creating_a_folder.md index 78e4e42..697e2db 100644 --- a/src/files/creating_a_folder.md +++ b/src/files/creating_a_folder.md @@ -14,6 +14,6 @@ class Main { } ``` -This, like the other methods in `Files`, throws an `IOException`. +This, like the other methods in `Files`, might throw an `IOException`. `Files.createDirectory` will fail if the folder already exists. diff --git a/src/first_steps.md b/src/first_steps.md index 15549ef..2a56d24 100644 --- a/src/first_steps.md +++ b/src/first_steps.md @@ -32,7 +32,7 @@ System.out.println("Hello, World!"); This bit of magic here - `System.out.println` - is a "statement" that "prints" the text inside the `(` and `)` as well as a "new line" to the screen. -`print` with new `l`i`n`e. +**print** with new **l**i**n**e. If you were to replace it with `System.out.print`, then the output would lack that new line. This makes the following program be functionally identical to the first. diff --git a/src/generics.md b/src/generics.md index c7e8cef..d428f73 100644 --- a/src/generics.md +++ b/src/generics.md @@ -1,4 +1,15 @@ # 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 +store different kinds of data. + +Generics help us make these generically useful containers. + ```java -public class \ No newline at end of file +class Box { + T value; +} +``` + diff --git a/src/generics/inference.md b/src/generics/inference.md new file mode 100644 index 0000000..659b409 --- /dev/null +++ b/src/generics/inference.md @@ -0,0 +1,45 @@ +# Inference + +Sometimes Java has enough information to know +what the right values for type variables would be without you +specifying them. + +In these cases you can rely on the compiler to infer them by writing `<>` with no type variables +in between. + +```java +class Box { + T data; +} + +void main() { + // It is being assigned to a Box on the left + // so Java can figure out what should be on the right. + Box boxOfString = new Box<>(); + + boxOfString.data = "abc"; + + String s = boxOfString.data; + + System.out.println(s); +} +``` + +This inference does not work on the left-hand side of an `=`. + +```java,does_not_compile +class Box { + T data; +} + +void main() { + // Use var if you want inference for local variables. + Box<> boxOfString = new Box(); + + boxOfString.data = "abc"; + + String s = boxOfString.data; + + System.out.println(s); +} +``` \ No newline at end of file diff --git a/src/generics/instantiation.md b/src/generics/instantiation.md new file mode 100644 index 0000000..ebdcf55 --- /dev/null +++ b/src/generics/instantiation.md @@ -0,0 +1,23 @@ +# Instantiation + +When you make an instance of a class that takes generic parameters +you are expected to provide the actual concrete types you want to be used +in place of any type variables. + +You do this by writing the name of the types inside a `<>` alongside the call to `new`. + +```java +class Box { + T data; +} + +void main() { + var boxOfString = new Box(); + + boxOfString.data = "abc"; + + String s = boxOfString.data; + + System.out.println(s); +} +``` \ No newline at end of file diff --git a/src/generics/naming.md b/src/generics/naming.md new file mode 100644 index 0000000..da1ce4b --- /dev/null +++ b/src/generics/naming.md @@ -0,0 +1,10 @@ +# Naming + +Type variables don't have to be only a single letter, though that is common. If you pick a longer name +you are generally expected to name them as if they were classes. + +```java +class Box { + Data data; +} +``` \ No newline at end of file diff --git a/src/generics/raw_types.md b/src/generics/raw_types.md new file mode 100644 index 0000000..3ae11d1 --- /dev/null +++ b/src/generics/raw_types.md @@ -0,0 +1,51 @@ +# Raw Types + +If generics are cramping your style, you are +technically allowed to turn them off. + +If you make an instance of a generic class without +any `<>` we call that a "raw type." + +```java +class Box { + T data; +} + +void main() { + Box b = new Box(); +} +``` + +When you have a raw type you will see `Object` in any place +you put[^unbounded] a type variable. + +This lets you do whatever you want without the burden of having to make sense to Java. + +```java +class Box { + T data; +} + +void main() { + Box b = new Box(); + b.data = 123; + b.data = "abc"; + + if (b.data instanceof String s) { + System.out.println(s); + } +} +``` + +Raw types exist for two basic reasons + +1. Every now and then Java isn't smart enough. Trust that there are valid reasons to turn off generics, even +I haven't shown you any yet. Avoid doing so yourself - at least for awhile. +2. Generics weren't always in Java! Classes that later were made generic had to stay compatible with old "raw" +usages somehow. + +All that is to say: Be aware raw types exist. Make sure you are always putting `<>` otherwise you are falling +into that mode. Avoid that mode. + + +[^unbounded]: An "unbounded" type variable to be exact. We'll visit generic bounds later. \ No newline at end of file diff --git a/src/generics/soundness.md b/src/generics/soundness.md new file mode 100644 index 0000000..6f4f00a --- /dev/null +++ b/src/generics/soundness.md @@ -0,0 +1,22 @@ +# Soundness + +Even though every `String` is assignable to `Object`, +a `Box` is not assignable to `Box`. + +If that was allowed then you could do something like the following. + +```java,no_run,does_not_compile +Box stringBox = new Box<>(); +// This might feel innocent +Box objectBox = stringBox; +// But now we can put anything, +// like an Integer, in the Box +objectBox.data = 123; +// Which is a problem since that affects +// stringBox as well +String s = stringBox.data; // But its actually an Integer! Crash! +``` + +We call this property - that the types don't let you accidentally make +silly situations - soundness. Java isn't _fully_ sound, but its sound enough +for most people. \ No newline at end of file diff --git a/src/generics/type_variables.md b/src/generics/type_variables.md new file mode 100644 index 0000000..f1144fc --- /dev/null +++ b/src/generics/type_variables.md @@ -0,0 +1,25 @@ +# Type Variables + +After the name of a class in the class definition you can put one or more "type variables." +These look like `<` followed by a comma separated list of "type names" and ended by a `>`. + +```java,no_run +class Box { + +} +``` + +Inside a class definition you can use these type variables on fields, method arguments, +and method return types. + +```java,no_run +class Box { + T data; + + void setData(T newData) { + this.data = newData; + } +} +``` + +This indicates that you would be okay with any type being used in place of the type variable (in the above code `T`). diff --git a/src/getting_started.md b/src/getting_started.md index dd99463..64fc8cd 100644 --- a/src/getting_started.md +++ b/src/getting_started.md @@ -23,6 +23,19 @@ 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). + +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. + +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 diff --git a/src/hash_maps/get_items.md b/src/hash_maps/get_items.md index 82fdc4a..6a50c14 100644 --- a/src/hash_maps/get_items.md +++ b/src/hash_maps/get_items.md @@ -86,31 +86,41 @@ class CarsCharacterHashMap { return carsCharacter.lastName().charAt(0); } - void put(CarsCharacter character) { + void put(String lastName, CarsCharacter character) { // 1. Compute the hash code - char hash = hashFunction(character); + char hash = hashFunction(lastName); // 2. Find the bucket Bucket bucket = buckets[indexFor(hash)]; // 3. Add to the bucket bucket.add(character); } +} - ` get(CarsCharacter character) { + CarsCharacter get(String lastName) { // 1. Compute the hash code char hash = hashFunction(character); // 2. Find the bucket Bucket bucket = buckets[indexFor(hash)]; - // 3. Add to the bucket - bucket.add(character); + // 3. Go through everything in the bucket + for (int i = 0; i < bucket.size(); i++) { + CarsCharacter inBucket = bucket.get(i); + if (inBucket.equals(character)) { + return inBucket; // Return any matches + } + } + + // Null if no matches + return null; } } void main() { var map = new CarsCharacterHashMap(); - map.put(new CarsCharacter("Sally", "Carrera")); - map.put(new CarsCharacter("Doc", "Hudson")); - map.put(new CarsCharacter("Lightning", "McQueen")); + map.put("Carrera", new CarsCharacter("Sally", "Carrera")); + map.put("Hudson", new CarsCharacter("Doc", "Hudson")); + map.put("McQueen", new CarsCharacter("Lightning", "McQueen")); + System.out.println(map.buckets[0].size()); System.out.println(map.buckets[1].size()); diff --git a/src/hash_maps/hash_functions.md b/src/hash_maps/hash_functions.md index 5eaa71e..280e867 100644 --- a/src/hash_maps/hash_functions.md +++ b/src/hash_maps/hash_functions.md @@ -10,15 +10,15 @@ record CarsCharacter( ) {} char hashFunction( - CarsCharacter carsCharacter + String lastName ) { - return carsCharacter.lastName().charAt(0); + return lastName.charAt(0); } void main() { var lightning = new CarsCharacter("Lightning", "McQueen"); - char firstOfLast = hashFunction(lightning); + char firstOfLast = hashFunction(lightning.lastName()); System.out.println(firstOfLast); } ``` diff --git a/src/hash_maps/put_items.md b/src/hash_maps/put_items.md index 0040610..3427184 100644 --- a/src/hash_maps/put_items.md +++ b/src/hash_maps/put_items.md @@ -4,7 +4,7 @@ Putting it all together[^getit], we can "put" items into our hash map by following these steps. 1. Compute the hash code of the key. -2. Find the bucket that item should go in. +2. Find the bucket that item should go in based on the key. 3. Add the item to the bucket. ```java @@ -81,14 +81,14 @@ class CarsCharacterHashMap { } char hashFunction( - CarsCharacter carsCharacter + String lastName ) { - return carsCharacter.lastName().charAt(0); + return lastName.charAt(0); } - void put(CarsCharacter character) { + void put(String lastName, CarsCharacter character) { // 1. Compute the hash code - char hash = hashFunction(character); + char hash = hashFunction(lastName); // 2. Find the bucket Bucket bucket = buckets[indexFor(hash)]; // 3. Add to the bucket @@ -99,9 +99,9 @@ class CarsCharacterHashMap { void main() { var map = new CarsCharacterHashMap(); - map.put(new CarsCharacter("Sally", "Carrera")); - map.put(new CarsCharacter("Doc", "Hudson")); - map.put(new CarsCharacter("Lightning", "McQueen")); + map.put("Carrera", new CarsCharacter("Sally", "Carrera")); + map.put("Hudson", new CarsCharacter("Doc", "Hudson")); + map.put("McQueen", new CarsCharacter("Lightning", "McQueen")); System.out.println(map.buckets[0].size()); System.out.println(map.buckets[1].size()); diff --git a/src/hyrums_law.md b/src/hyrums_law.md new file mode 100644 index 0000000..17d8adf --- /dev/null +++ b/src/hyrums_law.md @@ -0,0 +1,8 @@ +# Hyrum's Law + +This is Hyrum's Law. + +> With a sufficient number of users of an API, +> it does not matter what you promise in the contract: +> all observable behaviors of your system +> will be depended on by somebody. \ No newline at end of file diff --git a/src/hyrums_law/authority.md b/src/hyrums_law/authority.md new file mode 100644 index 0000000..1dd10af --- /dev/null +++ b/src/hyrums_law/authority.md @@ -0,0 +1,11 @@ +# Authority + +Hyrum's Law, and many other "laws" in software development, +are not really laws in the same way as "The Law of Conservation of Energy." + +Those sorts of laws are observed truths about a field. Best we can tell they are always true, no matter what. + +Hyrum's Law is just an aphorism. Its one specific person's view on the field. + +This sounds sketchy, and it is, but the state +of the computing field is such that aphorisms and personal anecdotes are often the best information we have. diff --git a/src/hyrums_law/emergent_properties.md b/src/hyrums_law/emergent_properties.md new file mode 100644 index 0000000..37ef9b5 --- /dev/null +++ b/src/hyrums_law/emergent_properties.md @@ -0,0 +1,11 @@ +# Emergent Properties + +Emergent properties are things that simply "emerge" +as a consequence of other factors. + +Hyrum's law is one of those sorts of things. Nobody sat down and agreed +that they will use an API in wacky ways. It just happens when you throw enough +monkeys in a pile and they all reach for a keyboard. + +As such, the way to deal with it isn't to put blame upon the monkeys. Its +to accept it as a naturally occurring phenominon and plan accordingly. \ No newline at end of file diff --git a/src/hyrums_law/importance.md b/src/hyrums_law/importance.md new file mode 100644 index 0000000..5cf8cbd --- /dev/null +++ b/src/hyrums_law/importance.md @@ -0,0 +1,20 @@ +# Importance + +So why is this person's take important for you to know about? + +The reason is that the core concept - that +every observable property will be depended on +by somebody with enough consumers - is essential +context for understanding why Java is the way that it is. + +Many of Java's features are intended to give you ways +to minimize the number of observable properties of +the software you produce. + +Private fields and methods are the easiest examples of this so far. If you +make fields or methods private then you can start to expect that +consumers of that class do not observe them. Thus, if needed, you +will not break anyone by changing them.[^setAccessible] + +[^setAccessible]: There are asterisks to this particular point, unfortunately. +There are ways to magically get at private fields like criminal scum. \ No newline at end of file diff --git a/src/hyrums_law/validity.md b/src/hyrums_law/validity.md new file mode 100644 index 0000000..fe32117 --- /dev/null +++ b/src/hyrums_law/validity.md @@ -0,0 +1,23 @@ +# Validity + +Now is this law literally true? No. It can't be. There are more +"properties" you can observe about a piece of software than +people on the planet. + +The specific number of degrees your CPU heats up when running a program can, hypothetically, be relied on. Chances are that +won't happen though. + +Personally I think that the expanded explanation on his website ([https://www.hyrumslaw.com/](https://www.hyrumslaw.com/)) is +more nuanced than the short version, if a lot less snappy and easy to remember. + +> Over the past couple years of doing low-level infrastructure migrations in one of the most complex software systems on the planet, I’ve made some observations about the differences between an interface and its implementations. We typically think of the interface as an abstraction for interacting with a system (like the steering wheel and pedals in a car), and the implementation as the way the system does its work (wheels and an engine). This is useful for a number of reasons, foremost among them that most useful systems rapidly become too complex for a single individual or group to completely understand, and abstractions are essential to managing that complexity. +> +> Defining the correct level of abstraction is a completely separate discussion (see Mythical Man-Month), but we like to think that once an abstraction is defined, it is concrete. In other words, an interface should theoretically provide a clear separation between consumers of a system and its implementers. In practice, this theory breaks down as the use of a system grows and its users start to rely upon implementation details intentionally exposed through the interface, or which they divine through regular use. Spolsky’s “Law of Leaky Abstractions” embodies consumers’ reliance upon internal implementation details. +> +> Taken to its logical extreme, this leads to the following observation, colloquially referred to as “The Law of Implicit Interfaces”: Given enough use, there is no such thing as a private implementation. That is, if an interface has enough consumers, they will collectively depend on every aspect of the implementation, intentionally or not. This effect serves to constrain changes to the implementation, which must now conform to both the explicitly documented interface, as well as the implicit interface captured by usage. We often refer to this phenomenon as "bug-for-bug compatibility." +> +> The creation of the implicit interface usually happens gradually, and interface consumers generally aren’t aware as it’s happening. For example, an interface may make no guarantees about performance, yet consumers often come to expect a certain level of performance from its implementation. Those expectations become part of the implicit interface to a system, and changes to the system must maintain these performance characteristics to continue functioning for its consumers. +> +> Not all consumers depend upon the same implicit interface, but given enough consumers, the implicit interface will eventually exactly match the implementation. At this point, the interface has evaporated: the implementation has become the interface, and any changes to it will violate consumer expectations. With a bit of luck, widespread, comprehensive, and automated testing can detect these new expectations but not ameliorate them. +> +> Implicit interfaces result from the organic growth of large systems, and while we may wish the problem did not exist, designers and engineers would be wise to consider it when building and maintaining complex systems. So be aware of how the implicit interface constrains your system design and evolution, and know that for any reasonably popular system, the interface reaches much deeper than you think. \ No newline at end of file diff --git a/src/instance_methods/challenges.md b/src/instance_methods/challenges.md new file mode 100644 index 0000000..6e07bef --- /dev/null +++ b/src/instance_methods/challenges.md @@ -0,0 +1,206 @@ +# 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 class named `PirateShip` which has one `int` field +named `crewSize`. + +Add an instance method to that class named `sail` which outputs + +``` +N sailors, ready to sail! +``` + +Where *N* is the size of the crew. + +```java,editable +// ---------------- +// CODE HERE +// ---------------- + +void main() { + PirateShip ship = new PirateShip(); + ship.crewSize = 25; + ship.sail(); +} +``` + +## Challenge 2. + +Make a class named `StringArrayView` which has +one `String[]` field named `value` + and two methods named `get` and `length`. + +`get` should take in an index and return the matching element +of the array. + +`length` should take no arguments and give the length of the array. + +```java,editable +// ---------------- +// CODE HERE +// ---------------- + +void main() { + StringArrayView view = new StringArrayView(); + view.value = new String[] { "A", "B", "C" }; + + // 3 + System.out.println(view.length()); + + // A + System.out.println(view.get(0)); + + // C + System.out.println(view.get(2)); +} +``` + +## Challenge 3. + +Alter the `VoiceActor` class so that it has a method named `fullName` +that returns their `firstName` followed by their `lastName` and separated +by a space. + +If their `lastName` is `null`, you should have no trailing space. +If their `firstName` is `null`, you should have no leading space. + +If both their `firstName` and `lastName` are `null`, you should +return `"No Name"`. + +```java,editable +class VoiceActor { + String firstName; + String lastName; + + // ----------------- + // CODE HERE + // ----------------- +} + +void main() { + VoiceActor goku = new VoiceActor(); + goku.firstName = "Masako"; + goku.lastName = "Nozawa"; + + // "Masako Nozawa" + String gokuFullName = goku.fullName(); + System.out.println(gokuFullName); + + // "Nozawa" + goku.firstName = null; + gokuFullName = goku.fullName(); + System.out.println(gokuFullName); + + // "No Name" + goku.lastName = null; + gokuFullName = goku.fullName(); + System.out.println(gokuFullName); + + // "Horikawa" + VoiceActor vegeta = new VoiceActor(); + vegeta.lastName = "Horikawa"; + System.out.println(vegeta.fullName()); +} +``` + +## Challenge 4. + +Make a `Rectange` class which has a `width` field and a `height` +field. Give it an instance method named `toCharArray` which gives +a `char[]` that can be printed to display a rectangle of the given +width and height. + +```java,editable +// ------------ +// CODE HERE +// ------------ + +void main() { + Rectangle rectangle = new Rectangle(); + rectangle.width = 3; + rectangle.height = 4; + + /* + *** + *** + *** + *** + */ + char[] c = rectangle.toCharArray(); + System.out.println(c); +} +``` + +## Challenge 5. + +Update the definition for the `Taco` class so that it has a method named +`deluxe`. This should set the taco to have beef, sour cream, cheese, +and onion. Use the existing instance methods instead of directly accessing +fields. + +```java,editable +class Taco { + boolean beef; + boolean sourCream; + boolean cheese; + boolean onion; + + void addBeef() { + this.beef = true; + } + + void addSourCream() { + this.sourCream = true; + } + + void addCheese() { + this.cheese = true; + } + + void addOnion() { + this.onion = true; + } + + void deluxe() { + // ------------ + // CODE HERE + // ------------ + } +} + +void main() { + var taco = new Taco(); + taco.deluxe(); + + System.out.println("Has Beef: " + taco.beef); + System.out.println("Has Sour Cream: " + taco.sourCream); + System.out.println("Has Cheese: " + taco.cheese); + System.out.println("Has Onion: " + taco.onion); +} +``` + +## Challenge 6. + +Why doesn't this code function as you'd expect? Fix it by changing one line. + +```java,editable +class Oscar { + boolean grouchy; + + void setGrouchy(boolean grouchy) { + grouchy = grouchy; + } +} + +void main() { + var oscar = new Oscar(); + oscar.setGrouchy(true); + System.out.println(oscar.grouchy); +} +``` \ No newline at end of file diff --git a/src/integers/equality.md b/src/integers/equality.md index 931f100..134f2da 100644 --- a/src/integers/equality.md +++ b/src/integers/equality.md @@ -13,10 +13,10 @@ boolean universeBroken = 1 == 2; System.out.println(universeBroken); int loneliestNumber = 1; -int otherLonelyNumber = 2; +int canBeAsBadAsOne = 2; // this will be true -boolean bothLonely = loneliestNumber == (otherLonelyNumber - 1); +boolean bothLonely = loneliestNumber == (canBeAsBadAsOne - 1); System.out.println(bothLonely); ~} ``` diff --git a/src/integers/operator_precedence.md b/src/integers/operator_precedence.md index 6384a87..9e28d38 100644 --- a/src/integers/operator_precedence.md +++ b/src/integers/operator_precedence.md @@ -1,4 +1,4 @@ -# Operator Precedance +# Operator Precedence Just like boolean operators, `+`, `-`, `*`, `/`, and `%` have a defined precedence order. diff --git a/src/integers/remainder.md b/src/integers/remainder.md index 97250df..3d422d5 100644 --- a/src/integers/remainder.md +++ b/src/integers/remainder.md @@ -41,7 +41,10 @@ System.out.println(value); // the remainder of (2 + 1) divided by 3 is 0 -// value will again be 0! +// value will again be 0. +// +// We never reach 3 because 3 divided by 3 +// always has a remainder of zero. value = (value + 1) % 3; System.out.println(value); @@ -51,6 +54,15 @@ value = (value + 1) % 3; System.out.println(value); // and so on. +// +// If you did this process with 5 you would go +// 0, 1, 2, 3, 4, 0, 1, ... +// +// If you did this process with 7 you would go +// 0, 1, 2, 3, 4, 5, 6, 0, 1, ... +// +// You always go back to the start just before you reach +// the number you are getting the remainder by. ~} ``` diff --git a/src/interface_extension.md b/src/interface_extension.md new file mode 100644 index 0000000..fbd7f53 --- /dev/null +++ b/src/interface_extension.md @@ -0,0 +1 @@ +# Interface Extension diff --git a/src/interfaces.md b/src/interfaces.md index 89ce18f..456e223 100644 --- a/src/interfaces.md +++ b/src/interfaces.md @@ -1 +1,14 @@ # 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. + +Interfaces do a related thing. They let you write code that takes advantage +of commonalities. + +```java +inteface Dog { + void bark(); +} +``` diff --git a/src/interfaces/fields.md b/src/interfaces/fields.md new file mode 100644 index 0000000..cb06804 --- /dev/null +++ b/src/interfaces/fields.md @@ -0,0 +1 @@ +# Fields diff --git a/src/interfaces/implementation.md b/src/interfaces/implementation.md new file mode 100644 index 0000000..6ce34f6 --- /dev/null +++ b/src/interfaces/implementation.md @@ -0,0 +1,45 @@ +# Implementation + +All interfaces do[^fornow] is hold method declarations. + +If you want to have a class which "implements" +the interface you can do so by writing `implements` followed by the interface +name. + +```java +interface Dog { + void bark(); + + String fetch(String ball); +} + +class Mutt implements Dog { + +} +``` + +Then you all you need to do is declare methods which match up with the methods defined in the interface. +Keep in mind that while you didn't write `public` in the interface, you need to write `public` +when implementing a method from an interface.[^all] + +```java +interface Dog { + void bark(); + + String fetch(String ball); +} + +class Mutt implements Dog { + public void bark() { + System.out.println("Bark"); + } + + public String fetch(String ball) { + return ball + " (with drool)"; + } +} +``` + +[^fornow]: For now* + +[^all]: All methods that come from an interface must be `public`. \ No newline at end of file diff --git a/src/interfaces/instances.md b/src/interfaces/instances.md new file mode 100644 index 0000000..c2b105a --- /dev/null +++ b/src/interfaces/instances.md @@ -0,0 +1 @@ +# Instances diff --git a/src/interfaces/interface_declaration.md b/src/interfaces/interface_declaration.md new file mode 100644 index 0000000..a88ad8c --- /dev/null +++ b/src/interfaces/interface_declaration.md @@ -0,0 +1,19 @@ +# Interface Declaration + +To declare an interface you write the word `interface` followed by the name of the interface +and `{}`. + +```java +interface Dog {} +``` + +Inside of the `{}` you can write the signatures for methods followed by a `;`. That means +no method body and no `public` or `private` modifiers. + +```java,no_run +interface Dog { + void bark(); + + String fetch(String ball); +} +``` diff --git a/src/interfaces/methods.md b/src/interfaces/methods.md new file mode 100644 index 0000000..8f83418 --- /dev/null +++ b/src/interfaces/methods.md @@ -0,0 +1 @@ +# Methods diff --git a/src/interfaces/multiple_implementations.md b/src/interfaces/multiple_implementations.md new file mode 100644 index 0000000..da84c1d --- /dev/null +++ b/src/interfaces/multiple_implementations.md @@ -0,0 +1,52 @@ +# Multiple Implementations + +Interfaces can be implemented any number of times. This means +that code which accepts an interface can't truly know the specifics +of how methods will work. + +This is a problem if you want maximum predictability but its +also the whole point of using an interface over a regular class. +You can write code that depends on a few key methods being defined +and be flexible to different ways of defining those methods. + +```java +interface Dog { + void bark(); + + String fetch(String ball); +} + +class Mutt implements Dog { + @Override + public void bark() { + System.out.println("Bark"); + } + + @Override + public String fetch(String ball) { + return ball + " (with drool)"; + } +} + +class Cat implements Dog { + @Override + public void bark() { + System.out.println("Meow"); + } + + @Override + public String fetch(String ball) { + return "no."; + } +} + +void barkAndFetch(Dog dog) { + dog.bark(); + System.out.println(dog.fetch("Ball")); +} + +void main() { + barkAndFetch(new Mutt()); + barkAndFetch(new Cat()); +} +``` diff --git a/src/interfaces/naming.md b/src/interfaces/naming.md new file mode 100644 index 0000000..15a7eef --- /dev/null +++ b/src/interfaces/naming.md @@ -0,0 +1,16 @@ +# Naming + +Interfaces are named in the same way as classes - `LikeThis`. + +In the wild west of the real world you might see people prefix any interface +name with `I`, for interface. + +In these cases instead of `Dog` you would see `IDog`. Instead of `PartyAnimal` you would +see `IPartyAnimal` and so on. + +The reason someone might do this is if they think it is worthwhile to have a visual indicator +of whether a type represents an interface or an actual class. Personally, I don't think that is +too useful, but there is nothing horrible about it. + +Just judge the social context you are in and don't hold it against people too hard if they do it a way +you don't like. \ No newline at end of file diff --git a/src/interfaces/override.md b/src/interfaces/override.md new file mode 100644 index 0000000..765ed8a --- /dev/null +++ b/src/interfaces/override.md @@ -0,0 +1,30 @@ +# @Override + +Just like when defining your own `equals`, `hashCode`, and `toString` you +can use `@Override` when implementing methods that come from an interface. + +```java +interface Dog { + void bark(); + + String fetch(String ball); +} + +class Mutt implements Dog { + @Override + public void bark() { + System.out.println("Bark"); + } + + @Override + public String fetch(String ball) { + return ball + " (with drool)"; + } +} +``` + + +Right now there isn't a mechanical use for this since Java will yell at you if +you defined the interface method wrong anyways, but there will be later. A small benefit +is that it makes it easier to tell at a glance which methods come from an interface +and which do not. \ No newline at end of file diff --git a/src/interfaces/subtypes.md b/src/interfaces/subtypes.md new file mode 100644 index 0000000..cd35a6d --- /dev/null +++ b/src/interfaces/subtypes.md @@ -0,0 +1,63 @@ +# Subtypes + +Just as everything is a subtype of `Object`, anything which +implements an interface is a subtype of that interface. + +For the following code this means that any field or variable +which holds a `Dog` can be assigned to an instance of `Mutt`. + +```java +interface Dog { + void bark(); + + String fetch(String ball); +} + +class Mutt implements Dog { + @Override + public void bark() { + System.out.println("Bark"); + } + + @Override + public String fetch(String ball) { + return ball + " (with drool)"; + } +} + +void main() { + Dog dog = new Mutt(); +} +``` + +Through a `Dog` variable you will be able to call any methods defined on the interface. +These will use the actual underlying implementation - in this case from `Mutt`. + +```java +interface Dog { + void bark(); + + String fetch(String ball); +} + +class Mutt implements Dog { + @Override + public void bark() { + System.out.println("Bark"); + } + + @Override + public String fetch(String ball) { + return ball + " (with drool)"; + } +} + +void main() { + Dog dog = new Mutt(); + + dog.bark(); + + System.out.println(dog.fetch("Ball")); +} +``` + diff --git a/src/iterable_and_iterator.md b/src/iterable_and_iterator.md new file mode 100644 index 0000000..5ca10f5 --- /dev/null +++ b/src/iterable_and_iterator.md @@ -0,0 +1 @@ +# Iterable and Iterator diff --git a/src/loops_ii/empty_statements.md b/src/loops_ii/empty_statements.md index 1c8076a..6371eac 100644 --- a/src/loops_ii/empty_statements.md +++ b/src/loops_ii/empty_statements.md @@ -1,7 +1,7 @@ # Empty Statements You can even leave the statement part of a `for` loop blank. This means that at -the end of an iteration there is nothing guarenteed to run. +the end of an iteration there is nothing guaranteed to run. ```java ~void main() { diff --git a/src/loops_iii/concurrent_modifications.md b/src/loops_iii/concurrent_modifications.md new file mode 100644 index 0000000..f6f973c --- /dev/null +++ b/src/loops_iii/concurrent_modifications.md @@ -0,0 +1 @@ +# Concurrent Modifications diff --git a/src/loops_iii/for_each_loops.md b/src/loops_iii/for_each_loops.md new file mode 100644 index 0000000..7f73c77 --- /dev/null +++ b/src/loops_iii/for_each_loops.md @@ -0,0 +1 @@ +# For-each loops diff --git a/src/multi_dimensional_arrays.md b/src/multi_dimensional_arrays.md index 2d2fe10..f01263e 100644 --- a/src/multi_dimensional_arrays.md +++ b/src/multi_dimensional_arrays.md @@ -1 +1,20 @@ # 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." + +```java +class Main { + void main() { + int width = 30; + int height = 30; + int[][] pixels = new int[width][height]; + + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + pixels[i][j] = 0; + } + } + } +} +``` diff --git a/src/null/challenges.md b/src/null/challenges.md new file mode 100644 index 0000000..a3ebda4 --- /dev/null +++ b/src/null/challenges.md @@ -0,0 +1,143 @@ +# 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 which takes in a `String[]` representing +a series of names and prints out every name in sequence. + +If this method is given `null`, it should act as if it +was given an empty array. + + +```java,editable +void printNames(String[] names) { + +} + +void main() { + printNames(new String[] { + "Joker", + "Batman", + "Alfred" + }); +} +``` + +## Challenge 2. + +Alter the method you wrote in the previous challenge +so that if it is given `null` it outputs the message +`You do not know any names yet`. + +If it is given an empty `String[]` it should continue +to simply output nothing. + +```java,editable +void printNames(String[] names) { + +} + +void main() { + printNames(new String[] { + "Joker", + "Batman", + "Alfred" + }); +} +``` + +## Challenge 3. + +Will the following code throw a `NullPointerException`? +Why or why not? + +```java +void main() { + String[] jobs = new String[] { + "Carpenter", + "Baker", + null, + "Astronomer" + }; + + for (int i = 0; i < jobs.length; i++) { + System.out.println(jobs[i]); + } +} +``` + +## Challenge 4. + +The following code won't work. Give your best guess as to why +and then try running it. + +```java +void main() { + int[] numbers = new int[] { + 45, + 32, + null, + 94 + }; + + for (int i = 0; i < numbers.length; i++) { + System.out.println(numbers[i]); + } +} +``` + +## Challenge 5. + +Without changing anything in the `main` method, make the `bigness` +method not throw a `NullPointerException` and still have the "correct" +behavior for non-null inputs. + +```java,editable +String bigness(String letters) { + int bigness = 0; + for (int i = 0; i < letters.length(); i++) { + bigness++; + } + + if (bigness < 5) { + return "small"; + } + else if (bigness < 10) { + return "medium" + } + else { + return "big"; + } +} + +void main() { + System.out.println( + bigness("bore") + ); + + System.out.println( + bigness("boiler") + ); + + System.out.println( + bigness("filter") + ); + + System.out.println( + bigness("knower") + ); + + System.out.println( + bigness("chrysanthemum") + ); + + System.out.println( + bigness(null) + ); +} +``` \ No newline at end of file diff --git a/src/objects/equals_and_hashCode.md b/src/objects/equals_and_hashCode.md index 7ca5cb0..f256e5f 100644 --- a/src/objects/equals_and_hashCode.md +++ b/src/objects/equals_and_hashCode.md @@ -1,2 +1,79 @@ # equals and hashCode +In addition to `toString`, two methods that all `Object`s have defined +are `equals` and `hashCode`. + +`equals` is a method that takes another object and returns a `boolean` based +on whether you would consider those objects to be equivalent. + +By default, `equals` will behave identically to `==`. + +```java +class Thing {} + +class Main { + void main() { + var t1 = new Thing(); + var t2 = new Thing(); + + System.out.println(t1 == t1); + System.out.println(t1.equals(t1)); + + System.out.println(t2 == t2); + System.out.println(t2.equals(t2)); + + + System.out.println(t1 == t2); + System.out.println(t1.equals(t2)); + } +} +``` + +Many types will have `equals` overridden to do things like return equal if +the types represent the same context, as is the case with `Integer`. + +```java +class Main { + void main() { + Integer a = 3; + Integer b = 3; + Integer c = 4; + + System.out.println(a.equals(b)); + System.out.println(a.equals(c)); + } +} +``` + +`hashCode` is a method that tells you if things _might_ be equal. It returns an `int`. +If two objects give different hash codes then its assumed that the result of `a.equals(b)` +will be `false`. If they give the same hash code, then the result of `a.equals(b)` might +be either `true` or `false`. + +Its sort-of like asking what the first letter of someone's name is. If their names start with different letters +they definitely have different names. If their names both start with `B` they _might_ both be named Bob, but maybe one is Bob and the other is Barry. + +```java +class Thing {} + +class Main { + void main() { + String a = "abc"; + String b = "abc"; + String c = "bca"; + + System.out.println(a.hashCode()); + // a.equals(b) will return true, so they will have the same hash code + System.out.println(b.hashCode()); + // a.equals(c) will return false, so they may or may not have the same hash code + System.out.println(c.hashCode()); + + Thing t1 = new Thing(); + Thing t2 = new Thing(); + + // The default .equals() is the same as == + System.out.println(t1.hashCode()); + System.out.println(t2.hashCode()); + } +} +``` diff --git a/src/objects/override_equals_and_hashCode.md b/src/objects/override_equals_and_hashCode.md index ed22994..63dc3c3 100644 --- a/src/objects/override_equals_and_hashCode.md +++ b/src/objects/override_equals_and_hashCode.md @@ -1,2 +1,139 @@ # Override equals and hashCode + +If you want to customize the `equals` method of an object the +general pattern for doing so is the following. + +First, declare an equals method that matches the one that comes from `Object`. +Don't forget `@Override`. + +```java,no_run +class Position { + int x; + int y; + + @Override + public boolean equals(Object o) { + + } +} +``` + +This definition of equals accepts any `Object` as an argument. Therefore we first +need to make sure that the class of that `Object` is the same as the class you are +comparing it to. + +```java,no_run +class Position { + int x; + int y; + + @Override + public boolean equals(Object o) { + if (o instanceof Position otherPosition) { + + } + else { + return false; + } + } +} +``` + +Then you compare the all the fields to make sure they are equal to each other as well. + +```java,no_run +class Position { + int x; + int y; + + @Override + public boolean equals(Object o) { + if (o instanceof Position otherPosition) { + return this.x == otherPosition.x && this.y == otherPosition.y; + } + else { + return false; + } + } +} +``` + +How you do those comparisons depends on what is in the fields. For `int` and such you can use `==`. For fields +like `String` you need to use `.equals`. + +```java,no_run +class Tree { + String barkDescription; + + @Override + public boolean equals(Object o) { + if (o instanceof Tree otherTree) { + // + return this.barkDescription.equals(otherTree.barkDescription); + } + else { + return false; + } + } +} +``` + +If you anticipate, or don't take actions to prevent, a field potentially being `null` +then instead of `a.equals(b)` use `java.util.Objects.equals`. All it does special is handle +`null` without crashing. + +```java,no_run +import java.util.Objects; + +class Tree { + String barkDescription; + + @Override + public boolean equals(Object o) { + if (o instanceof Tree otherTree) { + return Objects.equals(otherTree.barkDescription); + } + else { + return false; + } + } +} +``` + +Whenever you override `equals` you are supposed to override `hashCode` as well. +This is because - by default - every `Object` is going to give a `hashCode` consistent +with the default `equals` method. If you change how `equals` works, then you could +violate the contract of "if hashCode gives a different value, they definitely aren't equal." + +Some parts of Java will rely on that, so its worth addressing. + +If you define your `equals` method as above - essentially "they are equal if all their fields are equal" - then you can use `java.util.Objects.hash` to define your `hashCode`.[^exotic] + +```java,no_run +import java.util.Objects; + +class Position { + int x; + int y; + + @Override + public boolean equals(Object o) { + if (o instanceof Position otherPosition) { + return this.x == otherPosition.x && this.y == otherPosition.y; + } + else { + return false; + } + } + + @Override + public int hashCode() { + // Just give it all the fields you have. + return Objects.hash(this.x, this.y); + } +} +``` + +[^exotic]: If you defined a more exotic form of `equals` then how to properly make a `hashCode` is an exercise for you, the reader. If you give up then `return 1;` will always be "correct," if not ideal for the code +that uses `hashCode` as a quick "might be equal" check. diff --git a/src/packages/public_methods.md b/src/packages/public_methods.md index 9e24dcc..87c48f3 100644 --- a/src/packages/public_methods.md +++ b/src/packages/public_methods.md @@ -29,6 +29,7 @@ public class Well { You need this to be both public and static to be able to write Well.drawWater() """); + return 235; } } ``` diff --git a/src/packages/the_default_package.md b/src/packages/the_default_package.md index d4dcdce..ec5e05c 100644 --- a/src/packages/the_default_package.md +++ b/src/packages/the_default_package.md @@ -1,6 +1,6 @@ # The Default Package -When your classes don't have a package declaration at, we say those are in the "default package." +When your classes don't have a package declaration, we say those are in the "default package." ```java,no_run // No package declaration means default package diff --git a/src/recur b/src/recur new file mode 100644 index 0000000..9ceb45d --- /dev/null +++ b/src/recur @@ -0,0 +1 @@ +# Accumulators diff --git a/src/recursion/accumulators.md b/src/recursion/accumulators.md new file mode 100644 index 0000000..37cbe4b --- /dev/null +++ b/src/recursion/accumulators.md @@ -0,0 +1,66 @@ +# Accumulators + +A common thing to do with loops is to "accumulate" +some value each time you go through the loop. + +```java +class Main { + int timesTwo(int x) { + int total = 0; + while (x > 0) { + total += 2; + x--; + } + + return total; + } + + void main() { + System.out.println( + timesTwo(4) + ); + } +} +``` + +To accomplish the same task with recursion you need two things. + +The first is an extra "accumulator" argument to your function. +This is what you will update with the new value each time +you recurse. + +The second is an overload of your method which takes all the +same arguments minus that accumulator. This overload should +immediately delegate to the one that takes all the arguments +with some starting value for the accumulator. + +```java +class Main { + int timesTwo(int x, int accumulator) { + if (x > 0) { + return timesTwo(x - 1, accumulator + 2); + } + else { + return accumulator; + } + } + + int timesTwo(int x) { + timesTwo(x, 0); + } + + void main() { + System.out.println( + timesTwo(4) + ); + } +} +``` + +The reason we need to do this is because, unlike with a loop, it is difficult to share variables +across recursive calls. Adding an extra function parameter lets us have a place +to put what would otherwise be a local variable updated by a loop.[^naming] + +[^naming]: You may have noticed that the name changed from `total` to `accumulator`. There +is no particularly good reason for this other than to highlight that the total being built +up is an example of an "accumulator." \ No newline at end of file diff --git a/src/recursion/counting_down.md b/src/recursion/counting_down.md new file mode 100644 index 0000000..3fa7e1f --- /dev/null +++ b/src/recursion/counting_down.md @@ -0,0 +1,43 @@ +# Counting Down + +One example of a recursive function is one that counts down. + +You start by taking a number as an argument. If that number +is greater than 0 you "recurse" with a +number one lower than you were given. if it isn't you are done. + +```java +void countDown(int x) { + if (x > 0) { + System.out.println("DONE"); + } + else { + System.out.println("x: " + x); + countDown(x - 1); + } +} + +void main() { + countDown(5); +} +``` + +In this situation `x <= 0` is our base case since that is the situation where we don't +recurse. Each time we do recurse, we get closer to that base case. `x - 1` is always going +to be closer to x being less or equal to 0. + +All of this is equivalent to a `while` loop that looks like the following. + +```java +void countDown(int x) { + while (x > 0) { + System.out.println("x: " + x); + x = x - 1; + } + System.out.println("DONE"); +} + +void main() { + countDown(5); +} +``` diff --git a/src/recursion/recursing_over_arrays.md b/src/recursion/recursing_over_arrays.md new file mode 100644 index 0000000..f5b4949 --- /dev/null +++ b/src/recursion/recursing_over_arrays.md @@ -0,0 +1 @@ +# Recurse Over an Array diff --git a/src/recursion/recursing_over_strings.md b/src/recursion/recursing_over_strings.md new file mode 100644 index 0000000..7bad604 --- /dev/null +++ b/src/recursion/recursing_over_strings.md @@ -0,0 +1,5 @@ +# Recurse Over a String + +To write a recursive function which acts over each character +of a `String` you need to do much the same task as with regular loops, +just by having your "current index" be a parameter to the \ No newline at end of file diff --git a/src/reflection.md b/src/reflection.md new file mode 100644 index 0000000..e71e12a --- /dev/null +++ b/src/reflection.md @@ -0,0 +1,11 @@ +# Reflection + +Reflection is the what we call it when a program +uses information about how it - the program - is structured +in order to do things while running. + +We call it reflection because its as if your code +looks into a mirror and sees its own reflection.[^whenhumans] + +[^whenhumans]: When a human looks into a mirror they might see their own eye color. +If a mirror weren't there it would be difficult to know such things. \ No newline at end of file diff --git a/src/reflection/class_objects.md b/src/reflection/class_objects.md new file mode 100644 index 0000000..4ee12d3 --- /dev/null +++ b/src/reflection/class_objects.md @@ -0,0 +1,45 @@ +# Class Objects + +You can get an object representing a class in one of a few ways. + +The first is to write the name of the class followed by `.class`. +This works if you know exactly the class you want to reflect on. + +While this looks like accessing a field, it is technically its own +special thing. + +```java +class Main { + void main() { + Class stringClass = String.class; + System.out.println(stringClass); + } +} +``` + +Another is to call the `getClass` method on an object. This +is a method available on `java.lang.Object` and so will work for +anything. + +This is what you will use if you don't know up front exactly what +type of thing you will be reflecting over. + +```java +class Main { + void main() { + String s = "Hello"; + Class stringClass = s.getClass(); + System.out.println(stringClass); + } +} +``` + +These class objects have a generic parameter that can hold the class the +class object represents.[^ifconfused] When you don't know that information +up front you should use a wildcard. + + +[^ifconfused]: If that seems confusing and useless, you are half +right. It certainly is confusing, but it is pretty useful sometimes. +If its still beyond your ken just always write `Class` and we'll +loop back to it. \ No newline at end of file diff --git a/src/reflection/get_all_fields.md b/src/reflection/get_all_fields.md new file mode 100644 index 0000000..42063e6 --- /dev/null +++ b/src/reflection/get_all_fields.md @@ -0,0 +1,59 @@ +# Get all Fields + +If you have a class object, you can get all the public +fields of that class using `getFields`. This gives you +an array of field objects. + +```java +import java.lang.reflect.Field; + +class Main { + void main() { + Class drinkClass = Drink.class; + + Field[] fields = drinkClass.getFields(); + for (var field : fields) { + System.out.println(field.getName()); + } + } +} + +class Drink { + public String name; + public boolean caffeinated; +} +``` + +Its worth knowing that `getFields` will not list non-public fields. To +get a list of all fields, including private and package-private ones, +you need to use `getDeclaredFields`. + +```java +import java.lang.reflect.Field; + +class Main { + void main() { + Class soupClass = Soup.class; + + System.out.println("Using getFields"); + Field[] publicFields = soupClass.getFields(); + for (var field : publicFields) { + System.out.println(field.getName()); + } + + System.out.println("-------------"); + + System.out.println("Using getDeclaredFields"); + Field[] allFields = soupClass.getDeclaredFields(); + for (var field : allFields) { + System.out.println(field.getName()); + } + } +} + +class Soup { + public String name; + boolean isChicken; + private boolean hasVeggies; +} +``` \ No newline at end of file diff --git a/src/return_values/challenges.md b/src/return_values/challenges.md new file mode 100644 index 0000000..e33720d --- /dev/null +++ b/src/return_values/challenges.md @@ -0,0 +1,88 @@ +# 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 that takes a `String` as an argument and returns an `int` +as a result. + +How the value for that int is determined is up to you. + +```java,editable +// CODE HERE + +void main() { + int x = process("abc"); + System.out.println("Got " + x); +} +``` + +## Challenge 2. + +Define three methods such that the given main method will run. + +```java,editable +// CODE HERE + +void main() { + f(g(h(4), "b"), "e", "s"); +} +``` + +## Challenge 3. + +Make the following `multiply` method work for negative numbers. +Do this without simply multiplying using the `*` operator. + +```java,editable +int multiply(int x, int y) { + int total = 0; + for (int i = 0; i < y; i++) { + total += x; + } + return total; +} + +void main() { + System.out.println(multiply(3, 5)); + + // System.out.println(multiply(-5, 5)); + // System.out.println(multiply(-5, -2)); + // System.out.println(multiply(9, -2)); +} +``` + +## Challenge 4. + +Define a method, `subtractInt`, which makes the following +code run and produce the "correct" result. + +You will need to perform a narrowing conversion. + +```java,editable +// CODE HERE + +double add(double x, double y) { + return x + y; +} + +double multiply(double x, double y) { + return x * y; +} + +void main() { + int x = 5; + int y = 8; + int z = subtractInt(add(4, 5), mul(4, 2)); + + System.out.println(z); +} +``` + +## Challenge 5. + + diff --git a/src/standard_input/transporting_data.md b/src/standard_input/transporting_data.md index af13954..cb1748c 100644 --- a/src/standard_input/transporting_data.md +++ b/src/standard_input/transporting_data.md @@ -42,8 +42,9 @@ void main() { if (firstName.isBlank()) { System.out.println("First name cannot be blank."); } - - break; + else { + break; + } } while (true); String lastName; @@ -53,8 +54,9 @@ void main() { if (lastName.isBlank()) { System.out.println("First name cannot be blank."); } - - break; + else { + break; + } } while (true); System.out.println("Hello " + firstName + " " + lastName + "."); @@ -94,8 +96,9 @@ Person askForName() { if (firstName.isBlank()) { System.out.println("First name cannot be blank."); } - - break; + else { + break; + } } while (true); String lastName; @@ -105,8 +108,9 @@ Person askForName() { if (lastName.isBlank()) { System.out.println("First name cannot be blank."); } - - break; + else { + break; + } } while (true); return new Person(firstName, lastName); diff --git a/src/strings_ii/challenges.md b/src/strings_ii/challenges.md new file mode 100644 index 0000000..9cac1a8 --- /dev/null +++ b/src/strings_ii/challenges.md @@ -0,0 +1,113 @@ +# 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 `isUpperCase` which tells you if a +given `String` is already made of all upper-case characters. + +Hint: One way to do this is to call `.toUpperCase` and check +if the result is the same as the input you were given. + +```java,editable +boolean isUpperCase(String s) { + // ----------- + // CODE HERE + // ----------- +} + +void main() { + // true + System.out.println(isUpperCase("ABC")); + // false + System.out.println(isUpperCase("abc")); + // false + System.out.println(isUpperCase("AbC")); +} +``` + +## Challenge 2. + +Do the same as above, but check if a given `String` is +made of all lower case letters. + +```java,editable +boolean isLowerCase(String s) { + // ----------- + // CODE HERE + // ----------- +} + +void main() { + // false + System.out.println(isLowerCase("ABC")); + // true + System.out.println(isLowerCase("abc")); + // false + System.out.println(isLowerCase("AbC")); +} +``` + +## Challenge 3. + +Add an instance method named `scream` to the `Muppet` class. This +should replace the name of the muppet with the name in upper case +letters + an exclamation point (`!`) at the end. + +```java,editable +class Muppet { + String name; + + // ------------- + // CODE HERE + // ------------- +} + +void main() { + var kermit = new Muppet(); + kermit.name = "kermit"; + + // kermit + System.out.println(kermit.name); + + // KERMIT! + kermit.scream(); + System.out.println(kermit.name); +} +``` + +## Challenge 4. + +Write a method called `echo`. + +If `echo` is given a non-blank `String`, it should print `You Said: <...>` +where `<...>` is the `String` they +gave minus any leading or trailing whitespace. + +If `echo` is given a `blank` `String`, it should print `You Didn't Say Anything`. + +```java,editable +void echo(String s) { + // ------------- + // CODE HERE + // ------------- +} + +void main() { + // You Said: Hello + echo("Hello"); + + // You Said: Hello + echo(" Hello "); + + // You Didn't Say Anything + echo(""); + + // You Didn't Say Anything + echo(" "); +} +``` \ No newline at end of file diff --git a/src/switch/challenges.md b/src/switch/challenges.md new file mode 100644 index 0000000..d43bafb --- /dev/null +++ b/src/switch/challenges.md @@ -0,0 +1,217 @@ +# 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 `isSorcerer`. If the method is given +any of `"yuji"` or `"gojo"` return `true`. Otherwise return `false`. + +Start by writing this with `if` and `else`. Then alter the code so it uses +`switch` instead. + +```java +boolean isSorcerer(String name) { + // CODE HERE +} + +void main() { + System.out.println( + isSorcerer("yuji") + ); + + System.out.println( + isSorcerer("gojo") + ); + + System.out.println( + isSorcerer("yugi") // Wrong series + ); +} +``` + +## Challenge 2. + +Same basic challenge as above, but write a method named `didRedSoxWin` +which takes an `int` representing a year. + +Return `true` if that is a year the Boston Red Sox won a world series. +`false` otherwise. This time start off by using a switch. + +```java +boolean didRedSoxWin(int year) { + // CODE HERE +} + +void main() { + System.out.println( + "2004: " + didRedSoxWin(2004) + ); + + System.out.println( + "1998: " + didRedSoxWin(1998) + ); + + System.out.println( + "2013: " + didRedSoxWin(2013) + ); + + System.out.println( + "1903: " + didRedSoxWin(1903) + ); +} +``` + + +## Challenge 3. + +Make a method named `transition` which takes in a `StopLight` +and returns the next light it will transition to. + +For those who don't drive cars: red lights go to green, +green lights go to yellow, and yellow lights go to red. + +This is a duplicate of a challenge from a previous section. This time +do it using a `switch`. + +```java,editable +enum StopLight { + RED, + YELLOW, + GREEN +} + +StopLight transition(StopLight current) { + // ------------ + // CODE HERE + // ------------ +} + +void main() { + var light = StopLight.RED; + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); +} +``` + +## Challenge 4. + +In reality a `StopLight` can also be broken and not function +at all. Alter `transition` so it accounts for a new `BROKEN` +state which transitions to itself. (`BROKEN` goes to `BROKEN`). + +```java,editable +enum StopLight { + RED, + YELLOW, + GREEN, + BROKEN +} + +StopLight transition(StopLight current) { + // ------------ + // CODE HERE + // ------------ +} + +void main() { + var light = StopLight.RED; + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); + + light = transition(light); + System.out.println(light); +} +``` + +## Challenge 5. + +Given a type of bear, return the correct course of action +if you run into one in the wild and it attacks you. + +If the bear is `null`, return `null` as the action to take. + +If you don't know what to do when you run into a bear, look it up. Use `switch` +first then try writing the same logic using `if` and `else`. + +```java +enum Bear { + POLAR, + BROWN, + BLACK, + PANDA, + KOALA +} + +enum Action { + LAY_DOWN, + FIGHT_BACK, + RUN_AWAY + YEET +} + +Action inCaseOfBearAttack(Bear bear) { + // CODE HERE +} + +void main() { + System.out.println( + inCaseOfBearAttack(Bear.POLAR) + ); + + System.out.println( + inCaseOfBearAttack(Bear.BROWN) + ); + + + System.out.println( + inCaseOfBearAttack(Bear.BLACK) + ); + + System.out.println( + inCaseOfBearAttack(Bear.PANDA) + ); + + System.out.println( + inCaseOfBearAttack(Bear.KOALA) + ); + + System.out.println( + inCaseOfBearAttack(null) + ); +} +``` \ No newline at end of file diff --git a/src/tcp b/src/tcp new file mode 100644 index 0000000..94dd956 --- /dev/null +++ b/src/tcp @@ -0,0 +1 @@ +# Low Level versus High Level diff --git a/src/tcp_over_ip.md b/src/tcp_over_ip.md new file mode 100644 index 0000000..ca09049 --- /dev/null +++ b/src/tcp_over_ip.md @@ -0,0 +1,13 @@ +# TCP over IP + +Most of the internet is built on top of something called "TCP over IP" +or "Transmission Control Protocol over Internet Protocol." + +If those words don't make it immediately obvious what that means, thats normal. +The short version is that the "IP" part refers to how computers find eachother +on the internet and the "TCP" part refers to the how those computers +speak to eachother once they've found eachother. + +For the long version, I'll defer to this youtube video. + + diff --git a/src/tcp_over_ip/low_level_versus_high_level.md b/src/tcp_over_ip/low_level_versus_high_level.md new file mode 100644 index 0000000..011b1f0 --- /dev/null +++ b/src/tcp_over_ip/low_level_versus_high_level.md @@ -0,0 +1,19 @@ +# Low Level versus High Level + +TCP over IP, and by extension Sockets, are generally +considered to be a "low level" mechanism. + +When we say something is "low level" its usually in reference to +a "high level" thing that is built on top of the lower level thing. +In the case of TCP over IP, there are multiple protocols that use it +behind the scenes but don't generally require a programmer +to know some of the finer details of how that is used. + +We will get to a few of these eventually, but I bring this up +mostly to try and break the cultural taboo of using a low level +thing. There is a cultural bias in the programming field to prefer +higher levels mechanisms. This can sometimes be well-founded +but just as often be conditional on the situation. + +Which is all to say, if someone mocks you for using sockets directly +don't feel bad about yourself. We are building up slowly for a reason. \ No newline at end of file diff --git a/src/tcp_over_ip/sockets.md b/src/tcp_over_ip/sockets.md new file mode 100644 index 0000000..6b8d1b7 --- /dev/null +++ b/src/tcp_over_ip/sockets.md @@ -0,0 +1,7 @@ +# Sockets + +To communicate with a server using TCP over IP you use the `Socket` +class. + +// ---- +Th \ No newline at end of file diff --git a/src/time/duration.md b/src/time/duration.md index 5e7d0b7..c9e7eb9 100644 --- a/src/time/duration.md +++ b/src/time/duration.md @@ -1 +1,58 @@ # Duration + +A `Duration` stores a duration of time. + +You can make these with `Duration.ofMinutes`, `Duration.ofMillis` +and other similarly named methods. + +```java +import java.time.Duration; + +void main() { + var fiveMinutes = Duration.ofMinutes(5); + System.out.println(fiveMinutes); + + var twelveMilliSeconds = Duration.ofMillis(12); + System.out.println(twelveMilliSeconds); +} +``` + +You can use these get the duration between two `Instant`s with +`Duration.between`. + +```java +import java.time.Instant; +import java.time.Duration; + +void main() { + var january2nd = Instant.ofEpochMilli(86400000); + System.out.println(january2nd); + + var january3rd = Instant.ofEpochMilli(86400000 * 2); + System.out.println(january3rd); + + Duration twentyFourHours = Duration.between(january2nd, january3rd); + System.out.println(twentyFourHours); +} +``` + +And you can move `Instant`s by a given `Duration` of time using its `.plus` and `.minus` +methods. + +```java +import java.time.Instant; +import java.time.Duration; + +void main() { + var january1st = Instant.ofEpochMilli(0); + System.out.println(january1st); + + System.out.println( + january1st.plus(Duration.ofHours(45)) + ); + + System.out.println( + january1st.minus(Duration.ofHours(1)) + ); +} +``` diff --git a/src/time/instant.md b/src/time/instant.md index 96de49a..6421297 100644 --- a/src/time/instant.md +++ b/src/time/instant.md @@ -2,3 +2,28 @@ A `java.time.Instant` stores information on a particular moment in time. +You can get the current "instant" with `Instant.now`. + +```java +import java.time.Instant; + +void main() { + var now = Instant.now(); + System.out.println(now); +} +``` + +But if you happen to know a number milliseconds after January 1, 1970 UTC[^epoch] you +can get an `Instant` which represents that point in time with `Instant.ofEpochMilli`. + +```java +import java.time.Instant; + +void main() { + var january2nd = Instant.ofEpochMilli(86400000); + System.out.println(january2nd); +} +``` + +[^epoch]: https://en.wikipedia.org/wiki/Unix_time + diff --git a/src/time/local_date_time.md b/src/time/local_date_time.md index 5ae094f..2c5e886 100644 --- a/src/time/local_date_time.md +++ b/src/time/local_date_time.md @@ -12,9 +12,11 @@ import java.time.LocalDate; import java.time.LocalTime; import java.time.LocalDateTime; -void main() { - var jan10 = LocalDate.of(2024, 10, 1); - var tenTwentyFour = LocalTime.of(10, 24, 0); - System.out.println(LocalDateTime.of(jan10, tenTwentyFour)); +class Main { + void main() { + var jan10 = LocalDate.of(2024, 10, 1); + var tenTwentyFour = LocalTime.of(10, 24, 0); + System.out.println(LocalDateTime.of(jan10, tenTwentyFour)); + } } ``` \ No newline at end of file diff --git a/src/time/time_zones.md b/src/time/time_zones.md new file mode 100644 index 0000000..db59bef --- /dev/null +++ b/src/time/time_zones.md @@ -0,0 +1,64 @@ +# Time Zones + +Partially because of how day night cycles work +and sometimes because of arcane rules made up +to help out farmers, we have time zones. + +This means that while it might be 9am in Boston +it would simultaneously be 10pm in Tokyo. Boston and Tokyo +are in different time zones. + +You can get access to the time zone your computer +is using with `ZoneId.systemDefault()`. This gives you +a `ZoneId` which identifies a time zone. + +```java +import java.time.ZoneId; + +class Main { + void main() { + ZoneId tz = ZoneId.systemDefault(); + + System.out.println(tz); + } +} +``` + +If you want to get the identifier for a different time zone +you use `ZoneId.of` and give it a `String` identifying +the time zone. These come from [a big list](https://www.iana.org/time-zones) +published by the Internet Assigned Numbers Authority (IANA). + + +```java +import java.time.ZoneId; + +class Main { + void main() { + // Eastern Standard Time + ZoneId tz = ZoneId.of("US/Eastern"); + + System.out.println(tz); + } +} +``` + +While you won't be using this directly, every `ZoneId` +has the information needed to determine what time it would +be accessible via `getRules()`. + +```java +import java.time.ZoneId; + +class Main { + void main() { + // Eastern Standard Time + ZoneId tz = ZoneId.of("US/Eastern"); + + System.out.println(tz.getRules()); + } +} +``` + +And it is this machinery used by the parts of the time API which +determine the exact time it would be in a given time zone. diff --git a/src/time/zoned_date_time.md b/src/time/zoned_date_time.md index dd8f24f..58d5836 100644 --- a/src/time/zoned_date_time.md +++ b/src/time/zoned_date_time.md @@ -1 +1,59 @@ # ZonedDateTime + +A `ZonedDateTime` has all the information of +a `LocalDateTime`, but with the addition of a time zone. + +These are useful for recording the time that events took place +in a way that can be communicated. + +```java +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.time.ZoneId; + +class Main { + void main() { + var jan10 = LocalDate.of(2024, 10, 1); + var tenTwentyFour = LocalTime.of(10, 24, 0); + var est = ZoneId.of("US/Eastern"); + + LocalDateTime localDateTime = LocalDateTime.of(jan10, tenTwentyFour); + ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, est); + + System.out.println(zonedDateTime); + } +} +``` + +You can get the current `ZonedDateTime` for the time zone your computer is running in +with `ZonedDateTime.now()`. + +```java +import java.time.ZonedDateTime; + +class Main { + void main() { + var now = ZonedDateTime.now(); + + System.out.println(now); + } +} +``` + +And you can do the same for an arbitrary time zone by giving a `ZoneId` to +`now`. + +```java +import java.time.ZonedDateTime; +import java.time.ZoneId; + +class Main { + void main() { + var now = ZonedDateTime.now(ZoneId.of("US/Eastern")); + + System.out.println(now); + } +} +``` diff --git a/src/visibility/the_game.md b/src/visibility/the_game.md new file mode 100644 index 0000000..a3a82cb --- /dev/null +++ b/src/visibility/the_game.md @@ -0,0 +1,11 @@ +# The Game + +The only reason you would care about how visible a particular method or field is +if you were playing what I call "the game[^that]" + +In the game you take some unit of code, like a class, and pretend that someone is going to +try and do every possible thing that you can do with it. + +If you have methods named `stepOne()`, `stepTwo()` and `stepThree()` - what happens if someone calls them in the wrong order. If you have a field named `denominator` - what happens if someone sets it to zero. That sort of thing. + +[^that]: Not the game that you just lost by remembering you were playing the game. \ No newline at end of file