*Markus: Laden eines Bildes nur sehr langsam

Hallo,

da meine Java-Kenntnisse noch relativ bescheiden sind, habe ich mich zu den Grundlagen mal ein bisschen mit dem Laden von Grafiken in ein Fenster beschäftigt.
Dabei verwendete ich Code aus verschiedenen Java-Beispielen. Jetzt ist es so, dass diese kleine Fenster, das hier erzeugt wird, immens lange zum Laden braucht. Das Fenster öffnet sich erst eine Sekunde nach Programmstart. Die Grafik darin erscheint erst zwei Sekunden nach Programmstart. Offensichtlich ist eine Menge Overhead vorhanden. Ich kann mir auch nicht vorstellen, dass ich für das Laden von Bildern Methoden überschreiben muss, wie ich es aber bisher nur sah. Wie macht man es richtig? Hier das Beispielprogramm:

  
import javax.swing.*;  
import java.awt.Toolkit;  
import java.awt.Graphics;  
import java.awt.Image;  
  
class Bild extends JComponent    {  
 private Image image = Toolkit.getDefaultToolkit().getImage("/home/markus/Bilder/ws.jpg");  
  
 @Override  
    public void paint(Graphics g) {  
        g.drawImage(image, 0, 0, this);  
    }  
}  
  
public class GrafikVersuch1   {  
  
 public static void main(String[] args) {  
  JFrame f = new JFrame("GrafikVersuch1");  
  f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );  
  f.setLocation(0, 0);  
  f.setSize(400, 200);  
  f.add( new Bild() );  
  f.setVisible(true);  
 }  
}  

Markus.

