Det här med skräpinsamling, hur var det med det egentligen? Om jag t ex skapar objekt med "new" hur tar jag bort dem? Det där automatiska var ju inte att lita på?Om skräpsamling, finalize och diverse close-metoder:
Programmet är förstås lite förenklat. Ett användbart program skulle läsa från filen, och det skulle inte öppna samma fil gång på gång.import java.io.*; import java.util.*; public class FileCounter1 { public static void main(String[] args) { try { int nrFiles = 0; ArrayList<BufferedReader> files = new ArrayList<BufferedReader>(); while (true) { BufferedReader in = new BufferedReader(new FileReader("dummy.txt")); files.add(in); ++nrFiles; System.out.println("nrFiles = " + nrFiles); } } catch (FileNotFoundException e) { System.out.println("Exception caught: " + e.getMessage()); } } // main } // class FileCounter1
FileCounter1 kraschar:
Inte så konstigt att programmet kraschar: Alla de öppnade filerna sparas i arraylistan files. Eller egentligen är det ju BufferedReader-objekt som sparas, men ett sådant innehåller eller refererar till en notering om den öppna filen, som är en operativsystemresurs, och denna öppna fil stängs inte förrän BufferedReader-objektet skräpsamlas. Och det görs aldrig i detta program, eftersom alla BufferedReader-objekten fortfarande är åtkomliga, via arraylistan files.nrFiles = 1 nrFiles = 2 nrFiles = 3 nrFiles = 4 nrFiles = 5 nrFiles = 6 nrFiles = 7 ... nrFiles = 1017 nrFiles = 1018 nrFiles = 1019 nrFiles = 1020 nrFiles = 1021 Exception caught: dummy.txt (Too many open files)
Programmet FileCounter2.java:
FileCounter2 kraschade inte, i alla fall inte innan jag tröttnade på att vänta:import java.io.*; import java.util.*; public class FileCounter2 { public static void main(String[] args) { try { int nrFiles = 0; while (true) { BufferedReader in = new BufferedReader(new FileReader("dummy.txt")); ++nrFiles; System.out.println("nrFiles = " + nrFiles); } } catch (FileNotFoundException e) { System.out.println("Exception caught: " + e.getMessage()); } } // main } // class FileCounter2
Vi har tur. Skräpsamlaren samlar ihop och stänger hela tiden de öppna filerna, innan de blir för många.nrFiles = 1 nrFiles = 2 nrFiles = 3 nrFiles = 4 nrFiles = 5 nrFiles = 6 nrFiles = 7 ... nrFiles = 999998 nrFiles = 999999 nrFiles = 1000000 nrFiles = 1000001 nrFiles = 1000002 nrFiles = 1000003 ...
Programmet FileCounter3.java:
FileCounter3 provkördes flera gånger. Ibland körde programmet i timmar utan problem. Ibland kraschade det:import java.io.*; import java.util.*; class MyFileObject { private final BufferedReader reader; static int nrInstances = 0; static int maxNrInstances = 0; public MyFileObject(BufferedReader reader) { this.reader = reader; ++nrInstances; if (nrInstances > maxNrInstances) maxNrInstances = nrInstances; System.out.println("nrInstances up, now " + nrInstances + " (max " + maxNrInstances + ")"); } public void finalize() { --nrInstances; System.out.println("nrInstances down, now " + nrInstances); } } // class MyFileObject public class FileCounter3 { public static void main(String[] args) { try { int nrFiles = 0; while (true) { BufferedReader in = new BufferedReader(new FileReader("dummy.txt")); MyFileObject mfo = new MyFileObject(in); ++nrFiles; System.out.println("nrFiles = " + nrFiles); } } catch (FileNotFoundException e) { System.out.println("Exception caught: " + e.getMessage()); } } // main } // class FileCounter3
Övning: Den stannar på 707. Varför räknar den inte ner till 0? [Svar]nrInstances up, now 1 (max 1) nrFiles = 1 nrInstances up, now 2 (max 2) nrFiles = 2 nrInstances up, now 3 (max 3) nrFiles = 3 ... nrInstances up, now 598 (max 598) nrFiles = 598 nrInstances up, now 599 (max 599) nrFiles = 599 nrInstances down, now 598 nrInstances down, now 598 nrInstances down, now 597 ... nrInstances up, now 1014 (max 1014) nrFiles = 1232 nrInstances up, now 1015 (max 1015) nrFiles = 1233 Exception caught: dummy.txt (Too many open files) nrInstances down, now 1015 nrInstances down, now 1014 nrInstances down, now 1013 ... nrInstances down, now 709 nrInstances down, now 708 nrInstances down, now 707
Man måste frigöra resursern (den öppna filen) själv, med en metod som till exempel heter cleanup eller close. Programmet FileCounter4.java:
import java.io.*; import java.util.*; class MyFileObject { private final BufferedReader reader; static int nrInstances = 0; public MyFileObject(BufferedReader reader) { this.reader = reader; ++nrInstances; System.out.println("nrInstances up, now " + nrInstances); } public void finalize() { --nrInstances; System.out.println("nrInstances down, now " + nrInstances); } public void close() throws IOException { reader.close(); } } // class MyFileObject public class FileCounter4 { public static void main(String[] args) { try { int nrFiles = 0; while (true) { BufferedReader in = new BufferedReader(new FileReader("dummy.txt")); MyFileObject mfo = new MyFileObject(in); ++nrFiles; System.out.println("nrFiles = " + nrFiles); mfo.close(); } } catch (FileNotFoundException e) { System.out.println("Exception caught: " + e.getMessage()); } catch (IOException e) { System.out.println("Exception caught: " + e.getMessage()); } } // main } // class FileCounter4
Exempel:
Klassen ChatButtonWindow i programmet
Client5.java
från
föreläsning 8.
Ur programmet Client6.java:
En tumregel om namn: Ju större scope ett namn har, desto mer informativt ska namnet vara!private class ChatButtonWindow extends JFrame { private PrintWriter out; public ChatButtonWindow(PrintWriter outParameter) { super("Extra chat buttons"); out = outParameter; Container cp = getContentPane(); cp.setLayout(new FlowLayout()); JButton button1 = new JButton("Skicka förolämpning"); cp.add(button1); button1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { out.println("Ni, min herre, är en apa."); System.out.println( "Skickade en förolämpning."); } }); JButton button2 = new JButton("Skicka beröm"); cp.add(button2); button2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { out.println("Du är bäst!"); System.out.println("Skickade beröm."); } }); JButton button3 = new JButton("Avsluta"); cp.add(button3); button3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.out.println("Avslutar."); System.exit(0); } }); setSize(200, 200); setVisible(true); } } // class ChatButtonWindow
(Java följer skrivbordsschemat när man använder Windows-look-and-feelen.)
Programmet Sqrt1.java (som påminner mycket om appleten Sqrt.java från föreläsning 3):
I programmet Sqrt2.java, som använder Windows-look-and-feelen, har vi lagt till följande rader först i main, innan vi skapar det första fönstret:import javax.swing.*; import java.awt.event.*; import java.awt.*; public class Sqrt1 extends JFrame { private JLabel label = new JLabel("Här visas roten"); private JTextField text = new JTextField("Skriv talet här"); private JButton button = new JButton("Visa rot!"); private ButtonListener bl = new ButtonListener(); public Sqrt1() { super("Sqrt1"); button.addActionListener(bl); Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(text); cp.add(button); cp.add(label); } // Inre klass för att lyssna på knapp class ButtonListener implements ActionListener { // Hantera klick på roten-ur-knapp public void actionPerformed(ActionEvent event) { String numbuf = text.getText(); try { float num = Float.parseFloat(numbuf); float res = (float)Math.sqrt(num); if (Float.isNaN(res)) label.setText("Det måste vara ett positivt tal."); else label.setText("Roten ur " + num + " är " + res); } catch (NumberFormatException exc) { label.setText("'" + numbuf + "' är inte ett tal."); } } // actionPerformed } // class ButtonListener public static void main(String[] args) { Sqrt1 frame = new Sqrt1(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(200, 100); frame.setVisible(true); } } // class Sqrt1
String laf = UIManager.getSystemLookAndFeelClassName(); try { UIManager.setLookAndFeel(laf); // If you want the Cross Platform L&F instead, replace // UIManager.getSystemLookAndFeelClassName() // with // UIManager.getCrossPlatformLookAndFeelClassName() } catch (UnsupportedLookAndFeelException exc) { System.err.println("Warning: UnsupportedLookAndFeel: " + laf); } catch (Exception exc) { System.err.println("Error loading " + laf + ": " + exc); }
(Klicka på bilderna nedan för att se dem i större format.)
Swing-demons första exempel, körd på Linux:
Sådär ser det ut 2007. Resten av bilderna på SwingSet2 (både från Linux och Windows) är från 2003, och då var Javas gränssnitt betydligt fulare.
Linux igen, från 2003:
Swing-demons första exempel, körd på Windows. Eftersom vi använder default-look-and-feelen ser det precis likadant ut inuti fönstret:
Swing-demons första exempel, körd på Windows, men nu med Windows-look-and-feel påslagen:
Tydlig skillnad på look and feel kan man se i filväljardialogen. Här en filväljardialog med Windows-look-and-feel:
Sama filväljardialog med default-look-and-feel:
Några fler exempel ur Swing-demon. (Provkör gärna själv!)
Programmet ComboBoxTest.java:
import javax.swing.*; import java.awt.event.*; import java.awt.*; public class ComboBoxTest extends JFrame { String[] description = { "Tripp", "Trapp", "Trull" }; JComboBox c = new JComboBox(); JTextField t = new JTextField(5); public ComboBoxTest() { c.addItem("Tripp"); c.addItem("Trapp"); c.addItem("Trull"); t.setEditable(false); c.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e){ JComboBox box = (JComboBox)e.getSource(); t.setText(box.getSelectedItem().toString()); } }); Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(t); cp.add(c); } public static void main(String[] args) { ComboBoxTest frame = new ComboBoxTest(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(200, 80); frame.setVisible(true); } } // class ComboBoxTest
Programmet ButtonGroupTest.java:
import javax.swing.*; import java.awt.*; import java.awt.event.*; import javax.swing.border.*; public class ButtonGroupTest extends JFrame { public ButtonGroupTest() { super("ButtonGroupTest"); Container cp = getContentPane(); cp.setLayout(new FlowLayout()); ButtonGroup group = new ButtonGroup(); JPanel panel = new JPanel(); panel.setBorder(new TitledBorder("3 x JRadioButton")); JRadioButton button1 = new JRadioButton("Tripp"); group.add(button1); panel.add(button1); JRadioButton button2 = new JRadioButton("Trapp"); group.add(button2); panel.add(button2); JRadioButton button3 = new JRadioButton("Trull"); group.add(button3); panel.add(button3); cp.add(panel); } public static void main(String[] args) { ButtonGroupTest frame = new ButtonGroupTest(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(300, 100); frame.setVisible(true); } } // class ButtonGroupTest
Programmet ButtonGroups1.java:
// Adapted from Bruce Eckel's "Thinking in Java", 3d Ed, Chapter 14 import javax.swing.*; import java.awt.*; import java.awt.event.*; import javax.swing.border.*; public class ButtonGroups1 extends JFrame { private static String[] ids = { "Tripp", "Trapp", "Trull", }; public ButtonGroups1() { super("ButtonGroups"); Container cp = getContentPane(); cp.setLayout(new FlowLayout()); ButtonGroup group = new ButtonGroup(); JPanel panel = new JPanel(); panel.setBorder(new TitledBorder("3 x JButton")); for (int i = 0; i < ids.length; i++) { JButton button = new JButton(ids[i]); group.add(button); panel.add(button); } cp.add(panel); group = new ButtonGroup(); panel = new JPanel(); panel.setBorder(new TitledBorder("3 x JToggleButton")); for (int i = 0; i < ids.length; i++) { JToggleButton button = new JToggleButton(ids[i]); group.add(button); panel.add(button); } cp.add(panel); group = new ButtonGroup(); panel = new JPanel(); panel.setBorder(new TitledBorder("3 x JCheckBox")); for (int i = 0; i < ids.length; i++) { JCheckBox button = new JCheckBox(ids[i]); group.add(button); panel.add(button); } cp.add(panel); group = new ButtonGroup(); panel = new JPanel(); panel.setBorder(new TitledBorder("3 x JRadioButton")); for (int i = 0; i < ids.length; i++) { JRadioButton button = new JRadioButton(ids[i]); group.add(button); panel.add(button); } cp.add(panel); } public static void main(String[] args) { ButtonGroups1 frame = new ButtonGroups1(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(300, 300); frame.setVisible(true); } } // class ButtonGroups1
Programmet ReflectionTest.java:
Utskrifter:import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.lang.reflect.*; public class ReflectionTest extends JFrame { public static void showClassInfo(Object o) { Class c = o.getClass(); System.out.println("Klass: " + c.getName()); Class sc = c.getSuperclass(); System.out.println("Superklass: " + sc.getName()); Method[] methods = c.getDeclaredMethods(); System.out.println("Metoder:"); for (int i = 0; i < methods.length; ++i) System.out.println(" " + methods[i].getName()); Constructor[] constructors = c.getDeclaredConstructors(); System.out.println("Konstruktorer:"); for (int i = 0; i < constructors.length; ++i) System.out.println(" " + constructors[i].getName()); Field[] fields = c.getDeclaredFields(); System.out.println("Fält:"); for (int i = 0; i < fields.length; ++i) System.out.println(" " + fields[i].getName()); } public static void main(String[] args) { JButton button = new JButton("Hej"); showClassInfo(button); } } // class ReflectionTest
Klass: javax.swing.JButton Superklass: javax.swing.AbstractButton Metoder: writeObject getAccessibleContext paramString removeNotify getUIClassID updateUI isDefaultButton isDefaultCapable setDefaultCapable Konstruktorer: javax.swing.JButton javax.swing.JButton javax.swing.JButton javax.swing.JButton javax.swing.JButton Fält: uiClassID
Samma knappgruppsexempel som ovan, men nu med en metod som använder reflektion.
Programmet ButtonGroups2.java:
// Adapted from Bruce Eckel's "Thinking in Java", 3d Ed, Chapter 14 // Uses reflection to create groups // of different types of AbstractButton. import javax.swing.*; import java.awt.*; import java.awt.event.*; import javax.swing.border.*; import java.lang.reflect.*; public class ButtonGroups2 extends JFrame { private static String[] ids = { "Tripp", "Trapp", "Trull", }; private JPanel makeButtonPanel(Class klass, String[] ids) { ButtonGroup group = new ButtonGroup(); JPanel panel = new JPanel(); String title = klass.getName(); panel.setBorder(new TitledBorder(title)); for (int i = 0; i < ids.length; i++) { AbstractButton button = new JButton("failed"); try { // Get the dynamic constructor method // that takes a String argument: Constructor ctor = klass.getConstructor(new Class[] {String.class}); // Create a new object: button = (AbstractButton) ctor.newInstance(new Object[] {ids[i]}); } catch (Exception e) { System.out.println("Exception caught: " + e); } group.add(button); panel.add(button); } return panel; } public ButtonGroups2() { super("ButtonGroups2"); Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(makeButtonPanel(JButton.class, ids)); cp.add(makeButtonPanel(JToggleButton.class, ids)); cp.add(makeButtonPanel(JCheckBox.class, ids)); cp.add(makeButtonPanel(JRadioButton.class, ids)); } public static void main(String[] args) { ButtonGroups2 frame = new ButtonGroups2(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(300, 300); frame.setVisible(true); } } // class ButtonGroups2