{"id":"libraries-and-tools-end","slug":"packaging-and-the-end","title":"Packaging, other tools, and the end of the course","text":"A finished program should run on computers that have no editor and no Maven, only Java. The last bits of section 14.4 are how to pack a program for that, and a few more tools worth knowing.\n\n### A jar\n\nA **jar** (Java archive) is a zip file of compiled classes, with a small text file inside, the **manifest**, that can name the class whose `main` starts the program. The JDK can make one on its own, with no Maven:\n\n```shell\njavac -d classes src/main/java/com/example/repairs/*.java\njar --create --file repairs.jar --main-class com.example.repairs.Main -C classes .\njava -jar repairs.jar\n```\n\nThe first line compiles the classes into the folder `classes`, the second packs that folder into `repairs.jar` with `Main` as its start, and the third runs it on any computer with Java installed.\n\nIn a Maven project, `mvn package` makes `target/repair-queue-1.0-SNAPSHOT.jar` (the artifactId and the version). To name the main class in its manifest, pom.xml gets a `build` section that configures the plugin that makes the jar:\n\n```xml\n<build>\n    <plugins>\n        <plugin>\n            <groupId>org.apache.maven.plugins</groupId>\n            <artifactId>maven-jar-plugin</artifactId>\n            <version>3.3.0</version>\n            <configuration>\n                <archive>\n                    <manifest>\n                        <mainClass>com.example.repairs.Main</mainClass>\n                    </manifest>\n                </archive>\n            </configuration>\n        </plugin>\n    </plugins>\n</build>\n```\n\nThat jar holds only the project's own classes. A program with libraries, such as H2, also needs their jars when it runs: plugins such as the Maven Shade Plugin or the Maven Assembly Plugin make one bigger jar with the libraries inside. And the JDK's `jpackage` tool makes an installer for the system you run it on (Windows, macOS or Linux) that brings its own Java along, so the person installing it doesn't need Java at all. The MOOC's [section 14.4](https://java-programming.mooc.fi/part-14/4-maven-and-third-party-libraries) points to more packaging tools.\n\n### Other tools, in a sentence each\n\n- **Gradle** does what Maven does, with build files written as code instead of XML; Android apps are built with it.\n- **An IDE** (IntelliJ IDEA, NetBeans, Eclipse, VS Code with Java extensions) compiles as you type, runs and debugs programs step by step, and runs Maven or Gradle for you.\n- **Git** keeps every version of a project's files, so you can go back to any of them and work on a project together with others.\n- **JUnit** (module 26) runs a project's tests; with Maven, `mvn test` runs them all.\n- **JavaFX** on your own computer is a library like any other: the dependency `org.openjfx:javafx-controls`, with the `javafx-maven-plugin` to run it with `mvn javafx:run` (module 48 links to the MOOC's instructions).\n- **Libraries for almost everything**: reading JSON, talking to web services, writing web servers, making games (the MOOC mentions FXGL, a game library built on JavaFX). Search Maven Central, read the library's guide, and start with its smallest example.\n\n### The end of the course\n\nThis was the last section of the MOOC's material, and the last module of the course here. Looking back over the 54 modules:\n\n- **Parts 1 to 3**: printing, reading input, variables and calculations, decisions, loops, methods and return values, finding bugs, lists, arrays and strings. The tools every program is made of.\n- **Parts 4 to 7**: classes and objects, constructors, getters and `toString`, objects in lists, files, references and `null`, objects working together, a user interface kept apart from the logic, unit tests, sorting and searching, and planning larger programs.\n- **Parts 8 to 10**: hash maps, `equals` and `hashCode`, inheritance, abstract classes, interfaces and polymorphism, streams and lambdas, `Comparable` and `Comparator`, StringBuilder, regular expressions and enums.\n- **Parts 11 and 12**: class diagrams, packages, exceptions, writing files, type parameters, a list and a hash map of your own, random numbers and two-dimensional arrays.\n- **Parts 13 and 14**: windows with JavaFX, events and views, charts, drawing and images, a game with shapes, keys and a game loop, and, in this module, build tools, libraries, DAOs and databases.\n\n### What next\n\nThe best next step is a small project of your own, in a Maven project on your own computer: a tracker for something you collect, a game you'd like to play, a tool that tidies a file you use. You'll get stuck, look things up and get unstuck, and that's most of what programming is. Then, depending on what you liked:\n\n- **Databases and SQL**, to go further than this module's peek: designing tables and writing queries.\n- **Data structures and algorithms**: how lists, hash maps and trees work inside, and how fast a program is as its data grows.\n- **Web programming**: servers written in Java (or another language), and pages that talk to them.\n- **Newer Java**: records, `switch` expressions, `var`, text blocks and sealed types. This site's course plan lists extra modules on these after this one, beyond the MOOC's material.\n\nThe MOOC ends with its own [conclusion](https://java-programming.mooc.fi/part-14/5-conclusion), a short page to read on its site. Thank you for working through the course here.\n","task":"The challenges of this step are a small review across the course. A pottery studio logs its kiln firings: `Firing.java` is ready. In `Main.java`, write the method `piecesAtLeast(List<Firing> firings, int degrees)`, which returns the names of the pieces fired at `degrees` or more, in the same order, as a new list. Use a stream: `filter`, `map` and `collect` (module 37). The program prints:\n\n```\n[tall vase, tea bowl]\n[]\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Firing> firings = new ArrayList<>();\n        firings.add(new Firing(\"tall vase\", 1240));\n        firings.add(new Firing(\"bead set\", 950));\n        firings.add(new Firing(\"tea bowl\", 1280));\n        System.out.println(piecesAtLeast(firings, 1200));\n        System.out.println(piecesAtLeast(firings, 1300));\n    }\n\n    public static List<String> piecesAtLeast(List<Firing> firings, int degrees) {\n        return new ArrayList<>();\n    }\n}\n\n// ==== Firing.java ====\npublic class Firing {\n    private String piece;\n    private int degrees;\n\n    public Firing(String piece, int degrees) {\n        this.piece = piece;\n        this.degrees = degrees;\n    }\n\n    public String getPiece() {\n        return this.piece;\n    }\n\n    public int getDegrees() {\n        return this.degrees;\n    }\n}\n","solution":"// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Firing> firings = new ArrayList<>();\n        firings.add(new Firing(\"tall vase\", 1240));\n        firings.add(new Firing(\"bead set\", 950));\n        firings.add(new Firing(\"tea bowl\", 1280));\n        System.out.println(piecesAtLeast(firings, 1200));\n        System.out.println(piecesAtLeast(firings, 1300));\n    }\n\n    public static List<String> piecesAtLeast(List<Firing> firings, int degrees) {\n        return firings.stream()\n                .filter(firing -> firing.getDegrees() >= degrees)\n                .map(firing -> firing.getPiece())\n                .collect(Collectors.toList());\n    }\n}\n\n// ==== Firing.java ====\npublic class Firing {\n    private String piece;\n    private int degrees;\n\n    public Firing(String piece, int degrees) {\n        this.piece = piece;\n        this.degrees = degrees;\n    }\n\n    public String getPiece() {\n        return this.piece;\n    }\n\n    public int getDegrees() {\n        return this.degrees;\n    }\n}\n","hints":["`firings.stream()` gives a stream of Firing objects. `filter` keeps the ones whose `getDegrees()` is at least `degrees`, and `map` turns each into its `getPiece()`.","End with `.collect(Collectors.toList())`, and import `java.util.stream.Collectors`."],"tests":[{"name":"Three firings","stdin":"","expect":"[tall vase, tea bowl]\n[]","hidden":false},{"name":"Test 2","stdin":"","call":"List<Firing> firings = new ArrayList<>();\nSystem.out.println(piecesAtLeast(firings, 1000));\nfirings.add(new Firing(\"plate\", 1000));\nfirings.add(new Firing(\"mug\", 999));\nfirings.add(new Firing(\"jug\", 1100));\nfirings.add(new Firing(\"plate\", 1050));\nSystem.out.println(piecesAtLeast(firings, 1000));\nSystem.out.println(firings.size());\n","expect":"[]\n[plate, jug, plate]\n4","hidden":true}],"require":[{"pattern":"\\.stream\\s*\\(\\s*\\)","message":"Use a stream: start from `firings.stream()`."},{"pattern":"\\.filter\\s*\\(","message":"Keep the hot enough firings with `filter`."}],"forbid":[],"more":[{"task":"An e-scooter app has several ways to charge for a ride, and the interface `Tariff.java` describes them all: `int price(int minutes)` returns a ride's price in cents. `PerMinuteTariff.java` is ready. Write two things:\n\n- the class `CappedTariff`, which implements `Tariff`: `new CappedTariff(centsPerMinute, cap)` charges `centsPerMinute` for every minute, but never more than `cap` cents for a ride\n- in `Main.java`, the method `cheapest(List<Tariff> tariffs, int minutes)`, which returns the lowest price any of the tariffs gives for a ride of that many minutes (the list is never empty)\n\nThe program prints:\n\n```\n150\n600\n500\n```\n","kind":"code","seed":"// ==== CappedTariff.java ====\npublic class CappedTariff {\n\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        Tariff capped = new CappedTariff(30, 600);\n        System.out.println(capped.price(5));\n        System.out.println(capped.price(40));\n\n        List<Tariff> tariffs = new ArrayList<>();\n        tariffs.add(new PerMinuteTariff(25));\n        tariffs.add(capped);\n        System.out.println(cheapest(tariffs, 20));\n    }\n\n    public static int cheapest(List<Tariff> tariffs, int minutes) {\n        return 0;\n    }\n}\n\n// ==== Tariff.java ====\npublic interface Tariff {\n    int price(int minutes);\n}\n\n// ==== PerMinuteTariff.java ====\npublic class PerMinuteTariff implements Tariff {\n    private int centsPerMinute;\n\n    public PerMinuteTariff(int centsPerMinute) {\n        this.centsPerMinute = centsPerMinute;\n    }\n\n    @Override\n    public int price(int minutes) {\n        return minutes * this.centsPerMinute;\n    }\n}\n","solution":"// ==== CappedTariff.java ====\npublic class CappedTariff implements Tariff {\n    private int centsPerMinute;\n    private int cap;\n\n    public CappedTariff(int centsPerMinute, int cap) {\n        this.centsPerMinute = centsPerMinute;\n        this.cap = cap;\n    }\n\n    @Override\n    public int price(int minutes) {\n        return Math.min(minutes * this.centsPerMinute, this.cap);\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Main {\n    public static void main(String[] args) {\n        Tariff capped = new CappedTariff(30, 600);\n        System.out.println(capped.price(5));\n        System.out.println(capped.price(40));\n\n        List<Tariff> tariffs = new ArrayList<>();\n        tariffs.add(new PerMinuteTariff(25));\n        tariffs.add(capped);\n        System.out.println(cheapest(tariffs, 20));\n    }\n\n    public static int cheapest(List<Tariff> tariffs, int minutes) {\n        int lowest = tariffs.get(0).price(minutes);\n        for (Tariff tariff : tariffs) {\n            int price = tariff.price(minutes);\n            if (price < lowest) {\n                lowest = price;\n            }\n        }\n        return lowest;\n    }\n}\n\n// ==== Tariff.java ====\npublic interface Tariff {\n    int price(int minutes);\n}\n\n// ==== PerMinuteTariff.java ====\npublic class PerMinuteTariff implements Tariff {\n    private int centsPerMinute;\n\n    public PerMinuteTariff(int centsPerMinute) {\n        this.centsPerMinute = centsPerMinute;\n    }\n\n    @Override\n    public int price(int minutes) {\n        return minutes * this.centsPerMinute;\n    }\n}\n","hints":["`CappedTariff implements Tariff`, keeps both numbers in instance variables, and its `price` returns the smaller of the full price and the cap: `Math.min(minutes * this.centsPerMinute, this.cap)`.","`cheapest` starts from the first tariff's price and goes through the list, keeping the lowest price so far. It only calls `price`, so it works for any kind of Tariff."],"tests":[{"name":"The capped tariff, and the cheapest of two","stdin":"","expect":"150\n600\n500","hidden":false},{"name":"Test 2","stdin":"","call":"Tariff capped = new CappedTariff(20, 500);\nSystem.out.println(capped.price(0) + \" \" + capped.price(25) + \" \" + capped.price(26));\nList<Tariff> tariffs = new ArrayList<>();\ntariffs.add(new CappedTariff(40, 1000));\ntariffs.add(new PerMinuteTariff(15));\ntariffs.add(new CappedTariff(35, 700));\nSystem.out.println(cheapest(tariffs, 10) + \" \" + cheapest(tariffs, 50) + \" \" + cheapest(tariffs, 90));\n","expect":"0 500 500\n150 700 700","hidden":true},{"name":"Test 3","stdin":"","call":"List<Tariff> tariffs = new ArrayList<>();\ntariffs.add(new PerMinuteTariff(10));\nSystem.out.println(cheapest(tariffs, 7));\n","expect":"70","hidden":true}],"require":[],"forbid":[]},{"task":"A community kitchen plans its volunteer shifts. Read lines such as `Saturday Ada` (a day, a space and a name) until an empty line. Then print, for each day, the day, a colon and the names of that day's volunteers in the order they were read, separated by `, `. Print the days in the order they first came up in the input.\n\nKeep the names in a `HashMap<String, List<String>>` from each day to its list of names (module 33). A HashMap doesn't keep its keys in the order they came, so keep the days' order in a list of its own. For the input shown, the program prints:\n\n```\nSaturday: Ada, Ben, Dina\nMonday: Carl\n```\n","kind":"code","seed":"import java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n\n    }\n}\n","solution":"import java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        HashMap<String, List<String>> shifts = new HashMap<>();\n        List<String> days = new ArrayList<>();\n        while (true) {\n            String line = scanner.nextLine();\n            if (line.isEmpty()) {\n                break;\n            }\n            String[] parts = line.split(\" \");\n            String day = parts[0];\n            if (!shifts.containsKey(day)) {\n                shifts.put(day, new ArrayList<>());\n                days.add(day);\n            }\n            shifts.get(day).add(parts[1]);\n        }\n        for (String day : days) {\n            System.out.println(day + \": \" + String.join(\", \", shifts.get(day)));\n        }\n    }\n}\n","hints":["When a day comes up for the first time (`!shifts.containsKey(day)`), put a new empty list for it in the map and add the day to the list of days. Then add the name to `shifts.get(day)`.","At the end, go through the list of days, not the map. `String.join(\", \", names)` joins a list of Strings with `, ` between them."],"tests":[{"name":"Test 1","stdin":"Saturday Ada\nSaturday Ben\nMonday Carl\nSaturday Dina\n\n","expect":"Saturday: Ada, Ben, Dina\nMonday: Carl","hidden":false},{"name":"Test 2","stdin":"Friday Eve\n\n","expect":"Friday: Eve","hidden":true},{"name":"Test 3","stdin":"Tuesday Sam\nWednesday Lee\nThursday Kim\nWednesday Ann\nTuesday Bo\nSunday Max\nWednesday Pat\n\n","expect":"Tuesday: Sam, Bo\nWednesday: Lee, Ann, Pat\nThursday: Kim\nSunday: Max","hidden":true}],"require":[{"pattern":"new\\s+HashMap","message":"Keep each day's names in a `HashMap<String, List<String>>`."}],"forbid":[]}]}