1 / 54

CS203 Lecture 3

CS203 Lecture 3 . John Hurley Cal State LA. Binary File I/O. Real world applications usually use databases to store large amounts of information Main exceptions are apps that have unique file formats that can’t easily be translated to databases, like word processors or paint software.

catori
Download Presentation

CS203 Lecture 3

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. CS203 Lecture 3 John Hurley Cal State LA

  2. Binary File I/O • Real world applications usually use databases to store large amounts of information • Main exceptions are apps that have unique file formats that can’t easily be translated to databases, like word processors or paint software. • If you have not studied text file I/O, take a look at the material in the textbook. • There is a very nice way to store data from OOP applications directly to disk, though: binary file i/o

  3. Binary File I/O • Binary files store data as sequences of bytes in a format that is usually far more compact than text files. • If you open a binary file in a word processor or text editor, you will see gibberish. It is possible to see the byte values in base 16 using a hex editor, but they will normally be unintelligible. • Every application formats its binary files differently. The only way to easily retrieve the data is by using an application that can parse the data, normally the app that generated the file in the first place. • Programming languages typically offer easy ways to store data in binary files. • Java and other object-oriented languages can format objects as binary sequences for file storage. They also offer automated ways to recreate objects form the data.

  4. Binary File I/O Output: • FileOutputStream is a stream for output to a file. • BufferedOutputStream is a stream with a buffer (write data in chunks for more efficiency, since there are fewer calls to the expensive input/output methods used by the operating system). BufferedOutputStream wraps an unbuffered stream such as a FileOutputStream to provide the buffer. • DataOutputStream provides methods for writing primitive types as well as Strings to the stream it wraps. • Methods like writeDouble() output the stated types All of these have equivalent input methods • The following example stores low-level types like Double and uses parsing code when the file is read, but you will soon see an example that stores and retrieve collections of objects of programmer-defined classes.

  5. package menudemo; // http://docs.oracle.com/javase/tutorial/essential/io/examples/DataStreams.java import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class DataStreams { static final String dataFile = "invoicedata"; static final double[] prices = { 19.99, 9.99, 15.99, 3.99, 4.99 }; static final int[] units = { 12, 8, 13, 29, 50 }; static final String[] descs = { "Java T-shirt", "Java Mug", "Duke Juggling Dolls", "Java Pin", "Java Key Chain" }; public static void main(String[] args) throws IOException { DataOutputStream out = null; try { out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile))); for (int i = 0; i < prices.length; i ++) { out.writeDouble(prices[i]); out.writeInt(units[i]); out.writeUTF(descs[i]); } } finally { out.close(); }

  6. DataInputStream in = null; double total = 0.0; try { in = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile))); double price; int unit; String desc; try { while (true) { price = in.readDouble(); unit = in.readInt(); desc = in.readUTF(); System.out.format("You ordered %d units of %s at $%.2f%n", unit, desc, price); total += unit * price; } } catch (EOFException e) { // this is really how Orace's documentation suggests you find the end of a file! System.out.println("EOF reached");} System.out.format("For a TOTAL of: $%.2f%n", total); } finally { in.close(); } } }

  7. Binary I/O • ObjectOutputStream and ObjectInputStream allow binary I/O of whole objects • Objects read from files must be cast correctly • Objects stored this way must be serializable, meaning they must implement the Serializable interface, which just identifies them as convertible to a binary stream. Serializable does not require any actual methods. • If the object contains references to other objects, all the classes must be serializable! • Key methods are writeObject() and readObject(). readObject() requires a cast, so you have to know what kind of objects are stored in the file you are reading.

  8. Monster Herd Persister • package monsterherd; • import java.io.Serializable; • public class Monster implements Serializable{ • private String name; • private String hometown; • private String rampageBehavior; • public Monster(String nameIn, String hometownIn, String rampageBehaviorIn){ • name = nameIn; • hometown = hometownIn; • rampageBehavior = rampageBehaviorIn; • } • public String getName() { • return name; • } • public void setName(String name) { • this.name = name; • } • public String getHometown() { • return hometown; • } • public void setHometown(String hometown) { • this.hometown = hometown; • }

  9. Monster Herd Persister • public String getRampageBehavior() { • return rampageBehavior; • } • public void setRampageBehavior(String rampageBehavior) { • this.rampageBehavior = rampageBehavior; • } • public String toString (){ • return name + " from " + hometown + " likes to " + rampageBehavior; • } • }

  10. package monsterherd; • import java.awt.Dimension; • import java.awt.event.ActionEvent; • import java.awt.event.ActionListener; • import java.awt.event.KeyEvent; • import java.io.File; • import java.util.List; • import javax.swing.JFileChooser; • import javax.swing.JFrame; • import javax.swing.JMenu; • import javax.swing.JMenuBar; • import javax.swing.JMenuItem; • import javax.swing.JOptionPane; • import javax.swing.JPanel; • import javax.swing.JScrollPane; • import javax.swing.JTable; • public class MonsterGUI { • MonsterHerd herd; • JFrame frame = null; • JPanel panel; • JMenuBar menubar; • JScrollPane scrollPane; • JTable table;

  11. public MonsterGUI() { • herd = new MonsterHerd(); • initUI(); • } • public final void initUI() { • frame = new JFrame(); • panel = new JPanel(); • table = setUpTable(); • menubar = new JMenuBar(); • JMenu file = new JMenu("File"); • file.setMnemonic(KeyEvent.VK_F); • final JFileChooser fc = new JFileChooser();

  12. // enums like KeyEvent evaluate to ints • createAndAddMenuItem(file, "Open", KeyEvent.VK_O, "Open File", • new ActionListener() { • public void actionPerformed(ActionEvent event) { • intretVal = fc.showOpenDialog(frame); • if (retVal == JFileChooser.APPROVE_OPTION) { • File selectedFile = fc.getSelectedFile(); • herd.populateFromFile(selectedFile); • updateTable(); • } • } • }); • createAndAddMenuItem(file, "Add Monster", KeyEvent.VK_A, "Add Monster", • new ActionListener() { • public void actionPerformed(ActionEvent event) { • String name = JOptionPane.showInputDialog(frame, • "please enter the monster's name"); • String hometown = JOptionPane.showInputDialog(frame, • "please enter the monster's hometown"); • String rampage = JOptionPane • .showInputDialog(frame, • "what does the monster do when it goes on a rampage?"); • Monster m = new Monster(name, hometown, rampage); • herd.addMonster(m); • updateTable(); • } • });

  13. createAndAddMenuItem(file, "Save", KeyEvent.VK_S, "Save File", • new ActionListener() { • public void actionPerformed(ActionEvent event) { • int retVal = fc.showOpenDialog(frame); • if (retVal == JFileChooser.APPROVE_OPTION) { • File selectedFile = fc.getSelectedFile(); • herd.saveBinary(selectedFile); • } • } • }); • createAndAddMenuItem(file, "Exit", KeyEvent.VK_E, "Exit application", • new ActionListener() { • public void actionPerformed(ActionEvent event) { • System.exit(0); • } • });

  14. Monster Herd Persister • menubar.add(file); • frame.setJMenuBar(menubar); • frame.setTitle("Monsters"); • frame.setSize(600, 200); • frame.setLocationRelativeTo(null); • frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); • scrollPane = new JScrollPane(table); • panel.add(scrollPane); • frame.add(panel); • frame.setVisible(true); • } • private void createAndAddMenuItem(JMenu menu, String message, int mnemonic, • String tooltip, ActionListener a) { • JMenuItem menuItem = new JMenuItem(message); • menuItem.setMnemonic(mnemonic); • menuItem.setToolTipText(tooltip); • menuItem.addActionListener(a); • menu.add(menuItem); • }

  15. private JTable setUpTable(){ • String[] columnNames = { "Name", "Hometown", "Rampage Behavior"}; • Object[][] data = listToArray(herd.getMonsters()); • JTable newTable = new JTable(data, columnNames); • newTable.setPreferredScrollableViewportSize(new Dimension(500, 70)); • newTable.setFillsViewportHeight(true); • newTable.setAutoCreateRowSorter(true); • newTable.doLayout(); • return newTable; • } • private void updateTable() { • for (Monster m : herd.getMonsters()) • System.out.println(m); • panel.removeAll(); • table = setUpTable(); • scrollPane = new JScrollPane(table); • panel.add(scrollPane); • panel.doLayout(); • } • private Object[][] listToArray(List<Monster> list) { • Object[][] array = new Object[list.size()][3]; • for (int counter = 0; counter < list.size(); counter++) { • array[counter][0] = list.get(counter).getName(); • array[counter][1] = list.get(counter).getHometown(); • array[counter][2] = list.get(counter).getRampageBehavior(); • } • return array; • } • public static void main(String[] args) { • MonsterGUI ex = new MonsterGUI(); • } • }

  16. Monster Herd Persister • package monsterherd; • import java.io.File; • import java.io.Serializable; • import java.util.ArrayList; • import java.util.List; • public class MonsterHerd implements Serializable{ • private List<Monster> monsters; • private MonsterHerdPersisterpersister; • public MonsterHerd(){ • monsters = new ArrayList<Monster>(); • persister = new MonsterHerdPersister(); • } • public List<Monster> getMonsters() { • return monsters; • } • public void saveBinary(File fileOut){ • persister.saveHerdToFile(fileOut, this); • }

  17. Monster Herd Persister • public void populateFromFile(File fileIn){ • monsters = persister.readHerdFromFile(fileIn).getMonsters(); • } • public void addMonster(Monster m){monsters.add(m);} • public String toString(){ • StringBuilder sb = new StringBuilder(); • for(Monster m: monsters) sb.append(m + "\n"); • return sb.toString(); • } • }

  18. Monster Herd Persister • package monsterherd; • import java.io.BufferedInputStream; • import java.io.BufferedOutputStream; • import java.io.File; • import java.io.FileInputStream; • import java.io.FileOutputStream; • import java.io.ObjectInputStream; • import java.io.ObjectOutputStream; • import java.io.Serializable; • public class MonsterHerdPersister implements Serializable{ • public void saveHerdToFile(File f, MonsterHerd mh) { • ObjectOutputStream out = null; • try { • out = new ObjectOutputStream(new BufferedOutputStream( • new FileOutputStream(f))); • out.writeObject(mh); • out.close(); • } catch (Exception e) { • e.printStackTrace(); • } • }

  19. Monster Herd Persister • public MonsterHerd readHerdFromFile(File f) { • ObjectInputStream in = null; • MonsterHerd m = null; • try { • in = new ObjectInputStream(new BufferedInputStream( • new FileInputStream(f))); • m = (MonsterHerd) in.readObject(); • in.close(); • } catch (Exception e) { • e.printStackTrace(); • } • return m; • } • }

  20. Binary I/O With List • Binary I/O can be used to store and retrieve lists of objects.

  21. Binary I/O With List • package vampirebinaryio; • import java.io.Serializable; • public class Crypt implements Serializable { • private String location; • public Crypt(String location) { • this.location = location; • } • public void setLocation(String location) { • this.location = location; • } • public String getLocation() { • return location; • } • public String toString(){ • return "a really mysterious crypt in " + location; • } • }

  22. package vampirebinaryio; • import java.io.Serializable; • import javax.swing.JOptionPane; • public class Vampire implements Serializable { • private String name; • private Crypt crypt; • public Vampire(String name, String location) { • this.name = name; • crypt = new Crypt(location); • } • public void setName(String name) { • this.name = name; • } • public String getName() { • return name; • } • public void setLocation(String location) { • crypt.setLocation(location); • } • public String getLocation() { • return crypt.getLocation(); • } • public void rampage() { • JOptionPane.showMessageDialog(null, name + " arises from " +crypt.toString() + " and sucks people's blood " • + "all night, then returns to a coffin to hide from sunlight"); • } • public String toString(){ • return name + " inhabits " + getLocation(); • } • }

  23. Binary I/O With List • package vampirebinaryio; • import java.io.File; • import java.util.ArrayList; • import java.util.List; • import javax.swing.JFileChooser; • import javax.swing.JOptionPane; • public class VampireIODriver { • private static List<Vampire> vampires; • public VampireIODriver() { • vampires = new ArrayList<Vampire>(); • } • public void prowl() { • String[] options = { "Quit", "List Vampires", "Add A Vampire", • "Save To File", "Load From File" }; • int choice = 0;

  24. Binary I/O With List • do { • choice = JOptionPane.showOptionDialog(null, "Next Action", • "Next Action", JOptionPane.DEFAULT_OPTION, • JOptionPane.QUESTION_MESSAGE, null, options, options[0]); • switch (choice) { • case 1: • listVampires(); • break; • case 2: • addVampire(); • break; • case 3: • saveToFile(); • break; • case 4: • loadFromFile(); • break; • } • } while (choice != 0); • }

  25. public void listVampires() { • StringBuilder sb = new StringBuilder(); • for (Vampire v : vampires) • sb.append(v.toString() + "\n"); • if (sb.length() == 0) • sb.append("no vampires"); • JOptionPane.showMessageDialog(null, sb); • } • public void addVampire() { • String name = JOptionPane • .showInputDialog("Please enter the vampire's name"); • String location = JOptionPane • .showInputDialog("Please enter the vampire's location"); • vampires.add(new Vampire(name, location)); • } • public void saveToFile() { • VampirePersister persister = new VampirePersister(); • JFileChooser fc = new JFileChooser(); • int retVal = fc.showOpenDialog(null); • if (retVal == JFileChooser.APPROVE_OPTION) { • File selectedFile = fc.getSelectedFile(); • persister.saveListToFile(selectedFile, vampires); • } • }

  26. Binary I/O With List • public void loadFromFile() { • VampirePersister persister = new VampirePersister(); • JFileChooser fc = new JFileChooser(); • int retVal = fc.showOpenDialog(null); • if (retVal == JFileChooser.APPROVE_OPTION) { • File selectedFile = fc.getSelectedFile(); • vampires = persister.readListFromFile(selectedFile); • } • } • public static void main(String[] args) { • VampireIODriver d = new VampireIODriver(); • d.prowl(); • } • }

  27. Binary I/O With List • package vampirebinaryio; • import java.io.BufferedInputStream; • import java.io.BufferedOutputStream; • import java.io.File; • import java.io.FileInputStream; • import java.io.FileOutputStream; • import java.io.ObjectInputStream; • import java.io.ObjectOutputStream; • import java.io.Serializable; • import java.util.List; • public class VampirePersister implements Serializable{ • public void saveListToFile(File f, List<Vampire> vl) { • ObjectOutputStream out = null; • try { • out = new ObjectOutputStream(new BufferedOutputStream( • new FileOutputStream(f))); • out.writeObject(vl); • out.close(); • } catch (Exception e) { • e.printStackTrace(); • } • }

  28. Binary I/O With List • public List<Vampire> readListFromFile(File f) { • ObjectInputStream in = null; • List<Vampire> vl = null; • try { • in = new ObjectInputStream(new BufferedInputStream( • new FileInputStream(f))); • vl = (List<Vampire>) in.readObject(); • in.close(); • } catch (Exception e) { • e.printStackTrace(); • } • return vl; • } • }

  29. Generics • Some data structures are suitable for handling objects of many different types. • Every ArrayList is a list of objects of some type, but the types might be Strings, Rectangles, or any other objects. • Yet, Java is a strongly-typed language, one in which references can only be passed if they are of the same type the receiver expects. • Compare this Java code to the JavaScript on the next slide: • public static void main(String[] args) { • String myName = "Earl"; • printOut(myName); • } • public static void printOut(String s) { • System.out.println(s); • }

  30. Generics • <script lang = "JavaScript"> • function printOut(v){ • document.write(v + "<br />"); • } • var myName = "Earl"; • var myAge = 42; • printOut(myName); • printOut(myAge); • </script>

  31. Generics • Generic classes and data structures are designed with the capability to incorporate any of a range of other types. They are parameterized by the type that is "plugged in". This is somewhat similar to the concept of an array as a collection of values of the same data type. Unlike arrays, though, generic data types can only use classes or interfaces as their underlying type • You can create an ArrayList<Integer>, using the class Integer, but not an ArrayList <int> using the primitive data type int. • It is possible to create Lists in Java which are not parameterized and can accept values of any type. This is left over from before generics were introduced to Java. Don’t do this unless you find some very good reason.

  32. Generics • The generic type is defined with a placeholderthat is essentially a variable representing the parameterizing type, for example • ArrayList<E>

  33. Generics • We can and should declare variables with the least-specific type possible. For example, we can declare a variable of type List<E> and then instantiate an ArrayList<E> and assign this object to the variable. • This way, if we decide later to use some other kind of List, it is very easy to change the code. We can even write code in which what type of list is assigned to the variable depends on things that happen at runtime. • This only works if we can limit our use of methods to those that are in the List interface • Similarly, we can choose the most general "plugged in" type available and actually add objects of subclasses or implementing classes to the generic data structure

  34. Generics • The rules for which types can be used in a parameterized data structure are similar to the rules for variable types: • If the data structure calls for a type, you can add objects of that type or its subtypes, but not of its supertypes • A subtype may be either a subclass, a subinterface, or a class that implements an interface • This is easy to understand using some examples: • Suppose Monster is a class with two subclasses, Zombie and Demon, which do not have any subclasses of their own • An ArrayList<Monster> can hold any combination of Monsters, Zombies, and Demons • An ArrayList<Zombie> can only hold Zombies • Suppose TVShow is an interface, and the classes GameShow and SitCom implement it and do not have any subclasses • An ArrayList<TVShow> can hold any combination of GameShows and SitComs. • An ArrayList<GameShow> can only hold GameShows

  35. Generic Type Although you could achieve similar results by just using the type Object instead of using generics, generics provide stricter compile-time type checking. This replaces runtime errors with syntax errors, which are much easier to deal with. Generic Instantiation Runtime error Improves reliability Compile error

  36. Generic ArrayList in JDK 1.5

  37. Generics • My sections of CS202 over the past year have completed a lab using the MyMath interface: • public interface MyMath<T> { • public T add(T o); • public T subtract(T o); • public T divide(T o); • public T multiply(T o); • } • In implementing MyMath <T>, you chose the type that T represents and applied binary operations like add(), whose operands were the current object and an object of class T

  38. Generics • Note that, when we implement MyMath <T>, we define what type T stands for. Therefore, the parameter types for the methods are determined at compile time, and there is no ambiguity for the compiler. • The generic method public static <E> void print(E[] list) is different, because the type of the generic parameter may not be known until runtime.

  39. Generic Methods Methods in non-generic classes may take generic parameters, but the syntax for this may be unexpected: Generic public static <E> void print(E[] list) { for (int i = 0; i < list.length; i++) System.out.print(list[i] + " "); System.out.println(); }

  40. Bounded Generic Type Methods that use generic types can limit the possible types to ones that extend a particular class or implement a particular interface For example, if your method finds the largest element in a list by using compareTo(), it will only work if the parameter objects include this method. You can make sure they do by only using objects of classes that implement Comparable.

  41. Bounded Generic Type package demos; import java.util.ArrayList; import java.util.List; public class Demo { public static void main(String[] args) { List<String> myList = new ArrayList<String>(); String[] myArray = {"Godzilla", "Dracula", "Frankenstein"}; for(String s:myArray) myList.add(s); System.out.println(min(myList)); } public static <E extends Comparable <E>> E min(List<E> theList) { E smallest = theList.get(0); for (E e: theList) if(e.compareTo(smallest) < 0) smallest = e; return smallest; } }

  42. Bounded Generic Type The placeholder E is an arbitrary choice. This min method is equivalent to the one on the last slide: public static <Z extends Comparable <Z>> Z min(List<Z> theList) { Z smallest = theList.get(0); for (Z z: theList) if(z.compareTo(smallest) < 0) smallest = z; return smallest; }

  43. Bounded Generic Type public static void main(String[] args ) { Rectangle rectangle = new Rectangle(2, 2); Circle9 circle = new Circle9(2); System.out.println("Same area? " + equalArea(rectangle, circle)); } public static <E extends GeometricObject> boolean equalArea(E object1, E object2) { return object1.getArea() == object2.getArea(); }

  44. Wildcards ? unbounded wildcard ? extends T bounded wildcard ? super T lower bound wildcard

  45. Generic Types and Wildcard Types

  46. Generics • The Java collections we have been using are themselves programmed in Java and follow some of the same patterns we are learning. • List<E> is an interface • There is a List class, but it is the old, pre-generics List. Eclipse or the compiler will produce a warning but let you use it. Don’t. • ArrayList<E> is a class that extends Abstract List<E>, which implements List • There are various other interfaces and classes involved too

  47. Type Erasure and Restrictions on Generics Generics are implemented using an approach called type erasure. The compiler uses the generic type information to compile the code, but erases it afterwards. So the generic information is not available at run time. This approach enables the generic code to be backward-compatible with the legacy code that uses raw types.

  48. Compile Time Checking For example, the compiler checks whether generics is used correctly for the following code in (a) and translates it into the equivalent code in (b) for runtime use. The code in (b) uses the raw type.

  49. Type Erasure A generic class is shared by all its instances regardless of its actual generic type. GenericStack<String> stack1 = new GenericStack<String>(); GenericStack<Integer> stack2 = new GenericStack<Integer>(); Although GenericStack<String> and GenericStack<Integer> are two types, there is only one class GenericStack loaded into the JVM. Stack1 and stack2 in this example are still two different instances of the GenericStack class

  50. Type Erasure Type Erasure is the reason why you can't construct objects of a generic class, even though it seems that the JVM should know at runtime what the class should be: • This will *NOT* work: • public static <E> void addOne(List<E> theList) { • theList.add(new E()); • }

More Related