Ich möchte in meiner Main ein Font laden. Darauf möchte ich von allen anderen Objekten zugreifen können. Also im Sinne einer globalen Variablen oder Konstanten.
Wie mache ich das?
Gruß
Heiko
Jaja, ich weiß, dies widerspricht den Idealen der objektorientierten Programmierung....
Du meinst, du willst den Klassen, die den Font brauchen, nicht immer Referenzen (zur Main) mitgeben? Hast du darüber nachgedacht, den Font einfach static zu machen? (Wahrscheinlich werden hier jetzt Kommentare folgen, die das als Misshandlung objektorientierter Programmierung geißeln werden, aber hey; übertreibt nicht )
Ja, genau! In verschiedenen Klassen steht bei mir Folgendes:
Code:
Font font;
ClassLoader cl =ClassLoader.getSystemClassLoader();
InputStream in = this.getClass().getResourceAsStream("font.TTF");
try {font = Font.createFont(java.awt.Font.TRUETYPE_FONT, in);}catch(Exception e){ font = null;}
font=font.deriveFont(Font.PLAIN,size);
g.setFont(font);
Und wenn ich mehrere Objekte aus diesen Klassen erzeuge, wird bei einem vollständigen RePaint das Applet sehr sehr langsam. Verständlicherweise, da ja das Font immer wieder neu geladen wird!
Wenn ich jetzt aber in die Main folgenden Code eingebe:
Code:
public static Font myfont() {
Font font;
ClassLoader cl =ClassLoader.getSystemClassLoader();
InputStream in = this.getClass().getResourceAsStream("font.TTF");
try {font = Font.createFont(java.awt.Font.TRUETYPE_FONT, in);}catch(Exception e){ font = null;}
font=font.deriveFont(Font.PLAIN,10);
return font;
}
erhalte ich beim Compilieren folgende Fehlermeldung:
compile:
[javac] Compiling 1 source file to /Applications/xampp/xamppfiles/htdocs/wb_e7/build
[javac] /Applications/xampp/xamppfiles/htdocs/wb_e7/source/Main.java:12: non-static variable this cannot be referenced from a static context
[javac] InputStream in = this.getClass().getResourceAsStream("font.TTF");
[javac] ^
[javac] Note: /Applications/xampp/xamppfiles/htdocs/wb_e7/source/Main.java uses or overrides a deprecated API.
[javac] Note: Recompile with -Xlint:deprecation for details.
[javac] 1 error
public static Font myfont() {
Font font;
ClassLoader cl =ClassLoader.getSystemClassLoader();
InputStream in = cl.getResourceAsStream("font.TTF");
try {font = Font.createFont(java.awt.Font.TRUETYPE_FONT, in);}catch(Exception e){ font = null;}
font=font.deriveFont(Font.PLAIN,10);
return font;
}
musst nur dran denken, die font auch wirklich nur einmal zu laden. also nicht einfach nur in eine statische methode auslagern, sondern auch noch in einer statischen variable speichern und wirklich nur einmal laden.
public class FontHolder{
private static Font font = null;
static{
Font font;
ClassLoader cl =ClassLoader.getSystemClassLoader();
InputStream in = cl.getResourceAsStream("font.TTF");
try {
font = Font.createFont(java.awt.Font.TRUETYPE_FONT, in);
font=font.deriveFont(Font.PLAIN,10);
}catch(Exception e){
font = null;
}
}
public static Font getFont(){
return font;
}
}
Hier wird der Font beim Laden der Klasse erzeugt und du brauchst dich um nichts mehr kümmern.
Habe ich das jetzt richtig verstanden: Das Font wird beim erstmaligen Laden der Klasse geladen und nicht bei jedesmal wenn ich ein Objekt daraus erzeuge?
Ja. Trotzdem (!) sollte man die FontHolder-Klasse ergänzen um einen privaten Konstruktor
Code:
public class FontHolder
{
/* private constructor to avoid instantiation */
private FontHolder() {}
private static Font font = null;
static{
Font font;
...
}
damit niemand auf die Idee kommt, sich Objekte einer Klasse zu erzeugen, von der es eigentlich keine Objekte geben muss (und darum auch nicht geben sollte oder darf)
ist ein statischer Initialisierungsblock, der eben nur einmalig beim erstmaligen Laden der Klasse aufgerufen wird.
Es gibt auch normale Initialisierungsblöcke:
Code:
public class Initializer{
//init
{
System.out.println("init");
}
public Initializer(){
super();
System.out.println("Konstruktor");
}
}
Bei diesem Beispiel wird jedes mal beim Erzeugen eines neuen Objektes der Block ausgeführt, und zwar gleich nach dem super Aufruf des Konstruktors (oder this).
Also würde
Code:
new Initializer();
diese Ausgabe produzieren:
Code:
init
Konstruktor
statische Initialisierungsblöcke werden sehr gerne beim Einbinden von C-Bibliotheken verwendet:
import java.io.*;
import java.awt.font.*;
import java.awt.*;
public class FontHolder{
private FontHolder() {}
private static Font font = null;
static{
Font font;
ClassLoader cl =ClassLoader.getSystemClassLoader();
InputStream in = cl.getResourceAsStream("font.TTF");
try {
font = Font.createFont(java.awt.Font.TRUETYPE_FONT, in);
font=font.deriveFont(Font.PLAIN,10);
}catch(Exception e){
font = null;
}
}
public static Font getFont(){
return font;
}
}
Wenn ich aus anderen Klassen den Aufruf
Code:
FontHolder fh = new FontHolder();
Font font = fh.getFont();
mache, erhalte ich lediglich das NULL Objekt zurück :-(
---------------
Die Ergänzung der FontHolder Klasse um
Code:
private FontHolder() {}
bringt mir beim Compilieren folgenden Fehlercode:
[javac] /Applications/xampp/xamppfiles/htdocs/wb_e7/source/TextTool.java:110: FontHolder() has private access in FontHolder
[javac] FontHolder fh = new FontHolder();
java.io.IOException: Stream closed
at java.io.BufferedInputStream.getInIfOpen(BufferedInputStream.java:134)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:254)
at java.io.BufferedInputStream.read(BufferedInputStream.java:313)
at java.awt.Font$1.run(Font.java:746)
at java.security.AccessController.doPrivileged(Native Method)
at java.awt.Font.createFont(Font.java:732)
at FontHolder.<clinit>(FontHolder.java:18)
Ich darf noch hinzufügen, dass das Laden des Fonts innerhalb der Objekte, in denen die Schrift benötigt wird, auch problemlos funktionierte.
Meine Klasse sieht im Moment so aus:
Code:
import java.io.*;
import java.awt.font.*;
import java.awt.*;
public class FontHolder{
private FontHolder() {}
private static Font font = null;
static{
// Font font;
ClassLoader cl =ClassLoader.getSystemClassLoader();
InputStream in = cl.getResourceAsStream("font.TTF");
try {
font = Font.createFont(java.awt.Font.TRUETYPE_FONT, in);
font=font.deriveFont(Font.PLAIN,10);
}catch(Exception e){
e.printStackTrace();
font = null;
}
if (font==null) {System.out.println("Font wurde NICHT geladen");}
}
public static Font getFont(){
return font;
}
}
In der 6letzen Zeile Frage ich ab, ob font==null ist. Und genau diesen Output erhalte ich in der Javakonsole.
Hm. Der Aufruf ist schon richtig. Wo genau die IOException herkommt ... ???:L hm.
Wenn du 100% (nicht 99.9% sondern 100%) sicher bist, dass er die "font.TTF" auch wirklich findet, könnte ich mir höchstens vorstellen, dass er bei der statischen Initialisierung irgendwie mit dem ClassLoader durcheinanderkommt. Du könntest die Klasse ggf. auch mal schreiben als
Code:
import java.io.*;
import java.awt.font.*;
import java.awt.*;
public class FontHolder{
private FontHolder() {}
private static Font font = null;
public static Font getFont()
{
if (font == null)
{
// Font font;
ClassLoader cl =ClassLoader.getSystemClassLoader();
InputStream in = cl.getResourceAsStream("font.TTF");
try {
font = Font.createFont(java.awt.Font.TRUETYPE_FONT, in);
font=font.deriveFont(Font.PLAIN,10);
}catch(Exception e){
e.printStackTrace();
font = null;
}
if (font==null) {System.out.println("Font wurde NICHT geladen");}
}
return font;
}
}
Aber das ist nur ins blaue geraten :? vielleicht hat jemand noch eine bessere Idee...
als sich der Code zum Laden des Fonts noch in der eigentlichen Klasse befand, funktionierte alles gut - bis auf dass bei 100 Instanzen das Applet äußerst zäh war
Also der folgende Code funktionierte definitiv:
Code:
Font font;
ClassLoader cl =ClassLoader.getSystemClassLoader();
InputStream in = this.getClass().getResourceAsStream("font.TTF");
try {font = Font.createFont(java.awt.Font.TRUETYPE_FONT, in);}catch(Exception e){ font = null;}
font=font.deriveFont(Font.PLAIN,size);
g.setFont(font);