--
http://www.apostrophitis.at
Maschiene währe Standart Gallerie vorraus Packete Objeckte tollerant vieleicht Strucktur
  1. Hallo,

    Dabei verwendete ich Code aus verschiedenen Java-Beispielen. Jetzt ist es so, dass diese kleine Fenster, das hier erzeugt wird, immens lange zum Laden braucht. Das Fenster öffnet sich erst eine Sekunde nach Programmstart. Die Grafik darin erscheint erst zwei Sekunden nach Programmstart. Offensichtlich ist eine Menge Overhead vorhanden.

    Wie sieht die Prozessorbelastung während dieser 3 Sekunden aus?

    Die 1. Sekunden könnte der normale Overhead sein, der beim Laden der
    VM und inbesondere der viiieeelen Swing-Klassen benötigt wird. (Swing
    ist recht umfangreich. Auch wenn du nur wenig machst, wird da im
    Hintergrund recht viel geladen und initialisiert...)

    Daß dein Bild erst nach 2 Sekunden erscheint, mag damit zusammen-
    hängen, daß es asynchron geladen wird. Durch den Aufruf von
    Toolkit.getImage() wird das Laden des Bildes nur "angestoßen". Es
    wird erst bei der ersten Anforderung im Hintergrund geladen. Je nach
    Implementierung kann dies die genannten 2 Sekunden dauern.

    Im Zweifelsfall solltest du das Bild mal über die ImageIO-Klasse
    laden, so wie es im Java-Almanac beschrieben ist. Dabei werden die Daten
    sofort aus der Datei geladen und stehen somit auch sofort zur Verfügung.

    Ich kann mir auch nicht vorstellen, dass ich für das Laden von Bildern Methoden überschreiben muss, wie ich es aber bisher nur sah.

    Üblicherweise wird das in Java aber so gemacht. Es gibt zwar im Prinzip
    auch andere Möglichkeit. Die bequemste ist aber, eine Komponente
    von Component (oder einer anderen passenden Klasse) abzuleiten und
    die paint-Methode zu überladen um dann über das Graphics-Objekt das
    Bild zu zeichnen.
    Dein Code sieht im Prinzip gut aus.

    Gruß
    Slyh

    1. Hallo,

      Wie sieht die Prozessorbelastung während dieser 3 Sekunden aus?

      Sie steigt kurz an, was aber für das Starten von Programmen typisch ist.

      Die 1. Sekunden könnte der normale Overhead sein, der beim Laden der
      VM und inbesondere der viiieeelen Swing-Klassen benötigt wird. (Swing
      ist recht umfangreich. Auch wenn du nur wenig machst, wird da im
      Hintergrund recht viel geladen und initialisiert...)

      So etwas in die Richtung dachte ich mir schon.

      Daß dein Bild erst nach 2 Sekunden erscheint, mag damit zusammen-
      hängen, daß es asynchron geladen wird. Durch den Aufruf von
      Toolkit.getImage() wird das Laden des Bildes nur "angestoßen". Es
      wird erst bei der ersten Anforderung im Hintergrund geladen. Je nach
      Implementierung kann dies die genannten 2 Sekunden dauern.

      Ehrlich gesagt finde ich das schon etwas viel.

      Ich kann mir auch nicht vorstellen, dass ich für das Laden von Bildern Methoden überschreiben muss, wie ich es aber bisher nur sah.

      Üblicherweise wird das in Java aber so gemacht. [...]

      Hmmm, mit dieser Art zu programmieren muss ich mich wahrscheinlich noch anfreunden. Desweiteren frage ich mich, wie das vorher gehandhabt wurde, da das Überschreiben von Methoden doch erst seit Java 5 funktioniert.

      Markus.

      --
      http://www.apostrophitis.at
      Maschiene währe Standart Gallerie vorraus Packete Objeckte tollerant vieleicht Strucktur
      1. Hallo,

        Ich kann mir auch nicht vorstellen, dass ich für das Laden von Bildern Methoden überschreiben muss, wie ich es aber bisher nur sah.

        Üblicherweise wird das in Java aber so gemacht. [...]

        Hmmm, mit dieser Art zu programmieren muss ich mich wahrscheinlich noch anfreunden. Desweiteren frage ich mich, wie das vorher gehandhabt wurde, da das Überschreiben von Methoden doch erst seit Java 5 funktioniert.

        Nein, das ging schon immer. Das ist ja eine grundlegende Methodik der OOP und Java war schon immer _nur_ objektorientiert. Überschrieben wird/wurde eine Methode der Superklassen dann, wenn die Methode der abgeleiteten Klasse genau so heißt, die selben Argumente hat und den selben Objekttyp oder Datentyp zurück gibt, wie die Methode der Superklasse. Dass dies schon immer ging, beweist die Methode public String toString(). Diese ist in allen Objekten vorhanden, wird nur jeweils Objektspezifisch überschrieben.

        Die @Override-Notation ab 1.5 zeigt nur dem Compiler, dass hier eine Methode der Superklasse überschrieben wird. Er reagiert dann mit einem Compiler-Fehler, wenn das schief läuft. Wenn man es also macht, wie immer, kann man @Override weglassen. Und diese Notation erlaubt, dass der Rückgabewert nicht absolut identisch sein muss. Er kann nun auch ein vom Rückgabenwert der zu überschreibenden Methode abgeleiteter Objekttyp sein.

        viele Grüße

        Axel

        1. Hallo Axel,

          Und diese Notation erlaubt, dass der Rückgabewert nicht absolut identisch sein muss. Er kann nun auch ein vom Rückgabenwert der zu überschreibenden Methode abgeleiteter Objekttyp sein.

          Das geht unabhängig davon, ob man diese Annotation angibt oder nicht.

          Grüße

          Daniel

          1. Hallo,

            Und diese Notation erlaubt, dass der Rückgabewert nicht absolut identisch sein muss. Er kann nun auch ein vom Rückgabenwert der zu überschreibenden Methode abgeleiteter Objekttyp sein.
            Das geht unabhängig davon, ob man diese Annotation angibt oder nicht.

            Stimmt. Die Annotation @Override schützt also nur vor versehentlichem Nichtüberschreiben ;-)

            Beispiel: Main.java:

              
            class MyString1 {  
             String myStr = "Mein String";  
             MyString1(String s) {  
               myStr = s;  
             }  
             public String getMyString() {  
               return myStr;  
             }  
            }  
              
            class MyString2 extends MyString1 {  
             MyString2(String s) {  
               super(s + " erweitert");  
             }  
            }  
              
              
            class TestOverride1 {  
             TestOverride1() {  
               return;  
             }  
             public MyString1 test() {  
               return new MyString1("Hallo Welt1");  
             }  
            }  
              
            class TestOverride2 extends TestOverride1 {  
             TestOverride2() {  
               super();  
             }  
             //ueberschreibt test() in TestOverride1 mit JDK1.5;  
             //JDK1.4 wuerde einen Compiler-Fehler bringen,  
             //weil die Typen nicht uebereinstimmen.  
             public MyString2 test() {  
               return new MyString2("Hallo Welt2");  
             }  
            }  
              
            class TestOverride3 extends TestOverride2 {  
             TestOverride3() {  
               super();  
             }  
             //ueberschreibt test() in TestOverride2 _nicht_,  
             //weil die Argumente nicht uebereinstimmen  
             //@Override würde einen Compiler-Fehler bringen  
              
             //@Override  
             public MyString1 test(String s) {  
               return new MyString1("Hallo Welt3 " + s);  
             }  
            }  
              
              
            class Main {  
             public static void main(String[] args) {  
              TestOverride1 to1 = new TestOverride1();  
              TestOverride2 to2 = new TestOverride2();  
              TestOverride3 to3 = new TestOverride3();  
              System.out.println(to1.test());  
              System.out.println(to1.test().getMyString());  
              System.out.println(to2.test());  
              System.out.println(to2.test().getMyString());  
              //Das Folgende ruft die Methode .test() von TestOverride2 auf,  
              //weil kein Ueberschreiben stattgefunden hat.  
              System.out.println(to3.test());  
              System.out.println(to3.test().getMyString());  
             }  
            }  
            
            

            viele Grüße

            Axel

  2. Hallo Markus!

    ...

    ... Das Fenster öffnet sich erst eine Sekunde nach Programmstart.

    So lange kann es dauern - wenn Rechner schwach oder sonstwie beschäftigt ist. ;o)

    Die Grafik darin erscheint erst zwei Sekunden nach Programmstart. Offensichtlich ist eine Menge Overhead vorhanden.

    Das ist doch überraschend, wie groß ist das Bild?

    Ich kann mir auch nicht vorstellen, dass ich für das Laden von Bildern Methoden überschreiben muss, wie ich es aber bisher nur sah.

    Doch das ist die übliche Methode

    Wie macht man es richtig? Hier das Beispielprogramm:

    Richtig ist relativ - ich würd es so machen:

    Eine Klasse für das Bild:

      
    import java.awt.Graphics;  
    import java.awt.Image;  
    import java.io.File;  
    import javax.imageio.ImageIO;  
    import javax.swing.JComponent;  
    public class Bild extends JComponent    {  
      private Image image = null;  
      
      public Bild(String fileName){  
        File file= new File(fileName);  
        try {  
          image= ImageIO.read(file);  
        } catch (Exception e){  
          System.err.println(e);  
          System.exit(1);  
        }  
      }  
      
      @Override  
      public void paint(Graphics g) {  
        g.drawImage(image, 0, 0, this);  
      }  
    }  
    
    

    Eine Klasse zum Starten:

      
    import javax.swing.JFrame;  
    public class Viewer1 {  
     public static void main(String[] args) {  
        JFrame f = new JFrame("GrafikVersuch1");  
        f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );  
        f.setLocation(0, 0);  
        f.setSize(400, 200);  
        f.add(new Bild("ws.jpg") );  
        f.setVisible(true);  
     }  
    }  
    
    

    Und dann würde ich der Breite und Höhe des Bildes und ...

    Viel Spaß beim Tüfteln, du ist am richtigen Weg.

    Viele Grüße

    H-P Ortner