You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

33 KiB

title: CSSE2002 author: Alistair Michael # Java Collections - Stack - Vector - List - Set - Map ## Stack - Last in first out - empty() - empty stack - peek() - return reference to top of stack - pop() - return top of stack and remove from stack - push(obj) - can not store primitive types, there are wrapper classes - automatically converted on construction ### Generic Types - collections contain generic types. The specific type of a generic is defined when the object is declared. ## Lists - grow and shrink automatically - walk along - insert anywhere - remove an item - check if an item is in the list ### Different Implementatiosn - LinkedList - adding and removing items in the middle - ArrayList - good for random access - Vector - for concurrency ### Methods - size(); the number of elements in the list // literally just look up the documentation ## Iterators - more flexible way of moving through the list than using for each loops - Call .iterator() method on another collection to get the iterator. - starts before the 1st element in the list - iterator.next() returns the next element in the list - it.hasNext() return true if there is another element in the list. - they can manipulate the contents of the collection - if you modify one iterator, other iterators on the same object will fail fast ### Kinds - ListIterator; knows more about how to iterate lists lists list.listIterator() - .add() method (at iterators current position) ## Sets - orderless ### TreeSet E needs to implement Comparable ### HashSet E neeeds to have sensible hashCode() and equals() ## Map - stores unordered key-value pairs - Map<Integer, String>; set type of key and value - not good for iterating - should not use mutable objects as keys although it works unless you change the object after setting it as the key - don't add a map inside of itself # Automated Testing - Testing procedures - test frameworks - test coverage ## term - unit testing - regressino testing - black box - white box - test driven development # Procedures - unit testing - each unit (class) works) - integration testing - components work together - system testing - does the whole program work - acceptance testing - do the users agree that the system does what it is supposed to # Frameworks : xUnit family JUnit for java - regression testing - does new stuff work - has it broken old things - blackbox testing - test the interface does what it is supposed to without knowledge of the implementation - does not test whether the implementation has like bad programming - glass box testing - knowledge of internal - code coverage - tests whether the complex parts of the internal is complex # TDD - write the tests before the code - requirements drive the code more directly - If you find a bug that is not caught by the tests, you can write a unit test, and add it to the regression test suite # Junit4 Framework - Write a test class for each object - all test methods have the @Test annotation. - method names tell you what they do. - use a piece of code, and use assert methods - eg Assert.assertEquals(val, val) - Each test method should only test one thing (conceptually / logically) - @Before and @After labels put on each test, are used to setup and tear-down the environment before and after each test. To ensure they don't interfere with eachother. ## Assert - assertEquals - assertArrayEquals - assertFalse / assertTrue - assertSame / assertNotSame - fail - can import all these as static methods so they can be called without the junit.etst.sjhda.Assert garbage. ## Setup - need junit and hamcrest libraries ## What to test - Input possibilities and features - Should identify potential problem areas - Should not be too big ## Things to test - boundary cases - 0, negative numbers - NULL, empty containers, sets lists - Floating point extremities - large datasets - resource access denied, failed - non-existent resources ## Code Coverage - How much of the code is tested - statement coverage - the code is tested by being executed once - branch coverege - all possible branching paths that the logic creates are executed - path coverege - all paths thru the loop are tested - for for loops - init fails - init test fails - init test body fails - init test body iterate fails - eg recursion is also painful # Procedural Abstraction - interested in what the methods do - javadoc clearly explain functionality - methods need to have one clear function Be suspicious of - control flow on parameter types - long and complex methods - repeated code - methods that have more than one function - can change the implementation without changing the specification ## Specifications - javadoc - allow the implementation to be changed without changing methods that use it - draw attention to possible consequences of implementation details - be sufficiently restrictive if implementation is limited. - the information needed to use the method - be precise to avoid incorrect implementations - have generality to allow acceptable alternative versions - have clarity, utilise formal languages, ## Contract Meaning if the caller meets the precondition, the method guarantees that postcondition will be true. java /** @require precondition @ensure postcondition */ Can use assert to ensure this at compile time, and can also use it as a test system at runtime with special conditions. # Defensive Programming Assume that input is bad. - Explicitly check for invalid inputs - Ensure that no dangerous behaviour results - Always apply it to data coming from outside of the program, and treat things coming from inside the program as valid. ## Null Problems - Always reject null, because it can easily appear inside the program. # Substitution Principle, Specifications and Inheritance > An object of a subclass type can be used at any point where a super-class's method is expected - WRT to Contracts, the child is bound by the contracts with the parent. - Parents contract must be sufficient for child's contract - ONLY can be weakening of the precondition # Miscellaneous Java ## Instanceof expression instanceof type Returns true if the value of expression is an instance of type type. ### Questionable Uses - Use it in conditionals to determine what methods should be used on that thing. - Otherwise: use encapsulation - Put the code in the classes themselves so you do not need to use a conditional to determine which class is being used - use a generic interface - use helper methods in the classes ## Newlines - println will always use the correct newline character for the operating system (at runtine) - Unix uses "\n" - Windows uses "\r\n" - System.lineSeparator() and String.format("%n") will return the correct line separator. ## Pre / post increment - The same as C expression x= returns ++x 1 1 x++ 2 1 x-- 1 2 --x 0 0 ## Ternary int a = (conditional) ? do if true : do if false; ## Final keyword final has two meanings depending on its context. ### Variables java public void stuff() { final int x = 5; x = 4; // compile error } In the case of member variables, they must be set once in the constructor, and nowhere else. java public class Chem { public static final double AVAGADRO = 3.023e23; } ### References It means that the reference value cannot be changed, not that the object it refers to cannot be changed. java final Test x = new Test(10); x.q = 15; Will compile, you can change the state of the object without changing it's identity. // What about the objects hash value, for maps etc. That would probably be weird. ### Methods A method that is labelled final cannot be overwritten in a subclass. ### Classes - Cannot create new classes that inherit from final classes - Member variables cannot be changed once initialised ## Abstract An abstract class is a class that it is not meaningful to create an object of, but is useful in the structure of the class heirachy. An abstract class does not have to have abstract methods. An abstract method has no implementation, it exists to be defined later. public abstract void doStuff(); If a class contains abstract methods, the class must also be declared abstract. And abstract classes cannot be instantiated, although they can be extended. This is used for defining interfaces. ## Short Circuit evaluation The entirety of a conditional statement is not computed if its value can be determined earlier. True || f(x) || g(x) will not execute f or g. g(x) && False && g(x): g(x) will never get executed. # StringBuilder / StringBuffer StringBuffer is older and slower, but threadsafe. Strings are immutable so += on strings requires re-allocating memory, doing it in a loop is very inefficient. StringBuilder is not threadsafe. java StringBuilder sb = new StringBuilder("starting text"); for (int i = 0; i ++; i < 10) { sb.append(i); } System.out.println(sb.toString()); // need to use toString() //to get the string value # Copying (clone) Objects are always manipulated by reference, by default. java Objext x = y // makes x refer to the same thing as y Object class has a protected .clone() method, since it is protected some work is required to be able to use it. This is to ensure it is implemented properly for the classes you are truing to use it for, since reference members may also need to be copied. java public class MessageHolder implements Cloneable { // need to implement the // Cloneable interface private MessageBuilder msg; //... @Override public Object clone() { try { // have to catch CloneNotSupportedException MessageHolder nm = super.clone(); // we can call clone() here because this class has the // Cloneable marker interface set, indicating it is // allowed nm.msg = new StringBuilder(msg.toString); return nm; } catch (CloneNotSupportedException e) { } return null; } } - Typically you can just call Object.clone() - Deep-copy is not a concern for immutable objects. - Deep-copy is general expensive Note that: - x.clone != x - x.clone.getClass() == x.getClass() - x.clone().equals(x) should usually true, but there are practical exceptions # Properties of .equals() - reflexive - symmetric (in terms of the same class type) - transitive - deterministic, should always give the same result, unless the objects' state changes - x.equals(null) is false Be mindful of the comparability of the classes whose .equals() method you are calling. - If you override .equals() you also need to override .hashCode() - x.equals(y) $\implies$ x.hashCode() == y.hashCode() - .equals() can refer to object state or object identity - you need to make a decision on whether to include the mutable parts of the class in computing the hashcode accordingly. - including mutable state in the hashcode means changing the state changes whether .equals() is true for a copy with different state. It is probably better to use .equals() for state, since the reference provides the object's identity. ## Hash Codes Returns a numeric value for some given values. - has to be deterministic - may not neccessarily uniquely identify an object - collisions should be rare and able to be managed - not as good for determining object identity - x.equals(y) $\implies$ x.hashCode() == y.hashCode() - Must be efficient, for fast lookup - used for looking up items quickly - comparing passwords without comparing the actual values - if it is being used as a lookup key - does changing the state make it a different object? # Input-Output ## Scanners There is a collection of scanner object methods that can be used to get specific items from these filetypes. See Javadoc java import java.util.Scanner; // ... Scanner input = new Scanner(System.in); Scanner file = new Scanner(new File("filename.txt")); int total = 0; while (true) { total += input.nextInt(); } ## Encoding Java uses unicode, chars are not neccessarily single bytes and there is no single automatic way to translate between bytes and UTF-X chars. So I'm assuming their environment system is less of a tranwreck than C's. 1. Binary - More compact - Sensitive to system differences - Otherwise it is more direct 2. Text - Human readable - Requires parseing and markup delimiters etc. - Not really efficient for machine-interpretation as a middle abstraction ## Streams Abstractions for input and output - Works with multiple origins of input; keyboard, disk, files, network - Buffering - Unified interface for different types of input/output data ### java.io.InputStream - FileInputStream; for files - ByteArrayInputStream; get bytes from an array in memory as an input stream Methods that use InputStreams should ask for an object of the InputStream class (interface style calss), not its children classes. Low-level read() ### Buffered input BufferedInputStream is a class that wraps InputStream to provide buffering to improve performance. It can read a single character, a specific number of characters, or a line. For other methods see javadoc java try { InputStream is = new BufferedInputStream(new FileInputStream("dat")); String s = is.readLine(); } catch (FileNotFoundException e) { // fail } finally { is.close(); } You have to then parse the returned string with string-parsing functions. ### Readers java.io.Reader is a base class for objects which read InputStreams. new FileReader("filename") $\approx$ new InputStreamReader(new FileInputStream("filename")) ### Try with Resources Readers and streams need to be closed. Can use try catch and finally to close files at the end, however close() can also throw exceptions hence Java provides try catch syntax that automatically handles file resources. java try (BufferedReader r = new BufferedReader(new FileReader("file"))) { r.readLine(); } catch (IOException e) { // handle } ### Parsers Each simple type (int, char...) has a wrapper class and these have string parsing methods. java int i = Interger.parseInt("1"); Now $i = 1$. ## Output For the most part just replace Input with Output and Reader with Writer. The standard output is System.out, standard error is System.err, it is a print stream that provides print methods. - print(), println() - flush() flushes output buffer - printf(String format, Object args) Use C-style format strings. - write(byte[] buf, int off, int len) write len bytes from a byte array. ### PrintWriter PrintWriter is a better tool for writing characters. It can write to any type of Stream or file. Look at the constructors in the javadoc. You often have to flush the output because otherwise it only gets sent once the output is full, or if it is closed. This is important for - interactive programs where you prompt for input - debugging where you need outputs to be in order and up to date. ## Serialisation Converting a java object to bytes. For a class to be serialisable it must: - Must implement Serializable interface - Any objects referenced must also be Serializable - Object streams can read and write to any type of Stream---to files, networks etc. - Read using ObjectInputStream - Write using ObjectOutputStream java ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file")); oos.write(new Integer(5)); oos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("file")); Integer five = ois.readObject(); ### Limitations - If you change the class after saving a serialisable object and try to read it again it will produce an version error. - Deserialising untrusted data is very unsafe / insecure # Parsing Text Files Often need to - find delimiters - split strings on delimiters - convert strings to primitive types - construct new objects based on parameters from a file ## Split Strings java String[] splitstrings = "a b c".split("delimiter"); String[] splitstrings = "a b c".split("delimiter"); String s = "lalala".substring(0, 2); // s = la ## Regular Expressions Exist in java (pattern) ## Converting Strings java Integer.parseInt() Float.parseFloat() Double.parseDouble() Boolean.parseBoolean() // converts to false for anything other than true All number types return NumberFormatException if the number format is wrong. ## Objects Usually putting the parsing code in an initialiser is not appropriate, it doesnt separate IO classes from logic classes, and it requires files to be organised as one class to a file or other complexity. It is better to have another IO class that parses the file, then calls the objec initialisers. ## File Objects Creating, renaming, moving etc; filsystem manipulations, not manipulating file objects (that is through the stream abstraction). java.nio.File package contains - java.nio.file.Path - java.nio.file.Files ## Exit System.exit(1); // exit with an error code Default exit status is 0, which means success. # JavaFX Good example of Object Oriented, and event-driven programming. It has newer features than older GUI libraries, it automatically manages threads, etc. Need to add a java module through VM options in IntelliJ (to add jvm arguments). Create a class - Extends javafx.application.Application - Override the start() method ## Stage A stage is the main window which displays a single Scene which holds all the widgets. The scene holds a heirachial scene-graph that holds GUI elements. ## Layout Panes - groups nodes (GUI Elements) - can use column, row ordering to place nodes within a grid - grid.add(new Label("hello"), col,row) ## Controls Buttons, labels, areas, textfields etc ## Panes Boxes that hold groups of nodes and can be laid out as nodes themselves. BorderPane(): a pane with borders ## Canvas Draw shapes and things in a space. Look at examples its not very complex. ## EventHandlers Interactions with the gui generate events, and program functions also generate events. We only consider ActionEvent in javafx.event for handling gui events. For something to happen as a result of an Event, there needs to be an EventHandler associated with that event. EventHandler is an interface which we use to implement our event handlers. They can be connected to buttons with setOnAction java // package private class, can be in the same file as ButtonDemo // note that package private classes still generate .class files so // you have to be careful of name conflicts in the package scope still class ButtonDoer implements EventHandler<ActionEvent> { public void handle(ActionEvent e) { System.out.println("Send to console"); } } public class ButtonDemo extends javafx.application.Application { ... public void start() { ... Button button = new Button("useless button"); button.setOnAction(new ButtonDoer()); ... } } ### Better Design - link the events and what they do more loosely, - to avoid putting application logic in event handlers - use inner classes instead of package private classes for event handlers for better encapsulation java public class ButtonDemo extends javafx.application.Application { private Stage stage; public static void main(String[] args) { Launch(args); } public void start(Stage stage) { this.stage = stage; Button button = new Button("useless button"); button.setOnAction(new ButtonDoer()); GridPane grid = new GridPane(); grid.add(button, 0,0); Scene scene = new Scene(grid); stage.setScene(scene); stage.show(); } public void respondToButton() { // do stuff } // no one else can do actions that aren't tied to events // logic is encapsulated with the gui interactions private class ButtonDoer implements EventHandler<ActionEvent> { public void handle (ActionEvent e) { repondToButton(); } } } - can use a single handler for many buttons, make buttons private memebers and switch over event.getSource() == button1. - nested classes are non-examinable ### Anonymous Classes Allows you to create an event handler immediately to do the things that you want the button to do. java button.setOnAction(new EventHandler<ActionEvent>() { public void handle(ActionEvent e) { respondToButton(); } }// end of class button2.setOnAction(new EventHandler<ActionEvent>() { public void handle(ActionEvent e) { respondToButton2(); } }// end of class ); ## TextFields For entering text java tf = new TextField(); String text = tf.getText(); tf.setText("Hello World") ## Dialogs For confirmation prompts etc, there are pre-designed dialogue types. java public void doButton() { TextInputDialog inputDialog = new TextInputDialog(); inputDialog.initStyle(StageStyle.DECORATED); inputDialog.setHeaderText("Hello World"); ImageView iv = new ImageView(new Image("photo.png")); iv.setFitHeight(40); iv.setPreserveRatio(true); inputDialog.setGraphic(iv); Optional<String> result = inputDialog.showAndWait(); if (result.isPresent()) { System.out.println(result.get()) } } ### FileChooserDialog java public class FileChooserDemo extends javafx.application.Application { private Stage stage private FileChooser fileChooser = new FileChooser(); private void respondToButton() { File file = fileChooser.showOpenDialog(stage); if (file != null) { openFile(file); } } private void openFile (File file) { // do stuff } } # Design Quality ## Cohesion - Does a class/object make sense as a single entity - Do all the data and methods fit together for a single purpose or abstract concept - minimises extraneous ideas to understand - simpler unit to test - modification is easier ## Coupling - How strongly a class depends on another class - How much of the internal state is passed to another class through methods? - How many methods of other classes are called? - Can another object influence the flow of control in this object? - Low coupling is preferable - highly coupled classes are harder to write and test in isolation - high coupling can indicate that a class has been split when it shouldn't have been ### Law of Demeter The target of a message can only be one of the following objects: - The methods object (this) - An object passed as a parameter - An object referred to by an attribute of the object - Weak form of Demeter: and anything in that collection o - An object created by the method - Object referred to by a global variable Avoid chained messages a.getB().getC().doSomething() - this increases coupling ## Mindless Classes - A class should manage its own flow of control - restrict other classes from accessing its state - data members are private - minimise accessor methods - The logic that is applied to the classes data should be within the class, not in other classes that access the data through getters - These tend to have low cohesion and high coupling ## God Classes - Classes that do everything within their context and contain all the data - High coupling and low cohesion ## Mitigation A class should only depend on the public interface of another class. - Attributes should only belong to one class. - This is often violated when classes have many accessors - A class should represent a single abstract concept - Unrelated data and functionality should be factored out to other classes - system logic should arise from the classes working to gether to implement behaviour, it should be shared between classes uniformly ## Fragile Super Class - Inheritance creates strong coupling between the superclass and the subclass - Does the design rely on the knowledge of the private methods of the superclas; changing the privates in the superclass should not change behaviour or cause problems for the subclasses. - Public or protected methods should only change behaviour if the specification of their functionality changes - should usually be overridden in the subclasses ### Downcall - Calling a method from a childs class # GUI Design - Model - Conceptual things: entities, in the system - State - invariants - methods that enforce the invariants - View - A presentation of the state, and a way to interact with it ## Why MVC? - Decompose the task - Separate interface from model - Can change the UI independently of the model - Might want to support multiple interfaces - GUIs, web, mobile screenreaders - Responsibility for enforcing invariants should be in only one place ## How MVC: Challenges View and Model need to communicate - find current state - may need to notify the interface for when state changes - The user has to get information and send commands - The interface needs to know if the model has changed We want them to be loosely coupled, so the model shouldn't know about the view. - one way access from interface to model is satisfactory only for a small model and interface. It is generally not safe to assume that the model is synchronous with the view. - Dont want to make the user wait for the model to sort itself out - Multiple interfaces being connected could easily make a deadlock - Model generally is updated independently to the actions happening in the interface, from things like external input, network input, or just calculating results of requested functions ## Callbacks and Observers - User Interfaces implement an interface that the model knows about, and which it uses to tell the interface that the model has changed, and details about what has changed - The interface can then ask the model for further information only when there is new information available, without having to poll constantly to ask - So UI updates are driven by events sent by the model ## Input Processing 1. Getting Values from UI components to assemble a method call - maybe the processing to generate the call to the model, needs to be in a separate class if it is very complex 2. Making changes to the model based on that call - belongs in the model ## Model View Controller - View: - sends messages to the controller based on User interaction - recieves callbacks from the model and queries details about state - Controller: - recieves requests from the view and figures out what to do to the model in response - Model: - Stores state, does program logic, and implements functionality - Tells the view when it has changed ### Model View Adapter Isolate the view from the model using an adapter - View - interacts with user - sends events to an adapter - recieves updates from adapter - Adapter - reads state from model - manipulates model - recieves callbacks from model about state - sends updates to the view - Model: - Stores state, does program logic, and implements functionality - Tells the adapter when it has changed An example of an Adapter might be a REST API to a server-side functionality ### Model View Presenter Same as MVA except the view and presenter is more tightly coupled. Every view class has its own presenter class. Presenter manages the display, not just bridging between view requests and the model, so it can do more complex things with the view to allow it to be more responsive and intelligent, without having a lot of that complexity in the actual UI code. ### Model View ViewModel ViewModel: - encapsulates the state (of the model) that is displayed by the view - tight coupling with the view: two way binding - user changing state immediately updates viewmodel, - Is provided by libraries - less boilerplate code required for implementing event-sending between the view and controller and model - the most restricted version here - Cannot easily have different views # Generic Programming - Using Object is bad because it has no type safety. - Generic types solve this, since you can use parameterised types. - still has compile-time type checking - don't need to cast in and out java public class X<T> Boo { private T myFirstVariable; // T is the type of myFirstVariable // ... constructor goes here ... ... } You can now instantiate the class and give it a specific type. java Boo<Integer> = new Boo<>(); You can an arbitrary number of type parameters in clases. There is a naming convention. - E: Element (in collections) - K: Key - N: number - T: Type - V: Value - S,U,V... Additional types ## Generic Methods Do not have to be in a generic class java public static <T> int count(T[] array, T value) { for (T item : array) { ; } } ## Bounded Type Parameters - Types can be restricted to being a subset of classes. java public class<T extends Number> {...} This allows anything that is a sub-class of Number. ## Generic Inheritance java class X <T> extends class Y <T> {} class X <T> extends class Y <String> {} - note that using a subclass as a generic parameter, does not imply that the classes themselves have an inheritance relationship. ## Wildcards ? Represents an unknown type, but not a specific unknown type. They are useful when generic types are needed but they do not need to be named and referenced. ? - any type ? extends Type - any subclass of Type ? super Type - any superclass of Type ## Implementation Type Erasure: Generics are handled at compile time by replacing the generic types with Object, replacing bounded generics with the bounding Type and adding casts and bridging methods. Java only knows that types are at runtime, not at compile time. ## Restrictions on Generics - Cannot be primitives - Cannot instantiate generics: new T() - Cannot be static - Cannot have arrays of generic types - No generic exceptions - There are restrictions on overloading # Object Oriented Design ## Textual Analysis Considering the description of the system Identify elements to be modelled. - nouns -> data, categories -> attributes, classes - verbs -> processes -> methods Be mindful of relevance, relatedness, and relationships between the nouns in the model. ## Common Class Patterns Find candidate classes using classification theory: - Concepts - Events - Organisation - People - Place are all class-candidates. ## Class-Responsibilities-Collaborators After identifying candidate-classes, consider the behaviour and interactions between classes. Using humans, roleplay to model how the classes should deliver the system behavoir. For pinning down the responsibilities of a class, and defining the collaborators that facilitate their interaction. This helps develop a shared understanding of the system design. Reading ### OOPSLA89 Paper Summary Responsibilities are problems to be solved. The responsibilities of an object are active verb phrases. All objects exist in relationships to other objects. Collaborators are objects that send messages, or are sent messages, in order to satisfy their responsibilities. Make cards like this Image: 3 index cards showing a model, view and controller. The top of the card is theClass name and below it a list of responsibilities The first line is the class name, followed by a list of responsibilties. Design the model by role playing the execution of the model from some starting point in the model: > start with only one or two obvious cards and start playing "what-if". If the > situation calls for a responsibility not already covered by one of the objects > we either add the responsibility to one of the objects, or create a new object > to address that responsibility Only create objects to address the immediate need, not a hypothetical future need. If they are needed in the future, then they will be created in the future.