├── 1.1_Java_as_a_programming_language.md ├── 1.2._Variables,_input_and_output,_type_conversion,_and_arithmetic_operators.md ├── 1.3_Control_structures.md ├── 1.4_Arrays.md ├── 2.1_Class_object_constructor_instance_variable_method.md ├── 2.2_Collections.md ├── 2.3_Association.md ├── 3.1_Inheritance.md ├── 3.2_Interface.md ├── 3.3_StaticVarsAndMethods_Packages_Modifiers.md ├── 3.4_DataStreamsAndExceptions.md ├── 4_VCS_and_AI-assisted_coding.md ├── 5.1_Threads.md ├── 5.2_Synchronization.md ├── 6.1_Principles_of_GUI,_events,_MVC_model.md ├── 6.2_Layouts,_building_the_UI.md ├── 6.3_Graphics,_mouse_and_keyboard_events,_updating_the_GUI.md ├── 6.4_GUI_design_tools.md ├── 7.1_Preparing_the_database_and_the_connection.md ├── 7.2_Database_usage_via_JDBC_driver.md ├── 7.3_Basics_of_object-relational_mapping.md ├── 7.4_Association_in_object-relational_mapping.md ├── 8.1_UnitTesting.md ├── 8.2_TDD.md ├── 8.3_Lambda.md ├── 8.4_Functional.md ├── README.md └── images ├── AbstractArt.png ├── Amdahl.png ├── BWAbstraction.png ├── COLAbstraction.png ├── ClassVariable.png ├── DataInputStream.png ├── EnergyEfficiency.png ├── Exceptions.png ├── JUnitProjectStructure.png ├── JUnitTestResults.png ├── JavaCollection.png ├── MooreLaw.png ├── PersonHex.png ├── ProcessVsThread.png ├── StreamAPI.png ├── SynchronizationProblem.png ├── SysComplexity.png ├── VariableReadWrite.png ├── anchorpane.png ├── array.png ├── associated_classes.PNG ├── bmi_calculator.png ├── borderpane.png ├── calculator_ui.png ├── conflicts_dialog.png ├── conflicts_resolve.png ├── css.png ├── dice_structure.png ├── dice_ui.png ├── flightControlPackages.png ├── game.png ├── gb_mold.png ├── gingerbread.png ├── git_branch_selector.png ├── git_head.png ├── github_edit.png ├── gitmenu.png ├── gridpane.png ├── guiclicks.png ├── hbox.png ├── heidi_connection.png ├── heidisql.png ├── hello_world_window.png ├── hello_world_with_button.png ├── jpa_entities_uml.png ├── jpa_project_structure.PNG ├── nested.png ├── new_feature.png ├── one_branch.png ├── packageDemo.png ├── projectModule.png ├── push_alert.png ├── sb_gui.png ├── sb_hierarchy.png ├── scene_builder_newly_opened.png ├── source_and_bytecode.png ├── stackpane.png ├── structure_of_project.PNG ├── tilepane.png └── vbox.png /1.1_Java_as_a_programming_language.md: -------------------------------------------------------------------------------- 1 | # 1.1. Java as a programming language 2 | 3 | Welcome to learn Java at the 'Object-oriented programming with Java' course at Metropolia! 4 | 5 | This course is an introduction to the Java programming language. You will not only learn the basics of Java, but you also learn advanced programming techniques, such as object-oriented and functional programming. Moreover, you learn to develop graphical user interfaces (GUIs) with JavaFX, and write programs that use a database. This course lays the foundation for your future studies in software engineering. 6 | 7 | In this first module, and the four submodules within, we focus on the basics of Java programming. It is assumed that you have elementary programming skills in Python acquired in your first-year studies. Thus, we will not go through the basics of programming, but rather focus on learning how things are done in Java. 8 | 9 | At this point, let's begin with the classic in all programming courses: the "Hello World!" program: 10 | 11 | ```java 12 | public class HelloWorld { 13 | public static void main(String[] args) { 14 | System.out.println("Hello World!"); 15 | } 16 | } 17 | ``` 18 | 19 | As you see, Hello World is not printed with just one line of code as in Python. This reflects the fundamental difference between Java and Python: Python is a scripting language, which means that the syntax is simple and the code is interpreted and executed line by line. Java, on the other hand, is a compiled language, which means that the code is first compiled into a binary format, and then executed. 20 | 21 | The approach for Java means that the code is more verbose, but also that it is more efficient. (At this point, it is worth noting that Java programs are not really compiled into binary code, but into Java bytecode, which is then executed by the Java Virtual Machine (JVM). This is something we will address slightly later.) 22 | 23 | Partly because of the above compilability, Java programs tend to be more efficient than Python programs (see the figure below, Comparison of different programming languages, regarding energy efficiency, performance (Time) and program size (Mb), source [https://stratoflow.com/efficient-and-environment-friendly-programming-languages/](https://stratoflow.com/efficient-and-environment-friendly-programming-languages/)). In addition, modern, large data centres consume a lot of energy and cloud computing software has grown in size. Therefore, the energy efficiency of software is, unsurprisingly, becoming increasingly important. The Java language is doing quite well in terms of energy efficiency; compared to Python, energy efficiency is 38 times better. 24 | 25 | ![Energy Efficiency of various programming languages](./images/EnergyEfficiency.png) 26 | 27 | Java is a popular programming language first released by [Sun Microsystems](https://en.wikipedia.org/wiki/Sun_Microsystems) in 1995. Since then, it has undergone many updates and releases, each introducing new features, improvements, and bug fixes to the language. These versions are identified by a [unique number](https://en.wikipedia.org/wiki/Java_version_history), like Java 17. Each new version of Java typically brings enhancements in performance, security, and developer productivity, along with new language features and [API](https://en.wikipedia.org/wiki/API)s. [New Java versions](https://dev.java/evolution/) are released at six-month periods. Developers often choose the version of Java based on their project requirements, compatibility concerns, and the availability of support and updates. Additionally, the LTS versions provide longer-term support, which is preferred for certain enterprise applications. In our course, we prefer version Java 17. 28 | 29 | ## 1.1.1. Installing IntelliJ IDEA 30 | 31 | The first thing to do is to install an IDE (Integrated Development Environment) for Java. There are several alternatives, but we will use IntelliJ IDEA, which is a free and open-source IDE developed by JetBrains. You can download it from [https://www.jetbrains.com/idea/download/](https://www.jetbrains.com/idea/download/). 32 | 33 | You should feel immediately at home With IntelliJ IDEA if you have used PyCharm, which is also developed by JetBrains. If you have not used PyCharm, you will learn to use IntelliJ IDEA in no time. 34 | 35 | There are two distributions as download options: Ultimate and Community. As a Metropolia student, you get a free license for the Ultimate version, so you might want to sign in your JetBrains account and download that. You need to have a JetBrains account to get the license needed for the Ultimate version. (It is highly likely that you already have a JetBrains account if you took part in Metropolia's first year courses.) 36 | 37 | >If you don't have a licence yet, you can acquire one now. The installation program will guide you through this. Activation requires the creation of a JetBrains account and the acquisition of a student license. As a student, you can obtain a license that is valid for one year at a time for free by clicking on the shopping cart icon at the top of the JetBrains website and selecting **Special offers / For students and teachers**. Enter the form details using your Metropolia email address and finalize the activation according to the instructions you receive by email. When the license is about to expire in a year, you will automatically receive an email with instructions on how to renew it. 38 | 39 | ## 1.1.2. Running your first Java program 40 | 41 | Writing your first Java program is easy. Just follow these steps: 42 | 43 | 1. Open IntelliJ IDEA 44 | 2. Create a new project by selecting **File / New / Project...** from the menu 45 | 3. Select **Java** from the list on the left 46 | 4. Select **Java SDK 17** from the list on the right. (If the option is not available, you need to install Java 17 first. To do so, just click **Download JDK**, choose version 17 and follow the instructions.) 47 | 5. Click **Next** 48 | 6. Give your project a name, e.g. **HelloWorld** 49 | 7. Click **Finish** 50 | 8. Right-click on the **src** folder in the project view on the left 51 | 9. Select **New / Java Class** from the menu 52 | 10. Give your class a name, e.g. **HelloWorld** 53 | 11. Click **OK** 54 | 12. Copy the code snippet above into the editor window 55 | 13. Right-click on the editor window 56 | 14. Select **Run 'HelloWorld.main()'** from the menu 57 | 58 | You should now see the output window at the bottom of the screen displaying the text: 59 | 60 | ```text 61 | Hello World! 62 | ``` 63 | 64 | In step 4, you selected Java 17 as the SDK (Software Development Kit) for your project. The Java SDK is a collection of tools needed for developing Java programs. The most important of these tools is the Java compiler, which is used to compile Java source code into Java bytecode. The Java SDK also includes the Java Runtime Environment (JRE), which is necessary to run Java programs. The JRE includes the Java Virtual Machine (JVM), which is used to execute Java bytecode. The JVM is the reason why Java programs are platform-independent: the same bytecode can be executed on any platform that has a JVM. 65 | 66 | >The acronym JDK stands for Java Development Kit. It has roughly the same meaning as SDK, but is more specific to Java. 67 | 68 | >The choice of the SDK version is important. If you choose a version that is too old, you will not be able to use the latest features of Java. If you choose a version that is too new, you will not be able to run your programs on older systems. In this course, we will use Java 17, which is a recent LTS (Long-Term Support) version of Java. This means that it will be supported for a long time, and it is safe to use it in production. Specifically, JDK versions 8, 11, and 17, and 21 are LTS versions. For more information about JDK versions, look [this](https://en.wikipedia.org/wiki/Java_version_history). 69 | 70 | The bytecode is interpreted by the JVM, which means that the JVM reads the bytecode instructions and executes them. This is why Java is often referred to as an interpreted language. (To be specific, the JVM can also compile the bytecode into native machine code, which is then executed by the CPU. This is called just-in-time compilation (JIT), and it is used to improve the performance of Java programs.) 71 | 72 | The following image shows the phases of compiling and interpreting Java programs: 73 | 74 | ![Java compilation and interpretation](./images/source_and_bytecode.png) 75 | 76 | When you ran the Hello World application, the Java compiler compiled the source code into [bytecode](https://medium.com/swlh/an-introduction-to-jvm-bytecode-5ef3165fae70), which was then executed by the [JVM](https://en.wikipedia.org/wiki/Java_virtual_machine). The JVM is a virtual machine, which means that it is a software implementation of a computer that executes programs like a physical machine. 77 | 78 | You can think of the bytecode as the 'machine code' of the JVM. The Java virtual machine (which is an application in spite of its name) reads these bytecode instructions and interprets them to instructions understood by your system. This is why Java is platform-independent: the same bytecode can be executed on any platform that has a JVM. You can look at the bytecode generated from the source file in IntelliJ IDEA using the menu command `View-Show Bytecode`. 79 | 80 | Thus, a lot happens behind the scenes when you run a Java program. As a developer, you do not generally need to worry about this: you just write your code, and the IDE takes care of the rest. 81 | 82 | 83 | ## 1.1.3. Java basic syntax 84 | 85 | Before writing new programs, let's study the Hello World example in more detail. 86 | 87 | The first line of the program is: 88 | 89 | ```java 90 | public class HelloWorld { 91 | ``` 92 | 93 | That line defines a class named `HelloWorld`. The class is defined as public, which means that it can be accessed from outside the class. In Java, all code (or almost all, to be specific) is written inside classes. Thus, the `HelloWorld` class contains the entire program. It is not possible to just write the print statement without defining a class first in a way similar to Python. 94 | 95 | > By convention, class names start with a capital letter. This is not required by the compiler, but it is a good practice to follow. If the class name consists of several words, the words are written in camel case where the first letter of each word is capitalized. 96 | 97 | Inside the class, there is a method definition: 98 | 99 | ```java 100 | public static void main(String[] args) { 101 | ``` 102 | 103 | The functionality of each class is written inside methods. The `main` method is the entry point of the program. When you run a Java program, the JVM looks for the `main` method and executes it. 104 | 105 | The `main` method is defined as `public`, which means that it can be accessed from outside the class. The `main` method is also defined as `static`, which means that it can be accessed without creating an instance (that is, an object) of the class. 106 | 107 | The main method is also defined as `void`, which means that it does not return any value. The `main` method takes one parameter, which is an array of strings. The name of the parameter is by convention `args`, but it could be anything. The parameter is not used in the Hello World program, but it is needed because the main method is defined to take one parameter. The purpose of this `args` parameter is to pass command-line arguments to the program. In most cases, you do not need to worry about this parameter. 108 | 109 | > Method names are by convention written in camel case where the first letter is lowercase. If the method name consists of several words, the words are written in camel case where the first letter of each word is capitalized. In this example, we have no real choice for the method name, as the name of the method that is the entry point of the program must be `main`. 110 | 111 | 112 | The program continues with the following line: 113 | 114 | ```java 115 | System.out.println("Hello World!"); 116 | ``` 117 | 118 | This is the line that prints the text "Hello World!" to the console. The `System.out.println` method is defined in the `System` class, which is part of the `java.lang` package. The `System.out.println` method takes one parameter, which is a string. The `println` method prints the string to the console and adds a newline character at the end. 119 | 120 | > You can write the `System.out.println` using the `sout` abbreviation in IntelliJ IDEA. Just type `sout` and press the `Tab` key, and the `System.out.println` statement is written for you. This is called as Live Template in IntelliJ IDEA. There are other live templates available, and you can also create your own live templates, see `File-Settings-Editor-Live Templates`. 121 | 122 | 123 | The line above ends with a semicolon `;`. In Java, all statements must end with a semicolon. This is different from Python, where the end of a line also marks the end of a statement. It is a common mistake to forget the semicolon at the end of a statement. If you do so, don't worry: the compiler will promptly give you an error message and tell you to add the semicolon. 124 | 125 | After the `println` statement, there are no more statements in the `main` method, so the program ends there. 126 | 127 | There is something else in the program that deserves attention, though. The curly braces `{` and `}` are used to define blocks of code. The `main` method is defined inside the `HelloWorld` class, so the `main` method is a block of code inside the `HelloWorld` class. The `println` statement is a block of code inside the `main` method. The curly braces are used to define the beginning and the end of each block of code. This is quite different from Python, where indentation is used to define blocks of code. 128 | 129 | > Before, we mentioned that each statement ends with a semicolon. On the other hand, the blocks of code, denoted by curly braces, do not end with a semicolon. Adding the extra semicolon is a common mistake, so be careful not to add semicolons after curly braces. In some cases, an extra semicolon after a closing curly brace will not affect the execution of the program or make it syntactically erroneous, but it is still a style violation. 130 | 131 | 132 | > Technically, the Java compiler doesn't care about indentation. However, indentation is very important for readability, so you should always indent your code properly. All professional Java developers do so. In IntelliJ IDEA, you can use the **Code / Reformat Code** command to automatically indent your code. That is possible, since the IDE knows the structure of the code based on the curly braces. 133 | 134 | In the Java source code, there are two types for commenting the code: single-line comments and multi-line comments. Single-line comments start with `//` and continue until the end of the line. Multi-line comments start with `/*` and end with `*/`. 135 | 136 | In a single-line comment, each row of the comment starts with `//`: 137 | 138 | ```java 139 | // This is a single-line comment 140 | ``` 141 | 142 | The start and end symbols of a multi-line comments provide an easy way to write a longer comment: 143 | 144 | ```java 145 | /* 146 | This is a multi-line comment. 147 | It can span multiple lines. 148 | */ 149 | ``` 150 | 151 | When the code is compiled into bytecode, the comments are removed. Thus, the comments are not visible in the bytecode, and they do not affect the execution of the program. The purpose of comments is to make the code more readable for humans. 152 | 153 | 154 | ## 1.1.4. Using GitHub with IntelliJ IDEA 155 | 156 | Just like in the first-year studies, you will use GitHub to store your code. GitHub is a web-based hosting service for version control using Git. Git is a distributed version control system for tracking changes in source code during software development. It is designed for coordinating work among programmers, but it can be used to track changes in any set of files. GitHub is the most popular Git hosting service, and it is used by many companies and open-source projects. 157 | 158 | In IntelliJ IDEA, you can create a new repository on GitHub based on your project. You can also commit and push your changes to GitHub directly from the IDE. To do so, follow these steps: 159 | 160 | 1. Open the project you want to store on GitHub 161 | 2. Select **VCS / Share Project on GitHub** from the menu 162 | 3. Enter your GitHub username and password if the system so prompts 163 | 4. Click **Share** 164 | 5. Enter a name for the repository 165 | 6. Click **OK** 166 | 167 | The project is now stored on GitHub. 168 | 169 | The **VCS** menu in the menu bar is replaced by the **Git** menu when you login into GitHub via IntelliJ Idea. The **Git** menu contains all the commands you need for working with Git repositories. See the image below. 170 | 171 | ![The GIT menu in IntelliJ IDEA](images/gitmenu.png) 172 | 173 | The basic workflow with a GitHub repository involves pulling, committing, and pushing. 174 | - Pulling means downloading the latest version of the repository from GitHub. You should pull always when you start your working session. 175 | - Committing means saving your changes to the local repository. You should commit each time you have written or modified your code, and it compiles without errors. 176 | - Pushing means uploading your changes to GitHub. You should push whenever you finish your working session. You should also push before you start your working session, in case you have forgotten to push earlier. 177 | 178 | To pull the latest version of the repository from GitHub, select **Git / Repository / Pull** from the menu. To commit your changes to the local repository, select **Git / Commit** from the menu. To push your changes to GitHub, select **Git / Repository / Push** from the menu. 179 | 180 | Remember to add a good description of your changes when you commit them. This will help you and others to understand what the changes are about. 181 | 182 | At this point, you should be able to write your first Java program, run it, and save your versions to GitHub. 183 | 184 | ## 1.1.5. One repo with multiple modules with IntelliJ IDEA 185 | 186 | This course contains multiple independent tasks. It would be nice if we could have only one GitHub repository for all these tasks. This is possible with the IntelliJ IDEA module concept. You can think the module as a separate Java program. First, you should have a project. Then, when you start a new Java application or task, create a module for it. Right-click the project name, then select `new` menu command, then select `module`. See the picture below. 187 | 188 | ![The project menu in IntelliJ IDEA](images/projectModule.png) 189 | 190 | In the picture, we have a project named `OO-CourseMaterialCodes`. This project contains multiple modules (folder symbol with blue square symbol on it), e.q., `AssosiationDemo`, `CollectionsDemo`, etc. Now you are able to put this project with multiple separete Java applications to a one repo. 191 | 192 | ## Assignment: Writing your first Java programs 193 | 194 | It is time to start writing Java programs. The main goal is to have the programming environment up and running, so the assignments are very simple. 195 | 196 | **Task 1** 197 | 198 | Install the environment, create a new project, and write a simple program that greets the user with a fixed message. Run the program and make sure that it works. Commit your changes to GitHub. 199 | 200 | **Task 2** 201 | 202 | Write a program that prints the following: 203 | 204 | ``` 205 | * 206 | *** 207 | ***** 208 | ******* 209 | 210 | ``` 211 | 212 | The program should print the exact number of spaces and asterisks shown above. Commit your changes to GitHub. 213 | 214 | 215 | **Task 3** 216 | 217 | Run the following Java program that asks the user three integers and prints their sum. 218 | 219 | ```java 220 | import java.util.Scanner; 221 | 222 | public class SumOfThreeNumbers { 223 | 224 | public static void main(String[] args) { 225 | Scanner scanner = new Scanner(System.in); 226 | 227 | System.out.println("Give the first number:"); 228 | int first = Integer.parseInt(scanner.nextLine()); 229 | 230 | System.out.println("Give the second number:"); 231 | int second = Integer.parseInt(scanner.nextLine()); 232 | 233 | System.out.println("Give the third number:"); 234 | int third = Integer.parseInt(scanner.nextLine()); 235 | 236 | System.out.println("The sum of the numbers is " + (first + second + third)); 237 | } 238 | } 239 | ``` 240 | 241 | Then, based on your previous programming experience, modify the program so that the program also prints the product and average of the three integers. **Hint:** try the modified program with integers 3, 4, and 6. The sum should be 13, the product 72, and the average 4.3333333333333. If the average is something else, find out why and modify the program accordingly. Add the explanation as a comment to the program. Commit your changes to GitHub. 242 | 243 | 244 | **Task 4** 245 | 246 | Here is a simple object-oriented program: 247 | 248 | ```java 249 | public class Cat { 250 | private String name; 251 | 252 | public Cat(String name) { 253 | this.name = name; 254 | } 255 | 256 | public void meow() { 257 | System.out.println("The cat named " + name + " says: Meow!"); 258 | } 259 | 260 | public static void main(String[] args) { 261 | // Create an instance of the Cat class with a name 262 | Cat cat = new Cat("Whiskers"); 263 | 264 | // Call the meow method on the cat instance 265 | cat.meow(); 266 | } 267 | } 268 | ``` 269 | 270 | 271 | Run the program. Then, based on your previous programming experience, modify the `main` method so that two cat objects are created, and the program behaves in the following way: 272 | 273 | ``` 274 | The cat named Whiskers says: Meow! 275 | The cat named Whiskers says: Meow! 276 | The cat named Rex says: Meow! 277 | The cat named Whiskers says: Meow! 278 | ``` 279 | 280 | Commit your changes to GitHub. 281 | 282 | 283 | For this assignment, you get points in the following way: 284 | 1. Task 1 completed, the code runs successfully: 1 point 285 | 2. Task 2 completed, the output is exactly as it should be: 1 point 286 | 3. Task 3 completed, the code is correctly modified and contains the explanation: 2 points 287 | 4. Task 4 completed, the code is correctly modified: 2 points 288 | 289 | --- 290 | _This learning material has been produced with assistance from OpenAI's ChatGPT-4 and GitHub Copilot. These large language models have provided suggestions and solutions that have assisted the author in producing and supplementing the material. While their contribution has been significant, the final responsibility for the content and its correctness resides with the author._ 291 | 292 | -------------------------------------------------------------------------------- /1.3_Control_structures.md: -------------------------------------------------------------------------------- 1 | # 1.3. Control structures 2 | 3 | The control structures make it possible to control the flow of the program. These include the conditional statements and loops. 4 | 5 | In Java, the control structures are the same as in JavaScript, which in turn are the same as in C and C++. The syntax is also the same as in C and C++. 6 | 7 | In this subsection we review the control structures in Java. For simplicity, we use simple examples where the entire program logic is in the main method. (In the next module, we learn how to split the program logic into separate classes and methods in an object-oriented way.) 8 | 9 | ## 1.3.1. Conditional statements 10 | 11 | The conditional statements in Java are: 12 | 13 | * `if` 14 | * `if-else` 15 | * `if-else if-else` 16 | * `switch/case` 17 | 18 | 19 | ### if 20 | 21 | The `if` statement splits the flow of control into two alternative paths. 22 | 23 | The following example asks the user for a number. If the number is greater than zero, the program prints the message "The number is positive". 24 | 25 | ```java 26 | import java.util.Scanner; 27 | 28 | public class PositiveNumber { 29 | 30 | public static void main(String[] args) { 31 | Scanner scanner = new Scanner(System.in); 32 | 33 | System.out.println("Give a number:"); 34 | int number = Integer.parseInt(scanner.nextLine()); 35 | 36 | if (number > 0) { 37 | System.out.println("The number is positive."); 38 | } 39 | } 40 | } 41 | ``` 42 | 43 | The condition in the `if` statement is always a logical expression. In the example above, the condition is `number > 0`. If the condition is true, the program executes the code block that follows the `if` statement. If the condition is false, the program does not execute the code block. 44 | 45 | A logical expression is an expression that evaluates to a boolean value (`true` or `false`). A complex logical expression can be built from simpler logical expressions using logical operators. The most common logical operators in Java are: 46 | 47 | * `==` - equal to 48 | * `!=` - not equal to 49 | * `>` - greater than 50 | * `>=` - greater than or equal to 51 | * `<` - less than 52 | * `<=` - less than or equal to 53 | * `!` - logical NOT 54 | * `&&` - logical AND 55 | * `||` - logical OR 56 | 57 | The order (i.e., operator [precedence](https://introcs.cs.princeton.edu/java/11precedence/)) in which the operators are evaluated is the same as in Python. The comparison operators are applied first. After that, the logical NOT operator has the highest precedence, followed by the logical AND operator, and, after that, the logical OR operator. The order of evaluation can be changed by using parentheses, which have the highest precedence of all. 58 | 59 | An example of an `if` statement with a complex condition is below: 60 | 61 | ```java 62 | if ((number > 0 && number < 10) || number==-1) { 63 | System.out.println("The number is between 1 and 9, or it is -1."); 64 | } 65 | ``` 66 | 67 | In the previous example, the inner parentheses are not necessary as the `&&` operator has higher precedence than the `||` operator. However, the parentheses make the code easier to read. 68 | 69 | ### if-else 70 | 71 | The `if-else` statement splits the flow of control into two alternative paths. The `else` statement is executed if the condition of the `if` statement is false. 72 | 73 | In the following example, the user is asked for a number. If the number is greater than zero, the program prints the message "The number is positive". Otherwise, the program prints the message "The number is not positive". 74 | 75 | ```java 76 | import java.util.Scanner; 77 | 78 | public class PositiveNumber { 79 | 80 | public static void main(String[] args) { 81 | Scanner scanner = new Scanner(System.in); 82 | 83 | System.out.println("Give a number:"); 84 | int number = Integer.parseInt(scanner.nextLine()); 85 | 86 | if (number > 0) { 87 | System.out.println("The number is positive."); 88 | } 89 | else { 90 | System.out.println("The number is not positive."); 91 | } 92 | } 93 | } 94 | ``` 95 | 96 | In Java, the condition of the `if` statement must be a boolean value. If the condition is true, the program executes the code block that follows the `if` statement. If the condition is false, the program executes the code block that follows the `else` statement. The condition should be placed in parentheses. 97 | 98 | ### if-else if-else 99 | 100 | The `if-else if-else` statement splits the flow of control into several alternative paths. The `else if` statement is executed if the condition of the `if` statement is false. The `else` statement is executed if all the previous conditions are false. 101 | 102 | Continuing the previous example, the following program asks the user for a number. If the number is greater than zero, the program prints the message "The number is positive". If the number is less than zero, the program prints the message "The number is negative". Otherwise, the program prints the message "The number is zero". 103 | 104 | ```java 105 | import java.util.Scanner; 106 | 107 | public class PositiveNumber { 108 | 109 | public static void main(String[] args) { 110 | Scanner scanner = new Scanner(System.in); 111 | 112 | System.out.println("Give a number:"); 113 | int number = Integer.parseInt(scanner.nextLine()); 114 | 115 | if (number > 0) { 116 | System.out.println("The number is positive."); 117 | } 118 | else if (number < 0) { 119 | System.out.println("The number is negative."); 120 | } 121 | else { 122 | System.out.println("The number is zero."); 123 | } 124 | } 125 | } 126 | ``` 127 | 128 | In the example, there was one `else-if` statement. There can be any number of `else-if` statements. The `else` statement is optional. 129 | 130 | ### switch/case 131 | 132 | The purpose of the `switch/case` statement is to select one of several alternatives. The `switch/case` statement is an alternative to the `if-else if-else` statement. The main difference is that the value of the variable is compared to the values of the `case` statements. 133 | 134 | An example of the `switch/case` statement is the following program that asks the user for a number. If the number is 1, the program prints the message "The number is one". If the number is 2, the program prints the message "The number is two". Otherwise, the program prints the message "The number is neither one nor two". 135 | 136 | ```java 137 | import java.util.Scanner; 138 | 139 | public class OneOrTwo { 140 | 141 | public static void main(String[] args) { 142 | Scanner scanner = new Scanner(System.in); 143 | 144 | System.out.println("Give a number:"); 145 | int number = Integer.parseInt(scanner.nextLine()); 146 | 147 | switch (number) { 148 | case 1: 149 | System.out.println("The number is one."); 150 | break; 151 | case 2: 152 | System.out.println("The number is two."); 153 | break; 154 | default: 155 | System.out.println("The number is neither one nor two."); 156 | break; 157 | } 158 | } 159 | } 160 | ``` 161 | 162 | In the example above, each branch ends in a `break` statement. The `break` statement ends the execution of the `switch/case` statement. If the `break` statement is omitted, the execution continues to the next branch. If we omitted all the `break` statements, and the value of the variable `number`was 1, the program would print the following: 163 | 164 | ```plaintext 165 | The number is one. 166 | The number is two. 167 | The number is neither one nor two. 168 | ``` 169 | 170 | This would make little sense. Therefore, we should always remember to add the `break` statements. 171 | 172 | There are, however, cases where the `break` statement is deliberately omitted. The following program asks the user for a character. Then the program notifies the user if the letter is a Scandinavian letter, after which it lets the user know if the character is a vowel or not. (Note that all Scandinavian characters are vowels, too.) 173 | 174 | ```java 175 | import java.util.Scanner; 176 | 177 | public class VowelOrNot { 178 | 179 | public static void main(String[] args) { 180 | Scanner scanner = new Scanner(System.in); 181 | 182 | System.out.println("Give a character:"); 183 | char character = scanner.nextLine().toLowerCase().charAt(0); 184 | 185 | switch (character) { 186 | case 'å': 187 | case 'ä': 188 | case 'ö': 189 | System.out.println("The character is a Scandinavian character."); 190 | case 'a': 191 | case 'e': 192 | case 'i': 193 | case 'o': 194 | case 'u': 195 | System.out.println("The character is a vowel."); 196 | break; 197 | default: 198 | System.out.println("The character is not a vowel."); 199 | break; 200 | } 201 | } 202 | } 203 | ``` 204 | 205 | Let's run the program and enter letter *e* as input. The output is as follows: 206 | 207 | ```plaintext 208 | Give a character: 209 | e 210 | The character is a vowel. 211 | ``` 212 | 213 | Next, we rerun the program and enter the Scandinavian letter *ä*: 214 | 215 | ```plaintext 216 | Give a character: 217 | ä 218 | The character is a Scandinavian character. 219 | The character is a vowel. 220 | ``` 221 | 222 | ## 1.3.2. Loops 223 | 224 | Loops make it possible to repeat a block of code as long as a certain condition is met. 225 | 226 | There are four types of loops in Java: `while`, `do-while`, `for`, and `for-each`. The `while` loop is the most basic loop. The `for` loop is used when the number of iterations is known. The `for-each` loop is used to iterate over the elements of an array or a collection. 227 | 228 | In addition, there is the `for/each` loop, which is used to iterate over the elements of an array or a collection. We will discuss the `for/each` loop later, in conjunction with arrays. 229 | 230 | ### while 231 | 232 | The `while` loop is used to repeat a block of code as long as a condition is true. The `while` loop is useful when we do not know in advance how many times the block of code needs to be repeated. 233 | 234 | The following program asks the user for a number. If the number is greater than zero, the program prints the message "The number is positive". Otherwise, the program prints the message "The number is not positive". The program repeats this until the user enters the number 0. 235 | 236 | ```java 237 | import java.util.Scanner; 238 | 239 | public class PositiveNumber { 240 | 241 | public static void main(String[] args) { 242 | Scanner scanner = new Scanner(System.in); 243 | System.out.println("Give a number:"); 244 | int number = Integer.parseInt(scanner.nextLine()); 245 | 246 | while (number != 0) { 247 | if (number > 0) { 248 | System.out.println("The number is positive."); 249 | } 250 | else if (number < 0) { 251 | System.out.println("The number is not positive."); 252 | } 253 | System.out.println("Give another number:"); 254 | number = Integer.parseInt(scanner.nextLine()); 255 | } 256 | } 257 | } 258 | ``` 259 | 260 | Let's run the program and enter numbers -1, 5, and 0 as input. The output is as follows: 261 | 262 | ```plaintext 263 | Give a number: 264 | -1 265 | The number is not positive. 266 | Give another number: 267 | 5 268 | The number is positive. 269 | Give another number: 270 | 0 271 | ``` 272 | 273 | 274 | ### do-while 275 | 276 | The following program should behave identically to the previous program: it asks the user for a number, and prints either the text "The number is positive", or the text "The number is not positive". The program terminates when the user enters the number 0. 277 | 278 | ```java 279 | import java.util.Scanner; 280 | 281 | public class PositiveNumber { 282 | 283 | public static void main(String[] args) { 284 | Scanner scanner = new Scanner(System.in); 285 | int number; 286 | 287 | do { 288 | System.out.println("Give a number:"); 289 | number = Integer.parseInt(scanner.nextLine()); 290 | 291 | if (number > 0) { 292 | System.out.println("The number is positive."); 293 | } 294 | else if (number < 0) { 295 | System.out.println("The number is not positive."); 296 | } 297 | } while (number != 0); 298 | } 299 | } 300 | ``` 301 | 302 | Let's run the program and enter numbers 7, -2, and 0 as input. The output is as follows: 303 | 304 | ```plaintext 305 | Give a number: 306 | 7 307 | The number is positive. 308 | Give a number: 309 | -2 310 | The number is not positive. 311 | Give a number: 312 | 0 313 | ``` 314 | 315 | The difference between the `while` and `do-while` loops is that the `do-while` loop always executes the code block at least once. The `while` loop, on the other hand, does not execute the code block at all if the condition is not true. 316 | 317 | ### for 318 | 319 | The `for` loop is used to repeat a block of code a known number of times. The `for` loop is useful when we know in advance how many times the block of code needs to be repeated. 320 | 321 | The following program prints the numbers 1 to 10: 322 | 323 | ```java 324 | public class FromOneToTen { 325 | 326 | public static void main(String[] args) { 327 | for (int i = 1; i <= 10; i++) { 328 | System.out.println(i); 329 | } 330 | } 331 | } 332 | ``` 333 | 334 | Technically, the `for` loop consists of three parts: the initialization, the condition, and the increment/decrement. 335 | - The initialization is executed once when the loop starts. 336 | - The condition is checked before each iteration. If the condition is true, the code block is executed. 337 | - After the code block has been executed, the increment/decrement is executed, and the condition is checked again. 338 | 339 | The following program prints the numbers 10 to 1: 340 | 341 | ```java 342 | public class FromTenToOne { 343 | 344 | public static void main(String[] args) { 345 | for (int i = 10; i > 0; i--) { 346 | System.out.println(i); 347 | } 348 | } 349 | } 350 | ``` 351 | 352 | The loop variable `i` is not available after the loop. If you want to utilize it after the loop, declaration of the loop variable should be in the outer block: 353 | ```java 354 | public class FromOneToEleven { 355 | 356 | public static void main(String[] args) { 357 | int i; 358 | 359 | for (i = 1; i <= 10; i++) { 360 | System.out.println(i); 361 | } 362 | System.out.println(i); 363 | } 364 | } 365 | ``` 366 | 367 | ## Nested control structures 368 | 369 | Control structures can be nested inside each other. For example, we can place a `while` loop inside a `for` loop, or a `for` loop inside a `while` loop. We can also place a `for` loop inside another `for` loop, and there can be and `if-else` statement inside a `for` loop, and so on. 370 | 371 | In the following example, we compute all the products of two integers where each integer is in the range 1 to 5. For each product, the program prints whether it is even or odd. 372 | 373 | ```java 374 | 375 | public class NestedLoops { 376 | 377 | public static void main(String[] args) { 378 | for (int i = 1; i <= 5; i++) { 379 | for (int j = 1; j <= 5; j++) { 380 | int product = i * j; 381 | System.out.print(i + "*" + j + "=" + product + " "); 382 | if (product % 2 == 0) { 383 | System.out.println("even"); 384 | } else { 385 | System.out.println("odd"); 386 | } 387 | } 388 | } 389 | } 390 | } 391 | ``` 392 | 393 | We run the program and the output is as follows: 394 | 395 | ```plaintext 396 | 1*1=1 odd 397 | 1*2=2 even 398 | 1*3=3 odd 399 | 1*4=4 even 400 | 1*5=5 odd 401 | 2*1=2 even 402 | 2*2=4 even 403 | ... 404 | 5*3=15 odd 405 | 5*4=20 even 406 | 5*5=25 odd 407 | ``` 408 | 409 | This is an example of nesting control structures. It is a common yet powerful technique that makes it possible to implement any program logic. 410 | 411 | 412 | 413 | ## Assignments: 414 | 415 | **Task 1: Quadratic equation solver** 416 | 417 | Write a program that prompts the user to enter the coefficients of a quadratic equation (ax^2 + bx + c = 0) and calculates its roots using the quadratic formula. Display the roots on the console. If the equation has no real roots, display the message "No real roots". 418 | 419 | 420 | **Task 2: Binary values** 421 | 422 | Write a program that prompts the user to enter a binary number (composed of 0s and 1s) and converts it to decimal. Display the decimal equivalent on the console. **Hint**: use the `charAt` method of the `String` class to retrieve the individual bits in the input string. 423 | 424 | 425 | **Task 3: Prime number generator** 426 | 427 | Write a program that prompts the user to enter two positive integers, start and end (where start < end). The program should generate and display all the prime numbers between start and end, inclusive. 428 | 429 | A prime number is a positive integer greater than 1 that has no positive divisors other than 1 and itself. 430 | 431 | Your program should use control structures (such as loops and conditional statements) to implement the logic for generating and checking prime numbers. 432 | 433 | **Task 4: Multiplication table exam** 434 | 435 | In primary school, students must memorize the multiplication tables for numbers 1 to 10. Create a program to assist primary school students that presents the user with ten randomly generated multiplication problems, where the factors are integers between one and ten. After each answer, the program indicates whether it was correct or not. The user receives one point for each correctly answered question. If the user scores ten points for the entire set of problems, the program congratulates them on mastering the multiplication tables and terminates. Otherwise, the program starts a new set of problems. 436 | 437 | For this assignment, you get points in the following way: 438 | 439 | 1. Task 1 completed: 1 point 440 | 2. Task 2 completed: 1 point 441 | 3. Task 3 completed: 2 points 442 | 4. Task 4 completed: 2 points 443 | 444 | --- 445 | _This learning material has been produced with assistance from OpenAI's ChatGPT-4 and GitHub Copilot. These large language models have provided suggestions and solutions that have assisted the author in producing and supplementing the material. While their contribution has been significant, the final responsibility for the content and its correctness resides with the author._ 446 | -------------------------------------------------------------------------------- /1.4_Arrays.md: -------------------------------------------------------------------------------- 1 | # 1.4. Arrays 2 | 3 | In Java, an array is a container object that holds a fixed number of values of a single type. Each element in an array is associated with an index, which is a number that represents the element's position in the array. The first element in an array has index 0, the second element has index 1, and so on. 4 | 5 | The following image illustrates the concept of an array: 6 | 7 | ![Array](images/array.png) 8 | 9 | The array in the example contains seven elements of integer type. 10 | We can specify the element in an array by using the array name and the index of the element. For instance, we can say that the value of the element with index 3 in the `numbers` array is 6. 11 | 12 | Indices are like numbers in doors of hotel rooms: once you know the number, you gain access to the correct content. 13 | 14 | Arrays are static data structures, meaning that the size of an array is fixed once it is created. As a consequence, the JVM can reserve a contiguous block of memory for the array. This makes accessing the elements of an array very efficient, as the exact memory location of each element can be calculated based on the starting address of the array and the index of the element. The downside is that the size of an array cannot be changed after it has been created. If you ever need to expand an array, you must create a new array and copy the elements from the old array to the new array. 15 | 16 | The size of an array, although fixed, can be determined at runtime. This means that you can ask the user to specify the size of an array. 17 | 18 | The elements in an array may be of any elementary type, or they may be references to objects of any class. All elements in an array must, however, share the same type. Thus, you cannot create an array that contains both integers and strings. 19 | 20 | 21 | ## 1.4.1. Declaring and creating arrays 22 | 23 | The following program asks the user for the number of integers, creates an array of the specified size, and then asks the user to enter the integers. Finally, the program prints the integers in reverse order. 24 | 25 | ```java 26 | import java.util.Scanner; 27 | 28 | public class ArrayExample { 29 | public static void main(String[] args) { 30 | Scanner scanner = new Scanner(System.in); 31 | 32 | System.out.print("How many integers? "); 33 | int size = scanner.nextInt(); 34 | 35 | int[] numbers = new int[size]; 36 | 37 | System.out.println("Enter the integers:"); 38 | 39 | for (int i = 0; i < size; i++) { 40 | numbers[i] = scanner.nextInt(); 41 | } 42 | 43 | System.out.println("The integers in reverse order:"); 44 | 45 | for (int i = size - 1; i >= 0; i--) { 46 | System.out.println(numbers[i]); 47 | } 48 | } 49 | } 50 | ``` 51 | 52 | Here is an example of running the program: 53 | 54 | ``` 55 | How many integers? 5 56 | Enter the integers: 57 | 13 58 | -5 59 | 0 60 | 8 61 | 5 62 | The integers in reverse order: 63 | 5 64 | 8 65 | 0 66 | -5 67 | 13 68 | ``` 69 | 70 | Let's look at the creation of the array in more detail: 71 | 72 | ```java 73 | int[] numbers = new int[size]; 74 | ``` 75 | 76 | On the right-hand side of the assignment operator, we have the expression `new int[size]`. This expression creates a new array of integers and returns a reference to the array. The reference is then assigned to the variable `numbers`. 77 | 78 | The variable `numbers` is a variable that points to the array. It is declared to be of type `int[]`, which means that it can hold a reference to an array of integers. 79 | 80 | There is an alternative way for declaring and initializing an array. If we know all the values that we want to store in the array at the time of creation, we can use the following syntax: 81 | 82 | ```java 83 | int[] numbers = { 1, -2, 14, -4, 0 }; 84 | ``` 85 | 86 | This is called an array initializer. The size of the array is determined by the number of values in the initializer. In the example above, the size is automatically set to 5. 87 | 88 | 89 | ## 1.4.2. Accessing array elements 90 | 91 | As mentioned earlier, the elements of an array are accessed by their indices. The index of the first element is 0, the index of the second element is 1, and so on. The index of the last element is the size of the array minus one. 92 | 93 | To access an element at place `i` in an array, we use the following syntax: 94 | 95 | ```java 96 | array[i] 97 | ``` 98 | 99 | For example, the following code snippet prints the first and the last element of an array: 100 | 101 | ```java 102 | int[] numbers = { 1, -2, 14, -4, 0 }; 103 | 104 | System.out.println(numbers[0]); 105 | System.out.println(numbers[4]); 106 | ``` 107 | 108 | The output of the program is: 109 | 110 | ``` 111 | 1 112 | 0 113 | ``` 114 | 115 | If we try to access an element that does not exist, we get an `ArrayIndexOutOfBoundsException`. For example, the following code snippet causes an exception: 116 | 117 | ```java 118 | int[] numbers = { 1, -2, 14, -4, 0 }; 119 | 120 | System.out.println(numbers[5]); 121 | ``` 122 | 123 | The output of the program is: 124 | 125 | ``` 126 | Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5 127 | at ArrayExample.main(ArrayExample.java:7) 128 | ``` 129 | 130 | It is the responsibility of the programmer to ensure that the index is within the bounds of the array. If you are not sure whether the index is valid, you can check it with the `length` field of the array: 131 | 132 | ```java 133 | if (i >= 0 && i < array.length) { 134 | // Access the element at index i 135 | } 136 | ``` 137 | 138 | The `length` field of an array contains the number of elements in the array. The length of an array is always one more than the index of the last element. For example, if an array has five elements, the indices of the elements are 0, 1, 2, 3, and 4, and the length of the array is 5. 139 | 140 | ## 1.4.3. Iterating over arrays 141 | 142 | The elements in an array can be iterated over using a `for` loop, a `while` loop, or a `do/while` loop. In the earlier example, we used a `for` loop to iterate over the elements of the array in a reverse order: 143 | 144 | ```java 145 | 146 | for (int i = size - 1; i >= 0; i--) { 147 | System.out.println(numbers[i]); 148 | } 149 | ``` 150 | 151 | There is a more convenient way to iterate over the elements of an array. The `for` loop can be replaced with a `for-each` loop, which is a special kind of a `for` loop that iterates over the elements of an array or a collection. The syntax of a `for-each` loop is as follows: 152 | 153 | ```java 154 | for (type variable : array) { 155 | // Do something with variable 156 | } 157 | ``` 158 | 159 | To iterate over the elements of an array, we can use the following code: 160 | 161 | ```java 162 | for (int number : numbers) { 163 | System.out.println(number); 164 | } 165 | ``` 166 | 167 | This time, however, the elements are printed in the original order: 168 | 169 | ``` 170 | 13 171 | -5 172 | 0 173 | 8 174 | 5 175 | ``` 176 | 177 | Iterating over an array is just one of the use cases of the `for-each` loop. We will discuss other use cases later. 178 | 179 | ## 1.4.4. Two-dimensional arrays 180 | 181 | A two-dimensional array is an array of arrays. It can be used to represent a matrix or a table of rows and columns. The following example shows how to create a two-dimensional array and how to access its elements. In the example, we construct a chessboard, and assign pieces to their initial positions. Each piece is represented by a single character. 182 | 183 | ```java 184 | 185 | char[][] chessboard = { 186 | { 'R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R' }, 187 | { 'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P' }, 188 | { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }, 189 | { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }, 190 | { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }, 191 | { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }, 192 | { 'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p' }, 193 | { 'r', 'n', 'b', 'q', 'k', 'b', 'n', 'r' } 194 | }; 195 | ``` 196 | 197 | In this example, we have an array of eight arrays. Each of the eight arrays represents a row of the chessboard. The first array contains the pieces of the first row, the second array contains the pieces of the second row, and so on. 198 | 199 | Let's iterate over the elements of the array and print the chessboard: 200 | 201 | ```java 202 | for (char[] row : chessboard) { 203 | for (char piece : row) { 204 | System.out.print(piece); 205 | } 206 | System.out.println(); 207 | } 208 | ``` 209 | 210 | The output of the program is: 211 | 212 | ``` 213 | RNBQKBNR 214 | PPPPPPPP 215 | 216 | 217 | 218 | 219 | pppppppp 220 | rnbqkbnr 221 | ``` 222 | 223 | In the previous example, we used array literals to initialize the array. We can also create a two-dimensional array by first creating an array of arrays, and then assigning values to the elements one by one: 224 | 225 | ```java 226 | char[][] chessboard = new char[8][8]; 227 | 228 | chessboard[0][0] = 'R'; 229 | chessboard[0][1] = 'N'; 230 | chessboard[0][2] = 'B'; 231 | chessboard[0][3] = 'Q'; 232 | 233 | // ... 234 | 235 | chessboard[7][7] = 'r'; 236 | ``` 237 | 238 | The idea of a two-dimensional array can be generalized to arrays of higher dimensions. In practice, however, two-dimensional arrays are the most commonly used multidimensional arrays. 239 | 240 | In this submodule, we have learned how to create and use arrays. In the next submodule, we will learn how to create and use classes. Once you learn about the object-oriented features of Java, you can use arrays to store objects of a class. For example, you can create an array of `Person` objects, and store the objects in the array. We will discuss this in more detail later. 241 | 242 | ## Assignments 243 | 244 | **Task 1: A name generator** 245 | 246 | Write a program that generates random names by combining first and last names from hard-coded name arrays. The program should work as follows: 247 | 248 | Create two arrays, `firstNames` and `lastNames`, which contain first and last names. 249 | 250 | Ask the user how many random names the program should generate. 251 | 252 | Generate random names using the following logic: 253 | 1. Choose the index value for the first name randomly. 254 | 2. Choose the index value for the last name randomly. 255 | 3. Use the index values to create a random full name (i.e. first name and last name). 256 | 4. Print the generated full name. 257 | 258 | Repeat these steps as many times as the user-specified number of names to generate. 259 | 260 | **Task 2: Finding the maximum subarray sum** 261 | 262 | Write a program that takes an array of integers as input from the user and finds the subarray with the maximum sum. A subarray is a portion of an array, which consists of contiguous elements from the original array in the same order. The program should work as follows: 263 | 264 | 1. Ask the user for the number of integers in the array. 265 | 2. Prompt the user to enter the integers into the array. 266 | 3. Find the subarray with the maximum sum using the following logic: 267 | - Iterate through all possible subarrays in the array. 268 | - Calculate the sum of each subarray. 269 | - Keep track of the maximum sum found and the corresponding subarray indices. 270 | 4. Print the maximum sum and the corresponding indices. (Use the indices shown to the user, i.e., starting from 1.) 271 | 272 | Example: 273 | 274 | ```text 275 | Enter the size of the array: 8 276 | Enter the integers into the array: 277 | Enter integer 1: -2 278 | Enter integer 2: 1 279 | Enter integer 3: -3 280 | Enter integer 4: 4 281 | Enter integer 5: -1 282 | Enter integer 6: 2 283 | Enter integer 7: 1 284 | Enter integer 8: -5 285 | 286 | Maximum sum: 6 287 | Integers: 4-7 288 | ``` 289 | 290 | **Task 3: Removing duplicates** 291 | 292 | Write a program that takes an array of integers as input from the user and removes all the duplicate numbers from it. The program should work as follows: 293 | 294 | 1. Ask the user for the size of the array. 295 | 2. Prompt the user to enter the integers into the array. 296 | 3. Remove all the duplicate numbers from the array using the following logic: 297 | - Create a new array that stores only one occurrence of each number. 298 | - Keep only the first occurrence of each number and discard any subsequent occurrences. 299 | - Print the resulting array without the duplicate numbers. 300 | 301 | In this assignment, you can make the new array as big as the original array, even though it may not be completely filled. 302 | 303 | 304 | Example: 305 | 306 | ```text 307 | Enter the size of the array: 8 308 | Enter the integers into the array: 309 | Enter integer 1: 1 310 | Enter integer 2: 2 311 | Enter integer 3: 3 312 | Enter integer 4: 2 313 | Enter integer 5: 1 314 | Enter integer 6: 3 315 | Enter integer 7: 4 316 | Enter integer 8: 5 317 | 318 | The array without duplicates: 319 | 1 2 3 4 5 320 | ``` 321 | 322 | For this assignment, you get points in the following way: 323 | 324 | 1. Task 1 completed: 2 points 325 | 2. Task 2 completed: 2 points 326 | 3. Task 3 completed: 2 points 327 | 328 | 329 | 330 | --- 331 | _This learning material has been produced with assistance from OpenAI's ChatGPT-4 and GitHub Copilot. These large language models have provided suggestions and solutions that have assisted the author in producing and supplementing the material. While their contribution has been significant, the final responsibility for the content and its correctness resides with the author._ 332 | -------------------------------------------------------------------------------- /2.3_Association.md: -------------------------------------------------------------------------------- 1 | # Association 2 | 3 | An _association_ is a relationship between two classes that signifies a connection or interaction between their instances. It represents the way objects from one class are related to objects of another class. Associations allow you to model the interactions and dependencies between different parts of your program. 4 | 5 | Associations are a fundamental concept in object-oriented programming and play a crucial role in designing and modeling complex systems. 6 | 7 | ## Object references 8 | 9 | Variables whose type is a primitive type hold the real value of the variable. Whereas, variables whose type is a class hold a reference to the value of the variable (You can think of this being just like [a C pointer](https://www.javatpoint.com/how-to-use-pointers-in-c-language), but JVM may implement references any way it likes, 10 | for example, by pointers to pointers), we call them as a _reference variable_. 11 | 12 | If you try to print out the content of reference variable like 13 | ```Java 14 | Car myCar; 15 | 16 | myCar = new Car("Toyota Corolla"); 17 | System.out.println(myCar); 18 | ``` 19 | the output is something like (assuming `Car` does not overwrite the `toString()` method) 20 | ```text 21 | Car@1b6d3586 22 | ``` 23 | String before the @-character is the name of the class who created this object, and the string after the character is the `hashCode()` in [hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) notation. 24 | 25 | If you don't assign anything to the reference variable, like 26 | ```Java 27 | Car myCar; 28 | 29 | System.out.println(myCar); 30 | ``` 31 | then the Java run-time system initialized the reference variable to a special value `null`. This value means that the reference variable does not refer to anything. The output is therefore: 32 | ```text 33 | null 34 | ``` 35 | > Modern integrated development environments, like IntelliJ IDEA, may be able to detect this kind of uninitialized reference errors and can produce an error message like "java: variable demo might not have been initialized." 36 | 37 | Let's create two `Car` objects 38 | ```Java 39 | myCar = new Car(); 40 | yourCar = new Car(); 41 | ``` 42 | Now we have two (reference) variables, `myCar` and `yourCar` who holds references to two _different_ `Car` objects. 43 | ```mermaid 44 | graph TD 45 | r1[myCar] --> o1(Object Car #1) 46 | r2[yourCar] --> o2(Object Car #2) 47 | ``` 48 | After we make the following assignment 49 | ```Java 50 | myCar = yourCar; 51 | ``` 52 | The object myCar was referring to (before assignment `myCar = yourCar`) is now orphaned—no other object in the application is referring to it anymore. 53 | ```mermaid 54 | graph TD 55 | o1(Object Car #1) 56 | r1[myCar] --> o2(Object Car #2) 57 | r2[yourCar] --> o2(Object Car #2) 58 | ``` 59 | JVM will take care of releasing memory occupied by orphaned objects by doing [garbage collection](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)). 60 | There is no need (and no way) to explicitly call anything like free(). Sometimes JVM garbage collector causes 61 | problems—it may start at any time, and it may require processor resources in a way that is visible in application performance. 62 | 63 | ## Comparing objects 64 | 65 | When you compare values of primitive types, the comparisons operate in the normal way. But since object references are like pointers, what is actually compared? 66 | `myCar == yourCar` is true iff (if and only if) `myCar` and `yourCar` refer to the same object (which is the case after the assignment `myCar = yourCar`). If the purpose is to compare object values (for example, check if two car objects relate to the same real car), a method for the comparison needs to be invoked. `mycar.equals(yourCar)` is true iff the `equals` method defined in `Car` class returns true. In the implementation of `equals()` you decide the conditions under which two objects 67 | are equal, e.g., what to include in equality comparisons (register plate, manufacturer serial number, etc.). 68 | 69 | `String` in Java is an object. Therefore, if you have a piece of definitions like: 70 | ```Java 71 | String s1 = scanner.nextLine(); 72 | String s2 = "Metropolia"; 73 | ``` 74 | and you want to compare whether s1 is equal to s2, you may try this: 75 | ```Java 76 | if (s1 == s2) 77 | System.out.println("They are the same"); 78 | ``` 79 | but the result of comparison is always false because we are actually comparing whether `s1` and `s2` are referring to the same object. 80 | 81 | The right way to compare string is to call `equals` method in the `String` class. 82 | ```Java 83 | if (s1.equals(s2)) 84 | System.out.println("They are the same"); 85 | ``` 86 | now we are comparing the content of the objects, not their referencies. 87 | 88 | ## Object relations 89 | 90 | Co-operation between objects requires that they both know each other. You need to know (i.e., have a 91 | reference) the other object, in order to call its methods. 92 | 93 | How can an object know another object, e.g., Car? 94 | 1. Object creates the other Car objects by itself. Then it gets the reference to the newly created object. 95 | 2. Object gets the reference to the other object as a parameter in a method call 96 | - Constructor with a parameter. The other object is then created in somewhere else. 97 | - Method with a parameter. Both objects are created already; it does not matter, which is created first. 98 | 99 | ### Case constructor with a reference parameter 100 | 101 | ```Java 102 | public class Bicycle { 103 | private Person owner; 104 | private String model; 105 | private int gears; 106 | private String sound; 107 | 108 | public Bicycle(Person owner, String model, int gears, String sound) { 109 | this.owner = owner; 110 | this.model = model; 111 | this.gears = gears; 112 | this.sound = sound; 113 | } 114 | 115 | public void drive() { 116 | System.out.println(this.model + ": " + owner.getName() + " drives"); 117 | } 118 | 119 | public String getSound() { 120 | return sound; 121 | } 122 | } 123 | ``` 124 | 125 | `Bicycle` class has an instance variable `owner` which is a reference to the `Person` type object (defined in the next subsection). 126 | Initially this instance variable has a value `null` (remember that this was a special value, which means that it does not refer to any object). When the `Bicycle` object is being created, the constructor gets the reference to the other object, which has been created before, as a parameter (`owner`). 127 | 128 | The given parameter is stored to the instance variable `owner` which then refers to the `Person` object created elsewhere. Now other methods in `Bicycle` objects knows the `Person` object so long the `Bicycle` object exists. The `Person` object is passed to the `Bicycle`object in this way: 129 | ```Java 130 | public class BicycleTest { 131 | public static void main(String[] args) { 132 | // main() knows both the Person and Bicycle objects, because main() creates both of them 133 | Person benzino; 134 | Bicycle bike; 135 | 136 | benzino = new Person("Enzio", "Benzino", 1982); // this object needs to be created first 137 | bike = new Bicycle(benzino, "Tunturi", 5, "Viuh"); 138 | bike.drive(); 139 | } 140 | } 141 | ``` 142 | When you run the given application, the output is like this: 143 | ```text 144 | Tunturi: Enzio Benzino drives 145 | ``` 146 | 147 | ### Case method call with a reference parameter 148 | 149 | ```Java 150 | public class Person { 151 | private String firstname; 152 | private String lastname; 153 | private int birthYear; 154 | 155 | public Person(String firstname, String lastname, int birthYear) { 156 | this.firstname = firstname; 157 | this.lastname = lastname; 158 | this.birthYear = birthYear; 159 | } 160 | 161 | public String getName() { 162 | return firstname + " " + lastname; 163 | } 164 | 165 | public void drive(Bicycle bike, int kilometers) { 166 | System.out.println(""); 167 | for (int i = 1; i <= kilometers; i++){ 168 | System.out.println(bike.getSound() +" "+ i + " km travelled"); 169 | } 170 | } 171 | } 172 | ``` 173 | `Person` class does not contain persistent reference to the `Bicycle` object. Method `drive()` only 174 | gets the reference as a parameter when called. Both objects should exist when the method is called. 175 | `Person` object knows the `Bicycle` object only temporarily, i.e., during the execution time of the 176 | method `drive()`. 177 | 178 | In this application the `Bicycle` class does not have any references to the `Person`. Main part of the application creates a `Person` object and two `Bicycle` objects. They are conceptually not connected at all. `drive()` method of `Person` class will get the reference to `Bicycle` object when called. 179 | 180 | ```Java 181 | public class BicycleTest2 { 182 | public static void main(String[] args) { 183 | Person benzino; 184 | Bicycle tunturi, nopsa; 185 | 186 | benzino = new Person("Enzio", "benzino", 1982); 187 | tunturi = new Bicycle(benzino, "Tunturi", 5, "Viuh"); 188 | nopsa = new Bicycle(benzino,"Nopsa", 3, "Ka...boom"); 189 | benzino.drive(tunturi, 3); 190 | benzino.drive(nopsa, 2); 191 | } 192 | } 193 | ``` 194 | When you run the given application, the output is like this: 195 | ```text 196 | Viuh 1 km travelled 197 | Viuh 2 km travelled 198 | Viuh 3 km travelled 199 | 200 | Ka...boom 1 km travelled 201 | Ka...boom 2 km travelled 202 | ``` 203 | 204 | ## Association as an object relation 205 | 206 | Situation where an object knows another object of another class is called as an _association_. An object may have been created the other object by itself, or a reference to the other existing object is passed as a parameter. 207 | 208 | The association can be 209 | - **permanent**, when the object has created the other object, or it has stored the given reference 210 | (as a parameter) to the instance variable. In this case the association exists during the lifetime of the object. 211 | - **temporary**, when an object receives the reference to the other object as a parameter and does not 212 | store it to its own variables. In this case the other object associated can be different in successive 213 | method calls. 214 | 215 | In the previous 'case constructor with a reference parameter' `Bicycle` object knows permanently the `Person` object. Therefore, it can call anytime `Person` object public methods. In the 'case method call with a reference parameter' `Person` object knows temporarily the `Bicycle` object and during that time it can call public methods of the `Bicycle` object. 216 | 217 | ## Assignments 218 | 219 | **Task 1: Library and Books Association** 220 | 221 | Exercise Description: 222 | In this exercise, you will create a Java program that demonstrates the concept of association between two classes: `Library` and `Book`. The program will model a library containing a collection of books. Each book will have a title, author, and publication year. The `Library` class will have methods to add books, display the list of books, and find books by a specific author. 223 | 224 | Instructions: 225 | 1. Create a Java class named `Book`. 226 | 2. Inside the `Book` class, declare private instance variables for the book's title, author, and publication year. Implement a constructor and getter methods to initialize and access these variables. 227 | 228 | 3. Create another Java class named `Library`. 229 | 4. Inside the `Library` class, declare an `ArrayList` to store `Book` objects. Use the following code to create the `Library` class: 230 | ```java 231 | import java.util.ArrayList; 232 | 233 | public class Library { 234 | private ArrayList books = new ArrayList<>(); 235 | 236 | // Add methods here 237 | } 238 | ``` 239 | 240 | 5. Implement the following methods inside the `Library` class: 241 | 242 | - `addBook(Book book)`: This method should add the given `Book` object to the library's collection. 243 | 244 | - `displayBooks()`: This method should display the details of all books in the library's collection, including their titles, authors, and publication years. 245 | 246 | - `findBooksByAuthor(String author)`: This method should search and display the details of books written by the specified author. 247 | 248 | 6. Create a `main` method in a separate class (e.g., `LibraryMain`) to demonstrate the functionality of the `Library` and `Book` classes. In the `main` method, perform the following actions: 249 | 250 | - Create instances of `Book` representing different books. 251 | - Create an instance of `Library`. 252 | - Add the book instances to the library. 253 | - Display the list of all books in the library. 254 | - Search for books by a specific author and display the results. 255 | 256 | Example Output: 257 | ``` 258 | Library Catalog: 259 | 1. Title: "Introduction to Java Programming", Author: "John Smith", Year: 2020 260 | 2. Title: "Data Structures and Algorithms", Author: "Jane Doe", Year: 2018 261 | 3. Title: "The Art of Fiction", Author: "Alice Johnson", Year: 2019 262 | 263 | Books by Author "Jane Doe": 264 | Title: "Data Structures and Algorithms", Year: 2018 265 | ``` 266 | 267 | **Task 2: Book Borrowing System** 268 | 269 | Enhance the `Library` class to include a book borrowing system. Add the following methods: 270 | 271 | - `borrowBook(String title)`: This method should simulate a book being borrowed. It should remove the book from the library's collection if available. 272 | 273 | - `returnBook(Book book)`: This method should simulate a book being returned to the library. It should add the book back to the library's collection. 274 | 275 | **Task 3: Book Availability Check** 276 | 277 | Add a method to the `Library` class to check the availability of a specific book by its title: 278 | 279 | - `isBookAvailable(String title)`: This method should return a boolean indicating whether the book with the specified title is available in the library. 280 | 281 | **Task 4: Book Ratings and Reviews** 282 | 283 | Extend the `Book` class to include a rating and review system. Add the following methods: 284 | 285 | - `setRating(double rating)`: This method should set the rating of the book. 286 | 287 | - `addReview(String review)`: This method should add a review to the book. 288 | 289 | **Task 5: Library Statistics** 290 | 291 | Implement methods in the `Library` class to calculate and display statistics about the books in the library: 292 | 293 | - `getAverageBookRating()`: This method should calculate and return the average rating of all books in the library. 294 | 295 | - `getMostReviewedBook()`: This method should return the book with the highest number of reviews. 296 | 297 | **Task 6: Library Users** 298 | 299 | Create a `User` class with attributes like name, age, and a list of borrowed books. Modify the `Library` class to keep track of library users and their borrowed books. 300 | 301 | For this assignment, you get points in the following way: 302 | 1. Task 1 completed: 1 point 303 | 2. Task 2 completed: 1 point 304 | 3. Task 3 completed: 1 point 305 | 4. Task 4 completed: 1 point 306 | 5. Task 5 completed: 1 point 307 | 6. Task 6 completed: 1 point 308 | -------------------------------------------------------------------------------- /5.2_Synchronization.md: -------------------------------------------------------------------------------- 1 | # Synchronization 2 | 3 | ## An example 4 | 5 | We would like to implement a bank account. We should be able to deposit and withdraw money, and check the saldo of the bank account. [Floating point numbers](https://en.wikipedia.org/wiki/doubleing-point_arithmetic) have only finite precision available (Java `double` type conforms [IEEE 754 floating-Point Standard](https://en.wikipedia.org/wiki/IEEE_754), and has 15 digits precision). If you have to deal with huge sums of money, accuracy of `double` datatype may not be enough, i.e., you are not able to represent single euros when your values are in Quadrillions. That is not acceptable when we are dealing with money. 6 | > If you execute the following Java-statement `System.out.println(0.1 + 0.2);`, you will get `0.30000000000000004` as a result. This is because of the limited precision of the `double` datatype. 7 | 8 | For those cases when we need to be able to represent huge values with full precision, we have `BigInteger` package in Java. [BigInteger](https://docs.oracle.com/javase/8/docs/api/java/math/BigInteger.html) is able to represent numbers in range $-2^{2^{31}}...+2^{2^{31}}$ with full accuracy which should enough for most of the requirements (Note: the number of stars that exist in the universe is about $10^{21}$ which fits easily to the BigInteger). Because we are dealing with money in this application, we choose to use `BigInteger` to represent our values. 9 | 10 | To have multiple independent accounts, we build the account as a class (so that the reusing of it is straightforward, simply create a new account object with `new`-operator): 11 | ```Java 12 | import java.math.BigInteger; 13 | 14 | class Account { 15 | private BigInteger credit; 16 | 17 | public Account() { 18 | credit = BigInteger.ZERO; // 0 to credit 19 | } 20 | 21 | public void deposit(BigInteger amount) { 22 | credit = credit.add(amount); 23 | } 24 | 25 | public boolean withdraw(BigInteger amount) { 26 | BigInteger result = credit.subtract(amount); 27 | 28 | if (result.signum() == -1) { // test whether the result is negative 29 | return false; 30 | } else { 31 | credit = result; 32 | return true; 33 | } 34 | } 35 | 36 | public BigInteger getSaldo() { 37 | return credit; 38 | } 39 | } 40 | ``` 41 | 42 | Then we want to have individuals who deposit money to the bank account. Again, we want to have many of them, so we create a `Depositor` class. This class is also a worker (depositing money to an account is work), and we want that this work can be done simultaneously with other depositors. Thus, we implement `Runnable` interface here: 43 | ```Java 44 | // Depositor is our worker here 45 | class Depositor implements Runnable { 46 | private Account account; 47 | private int n; 48 | 49 | public Depositor(Account account, int n) { 50 | this.account = account; 51 | this.n = n; 52 | } 53 | 54 | public void run() { 55 | for (int i = 0; i < n; i++) 56 | account.deposit(BigInteger.ONE); // we deposit 1 (unit of money) 57 | } 58 | } 59 | ``` 60 | 61 | Then in the `main` method, we create two depositor workers, william and jeff. Start their work, and then we wait for them to have completed their work using the `join` method. 62 | ```Java 63 | public class Main { 64 | static final int N = 1000; 65 | static final int M = 10; 66 | 67 | public static void main(String[] args) { 68 | for (int i = 0; i < M; i++) { 69 | Account account = new Account(); 70 | 71 | Thread william = new Thread(new Depositor(account, N)); 72 | Thread jeff = new Thread(new Depositor(account, N)); 73 | 74 | // depositors start their work 75 | william.start(); jeff.start(); 76 | 77 | // here we wait for them to be ready 78 | try { 79 | william.join(); jeff.join(); 80 | } catch (InterruptedException e) { 81 | } 82 | 83 | System.out.println("Account balance is: " + account.getSaldo()); 84 | } 85 | } 86 | } 87 | ``` 88 | Both depositors deposit 1000 times 1€. Therefore, the account balance after the deposits should be 2000€. But when we run the application, we will get various results: 89 | ```text 90 | Account balance is: 1029 91 | Account balance is: 2000 92 | Account balance is: 2000 93 | Account balance is: 1134 94 | Account balance is: 2000 95 | Account balance is: 1000 96 | Account balance is: 1099 97 | Account balance is: 1959 98 | Account balance is: 2000 99 | ``` 100 | There are correct answers, but a lot of incorrect results. The output seems random, aren't computers deterministic machines? 101 | 102 | ## The problem 103 | 104 | Let's analyze what happens in the statement 105 | ```Java 106 | credit = credit.add(amount); 107 | ``` 108 | We can model this statement as 109 | ```Java 110 | credit = credit + amount; 111 | ``` 112 | Now we need to think a little how the computer evaluates this statement. The variable `credit` is stored in the central memory (RAM). It needs to be read from the memory to the processor (CPU) register. Then we can add the content of another memory location, variable `amount`, to it. After we have done the addition, the result should be written back to the memory, where the variable `credit` resides. 113 | ![](images/VariableReadWrite.png) 114 | 115 | As a matter of fact, an addition is made of three operations: 116 | 1. Read the current value of variable 117 | 2. Add another value to the current value 118 | 3. Write that new value to variable 119 | 120 | When you run that code using a single thread, there are no problems. 121 | It will execute each part of the operation one after another. 122 | But when you have several threads, you can start having troubles. 123 | 124 | ![](images/SynchronizationProblem.png) 125 | 126 | Imagine this situation: 127 | 1. Thread 1 : read the value, get 3, add 1, so value = 4 128 | 2. Thread 2 : read the value, get 3, add 1, so value = 4 129 | 3. Thread 1 : write 4 to the field value and return 4 130 | 4. Thread 2 : write 4 to the field value and return 4 131 | 132 | These situations come from what we call interleaving. Interleaving describe the possible situations of several threads executing some statements. 133 | Even for three operations and two threads, there are a lot of possible interleaving. When you have more threads and more operations, it is almost impossible to enumerate the possible interleavings. 134 | The problem can also occur when a thread gets preempted between instructions of the operation. This phenomenon is called a [data race](https://www.mathworks.com/products/polyspace/static-analysis-notes/what-data-races-how-avoid-during-software-development.html). 135 | 136 | To fix the problem, we need to make the addition operation indivisible, i.e., when one thread starts the operation, it should be able to complete the operation before any other thread will start the same operation. This kind of operation is called as an [atomic operation](https://wiki.osdev.org/Atomic_operation). 137 | 138 | ## The solution 139 | 140 | There are [several solutions](https://en.wikipedia.org/wiki/Synchronization_(computer_science)) to fix this problem: 141 | - Semaphores 142 | - Atomic references 143 | - Monitors 144 | - Condition codes 145 | - Compare and swap 146 | - etc. 147 | 148 | ### The Java synchronized Keyword 149 | 150 | Synchronized blocks in Java are marked with the `synchronized` keyword. A synchronized block in Java is synchronized on an object. All synchronized blocks synchronized on the same object can only have one thread executing inside them at the same time. All other threads attempting to enter the synchronized block are blocked until the thread inside the synchronized block exits the block. 151 | 152 | The synchronized keyword can be used to mark four different types of blocks: 153 | 1. Instance methods 154 | 2. Static methods 155 | 3. Code blocks inside instance methods 156 | 4. Code blocks inside static methods 157 | 158 | These blocks are synchronized on different objects. Which type of synchronized block you need depends on the concrete situation. 159 | 160 | ### Synchronized Instance Methods 161 | 162 | Here is a synchronized instance method: 163 | ```Java 164 | import java.math.BigInteger; 165 | 166 | class Account { 167 | private BigInteger credit; 168 | 169 | public Account() { 170 | credit = BigInteger.ZERO; // 0 to credit 171 | } 172 | 173 | public synchronized void deposit(BigInteger amount) { 174 | credit = credit.add(amount); 175 | } 176 | 177 | public synchronized boolean withdraw(BigInteger amount) { 178 | BigInteger result = credit.subtract(amount); 179 | 180 | if (result.signum() == -1) { // test whether the result is negative 181 | return false; 182 | } else { 183 | credit = result; 184 | return true; 185 | } 186 | } 187 | 188 | public BigInteger getSaldo() { 189 | return credit; 190 | } 191 | } 192 | ``` 193 | Notice the use of the synchronized keyword in the `deposit` method declaration. This tells Java that the method is synchronized. 194 | 195 | A synchronized instance method in Java is synchronized on the instance (object) owning the method. Thus, each instance has its synchronized methods synchronized on a different object: the owning instance. 196 | 197 | Only one thread per instance can execute inside a synchronized instance method. If more than one instance exist, then one thread at a time can execute inside a synchronized instance method per instance. One thread per instance. 198 | 199 | This is true across all synchronized instance methods for the same object (instance). Thus, in the previous example, only one thread can execute inside either of of the two synchronized methods. One thread in total per instance. 200 | 201 | Now when we run the application, the results are correct: 202 | ```text 203 | Account balance is: 2000 204 | Account balance is: 2000 205 | Account balance is: 2000 206 | Account balance is: 2000 207 | Account balance is: 2000 208 | Account balance is: 2000 209 | Account balance is: 2000 210 | Account balance is: 2000 211 | Account balance is: 2000 212 | ``` 213 | 214 | A visual representation of synchronized method: 215 | ```mermaid 216 | flowchart TD 217 | subgraph Willian [Thread – William] 218 | A1[Start William] 219 | A2["Do something (William)"] 220 | SyncStartA["-> deposit()"] 221 | A3["Continue (William)"] 222 | A4[End Willian] 223 | end 224 | 225 | subgraph Jeff [Thread – Jeff] 226 | B1[Start Jeff] 227 | B2["Do something (Jeff)"] 228 | SyncStartB["-> deposit()"] 229 | B3["Continue (Jeff)"] 230 | B4[End Jeff] 231 | end 232 | 233 | A1 --> A2 --> SyncStartA 234 | B1 --> B2 --> SyncStartB 235 | 236 | SyncStartA & SyncStartB --> SyncFunc[" synchronized deposit()"] 237 | 238 | SyncFunc --> A3 --> A4 239 | SyncFunc --> B3 --> B4 240 | ``` 241 | 242 | ### Synchronized Blocks in Instance Methods 243 | You do not have to synchronize a whole method. Sometimes it is preferable to synchronize only part of a method. Java synchronized blocks inside methods make this possible. 244 | 245 | Here is a synchronized block of Java code inside an unsynchronized Java method: 246 | ```Java 247 | public void deposit(BigInteger amount) { 248 | // here we can have unsychronized tasks 249 | synchronized(this) { 250 | credit=credit.add(amount); 251 | } 252 | // here we can have unsychronized tasks 253 | } 254 | ``` 255 | This example uses the Java synchronized block construct to mark a block of code as synchronized. This code will now execute as if it was a synchronized method. 256 | 257 | Notice how the Java synchronized block construct takes an object in parentheses. In the example `this` is used, which is the instance the add method is called on. The object taken in the parentheses by the synchronized construct is called a monitor object. The code is said to be synchronized on the monitor object. A synchronized instance method uses the object it belongs to as monitor object. 258 | 259 | Only one thread can be executed inside a Java code block synchronized on the same monitor object. 260 | 261 | ## Thread Safety 262 | 263 | We have noticed that building concurrent programs requires the correct use of threads and locks. But they are just mechanisms—means to an end. Writing thread-safe code is, at its core, about managing access to *state*, and in particular to *shared*, *mutable state*. 264 | 265 | Informally, an object's *state* is its data, stored in *state variables* such as instance or static fields. An object's state may include fields from other, dependent objects; a `HashMap`'s state is partially stored in the `HashMap` object itself, but also in many `Map.Entry` objects. An object's state encompasses any data that can affect its externally visible behaviour. 266 | 267 | By *shared*, we mean that a variable could be accessed by multiple threads; by *mutable*, we mean that its value could change during its lifetime. We may talk about thread safety as if it were about *code*, but that we are really trying to do is protect *data* from uncontrolled concurrent access. 268 | 269 | Whether an object needs to be thread-safe depends on whether it will be accessed from multiple threads. This is a property of how the object is *used* in a program, not what it *does*. Making an object thread-safe requires using synchronization to coordinate access to its mutable state; failing to do so could result in data corruption and other undesirable consequences. 270 | 271 | *Whenever more than one thread accesses a given state variable, and one of them might write to it, they all must coordinate their access to it using synchronization*. The primary mechanism for synchronization is Java is the `syncronized` keyword, which provides exclusive locking, but the term "syncronization" also includes the use of other Java tools ([`volatile` variables](https://jenkov.com/tutorials/java-concurrency/volatile.html), [explicit locks](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html), and [atomic variables](https://docs.oracle.com/javase/8/docs/api///?java/util/concurrent/atomic/package-summary.html)). 272 | 273 | You should avoid the temptation to think that there are "special" situations in which this rule does not apply. A program that omits necessary synchronization might appear to work, passing its tests and performing well for years, but it is still broken and may fail at any moment. 274 | 275 | If multiple threads access the same mutable variable without appropriate synchronization, *your program is broken*. There are three ways to fix it: 276 | - *Don't share* the state variable across threads 277 | - Make the state variable *immutable* 278 | - Use *synchronized* whenever accessing the state variable 279 | 280 | When designing thread-safe classes, good object-oriented techniques—encapsulation, immutability, and clear specification of invariants—are your best friends. 281 | 282 | ### What is a thread safety 283 | 284 | >A class is *thread-safe* if it behaves correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment, and with no additional synchronization or other coordination on the part of the calling code. 285 | 286 | Thread-safe classes encapsulate any needed synchronization so that clients need not provide their own. 287 | 288 | 289 | 290 | ## Assignments 291 | 292 | **Task 1: Ticket Reservation System** 293 | 294 | Create a Java program that simulates a ticket reservation system for a limited number of seats in a theater. Implement synchronization to ensure that multiple threads representing customers can reserve seats without exceeding the available capacity. 295 | 296 | One possibility for the output: 297 | ```text 298 | Customer 2 reserved 1 tickets. 299 | Customer 5 reserved 2 tickets. 300 | Customer 1 reserved 2 tickets. 301 | Customer 3 reserved 3 tickets. 302 | Customer 4 reserved 1 tickets. 303 | Customer 6 couldn't reserve 2 tickets. 304 | Customer 7 reserved 1 tickets. 305 | Customer 9 couldn't reserve 3 tickets. 306 | Customer 8 couldn't reserve 1 tickets. 307 | Customer 10 couldn't reserve 3 tickets. 308 | Customer 11 couldn't reserve 2 tickets. 309 | Customer 14 couldn't reserve 4 tickets. 310 | Customer 15 couldn't reserve 3 tickets. 311 | Customer 12 couldn't reserve 4 tickets. 312 | Customer 13 couldn't reserve 1 tickets. 313 | ``` 314 | 315 | **Task 2: Thread-safe** 316 | 317 | Assuming `ArrayList` is not thread-safe, design a class that has methods for adding an element to an `ArrayList` collection, querying the size of the collection, and removing an element from the collection. The elements may be any objects of your choice, such as Strings. The class you design should be thread-safe. Design a class that you can use to test thread-safety of your class. 318 | 319 | For this assignment, you get points in the following way: 320 | 1. Task 1 completed: 3 points 321 | 2. Task 2 completed: 3 points 322 | -------------------------------------------------------------------------------- /6.4_GUI_design_tools.md: -------------------------------------------------------------------------------- 1 | # 6.4. GUI design tools 2 | 3 | In the previous submodules we have learned to build a graphical user interface from scratch. However, there are some tools that can help us to build a GUI in a faster way. In this submodule, we will learn to use the Scene Builder tool to build a GUI. 4 | 5 | Scene Builder is a visual layout tool that lets users quickly design JavaFX application user interfaces, without coding. Users can drag and drop UI components to a work area, modify their properties, apply style sheets, and the FXML code for the layout that they are creating is automatically generated in the background. The result is an FXML file that can then be combined with a Java project by binding the UI to the application’s logic. 6 | 7 | [FXML](https://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html) is an XML-based user interface markup language created by Oracle Corporation for defining the user interface of a JavaFX application. 8 | > [XML](https://en.wikipedia.org/wiki/XML) (eXtensible Markup Language) is a markup language designed to store, transport, and organize data in a format that is both human-readable and machine-readable. It is a flexible text format derived from SGML (Standard Generalized Markup Language) and is widely used for data interchange between systems, especially in web development, configuration files, and document storage. 9 | 10 | ## 6.4.1. Installing Scene Builder 11 | 12 | Scene Builder is a standalone design tool that can be used with any Java IDE to create JavaFX UI designs. It is developed by [Gluon](https://gluonhq.com/about-us/people/) and delivered as part of the OpenJFX distribution. It is free to download and use, and it is available for Windows, macOS, and Linux. 13 | 14 | To install Scene Builder, follow these steps: 15 | 16 | 1. Download the latest version of Scene Builder from the [Scene Builder website](https://gluonhq.com/products/scene-builder/). If you are using Windows, download the Windows installer (.msi). If you are using macOS, download the macOS installer. 17 | 2. Run the installer and follow the instructions to install Scene Builder on your computer. 18 | 19 | ## 6.4.2. Building the first application with Scene Builder 20 | 21 | Our goal is to build a simple BMI calculator with Scene Builder. The final result will be the following: 22 | 23 | ![BMI calculator](./images/bmi_calculator.png) 24 | 25 | There are various ways in which you can utilize Scene Builder to create a JavaFX application. In this submodule, we will use the following approach: 26 | 27 | 1. Create a new JavaFX project in IntelliJ IDEA. 28 | 2. Add an empty FXML file to the project. 29 | 2. Create the UI design in Scene Builder. The design will be saved in the FXML file. 30 | 31 | This is the recommended approach for beginners that provides a good balance between the use of Scene Builder and IntelliJ IDEA. 32 | 33 | First, generate a new JavaFX project in IntelliJ IDEA, and add the `model`, `view` and `controller` packages into the `src` folder. To do so, follow the steps described in the previous submodules. 34 | 35 | Then, add a new FXML file to the src/resources folder of the 36 | project. To do so, right-click on the `resources` folder and select `New` > `FXML File`. Name the file `bmi_view.fxml` and click `OK`. IntelliJ IDEA creates a simple FXML file for a user interface that has an `AnchorPane` as the root element. 37 | 38 | At this point, open Scene Builder and load the FXML file that you have just created. To do so, click `Open Project` and select the `bmi_view.fxml` file. The Scene Builder window will look like this: 39 | 40 | ![Scene Builder](./images/scene_builder_newly_opened.png) 41 | 42 | On the Hierarchy card on the left, you see the structure of the user interface. It just displays one node, the AnchorPane, in the user interface. As we want to replace the AnchorPane with the BorderPane, we need to delete the AnchorPane. To do so, select the AnchorPane in the Hierarchy card and press the `Delete` key. The AnchorPane will be removed from the user interface. 43 | 44 | Now that the user interface has changed, we need to save the changes to the FXML file. To do so, click `File/Save` in Scene Builder. The FXML file will be updated with the changes that you have made. 45 | 46 | If you take a quick peek at your code in IntelliJ IDEA, you will see that the FXML file has been updated with the changes that you have made in Scene Builder. The FXML file is now empty, as you have deleted the AnchorPane. 47 | 48 | Now, let's build the following hierarchy into our user interface by dragging and dropping the components from the Containers and Controls cards to the Hierarchy card on the left. First draw a BorderPane into the user interface. 49 | 50 | We follow these principles: 51 | - In the TOP area, we will place a Label (in the Controls section) with the text "My BMI app". 52 | - In the CENTER area, there is a VBox (in the Containers section) that contains two HBox containers. 53 | - Each HBox container contains a Label and a slider. 54 | - In the BOTTOM area, there is a HBox with two labels, one holding the text "BMI: " and the other one holding the result of the calculation. 55 | - The HBox elements have proper padding. 56 | 57 | On the Properties card on the right, you are able to set main properties of the selected Control, e.g., the text which is displayed on the Label control. On the Layout card on the right, you can modify those parameters that have an effect on how the control is positioned on the screen, e.g., margin and paddings. 58 | 59 | As a consequence, the Hierarchy card on the left will look like this: 60 | 61 | ![Scene Builder](./images/sb_hierarchy.png) 62 | 63 | The user interface looks like this: 64 | 65 | ![Scene Builder](./images/sb_gui.png) 66 | 67 | Now that the user interface is ready, we need to add event handlers to the sliders. We want the BMI update to be triggered when the user moves the sliders. First, we need to specify the Java class which is used to control the UI. This is done on the **Controller** card of the stacked menu on the left-hand side of the SceneBuilder user interface. Give name `controller.BMIController` to the field `Controller class`. 68 | 69 | After that, we need to give names (identity) to those controls that we want to manipulate from our Java program. This is done by specifying the `fx:id` on the **Code** card of the stacked menu on the right-hand side of the SceneBuilder user interface. Give names `heightSlider` and `weightSlider` to the appropriate sliders. Then we specify Java program methods that are called when events in JavaFX controls (sliders in our application) are generated. This is done by giving method name `updateBMI` to the events `onDragDetected`, `onMouseDragged` and `onMouseReleased`. 70 | 71 | Finally, locate the `Label` element that stores the result of the calculation, and give the value `bmiLabel` to the `fx:id` attribute. Be sure to click `File/Save` in Scene Builder to make sure that the changes are written to the FXML file. 72 | > If you open the `bmi_view.fxml` file and locate the `Slider` elements in the FXML file you are able to see how Scene Builder generates the FXML file: 73 | >```xml 74 | > 75 | > 76 | >``` 77 | > The three dots in the code above are just an abbreviation for the other attributes of the `Slider` elements. 78 | 79 | Now, we need to implement the `updateBMI` method in the `BmiController` class. As a result, the class looks like this: 80 | 81 | ```java 82 | package controller; 83 | 84 | import view.BMI; 85 | import javafx.fxml.FXML; 86 | import javafx.scene.control.Label; 87 | import javafx.scene.control.Slider; 88 | import java.util.Locale; 89 | 90 | public class BMIController { 91 | 92 | @FXML 93 | private Slider heightSlider; 94 | 95 | @FXML 96 | private Slider weightSlider; 97 | 98 | @FXML 99 | private Label bmiLabel; 100 | 101 | @FXML 102 | private void updateBMI() { 103 | System.out.println("updateBMI() called"); 104 | double height = heightSlider.getValue()/100.0; 105 | double weight = weightSlider.getValue(); 106 | double bmi = weight / Math.pow(height, 2); 107 | System.out.println("BMI: " + bmi); 108 | 109 | String bmiFormatted = String.format(Locale.US, "%.1f", bmi); 110 | bmiLabel.setText(bmiFormatted); 111 | } 112 | 113 | public static void main(String[] args) { 114 | BMI.launch(BMI.class); 115 | } 116 | } 117 | ``` 118 | 119 | The `@FXML` annotations bind the attributes of the `BmiController` class to the elements of the user interface. The `updateBMI` method is called when the user interacts with the sliders. The method calculates the BMI and displays it in the `bmiLabel` label. 120 | 121 | In the end, the FXML file looks like this: 122 | 123 | ```xml 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 139 | 140 |
141 | 142 | 143 | 144 | 145 | 148 | 149 | 150 | 151 | 154 | 155 | 156 | 157 |
158 | 159 | 160 | 161 | 165 | 169 | 170 | 171 | 172 | 173 |
174 | ``` 175 | 176 | Next, we create the user interface class in the `view` package. The class is called `BMI` and it looks like this: 177 | 178 | ```java 179 | package view; 180 | 181 | import javafx.application.Application; 182 | import javafx.scene.Scene; 183 | import javafx.scene.Parent; 184 | import javafx.stage.Stage; 185 | import javafx.fxml.FXMLLoader; 186 | 187 | public class BMI extends Application { 188 | 189 | @Override 190 | public void start(Stage stage) throws Exception { 191 | FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/bmi_view.fxml")); 192 | Parent root = fxmlLoader.load(); 193 | 194 | stage.setScene(new Scene(root)); 195 | stage.show(); 196 | } 197 | 198 | public static void main(String[] args) { 199 | launch(args); 200 | } 201 | } 202 | ``` 203 | 204 | In the `start` method, the FXML file is loaded and the contents of the user interface are displayed in a window. 205 | 206 | At this point, you have learnt the basics of building a user interface with Scene Builder and JavaFX. You can now start to build your own user interfaces. 207 | 208 | ## Assignment: A notebook application 209 | 210 | Design and implement a JavaFX application using the MVC (Model-View-Controller) pattern, utilizing SceneBuilder and FXML. The goal is to create a simple note-taking application with the following features: 211 | 212 | 1. The application should have a graphical user interface (GUI) created in SceneBuilder, consisting of the following elements: 213 | 214 | - A `TextArea` where users can write their notes. 215 | - A `TextField` where users can enter the title of the note. 216 | - A `Button` labeled **Add** that, when clicked, saves the note. 217 | - A container element, such as a `VBox` or `GridPane`, to arrange the elements. 218 | 219 | 2. When the user clicks the **Add** button, the application should save the note with its title and content. The saved notes should be displayed in a textual format, such as a separate section below the input elements. 220 | 221 | 3. Implement the Model, which represents the data and logic of the application. The Model should include the following classes: 222 | - `Note`: Represents a single note and has properties for the title and content of the note, along with appropriate getters and setters. 223 | - `Notebook`: Represents a collection of notes. The `Notebook` class should have a list structure to store the Note objects, as well as methods for adding and retrieving notes. 224 | 225 | 4. Implement the `Controller`, which serves as an intermediary between the `View` (GUI) and the `Model`. The `Controller` should have methods for handling user interactions and managing the notes. For example, when the user clicks the **Add** button, the `Controller` should create a new `Note` instance, populate it with the entered title and content, and add it to the `Notebook`. 226 | 227 | 5. Create an FXML file using SceneBuilder to define the layout of the GUI. Set the appropriate event handlers for the user interactions, such as button clicks, in the FXML file. 228 | 229 | 6. Create a main Java class to launch the application. In the main method, load the FXML file, instantiate the View, and set it as the application's primary stage. 230 | 231 | For this assignment, you get points for the following: 232 | 233 | 1. The user interface is generated in SceneBuilder, and the layout is defined in an FXML file. The user interface contains the required elements. (1 point) 234 | 2. The application reads the FXML file successfully and displays the user interface. (1 point) 235 | 3. The application has the Model classes `Note` and `Notebook` with the relevant properties and methods (1 point) 236 | 4. The application has the Controller class `NoteController` with the required methods. (1 point) 237 | 5. When the user writes a note and clicks the **Add** button, the note is saved and successfully displayed in a textual format. (2 points) 238 | 239 | Optional advanced tasks for extra points: 240 | 241 | 6. Learn about the `ListView` element in JavaFX. Use the `ListView` element to display the notes. (+1 point) 242 | 7. Provided that you use the `ListView` element, implement a feature where the user can select a note from the list and edit or delete it. (+1 point) 243 | 244 | > **_NOTE:_** Mark clearly what you have done when returning the assignment. For example: 5/5 done + 6-7 extra points. Or 7/7 done. 245 | --- 246 | _This learning material has been produced with assistance from OpenAI's ChatGPT-4 and GitHub Copilot. These large language models have provided suggestions and solutions that have assisted the author in producing and supplementing the material. While their contribution has been significant, the final responsibility for the content and its correctness resides with the author._ 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /7.1_Preparing_the_database_and_the_connection.md: -------------------------------------------------------------------------------- 1 | # 7.1. Preparing the database and the connection 2 | 3 | In this module, we learn to use a relational database programmatically. For this purpose, we start 4 | by installing the necessary applications. We use MariaDB as the database server and HeidiSQL as the database editor because they are easy to use and install. We also install the MariaDB JDBC driver in our Java project. 5 | > MariaDB is an open-source [relational database management system (RDBMS)](https://www.oracle.com/database/what-is-a-relational-database/) that originated as a community-driven fork of MySQL. Its development was prompted by concerns about the acquisition of MySQL by Oracle Corporation in 2010, as Oracle's plans for MySQL’s future were uncertain. MariaDB is maintained by the MariaDB Foundation, a non-profit organization that promotes the development of MariaDB and provides support and training for the database. MariaDB is compatible with MySQL, meaning that MariaDB can replace MySQL in most applications without any changes to the code. MariaDB is widely used in web applications and is the default database in many Linux distributions. There are other relational database management systems available, such as [PostgreSQL](https://www.postgresql.org/), [SQLite](https://www.sqlite.org/) and [CockroachDB](https://www.cockroachlabs.com/). 6 | 7 | > Note that there exists also databases that are not using a relational data model. If your data is unstructured and doesn't fit neatly into tables (like text, images, logs, or sensor data), NoSQL databases like [MongoDB](https://www.mongodb.com/) (document-based), [Cassandra](https://cassandra.apache.org/_/index.html) (wide-column store) or [Elasticsearch](https://www.elastic.co/enterprise-search) (used for full-text search and real-time data analysis) are usually the best fit. 8 | 9 | ## 7.1.1. Installing MariaDB 10 | 11 | If you have not yet done so, install MariaDB on your computer. You can find the installation instructions 12 | at the MariaDB website: https://mariadb.org/download/. In the installation process, you need to set 13 | the root password for the database. Remember the password, as you will need it later. 14 | 15 | Once the MariaDB database server has been installed, it will automatically run in the background. 16 | 17 | > If you run MacOS instead of Windows, you can either install MariaDB homebrew or use MySQL Community Server. The installation instructions for MySQL Community Server are available at https://dev.mysql.com/downloads/mysql/. The installation instructions for homebrew are available at https://mariadb.com/kb/en/installing-mariadb-on-macos-using-homebrew/. In both cases, the usage is similar to the regular installation of MariaDB. 18 | 19 | ## 7.1.2. Installing a database editor 20 | 21 | In general, there are two options to work with databases: 22 | 1. via the command prompt 23 | 2. using a database editor 24 | 25 | It is usually easier to work with a database editor such as HeidiSQL that allows you to easily compose your SQL statements, see their results, and monitor the structure and content of the database tables. 26 | > Current versions of IntelliJ IDEA also have a database editor, you can use it as well. You can open the database editor by selecting **View/Tool Windows/Database** from the menu. The database editor is located in the **Database** tab. You can connect to the database by clicking the **+** button and selecting the database type. You can also run SQL queries in the database editor. The database editor is a good tool for debugging your SQL queries. 27 | 28 | The following image shows the HeidiSQL database editor in use: 29 | 30 | ![HeidiSQL editor](images/heidisql.png) 31 | 32 | You can download HeidiSQL from https://www.heidisql.com/download.php, or it may come 33 | bundled with the Windows version of MariaDB. 34 | 35 | >If you use a MacOS computer instead, you can use SequelPro as a database editor. You can download it from https://www.sequelpro.com/. The course material has been written with HeidiSQL in mind, but all the same things can be done with SequelPro. 36 | 37 | One the database editor is installed, you can use it to connect to the MariaDB database server. While connecting, use root as the username 38 | and the password you set during the installation process. Set localhost as the host name and 3306 as the port number. That port number 39 | is the default port number for MariaDB. In HeidiSQL, the connection parameters are entered like this: 40 | 41 | ![establishing a database connection from HeidiSQL](images/heidi_connection.png) 42 | 43 | While carrying out administrator tasks such as creating a database, you need to use the root user account. In the future, when your application connects to a database, it should not use the root user account but a separate user account created for the application. That enables you to restrict the permissions of the application to those that it actually needs. 44 | We will discuss the user accounts later in this module. 45 | 46 | ## 7.1.2. Creating the database 47 | 48 | The commands for setting up a database are typically collected into a database script. You can compose the script using HeidiSQL or a text editor. 49 | The database script is just a text file that contains SQL commands. The SQL commands are executed in the order they appear in the script. 50 | 51 | We design the database script in such a way that it completely recreates the database. 52 | That is, if you ever need to modify the structure of the database, you can simply run the script again and the database will be 53 | recreated from the scratch. 54 | 55 | To achieve this, we enter the following commands into the database script: 56 | 57 | ```sql 58 | DROP DATABASE IF EXISTS company; 59 | CREATE DATABASE company; 60 | USE company; 61 | ``` 62 | 63 | The `IF EXISTS` clause in the `DROP DATABASE` statement ensures that the database is dropped only if it exists. If the database does not exist, 64 | the `DROP DATABASE` statement does not cause an error. 65 | 66 | The `USE` statement selects the database that we want to use. In this case, we select the company database. 67 | 68 | 69 | ## 7.1.3. Creating the table 70 | 71 | To begin with, we just add one table to the database. The table is called `EMPLOYEE` and it contains the following columns: 72 | - `id` - the unique identifier of the employee 73 | - `first_name` - the first name of the employee 74 | - `last_name` - the last name of the employee 75 | - `email` - the email address of the employee 76 | - `salary` - the salary of the employee 77 | 78 | We also set the id as a primary key. For each table, it is important to set a primary key that uniquely identifies the rows in the table. 79 | The primary key is used to identify the rows when we want to update or delete them. Also, the database management system automatically 80 | creates an index for the primary key. Indexes are used to speed up the retrieval of data from the database. 81 | 82 | The following statement creates the `employee` table. The statement specifies the table name, the field names and data types as well as the primary key: 83 | 84 | ```sql 85 | CREATE TABLE EMPLOYEE ( 86 | id INT NOT NULL AUTO_INCREMENT, 87 | first_name VARCHAR(50) NOT NULL, 88 | last_name VARCHAR(50) NOT NULL, 89 | email VARCHAR(50) NOT NULL, 90 | salary DECIMAL(10, 2) NOT NULL, 91 | PRIMARY KEY (id) 92 | ); 93 | ``` 94 | The `NOT NULL` clause ensures that the field cannot be empty. The `AUTO_INCREMENT` clause ensures that the id field is set automatically by the database management system. The `DECIMAL(10, 2)` clause ensures that the salary field can store a number with two decimal places. 95 | 96 | The `CREATE TABLE` statement is inserted into the database script after the `DROP DATABASE` and `CREATE DATABASE` statements. 97 | 98 | ## 7.1.4. Inserting data into the table 99 | 100 | Sometimes, we want to insert some data into the table when we create the table. For example, we may want to insert some 101 | initial data into the table. We can do this by adding the `INSERT INTO` statement to the database script. 102 | 103 | The following statement inserts three employees into the `EMPLOYEE` table: 104 | 105 | ```sql 106 | INSERT INTO EMPLOYEE (first_name, last_name, email, salary) VALUES 107 | ('John', 'Doe', 'johndoe@somemail.com', 1000.00), 108 | ('Mary', 'Smith', 'msmith@goodmail.com', 2000.00), 109 | ('Peter', 'Jones', 'peterjones@greatmail.com', 3000.00); 110 | ``` 111 | 112 | The `INSERT INTO` statement is inserted into the database script after the `CREATE TABLE` statement. 113 | 114 | Now you have created a database script, you can run it in HeidiSQL. To do this, select the script in HeidiSQL and click the 115 | **Execute** button. The script will be executed and the database will be created. 116 | 117 | ## 7.1.4. Creating a user account and setting the privileges 118 | 119 | So far, we have been working work the `root` user account. However, when you are developing an application, you should not use 120 | the `root` account, as it is a major security risk. Instead, you should create a separate user account for the application. 121 | Normally, it is enough to create just one user account with the `SELECT`, `INSERT`, `UPDATE`, and `DELETE` privileges. 122 | 123 | Technically, we can insert the user creation commands into the database script. However, it is better to create the user account 124 | separately. This way, we can easily change the password of the user account without having to modify the database script. 125 | 126 | The next statement creates a user account called `appuser`: 127 | 128 | ```sql 129 | CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'password'; 130 | ``` 131 | 132 | In the statement above, `appuser` is the username and `password` is the password. The `@'localhost'` part specifies that the user 133 | account can be used only from the localhost. Normally, it is wise to restrict the user account to the localhost, as it is a major 134 | security risk to allow the user account to be used from any computer. In reality, you may want to locate the database server software 135 | on a separate computer and allow the user account to be used only from the computer that runs the backed code (such as the web server or the Flask backend). 136 | 137 | The next statement grants the `SELECT`, `INSERT`, `UPDATE`, and `DELETE` privileges to the `appuser` account: 138 | 139 | ```sql 140 | GRANT SELECT, INSERT, UPDATE, DELETE ON company.* TO 'appuser'@'localhost'; 141 | ``` 142 | 143 | The statement above grants the privileges to all tables of the `company` database. If you want. for example, 144 | to grant just `SELECT` privileges to the `employee` table for `appuser` connecting from a fixed IP address 139.112.64.8, you can use the following statement: 145 | 146 | ```sql 147 | GRANT SELECT ON company.employee TO 'appuser'@'139.112.64.8'; 148 | ``` 149 | 150 | This time, we use the former version of the `GRANT` statement, as the connection is initiated from localhost. Also, 151 | we may add additional tables to the company database later, so we want to grant the privileges to all tables at this point. 152 | 153 | ## 7.1.5. Testing the user account 154 | 155 | To test the user account, we can connect to the database server using HeidiSQL. While connecting, use appuser as the username 156 | and the password you set during the user creation process. Set localhost as the host name and 3306 as the port number. That port number 157 | is the default port number for MariaDB. 158 | 159 | If you have set the privileges correctly, you should be able to connect to the database server using the `appuser` account. 160 | 161 | You can specify the database name in the database field at the login phase, or you can select the database after you have logged in. 162 | To select the database after you have logged in, click the **Database** menu and select the `company` database. Alternatively, you can 163 | issue the `USE company` statement. 164 | 165 | Once connected, why not try a `SELECT` statement to see if the user account indeed has the `SELECT` privilege? 166 | 167 | Type the following statement into HeidiSQL: 168 | 169 | ```sql 170 | SELECT * FROM company.employee; 171 | ``` 172 | 173 | You should see the following output: 174 | 175 | ``` 176 | +----+-----------+----------+-------------------------+--------+ 177 | | id | first_name| last_name| email | salary | 178 | +----+-----------+----------+-------------------------+--------+ 179 | | 1 | John | Doe | johndoe@somemail.com | 1000.00| 180 | | 2 | Mary | Smith | msmith@goodmail.com | 2000.00| 181 | | 3 | Peter | Jones | peterjones@greatmail.com| 3000.00| 182 | +----+-----------+----------+-------------------------+--------+ 183 | ``` 184 | 185 | If you get an error message, you have probably not set the privileges correctly. Check the privileges again and try again. 186 | 187 | You can also try submitting other SQL statements, such as a `CREATE TABLE` statement with the `appuser`account. That should fail, as the `appuser` account does not have the `CREATE` privilege. 188 | 189 | At this point, let's switch back to the `root` account. We will need the `root` account to create the database script. Later, when our Java application connects to the database, it will use the `appuser` account. 190 | 191 | 192 | ## 7.1.6. Creating the database script 193 | 194 | Remember that previously you composed the `DROP DATABASE`, `CREATE DATABASE`, `CREATE TABLE` and `INSERT` statements in a database script. 195 | Now that you have created the database script, you can save it to a file. The file name should be something like `company.sql`. 196 | You can save the file in the same directory where you have saved the Java source code files. 197 | 198 | As the database script is now ready, and the database created, we can connect to the database from a Java application. 199 | 200 | Save the script for later use. You can always run the script again to recreate the database, if things go wrong. 201 | 202 | 203 | ## 7.1.7. Creating the structure for a Java project 204 | 205 | Now it's time to get started with the Java code. We need a special JDBC (Java Database Connectivity) driver to connect to the database server (MariaDB in our case). The JDBC driver is a library that enables Java programs to connect to a database and execute SQL statements. Different database servers (e.g., PostgresSQL, MySQL, Oracle) have their own JDBC drivers. Using the driver as an abstraction layer, we can make our Java applications database server independent. In this course, we will use the MariaDB JDBC driver. 206 | 207 | We will create a new Java project in IntelliJ IDEA. In creating the project, we select [Maven](https://maven.apache.org/index.html) as the build system. As a consequence, the project will be created with a `pom.xml` (Project Object Model) file. We will use the `pom.xml` file to install the JDBC driver. 208 | 209 | The JDBC driver is not part of the Java SE standard library. It is a separate library that you need to install in your project. The JDBC driver is available from the Maven Central Repository (https://mvnrepository.com/). You can install the driver in your project by adding the following dependency to your `pom.xml` file. 210 | 211 | ```xml 212 | 213 | . 214 | . 215 | . 216 | 217 | org.mariadb.jdbc 218 | mariadb-java-client 219 | 3.1.2 220 | 221 | . 222 | . 223 | . 224 | 225 | ``` 226 | 227 | > If you use Mac OS and installed MySQL Community server instead of MariaDB, look for a Connector/J driver in the Maven Central Repository. 228 | 229 | Hint: Some JDBC drivers may use SLF4J for logging, and therefore require SLF4J-compatible logging libraries. 230 | Without installing one, you may get warnings while running JDBC-enabled programs. 231 | Thus, at this point, you might also want to add a dependency for a logging library: 232 | 233 | ```xml 234 | 235 | ch.qos.logback 236 | logback-classic 237 | 1.4.6 238 | 239 | ``` 240 | 241 | Both `dependency` elements go inside the `dependencies` element. The `groupId` and `artifactId` elements 242 | specify the name of the library. The `version` element specifies the version of the library. 243 | Replace 3.1.2. with the current version of the JDBC driver, and 1.4.6. with the current version of the logging library. 244 | 245 | After the `pom.xml` has been edited, you can select **Maven/Reload Project** from the menu. This will install the JDBC driver 246 | and the logging library in your project. 247 | 248 | If the installation is successful, you should see a tick mark on the Build tab of the Maven tool window. 249 | 250 | Once the libraries have been installed, you can use JDBC in your project. In the next submodule, we will learn how to 251 | connect to the database, and persist and retrieve object data using the JDBC driver. 252 | 253 | ## Assignment: A database for the Currency Converter application 254 | 255 | In submodule 6.2., you designed an application for the currency converter that had a graphical user interface. In this assignment, the goal is to design a database for the application. Later, we will make the application use the database. 256 | 257 | The Model of the application contains the `Currency` class, which stores for each currency the following data: the abbreviation, name, and conversion rate to a fixed currency (e.g., USD). In addition, you need to store the list of currencies somewhere, either in a Model class or in the Controller class. 258 | 259 | Now, design a database that stores the data of the `Currency` objects. Make a database script that contains the following things: 260 | 261 | 1. A statement for dropping the previous version of the database, if it exists. 262 | 2. A statement for creating the database. 263 | 3. A statement for creating a table for storing the `Currency` objects. 264 | 4. Statements for populating the table with data. You should include at least eight currencies with up-to-date exchange rates in the table. 265 | 5. A statement for dropping the user account `appuser`, if it exists. 266 | 6. A statement for creating the user account `appuser`. 267 | 7. Statements for granting the privileges to the user account `appuser`. Think of your application: what privileges does it need? The user account should have only the privileges it needs, and no more. 268 | 269 | Save the database script and run it to create the MariaDB database. Verify that it works, even if you run it more than once. 270 | 271 | Once the database is established, write the following SQL queries to test it: 272 | 1. A query that retrieves all the currencies from the database. 273 | 2. A query that retrieves the currency with the abbreviation `EUR` (or other abbreviation, if you don't have EUR in your database). 274 | 3. A query that retrieves the number of currencies in the database. 275 | 4. A query that retrieves the currency with the highest exchange rate. 276 | 277 | Do not include the queries in the database script. Instead, write them in a separate file, e.g., `queries.sql`. You can run the queries either from HeidiSQL or from the command line with the command `mysql -u root -p < queries.sql`. 278 | 279 | For this assignment, you get points as follows: 280 | - The script contains the statements 1-4. (1 point) 281 | - The script contains the statements 5-6. (1 point) 282 | - The script contains the statement 7. (1 point) 283 | - The script works correctly and can be run multiple times. (1 point) 284 | - The queries 1-2 work correctly. (1 point) 285 | - The queries 3-4 work correctly. (1 point) 286 | 287 | --- 288 | _This learning material has been produced with assistance from OpenAI's ChatGPT-4 and GitHub Copilot. These large language models have provided suggestions and solutions that have assisted the author in producing and supplementing the material. While their contribution has been significant, the final responsibility for the content and its correctness resides with the author._ 289 | -------------------------------------------------------------------------------- /7.2_Database_usage_via_JDBC_driver.md: -------------------------------------------------------------------------------- 1 | # 7.2. Database usage via JDBC driver 2 | 3 | In this submodule, we learn to persist information into the database and retrieve information from the database. 4 | 5 | We use the `company` database introduced in the previous submodule as an example. So far, the database contains 6 | just one table, the `EMPLOYEE` table. 7 | 8 | Our goal is to provide functionality for three operations: 9 | 1. to retrieve all employees from the database 10 | 2. to retrieve an employee from the database by id 11 | 3. to persist an employee into the database 12 | 13 | ## 7.2.1. Designing the structure of the application 14 | 15 | At this point, we design a structure for our application. We create a new Java project, and split the code into four 16 | packages: 17 | - `application`. This package contains the code for the main application. 18 | - `dao`. This package contains the code for accessing the database. 19 | - `datasource`. This package contains the code for connecting to the database. 20 | - `entity`. This package contains the entity classes. Using MVC terms, this constitutes the model. 21 | 22 | The following image shows the structure of the project: 23 | 24 | ![Structure of a database project](images/structure_of_project.PNG) 25 | 26 | In each package, there can be one or more classes. In our example, there is just one class in each package. 27 | 28 | ## 7.2.2. The entity class 29 | 30 | The entity class is a class that represents an entity in the database. In our example, the entity class is the `Employee` class. It looks like this: 31 | 32 | ```java 33 | package entity; 34 | 35 | public class Employee { 36 | 37 | private String firstName, lastName, email; 38 | private double salary; 39 | 40 | public Employee(String firstName, String lastName, String email, double salary) { 41 | this.firstName = firstName; 42 | this.lastName = lastName; 43 | this.email = email; 44 | this.salary = salary; 45 | } 46 | 47 | public String getFirstName() { 48 | return firstName; 49 | } 50 | 51 | public void setFirstName(String firstName) { 52 | this.firstName = firstName; 53 | } 54 | 55 | public String getLastName() { 56 | return lastName; 57 | } 58 | 59 | public void setLastName(String lastName) { 60 | this.lastName = lastName; 61 | } 62 | 63 | public String getEmail() { 64 | return email; 65 | } 66 | 67 | public void setEmail(String email) { 68 | this.email = email; 69 | } 70 | 71 | public double getSalary() { 72 | return salary; 73 | } 74 | 75 | public void setSalary(double salary) { 76 | this.salary = salary; 77 | } 78 | } 79 | ``` 80 | 81 | The class contains the declarations of the instance variables, the constructor, and the getters and setters for the instance variables. 82 | 83 | ## 7.2.3. The datasource class 84 | 85 | In our example, `MariaDbConnection` is the datasource class. It looks like this: 86 | 87 | ```java 88 | package datasource; 89 | 90 | import java.sql.Connection; 91 | import java.sql.DriverManager; 92 | import java.sql.SQLException; 93 | 94 | public class MariaDbConnection { 95 | 96 | private static Connection conn = null; 97 | 98 | public static Connection getConnection() { 99 | if (conn==null) { 100 | // connect if necessary 101 | try { 102 | conn = DriverManager.getConnection( 103 | "jdbc:mariadb://localhost:3306/company?user=appuser&password=password"); 104 | } catch (SQLException e) { 105 | System.out.println("Connection failed."); 106 | e.printStackTrace(); 107 | } 108 | return conn; 109 | } 110 | else { 111 | return conn; 112 | } 113 | } 114 | 115 | public static void terminate() { 116 | try { 117 | getConnection().close(); 118 | } catch (SQLException e) { 119 | // TODO Auto-generated catch block 120 | e.printStackTrace(); 121 | } 122 | } 123 | } 124 | 125 | ``` 126 | 127 | This class is a utility class for connecting to the database. The method `getConnection()` returns the connection object. The method `terminate()` closes the connection. 128 | 129 | The `getConnection()` method ensures that only one connection is established in the lifetime of the application (in a way that resembles the singleton pattern). 130 | The motivation for that is the fact that establishing a connection is a costly operation that takes some time. We want to avoid establishing a connection for each request. 131 | 132 | ## 7.2.4. The DAO class 133 | 134 | The idea of the DAO (Data Access Object) class is to encapsulate the database access code. The DAO class contains the code for accessing the database. In our example, the DAO class is the `EmployeeDao` class. It looks like this: 135 | 136 | ```java 137 | package dao; 138 | 139 | import entity.Employee; 140 | import java.sql.*; 141 | import datasource.MariaDbConnection; 142 | import java.util.*; 143 | 144 | public class EmployeeDao { 145 | 146 | public List getAllEmployees() { 147 | Connection conn = MariaDbConnection.getConnection(); 148 | String sql = "SELECT first_name, last_name, email, salary FROM employee"; 149 | List employees = new ArrayList(); 150 | 151 | try { 152 | Statement s = conn.createStatement(); 153 | ResultSet rs = s.executeQuery(sql); 154 | 155 | while (rs.next()) { 156 | String firstName = rs.getString(1); 157 | String lastName = rs.getString(2); 158 | String email = rs.getString(3); 159 | double salary = rs.getDouble(4); 160 | Employee emp = new Employee(firstName, lastName, email, salary); 161 | employees.add(emp); 162 | } 163 | } catch (SQLException e) { 164 | e.printStackTrace(); 165 | } 166 | 167 | return employees; 168 | } 169 | 170 | 171 | public Employee getEmployee(int id) { 172 | Connection conn = MariaDbConnection.getConnection(); 173 | String sql = "SELECT first_name, last_name, email, salary FROM employee WHERE id=?"; 174 | 175 | String firstName = null; 176 | String lastName = null; 177 | String email = null; 178 | double salary = 0.0; 179 | int count = 0; 180 | 181 | try { 182 | PreparedStatement ps = conn.prepareStatement(sql); 183 | ps.setInt(1, id); 184 | 185 | ResultSet rs = ps.executeQuery(); 186 | 187 | while (rs.next()) { 188 | count++; 189 | firstName = rs.getString(1); 190 | lastName = rs.getString(2); 191 | email = rs.getString(3); 192 | salary = rs.getDouble(4); 193 | } 194 | } catch (SQLException e) { 195 | e.printStackTrace(); 196 | } 197 | 198 | if (count==1) { 199 | return new Employee(firstName, lastName, email, salary); 200 | } 201 | else { 202 | return null; 203 | } 204 | } 205 | 206 | public void persist(Employee emp) { 207 | Connection conn = MariaDbConnection.getConnection(); 208 | String sql = "INSERT INTO employee (first_name, last_name, email, salary) VALUES (?, ?, ?, ?)"; 209 | try { 210 | PreparedStatement ps = conn.prepareStatement(sql); 211 | ps.setString(1, emp.getFirstName()); 212 | ps.setString(2, emp.getLastName()); 213 | ps.setString(3, emp.getEmail()); 214 | ps.setDouble(4, emp.getSalary()); 215 | 216 | ps.executeUpdate(); 217 | } catch (SQLException e) { 218 | e.printStackTrace(); 219 | } 220 | } 221 | } 222 | ``` 223 | 224 | The class contains three methods: 225 | - `getAllEmployees()` returns a list of all employees in the database. 226 | - `getEmployee(int id)` returns the employee with the given id. 227 | - `persist(Employee emp)` persists the given employee in the database. 228 | 229 | The class uses the connection object from the datasource class. 230 | 231 | The `getAllEmployees()` method first creates a list of employees. Then it executes a query to retrieve all employees from the database. 232 | For each employee, it creates an Employee object and adds it to the list. Finally, it returns the list. 233 | 234 | As a `SELECT` query always returns a result set, we use a `ResultSet` object to store the result of the query. A `ResultSet` object maintains a _SQL cursor_ pointing to its current row of data. Initially, the cursor is positioned before the first row. The `next()` method moves the cursor to the next row, and because it returns false when there are no more rows in the `ResultSet` object, it can be used in a while loop to iterate through the result set. An SQL cursor is a database object used to retrieve, manipulate, and navigate through records in a result set from a SQL query. Cursors are especially useful when you need to work with one row at a time within a result set, allowing for more granular control over data manipulation. Cursor in a text editor marks the position (in a file) where modifications to the text are being done. In a similar way, cursor in SQL marks on what row (of the SQL table) the operation is being done. 235 | 236 | For each row, the data is retrieved using the `getString()` and `getDouble()` methods. 237 | Based on this data, an `Employee` object is created and added to the list of employees that is eventually returned. 238 | 239 | The `getEmployee(int id)` method treats the SQL statement slightly differently. 240 | Instead of using a `Statement` object to pass the query to the database, we use a `PreparedStatement` object. 241 | A prepared statement is an SQL statement with a placeholder for one or more of the parameters. In this case, there is one parameter, the id. 242 | The placeholder is represented by a question mark. The prepared statement is first created by a `Statement` object. The `Statement` object is, in turn, created 243 | using the connection object given by the datasource class. 244 | 245 | Before we are able to execute the prepared statement, we have to set the value of the placeholder. This is done using the `setInt()` method. The first parameter is the index of the placeholder. The second parameter is the value of the placeholder. 246 | There are dedicated methods for different types of placeholders. 247 | If the type of the placeholder is `String`, we use the `setString()` method. If the type of the placeholder is `double`, we use the `setDouble()` method. 248 | 249 | The prepared statement is executed using the `executeQuery()` method. The `executeQuery()` method then returns a `ResultSet` object, 250 | which can be iterated in a similar way as before. 251 | 252 | This time, since we are only interested in one employee, we use a counter to check whether there is only one employee with the given id. 253 | If there is exactly one employee with the given id, we return the employee. As the id is the primary key of the employee table, 254 | there should never be a situation where the result set contains more than one employee. However, it is possible that there is no employee with the given id. 255 | In this case, we return null. 256 | 257 | The `persist(Employee emp)` method also creates a prepared statement. This time, the prepared statement is an insert statement that contains four placeholders. 258 | As before, the placeholders are replaced by the values of the `Employee` object. 259 | Since this is a data modification statement, we use the `executeUpdate()` method instead of the `executeQuery()` method. 260 | The `executeUpdate()` method always returns the number of rows that were affected by the statement. In our example, the number of affected rows is always 1. 261 | 262 | Finally, it is worth noting that in the previous example we had just one entity class that was well represented by a single DAO class. 263 | If we had more entity classes, we might want to create a separate DAO class for each entity class. Alternatively, we could create a single DAO class that 264 | contains methods for all entity classes. 265 | 266 | 267 | ## 7.2.5. The application class 268 | 269 | The application class is the class that contains the main method. It looks like this: 270 | 271 | ```java 272 | package application; 273 | 274 | import entity.*; 275 | import dao.*; 276 | import java.util.*; 277 | 278 | public class CompanyApp { 279 | 280 | public static void main(String[] args) { 281 | 282 | EmployeeDao empdao = new EmployeeDao(); 283 | 284 | List employees = empdao.getAllEmployees(); 285 | for (Employee emp : employees) { 286 | System.out.println(emp.getFirstName() + " " + emp.getLastName()); 287 | } 288 | 289 | Employee emp = empdao.getEmployee(2); 290 | System.out.println(emp.getFirstName() + " " + emp.getLastName()); 291 | 292 | empdao.persist(new Employee("Viivi", "Puro", "viivip@mymail.fi", 8300.00)); 293 | 294 | datasource.MariaDbConnection.terminate(); 295 | } 296 | } 297 | 298 | ``` 299 | 300 | The simple application class just demonstrates the use of the `EmployeeDao` class. 301 | 302 | The first thing we do is to create an instance of the `EmployeeDao` class. This is done using the `new` operator. 303 | The `EmployeeDao` class has a default constructor, so we don't have to pass any parameters to the constructor. 304 | 305 | The `getAllEmployees()` method is called and the result is stored in a list of employees. The list is then iterated and the first and last names of each employee are printed. 306 | 307 | Next, we find the data of a single employee. The `getEmployee(int id)` method is called and the result is stored in an `Employee` object. 308 | Then, the first and last names of the employee are printed. 309 | 310 | Finally, we create a new employee and persist it in the database. The `EmployeeDao` class has a `persist(Employee emp)` method for this purpose. 311 | The new employee is created using the `Employee` constructor. The constructor takes four parameters: the first name, the last name, the email and the salary. 312 | The new employee is then passed to the `persist(Employee emp)` method. 313 | 314 | Finally, we terminate the connection to the database. This is done using the `terminate()` method of the `MariaDbConnection` class. 315 | The call to the `terminate()` method is not strictly necessary, but it is good practice to close the connection to the database when we are done with it. 316 | The resources would be released anyway when the application terminates. 317 | 318 | ## 7.2.6. Error handling 319 | 320 | In the `EmployeeDao` class, we have used a try-catch block to handle exceptions. The try-catch block is used to catch exceptions that are thrown by the code inside the try block. 321 | 322 | As we connect to a database, various exceptions can occur. For example, the database might not be available. In this case, an exception is thrown and the application terminates. 323 | Also, if the SQL statement is invalid, an exception is thrown. 324 | In the code above, we only have a simple catch block that catches all exceptions. In a real application, we would have to handle the different exceptions separately. 325 | Moreover, we should replace the `printStackTrace()` method call with a more meaningful error handling procedure. 326 | 327 | ## Assignment: A database-enhanced currency converter 328 | 329 | In the previous assignment, you created a database for storing information about currencies. Now, you will expand your earlier currency converter application so that it uses the database. 330 | 331 | First, redesign your project structure so that you have separate packages for: 332 | - the user interface (the view of the application) 333 | - the application logic (the controller of the application) 334 | - the data access layer (the DAO classes) 335 | - the entity classes (the classes that represent the data in the database. These are probably one-to-one to your model classes.) 336 | - the datasource class (the class that handles the connection to the database) 337 | 338 | You may modify the existing project, or create a new one. If you work on the existing project, consider creating a new branch for the new version. 339 | 340 | Make your application connect to the database. For inspiration, check the `MariaDbConnection` class in the example above. 341 | 342 | In the earlier application, your controller class is responsible for reacting to the button presses that initiate the conversion. Now, add a `CurrencyDao` class into the `dao` package in your project. The class is responsible for communicating with the database. Add a method for retrieving the exchange rate of a currency from the database, and embed the required SQL statement into the method. The method should take the abbreviation of the currency as a parameter, and return the exchange rate as a double value. 343 | Modify your controller method so that it uses the values fetched from the database instead of the hard-coded values. (By the way, now is a good time to delete the hard-coded currencies from your code, if you have them.) 344 | 345 | Prepare for errors. For instance, if the database is not available, your application should not crash. Instead, it should display an error message to the user. While testing, an easy way to simulate an error is to change the server name in the `MariaDbConnection` class to something that is not a valid server name. 346 | 347 | At this point, your application should work as before, but the exchange rates are fetched from the database. 348 | 349 | For this assignment, you get points as follows (PLEASE NOTE! You need to inform in your answer what of the following items (1–6) you have done, otherwise you will not get points for the assignment): 350 | 1. You have refactored your code to reflect the new project structure. (1 point) 351 | 2. Your application successfully connects to the database. (1 point) 352 | 3. You have created a `CurrencyDao` class that contains a method for fetching the exchange rate of a currency from the database. (1 point) 353 | 4. Your Controller class uses the aforementioned method to successfully fetch the exchange rate. (1 point) 354 | 5. Your application works as before (meaning: as described in the earlier GUI assignment), except for the fact that the exchange rates are fetched from the database. (1 point) 355 | 6. Your application displays an appropriate error message in the user interface if the database is not available. (1 point) 356 | 357 | --- 358 | _This learning material has been produced with assistance from OpenAI's ChatGPT-4 and GitHub Copilot. These large language models have provided suggestions and solutions that have assisted the author in producing and supplementing the material. While their contribution has been significant, the final responsibility for the content and its correctness resides with the author._ -------------------------------------------------------------------------------- /7.4_Association_in_object-relational_mapping.md: -------------------------------------------------------------------------------- 1 | # 7.4. Association in object-relational mapping 2 | 3 | In the previous submodule, we learned how to map a class to a table in the database. In this submodule, we will learn how to map associations between classes to tables in the database. 4 | 5 | This time we let Hibernate generate the database tables for us. We will use the same example as in the previous submodule, but this time we will add a new class, `Department`, and an association between `Employee` and `Department`. 6 | 7 | 8 | ## 7.4.1. Association, navigability, and cardinality 9 | 10 | Association means that two classes are related to each other. In the example, `Employee` and `Department` are associated with each other. 11 | 12 | Navigability means that we can navigate from one class to another class. For example, if an `Employee` object knows which `Department` it belongs to, we can navigate from `Employee` to `Department`. If a `Department` object knows which `Employee`s belong to it, we can navigate from `Department` to `Employee`. An association can be unidirectional or bidirectional. 13 | 14 | Cardinality means how many objects of one class can be associated with an object of another class. For example, an `Employee` can belong to only one `Department`, but a `Department` can have many `Employee`s. When annotating associations, it is vital to specify the cardinality correctly. If we specify the cardinality incorrectly, Hibernate will not be able to generate the database tables correctly. 15 | 16 | Let's consider the following example shown in the UML class diagram: 17 | 18 | ![Associated classes](images/associated_classes.PNG) 19 | 20 | In the image, the arrowhead displays the navigability of the association. The arrowhead points from `Employee` to `Department`, which means that we can navigate from `Employee` to `Department`, or that the `Employee`object knows which `Department` it belongs to. Other solutions are also possible at the Java level: for instance we could have a `Department` object that knows which `Employee`s belong to it. In this case, the arrowhead would point from `Department` to `Employee`. Or, we could make the association bi-directional, so that both `Employee` and `Department` know about each other. In this case, the arrowhead would point from both `Employee` and `Department` to each other. Irrespective of how we choose the navigability of the association, Hibernate will generate the database tables correctly, provided that we specify the cardinality correctly. 21 | 22 | The symbols `*` and `1` in the diagram indicate the cardinality. That is, one department per employee, and many employees per department. 23 | 24 | ## 7.4.2. Preparing the database 25 | 26 | As we don't want to damage our existing `company` database, we create a new database for this example. Let's call the database `company2`. 27 | 28 | First, we create the database in the SQL editor (such as HeidiSQL) or command prompt: 29 | 30 | ```sql 31 | CREATE DATABASE company2; 32 | ``` 33 | 34 | Then we give our existing user account the privileges to perform CRUD operations on the new database: 35 | 36 | ```sql 37 | GRANT SELECT,INSERT,UPDATE,DELETE ON company2.* TO 'appuser'@'localhost'; 38 | ``` 39 | 40 | As we let Hibernate create (and drop) tables this time, we also need to grant the privileges for that: 41 | ```sql 42 | GRANT CREATE, DROP ON company2.* TO 'appuser'@'localhost'; 43 | ``` 44 | 45 | Now we are ready to start writing the application. We can copy the previous example of submodule 7.3. and modify it to suit our needs. 46 | 47 | 48 | ## 7.4.3. Persisting the data 49 | 50 | The first thing to do is to tell Hibernate that we want the database tables to be created automatically. We do this by modifying the 51 | property with the name `jakarta.persistence.schema-generation.database.action` in the `persistence.xml` file. We change the value from `none` to `drop-and-create`: 52 | 53 | ```xml 54 | 55 | ``` 56 | 57 | Then we write the DAO classes for our application. This time, we add a new instance variable into the `Employee` class that contains a reference 58 | to the `Department` object that the employee belongs to: 59 | 60 | ```java 61 | package entity; 62 | 63 | import jakarta.persistence.*; 64 | 65 | @Entity 66 | public class Employee { 67 | 68 | @Id 69 | @GeneratedValue(strategy=GenerationType.IDENTITY) 70 | private int id; 71 | private String firstName; 72 | private String lastName; 73 | private String email; 74 | private double salary; 75 | @ManyToOne 76 | private Department department; 77 | 78 | public Employee(String firstName, String lastName, String email, double salary, Department department) { 79 | this.firstName = firstName; 80 | this.lastName = lastName; 81 | this.email = email; 82 | this.salary = salary; 83 | this.department = department; 84 | } 85 | 86 | public Employee() { 87 | } 88 | 89 | public int getId() { 90 | return id; 91 | } 92 | 93 | public void setId(int id) { 94 | this.id = id; 95 | } 96 | 97 | public String getFirstName() { 98 | return firstName; 99 | } 100 | 101 | public void setFirstName(String firstName) { 102 | this.firstName = firstName; 103 | } 104 | 105 | public String getLastName() { 106 | return lastName; 107 | } 108 | 109 | public void setLastName(String lastName) { 110 | this.lastName = lastName; 111 | } 112 | 113 | public String getEmail() { 114 | return email; 115 | } 116 | 117 | public void setEmail(String email) { 118 | this.email = email; 119 | } 120 | 121 | public double getSalary() { 122 | return salary; 123 | } 124 | 125 | public void setSalary(double salary) { 126 | this.salary = salary; 127 | } 128 | } 129 | 130 | ``` 131 | 132 | The reference to the `Department` object is annotated with the `@ManyToOne` annotation. This annotation tells Hibernate that the association is many-to-one, that is, many employees can belong to one department. This time, as we let Hibernate generate the database tables, we don't need to specify the names of the columns. Hibernate will automatically use the instance variable names as the column names. Also, the table name will be taken directly from the class name. 133 | 134 | It may be self-evident, but it is worth mentioning that all references to other classes must be proper object references, not just integers or strings. For example, the `department` instance variable in the `Employee` class is a reference to a `Department` object, not just an integer that contains the department id. 135 | 136 | Next, we add the new class, `Department` to the `entity`package that now contains two classes. The `Department` class looks like this: 137 | 138 | ```java 139 | package entity; 140 | 141 | import jakarta.persistence.*; 142 | 143 | @Entity 144 | public class Department { 145 | 146 | @Id 147 | private int id; 148 | private String name; 149 | 150 | public Department(int id, String name) { 151 | this.id = id; 152 | this.name = name; 153 | } 154 | 155 | public Department() { 156 | } 157 | 158 | public String getName() { 159 | return name; 160 | } 161 | 162 | public void setName(String name) { 163 | this.name = name; 164 | } 165 | 166 | public int getId() { 167 | return id; 168 | } 169 | 170 | public void setId(int id) { 171 | this.id = id; 172 | } 173 | } 174 | 175 | ``` 176 | 177 | This class contains only the mandatory annotations. The `@Id` annotation tells Hibernate that the `id` instance variable is the primary key of the table. The `@Entity` annotation tells Hibernate that this class is an entity class. 178 | 179 | As the Department objects do not know anything about the Employee objects, we don't need to add any annotations about the association to the `Department` class. 180 | 181 | We also write a new DAO class, `DepartmentDAO` that contains the methods for saving and retrieving `Department` objects: 182 | 183 | ```java 184 | package dao; 185 | 186 | import entity.*; 187 | import jakarta.persistence.EntityManager; 188 | 189 | public class DepartmentDao { 190 | 191 | public void persist(Department dept) { 192 | EntityManager em = datasource.MariaDbJpaConnection.getInstance(); 193 | em.getTransaction().begin(); 194 | em.persist(dept); 195 | em.getTransaction().commit(); 196 | } 197 | 198 | public Department find(int id) { 199 | EntityManager em = datasource.MariaDbJpaConnection.getInstance(); 200 | Department dept = em.find(Department.class, id); 201 | return dept; 202 | } 203 | } 204 | ``` 205 | 206 | For simplicity, we omitted the other methods from the DAO classes, such as the ones for updating and deleting objects. 207 | 208 | Finally, the updated `CompanyApp` class in the `application` package inserts a few employees into the database: 209 | 210 | ```java 211 | package application; 212 | 213 | import entity.*; 214 | import dao.*; 215 | public class CompanyApp { 216 | public static void main(String[] args) { 217 | 218 | EmployeeDao empdao = new EmployeeDao(); 219 | DepartmentDao deptdao = new DepartmentDao(); 220 | 221 | Department d1 = new Department(1, "Sales"); 222 | Department d2 = new Department(2, "Marketing"); 223 | 224 | deptdao.persist(d1); 225 | deptdao.persist(d2); 226 | 227 | empdao.persist(new Employee("Viivi", "Puro", "viivip@mymail.fi", 7300.00, d2)); 228 | empdao.persist(new Employee("Tero", "Koski", "tero.koski@mymail.fi", 3750.00, d1)); 229 | empdao.persist(new Employee("Ahmed", "Bakir", "ahmed.bakir@mymail.fi", 4800.00, d1)); 230 | 231 | } 232 | } 233 | ``` 234 | 235 | As we run the application, we get the following console output: 236 | ``` 237 | Hibernate: alter table if exists Employee drop foreign key if exists FK14tijxqry9ml17nk86sqfp561 238 | Hibernate: drop table if exists Department 239 | Hibernate: drop table if exists Employee 240 | Hibernate: create table Department (id integer not null, name varchar(255), primary key (id)) engine=InnoDB 241 | Hibernate: create table Employee (id integer not null auto_increment, email varchar(255), firstName varchar(255), lastName varchar(255), salary float(53) not null, department_id integer, primary key (id)) engine=InnoDB 242 | Hibernate: alter table if exists Employee add constraint FK14tijxqry9ml17nk86sqfp561 foreign key (department_id) references Department (id) 243 | Hibernate: insert into Department (name, id) values (?, ?) 244 | Hibernate: insert into Department (name, id) values (?, ?) 245 | Hibernate: insert into Employee (department_id, email, firstName, lastName, salary) values (?, ?, ?, ?, ?) 246 | Hibernate: insert into Employee (department_id, email, firstName, lastName, salary) values (?, ?, ?, ?, ?) 247 | Hibernate: insert into Employee (department_id, email, firstName, lastName, salary) values (?, ?, ?, ?, ?) 248 | ``` 249 | 250 | We see that a lot has happened behind the scenes: 251 | 252 | 1. Hibernate has dropped the old tables from the database (if they existed). 253 | 2. Hibernate has created the tables for the `Employee` and `Department` classes. The foreign key field `department_id` has been added to the `Employee` table. 254 | 3. Hibernate has inserted the data into the tables. 255 | 256 | The content of the database tables after the execution looks as follows: 257 | 258 | ``` 259 | +----+-----------+ 260 | | id | name | 261 | +----+-----------+ 262 | | 1 | Sales | 263 | | 2 | Marketing | 264 | +----+-----------+ 265 | 266 | +----+-----------------------+-----------+----------+--------+---------------+ 267 | | id | email | firstName | lastName | salary | department_id | 268 | +----+-----------------------+-----------+----------+--------+---------------+ 269 | | 1 | viivip@mymail.fi | Viivi | Puro | 7300 | 2 | 270 | | 2 | tero.koski@mymail.fi | Tero | Koski | 3750 | 1 | 271 | | 3 | ahmed.bakir@mymail.fi | Ahmed | Bakir | 4800 | 1 | 272 | +----+-----------------------+-----------+----------+--------+---------------+ 273 | ``` 274 | 275 | In the example above, the objects have to be persisted in the database in the correct order. First, the `Department` objects have to be persisted, and then the `Employee` objects. If we try to persist the `Employee` objects first, we get an exception 276 | in reaction to the error code sent by the database server. The error would be caused by a foreign key constraint violation, as the `department_id` field in the `Employee` table is a foreign key that references the `id` field in the `Department` table. 277 | 278 | It is worth noting that JPA allows automatic cascading of persist operations. This means that if we persist an `Employee` object, the `Department` object that is referenced by the `Employee` object is also persisted automatically. This is a very useful feature, but it is not enabled by default. We can enable it by adding the `cascade` attribute to the `@ManyToOne` annotation in the `Employee` class: 279 | 280 | ```java 281 | @ManyToOne(cascade = CascadeType.PERSIST) 282 | ``` 283 | 284 | On this course, we will not use this feature, but it is good to know that it exists. 285 | 286 | 287 | 288 | ## 7.4.3. Retrieving objects 289 | 290 | In the previous example, objects were persisted in the database. Now, we want to retrieve the objects from the database. We can do this by writing a DAO method `find()` in the `EmployeeDao` class that uses the EntityManager's `find` method : 291 | 292 | ```java 293 | public Employee find(int id) { 294 | EntityManager em = datasource.MariaDbJpaConnection.getInstance(); 295 | Employee emp = em.find(Employee.class, id); 296 | return emp; 297 | } 298 | ``` 299 | 300 | We can test the `find()` method by adding the following code to the `CompanyApp` class: 301 | 302 | ```java 303 | Employee emp = empdao.find(1); 304 | System.out.println(emp.getFirstName() + " " + emp.getLastName()); 305 | ``` 306 | 307 | The output of the application is as follows: 308 | 309 | ``` 310 | Viivi Puro 311 | ``` 312 | 313 | The DAO method did not create a `SELECT` statement to fetch the individual's data from the database. The reason for this is that the `Employee` object was already managed by the `EntityManager`. `EntityManager` keeps track of all the objects that it has fetched from (or persisted to) the database. If we try to fetch an object that is already managed by the `EntityManager`, the EntityManager returns the managed object instead of fetching it from the database. 314 | 315 | If we wanted to detach the object from the `EntityManager` (and, consequtively, force `EntityManager` to fetch the data from the database the next time the object's data was requested), we could use the `EntityManager.detach()` method. The following DAO method detaches an object from the `EntityManager`: 316 | 317 | ```java 318 | public void detach(Employee emp) { 319 | EntityManager em = datasource.MariaDbJpaConnection.getInstance(); 320 | em.detach(emp); 321 | } 322 | ``` 323 | 324 | Now, let's try to fetch the same object again: 325 | ```java 326 | empdao.detach(empdao.find(1)); 327 | Employee emp = empdao.find(1); 328 | System.out.println(emp.getFirstName() + " " + emp.getLastName()); 329 | ``` 330 | 331 | This time, as the object was detached from the `EntityManager`, the `EntityManager` fetched the data from the database. The output of the application is as follows: 332 | 333 | ``` 334 | Hibernate: select e1_0.id,d1_0.id,d1_0.name,e1_0.email,e1_0.firstName,e1_0.lastName,e1_0.salary from Employee e1_0 left join Department d1_0 on d1_0.id=e1_0.department_id where e1_0.id=? 335 | Viivi Puro 336 | ``` 337 | 338 | Normally, the call of the `detach()` method is not necessary. The previous example is just for the sake of demonstration. 339 | 340 | If you look carefully at the previous SQL statement, you notice that there is a `LEFT JOIN` operation to make the `SELECT` statement fetch not only the `Employee` object's data but also the `Department`object's data. This is because the `Employee` class has a `@ManyToOne` association with the `Department` class. For `@ManyToOne` associations, Hibernate by default uses eager fetching, which means that the data of the associated object is fetched automatically. Eager fetching makes programming easier, but it can cause performance problems if the associated object is not needed in the application. Because of that, 341 | lazy fetching is often preferred in applications where performance is important. We can change the fetching strategy of the `@ManyToOne` association to lazy fetching by adding the `fetch` attribute to the `@ManyToOne` annotation: 342 | ```java 343 | @ManyToOne(fetch = FetchType.LAZY) 344 | ``` 345 | In this case, the `SELECT` statement generated by the `find()` method would only create a one-table `SELECT` statement that fetches the `Employee` object's data from the database. The `Department` object's data would need to be separately fetched only at the time it is needed. Hibernate can do this automatically, if we are in the scope of the `EntityManager`. If we are not in the scope of the `EntityManager`, we need to use the `EntityManager.find()` method to fetch the `Department`'s data. 346 | 347 | The choice between eager and lazy loading is not always easy. On this course, you may prefer to use eager loading, as it eliminates the need to write additional code to fetch the associated objects. 348 | 349 | Now that eager loading was on, we can retrieve the data of the related department simply by using the getter method of the `department` field: 350 | 351 | ```java 352 | System.out.println(emp.getDepartment().getName()); 353 | ``` 354 | 355 | The output is as follows: 356 | 357 | ``` 358 | Marketing 359 | ``` 360 | 361 | 362 | 363 | ## 7.4.3. Other types of associations 364 | 365 | So far, we have covered association of type `@ManyToOne`. There are other types of associations as well: 366 | 367 | * `@OneToOne` - one-to-one association 368 | * `@OneToMany` - one-to-many association 369 | * `@ManyToMany` - many-to-many association 370 | 371 | Also, you can make each association either uni-directional or bi-directional. A bidirectional association means that the association is defined in both classes. For example, in the `Employee` class, we have a `@ManyToOne` association with the `Department` class. In the `Department` class, we can also have a `@OneToMany` association with the `Employee` class. This way, we can navigate from the `Employee` object to the `Department` object and vice versa. 372 | 373 | The use of a bi-directional association means that you have to specify the owner of the association. The owner is the class that is in charge of persisting the information about the relationship of the associated objects. 374 | 375 | These other association types, as well as bi-directional associations, will not be covered on this course, but feel free to explore them on your own. 376 | 377 | 378 | ## Assignment: Storing currency exchange transactions 379 | 380 | In the previous assignment, you modified the currency converter application so that it uses JPA to store the currency data in the database. Now, modify the application so that it also stores the currency exchange transactions in the database. An example of a transaction is: _convert 100 euros to Swedish crowns_. 381 | 382 | For the modification, it is advisable to make a new branch in your Git repository. You may create a new database for the application, or you may choose to use the same database as in the previous assignment. 383 | 384 | 385 | Use the following class structure for your entities: 386 | 387 | ![Class diagram](images/jpa_entities_uml.png) 388 | 389 | As you see from the diagram, each `Transaction` object is associated to two `Currency` objects: the source currency and the target currency. 390 | 391 | It is enough to store the transactions in the database. You do not need to modify the application so that it would read the transactions from the database. 392 | In general, the observed behaviour of the application should not change from the previous application. The only difference is that the transactions are now stored in the database. 393 | 394 | This time, let JPA construct the schema for you based on your annotations. You can do this by modifying the corresponding property in the `persistence.xml` file: 395 | 396 | ```xml 397 | 398 | ``` 399 | 400 | Each transaction has a transaction id. The transaction id should become the primary key in the database table. Let the database server generate the transaction id automatically. 401 | 402 | 403 | Use HeidiSQL to check that the database schema was created correctly. Pay attention to the primary keys and foreign keys: are they correct? 404 | 405 | For this assignment, you get points in the following way: 406 | 407 | - Both `Currency` and `Transaction` classes are implemented and annotated: 1 point 408 | - The application successfully generates the database schema: 1 point 409 | - There is a new DAO class for the `Transaction` class with a method for storing a transaction in the database: 1 point 410 | - The handler method for the conversion button calls the new DAO method to store the transaction in the database: 1 point 411 | - The application successfully stores the transactions in the database: 1 point 412 | - The transaction id is generated automatically by the database server: 1 point 413 | 414 | --- 415 | _This learning material has been produced with assistance from OpenAI's ChatGPT-4 and GitHub Copilot. These large language models have provided suggestions and solutions that have assisted the author in producing and supplementing the material. While their contribution has been significant, the final responsibility for the content and its correctness resides with the author._ 416 | -------------------------------------------------------------------------------- /8.2_TDD.md: -------------------------------------------------------------------------------- 1 | # Test-Driven Development 2 | 3 | [Test-driven development](https://en.wikipedia.org/wiki/Test-driven_development) is a software development process that's based on constructing a piece of software in small iterations. In test-driven software development, the first thing a programmer always does is write an automatically-executable test, which tests a single piece of the computer program. 4 | The test will not pass because the functionality that satisfies the test, i.e., the part of the computer program to be examined, is missing. Once the test has been written, functionality that meets the test requirements is added to the program. The tests are then run again. If all tests pass, a new test is added, or alternatively, if the tests fail, the already-written program is corrected. If necessary, the internal structure of the program will be corrected or refactored, so that the functionality of the program remains the same, but the structure becomes clearer. 5 | 6 | Test-driven software development consists of five steps that are repeated until the functionality of the program is complete. 7 | 8 | 1. Write a test. The programmer decides which program functionality to test and writes a test for it. 9 | 2. Run the tests and check if the tests pass. When a new test is written, the tests are run. If the test passes, the test is most likely erroneous and should be corrected—the test should only test functionality that hasn't yet been implemented. 10 | 3. Write the functionality that meets the test's requirements. The programmer implements functionality that only meets the test requirements. Note: this doesn't do things that the test does not require - functionality is only added in small increments. 11 | 4. Perform the tests. If the tests fail, there is likely to be an error in the functionality written. Correct the functionality—or, if there is no error in the functionality, fix the latest test that was performed. 12 | 5. Repair the internal structure of the program. As the size of the program increases, its internal structure is adjusted as needed. Methods that are too long are broken down into multiple parts and classes representing concepts are isolated. The tests are not modified, but are instead used to verify the correctness of the changes made to the program's internal structure—if a change in the program structure changes the functionality of the program, the tests will produce a warning and the programmer can remedy the situation. 13 | 14 | ```mermaid 15 | flowchart TD 16 | wrtst(Write Test) --> exetst(Execute Test) --> tsttst{Test Pass?} -->|Yes|wrtst 17 | tsttst -->|No|refcode(Refactor the code) --> exetst2(Execute Test) --> tsttst2{Test Pass?} -->|No|refcode 18 | tsttst2 -->|Yes|cleancode(Code Cleanup) 19 | ``` 20 | 21 | ## Creating the program in small steps 22 | 23 | 1. Create a test that tests some feature that will be added to the program. 24 | 2. Run the test. It should not pass. 25 | - If the test passes, move to step 1. 26 | 3. Develop the program so that it has the functionality required to pass the test. 27 | 4. Run the tests. 28 | - If the tests don’t pass, move to step 3 and further develop the functionality. 29 | 5. Refactor 30 | - If the program is ready, stop ... 31 | - Otherwise, go to step 1. 32 | 33 | ## An example 34 | 35 | ### Preparation: Create a skeleton for the class to be tested 36 | 37 | In this phase we design methods and their parameters (API - Application Programmers Interface), but **not** the implementation. Code compiles, but does nothing. 38 | ```Java 39 | public class Calculator { 40 | private int result; 41 | 42 | public void clear() { 43 | // not yet implemented 44 | } 45 | public void add(int n) { 46 | // not yet implemented 47 | } 48 | public void sub(int n) { 49 | // not yet implemented 50 | } 51 | public void mul(int n) { 52 | // not yet implemented 53 | } 54 | public void div(int n) { 55 | // not yet implemented 56 | } 57 | public int giveResult() { 58 | return result; 59 | } 60 | } 61 | ``` 62 | 63 | ### Device a test case and create a test method 64 | 65 | Create a separate tester class for the class to be tested. Don't insert any testing code to the class to be tested. Then write to the tester class methods that invoke those methods to be tested (be careful with the parameters). For every method to be tested, create one or more tester methods (e.g., one for each test case). According to TDD principles, first run tests so that the result is failure (to check that the test skeleton works). 66 | 67 | Using JUnit 5 this tester class could be something like this: 68 | 69 | ```Java 70 | import org.junit.jupiter.api.Test; 71 | 72 | import static org.junit.jupiter.api.Assertions.fail; 73 | 74 | public class CalculatorTest { 75 | @Test 76 | void testClear() { 77 | fail("Not yet implemented"); 78 | } 79 | @Test 80 | void testAdd() { 81 | fail("Not yet implemented"); 82 | } 83 | @Test 84 | void testSub() { 85 | fail("Not yet implemented"); 86 | } 87 | @Test 88 | void testMul() { 89 | fail("Not yet implemented"); 90 | } 91 | @Test 92 | void testDiv() { 93 | fail("Not yet implemented"); 94 | } 95 | @Test 96 | void testGetResult() { 97 | fail("Not yet implemented"); 98 | } 99 | } 100 | ``` 101 | 102 | The logic behind testing method: 103 | - **arrange**: initialize test settings (e.g., create objects, etc.) 104 | - **act**: invoke the method to be tested with test case parameters 105 | - **assert**: check whether we got the expected result 106 | 107 | #### Annotations 108 | 109 | When you have many test methods in the test class, pre- and postprocessing operations for testing methods can be merged 110 | - `@BeforeEach` annotation 111 | - annotates method which is invoked **before every** testing method 112 | - used e.g., initializing variables, to open files, etc. 113 | - `@AfterEach` annotation 114 | - annotates method which is invoked **after every** testing method 115 | - used e.g., for closing files 116 | - `@BeforeAll` annotation 117 | - annotates method which is invoked **only once before** invoking the first testing method 118 | - used e.g., for resource allocation, opening files 119 | - `@AfterAll` annotation 120 | - annotates method which is invoked **only once after** invoking the last testing method 121 | - used e.g., for releasing resources, closing files 122 | - Complete list can found [here](https://junit.org/junit5/docs/current/api/), look Package org.junit.jupiter.api 123 | 124 | #### Annotations for testing methods 125 | 126 | Annotate testing methods with `@Test` or `@ParametrizedTest` annotations. Testing methods never return any values, returning type should be `void`. Visibility is package wide by default. There is no need to change it, so leave it without `private/protected/public` keywords. 127 | > A variable or method declared without any access control modifier is available to any other class in the same package. The default modifier cannot be used for methods, fields in an interface. 128 | 129 | JUnit invokes testing methods automatically and in random order. There is no need to implement `main()` method. 130 | 131 | It is possible to annotate testing method so that it is not invoked by `@Disabled("reason why we don't use this")`. 132 | 133 | You can have `private` methods inside testing class, they don't have any JUnit annotations, and they are executed when explicitly invoked. 134 | 135 | #### Check results using assertions 136 | 137 | Implement the test condition which should be true after invoking the method to be tested. If the test condition is not true, test was not successful. 138 | 139 | - `fail(String message)` 140 | - informs that the test was not successful 141 | - `assertTrue(boolean condition, String message)` 142 | - `assertFalse(boolean condition, String message)` 143 | - check whether the condition parameter given is true/false 144 | - `assertEquals(Object expected, Object actual)` 145 | - `assertEquals(Object expected, Object actual, String message)` 146 | - check if the parameters expected and actual are the same 147 | - if not, reports the message given 148 | - if comparing floating point values, the third parameter is the precision used in comparison (delta) 149 | - `assertEquals(100.0, result, 0.0)` → exact match 150 | - `assertEquals(100.0, result, 0.1)` → match with a precision of 0.1 151 | - `assertArrayEquals(Object[] expecteds, Object[] actuals)` 152 | - `assertArrayEquals(Object[] expecteds, Object[] actuals, String msg)` 153 | - every element of an array must be the same 154 | 155 | It is possible to limit the testing time, e.g. `@Timeout(value=2, unit=TimeUnit.SECONDS)`. Exception can be also a valid result, `assertThrows()`. Complete list can found [here](https://junit.org/junit5/docs/current/api/). 156 | 157 | ```Java 158 | import org.junit.jupiter.api.*; 159 | import static org.junit.jupiter.api.Assertions.*; 160 | 161 | public class CalculatorTest { 162 | private static Calculator c; 163 | 164 | @BeforeAll 165 | static void setupBeforeClass() throws Exception { 166 | c = new Calculator(); // same object in every test 167 | } 168 | @BeforeEach 169 | void setUp() { 170 | c.clear(); // clear the calculator before every test 171 | } 172 | @Test 173 | void testClear() { 174 | System.out.println("Clear"); 175 | c.clear(); 176 | assertEquals(0, c.giveResult(), "Clearing was not successful"); 177 | } 178 | @Test 179 | void testAdd() { 180 | System.out.println("Add"); 181 | c.add(2); 182 | assertEquals(2, c.giveResult(), "Addition was not succesful"); 183 | c.add(2); 184 | assertEquals(4, c.giveResult(), "Addition was not succesful"); 185 | } 186 | @Test 187 | void testSub() { 188 | fail("Not yet implemented"); 189 | } 190 | @Test 191 | void testMul() { 192 | fail("Not yet implemented"); 193 | } 194 | @Test 195 | void testDiv() { 196 | fail("Not yet implemented"); 197 | } 198 | @Test 199 | void testGetResult() { 200 | fail("Not yet implemented"); 201 | } 202 | } 203 | ``` 204 | 205 | > For curious: `static import` is a feature introduced in the Java programming language that allows members (fields and methods) which have been scoped within their container class as `public static`, to be used in Java code without specifying the class in which the field has been defined. 206 | 207 | `testAdd()` methods fails tests, because it has not been implemented yet. `textClear()` succeeds, because integer type instance variables have zero as an initial value. 208 | ```text 209 | Add 210 | org.opentest4j.AssertionFailedError: Addition was not succesful ==> 211 | Expected :2 212 | Actual :0 213 | org.opentest4j.AssertionFailedError: Not yet implmented 214 | org.opentest4j.AssertionFailedError: Not yet implmented 215 | org.opentest4j.AssertionFailedError: Not yet implmented 216 | Clear 217 | org.opentest4j.AssertionFailedError: Not yet implmented 218 | Process finished with exit code -1 219 | ``` 220 | 221 | ### Implement the method to be tested 222 | 223 | One simple implementation could be like this: 224 | ```Java 225 | public class Calculator { 226 | private int result; 227 | 228 | public void clear() { 229 | result = 0; 230 | } 231 | public void add(int n) { 232 | result += n; 233 | } 234 | public void sub(int n) { 235 | result -= n; 236 | } 237 | public void mul(int n) { 238 | result *= n; 239 | } 240 | public void div(int n) { 241 | result /= n; 242 | } 243 | public int giveResult() { 244 | return result; 245 | } 246 | } 247 | ``` 248 | 249 | ### Run tests again 250 | 251 | Now this implementation passes tests: 252 | ![](images/JUnitTestResults.png) 253 | 254 | ### Correct errors, improve the implementation 255 | 256 | Maybe the first way to implement the task was not the best possible. Think also re-using modules (Don't invent the wheel again). → refactor 257 | 258 | General instructions for refactoring can be found [here](https://sourcemaking.com/refactoring). 259 | 260 | Often tests should also be improved, e.g., more tests are needed, refactoring of tests. Maybe we need to test what happens when we divide by zero in our Calculator class. 261 | 262 | ## Parametrized test 263 | 264 | It is possible to arrange the JUnit to invoke the test method many times with different parameters. This is called as 265 | [Parametrized test](https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests). 266 | 267 | ## Reports 268 | 269 | Name the JUnit test methods with an identifier that describes the purpose of the test, e.g. test class CalculatorTest for a class Calculator, test class PersonTest for a class Person. 270 | 271 | Name the test methods with a verb describing the action/behavior 272 | 273 | Use `@DisplayName` annotation and attach a textual explanation. it will appear in the JUnit results window instead of the test method name, like `assertEquals(100, result, 0.0, "Upper limit not reached");` 274 | 275 | Please comment the test class code sufficiently. What you are testing in which case, also make sure the test coverage. Also consider special cases. 276 | 277 | Put a general comment at the beginning of the test class, indicating the test cases, i.e. what kind of situations will be tested. 278 | 279 | ## Other considerations 280 | 281 | - Each method marked with an annotation must be able to stand on its own 282 | - It cannot depend on the order in which the other test methods are run 283 | - You don't need your own self-evident tests for the obvious (too simply to break) 284 | - The constructor does not necessarily need its own test method if all variations are tested in other tests. Examine the effect of the constructor with getters: are the attributes assigned the expected values? 285 | - For getters and setters, a separate test is usually not necessary 286 | - They are tested alongside the other tests 287 | - If a getter programmatically calculates a return value, a test may be appropriate (e.g. returns a value in a different unit than it was stored) 288 | 289 | What if the method does not return a value (void) that could be examined in the assert? 290 | - If a method changes the value of a variable, examine it 291 | - it is so-called side effect 292 | - if addition increases the size of list, and deletion decreases the size → observe the size of the list 293 | - Make methods return something relevant, even if the caller doesn't use it 294 | - then you can check it using assert in the tests 295 | - it does not have any effect to invokes (of the method), because in Java the callee does not need to receive the return value 296 | - If necessary, extra method (e.g. `isValid()`) can be created to the class, which can test whether everything is ok 297 | - like `assertTrue(object.isValid())` 298 | 299 | What if the entity is using the service of another entity and the class has not yet been implemented? 300 | - Prioritize 301 | - implement and test the other class first 302 | - Use a fake object (test double, stub) 303 | - is pretending to be the original 304 | - does its job so that the caller doesn't notice the difference 305 | - return the right values for the caller 306 | - maybe with a hardcoded `return` statement 307 | - to be replaced with the real class 308 | - Use a mock 309 | - Produce functionality corresponding to that object in the mock library during the test 310 | - there are available libraries for that, e.g. Mockito, JMockit, EasyMock, jMock 311 | 312 | ## Assignments 313 | 314 | ### Task 1: creating a simple class and writing tests for it following the TDD approach 315 | 316 | Your task is to create a Java class called `PalindromeChecker` that can determine if a given string is a palindrome or not. A palindrome is a word, phrase, number, or other sequences of characters that reads the same forward and backward (ignoring spaces, punctuation, and capitalization). 317 | 318 | #### Steps 319 | 320 | 1. **Write Tests:** Begin by writing tests for the `PalindromeChecker` class based on different cases of palindromes and non-palindromes. 321 | 322 | 2. **Implement the Class:** Create the `PalindromeChecker` class with a method `isPalindrome(String str)` that takes a string as input and returns `true` if it's a palindrome and `false` otherwise. 323 | 324 | 3. **Run Tests:** Run your test suite. Initially, the tests should fail because you haven't implemented the class yet. 325 | 326 | 4. **Implement the Method:** Implement the `isPalindrome` method in the `PalindromeChecker` class to satisfy the test cases you've written. 327 | 328 | 5. **Refactor (if needed):** Once your tests pass, you can refactor your code for better readability and maintainability while ensuring that all tests still pass. 329 | 330 | #### Example 331 | 332 | Here's a sample set of test cases you might consider: 333 | 334 | ```java 335 | public class PalindromeCheckerTest { 336 | @Test 337 | public void testIsPalindrome() { 338 | PalindromeChecker checker = new PalindromeChecker(); 339 | 340 | assertTrue(checker.isPalindrome("radar")); 341 | assertTrue(checker.isPalindrome("A man, a plan, a canal, Panama")); 342 | assertFalse(checker.isPalindrome("hello")); 343 | assertFalse(checker.isPalindrome("openai")); 344 | } 345 | } 346 | ``` 347 | 348 | #### Your Task 349 | 350 | 1. Begin by writing the test cases in the `PalindromeCheckerTest` class. These tests should guide you on what the `isPalindrome` method should do. 351 | 352 | 2. Implement the `PalindromeChecker` class with the `isPalindrome` method, ensuring that it passes all the test cases. 353 | 354 | 3. Run your tests frequently to make sure you're making progress. 355 | 356 | This exercise follows the TDD approach: you start by writing tests, then implement the functionality to make the tests pass, and finally refactor your code if necessary. 357 | 358 | ### Task 2: creating a simple class and writing tests for it following the TDD approach 359 | 360 | Your task is to create a Java class called `ShoppingCart` that can manage items in a shopping cart. The class should allow users to add items, remove items, and calculate the total cost of items in the cart. 361 | 362 | #### Steps 363 | 364 | 1. **Write Tests:** Begin by writing tests for the `ShoppingCart` class. Consider test cases for adding items, removing items, and calculating the total cost. 365 | 366 | 2. **Implement the Class:** Create the `ShoppingCart` class with methods to add items, remove items, and calculate the total cost. 367 | 368 | 3. **Run Tests:** Run your test suite. Initially, the tests should fail because you haven't implemented the class yet. 369 | 370 | 4. **Implement the Methods:** Implement the methods in the `ShoppingCart` class to satisfy the test cases you've written. 371 | 372 | 5. **Refactor (if needed):** Once your tests pass, you can refactor your code for better readability and maintainability while ensuring that all tests still pass. 373 | 374 | #### Example 375 | 376 | Here's a sample set of test cases you might consider: 377 | 378 | ```java 379 | public class ShoppingCartTest { 380 | @Test 381 | public void testAddItem() { 382 | ShoppingCart cart = new ShoppingCart(); 383 | 384 | cart.addItem("Apple", 1.0); 385 | cart.addItem("Banana", 0.5); 386 | 387 | assertEquals(2, cart.getItemCount()); 388 | } 389 | 390 | @Test 391 | public void testRemoveItem() { 392 | ShoppingCart cart = new ShoppingCart(); 393 | 394 | cart.addItem("Apple", 1.0); 395 | cart.addItem("Banana", 0.5); 396 | cart.removeItem("Apple"); 397 | 398 | assertEquals(1, cart.getItemCount()); 399 | } 400 | 401 | @Test 402 | public void testCalculateTotal() { 403 | ShoppingCart cart = new ShoppingCart(); 404 | 405 | cart.addItem("Apple", 1.0); 406 | cart.addItem("Banana", 0.5); 407 | cart.addItem("Orange", 0.75); 408 | 409 | assertEquals(2.25, cart.calculateTotal(), 0.01); 410 | } 411 | } 412 | ``` 413 | 414 | #### Your Task 415 | 416 | 1. Begin by writing the test cases in the `ShoppingCartTest` class. These tests should guide you on what methods the `ShoppingCart` class should have. 417 | 418 | 2. Implement the `ShoppingCart` class with the methods to add items, remove items, and calculate the total cost. 419 | 420 | 3. Run your tests frequently to make sure you're making progress. 421 | 422 | Remember that this exercise follows the TDD approach: you start by writing tests, then implement the functionality to make the tests pass, and finally refactor your code if necessary. 423 | 424 | For this assignment, you get points in the following way: 425 | 1. Task 1 completed: 3 points 426 | 2. Task 2 completed: 3 points 427 | -------------------------------------------------------------------------------- /8.3_Lambda.md: -------------------------------------------------------------------------------- 1 | # Lambda 2 | 3 | 4 | [Lambda calculus](https://en.wikipedia.org/wiki/Lambda_calculus) (also written as λ-calculus) is a formal system in mathematical logic introduced in the 1930s by [Alonso Church](https://en.wikipedia.org/wiki/Alonzo_Church) for expressing computation based on function abstraction and application using variable binding and substitution. It is a universal model 5 | of computation that can be used to simulate any [Turing machine](https://en.wikipedia.org/wiki/Turing_machine). This means that Lambda calculus is theoretically capable of expressing all tasks accomplishable by computers (Davis, M., "Computability and Unsovability", Dover, 1982). For more theoretical treatment of Lambda calculus, see [here](https://www.irif.fr/~mellies/mpri/mpri-ens/biblio/Selinger-Lambda-Calculus-Notes.pdf). 6 | 7 | [Imperative programming languages](https://en.wikipedia.org/wiki/Imperative_programming) (like Java) 8 | in most cases implement the abstract function concept from the Lambda calculus. In Java, a lambda expression is essentially an anonymous function that can be used as a method parameter or assigned to a functional interface. The syntax of a lambda expression is: 9 | ```java 10 | (parameter list) -> body 11 | ``` 12 | In Java lambda expression, if there is only one parameter, you can leave out the parenthesis: 13 | ```java 14 | parameter -> body 15 | ``` 16 | 17 | In its simple form, a lambda could be represented as a comma-separated list of parameters, the `–>` symbol and the body. Notice that the lambda function does not have a name, therefore you are not able to call it from different places in your program code. Lambda functions are used mainly locally, i.e., called in only one place. Let's have a concrete example, where we first use anonymous class: 18 | ```java 19 | public class AnonDemo { 20 | public static void main(String[] args) { 21 | // Defining an anonymous method 22 | Runnable r = new Runnable() { 23 | public void run() { 24 | System.out.println("Running in Runnable thread"); 25 | } 26 | }; 27 | 28 | new Thread(r).start(); 29 | System.out.println("Running in main thread"); 30 | } 31 | } 32 | ``` 33 | The output is 34 | ```text 35 | Running in Runnable thread 36 | Running in main thread 37 | ``` 38 | In this example, we used anonymous class to implement the `Runnable` interface. Let's use lambda to do the same thing. 39 | ```java 40 | public class LambdaDemo { 41 | public static void main(String[] args) { 42 | Runnable r = () -> System.out.println("Running in Runnable thread"); 43 | new Thread(r).start(); 44 | System.out.println("Running in main thread"); 45 | } 46 | } 47 | ``` 48 | The output is the same as in the previous example. But why we were able to make the assignment to 49 | the variable `r`? 50 | 51 | ## Functional interface 52 | 53 | 54 | Lambda expression provides implementation of _functional interface_. An interface which has only one abstract method is called functional interface. Java provides an annotation `@FunctionalInterface`, which is used to declare an interface as functional interface. And the type of the variable `r` is `Runnable` which is one functional interface. 55 | Let's have another example: 56 | ```java 57 | @FunctionalInterface // optional, it allows the compiler to ensure that there is only one abstract function 58 | interface Drawable { 59 | public void draw(); 60 | } 61 | 62 | public class LambdaDemo2 { 63 | public static void main(String[] args) { 64 | int width = 10; 65 | 66 | Drawable d = ()-> System.out.println("Drawing " + width); 67 | d.draw(); 68 | } 69 | } 70 | ``` 71 | Because functional interface has only one method, it is possible to assign a lambda expression to it—even the lambda function does not have a name. In the previous example, lambda expression does not have a name, but because functional interface has only one method, the assignment is possible—and we can call the lambda function using the name `draw`. 72 | 73 | ## Return 74 | 75 | In Java lambda expression, if there is only one statement, you may or may not use return keyword. You must use the return keyword when lambda expression contains multiple statements. 76 | ```java 77 | interface Addable { 78 | int add(int a,int b); 79 | } 80 | 81 | public class LambdaDemo3 { 82 | public static void main(String[] args) { 83 | // Lambda expression without return keyword. 84 | Addable ad1 = (a,b)->(a+b); 85 | System.out.println(ad1.add(10,20)); 86 | 87 | // Lambda expression with return keyword. 88 | Addable ad2 = (int a,int b) -> { 89 | return (a+b); 90 | }; 91 | System.out.println(ad2.add(100,200)); 92 | } 93 | } 94 | ``` 95 | In the first case `ad1`, we used the _type inference_ to avoid specifying the types of parameters in the lambda expression. This type inference means that you don't have to explicitly specify the types of parameters in most cases. The compiler can infer the types based on the context or the functional interface to which the lambda is assigned. 96 | 97 | Usually we try to make lambda expressions as small as possible (one line functions), but sometimes you need more complicated operations to be done in lambda. Then you need to use brackets to combine multiple statements to one lambda block. 98 | 99 | ## Lambda as a parameter to make the method more general 100 | 101 | To use a lambda expression in a method, the method should have a parameter with a functional (single-method) interface as its type. Calling the interface's method will run the lambda expression: 102 | ```java 103 | interface StringFunction { 104 | String run(String str); 105 | } 106 | 107 | public class LambdaDemo4 { 108 | public static void main(String[] args) { 109 | StringFunction exclaim = (s) -> s + "!"; 110 | StringFunction ask = (s) -> s + "?"; 111 | 112 | printFormatted("Hello", exclaim); 113 | printFormatted("Hello", ask); 114 | } 115 | 116 | public static void printFormatted(String str, StringFunction format) { 117 | String result = format.run(str); 118 | System.out.println(result); 119 | } 120 | } 121 | ``` 122 | The output is 123 | ```Java 124 | Hello! 125 | Hello? 126 | ``` 127 | 128 | This kind of function who is able to take another function as a parameter is called a [higher-order function](https://en.wikipedia.org/wiki/Higher-order_function). It is an essential property of _functional programming_ that we'll discuss in the next section. 129 | 130 | ## Using predefined functional interfaces 131 | 132 | It is not always necessary to write your own functional interface. In Java, there are many predefined functional interfaces. 133 | 134 | ### Consumer 135 | 136 | Java Collections have the [Consumer](https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html) interface that has one method `accept()` which is called in many Collection methods. This method has a generic parameter, and it does not return anything (thus the name consumer). Let's have an example: 137 | ```Java 138 | import java.util.ArrayList; 139 | import java.util.function.Consumer; 140 | 141 | public class LambdaDemo5 { 142 | public static void main(String[] args) { 143 | ArrayList numbers = new ArrayList(); 144 | numbers.add(5); 145 | numbers.add(9); 146 | numbers.add(8); 147 | numbers.add(1); 148 | Consumer method = (n) -> System.out.println(n); 149 | numbers.forEach(method); 150 | } 151 | } 152 | ``` 153 | Java now has a type deduction for the [Generics](https://docs.oracle.com/javase/tutorial/java/generics/types.html); therefore, we are able to have a nice shortcut here: 154 | ```Java 155 | numbers.forEach((n) -> System.out.println(n)); 156 | ``` 157 | 158 | [Java Generics](https://baeldung.com/java-generics) are a powerful feature that allows you to write reusable (i.e., generic code) and type-safe code. The aim of generics are to: 159 | - Allow functions, methods and classes to work with arguments of any type whilst maintaining the information on the relationships between things, such as arguments and return values. 160 | - Better define how types can mix 161 | 162 | ### Predicate 163 | 164 | In scientific logic, a function that accepts an argument and, in return, generates a boolean value as an answer is known as a [predicate](https://en.wikipedia.org/wiki/Predicate_(mathematical_logic)). Similarly, in the Java programming language, a _predicate functional interface_ of Java is a type of function that accepts a single value or argument and does some sort of processing on it, and returns a boolean (True/False) answer. The implementation of the Predicate functional interface also encapsulates the logic of _filtering_ (a process that is used to filter stream components on the base of a provided predicate) in Java. Its syntax is: 165 | ```Java 166 | public interface Predicate { 167 | boolean test(T t); 168 | } 169 | ``` 170 | Here is a demo using the Predicate functional interface: 171 | ```Java 172 | import java.util.*; 173 | import java.util.function.Predicate; 174 | 175 | class LambdaDemo6 { 176 | public static void main(String args[]) { 177 | List names = Arrays.asList("Geek", "G-Man", "g1", "QA", "GeeksQuiz"); 178 | 179 | // declare the predicate type as string and use 180 | // lambda expression to create object 181 | Predicate p = (s) -> s.startsWith("G"); 182 | 183 | // Iterate through the list 184 | for (String st : names) { 185 | // call the test method 186 | if (p.test(st)) 187 | System.out.println(st); 188 | } 189 | } 190 | } 191 | ``` 192 | 193 | ### Method reference 194 | 195 | You use lambda expressions to create anonymous methods. Sometimes, however, a lambda expression does nothing but calls an existing method. In those cases, it's often clearer to refer to the existing method by name. [Method references](https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html) enable you to do this; they are compact, easy-to-read lambda expressions for methods that already have a name. 196 | 197 | ```java 198 | import java.util.Arrays; 199 | import java.util.List; 200 | 201 | public class MethodReferenceExample { 202 | public static void main(String[] args) { 203 | List names = Arrays.asList("Alice", "Bob", "Charlie"); 204 | 205 | // Example 1: Reference to a static method 206 | names.forEach(MethodReferenceExample::printName); 207 | 208 | // Example 2: Reference to an instance method of a particular object 209 | MethodReferenceExample example = new MethodReferenceExample(); 210 | names.forEach(example::printWithPrefix); 211 | } 212 | 213 | public static void printName(String name) { 214 | System.out.println(name); 215 | } 216 | 217 | public void printWithPrefix(String name) { 218 | System.out.println("Name: " + name); 219 | } 220 | } 221 | ``` 222 | 223 | In this example, we have a class `MethodReferenceExample` that demonstrates two types of method references. 224 | 225 | 1. **Reference to a static method**: In the first `forEach` method, we pass `MethodReferenceExample::printName` as the method reference. `printName` is a static method defined in the same class. It takes a `String` parameter and prints the name. The method reference allows us to pass the method as an argument without invoking it explicitly. It behaves similar to a lambda expression that calls the referenced method. 226 | 227 | 2. **Reference to an instance method of a particular object**: In the second `forEach` method, we pass `example::printWithPrefix` as the method reference. `printWithPrefix` is an instance method of the `MethodReferenceExample` class. It takes a `String` parameter and prints the name with a prefix. Here, we create an instance of `MethodReferenceExample` and use the instance method reference to call the method on that particular object. 228 | 229 | Both types of method references provide a concise and readable way to refer to existing methods. 230 | They can be used in situations where you want to pass behavior that is already implemented as a method, 231 | without the need for additional logic in a lambda expression. 232 | 233 | When you run the code, it will print the names from the `names` list using the method references. 234 | 235 | Output: 236 | ``` 237 | Alice 238 | Bob 239 | Charlie 240 | Name: Alice 241 | Name: Bob 242 | Name: Charlie 243 | ``` 244 | 245 | Java method reference offers a convenient way to refer to methods as functional interfaces, enabling you to write more expressive and modular code. 246 | 247 | ## Summary 248 | 249 | In Java, lambdas are a powerful feature in Java that allows you to 250 | write more concise and functional code. Here's a breakdown of the key components and syntax of 251 | lambda expressions in Java: 252 | 253 | 1. **Functional interfaces**: Lambdas are closely tied to functional interfaces, which are 254 | interfaces with a single abstract method. Java provides several built-in functional interfaces 255 | like `Runnable`, `Consumer`, `Predicate`, and `Function`. 256 | Functional interfaces are used as the target types for lambda expressions. 257 | 258 | 2. **Lambda syntax**: The basic syntax of a lambda expression consists of three parts: 259 | the parameter list, the arrow token `->`, and the body. The parameter list defines the parameters 260 | passed to the lambda, the arrow token separates the parameters from the body, and the body contains 261 | the code that gets executed when the lambda is invoked. 262 | 263 | 3. **Type inference**: Java has type inference, which means you don't have to explicitly specify 264 | the types of parameters in most cases. The compiler can infer the types based on the context or 265 | the functional interface to which the lambda is assigned. 266 | 267 | 4. **Method references**: In addition to lambdas, Java also supports method references, 268 | which provide a shorthand syntax for referring to an existing method. Method references can be 269 | used in place of lambda expressions when they simply call an existing method without any additional logic. 270 | 271 | By using lambdas and functional interfaces, you can write more concise and expressive code. Lambdas promote a more functional programming style in Java, allowing you to pass behavior as a parameter, write more compact code, and improve readability. 272 | 273 | Here's a simple example to demonstrate the usage of lambdas in Java: 274 | 275 | ```java 276 | public class LambdaExample { 277 | public static void main(String[] args) { 278 | // Example 1: Lambda with one parameter 279 | GreetingService greetingService = message -> System.out.println("Hello, " + message); 280 | greetingService.greet("John"); // Output: Hello, John 281 | 282 | // Example 2: Lambda with multiple parameters 283 | Calculator calculator = (a, b) -> a + b; 284 | int result = calculator.add(5, 3); // Result: 8 285 | 286 | // Example 3: Method reference 287 | List names = Arrays.asList("Alice", "Bob", "Charlie"); 288 | names.forEach(System.out::println); // Output: Alice, Bob, Charlie 289 | } 290 | } 291 | 292 | @FunctionalInterface 293 | interface GreetingService { 294 | void greet(String message); 295 | } 296 | 297 | @FunctionalInterface 298 | interface Calculator { 299 | int add(int a, int b); 300 | } 301 | ``` 302 | 303 | In this example, we define two functional interfaces (`GreetingService` and `Calculator`) and use lambdas to implement their abstract methods. We also demonstrate the usage of method references with the `forEach` method of the `List` interface. 304 | 305 | Lambdas are a powerful feature that promotes more functional and concise coding styles in the language. 306 | 307 | ## Assignments 308 | 309 | **Task 1: Sorting and Filtering using Lambda** 310 | 311 | Create a program that sorts and filters a list of objects using lambda expressions and the `Comparator` interface. 312 | 313 | #### Steps 314 | 315 | 1. **Create a Class:** Create a class called `Person` with fields `name` (String), `age` (int), and `city` (String). 316 | 317 | 2. **Create a List:** Create a list of `Person` objects with various names, ages, and cities. 318 | 319 | 3. **Sort by Age:** Use lambda expressions and the `Comparator` interface to sort the list of `Person` objects by age in ascending order. 320 | 321 | 4. **Filter by City:** Use lambda expressions to filter the sorted list and keep only the people from a specific city (e.g., "New York"). Hint: `removeIf()`. 322 | 323 | #### Your Task 324 | 325 | 1. Write a Java program that follows the steps mentioned above. 326 | 327 | 2. Experiment with different lambda expressions and `Comparator` implementations to customize the sorting and filtering behavior. 328 | 329 | 3. Compile and run your program to see the output. 330 | 331 | This exercise helps you practice using lambda expressions and the `Comparator` interface for sorting and filtering data without using the Stream API in Java. 332 | 333 | **Task 2: Collection Operations with Lambdas** 334 | 335 | Create a program that demonstrates basic operations on a collection of integers using lambda expressions without using the Stream API in Java. 336 | 337 | #### Steps 338 | 339 | 1. **Create a List:** Create a list of integers, e.g., `[10, 5, 8, 20, 15, 3, 12]`. 340 | 341 | 2. **Filter Even Numbers:** Use lambda expressions to filter out even numbers from the list. 342 | 343 | 3. **Double the Odd Numbers:** Use lambda expressions to double the value of odd numbers in the list. Hint: `replaceAll()` 344 | 345 | 4. **Sum the Numbers:** Use lambda expressions to calculate the sum of all numbers in the list. 346 | 347 | #### Your Task 348 | 349 | 1. Write a Java program that follows the steps mentioned above. 350 | 351 | 2. Experiment with different lambda expressions and operations to further understand working with collections. 352 | 353 | 3. Compile and run your program to see the output. 354 | 355 | For this assignment, you get points in the following way: 356 | 1. Task 1 completed: 3 points 357 | 2. Task 2 completed: 3 points 358 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Object-Oriented Programming 2 | Learning Resources for Java OOP in Metropolia's Software Engineering Curriculum 3 | 4 | _This material has been made by Vesa Ollikainen (modules 1, 4, 6, and 7) and Jarkko Vuori (modules 2, 3, 5, and 8)._ 5 | 6 | ## Part 1: Fundamentals of OOP in Java 7 | 8 | ### Module 1: Fundamentals of Java 9 | - [Java as a programming language](1.1_Java_as_a_programming_language.md) 10 | - [Variables, input and output, type conversion, and arithmetic operators](1.2._Variables,_input_and_output,_type_conversion,_and_arithmetic_operators.md) 11 | - [Control structures](1.3_Control_structures.md) 12 | - [Arrays](1.4_Arrays.md) 13 | 14 | ### Module 2: Data structures and interplay of objects 15 | - [Class, object, constructor, instance variable, method](2.1_Class_object_constructor_instance_variable_method.md) 16 | - [Collections](2.2_Collections.md) 17 | - [Association](2.3_Association.md) 18 | 19 | ### Module 3: Advanced Java Concepts 20 | - [Inheritance](3.1_Inheritance.md) 21 | - [Interface](3.2_Interface.md) 22 | - [Static variables and methods, packages and modifiers](3.3_StaticVarsAndMethods_Packages_Modifiers.md) 23 | - [Data streams, exceptions](3.4_DataStreamsAndExceptions.md) 24 | 25 | ### Module 4: Enhancing the coding 26 | - [Version control and AI-assisted coding](4_VCS_and_AI-assisted_coding.md) 27 | 28 | ## Part 2: Advanced development in Java 29 | 30 | ### Module 5: Multithreading 31 | - [Threads: the basics](5.1_Threads.md) 32 | - [Synchronization of threads](5.2_Synchronization.md) 33 | 34 | ### Module 6: GUI and event-driven programming 35 | - [Principles of GUI, events, MVC model](6.1_Principles_of_GUI,_events,_MVC_model.md) 36 | - [Layouts, building the UI](6.2_Layouts,_building_the_UI.md) 37 | - [Graphics, mouse and keyboard events, updating the GUI](6.3_Graphics,_mouse_and_keyboard_events,_updating_the_GUI.md) 38 | - [GUI design tools](6.4_GUI_design_tools.md) 39 | 40 | ### Module 7: Persistence 41 | - [Preparing the database](7.1_Preparing_the_database_and_the_connection.md) 42 | - [Database usage via JDBC driver](7.2_Database_usage_via_JDBC_driver.md) 43 | - [Basics of object-relational mapping](7.3_Basics_of_object-relational_mapping.md) 44 | - [Association in object-relational mapping](7.4_Association_in_object-relational_mapping.md) 45 | 46 | ### Module 8: Unit Testing and Functional programming 47 | - [Foundations of unit testing, running the tests, JavaDoc](8.1_UnitTesting) 48 | - [TDD and unit test design](8.2_TDD) 49 | - [Lambda statements](8.3_Lambda) 50 | - [Map/filter/reduce](8.4_Functional) 51 | 52 | 53 | -------------------------------------------------------------------------------- /images/AbstractArt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/AbstractArt.png -------------------------------------------------------------------------------- /images/Amdahl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/Amdahl.png -------------------------------------------------------------------------------- /images/BWAbstraction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/BWAbstraction.png -------------------------------------------------------------------------------- /images/COLAbstraction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/COLAbstraction.png -------------------------------------------------------------------------------- /images/ClassVariable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/ClassVariable.png -------------------------------------------------------------------------------- /images/DataInputStream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/DataInputStream.png -------------------------------------------------------------------------------- /images/EnergyEfficiency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/EnergyEfficiency.png -------------------------------------------------------------------------------- /images/Exceptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/Exceptions.png -------------------------------------------------------------------------------- /images/JUnitProjectStructure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/JUnitProjectStructure.png -------------------------------------------------------------------------------- /images/JUnitTestResults.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/JUnitTestResults.png -------------------------------------------------------------------------------- /images/JavaCollection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/JavaCollection.png -------------------------------------------------------------------------------- /images/MooreLaw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/MooreLaw.png -------------------------------------------------------------------------------- /images/PersonHex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/PersonHex.png -------------------------------------------------------------------------------- /images/ProcessVsThread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/ProcessVsThread.png -------------------------------------------------------------------------------- /images/StreamAPI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/StreamAPI.png -------------------------------------------------------------------------------- /images/SynchronizationProblem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/SynchronizationProblem.png -------------------------------------------------------------------------------- /images/SysComplexity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/SysComplexity.png -------------------------------------------------------------------------------- /images/VariableReadWrite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/VariableReadWrite.png -------------------------------------------------------------------------------- /images/anchorpane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/anchorpane.png -------------------------------------------------------------------------------- /images/array.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/array.png -------------------------------------------------------------------------------- /images/associated_classes.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/associated_classes.PNG -------------------------------------------------------------------------------- /images/bmi_calculator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/bmi_calculator.png -------------------------------------------------------------------------------- /images/borderpane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/borderpane.png -------------------------------------------------------------------------------- /images/calculator_ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/calculator_ui.png -------------------------------------------------------------------------------- /images/conflicts_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/conflicts_dialog.png -------------------------------------------------------------------------------- /images/conflicts_resolve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/conflicts_resolve.png -------------------------------------------------------------------------------- /images/css.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/css.png -------------------------------------------------------------------------------- /images/dice_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/dice_structure.png -------------------------------------------------------------------------------- /images/dice_ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/dice_ui.png -------------------------------------------------------------------------------- /images/flightControlPackages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/flightControlPackages.png -------------------------------------------------------------------------------- /images/game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/game.png -------------------------------------------------------------------------------- /images/gb_mold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/gb_mold.png -------------------------------------------------------------------------------- /images/gingerbread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/gingerbread.png -------------------------------------------------------------------------------- /images/git_branch_selector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/git_branch_selector.png -------------------------------------------------------------------------------- /images/git_head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/git_head.png -------------------------------------------------------------------------------- /images/github_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/github_edit.png -------------------------------------------------------------------------------- /images/gitmenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/gitmenu.png -------------------------------------------------------------------------------- /images/gridpane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/gridpane.png -------------------------------------------------------------------------------- /images/guiclicks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/guiclicks.png -------------------------------------------------------------------------------- /images/hbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/hbox.png -------------------------------------------------------------------------------- /images/heidi_connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/heidi_connection.png -------------------------------------------------------------------------------- /images/heidisql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/heidisql.png -------------------------------------------------------------------------------- /images/hello_world_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/hello_world_window.png -------------------------------------------------------------------------------- /images/hello_world_with_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/hello_world_with_button.png -------------------------------------------------------------------------------- /images/jpa_entities_uml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/jpa_entities_uml.png -------------------------------------------------------------------------------- /images/jpa_project_structure.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/jpa_project_structure.PNG -------------------------------------------------------------------------------- /images/nested.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/nested.png -------------------------------------------------------------------------------- /images/new_feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/new_feature.png -------------------------------------------------------------------------------- /images/one_branch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/one_branch.png -------------------------------------------------------------------------------- /images/packageDemo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/packageDemo.png -------------------------------------------------------------------------------- /images/projectModule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/projectModule.png -------------------------------------------------------------------------------- /images/push_alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/push_alert.png -------------------------------------------------------------------------------- /images/sb_gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/sb_gui.png -------------------------------------------------------------------------------- /images/sb_hierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/sb_hierarchy.png -------------------------------------------------------------------------------- /images/scene_builder_newly_opened.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/scene_builder_newly_opened.png -------------------------------------------------------------------------------- /images/source_and_bytecode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/source_and_bytecode.png -------------------------------------------------------------------------------- /images/stackpane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/stackpane.png -------------------------------------------------------------------------------- /images/structure_of_project.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/structure_of_project.PNG -------------------------------------------------------------------------------- /images/tilepane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/tilepane.png -------------------------------------------------------------------------------- /images/vbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vesavvo/Object-Oriented-Programming/d0f9a5df621310c21802f36452156c3c415e995a/images/vbox.png --------------------------------------------------------------------------------