View Full Version : [JAVA] un dubbio o mistero da sfatare
questa banalissima classe se compilata da luogo a questo fatto per me misterioso ed alquanto curioso.
Se si assegna y=x il file generato è di 410 bytes mentre se si assegna x=x il file generato è di 409 bytes
perchè ?
class Test {
public static void main (String[] args){
int x = 0;
double y = 0;
y = x;
}
}
attenzione!!!!
non è FORTRAN :D
Cosa c'è di strano?? x e y sono di 2 tipi differenti, e diverso sarà il modo del compilatore di tradurlo in bytecode no??
Per farti capire quante differenze ci possono essere, guarda come viene disassemblato il codice della tua banale classe con y=x:
Disassembling: C:\Marco\varie\x\Test.class
-------- ------------------------------ -----
OFFSET FIELD NAME VALUE
-------- ------------------------------ -----
00000000 Signature CAFE BABE
00000004 Minor Version 3
00000006 Major Version 45
Constant Pool
00000008 Constant Pool Count 17
0000000a CONSTANT_Class Entry (1) (14)
0000000d CONSTANT_Class Entry (2) (16)
00000010 CONSTANT_Methodref Entry (3) Class (2) Name/Type (4)
00000015 CONSTANT_NameAndType Entry (4) Name (7) Type (5)
0000001a CONSTANT_Utf8 Entry (5) ()V
00000020 CONSTANT_Utf8 Entry (6) ([Ljava/lang/String;)V
00000039 CONSTANT_Utf8 Entry (7) <init>
00000042 CONSTANT_Utf8 Entry (8) Code
00000049 CONSTANT_Utf8 Entry (9) ConstantValue
00000059 CONSTANT_Utf8 Entry (10) Exceptions
00000066 CONSTANT_Utf8 Entry (11) LineNumberTable
00000078 CONSTANT_Utf8 Entry (12) LocalVariables
00000089 CONSTANT_Utf8 Entry (13) SourceFile
00000096 CONSTANT_Utf8 Entry (14) Test
0000009d CONSTANT_Utf8 Entry (15) Test.java
000000a9 CONSTANT_Utf8 Entry (16) java/lang/Object
000000bc CONSTANT_Utf8 Entry (17) main
000000c3 Access Flags ACC_SUPER
000000c5 This Class Test
000000c7 Super Class java/lang/Object
Interfaces
000000c9 Interfaces Count 0
Fields
000000cb Fields Count 0
Methods
000000cd Methods Count 2
Method #1
000000cf Access Flags ACC_PUBLIC ACC_STATIC
000000d1 Name main
000000d3 Type ([Ljava/lang/String;)V
000000d5 Attributes Count 1
000000d7 Attribute Name Code
000000d9 Bytes Count 44
000000dd Max Stack 2
000000df Max Locals 4
000000e1 Code Count 8
0 iconst_0
1 istore_1
2 dconst_0
3 dstore_2
4 iload_1
5 i2d
6 dstore_2
7 return
000000ed Handlers Count 0
000000ef Attributes Count 1
000000f1 Attribute Name LineNumberTable
000000f3 Bytes Count 18
000000f7 Lines Count 4
000000f9 Start PC 0
000000fb Line Number 5
000000fd Start PC 2
000000ff Line Number 6
00000101 Start PC 4
00000103 Line Number 8
00000105 Start PC 7
00000107 Line Number 3
Method #2
00000109 Access Flags
0000010b Name <init>
0000010d Type ()V
0000010f Attributes Count 1
00000111 Attribute Name Code
00000113 Bytes Count 29
00000117 Max Stack 1
00000119 Max Locals 1
0000011b Code Count 5
0 aload_0
1 invokespecial java/lang/Object/<init>()V
4 return
00000124 Handlers Count 0
00000126 Attributes Count 1
00000128 Attribute Name LineNumberTable
0000012a Bytes Count 6
0000012e Lines Count 1
00000130 Start PC 0
00000132 Line Number 1
Attributes
00000134 Attributes Count 1
Attribute #1
00000136 Attribute Name SourceFile
00000138 Bytes Count 0
0000013c Source File Test.java
infatti, questo mi aspettavo da te :)
non ho colto la differenza tra le due versioni :confused:
puoi separarle ? :)
ok ti posto anche l'altra...dammi un secondo :D
La versione con x=x;
Disassembling: C:\Marco\varie\x\Test.class
-------- ------------------------------ -----
OFFSET FIELD NAME VALUE
-------- ------------------------------ -----
00000000 Signature CAFE BABE
00000004 Minor Version 3
00000006 Major Version 45
Constant Pool
00000008 Constant Pool Count 17
0000000a CONSTANT_Class Entry (1) (14)
0000000d CONSTANT_Class Entry (2) (16)
00000010 CONSTANT_Methodref Entry (3) Class (2) Name/Type (4)
00000015 CONSTANT_NameAndType Entry (4) Name (7) Type (5)
0000001a CONSTANT_Utf8 Entry (5) ()V
00000020 CONSTANT_Utf8 Entry (6) ([Ljava/lang/String;)V
00000039 CONSTANT_Utf8 Entry (7) <init>
00000042 CONSTANT_Utf8 Entry (8) Code
00000049 CONSTANT_Utf8 Entry (9) ConstantValue
00000059 CONSTANT_Utf8 Entry (10) Exceptions
00000066 CONSTANT_Utf8 Entry (11) LineNumberTable
00000078 CONSTANT_Utf8 Entry (12) LocalVariables
00000089 CONSTANT_Utf8 Entry (13) SourceFile
00000096 CONSTANT_Utf8 Entry (14) Test
0000009d CONSTANT_Utf8 Entry (15) Test.java
000000a9 CONSTANT_Utf8 Entry (16) java/lang/Object
000000bc CONSTANT_Utf8 Entry (17) main
000000c3 Access Flags ACC_SUPER
000000c5 This Class Test
000000c7 Super Class java/lang/Object
Interfaces
000000c9 Interfaces Count 0
Fields
000000cb Fields Count 0
Methods
000000cd Methods Count 2
Method #1
000000cf Access Flags ACC_PUBLIC ACC_STATIC
000000d1 Name main
000000d3 Type ([Ljava/lang/String;)V
000000d5 Attributes Count 1
000000d7 Attribute Name Code
000000d9 Bytes Count 37
000000dd Max Stack 1
000000df Max Locals 2
000000e1 Code Count 5
0 iconst_0
1 istore_1
2 iload_1
3 istore_1
4 return
000000ea Handlers Count 0
000000ec Attributes Count 1
000000ee Attribute Name LineNumberTable
000000f0 Bytes Count 14
000000f4 Lines Count 3
000000f6 Start PC 0
000000f8 Line Number 5
000000fa Start PC 2
000000fc Line Number 8
000000fe Start PC 4
00000100 Line Number 3
Method #2
00000102 Access Flags
00000104 Name <init>
00000106 Type ()V
00000108 Attributes Count 1
0000010a Attribute Name Code
0000010c Bytes Count 29
00000110 Max Stack 1
00000112 Max Locals 1
00000114 Code Count 5
0 aload_0
1 invokespecial java/lang/Object/<init>()V
4 return
0000011d Handlers Count 0
0000011f Attributes Count 1
00000121 Attribute Name LineNumberTable
00000123 Bytes Count 6
00000127 Lines Count 1
00000129 Start PC 0
0000012b Line Number 1
Attributes
0000012d Attributes Count 1
Attribute #1
0000012f Attribute Name SourceFile
00000131 Bytes Count 0
00000135 Source File Test.java
Originariamente inviato da cn73
ok ti posto anche l'altra...dammi un secondo :D
vai che stasera tiro le orecchie al prof :D
mi aveva detto che a livello di bytecode non cambiava nulla :confused:
scusa cn
puoi metterli su due file txt separati ?
grazie 1000
Occhio però che questo non è bytecode... e Assembler... il bytecode lo puoi vedere aprendo il.class con un editor di testo...Cmq le differenze ci sono eccome...basta che li apri entrambi...cambiano parecchi valori esadecimali...
Originariamente inviato da misterx
scusa cn
puoi metterli su due file txt separati ?
grazie 1000
Sono in 2 post separati...fai un copia e incolla...
Originariamente inviato da misterx
vai che stasera tiro le orecchie al prof :D
mi aveva detto che a livello di bytecode non cambiava nulla :confused:
Se devi andare dal professore per rinfacciargli qualcosa ti consiglio di andarci preparato
http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html
Tra le molte cose che si scoprono in quelle pagine web, c'è anche la struttura e gli operatori del bytecode java.
...
x=x
è più corto di
y=x
perchè y è un double e x un int, ragion per cui il bytecode generato conterrà l'istruzione "i2d" (integer to double), appartenente alla "serie" degli operatori JVM di conversione dei tipi "T2T".
Dai un occhiata a alle specifiche della jvm, si scoprono veramente cose curiose ;)
&Ciao
Originariamente inviato da cn73
La versione con x=x;
0 iconst_0
1 istore_1
2 iload_1
3 istore_1
4 return
Non vorrei dire una stupidaggine, ma questo dovrebbe essere generato da un codice in cui manca la dichiarazione e l'assegnamento di y, cioè non
int x=0;
double y=0;
x=x;
ma
int x=0;
x=x;
Perchè manca un dconst (caricamento nello stack di un valore double costante) e un dstore (estrazione dallo stack e assegnamento a variabile di un valore double). Okkio se porti questo al prof, dovrebbe accorgersene
Originariamente inviato da PGI
Se devi andare dal professore per rinfacciargli qualcosa ti consiglio di andarci preparato
http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html
Tra le molte cose che si scoprono in quelle pagine web, c'è anche la struttura e gli operatori del bytecode java.
...
x=x
è più corto di
y=x
perchè y è un double e x un int, ragion per cui il bytecode generato conterrà l'istruzione "i2d" (integer to double), appartenente alla "serie" degli operatori JVM di conversione dei tipi "T2T".
Dai un occhiata a alle specifiche della jvm, si scoprono veramente cose curiose ;)
&Ciao
la mia era una battuta, ci mancherebbe che vado a sfidare un prof; amo la vita tranquilla :)
il discorso è nato in aula quando si parlava della promozione di un int che viene convertito o "promosso" in double durante la compilazione
invece sembrerebbe che non viene convertito permanentemente un int in un doble, ma viene inserito nel bytecode codice atto a promuovere un int in un double solo se ve ne fosse la necessità
ho capito giusto ?
Originariamente inviato da misterx
invece sembrerebbe che non viene convertito permanentemente un int in un doble, ma viene inserito nel bytecode codice atto a promuovere un int in un double solo se ve ne fosse la necessità
ho capito giusto ?
Direi di sì, però ho qualche dubbio circa la conversione del valore di una variabile. Devo rileggermi un po' di cose prima di dare una risposta "certa", ma a naso direi che la conversione permanente non esista, cioè una variabile int non diventerà mai un double, al massimo il suo valore può essere temporaneamente promosso a double durante un'assegnazione o un'operazione...ma devo controllare.
Parlando di linguaggio java, cosa significano le due definizioni (a) (b) ?
Sempre se le due definizioni sono corrette.
(a) metodo statico
String iString = String.valueOf(i);
(b) metodo classico
int iLung = iString.lenght();
Originariamente inviato da misterx
Parlando di linguaggio java, cosa significano le due definizioni (a) (b) ?
Sempre se le due definizioni sono corrette.
(a) metodo statico
String iString = String.valueOf(i);
(b) metodo classico
int iLung = iString.lenght();
a) statico o "di classe" è l'aggettivo usato per definire metodi o campi di cui esiste una sola istanza per classe. Se inizializzi 300 oggetti String, ogni volta che chiami "miaStringa123.valueOf(i)" quel "valueOf" ti riferisci ad un metodo che risiede in una locazione di memoria identica per ogni oggetto String. In pratica ll metodo è allocato in memoria 1 sola volta durante l'inizializzazione della classe String e ogni istanza di String "risponde" alla chiamata "valueOf" con il contenuto di quella parte di memoria.
Tutti i metodi/campi "static" di una classe vengono "realizzati" durante l'inizializzazione della classe. L'uso di un metodo o l'accesso ad un campo "static" che preceda l'inizializzazione della classe a cui appartengono produce automaticamente l'inizializzazione della classe: supponendo di avere una classe così composta:
public class MiaClasse {
public static int valore=0;
}
se in un'altra applicazione scriviamo
int numero=MiaClasse.valore;
l'accesso a "valore" produce l'inizializzazione preventiva di "MiaClasse". Nota che l'inizializzazione di classe è un processo eseguito 1 sola volta per ogni classe, a prescindere dal numero di istanze della classe e, nel caso dell'invocazione di un metodo static, anche senza creazione di un'istanza di classe.
b) "classico" è la prima volta che lo sento, ma per definire i metodi che non sono static di solito "tutto fa brodo".
A differenza dei metodi/campi "static" i metodi "classici" (o non-static) hanno una locazione di memoria assegnata che è diversa per ogni istanza della classe, il che equivale a dire che, nel nostro caso, esiste un "lenght()" per ogni oggetto String che crei. A differenza dei metodi/campi static l'allocazione di memoria è fatta in corrispondenza della prima chiamata al metodo. Supponiamo di avere una classe così:
public class MiaClasse {
public static void metodoStatico();
public void metodoClassico();
}
Se eseguissimo un codice così:
MiaClasse classe=new MiaClasse();
viene allocato e inizializzato lo spazio di memoria per "metodoStatico()", vale a dire che "metodoStatico()" esiste già mentre "metodoClassico()" non ha ancora un contenuto. Scrivendo:
classe.metodoClassico();
viene inizializzato anche "metodoClassico()" (l'inizializzazione dei metodi non-static è anch'essa eseguita 1 sola volta ma per ogni istanza della classe e non una volta per tutte).
In pratica la differenza tra i metodi static e quelli non static stà nel momento dell'inizializzazione e nell'unità del riferimento: il fatto che l'invocazione di uno "static" inizializzi le classi non ancora "realizzate" spiega anche come faccia la JVM a sapere quale sia il valore a cui deve accedere anche senza un'istanza della classe.
La sintesi non è il forte, vero? :D
&Ciao.
ho letto la tua risposta con super interesse ed ancora qualche dubbio mi è rimasto; prima però di chiedere lumi a proposito di static o non static faccio un'altra domanda :)
supponiamo che mi venga in mente di scrivermi una bella classe, ad esempio una classe che serve a contare le ricorrenze della lettera a in un testo
e supponiamo che mi interessa utilizzare un metodo presente già in un'altra classe
domanda:
quando scrivo la mia classe devo includere del codice che richiami l'altra classe per poter usare i suoi metodi ?
mah, mi sa che non sono ancora pronto per domande del genere
scusa per la poca chiarezza ma mi serve anche da verifica e cioè, se non so domandare significa che non ho ancora capito :)
Originariamente inviato da misterx
quando scrivo la mia classe devo includere del codice che richiami l'altra classe per poter usare i suoi metodi ?
Tralasciando il modificatore "static" la risposta è sì. Va detto però che il concetto di "inclusione del codice", che è (forse) proprio della programmazione modulare, mal si addice alla programmazione orientata agli oggetti (sopratutto con un linguaggio OO puro come Java). Cosa che si capisce abbastanza bene traducendo in Java quel "includere codice per usare i metodi di un'altra classe" che si riduce alla creazione di una nuovo oggetto a partire dal modello definito nella classe.
Object oggetto=new Object(); <-- tutto qua :)
oggetto.nomeMetodo(...) e via così...
Ci sarebbe a dire il vero anche un problema di visibilità della classe dei metodi e dei campi a cui vuoi accedere e che dipende dal rapporto tra gli oggetti che crei/usi e dai modificatori di visibilità che usi (public, private, protected e modificatore-default, applicato se non specifichi).
public applicato ad una classe consente la creazione di istanze da un package esterno a quello di appartenenza della classe (se non specifichi il modificatore risulta invisibile all'esterno del package). L'alternativa è l'uso del modificatore di default (scelto se non si specifica "public") che nega l'accesso all'esterno del package.
per campi e metodi, public consente l'accesso ovunque e comunque (classe/sottoclasse dello stesso package, classe/sottoclasse esterna al package);
private consente l'accesso alla sola classe in cui il metodo è stato definito;
protected nega l'accesso ad una classe esterna al package che non sia estensione della classe che definisce il metodo;
"default=nessun modificatore" limita l'accesso all'ambito del package (classi e sottoclassi esterne non "vedono" il metodo/campo).
Per le classi interne valgono le regole dei campi/metodi.
scusa ma non so se sono riuscito a farmi capire
supponiamo che io scriva la classe Negozianti() e che al suo interno abbia implementato il metodo PagoBancomat()
ora scrivo la classe Pizzeria() con all'interno i suoi bei metodi
un'altra classe di nome Cartoleria()
ora mi metto a scrivere un programma che fa uso di tali classi ma così come sono, posso ad esempio usare il metodo PagoBancomat() presente nella classe Negozianti() senza che io nel momento della fabbricazione delle classi Pizzeria() e Cartoleria() abbia messo il benchè minimo legame tra loro ?
esempio:
Pizzeria cliente = new Pizzeria();
cliente.PagoBancomat(); - posso fare così ?
Originariamente inviato da misterx
Pizzeria cliente = new Pizzeria();
cliente.PagoBancomat(); - posso fare così ?
Direi proprio di no.
I nomi delle classi suggeriscono però che qui si stia parlando di ereditarietà.
L'esempio che hai fatto sarebbe infatti valido se "Pizzeria" fosse "figlio" di "Negoziante" e "pagoBancomat" fosse un metodo ereditabile (praticamente non-private, salvo particolari usi).
Ad esempio:
public class Negoziante {
public void pagoBancomat() {...}
}
class Pizzeria extends Negoziante {}
class Applicazione {
Applicazione() {
Pizzeria cliente=new Pizzeria();
cliente.pagoBancomat(); //uso di un metodo "ereditato"
}
}
In ogni caso è indubbio il fatto che un oggetto non possa richiamare metodi che non ha (perchè non definiti nel modello di classe o non ereditati da classi "genitore")
parlando sempre delle classi
Negoziante()
Pizzeria()
mi hanno detto che in cima alla gerarchia esiste la classe o superclasse Object()
quindi
Object()
|
Negoziante()
|
Pizzeria()
dove pizzeria e sottoclasse di negoziante e di conseguenza negoziante è sottoclasse di object ma negoziante è superclasse di pizzeria
se io dichiaro a questo punt:
Object o;
posso scrivere
int conto = o.PagoBancomat();
dove PagoBancomat è metodo di negoziante :confused:
mi dicono di no, ma è corretto <'
Originariamente inviato da misterx
Object o;
posso scrivere
int conto = o.PagoBancomat();
dove PagoBancomat è metodo di negoziante :confused:
mi dicono di no, ma è corretto <'
Sarebbe uno strano tipo di ereditarietà, in cui i vivi fanno testamento per i morti :D
Non è corretto, l'ereditarietà funziona verso il basso, Object è l'apice di tutti gli oggetti Java e tutti gli oggetti Java "sono" Object e hanno i metodi di Object.
Negoziante è "figlio" di Object, per cui eredita i metodi di Object ma quest'ultimo non riceve i metodi di negoziante, per cui è valido scrivere:
new Negoziante().toString() <-- metodo che Negoziante eredita da Object
ma non new Object().pagoBancomat() <-- perchè "Object" non riceve alcunchè da Negoziante.
quindi volendo dare una direzione all'ereditarietà è sempre dal basso verso l'alto giusto ?
tornando all'esempio static non static:
System.out.println("test " + String.valueOf(15));
// -------- sopra è un esempio static e sotto no ? --------
String s = new String().valueOf(15);
System.out.println("test " + s);
Dipende da chi c'è in basso :D Io avrei detto dall'alto verso il basso se pensi all'ereditarietà come ad un albero genealogico (poco tecnico ma per me rende l'idea)
Object
|
estensione di Object (eredita i metodi di Object)
|
estensione dell'estensione (eredità i metodi di tutte le classi superiori).
Per quanto riguarda gli esempi che hai fatto, entrambi contengono l'uso di un oggetto "static",un "PrintStream" contenuto nella classe System a cui è stato dato il nome di "out" (in teoria out è un campo statico della classe System), e di un metodo "static" che è "valueOf(int)" appartenente alla classe String.
new String().valueOf() e String.valueOf() sono la stessa cosa (anche se il primo introduce l'inizializzazione superflua di un nuovo oggetto String). E per "stessa cosa" puoi intendere "fisicamente la stessa cosa", perchè in tutti e due i casi quel "valueOf()" richiama l'identica locazione di memoria.
ma una superclasse=padre agisce solo come una sorta di vigile ?
a quanto mi è sembrato di capire al suo interno incorpora solo metodi comuni a tutte le sottoclassi !
se tu dovessi fare un paragone tra classi e DLL cosa esprimeresti ?
Che non so neanche cosa sia una DLL :D. Non conosco abbastanza le librerie dinamiche per poter dire qualcosa di sensato.
Non è vero in assoluto che una superclasse possieda solo i metodi destinati ad essere ereditati dalle sottoclassi, però è vero che tutte le sottoclassi ereditano i suoi metodi (a patto che ciò sia consentito dai modificatori di visibilità).
La cosa forse più "interessante" è che la relazione tra un oggetto padre ed uno figlio è che il figlio può essere considerato sia come tipo-padre che come tipo autonomo. Considera ad esempio le seguenti classi:
public class Negoziante {
public String insegna;
public String getInsegna() {
return insegna;
}
}
class Ristorante extends Negoziante {
Ristorante() {
this.insegna="Ristorante";
}
}
public class Pizzeria extends Negoziante {
public Pizzeria() {
this.insegna="Pizzeria";
}
}
class Application {
Application() {
Negoziante[] negozi={new Pizzeria(), new Ristorante()};
for(int i=0;i<negozi.length;i++) {
stampaInsegna(negozi[i]);
}
}
public void stampaInsegna(Negoziante n) {
System.out.println(n.getInsegna());
}
public static void main(String[] args) {
new Application();
}
}
La possibilità di usare il tipo padre per accedere al contenuto dei figli permette al codice una particolare flessibilità con una semplicità disarmante (la flessibilità è dell'OOP, la semplicità di Java :D)
come mai nella superclass "negoziante" non appaiono riferimenti a ristorante() e pizzeria() ?
in questo caso "negoziante" è ancora una classe stratta ?
perchè in negoziante appare getInsegna() ? per essere condiviso da tutte le sue sottoclassi ?
public class Negoziante {
public String insegna;
public String getInsegna() {
return insegna;
}
}
class Ristorante extends Negoziante {
Ristorante() {
this.insegna="Ristorante";
}
}
public class Pizzeria extends Negoziante {
public Pizzeria() {
this.insegna="Pizzeria";
}
}
class Application {
Application() {
Negoziante[] negozi={new Pizzeria(), new Ristorante()};
for(int i=0;i<negozi.length;i++) {
stampaInsegna(negozi[i]);
}
}
public void stampaInsegna(Negoziante n) {
System.out.println(n.getInsegna());
}
public static void main(String[] args) {
new Application();
}
}
Originariamente inviato da misterx
come mai nella superclass "negoziante" non appaiono riferimenti a ristorante() e pizzeria() ?
in questo caso "negoziante" è ancora una classe stratta ?
perchè in negoziante appare getInsegna() ? per essere condiviso da tutte le sue sottoclassi ?
1) E' più facile se ti rivolgo io la domanda, perchè avrebbero dovuto esserci dei "riferimenti" a "ristorante" e "pizzeria" in "Negoziante"?
Ma spendiamo anche qualche parola su quel "riferimento".
In Java (e non solo) "riferimento" ha un suo significato: il "riferimento" è una versione ristretta del puntatore, particolarmente noto in C. Il puntatore è una rappresentazione dell'indirizzo di memoria di un valore, un array, una struttura o della posizione all'interno di un array o di una struttura.
A differenza del puntatore C il riferimento Java rappresenta esclusivamente l'indirizzo di memoria di un oggetto (in Java anche gli array sono considerati oggetti, +o-). Inoltre, mentre in C esiste tutta un'aritmetica dei puntatori, in Java le sole operazioni possibili con i riferimenti sono l'assegnamento (=)e il confronto per identità (==).
E vediamolo, questo magico riferimento:
Pizzeria una_pizzeria = new Pizzeria();
"una_pizzeria" è il nome di una variabile di tipo "reference" che punta ad un oggetto di tipo "Pizzeria". Per brevità si dice che "una_pizzeria" è un oggetto Pizzeria, ma in realtà è un puntatore (nella versione ristretta definità "riferimento").
A destra dell'uguale (new Pizzeria()) c'è la creazione dell'oggetto "vero", che va a finire in una locazione di memoria il cui indirizzo viene fatto puntare a "una_pizzeria" attraverso l'operatore "=".
2) Ecco una classe astratta:
public abstract class Negoziante {
}
Una classe dichiarata "abstract" non può essere istanziata direttamente. Per il resto è identica a tutte le altre classi immaginabili. E che te ne fai di una classe astratta allora, ti chiederai. Per motivi di design del codice a volte può essere una buona idea che certe classi superiori (di solito quelle che stanno sopra a tutte le altre) siano astratte in modo tale da creare un modello usabile solo a patto che si passi per una sua estensione.
Nel codice che è stato postato fin'ora in questo thread non c'è mai stata una classe astratta (salvo l'esempio qui sopra).
E' probabile che a lezione o sui libri che studi usino il termine "astrazione o astratto" per spiegare la relazione tra classi padre-figlio.
Va benissimo, ma tieni conto che dicendo "Negoziante è una classe 'astratta' rispetto a Pizzeria o Ristorante" si indica il fatto che Negoziante rappresenta un modello più generale rispetto a Pizzeria, perchè ad esempio ha delle caratteristiche generali come modello (es. un Negozio ha certe caratterisitiche che sono comuni a tutti i tipi di negozi, una pizzeria è un negozio per cui avrà le stesse caratteristiche di tutti i negozi più altre che la rendono una pizzeria).
3) Esatto, Negoziante ha un metodo getInsegna() e quel metodo viene condiviso (ereditato) da tutte le sue sottoclassi.
Il metodo è solo un esempio: supponendo che tutti i negozianti abbiano un insegna ho messo nella classe "generale" Negoziante un metodo per ottenere l'insegna, in modo tale che tutti gli oggetti figli lo ereditino.
Scusa ma nelle classi Ristorante() e Pizzeria() non è stato definito alcun metodo o sbaglio ?
Io il metodo lo vedo nella classe Negoziante
P.S.
Ho tolto il "this" in quanto non lo hanno ancora spiegato ed ho messo al suo posto il return in quanto ho pensato che si ottenga il medesimo effetto
public class Negoziante {
public String insegna;
public String getInsegna() {
return insegna;
}
}
class Ristorante extends Negoziante {
Ristorante() {
return Ristorante";
}
}
public class Pizzeria extends Negoziante {
public Pizzeria() {
return "Pizzeria";
}
}
Originariamente inviato da misterx
Scusa ma nelle classi Ristorante() e Pizzeria() non è stato definito alcun metodo o sbaglio ?
Io il metodo lo vedo nella classe Negoziante
nè Ristorante nè Pizzeria hanno un metodo "proprio" che non sia il costruttore...ma non sempre quello che si vede è quello che è.
Vediamo se così è più chiaro: questo qui sotto è il codice della classe "padre":
public class Negoziante {
public String insegna;
public String getInsegna() {
return insegna;
}
}
Questo qua è il codice di un figlio, come lo scrivi e vedi nel file sorgente:
public class Pizzeria extends Negoziante {
public Pizzeria() {
insegna="Pizzeria";
}
}
Ma poichè "Pizzeria" è figlio di "Negoziante", l'oggetto "Pizzeria" usato dalla JVM è fatto così:
public class Pizzeria extends Negoziante {
public String insegna;
public Pizzeria() {
insegna="Pizzeria";
}
public String getInsegna() {
return insegna;
}
}
Allora è sicuramente vero che nella classe che scrivi tu, Pizzeria, non viene definito alcun metodo, ma per via dell'ereditarietà è come se tu avessi già scritto tutti i metodi e i campi che erano presenti in "Negoziante".
Originariamente inviato da misterx
class Ristorante extends Negoziante {
Ristorante() {
return Ristorante";
}
}
public class Pizzeria extends Negoziante {
public Pizzeria() {
return "Pizzeria";
}
}
Dio mio, e questi due cosi qui sopra cosa sono? :D Un costruttore non dovrebbe avere un "return", o meglio non scrivere mai un "return" in un costruttore perchè non è necessario (in verità si può usare un'istruzione "return;" nel costruttore ma non ho mai visto un caso in cui fosse indispensabile usarlo che non fosse risolubile con un costruttore "sovraccaricato").
inviato da PGI
Dio mio, e questi due cosi qui sopra cosa sono?
piccoli tentativi per capire :D
scusa ma in definitiva posso dire che:
una sottoclasse eredita tutti i metodi delle sue superclassi ?
e poi:
se desidero accedere ai metodi di una sottoclasse avendo definento come riferimento una variabile della superclasse è sufficiente che utilizzi il (cast) operator :)
Negozio n;
System.out.println( ((Pizzeria)n).Insegna() );
Originariamente inviato da misterx
piccoli tentativi per capire :D
scusa me in definitiva posso dire che: una sottoclasse eredita tutti i metodi delle sue superclassi ?
ed una superclasse eredita solo i suoi metodi salvo che si utilizzi una (cast) operator ? :)
Buona la prima, una sottoclasse eredita tutti i metodi delle sue superclassi (sempre che siano visibili, un metodo dichiarato "private" ad esempio non è ereditabile).
La seconda è uno svarione, una superclasse non eredita mai niente, l'ereditarietà funziona in una sola direzione, dalla superclasse alla sottoclasse, le sottoclassi non passano mai nulla alle superclassi.
Occhio con il casting che è un'operazione più complessa di quanto non possa far sospettare: il fatto che l'ereditarietà consenta la conversione di un super-tipo nel sotto-tipo va presa con le molle.
Prendi l'esempio:
Negoziante n=new Pizzeria();
Benchè "n" sia marcato come tipo-negoziante in realtà è un oggetto Pizzeria: il casting "Pizzeria p=(Pizzeria)n" non cambia un oggetto Negoziante in Pizzeria (tant'è che sputa un'eccezione ClassCastException nel caso in cui "n" non sia un riferimento ad un oggetto Pizzeria). In estrema sintesi possiamo dire che il sistema di ereditarietà consente un certo tipo di casting ma il casting non ha niente a che vedere con l'ereditarietà.
Originariamente inviato da misterx
se desidero accedere ai metodi di una sottoclasse avendo definento come riferimento una variabile della superclasse è sufficiente che utilizzi il (cast) operator :)
Negozio n;
System.out.println( ((Pizzeria)n).Insegna() );
Che fai, modifichi mentre rispondo? :D
L'esempio traballa un po'. Le due righe di codice sono errate come "codice reale", ma mi sembra di capire che sia una sintesi del concetto.
Secondo il rapporto tra classi padre e figlio, il tipo figlio è anche tipo padre ma il padre non è "compatibile" con il figlio: in soldoni,
Pizzeria è anche un tipo Negoziante, ma Negoziante non è un tipo Pizzeria.
Se crei un riferimento di tipo Negozio "n" ad un oggetto Negozio e poi cerchi di fare un casting a Pizzeria ottieni un errore di tipo ClassCastException (eccezione nella conversione della classe) durante l'esecuzione, perchè l'oggetto puntato dal riferimento "n" (cioè quello che c'è in memoria) non è compatibile con il modello Pizzeria.
Creando invece un riferimento di tipo "Negozio" "n" ad un oggetto di tipo "Pizzeria" il casting di "n" a "Pizzeria" è valido in compilazione perchè il tipo pizzeria è compatibile (in quanto sottotipo) con Negozio, ed è valido in esecuzione perchè la locazione di memoria a cui punta il riferimento "n" contiene un oggetto Pizzeria.
Detta così sembra che non serva a niente :D, ma ha una sua utilità. Quando poi comincerai a mischiare oggetti padre e figli con interfacce padre e figlie la faccenda diventerà ancora più ingarbugliata, ma le regole che la gestiscono sono quelle due o tre di cui abbiamo parlato finora.
Originariamente inviato da PGI
Che fai, modifichi mentre rispondo? :D
(a)
Se crei un riferimento di tipo Negozio "n" ad un oggetto Negozio e poi cerchi di fare un casting a Pizzeria ottieni un errore di tipo ClassCastException (eccezione nella conversione della classe) durante l'esecuzione, perchè l'oggetto puntato dal riferimento "n" (cioè quello che c'è in memoria) non è compatibile con il modello Pizzeria.
(b)
Creando invece un riferimento di tipo "Negozio" "n" ad un oggetto di tipo "Pizzeria" il casting di "n" a "Pizzeria" è valido in compilazione perchè il tipo pizzeria è compatibile (in quanto sottotipo) con Negozio, ed è valido in esecuzione perchè la locazione di memoria a cui punta il riferimento "n" contiene un oggetto Pizzeria.
alt, sei tu che rispondi mentre io sto correggendo i miei erroracci :D
abbi pazienza però :)
puoi mettermi due esempi di dichiarazione per il tipo (a) e (b) da te espressi ?
grazie 1000
Supponiamo che Negozio e Pizzeria siano le classi cha abbiamo definito da qualche parte in questo thread, e quindi che Pizzeria sia un'estensione di Negozio:
a) Casting sintatticamente corretto (cioè compila) ma che genera un'eccezione in esecuzione:
Negozio n=new Negozio();
Pizzeria p=(Pizzeria)n;
b) Casting corretto (compila ed esegue):
Negozio n=new Pizzeria();
Pizzeria p=(Pizzeria)n;
La relazione di compatibilità generata dall'ereditarietà è univoca, il che significa che mentre è vero che un riferimento di tipo padre è compatibile con un oggetto di tipo figlio non è vero il contrario, cioè un riferimento di tipo figlio non è compatibile con un oggetto padre.
Tieni anche presente la distinzione tra oggetto e riferimento: i metodi a cui puoi accedere dipendono dal tipo associato al riferimento (ed è qui che entra in gioco la necessità a volte di fare il casting). Ad esempio:
public class Padre {
Padre() {...}
public void metodoDelPadre() {...}
}
public class Figlio extends Padre{
Figlio() {...}
public void metodoDelFiglio() {...}
}
Supponiamo di avere nel codice la seguente linea:
Padre a=new Figlio();
Se scrivessimo:
a.metodoDelFiglio();
otterremmo un errore in compilazione, il cui messaggio sarebbe +o- "metodoDelFiglio() non esiste".
Il che può significare una sola cosa, e cioè che il compilatore usa il modello scelto per il riferimento e non quello dell'oggetto in memoria (ed è così).
A questo punto arriva il casting. Sapendo che "a" punta ad un oggetto di tipo Figlio noi possiamo accedere al metodo "metodoDelFiglio()" usando un riferimento di tipo Figlio che punti alla stessa locazione di memoria usata da "a":
Padre a=new Figlio();
Figlio castA=(Figlio)a;
castA.metodoDelFiglio();
La domanda sorge spontanea, usando il modello del supertipo per il riferimento ai sottotipi, come si fa a sapere cosa c'è veramente nella locazione di memoria a cui punta il riferimento?.
La risposta ce l'ha data il buon Gosling&Co. con l'operatore "instanceof" che ha la particolarità di andare a curiosare direttamente in memoria "saltando" il modello del puntatore. Non so se ti interessi per cui evito di appesantire il post, diciamo che ho buttato il proverbiale sasso nello stagno :D
si si, instanceof lo usiamo :)
avevo scritto un'altra cosa ma preferisco scriverla domattina a mente riposata; non vorrei continuare a scrivere fesserie :D
cmq, otima discussione :)
sarai mica un prof eh ?:confused:
Non sono un prof. :D
La discussione ha il pregio di parlare di argomenti di cui si parla poco e per me è un utilissimo ripasso (e oltretutto mi aiuta a capire su quali punti è meglio che riprenda in mano un bel librone :D).
eheh, siamo alle interfacce
posso chiederti se sono una sorta di classe virtuale ?
ciè, un contenitore di comdo per metodi di altre classi ?
BOh! :D
Un'interfaccia è...un'interfaccia :D, non ho mai trovato una definizione che dicesse qualcosa di più.
E' vero che possono essere usate come contenitori di comodo per metodi di altre classi, ma è la cima dell'iceberg.
Basta pensare al fatto che le interfacce Java supportano l'ereditarietà multipla (mentre le classi possono essere figlie di una sola classe, le interfacce possono avere più genitori) ed entrano in gioco nel modello "polimorfico" degli oggetti (parolaccia che indica il fatto già citato di un riferimento di tipo superiore per puntare ad un oggetto "figlio", nel caso delle interfacce, un oggetto che ne implementi una è anche un oggetto del tipo-interfaccia).
mi sa che ci vorrebbe un bell'esempio :D
tu hai provato ad implementarne qualcuna ?
L'uso di una delle interfacce esistenti nelle librerie Java è praticamente inevitabile (basta pensare ad ActionListener nelle applicazioni grafiche). Anche in applicazione a consolle è piuttosto facile imbattersi in una delle interfacce esistenti (a meno di non limitarsi a scrivere "Hello world" e altre amenità).
Con le interfacce puoi generare strutture gerarchiche piuttosto articolate. L'esempio che segue è un caso di "polimorfismo estremo": un oggetto che "è" di 4 tipi diversi (usando le sole interfacce, con l'ereditarietà di classe la cosa può complicarsi anche di più).
La prima interfaccia definisce due costanti e un metodo astratto. Tutti i campi di un interfaccia sono per definizione "public static final".
public interface Nominabile {
String NOMINABILE="ha un nome";
String INNOMINABILE="ha un nome ma non puo' dirlo";
public String getNominabile();
}
Seconda interfaccia, altre due costanti ed un metodo:
public interface Tipo {
String TIPO_1="oggetto di tipo 1";
String TIPO_2="oggetto di tipo 2";
public String getTipo();
}
Terza interfaccia, figlia di Nominabile e di Tipo (ereditarietà multipla, supportata solo dalle interfacce):
public interface TipoNominabile extends Tipo, Nominabile {
public String getCombinazione();
}
Oggetto che implementa l'interfaccia "TipoNominabile":
public class UnOggetto implements TipoNominabile {
public String getTipo() {
return TIPO_1;
}
public String getNominabile() {
return INNOMINABILE;
}
public String getCombinazione() {
return TIPO_1+" "+INNOMINABILE;
}
public String toString() {
return "un oggetto";
}
}
Codice che mostra come l'ereditarietà multipla interagisce con il polimorfismo dei riferimenti:
class Application {
Application() {
UnOggetto o=new UnOggetto();
stampaUnOggetto(o);
stampaNominabile(o);
stampaTipo(o);
stampaTipoNominabile(o);
}
public void stampaNominabile(Nominabile n) {
System.out.println(n.getNominabile());
}
public void stampaTipo(Tipo t) {
System.out.println(t.getTipo());
}
public void stampaTipoNominabile(TipoNominabile tn) {
System.out.println(tn.getCombinazione());
}
public void stampaUnOggetto(UnOggetto o) {
System.out.println(o);
}
public static void main(String[] args) {
new Application();
}
}
A titolo di curiosità, quando passi ad un metodo un riferimento di tipo interfaccia in realtà non passi un'istanza dell'interfaccia ma di un oggetto che implementa l'interfaccia.
Una cosa simile capita quando in un metodo definisci una variabile di tipo interfaccia:
...corpo di un metodo
Interfaccia interf=new Interfaccia() {
//implementazione dei metodi astratti...
}
metodo(interf);
Per farla breve, "new Interfaccia()" è una scorciatoia: in esecuzione quello che accade è che la jvm crea un oggetto che implementa l'interfaccia usando la definizione dei metodi che hai fornito nella classe astratta.
scusa ma entro un attimo nell'argomento delle classi astratte in quanto se non ho capito male, le interfacce altro non sono che surrogati di classi astratte
abbiamo
A _________
| |
B C
A=superclasse
B e C = sottoclassi che condividono qualcosa con A ma non tra loro
come mai nella superclasse A devono comparire, sotto forma di dichiarazioni abstract, i metodi implementati in B e C ?
forse sono una sorta di segnalatore che evitano alla JVM di cercare in A ciò che non troverebbero e dicono sempre alla JVM di cercare direttamente attraverso il riferimento comune nella classe istanziata alla quale punta il riferimento ?
spero di essere stato chiaro :)
La risposta potrebbe essere "ni" ad entrambe le questioni.
Per certi aspetti è vero che le interfacce sono versioni "minori" delle classi astratte (perchè una classe astratta può avere metodi con un corpo mentre un'interfaccia no)
Per la seconda domanda, non è del tutto esatto dire che una superclasse debba avere dei metodi astratti: l'uso del modificatore abstract ha uno scopo ulteriore rispetto alla semplice ereditarietà ed in particolare costringe ogni sottoclasse che non sia a sua volta "abstract" ad avere una propria definizione del corpo del metodo.
Hai però toccato (volontariamente o meno ;) ) un argomento su cui ho qualche perplessità (e che stò approfondendo) e che riguarda indirettamente il supporto Java al binding statico e dinamico: personalmente credo che Java supporti entrambe i tipi di "collegamento" ma, come si usa dire, al momento non ne ho la prova...tra qualche centinaia di pagine web ti saprò dire con più precisione.
Mi "riservo" spiegazioni ulteriori in futuro :D
io di dubbi ne ho anche di più, più proseguo e più ne colleziono
stavo leggendo QUI (http://nicchia.ingce.unibo.it/oop/web/4-abstract-classes.html)
dove è presente la costruzione step by step di alcune classi astratte/concrete
peccato che l'autore non dice cosa accadrebbe se le superclassi non le dichiara abstract :confused:
posso lavorare con le soli classi concrete allo stesso modo di quelle astratte ?:confused:
se lavoro con tutte classi concrete perdo l'ereditarietà ?
Dal punto di vista dell'ereditarietà non c'è differenza tra classi astratte e non astratte.
Quello che cambia è che non si può creare un nuovo oggetto a partire da un modello di classe astratta.
es.
abstract class ClasseAstratta{...}
ClasseAstratta a=new ClasseAstratta() <- errore (in compilazione)
Il motivo dipende da come la jvm inizializza gli oggetti: l'operatore "new" comporta la "creazione" di tutti i metodi e campi dichiarati in una classe. Poichè le classi astratte hanno alcuni metodi senza corpo l'operazione non è ammessa.
In pratica puoi sicuramente lavorare con classi "normali" come faresti con classi astratte, ma non puoi usare le classi astratte come faresti con quelle normali.
E allora che cavolo se ne fa uno delle classi astratte?
Una classe astratta può essere necessaria quando nella fase di progettazione scopri che avrai bisogno di un riferimento polimorfico "esclusivamente dinamico". Noterai che di solito gli esempi che trovi sull'argomento sono piuttosto simili:
superclasse astratta che indica un genere (veicolo, animale o mammifero ecc... la fantasia abbonda :D ) e sottoclassi che definiscono un tipo del genere (auto, moto, cane gatto ecc...).
la classe che indica il genere è usata proprio per esemplificare la necessità (o l'utilità) di un riferimento "generico" che può assumere in concreto svariate forme (una per tipo).
Originariamente inviato da PGI
Una classe astratta può essere necessaria quando nella fase di progettazione scopri che avrai bisogno di un riferimento polimorfico "esclusivamente dinamico".
cioè se la classe non è astratta non può esistere il polimorfismo ?
Originariamente inviato da misterx
cioè se la classe non è astratta non può esistere il polimorfismo ?
Nain! Il polimorfismo in sè dipende dall'ereditarietà (che coinvolge le classi in genere, astratte o meno).
Facciamo un esempio:
abstrac class ClassA {
abstract void doSome();
}
class Child extends ClassA {
void doSome{
System.out.println("Ciao");
}
}
riferimento polimorfico:
ClassA pointer;
Child child=new Child();
pointer=child;
pointer.doSome() <- questo doSome() non è mai quello della classe-tipo usata per il puntatore, il suo contenuto dipende sempre dalla classe a cui appartiene l'oggetto puntato.
Con classi "comuni":
class Parent {
public void doSome() {
System.out.println("Ciao");
}
}
class Child extends Parent {
}
Parent pointer;
Child child=new Child();
pointer=child;
pointer.doSome(); <- questo metodo è quello del tipo usato per il puntatore (cioè la classe genitore) a meno che la sotto-classe non lo ridefinisca.
stai diventato sempre più complicato :confused:
ascolta, propongo di tenerci il codice sotto per ragionare sulle classi astratte, concrete ed interfacce: che ne dici ?
nel frattempo mi manda in output null :(
class Negoziante {
public String insegna;
public String getInsegna() {
return insegna;
}
}
class Pizzeria extends Negoziante {
public String Pizzeria() {
return "Pizzeria";
}
}
class Ristorante extends Negoziante {
public String Ristorante() {
return "Ristorante";
}
}
:D:D:D
class Pgi {
public static void main (String[] args) {
Negoziante n = new Pizzeria();
System.out.println(n.getInsegna());
System.out.println("ciao");
}
}
Di che ti taglierei le dita :D
Negoziante n=new Pizzeria(): ok
System.out.println(n.getInsegna()): ok, ma qual'è il metodo "getInsegna()" che viene chiamato?
Poichè la sottoclasse Pizzeria non sovrascrive quello della superclasse è il metodo "getInsegna()" di Negoziante.
E va benissimo. Ora dobbiamo guardare quello che fa il metodo "getInsegna()" di Negoziante: restituisce la stringa "insegna".
Passiamo allora al campo "insegna" di Negoziante: è ereditato da Pizzeria? Si. Pizzeria definisce un valore per "insegna" (cioè pizzeria sovrascrive il campo o richiama un metodo che influenza il valore del campo) ? No.
Allora che valore ha "insegna" nell'oggetto di tipo Pizzeria? Ha il valore "originale", definito dalla superclasse Negoziante.
E il suo valore è null (insegna è un campo non inizializzato, in java punta automaticamente a "null" se è un oggetto) -> risolto il mistero.
Prima di passare a classi astratte e interfacce è meglio maneggiare un po' le classi "normali" (sbagliando si impara moltissimo, ma è meglio sbagliare una cosa alla volta).
class Pizzeria extends Negoziante {
public String Pizzeria() {
return "Pizzeria";
}
}
Cosa c'è che non va qui: in questo caso "Pizzeria()" non è il costruttore della classe. A scanso di equivoci, un costruttore non è un metodo con lo stesso nome della classe ma è un metodo che oltre ad avere lo stesso nome della classe non ha un tipo restituito (nè void nè primitivi nè oggetti). Quindi Pizzeria è una sottoclasse di Negoziante che definisce un metodo "Pizzeria()" che, se chiamato, restituisce una stringa con la parola "Pizzeria".
E ha poco a che vedere con lo scopo delle classi (ereditarietà e polimorfismo).
Il risultato presumibile si ottiene invece dando a Pizzeria un costruttore che sovrascriva il campo "insegna" così:
class Pizzeria extends Negoziante {
Pizzeria() {
insegna="Pizzeria";
}
}
Dovresti provare a fare qualche esercizio sull'ereditarietà creando qualche gerarchia di classi.
Siccome il thread assomiglia sempre di più ad una chat, che a me va benissimo, ma agli amministratori del forum un po' meno :D (per ottime ragioni tuttavia), ti mando in pvt la mia e-mail ;).
&Ciao.
misterx quand'è che la smetterai di metterti a culo busone ? :p
Originariamente inviato da a2000
misterx quand'è che la smetterai di metterti a culo busone ? :p
:confused:
uhm, sarai mica amante esclusivo del fai da te e = (super orgoglioso) eh ???
:O aho, mi faccio solo da me
lasiammo :sofico: stare va, che entrerei in un argomento un pò troppo spinoso ;)
"uhm" , probabilmente credo nelle tue capacità più di quanto ci creda tu stesso ...
forse sei pigro ...
ti devi solo applicare ...
Originariamente inviato da a2000
"uhm" , probabilmente credo nelle tue capacità più di quanto ci creda tu stesso ...
forse sei pigro ...
ti devi solo applicare ...
pigro non molto, arrugginito parecchio :D
cmq, è risaputo che è più facile imparare java per chi non conosce alcun linguaggio di programmazione
per chi come me che conosce già un linguaggio imperativo come il C si trova in una posizione leggermente sfavorevole; java è molto astratto, il C lo è molto meno
ma sto rimediando grazie anche alla pazienza degli utenti di questo forum :)
l'importante è tenere duro :muro:
nei post, come nella vita, conta solo l'inizio e la fine:
Originariamente inviato da misterx
pigro non molto, arrugginito parecchio :D
....
l'importante è tenere duro :muro:
vai tranquillo l'uccello non si arrugginisce mai ! :D
Originariamente inviato da a2000
nei post, come nella vita, conta solo l'inizio e la fine:
vai tranquillo l'uccello non si arrugginisce mai ! :D
cmq, so cosa intendi ;)
sono entrambi organi cavernosi ! :huh:
e con funzioni intercambiabili a seconda dell'oggetto su cui vengono applicati ! :D :rotfl:
POLIMORFISMO = La capacita' di ottenere comportamento specializzato, invocando un servizio generico, attraverso le informazioni sul tipo dell'oggetto al quale si richiede il servizio, che possono essere ottenute staticamente (static-binding), oppure dinamicamente (late-binding).
vedi misterx bisogna avere un approccio funning alla conoscenza:
se la gente ragionasse con l'uccello e scopasse con la testa (non in senso figurato) il mondo probabilmente andrebbe meglio.
se non altro ci sarebbero molti meno calvi in giro !!!!
:rotfl: :rotfl:
devo approfondirlo sto' concetto di polimorfismo ...
ma un uso polimorfo (nel senso suddetto) degli oggetti non confligge con l'ereditarietà ???? :confused:
Originariamente inviato da a2000
devo approfondirlo sto' concetto di polimorfismo ...
ma un uso polimorfo (nel senso suddetto) degli oggetti non confligge con l'ereditarietà ???? :confused:
ti stai dando la zappa zui piedi ?? :D :D :D
no, non vorrei che un uso polimorfo della testa possa avere gli stessi sterili effetti di un uso polimorfo che so' .... del culo, ecco ! :D
Originariamente inviato da a2000
no, non vorrei che un uso polimorfo della testa possa avere gli stessi sterili effetti di un uso polimorfo che so' .... del culo, ecco ! :D
sisi:mc:
Non ti perdere di coraggio se ti tocca lavorare molto e raccogliere poco…
:friend:
beato te che non hai na mazza da fare :p
misterx tu sei la bocca della verità ! :D
(ma limitati a quella. :p)
Originariamente inviato da a2000
misterx tu sei la bocca della verità ! :D
(ma limitati a quella. :p)
ho capito, il siediti ce lo metti tu :D :D
basta, che ci bannano,........ ed avrebbero ragione :)
Originariamente inviato da misterx
int x = 6;
int y = 15;
y = (x = y * 2) + (x++);
quanto valgono secondo voi x ed y dopo gli assegnamenti e perchè assumono tali valori ?
x = 31
y = 60
Vai a pigliare certe gatte da pelare... :D
Ecco perchè Y non è 61 o 62.
Di fronte ad un'operazione matematica la jvm riserva un pezzettino di memoria (detta pila degli operandi) per infilarci temporaneamente i valori estratti dalle variabili usate. L'estrazione (bytecodeop Tload, dove T è il tipo) separa il valore nella pila degli operandi da quello contenuto nell'indirizzo associato alla variabile.
Questa "separazione" avviene nell'ordine stabilito dagli operatori che usi nel calcolo.
Ecco cosa succede nella nostra operazione:
"y =" (la parte a sinistra) è l'ultima operazione eseguita (un istore, sempre in bytecode).
La prima è la parte a sinistra dell'addizione (perchè usi un operatore + tra due elementi). A x viene assegnato il valore estratto da y (15) moltiplicato per due, x diventa 30. Il valore di x viene estratto e infilato nella pila degli operandi.
30 viene sommato al risultato dell'operazione a destra (x++). che è...30.
Perchè? Perchè l'incremento destro (x++) opera dopo che il valore è stato estratto (a differenza dell'incremento sinistro ++x): e non è una questione di priorità tra operatori (le parentesi non influenzano il risultato).
E destra pertanto la jvm estrae il valore di x (30), lo infila nella pila degli operandi, poi assegna alla variabile x il valore di x+1.
A questo punto x è già diventato "31" ma l'operazione è stata eseguita sul valore di x precedente l'incremento.
E allora Y=60 e X=31.
Usando un incremento sinistro è chiaro che risulterà Y=61 e X=31.
M'hai fregato un'altra volta! :eek: , ci ho messo un secolo a scrivere :D
Originariamente inviato da misterx
ho capito, il siediti ce lo metti tu :D :D
...
no, come vedo, ce lo metti sempre tu ! :ciapet:
Originariamente inviato da a2000
no, come vedo, ce lo metti sempre tu ! :ciapet:
te l'ho già detto ma non mi stufo di ripetermi.......
esci dalla torre d'avorio e collabora col resto del mondo, che è meglio..........
:O sei troppo orgoglioso, chi fa da se sbaglia per tre :D
Originariamente inviato da PGI
M'hai fregato un'altra volta! :eek: , ci ho messo un secolo a scrivere :D
scusa PGI ma sto anche affrontando quel ragazzone :D di a2000
c'è chi invecchiando migliora ma ad a2000, quando lo hanno imbottogliato, mica glielo hanno messo il tappo :D:D:D
cmq, avavo postato............
int x = 6;
int y = 15;
y = (x = y * 2) + (x++);
quanto valgono secondo voi x ed y dopo gli assegnamenti e perchè assumono tali valori ?
x = 31
y = 60
in quanto ho trovato gente che mi dice che il risultato cambia in funzione del linguaggio, priprità differenti ??? :confused:
Gatta da pelare 2, la vendetta.
Il risultato di quell'espressione (e di tutte le espressioni) dipende da priorità e associatività degli operatori, oltre all'implementazione dell'incremento destro (in Java prima estrae e poi incrementa, non è detto che altri linguaggi si comportino allo stesso modo).
La priorità determina quale operatore è preso in considerazione per primo.
L'associatività (sinistra o destra) determina quale tra due operatori aventi uguale priorità verrà eseguito per primo.
Tieni conto che anche le parentesi sono un operatore (a priorità massima e associativo a sinistra in Java).
Esempio super-classico sull'associatività (lo trovi in ogni libro, è sempre lo stesso :D ):
y=10;
z=10;
x = y - z +10
+ e - hanno priorità uguale e associatività verso sinistra: questo significa che tra i due sarà eseguito per primo quello che si trova più a sinistra.
Quindi prima viene eseguito "y-z" (=0) poi "+10" e x diventa 10.
Se l'associatività fosse verso destra il risultato sarebbe -10.
Ora, in Java l'associatività segue le regole comuni della matematica, ma essendo una proprietà definità nella progettazione del linguaggio di programmazione niente esclude che, per qualche ragione si spera, un altro linguaggio dia agli operatori un'associatività diversa.
approfitto ancora un pò della tua pazienza e voglia di ripassare :D
classe astratta non vuol dire she sia necessariamente una superclasse vero ?
:)
E' esatto. Ma se ti chiedessi perchè una classe astratta non è necessariamente una super-classe, cosa risponderesti?
Per me è importante soffermarsi a lungo su questi aspetti, perchè nei linguaggi di alto livello (Java, c++ &co.) la conoscenza di come si scrive un ciclo for o di cosa faccia il programma quando dichiari un int contano come il due di picche: qui la potenza stà nella produttività e se conosci veramente le regole che "dirigono" le relazioni tra oggetti puoi passare dall'idea all'applicazione in un tempo incredibilmente breve, altrimenti ti ritrovi a trattare gli oggetti come se fossero variabili un po' più complicate.
Originariamente inviato da PGI
E' esatto. Ma se ti chiedessi perchè una classe astratta non è necessariamente una super-classe, cosa risponderesti?
una domanda/risposta potrebbe essere:
perchè sento la necessità di creare classi astratte ?
e rispondendomi:
per riunire(dirigere) attraverso una superclasse (dichiarando al suo interno i metodi astratti delle sottoclassi) sottoclassi che hanno qualcosa in comune tra loro
ma perchè creare una superclasse concreta sopra ad una classe astratta?
il tempo di pensarci e torno
:)
Originariamente inviato da PGI
supporto Java al binding statico e dinamico
Mi auto-cito per chiudere una "finestra lasciata aperta".
In Java esistono entrambe le forme di "binding", il "late binding" e l' "early binding".
Bella forza lo dice Eckel, penseranno molti. E' vero, ma per chi non si fidasse dell'autore, ci sono tre righe nelle specifiche del linguaggio Java (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Concepts.doc.html#16348) in cui è confermato che di fronte al modificatore "final" (e quindi anche a "private") il riferimento al metodo viene sostituito dal contenuto del metodo, il che corrisponde appunto al modello di collegamento statico.
Do una risposta alla domanda che ti avevo fatto:
Astrazione (e accesso/visibilità) ed ereditarietà appartengono a due aspetti complementari della programmazione OOP.
Astrazione e visibilità (public, protected, private) definiscono gli attributi di una classe (in sintesi il "com'è fatta").
L'ereditarietà è la disciplina della relazione tra classi e comprende l'estensione (che determina le relazioni padre-figlio) e il polimorfismo (conseguenza dell'estensione).
L'ereditarietà dipende dagli attributi della classe genitrice ma i due aspetti sono concettualmente separati, dal che si capisce perchè l'uso di una "tecnica" appartenente alla definizione del modello (ad esempio proprio "abstract") non dipenda dal rapporto ereditario.
Per quanto riguarda la necessità di creare classi astratte non è una necessità, come non lo è un approccio strettamente OOP quando usi Java (se crei un'applicazione usando una sola classe per fare tutto, magari eviti pure, per quanto possibile, di usare oggetti predefiniti nelle librerie Java, beh, quell'applicazione è OOP quanto io sono un sarchiapone).
Personalmente credo a quanti scrivono che la programmazione orientata agli oggetti è sopratutto pensare alla propria applicazione come se fosse il prodotto delle relazioni tra "entità vive". Gli oggetti nascono, muoiono e interagiscono (tanto per capirci, poi la faccenda la puoi immaginare come preferisci) ma soprattutto hanno delle caratteristiche che li distinguono. Il codice sorgente di un'applicazione scritta con un approccio OOP non deve essere una serie sterile di operazioni eseguite in una sequenza più o meno dipendente dalle azioni di chi la userà: è un mondo di tanti piccoli esserini che vivono tra loro che quanto arriva l'utente devono dire "Hey ragazzi, è arrivato l'imperatore".
La cosa è più importante di quanto il mio delirio filosofico faccia supporre :D
Tornando alle classi astratte, l'utilità si presenta quando nella gerarchia che crei un modello puro o semipuro rappresenta meglio la realtà che stai cercando di descrivere.
Ad esempio, creando una gerarchia di oggetti che rappresentino l'abusato modello animale, la superclasse "mammifero" dovrebbe essere un modello puro o semi-puro, perchè non esiste il "mammifero" ma esisono animali che sono mammiferi (in OOP, non è coerente che "mammifero" sia istanziabile direttamente).
&Ciao.
Originariamente inviato da PGI
...
è un mondo di tanti piccoli esserini che vivono tra loro che quanto arriva l'utente devono dire "Hey ragazzi, è arrivato l'imperatore".
La cosa è più importante di quanto il mio delirio filosofico faccia supporre :D
hai ragione è terribilmente più importante.
nel mio mondo non esistono esserini e imperatori ma furie incazzate che quando vedono il presunto imperatore non dicono ma .... fanno !
e hanno tutto il mio plauso.
mi pare, e vorrei sbagliarmi, che ci sia qualcosa di "ereditariamente" servile nel mondo dell'informatica.
ti vengo incontro:
non è che la classe "informatico" sia derivata dalla superclasse "servo" ?
esatto
come chimico che deriva da benzinaio :D
Originariamente inviato da a2000
ti vengo incontro:
non è che la classe "informatico" sia derivata dalla superclasse "servo" ?
Ammetto di essere un po' restio a risponderti a2000, perchè da un lato fai un uso inconsueto ma straordinario dell'italiano (non so quanti lo apprezzino ma a me piace e parecchio), dall'altro non sempre capisco ciò che dici (non ho fama di volpe in arguzia ma ho un apprezzabile livello di sincerità :) )
Sfortunatamente non ho preso la strada dell'informatica (benchè adori allo stesso modo quella che, invece, ho scelto), non so confermarti o meno se i programmatori siano "servi" (ma qui potrei aver frainteso il significato). Per quanto ho letto potrei dire che alcuni paiono un po' poco critici verso le informazioni che circolano ma potrebbe essere una questione di scarso interesse "teorico".
In Italia, almeno, perchè a guardare per il mondo, specialmente la parte anglofona, il panorama scade e parecchio: forsè lì l'informatico non estende "servo" ma di sicuro implementa "spocchia".
&Ciao.
Cosa hai studiato PGI ? :)
Ho studiato legge.
Per spegnere la modalità chat-line, se sei interessato ad una serie di autorevoli curiosità su Java, io caldeggio la lettura di questa serie di pagine web (che forse ho già segnalato)The JavaTM Virtual Machine Specification (http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html), che coprono tutti i concetti dell'OOP per come sono stati affrontati nella progettazione di Java.
con gli avvocati è gara persa.
A _________
| |
B D
|
C
così facendo:
A prova = new C();
la classe "C" a quali metodi può accedere ?
C ha i metodi non-private definiti nelle classi superiori (quindi B e A), idem per i campi.
Poichè tuttavia "prova" è un riferimento di tipo "A", in compilazione usando "prova" puoi accedere ai soli metodi definiti nella classe A (benchè l'oggetto a cui punti sia di tipo C). A complicare le cose sta il fatto che i "metodi" a cui puoi accedere attraverso "prova" hanno la segnatura definita in A ma, in esecuzione, il loro contenuto è determinato a partire dall'oggetto a cui puntano fino a trovarne la prima implementazione.
Esempio: definiamo le tre classi A, B e C come segue. A padre di B padre di C.
public class A {
public A() { }
public void stampaValore() { System.out.println("stampa valore di A"); }
}
public class B extends A{
public B() { }
}
public class C extends B{
public C() { }
public void stampaValore() { System.out.println("stampa valore di C");}
public void unMetodoDiC() {}
}
A possiede un metodo "stampaValore": stampaValore è ereditato da B, nella forma definita in A. stampaValore è ereditato anche da C, ma qui viene sovrascritto ed il metodo è destinato a restituire una stringa diversa in output.
A prova = new C();
prova.stampaValore(); -> output "stampa valore di C"
prova.unMetodoDiC(); -> errore in compilazione, "prova" è un tipo A, la classe A non definisce il metodo "unMetodoDiC()";
if(prova instanceof C) {
((C)prova).unMetodoDiC(); -> casting, valido //come si traduca "casting" in italiano non lo so, perchè "conversione" è già usato per un'altra faccenda
} else {
System.out.println("Il riferimento prova non punta ad un oggetto di tipo C");
}
prova=new B();
prova.stampaValore(); -> output "stampa valore di A": B eredita il metodo come definito in A.
In fondo potremmo dire (ma lo dico solo io) che l'ereditarietà determina sempre il trasferimento dal genitore al figlio della segnatura dei metodi ed eventualmente del corpo.
Dopo questa perla di saggezza dovrei uscire di scena con una capriola ma non ho trovato lo "smilies" addatto :D
prova con uno di questi:
http://www.insiemeweb.com/gif/people/people1/man006.gif http://www.insiemeweb.com/gif/people/people5/man030.gif http://www.insiemeweb.com/gif/people/people6/man034.gif http://www.insiemeweb.com/gif/people/people10/man060.gif http://www.insiemeweb.com/gif/people/people11/man062.gif
Originariamente inviato da a2000
prova con uno di questi:
non è che stai un tantino esagerando ?
Originariamente inviato da misterx
A _________
| |
B D
|
C
ok PGI, molto chiaro; quindi:
A prova = new C(); // vedo i metodi di A
B prova = new C(); // vedo i metodi di A+B
C prova = new C(); // vedo i metodi di A+B+C
oppure:
A prova = new C(); // vedo i metodi di A+B+C
se........((C)prova).metododesiderato();
idem se:
B prova = new C(); // vedo i metodi di A+B+C
e se........((C)prova).metododesiderato();
dovrebbe essere così
Originariamente inviato da misterx
non è che stai un tantino esagerando ?
hai ragione, scusa, non vi importuno più.
anche perchè hai molte probabilità che venga a romperti i c@gli@ni direttamente live in Mailand !!! :rotfl:
cala la pasta !
in che cosa consiste in soldoni, l'interfaccia Iterator ?
Mettiamola così: Iterator è il nuovo Enumeration. Lo scopo è identico, definire il nome di alcuni metodi per lo scorrimento degli elementi di una lista (iterator ed enumeration hanno lo stesso, elementare, scopo).
La ragione della novità è "storica" ma ne faccio una supersintesi: java è un linguaggio standardizzato da Sun, ma il "vero" standard inizia dalla versione 1.2 e (speriamo) giunge a compimento con la 1.4. Standard va qui inteso come "standard dei nomi": tra le cose che sono cambiate, oltre alla notazione maiuscola di tutte le costanti (Color.WHITE è ignoto sub-1.4), c'è anche il nome dei metodi (next, get, length, size, in, out sono alcune delle "nuove" notazioni).
Sun non poteva semplicemente ridefinire Enumeration perchè il nuovo JFC richiedeva un metodo aggiunto (quel remove() ) : aggiungerlo ad Enumeration avrebbe reso incompatibili i vecchi sorgenti con le nuove API. Così ci ha messo una pezza, ha chiamato la new entry Iterator, in futuro Enumeration scomparirà (ancora non è deprecated ma scommetto un soldo che lo sarà presto).
&Ciao.
pensavo fosse una sorta di puntatore da richiamare quando desidero riposizionarmi all'inizio di una lista (array) di oggetti
l'ho sparata grossa ? :confused:
Pensavo fosse un'affermazione :D
Non è un puntatore, e questo è evidente, Java non ha i puntatori (almeno non nella forma a cui generalmente si pensa) e non ha un'aritmetica dei puntatori.
Ma se pensare al comportamento di un oggetto che possiede i metodi di un Iterator come ad un puntatore ti aiuta a comprendere meglio il suo funzionamento segui la strada del modello-puntatore.
Delle affinità (molto vaghe) potrebbero anche esserci: secondo l'aritmetica dei puntatori in C (per quel poco che ne so), l'incremento unitario di un puntatore ad un array "salta" all'interno degli elementi dell'array al successivo elemento.
Analogo è il comportamento di un iterator, quando viene creato punta al primo elemento di una serie, il next() passa al successivo, lo scorrimento però è unidirezionale.
Rinnovo comunque l'invito: pensa agli oggetti nel modo in cui ti aiuta di più a capirli (occhio però a non rifilare in giro la storia del puntatore :D)
lo tengo per me, ma sono convinto che sia un bel puntatore, come del resto gli oggetti di java rinominati, che altro non sono che le buone vecchie strutture del buon vecchio "C" ;)
una dritta
ho una serie di stringhe ) file di testo, ne quale appaiono alcune frasi separate da particolari caratteri esempio:
frangean la biada col #rumor di# croste disse G.Gaber in #un suo# monologo molti anni fa
e se dopo bla bla #bla#
volevo usare lo StingTokenizer non sapendo se esiste già qualche metodo di una classe che fa di meglio il lavoro
che ne dici ?
E' pelino riduttivo il confronto che fai tra Java e C. Devi tener conto che Java non è nato da una costola di C, ma da più "costole" (Mesa, Beta, C, C++...). Anche qui, puoi usare le strutture C se ti aiuta a comprendere gli oggetti, ma un oggetto Java è una struttura quanto io sono una falena: abbiamo entrambi un DNA, ci muoviamo, ci piace la luce, ma, ad esempio, io non picchio la testa contro una lampadina accesa finchè qualcuno non la spegne.
Per lo StringTokenizer: direi che hai centrato lo scopo dell'oggetto, cioè separare una stringa in parti prendendo come "segnalatore" un carattere.
Tieni conto tuttavia che da quelle "ipotetiche" stringhe devi estrarre non ogni singolo pezzo ma solo quello contenuto tra i caratteri di marcatura #-#. La soluzione che troverai sarà poi valida (salvo un adattamento) anche quando dovrai affrontare il "parsing" delle voci di capitolo §-§ (§§...-§§...). Puoi usare uno StringTokenizer, ma il problema è se sia più semplice usare uno StringTokenizer oppure un metodo espressamente dedicato, perchè lo StringTokenizer è molto generico: non ti da solo quello che sta in mezzo ai tag, restituisce anche quello che sta prima e quello che sta dopo. Per capire se sia adatto dovresti chiederti che tipo di stringhe incontrerai: ad esempio, se in una linea ci fosse più di una parola chiave, io #tu# noi #voi# devi tener conto che solo "tu" e "voi" sono parole chiave, mentre "io " e " noi " non lo sono.
mah, ho bello che capito che mi tocca lavorare
ripiegherò su charAt()............contando i caratteri speciali (# o § oppure §§) e poi substring(start,fine) ed amenità simili
tutto sommato, il problema non è tanto scrivere le classi, giacchè ho visto che hai dato un'occhiata sul sito; il problema è capire e scrivere come piace agli altri
ci si sente blindati; e non mi è mai piaciuto :(
hai omesso il B ed il BCPL tra la catena di linguaggi da te menzionati
I linguaggi a cui ho fatto riferimento sono quelli indicati da Gosling in "Java Language Specifications 1st ed.".
Ho letto sia lo schema del progetto1 che del 2. La questione, ed a parer mio è segno di grande competenza, e che il modo in cui il vostro professore vi presenta i progetti non è una "costrizione" per programmare come piace a lui ma è essa stessa parte dell'insegnamento che vi fornisce.
Quegli schemi di progetto sono delle rappresentazioni del risultato di una fase di progettazione OOD (object oriented design, 'sti oggetti sono dappertutto :D ) e corrispodono ad un modulo da assegnare ad una parte del team di sviluppo del software.
Nel caso concreto poi si tratta di una distribuzione relativamente semplice, un modulo presumibilmente l'ha già sviluppato il professore (ed è quello che userà per testare il programma) e non credo che abbia scritto una scheda per sè stesso, quello che affida a voi è tutto il resto del programma. La presenza di uno schema da seguire non è aria fritta, serve proprio a rendere possibile la separazione della realizzazione di un software tra più programmatori.
Non che non sia possibile "fare software" senza OOD, ma il procedimento "all'ammucchiata" è decisamente più caotico (o può diventarlo).
Così parlò Zaratustra. :D
Ciao.
va che la mia non era una critica al docente :) ognuno fa il proprio lavoro, anche se il loro modo di esprimersi mi è di ostacolo esempio:
modella un ogetto..... modella...mah...
io non insegno il suo a lui nè tantomeno lui insegna il mio a mè :D
cmq, da fuori si ragiona in maniera molto più rilassata;)
Originariamente inviato da misterx
cmq, da fuori si ragiona in maniera molto più rilassata;)
Se non devi fare qualcosa allora ti viene benissimo.
Verità assoluta, vale anche per la programmazione :D.
Originariamente inviato da PGI
Se non devi fare qualcosa allora ti viene benissimo.
Verità assoluta, vale anche per la programmazione :D.
inizi a parlare cifrato come a2000
non ho capito la tua risposta: mi stai dando ragione? :confused:
hai PVT
ma.....scusatemi all inizio di sto topic si sindaca sul come mai venga piu grosso un file di un altro compilato se si assegna un int a una variabile piuttosto che un double.......non riesco a capire la spiegazione che avete dato per il mistero......potreste rispiegarmela (sono al 2 anno di informatica e sto programmando java......l unica cosa che mi viene in mente e che un double è piu grosso di un int in bytes ma non so che c entri col resto)......mi piacerebbe partecipare al 3d se ne avessi le competenze.....
misterx -> ti stavo dando ragione sul fatto che a mente fredda si ragionasse meglio.
Il thread è degenerato a pagina 2, punto a partire dal quale si parla del più e del meno riguardo a Java, OOP, pezzi dell'API standard e chi più ne ha più ne metta. Ma bene o male si è sempre in-topic (un dubbio o un mistero, chi ce l'ha lo posti).
Il mistero del double, Replica delle puntate precedenti
A
int x = 0;
double y = 0;
x = x;
B
int x = 0;
double y = 0;
y = x;
Premessa: il file class contiene una serie di istruzioni per la macchina virtuale Java, create a partire dal codice sorgente.
Perchè il file class creato dal sorgente B è più grande del A?
Perche il sorgente B richiede che la jvm esegua un'istruzione in più rispetto ad A. La faccenda si comprende "compilando a mano":
a sinistra il sorgente, a destra le operazioni jvm
A:
int x=0; iconst
istore
double y=0; dconst
dstore
x = x; iload
istore
6 istruzioni
B:
int x=0; iconst
istore
double y=0; dconst
dstore
y = x; iload
i2d <<<!
dstore
Perchè i2d? Perchè y è una variabile double, x contiene un valore int. Gli int non sono double (ovvio). Nel sorgente la conversione larga è "trasparente" perchè un double (64bit) ha sempre abbastanza spazio per contenere i 32bit di un int e gli ideatori di Java hanno preferito non ricordare al programmatore che stà sprecando spazio perchè in genere la scelta è consapevole, se usi un double per un int hai i tuoi motivi.
Ma il processore virtuale ha bisogno di convertire il valore in modo che si adatti al tipo di contenuto della variabile.
Allora se il file class di B contiene un'istruzione in più è chiaro che sarà più grande del file A, più grande di quel tanto che basta per contenere un'istruzione in più per la jvm.
Comunque è solo una curiosità, non è che sia un pilastro del buon programmatore Java.
Se cerchi un po' nel thread dovrebbe esserci un link alle specifiche della jvm, pubblicate da Sun, in cui di aneddoti del genere ne trovi a bizzeffe.
&Ciao.
cmq, PGI, beato te!
ho letto qualcosa QUA (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Concepts.doc.html#16432) a proposito delle interfacce; non mi pare brilli di chiarezza
Originariamente inviato da cn73
ok ti posto anche l'altra...dammi un secondo :D
ciao cn73. mi stavo chiedendo con cosa avevi disassemblato il codice java; mi interessava capire se si vedeva la fase di creazione/distruzione del record di attivazione di questo codice :)
public class Mio{
public static void main(String[] args){
int cip = ft(3);
System.out.println(cip);
}
public static int ft( int n){
int k=1;
if(n == 0)
return 1;
else
return n * ft(n-1);
}
}
Originariamente inviato da misterx
cmq, PGI, beato te!
ho letto qualcosa QUA (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Concepts.doc.html#16432) a proposito delle interfacce; non mi pare brilli di chiarezza
Beato me lo dico anch'io, ma perchè sono di carattere piuttosto allegro. Esattamente a cosa ti riferivi?
La descrizione contenuta nelle specifiche della jvm è tratta dal più voluminoso libro sulle specifiche del linguaggio Java (disponibile per il download da qualche parte sul sito di SUN) che è scritto in un americano terribilmente tecnico. Personalmente quando lo prendo per leggere qualcosa devo riguardare le stesse frasi sei o sette volte prima di capire bene cosa dica.
Per "disassemblare" un file class (diverso da decompilare) puoi usare il tool "javap" incluso nel JDK che produce un output su consolle con il bytecode "leggibile".
javap NomeFileClass(senza il .class) [invio]
&Ciao.
Originariamente inviato da PGI
Per "disassemblare" un file class (diverso da decompilare) puoi usare il tool "javap" incluso nel JDK che produce un output su consolle con il bytecode "leggibile".
javap NomeFileClass(senza il .class) [invio]
&Ciao.
ok, quello lo avevo trovato ma il record di attivazione ?
Mi era sfuggita la parte del record.
In sincerità, io il record di attivazione non so manco cosa sia :confused: .
&Ciao.
il record di attivazione è l'insieme di variabili che finiscono nello stack durante l'esecuzione e che contengono parametri, indirizzo di ritorno e variabili locali
non sono esperto di bytecode java (dopotutto che me ne faccio?) però la definizione di record di attivazione è pressapoco quella che ho dato sopra
del bytecode io ho già detto tutto quel che so e quindi sono tutto fuorchè un esperto. Ma siamo poi sicuri che non serva approfondire la cosa (lo chiedo per discuterne)?
In fondo è il bytecode ad essere eseguito e non il codice, è possibile che uno stesso algoritmo implementato in modi diversi e dato in pasto al compilatore generi un byte-code "più efficente" (a parità di condizioni generali, vale a dire usando gli stessi tipi di dato ecc.)?
io credo che la filosofia Java sia quella di fregarsene totalmente di quanto sta sotto.
se ho bisogno di scrivere del codice veloce e ottimizzato allora Java non è il top, è meglio c ad esempio.
studiare la VM può essere interessante, non lo nego, ma direi che èuna delle ultime cose da studiare di Java.
credo che venga fatta nei corsi di programmazione (come quello che sta seguendo misterx) perché si vuole far capire qualcosa sul funzionamento più a basso livello e questo mi sta bene. sarebbe però sbagliato iniziare a guardare il bytecode prima di aver imparato per bene a programmare in Java, si perderebbe del tempo secondo me.
Forse il grosso del problema "prestazioni" in Java stà proprio in quella filosofia (che io adoro). Poichè si ottengono risultati molto in fretta si tralascia quel grado di "finezza" a cui si è quasi costretti usando altri linguaggi e ad un certo punto si arriva a dire "Java è bello ma lento".
Eppure in due recenti libri che ho letto (Java2 SDK 1.4 e tal "Brecken", Developing Game in Java) si dice che la versione 1.4.2 è ormai un linguaggio ad elevate prestazioni, anche per merito delle ottimizzazioni della jvm HotSpot (che è poi quella ufficiale di Sun)... e perchè io non mi sono accorto della differenza?
Misteri dell'informatica.
Originariamente inviato da recoil
credo che venga fatta nei corsi di programmazione (come quello che sta seguendo misterx) perché si vuole far capire qualcosa sul funzionamento più a basso livello e questo mi sta bene. sarebbe però sbagliato iniziare a guardare il bytecode prima di aver imparato per bene a programmare in Java, si perderebbe del tempo secondo me.
infatti, e se si osserva una simulazione nel caso di una ricorsione diviene un argomento abbastanza lungo e laborioso da illustrare
io devo conoscere l'argomento in quanto te lo chiedono all'esame :)
a proposito
mi hanno detto che jbuilder ti fa vedere durante l'esecuzione di un programma, questi benedetti record di attivazione, e se nel programma è prevista una ricorsione vedi il motivo del celeberrimo "stack overflow"
Originariamente inviato da misterx
mi hanno detto che jbuilder ti fa vedere durante l'esecuzione di un programma, questi benedetti record di attivazione, e se nel programma è prevista una ricorsione vedi il motivo del celeberrimo "stack overflow"
non uso jbuilder quindi non so se è vero ma è molto probabile. del resto gli ambienti di sviluppo "avanzati" per Java hanno le funzionalità tipiche dei tool per c/c++ inclusa la possibilità di fare esecuzione passo passo e vedere lo stack.
PGI: nonostante quello che dicono i libri che citi io credo che Java abbia ancora qualche problema velocistico. me ne accorgo sia usando netbeans che è pesantuccio e me ne sono reso conto soprattutto facendo la mia tesi, sviluppata in Java.
del resto la portabilità e lasciatemelo dire anche la comodità si paga. però lo ripeto: w Java :)
Sicuramente gli autori degli ultimi libri che ho letto sono tutto fuorchè "internazionalmente autorevoli", hanno il pregio di essere recenti, comunque ti do ragione sul fatto che si debbano prendere con le molle.
D'altronde gli stessi membri del team di sviluppo Swing sul punto delle prestazioni hanno parlato chiaro dicendo:
"dopo aver introdotto Swing ricevemmo commenti entusiastici e una valanga di lamentele sulle prestazioni: così decidemmo di analizzare il codice alla ricerca di possibili ottimizzazioni e scoprimmo che le librerie erano piene di colli di bottiglia".
Dopo aver letto questa frase in genere sono abbastanza sospettoso quando sento dire "se il programma è lento è colpa di come lo hai scritto"... di solito mi attesto su un più probabile "al 75% è per come lo hai scritto, il 25% è nelle mani del signore (che ha scritto le librerie per Sun...)".
E comunque NetBeans è niente in confronto al mitico SunOneStudio, ricordo tempi di caricamento da era geologica su un XP1700+ con 512DDR :D
Originariamente inviato da PGI
E comunque NetBeans è niente in confronto al mitico SunOneStudio, ricordo tempi di caricamento da era geologica su un XP1700+ con 512DDR :D
infatti non ci penso minimamente a procurarmi SunOne
cmq non sono lente solo le Swing perché le basse prestazioni si notano anche sviluppando applicazioni senza interfaccia grafica.
diciamo che la lentezza di Swing si vede a occhio durante l'utilizzo di una applicazione, quella generale delle applicazioni Java la noto durante il caricamento iniziale che è piuttosto pesante...
Beh, swing alla fine è stato messo a posto, non è che sia poi così lento oggi (uso swing come esempio, ci sono altre parti che sono state pesantemente riviste, tipo il nuovo sistema di gestione dei flussi input/output "nio" ).
Per quanto riguarda il tempo di avvio è certamente vero che il caricamento delle classi sia un processo "lungo", tuttavia è anche vero che lo stesso processo è distribuibile all'interno dell'applicazione (a patto, naturalmente, che le classi da caricare non siano tutte necessaria nel costruttore della classe di partenza)
Per i curiosi, qui è disponibile on-line un libro che mostra, tra l'altro, alcune tecniche di ottimizzazione delle prestazioni in esecuzione di programmi Java.
http://java.sun.com/docs/books/performance/1st_edition/html/JPTitle.fm.html
Originariamente inviato da PGI
Beh, swing alla fine è stato messo a posto, non è che sia poi così lento oggi (uso swing come esempio, ci sono altre parti che sono state pesantemente riviste, tipo il nuovo sistema di gestione dei flussi input/output "nio" ).
Per quanto riguarda il tempo di avvio è certamente vero che il caricamento delle classi sia un processo "lungo", tuttavia è anche vero che lo stesso processo è distribuibile all'interno dell'applicazione (a patto, naturalmente, che le classi da caricare non siano tutte necessaria nel costruttore della classe di partenza)
Per i curiosi, qui è disponibile on-line un libro che mostra, tra l'altro, alcune tecniche di ottimizzazione delle prestazioni in esecuzione di programmi Java.
http://java.sun.com/docs/books/performance/1st_edition/html/JPTitle.fm.html
riguardo al package nio non mi esprimo perché non l'ho ancora usato. ho osservato che le swing sono un po' lente "a occhio" perché in effetti su pc poco potenti si nota la lentezza ad esempio scorrendo una lista o cliccando su un menù.
giusta la tua osservazione sulla distribuzione del carico "posticipando" la creazione degli oggetti quando possibile, anche se non sempre si riesce :(
infine grazie per il link, mi mancava
mah, se desidero prestazioni mi rivolgo al buon vecchio "C"
cmq, java lo trovo parecchio interessante
Originariamente inviato da misterx
lo tengo per me, ma sono convinto che sia un bel puntatore, come del resto gli oggetti di java rinominati, che altro non sono che le buone vecchie strutture del buon vecchio "C" ;)
Originariamente inviato da misterx
se desidero prestazioni mi rivolgo al buon vecchio "C"
:D E' già la seconda volta che mi provochi :boxe: ma non cederò a queste frecciatine :D
Originariamente inviato da PGI
:D E' già la seconda volta che mi provochi :boxe: ma non cederò a queste frecciatine :D
beh informaticamente parlando è vecchio si! Java è un giovincello a confronto :)
e io se voglio prestazionio mi rivolgo al giurassico assembly e non se ne parli più! :O :D
Originariamente inviato da PGI
:D E' già la seconda volta che mi provochi :boxe: ma non cederò a queste frecciatine :D
nono, è un semplice dato di fatto :)
:O recoil, scrivimi in assembler un semplice programma per il ridimensionamento di immagini, solo per vedere quanto ci impieghi :D
meglio il "C"
Originariamente inviato da misterx
:O recoil, scrivimi in assembler un semplice programma per il ridimensionamento di immagini, solo per vedere quanto ci impieghi :D
ok, ci risentiamo l'anno prossimo... anzi no aspetta, nel 2005 :mc:
cmq sai che dopo tanta programmazione Java faccio fatica con il c?
ormai sono assuefatto alla programmazione a oggetti :D
Originariamente inviato da misterx
un semplice programma per il ridimensionamento di immagini
Hey è divertente, facciamo a chi riesce a restare sotto i 1000 bytes con Java? (già, 1000bytes + 60megabyte di runtime environment :D)
Originariamente inviato da PGI
Hey è divertente, facciamo a chi riesce a restare sotto i 1000 bytes con Java? (già, 1000bytes + 60megabyte di runtime environment :D)
scherzi a parte, se devi scrivere un programma di elaborazione video anche il VB è più indicato :)
cmq, a parte i gusti personali java non dispiace neppure a me
recoil ho letto la tua definizione sul "record di attivazione" e mi è molto piaciuta; sai fare altrettanto per le interfacce ?
io riassumendo penso che siano un mezzo escogitato da sun per permettere la multi ereditarietà di una sottoclasse: o no ?:confused:
mi pare che il C++ possegga la multi ereditarietà
mi è stata già chiesta una definizione di interfaccia e trovo che non sia molto semplice darla.
allo stesso tempo non è facile capire il perché dell'esistenza delle interfacce. l'ereditarietà è un concetto intuitivo e se ne capisce subito l'utilità: ereditando una classe ne eredito i metodi e quindi posso estendere le funzionalità aggiungendone di nuovi.
con l'interfaccia si ereditano delle costanti, niente di più. allora perché è utile un'interfaccia? per consentire l'ereditarietà multipla, che in Java non è implementata. tuttavia se non ho codice che cosa eredito? eredito il "tipo".
ti faccio un esempio: se la tua classe Cane implementa l'interfaccia Animale allora una istanza di Cane potrà essere assegnata a una variabile di tipo Animale.
questo a che serve? continuiamo l'esempio
l'interfaccia Animale ha un metodo chiamato nutri. Cane ha la propria implementazione di nutri() che può essere diversa da quella degli altri animali ma se in qualche altra classe (ad esempio la classe Zoo) ho bisogno di chiamare il metodo nutri() su una bestia qualsiasi posso farlo tramite una istanza di Animale pur facendo riferimento al cane o al gatto proprio perché ho implementato l'interfaccia.
forse l'esempio è stupido ma spero che chiarisca le idee. le interfacce si trovano molto spesso e un uso abbastanza ricorrente è quello di utilizzarle per implementare degli handler (gestori). ad esempio quando si programma un applet si può creare una classe che implementa l'interfaccia ActionListener e tramite questa gestire gli eventi (pressione di pulsanti ecc.). lo si può fare perché la pressione di un pulsante implica la chiamata del metodo actionPerformed() dell'interfaccia ActionListener. se la tua classe ha implementato tale interfaccia allora il metodo actionPerformed() potrà essere richiamato
spero di essere stato chiaro, in effetti come dicevo non è affatto semplice parlare di interfacce ma ti assicuro che hanno una grandissima importanza in Java ed è utile imparare a usarle per bene
;)
Originariamente inviato da recoil
se la tua classe Cane implementa l'interfaccia Animale.......
ho fermato il tuo discorso lì dove lo vedi
perchè mi dici che Animale è un'interfaccia ?
Originariamente inviato da misterx
ho fermato il tuo discorso lì dove lo vedi
perchè mi dici che Animale è un'interfaccia ?
dico che è una interfaccia poiché abbiamo bisogno di sapere che cosa fare (ovvero che metodi eseguire) sugli animali. allora Animale può essere un'interfaccia che mi dice: se vuoi implementare un animale questi sono i metodi che devi sviluppare
si poteva usare anche una classe astratta ma secondo me quando possibile è sempre meglio usare le interfacce.
mi sa che fino a quando non ne implemento una per esigenza faticherò sempre a comprendere ste benedette interfacce :(
riesco solo a capire che un'interfaccia potrebbe essere una sorta di ponte tra du classi che poco hanno in comune ma a questo punto uso extends
Originariamente inviato da misterx
mi sa che fino a quando non ne implemento una per esigenza faticherò sempre a comprendere ste benedette interfacce :(
vedrai che quando ne avrai bisogno comprenderai la loro utilità :)
tra l'altro sono comodissime per definire delle costanti.
crei un'interfaccia con variabili precedute dalla parola chiave final e implementando tale interfaccia hai tutte le costanti che vi avevi definito
hai voglia di postarmi qualche semplice esempio ?:)
come ti dicevo quando progetti un'interfaccia grafica hai a che fare con le interfacce per gestire gli eventi quindi qualsiasi tutorial che puoi trovare sull'uso delle swing ti sarà utile
un esempio mio non ce l'ho (a parte quelle che credo per le costanti), di solito mi limito ad utilizzare le interfacce già scritte.
in progetti "piccoli" cmq non se ne sente il bisogno secondo me. per questo credo che non sia frequente scrivere interfacce, a meno che non si voglia scrivere una libreria, fornire un servizio...
grazie recoil anche se fortemente in ritardo
ma se si dichiara un campo statico in una classe, è possibile poi accedervi solo se si dichiara un metodo anch'esso statico ?
ho bisogno di una conferma
Auguri
dai miei rimembri dell' Eckel, solo metodi static possono accedere a variabili static.
Fai la prova del 9.
Crei una var int i; e cerche di accederci con il main() di quella classe :)
EDIT
Il contrario di quanto contenuto nella domanda di misterX
EDIT
Confermo...il contrario.
Un campo statico è accessibile ovunque (compatibilmente con il modificatore usato).
Un metodo statico può accedere solo a campi e metodi statici.
C'è anche il suo bel perchè, ma per l'anno nuovo ho deciso di limitare lo sciame di parole con cui solitamente investo il prossimo :D
Ciao&Auguri
(Ho editato perchè stavo rispondendo a misterx, poi ho visto un altra risposta e ho precisato a chi stessi rispondendo. Avrei dovuto quotare.)
"solo metodi static possono accedere a variabili static"
no
"i metodi static possono accedere solo a metodi/campi static"
si
quindi in parte ho ragione.
Tnk!
Ma perche queta cosa?
Buon Anno ^_^
Mi fate violare il mio "fioretto per l'anno nuovo".
Ve la siete cercata! :D
Tutto quello che è "static" viene creato dalla VM a prescindere dall'esistenza di un'istanza della classe.
Tutto quello che non è static viene creato in dipendenza da un'istanza della classe (un oggetto quindi).
La faccenda si coglie se provate ad usare un blocco di inizializzazione statico ed uno non statico:
public class Prova {
public static boolean nonStaticExists = false;
/*Blocco di inizializzazione statico*/
static {
System.out.println("E' stato creato il pool di campi/metodi statici");
}
/*Blocco di inizializzazione non statico*/
{
System.out.println("E' stato creato il pool di campi/metodi non statici");
nonStaticExists = true;
}
static String campoStatico = "Ecco un campo statico";
public String campoNonStatico = "Ecco un campo non statico";
}
L'esecuzione del main della classe che segue:
public class MainClass {
public static void main(String[] a) {
String message = "Campi e metodi NON statici ";
message += Prova.nonStaticExists ? "esistono" : "non esistono";
System.out.println(message);
}
}
produce l'output:
E' stato creato il pool di campi/metodi statici
Campi e metodi NON statici non esistono
Ma se cambiamo il main in:
public class MainClass {
public static void main(String[] a) {
String message = "Campi e metodi NON statici ";
message += new Prova().nonStaticExists ? "esistono" : "non esistono";
System.out.println(message);
}
}
il messaggio restituito risulta essere:
E' stato creato il pool di campi/metodi statici
E' stato creato il pool di campi/metodi non statici
Campi e metodi NON statici esistono
Nel primo caso infatti abbiamo usato un campo statico nell'operatore ternario e il tentativo di accesso produce per la VM una richiesta di "inizializzazione preventiva" della classe che coinvolge appunto le sole aree "static".
Nel secondo caso (notate che c'è un "new Prova()" nella riga dell'operatore [ ? : ]) è creato anche un oggetto e l'output riproduce l'ordine di inizializzazione "normale": prima inizializza le aree "static", poi passa alle "non static".
Il blocco di inizializzazione non statico (che è a tutti gli effetti paragonabile a campi e metodi non statici, salvo l'essere inizializzato con precedenza maggiore) cambia il valore di "nonStaticExists" a "true" (il che risponde direttamente alla questione se possa uno "scope" non statico accedere ad uno statico) e il messaggio indica questo cambiamento.
Spezzo in due il messaggio per renderlo più leggibile:
Cosa dovrebbe suggerire la faccenda che abbiamo visto sopra?
Dovrebbe dire che poichè le aree static non solo vengono inizializzate prima di quelle non static, ma possono essere inizializzate anche senza tutto il resto della classe, il compilatore rileva come errore il tentativo di un metodo static di accedere ad un'area che potrebbe anche non esistere e se esistesse sarebbe in ogni caso "attivata" dopo la creazione di ciò che è static.
Per me tutta la storia potrebbe anche essere vista come un caso particolare di retro-riferimento (quella cosa che in compilazione impedisce una riga del tipo "int x = ++x + 1" perchè la "x" a destra è valutata prima di quella a sinistra) ma sono finezze di importanza relativa.
Ciao & Auguri
PGI sei tremendo :D
cmq, bravo per l'approfondimento ed aggiungo alla mia precedente domanda:
i campi statici penso siano stati inventati per permettere di avere delle caratteristiche comuni per i medesimi tipi di oggetto
se invento ad esempio la classe orologio e ad ogni sua istanza definisco :
class Orologio{
char SeparatoreOreMinutiSecondi = '.';
......
}
e se istanzio 1000 oggetti di tale tipo e decido ad un tratto per un altro tipo di separatore, devo accedere ad ogni oggetto e cambiarlo uno dopo l'altro
con "static" metto in comune il Separatore.... con tutta la popolazione dei miei 1000 oggetti.
Se cambio Separatore.... in un baleno l'ho cambiato per tutti i 1000 oggetti istanziati.....
Magari avrò anche espresso male ma sforzati di comprendermi tra le righe :D
Accantonato il discorso per i campi statici che reputo comodissimi, mi sfugge l'utilità dei metodi statici
Se poi hai voglia di spendere due parole per il modificatore di visibilità "private"
Auguri
Originariamente inviato da misterx
i campi statici penso siano stati inventati per permettere di avere delle caratteristiche comuni per i medesimi tipi di oggetto
Accantonato il discorso per i campi statici che reputo comodissimi, mi sfugge l'utilità dei metodi statici
Se poi hai voglia di spendere due parole per il modificatore di visibilità "private"
Il "perchè" esistano gli "static" sinceramente non lo so. Forse è l'uso che se ne fa a stabilire perchè sia stato messo, in ogni caso è certamente vero ciò che dicei, cioè che possano essere usati per defire rapidamente degli stati condivisi tra classi autonome.
Per l'angolo del "forse non tutti sanno che", proprio la parola chiave "static" e ciò che comporta era uno dei punti dolenti di Java secondo Nygaard e Dahl (Dahl lamentava anche il fatto che fosse troppo simile per sintassi a C++).
E qui ti voglio: chi erano (morti entrambi nel 2002 :cry: ) Krysten Nygard e Ole-Johan Dahl? :confused:
Rullo di tamburi, sono stati coloro che hanno inventato l'OOP e ne hanno dimostrato l'uso nel primo linguaggio progettato per essere OOP, Simula67.
E perchè lo static non andava bene per un linguaggio che si dichiarasse OOP ? Perchè lo static, ed i blocchi di inizializzazione statici in particolare, permettono al programmatore di cambiare lo stato di un sistema senza passare per un transizione definita come comportamento di un oggetto (in breve, senza passare per un metodo).
Pensa un po' che con un blocco di inizializzazione statico ed eventualmente dei campi/metodi statici si può fare un programma java senza il metodo main (ma quante ne so? :D).
Ad esempio, l'applicazione che segue è una calcolatrice primordiale che esegue una somma sfruttando una "debolezza" della struttura delle applicazioni Java (è l'insieme che è "debole", compilatore e macchina virtuale non dovrebbero fare così ma è inevitabile che lo facciano, almeno per la struttura attuale):
import java.io.*;
public class MainClass {
static {
try {
BufferedReader r= new BufferedReader(new InputStreamReader(System.in));
System.out.println("Numero 1: ");
int x = Integer.parseInt(r.readLine());
System.out.println("Numero 2: ");
int y = Integer.parseInt(r.readLine());
System.out.println("La somma e': "+ (x + y));
r.close();
} catch(IOException e) {
System.out.println(e);
} finally {
System.out.println("Fine programma");
System.exit(0);
}
}
}
Pur non avendo un metodo "main", la calcolatrice viene lanciata ed eseguita.
E se hai capito come funziona la faccenda dello static dovresti anche sapermi dire perchè l' "applicazione" funziona senza rilasciare l'eccezione runtime "couldn't find main class".
Chiuso l'angolo delle bizzarrie.
I metodi statici offrono lo stesso "vantaggio" del campo statico, un qualcosa che esiste "prima" degli oggetti e che è unico per ogni JVM eseguita, solo che consente di eseguire delle operazioni anzichè contenere semplicemente un valore.
Con la parola chiave "private" arriviamo al cuore dell'OOP: prima ancora che tecnica di programmazione l'OO è un modo di pensare e questo "modo di pensare" prevede che le relazioni di ereditarietà debbano prevedere un modo per nascondere o limitare la visibilità innanzitutto tra "parenti". Con un "private" tu stabilisci che quella tal caratteristica o quel comportamento è proprio di una classe e non deve essere ereditato da nessun figlio.
Per me è una cosa non facile da capire, perchè da punto di vista della "scrittura di codice che funziona" tu potresti benissimo dichiarare tutto quanto "public" e poi scegliere nel codice cosa usare per i "figli" e cosa no.
Ma da un punto di vista più "filosofico" esistono certi elementi che sei "costretto ad usare" in una classe perchè senza non si va avanti e che tuttavia non c'entrano nulla con i rapporti padre-figlio (una costante per un calcolo, o un metodo "servente", quello che capita capita).
theClimber
31-12-2003, 18:32
Originariamente inviato da PGI
Il "perchè" esistano gli "static" sinceramente non lo so. Forse è l'uso che se ne fa a stabilire perchè sia stato messo, in ogni caso è certamente vero ciò che dicei, cioè che possano essere usati per defire rapidamente degli stati condivisi tra classi autonome.
Per l'angolo del "forse non tutti sanno che", proprio la parola chiave "static" e ciò che comporta era uno dei punti dolenti di Java secondo Nygaard e Dahl (Dahl lamentava anche il fatto che fosse troppo simile per sintassi a C++).
.......
Bella discussione :). Provo ad analizzare un attimo il punto dello static, perche ritengo che sia molto importante per capire Java e l'OOP. Scusatemi se magari non saro' chiarissimo, ma questo non e' un aspetto banale del linguaggio:
Se la classe e' lo "stampo" con cui si creano gli oggetti, i metodi e le variabili static sono le caratteristiche dello "stampo".
Il punto dolente di Java (Per ereditarieta dal C++, forse?) in questo e' che lo stampo, ovvero la classe non e' un ogggetto in se stesso. Per farti un esempio in Smalltalk, linguaggio OO creato tra il 1972 ed il 1980 ( che si ispira a Simula ) la classe di un oggetto e' un oggetto anch'essa, la cui classe e' un oggetto di tipo 'metaclass'.
In questa situazioni il corrispettivo dei metodi "static" in Smalltalk sono i metodi dell'oggetto classe corrispondente.
Cosa vuol dire questo? che se una classe e' un oggetto, sara' un istanza di una metaclasse. In pratica la metaclasse e' lo "stampo" per produrre lo "stampo".
la critica fatta a java e' che introducendo "static" come parola chiave e avendo un modello di classe che non e' un oggetto, si perde in potenza espressiva in quanto non e' possibile manipolare come normali object le classi. Proprio per questo la reflection e' molto + limitata in Java che in Smalltak, per cui diventa + difficile scrivere software che genera/modifica codice a runtime.
Un link con alcune info sulla gerarchia di classi in smalltak:
http://www.ifi.unizh.ch/richter/Classes/oose2/05_Metaclasses/02_smalltalk/02_metaclasses_smalltalk.html
Originariamente inviato da PGI
I metodi statici offrono lo stesso "vantaggio" del campo statico, un qualcosa che esiste "prima" degli oggetti e che è unico per ogni JVM eseguita, solo che consente di eseguire delle operazioni anzichè contenere semplicemente un valore.
Piccola precisazione: le classi non sono uniche per JVM ma per classloader. Questo in alcuni casi diventa un aspetto da considerare, che tipicamente emerge in applicazioni server side (J2EE) ...
theClimber,
se hai anche qualche informazione chiara sui "record di attivazione" giuro che non mi offendo :D
nel frattempo medito sulle vostre risposte :)
Auguri
theClimber
31-12-2003, 20:16
Originariamente inviato da misterx
se hai anche qualche informazione chiara sui "record di attivazione" giuro che non mi offendo :D
Immagino che ti riferisca al concetto di "activation frame" nella specifica del linguaggio. Vedi:
http://java.sun.com/docs/books/jls/first_edition/html/15.doc.html
In pratica sono le informazioni salvate sullo stack per l'invocazione dei metodi. In servono per veicolare le seguenti informazioni:
- Parametri di invocazione
- Variabili locali (scope)
- Valori ritorno
Dato che la loro implementazione e' un interna alla JVM, non dovrebbero un impatto diretto sulla "semantica" dell'invocazione dei metodi nell'uso normale del lingauggio.
Certo che e' un argomento da approfondire x implementare una virtual machine (o per leggere meglio il bytecode). Vedi ad esempio la JVM specification:
http://java.sun.com/docs/books/vmspec/2nd-edition/html/Overview.doc.html#17257
Ciao e Auguri di Buon Anno :)
Buon anno a tutti.
E con un enorme cerchio alla testa, proviamo a ripartire.
Originariamente inviato da theClimber
Piccola precisazione: le classi non sono uniche per JVM ma per classloader.
Altro che piccola, la precisazione vale un asso di briscola, perchè è fine e introduce un argomento di cui non si è parlato spesso sul forum (cioè mai a meno che non mi sbagli).
Però resto dell'idea che le classi debbano essere considerate uniche per JVM: poichè una VM può avere più di un ClassLoader (frase bruttissima, perchè la JVM in teoria dovrebbe "avere" solo due ClassLoader, il BootStrapClassLoader e il SystemClassLoader, gli altri sarebbero "moduli" dell'applicazione) sarebbe una violazione della sicurezza dei tipi se la JVM potesse considerare come identiche due classi con lo stesso nome (in compilazione) inizializzate da due diversi ClassLoader.
La soluzione della JVM di Sun è quella di definire a tempo di esecuzione uno "spazio dei nomi" (ma c'è una traduzione in italiano per namespace?) diverso da quello in compilazione e che, in particolare, associa ad ogni classe il classloader che l'ha inizializzata: il che rende diverse due classi "identiche" se caricate da due diversi class loader.
E c'è anche di più: poichè non è detto che il ClassLoader che inizializza la classe sia lo stesso che la carica (delegazione del caricamento) la JVM mantiene una mappa di "limiti al caricamento" che tiene conto del nome della classe, del ClassLoader di inizializzazione e del ClassLoader delegato al caricamento.
Il tutto per quello che mi è parso di capire, segnalazioni appunti e pesci in faccia sono i benvenuti. Tra l'altro, e giustamente lo segnala the Climber, sapere bene queste cose è forse più importante del conoscerne tante altre, perchè il Java degli "smanettoni" come me è molto diverso da quello usato in ambito professionale (anche se basta mettere insieme un mini-browser che supporti le Applet java per doversi scontrare con il caricamento dinamico delle classi)
C'è un paragrafo molto sintetico al riguardo nelle specifiche della JVM ma l'argomento è trattato meglio in un blueprint di qualche tempo fa (molto tempo fa):
Dynamic class loading in the Java Virtual Machine(PDF) (http://citeseer.ist.psu.edu/cache/papers/cs/16099/http:zSzzSzjava.sun.comzSzpeoplezSzgbrachazSzclassloaders.pdf/liang98dynamic.pdf/)
azz, mi è venuto un dubbio
se ho
A
|
B
|
C
|
D
e scrivo:
A test = new D();
significa che la classe D vede tutti i metodi delle classi A, B, C ???
oppure vede solo i metodi di C o addirittura quelli di A divenendo B e C trasparenti ???
theClimber
03-01-2004, 21:11
Originariamente inviato da misterx
A test = new D();
significa che la classe D vede tutti i metodi delle classi A, B, C ???
oppure vede solo i metodi di C o addirittura quelli di A divenendo B e C trasparenti ???
Attenzione ! Hai una reference ('test') di tipo A. Quindi, dato che gli accessi ai metodi li fai attraverso questa reference, il compilatore ti permettera' di usare liberamente (a meno di forzare un casting a B, C o D, tutti possibili in questo specifico caso) solo i metodi definiti in A.
Pero' l'oggetto e' di tipo D. Per cui ovviamente se ci sono override ai metodi di A in B, C o D, sara' utilizzato il metodo della sottoclasse.
Come definire tutto questo? dai un occhio al Polimorfismo. Ad esempio:
http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming
Occhio che in linguaggi con tipizzazione non statica (Cioe' in cui NON dichiari il tipo della reference, ovvero A, e.g. Smalltalk, Python), puoi provare a chiamare qualsiasi metodo (di A, B, C o D) ed il compilatore/interprete non si lamentera'. Se il metodo non esiste si avra' un errore a runtime.
Originariamente inviato da theClimber
Per cui ovviamente se ci sono override ai metodi di A in B, C o D, sara' utilizzato il metodo della sottoclasse.
e magari viene usato il metodo sovrascritto della sottoclasse più vicina a D senza che tu te ne renda conto ?
mi ero convinto che scrivendo:
A test = new D();
potessi usare i metodi definiti nella classe D, invece il compilatore genera un errore "cannot resolve symbol"
ecco perchè sul mio libro c'e scritto: "Nel linguaggio Java una classe può ereditare al massimo da una classe"
riassumendo se scrivo:
A test = new D();
vedo solo ed esclusivamente i metodi della classe A
theClimber
04-01-2004, 09:54
Originariamente inviato da misterx
e magari viene usato il metodo sovrascritto della sottoclasse più vicina a D senza che tu te ne renda conto ?
mi ero convinto che scrivendo:
A test = new D();
potessi usare i metodi definiti nella classe D, invece il compilatore genera un errore "cannot resolve symbol"
E' un errore in compilazione, dato che il tipo statico di test e' A. Pero' l'oggetto e' di tipo D, per cui usa le implementazioni di D (definite in D o ereditate da C,B,A)
Originariamente inviato da misterx
ecco perchè sul mio libro c'e scritto: "Nel linguaggio Java una classe può ereditare al massimo da una classe"
No, non mi pare che questo sia un effetto di ereditarieta' multipla vs singola, ma piuttosto derivato dal controllo statico sui tipi in fase di compilazione. Gli stessi effetti li hai anche in C++, dove puoi avere ereditarieta' multipla.
Originariamente inviato da misterx
riassumendo se scrivo:
A test = new D();
vedo solo ed esclusivamente i metodi della classe A
Si, perche' hai in mano una reference di tipo A.
Se vuoi usare i metodi di B, C o D:
((B)test).metodoDiB();
((C)test).metodoDiC();
((D)test).metodoDiD();
uhm, mi sa che sto pagando studiando java facendomi aiutare dai suggerimenti di NetBeans
ora se istanzio in questo modo:
D test = new D();
vedo sia i metodi di D, i metodi di C, B ed A
ma ciò va in contrapposizione a quanto recita il mio libro:confused:
theClimber
04-01-2004, 10:55
In questo caso la reference e' di tipo D, quindi il compilatore ti permette l'accesso a tutti i metodi di D (inclusi quelli di C, B ed A)
Originariamente inviato da misterx
ma ciò va in contrapposizione a quanto recita il mio libro:confused:
Che libro stai usando? cosa afferma esattamente? potrebbe essere anche un errore dell'autore, non si sa mai.... ;)
scusa ma sono andato in confuzione:muro:
se il tipo riferimento e di tipo A ma istanzio D vedo solo i metodi di A e non di D ?
se invece il tipo riferimento è di tipo D vedo tutti i metodi di D e di C, D, A ???
ma io dico:
se A è supertipo di D, dovrebbe essere corretto scrivere
A test = new D();
e con test.
dovrei vedere tutti i metodi delle superclassi di D
theClimber
04-01-2004, 11:20
Code example:
A test = new D();
/* Adesso il compilatore, che e' stupido, sa solo che test e' una reference di tipo A.*/
test.metodoDiA();
/* OK dato che il compilatore sa che test e' di tipo A o di
un sua sottoclasse, per cui il metodo esiste
sicuramente. Nota che il metodo puo' essere ridefinito in
B, C e/o D, che essendo sottoclassi di A, ne dovono
rispettare la signature/contratto */
test.metodoDiD();
/* ERRORE, il compilatore (stupido) non puo' sapere che
test supporta questo metodo, anche se l'oggetto
associato lo gestirebbe. */
((D)test).metodoDiD();
/* OK, ho fatto un upcast, dicendo al compilatore:
"Guarda, test e' di tipo D !". per cui e' ok accedere ai
metodi di D. NOTA: mi sono preso il rischio di un upcast,
che potrebbe generare un ClassCastException (Ma non
in questo caso)*/
theClimber
04-01-2004, 11:22
Ovviamente ho fatto l'ipotesi che A definisce metodoDiA(), B definisce metodoDiB(), etc...
azz, se la assimilo come regola questa storia dell'ereditarietà non ho problemi; la logica però non l'ho acora capita :(
A test = new D(); // così vedo i metodi della classe A
B test = new D(); // così vedo i metodi della classe A e B
C test = new D(); // così vedo i metodi della classe A, B e C
D test = new D(); // così vedo i metodi della classe A, B, C e D
non capisco l'utilità delle prime tre istanze :muro:
Originariamente inviato da misterx
non capisco l'utilità delle prime tre istanze :muro:
non ho seguito molto il discorso ma se B, C e D ridefiniscono dei metodi di A allora le prime tre istanze hanno un senso.
potrei aver bisogno, per qualche motivo, di un metodo di A che è stato ridefinito dalle sottoclassi e allora A test = new D(); ha senso.
Originariamente inviato da recoil
non ho seguito molto il discorso ma se B, C e D ridefiniscono dei metodi di A allora le prime tre istanze hanno un senso.
potrei aver bisogno, per qualche motivo, di un metodo di A che è stato ridefinito dalle sottoclassi e allora
A test = new D();
ha senso.
scusa ma se scrivo:
A test = new D();
A test = new A();
ottengo il medesimo risultato, quindi che senso ha ?
forse avrebbe senso se esistesse una classe E che estende A ed in A ci fossero implementati dei metodi astratti e definiti in B ed E, ad esempio ?
theClimber
04-01-2004, 20:32
Originariamente inviato da misterx
non capisco l'utilità delle prime tre istanze :muro:
La risposta sostanzialmente e' la seguente, ed e' collegata al polimorfismo: si vuole assegnare ad una reference un oggetto che implementa un insieme di metodi.
In particolare se il codice che scrivi usa una reference di tipo A, lo potrai ri-usare con oggetti di tipo B, C e D senza modificarlo, dove ciascuna di queste sottoclassi ri-implementa i metodi.
Forse il cofice, scritto in questo modo , con un solo assegnamento ha poco senso, ma la stessa cosa vale anche per i parametri dei metodi.
theClimber
04-01-2004, 20:35
Originariamente inviato da misterx
scusa ma se scrivo:
A test = new D();
A test = new A();
ottengo il medesimo risultato, quindi che senso ha ?
Assolutamente no. perche' nel 1° caso hai un oggetto di tipo D, quindi il codice che esegui e' diverso che nel caso di un oggetto di tipo A.
Ovviamente se D non cambia nulla rispetto ad A, e' inutile definere la sottoclasse
Originariamente inviato da theClimber
Assolutamente no. perche' nel 1° caso hai un oggetto di tipo D, quindi il codice che esegui e' diverso che nel caso di un oggetto di tipo A.
Ovviamente se D non cambia nulla rispetto ad A, e' inutile definere la sottoclasse
pardon, intendevo dire che con test vedo solo ed esclusivamente i metodi implementati nella classe A
forse sto usando delle classi troppo banali; eccole :)
class A{
public String metodoDiA(){
return "Hai richiamato un Metodo di A";
}
}
class B extends A{
public String metodoDiB(){
return "Hai richiamato un Metodo di B";
}
}
class C extends B{
public String metodoDiC(){
return "Hai richiamato un Metodo di C";
}
}
class D extends C{
public String metodoDiD(){
return "Hai richiamato un Metodo di D";
}
}
theClimber
04-01-2004, 20:52
Prova a fare questo:
- definisci D come:
class D extends C{
public String metodoDiD(){
return "Hai richiamato un Metodo di D";
}
public String metodoDiA(){
return "Pensavi di chiamere un metodo di A, ma io in quanto D ce l'ho diverso :D";
}
}
- Poi fai:
A test = new D();
System.out.println(test.metodoDiA());
That's all.
ok, grazie 1000; ci rifletto sopra un pò :)
cavolo, continuo a non vederci l'utilità di scrivere una cosa del genere:
A test = new D();
anche se in D ho sovrascritto (overriding) alcuni metodi di A :muro:
theClimber
05-01-2004, 11:10
Guarda, con l'esempio fatto l'utilita' e' nulla, in quanto invochi il metodo di A e/o D nella riga successiva dove hai creato l'oggetto. A cosa ti serve creare astrazioni in 2 righe di codice :confused:
Per servire a qualcosa devi generalizzare il tutto in situazioni piu' complesse. Piccolo esempio:
A e' la classe (o un interfaccia) che descrive un generico comportamento di un qualcosa (esempio: un titolo bancario). metodoDiA() e' il metodo x calcolare gli interessi.
Scriviamo un programma che calcola gli interessi. Scrivo solo il metodo, che sara' in una sua classe, distribuita in un suo jar :
public void calcolaInteressiCompositi(A titolo)
{
// Blah, Blah
titolo.metodoDiA();
// Blah, Blah
}
poi abbiamo la classe B, che rappresenta i titoli azionari
la classe C che rappresenta fondi con insiemi di titoli
la classe D che rappresenta un fondo specifico
a questo punto posso passare uno qualsiasi di questi oggetti al metodo definito, riusandone il codice e la logica. Pero' la chiamata al metodo verra' risolta con il comportamento dell'oggetto passato.
NOTA: A potrebbe essere un interfaccia, che definisce esclusivamente i metodi (Contratto) necessari al fine di calcolare gli interessi. Non si userebbe una classe concreta nel metodo calcolaInteressiCompositi, per evitare di farlo dipendere da un implementazione specifica rendendolo riusabile.
Keep on
PS: non prendere alla lettera l'esempio fatto, era la prima cosa che mi e' venuta in mente :D
Originariamente inviato da misterx
cavolo, continuo a non vederci l'utilità di scrivere una cosa del genere:
A test = new D();
anche se in D ho sovrascritto (overriding) alcuni metodi di A :muro:
prova a pensare all'esempio sugli animali che avevo fatto in questo thread.
se l'interfaccia (o la classe astratta) A ha un medoto per nutrire un generico animale tu utilizzando A test = D; puoi utilizzare quel metodo su un animale specifico (quello di tipo D), al posto di usare il metodo nutri() ridefinito dalla classe D.
probabilmente ti suona strano perché pensi che il metodo ridefinito dalla classe D sia il più adatto a trattare oggetti di tipo D ma per qualche motivo potresti aver bisogno dei vecchi metodi, ecco a cosa serve un assegnamento come questo :)
Esempio "da vita reale" (forse così è più intuibile)
Il metodo System.currentTimeMillis() di Java sotto Windows è meno preciso di quanto non sia lo stesso metodo sotto Linux, Solaris o MacOS. Windows però ha una libreria C che permette l'accesso ad un timer più preciso.
Naturalmente la tua applicazione deve girare sia su Windows che su Linux e sarebbe molto bello se si comportasse nello stesso modo su entrambi.
E tu che sei un programmatore Java non creresti mai due distribuzioni diverse, una per Windows e una per Linux/altri.
Dovendo "usare il timer" in continuazione preferisci evitare di inserire un "if" sul tipo di sistema operativo (naturalmente su un booleano).
"Simsalabim" un modo rapido per risolvere la faccenda viene proprio da quell' "A a = new D()" (cioè dal polimorfismo):
crei una superclasse "Cronos" che ha un solo metodo (getTime()) che restituisce il valore di System.currentTimeMillis().
poi crei una classe "WinCronos" figlia di "Cronos" che da una definizione diversa del metodo getTime() che vale solo per Windows.
Nella tua applicazione (durante l'inizializzazione) ci metti un bel:
Cronos crono;
if(SistemaOperativo == Windows) -> crono = new WinCronos()
else -> crono = new Cronos();
e nel resto del codice ti limiti ad usare
crono.getTime();
senza più preoccuparti di dove come e quando.
Puoi provare a pensare ad un modo per ottenere lo stesso risultato senza ricorrere al polimorfismo, scoprirai probabilmente che il codice che salta fuori è un po' meno ordinato.
Ciao.
Originariamente inviato da recoil
prova a pensare all'esempio sugli animali che avevo fatto in questo thread.
se l'interfaccia (o la classe astratta) A ha un medoto per nutrire un generico animale tu utilizzando A test = D; puoi utilizzare quel metodo su un animale specifico (quello di tipo D), al posto di usare il metodo nutri() ridefinito dalla classe D.
se non ho capito male quello che vuoi dirmi......
ma io posso accedere ai metodi di A anche scrivendo:
D test = new D();
in quanto così facendo ho a disposizione i metodi di tutte le superclassi di D; siano esse dirette "C" o indirette A e B
qui
http://nicchia.ingce.unibo.it/oop/web/4-abstract-classes.html
c'è un esempio che se seguito sino in fondo e se l'autore l'avesse terminato, mi avrebbe spiegato in definitiva l'utilità delle interfacce
L'autore è partito definendo un certo numero di classi astratte, alcune classi concrete e poi, dopo aver messo in evidenza un problema, quando stava per risolverlo attraverso l'ereditarietà multipla con l'uso delle interfacce ha interrotto il suo discorso:muro:
sono un caso disperato ? :D
Originariamente inviato da misterx
qui
http://nicchia.ingce.unibo.it/oop/web/4-abstract-classes.html
c'è un esempio che se seguito sino in fondo e se l'autore l'avesse terminato, mi avrebbe spiegato in definitiva l'utilità delle interfacce
L'autore è partito definendo un certo numero di classi astratte, alcune classi concrete e poi, dopo aver messo in evidenza un problema, quando stava per risolverlo attraverso l'ereditarietà multipla con l'uso delle interfacce ha interrotto il suo discorso:muro:
uhm troppo lungo da leggere, mi dici in che punto non ti è chiaro?
è partito con l'esempio degli animali ma poi alla fine non ha implementato alcuna interfaccia
azz :muro:
Originariamente inviato da recoil
mi è stata già chiesta una definizione di interfaccia e trovo che non sia molto semplice darla.
allo stesso tempo non è facile capire il perché dell'esistenza delle interfacce. l'ereditarietà è un concetto intuitivo e se ne capisce subito l'utilità: ereditando una classe ne eredito i metodi e quindi posso estendere le funzionalità aggiungendone di nuovi.
con l'interfaccia si ereditano delle costanti, niente di più. allora perché è utile un'interfaccia? per consentire l'ereditarietà multipla, che in Java non è implementata. tuttavia se non ho codice che cosa eredito? eredito il "tipo".
ti faccio un esempio: se la tua classe Cane implementa l'interfaccia Animale allora una istanza di Cane potrà essere assegnata a una variabile di tipo Animale.
questo a che serve? continuiamo l'esempio
l'interfaccia Animale ha un metodo chiamato nutri. Cane ha la propria implementazione di nutri() che può essere diversa da quella degli altri animali ma se in qualche altra classe (ad esempio la classe Zoo) ho bisogno di chiamare il metodo nutri() su una bestia qualsiasi posso farlo tramite una istanza di Animale pur facendo riferimento al cane o al gatto proprio perché ho implementato l'interfaccia.
recoil, tu intendevi una situazione del genere ?
interface Animale{
String nutri();
String corre();
}
class cane implements Animale{
public String nutri(){
return "una scatoletta di carne al gg";
}
public String corre(){
return "la sua velocità max è 50 Km/h";
}
}
class zoo{
public static void main(String[] args){
Animale c = new cane();
System.out.println( c.corre() );
}
}
Originariamente inviato da misterx
recoil, tu intendevi una situazione del genere ?
esatto!
ad una variabile di tipo Animale puoi assegnare un oggetto che è istanza di qualsiasi classe implementi Animale. l'overloading dinamico penserà a prendere i metodi giusti :)
scusa recoil ma quella da me messa in evidenza non è solo una delle caratteristiche delle interfacce ?
guarda quest'altra soluzione dove è sufficiente istanziare un oggetto, animale diverso ma che implementa l'interfaccia, che tutto il codice si adegua di conseguenza. Mi immagino un uso analogo ma per progetti molto complessi; ma non so se sia questo l'uso principale delle interfacce :(
interface Animale{
String nome();
String nutri();
String corre();
}
class cane implements Animale{
public String nome(){
return "cane";
}
public String nutri(){
return " una scatoletta di carne al gg ";
}
public String corre(){
return " 50 Km/h ";
}
}
class gatto implements Animale{
public String nome(){
return "gatto";
}
public String nutri(){
return " gatto: una scatoletta di carne al gg ";
}
public String corre(){
return " 30 Km/h ";
}
}
//--------------------------------------------------------
class Prova{
public static void main(String[] args){
Animale c = new cane();
System.out.println( "Il " + c.nome() + c.corre() );
}
}
scusate se rompo ma ho bisogno di conferme da chi è più esperto di me sul linguaggio java; quindi up
Originariamente inviato da misterx
guarda quest'altra soluzione dove è sufficiente istanziare un oggetto, animale diverso ma che implementa l'interfaccia, che tutto il codice si adegua di conseguenza. Mi immagino un uso analogo ma per progetti molto complessi; ma non so se sia questo l'uso principale delle interfacce :(
credo che l'utilità principale delle interfacce sia quella di rendere pubblico il comportamento di una certa classe.
sapendo che la classe x implementa l'interfaccia y (supponiamo che io conosca y) mi permette di conoscere alcuni dei metodi della classe e di utilizzarli.
ad esempio se una classe implementa l'interfaccia Comparable so che posso fare affidamento sulla presenza del metodo compareTo() per confrontare gli oggetti di tale classe.
un altro uso è proprio quello di poter avere una variabile "generica" alla quale assegnare istanze di diverse classi.
theClimber
10-01-2004, 17:15
Prova a pensare alle interfacce come a delle "proprietà" o "features" di una classe. Con le interfacce definisco un metodo che sicuramento so essere implementato secondo la specifica dell'interfaccia stessa, ma non sono abbilgato ad utilizzare la gerarchia di ereditarieta' singola.
Ad esempio la seguente interfaccia definisce una possibile caratteristica:
public interface Pesabile
{
double getPesoInKg();
}
a questo punto posso avere sia:
public class Animale implements Pesabile
{ ... }
public class Veicolo implements Pesabile
{ ... }
Dove Veicolo e Animale sono le 2 suprclassi di 2 gerarchie che poco altro hanno in comune tra loro.
Quindi, per precisare meglio quello che giustamente ha detto recoil, le interfacce servono per dichiarare pubblicamente sottoinsiemi significativi del comportamento della classe. Questi sottoinsiemi di comportamenti sono utilizzabili anche senza conoscere i dettagli di tutta l'implementazione della classe.
Ciao
mah, il mio libro dice che un'interfaccia è una promessa e tale promessa viene mantenuta dalla classe che implementa l'interfaccia
l'unica cosa che mi sfugge è se leggendo una tale affermazione ci si deve porre da sviluppatore della classe o utilizzatore...mah...
Originariamente inviato da misterx
mah, il mio libro dice che un'interfaccia è una promessa e tale promessa viene mantenuta dalla classe che implementa l'interfaccia
l'unica cosa che mi sfugge è se leggendo una tale affermazione ci si deve porre da sviluppatore della classe o utilizzatore...mah...
più che promessa di solito si parla di contratto. vedila così: implementando l'interfaccia x lo sviluppatore si impegna (da qui il contratto) a implementare i metodi che vi sono indicati.
quindi la promessa la mantiene lo sviluppatore della classe
theClimber
11-01-2004, 20:04
Originariamente inviato da misterx
mah, il mio libro dice che un'interfaccia è una promessa e tale promessa viene mantenuta dalla classe che implementa l'interfaccia
Corretto. Chi implementa l'interfaccia o una sottoclasse che modifica i metodi dell'interfaccia deve rispettarne il contratto. L'utilizzatore della classa/interfaccia la puo' usare proprio perche' esiste il contratto per cui sa quale e' il comportamento atteso del codice.
NOTA: prestare sempre attenzione quando si crea una sottoclasse e si sovrascrive un metodo, il contratto definito dalla superclasse (Che eventualmente puo' essere un interfaccia) deve rimanare valido. (La formalizzazione di questo si puo trovare facendo una ricerca sul 'principio di sostituzione di Liskov')
Originariamente inviato da theClimber
Se la classe e' lo "stampo" con cui si creano gli oggetti, i metodi e le variabili static sono le caratteristiche dello "stampo".
scusa ma, non sarebbe più giusto dire che:
una classe è uno stampo di variabili ed i suoi metodi servono a modellarle ?
mi pare preferibile la definizione di theClimber.
non mi piace pensare a una classe come collezione di variabili perché queste ne fanno parte.
le variabili sono in genere nascoste all'utente della classe, pertanto devi focalizzarti sui metodi, i quali forniscono un servizio.
se dai importanza alle variabili la classe diventa una specie di contenitore nel quale si può leggere e scrivere ma io preferisco vederla come uno strumento per la creazione di oggetti i quali forniscono servizi ad altri oggetti.
le classi "contenitore" esistono ma non sono certo il caso più frequente.
Abbraccio sicuramente la definizione di theClimber (e vien voglia di abbracciare anche theClimber, nota infatti il riferimento al fatto che variabili e metodi static siano caratteristiche dello stampo).
Come la mette misterx mi sembra invece un po' più oscura, perchè variabile è un termine della programmazione in generale, classe e oggetto appartengono alla programmazione oo.
Una classe è uno stampo per delle variabili: si, se, come credo, per variabili intendi gli oggetti (e allora la definizione coincide nella prima parte con quella di theClimber).
Sul fatto che i metodi modellino le variabili (oggetti) ho qualche difficoltà, ma è un ottimo appiglio per un problema di comprensione che mi affligge.
In "OO-teoria" i metodi gestiscono le transizioni di stato di un oggetto (per immediatezza, possiamo pensare ai metodi on() off() di un oggetto "interruttore").
Non ho grosse difficoltà a pensare all'impostazione di uno "stato" dell'oggetto (i vari metodi set...()) come ad una transizione, perchè effettivamente si può dire che cambiare un parametro è, in buona sostanza, una mutazione dell'oggetto.
Quello che non riesco ad infilare da nessuna parte è il problema dei metodi usati per ottenere informazioni sullo stato di un oggetto (i molti getQualcosa() ).
E' innegabile che qui non ci sia alcuna transizione: l'oggetto resta tale e quale.
Sono consapevole del fatto che l'accesso ad una proprietà dell'oggetto attraverso un metodo sia per lo più necessaria per proteggere l'integrità di una caratteristica dell'oggetto stesso.
La curiosità che ho è se esista un linguaggio OO che consenta l'accesso "pubblico" ad una o più proprietà di un oggetto in sola lettura salvo che si tratti di un "figlio" o dell'oggetto stesso.
Tra l'altro magari esiste un modo per farlo in Java e io nemmeno lo so (se c'è mi mangio la tastiera :D).
Ciao.
ragazzi facciamo un pò di ordine: le mie tapine definizioni servono solo a me per capire e scusate se chiedo in modo "public" in luogo di private :D
cmq, apprezzo molto le vostre considerazioni :)
:D Niente scuse, verrai frustato sulla pubblica piazza per aver osato avere dubbi! DUBBI! AH! Il vero programmatore non ne ha mai! :D
Ma sapevate che non esiste una definizione di Orientamento agli Oggetti?
Non molto tempo fa stavo peregrinando per la rete e ho incontrato una pagina web su Eiffel in cui si confrontava la struttura di questo linguaggio con Java e l'Autore concludeva dicendo che Eiffell è un linguaggio orientato agli oggetti e C++/Java no. Dopo aver letto e riletto l'articolo ho capito che l'Autore concludeva, ma non motivava... :mc:
Io avevo sempre pensato che ci fosse un qualche genere di accordo tacito, un manuale di riferimento, uno strano organismo internazionale di controllo degli standard...ciccia, non c'è niente, potremmo metterci qui e blaterare ognuno per ore su cosa sia l'oop e avremmo tutti ragione.
Mi viene da ridere mi viene, perchè, diciamoci la verità, quando poco sopra si parlava di interfacce, di contratti e cose così, io per primo pensavo "e adesso come faccio a spiegare con parole mie come funzionano veramente le interfacce"...ma con un minimo di onestà dovrebbero (e dovremmo) dire che le interfacce non hanno un significato, praticamente sono mezzi puri e, come categoria, assumono una definizione solo, ed esclusivamente, nel momento in cui vengono usate.
Il tutto per dire, misterx, che le tue "tapine definizioni" (:D) valgono tanto quanto quelle contenute nel migliore dei manuali su Java: è per questo che ho definito "un po' più oscura" ma non "sbagliata" la tua definizione di classe.
E queste sono cose che devono essere chieste "pubblicamente" perchè così ci rende conto che qui e adesso noi facciamo l'oop (ammazza che frase, sa di biblico, eh? :D).
Ciao.
Originariamente inviato da PGI
E queste sono cose che devono essere chieste "pubblicamente" perchè così ci rende conto che qui e adesso noi facciamo l'oop (ammazza che frase, sa di biblico, eh? :D)
biblica e condivisibile
;)
non abbiamo ancora parlato delle eccezioni controllate e NON controllate :D
Come ti giri un attimo scopri che un thread da prima pagina è finito in purgatorio :D
Con quel "controllata", riferito alle eccezioni, la banda di Gosling distingue tra le eccezioni la cui intercettazione deve essere necessariamente prevista prima della compilazione (es. IOException) e quelle che possono anche non essere intercettate (es. NullPointerException).
Naturalmente, nel secondo caso il programma si "impasta" durante l'esecuzione se viene generata l'eccezione.
Il motivo per cui esiste la distinzione è puramente "pratico": se prendi ad esempio l'eccezione NullPointerException, poichè ogni riferimento può essere potenzialmente "null", ogni volta che ne usi uno dovresti inserire un blocco try-catch, il che potrebbe anche far girare i marroni (un termine tecnico :D).
Ciao.
cmq, le eccezioni in java sono classi e come tali sottoposte ad una gerarchia
sono da definirsi eccezioni NON controllate tutte le sottoclassi di RuntimeException
controllate le superclassi di RuntimeException
se si decide di definire una propria eccezione del tipo controllata(dal compilatore ovviamente), si deve scrivere codice nel metodo che la solleva
una classe=fabbrica dell'oggetto
esempio:
class PorcaZozzaException extends Exception {
PorcaZozzaException (String s) {
super(s);
}
}
throw new PorcaZozzaException("che cacchio mi combini ?");
dichiarare che il metodo alza l'eccezione con
public WlaCiccia(String ciccia) throws PorcaZozzaException {
......................
}
usare codice nel chiamande del metodo che catturi l'eccezione sollevata dal metodo
try{
.....................................
}catch(PorcaZozzaException e){System.out.println(e.toString);}
se tutto ciò viene fatto in modo corretto, il compilatore si rilassa e vi compila il codice
a che serve sto casotto ?
mah, tutto sommato sembra una sorta di promemoria per il programmatore ,?in modo che sappia che un metodo da lui scritto solleva di sicuro una eccezione
a conti fatti W il C :D
Originariamente inviato da misterx
a conti fatti W il C :D
:D M'hai provocato...e mo me te magno :D
Quando gli antichi Romani cominciarono la loro espansione oltre i confini dell'Italia, cominciarono a sentire le prime parole di una lingua molto diversa dal latino, che già allora era una lingua estremamente elegante. La lingua di quegli uomini sembrava loro una serie indistinta e disarticolata di suoni che in latino suonavano come un continuo "BAR...BAR...BAR", dal che la parola BARBARO.
Dopo aver usato Java, tutti gli altri sembrano C-BARI.
ecco PGI, divertiti a capire col tuo compilatore preferito la sequenza di esecuzione di ste classi
Io ho provato con NetBeans ma si capisce poco nulla perchè:
a) arriva alla linea A c = new A(30)
b) la abbandona per inizializzare la variabile stativ
c) torna sulla (a)
d) punta sul costruttore e fa qualcosa
e) inizializza le altre variabili
f) entra nel costruttore e fa quello che deve fare
g) torna e legge la chiamata del metodo
h) finalmente lo stampa
non ho capito bene l'ordine; se hai tempo
class Prova{
public static void main(String[] args){
A c = new A(30);
System.out.println(c.getS());
A d = new A();
System.out.println(d.getS());
System.out.println(d.getP());
System.out.println(A.getV());
}
}
class A{
private static int v = 10;
private int p = 20;
private int s;
public A(int q){
v++;
p++;
s = q;
}
public A(){
this(7);
}
public int getP(){
return p;
}
public int getS(){
return s;
}
public static int getV(){
return v;
}
}
Mi sfugge più di una cosa, tra cui i dubbi che dovrebbe chiarire il codice che hai riportato (capisco che è solo un esempio, ma scrivere A c = new A e poi usare c v d...non aiuta :D).
A c = new A(30).
prima di eseguire il contenuto del costruttore la jvm esegue l'inizializzazione della classe, per cui l'ordine è:
v = 10
p = 20
s = 0
[q = 30]
v = 11
p = 21
s = q -> = 30
stampa il valore di s
il secondo costruttore inizializza i campi non-static per d e via così.
Dov'è che ti "ingrippi"? (ma soprattutto, perchè ti sei infilato in questa faccenda? è una curiostià quasi morbosa :D)
Ciao.
scusa ma non ho capito la tua risposta in quanto poco dettagliata
devo sostenere un orale; quello sopra è un esercizio di un compitino:mad:
Io non vorrei fraintendere quello che ti interessa sapere di quel codice, per poi rifilarti una pappardella che magari nemmeno ti interessa. Ti dico quello che so.
Supponendo che ti chiedano qual'è il flusso di esecuzione (lo chiamo io così, in inglese ho letto "runtime flow", non so quale sia il termine tecnico italiano) di quelle linee di codice
A c = new A(30);
System.out.println(c.getS());
A d = new A();
System.out.println(d.getS());
System.out.println(d.getP());
System.out.println(A.getV());
balza agli occhi il fatto che l'unico aspetto che possa dirsi minimamente interessante riguarda l'applicazione delle regole di inizializzazione di una classe, che, tra l'altro, sono piuttosto sintetiche.
Prendi la prima linea: quel
A c = new A(30)
presuppone un bel po' di lavoro per la macchina virtuale.
Prima di tutto verifica l'esistenza del modello A in memoria. Se manca inizializza effettua il caricamento della classe A, il che comporta l'inizializzazione dei campi static, l'esecuzione del codice contenuto in un blocco di inizializzazione statico e la creazione dei metodi static.
A c = new A(30). -> praticamente è la prima istruzione contenuta nel codice. In memoria non esiste il modello A, per cui prima di creare un istanza di A la macchina virtuale:
Crea in memoria il modello di classe A (in realtà è ClassLoader+A, ma a noi interessa relativamente).
Crea ed inizializza il campo statico di tipo int V (perchè per noi è la prima cosa static che trova nel codice binario della classe) associato al modello A. Ed quello che avevi già notato nel passaggio a) -> b) nel tuo post.
Poi carica in memoria anche il metodo getV(). Anche questo è static, per cui non può farne a meno. Anche questo è associato ad A.
Torna all'istruzione new A(30), adesso il modello A "esiste", può passare all'inizializzazione di un'istanza della classe, come richiesto dal costruttore.
Prima di eseguire il costruttore, la macchina virtuale inizializza tutto quello che non è static nella classe A
inizializza un intero a 20 e uno a 0 (p ed s), crea i metodi.
Poi effettivamente passa alle istruzioni richiamate nel costruttore.
Terminato il costruttore crea il riferimento di tipo A chiamato "c" è lo punta sulla locazione di memoria in cui ha infilato l'oggetto appena creato.
Vita morte e miracoli di ciò che succede dopo è frutto di quella semplicissima regola (che nell'esercizio che ti hanno rifilato è nascosta da inutili ripetizioni e dichiarazioni al limite dell'irritante).
Prima di creare un'istanza verifica che esista il modello.
Se manca il modello lo carica, inizializzando campi, e blocchi statici, nell'ordine in cui si presentano, più i metodi.
Se c'è il modello, crea l'istanza, inizializzando campi metodi e blocchi non statici.
Dall'istanza crea l'oggetto eseguendo il costruttore designato nel codice.
Quando ha l'oggetto in mano, crea il riferimento e lo punta all'oggetto.
La sequenza è logicamente necessaria e la puoi verificare controllando una serie di errori in compilazione, restituiti nel caso in cui non sia rispettata nel codice. E' il caso, ad esempio, in cui ti trovi quando in un metodo "static" cerchi di usare un campo un un metodo non static. La compilazione fallisce perchè non rispetta l'ordine di inizializzazione.
La stessa ragione sta alla base del fatto per cui non è possibile usare un campo static in un blocco di inizializzazione statico che preceda l'inizializzazionde del campo, mentre è possibile farlo se il blocco di inizializzazione è non-static: l'ordine di inizializzazione impone che uno static preesista a qualsiasi cosa sia non-static.
riassumendo è così ?
- verifica modello
- eventualmente lo crea
- inizializza variabili e metodi static (una sola volta per tutte)
- crea istanza ed inizializza altre variabili non static
non mi ricordo se avevamo parlato della Lazy Evaluation, considerando magari la seguente espressione:
(x > 0) && (y++ != x)
cosa ti fa esprimere una espressione di tale tipo ?
per i meno pigri, fate delle prove e raccontatemi cosa accade
Alla vista, esprimo un po di disagio :D
La parte a destra (y++ != x) è una trappola da evitare. Usare un incremento sulla stessa linea di un confronto non è proprio il massimo della vita, sopratutto per chi, in seguito, si trovasse a dover maneggiare un codice scritto così (perchè y viene incrementato dopo aver fatto il confronto, ma leggendo il codice è possibile che l'attenzione cada sul confronto e non sul fatto che dopo questo y aumenti in ogni caso di uno).
Invece gli operatori pigri, se usati con giudizio, permettono di rendere un po' più snella la lettura.
Supponiamo ad esempio che tu debba valutare una proprietà di un oggetto, pur sapendo che tale oggetto potrebbe essere null, qui l'operatore "pigro" (è un termine che mi piace un sacco :) ) permette di concentrare in una riga, che resta comunque altamente leggibile, un piccolo percorso ad ostacoli.
if( oggetto != null && oggetto.campo == qualcosa) ->...
theClimber
31-01-2004, 12:08
Originariamente inviato da misterx
non mi ricordo se avevamo parlato della Lazy Evaluation
Ok, la valutazione dell' && e' lazy, se fallisce il primo check partendo da sinistra il secondo check non viene nemmeno eseguito. La stessa cosa vale anche per l'operatore ||, ma la contrario, per cui se il primo test riesce non c'e' necessita di eseguire gli altri.
Vedi: http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#5247
NOTA stilistica:
il secondo test ha il side effect di modifcare y indipendentemente dall'esito del check eseguito, rendendo il codice poco evidente e di difficile lettura, in quanto normalmente tutti sono abiutuati al fatto che le espressioni booleane di questo tipo sono utilizzate per verificare valori e non cambiarli.
In questo contesto la modifica di y e' utile per verificare quali delle condizioni sono state eseguite, ma in una situazione di sviluppo reale usare codice di questo tipo dentro ad un IF dovrebbe essere evitato a tutti i costi, pena il licenziamento in tronco di chi ha scritto il codice :p ;)
theClimber
31-01-2004, 12:14
Ops, in contemporanea o quasi.
Concordo al 1000% con PGI.
Aggiungo solo che il peggio dell'incremento all'interno del test e' il fatto che viene eseguito indipendentemente dal fatto che il test fallisca o no. infatti ci sarebbero 3 casi:
x<0 , test fallisce ma y non viene incrementato
x>0 , secondo test fallisce ma y viene incrementato
X>0, secondo check riesce, y viene incrementato
Abbastanza per far venire il mal di testa :muro:
lasciate perdere le critiche, è codice prettamente didattico del docente :)
cmq, ho provato a compilare ol turbo C 3.0 e non incrementa "y" nel caso in cui la parte sinistra dell'espressione è falsa
significa che anche nel 1980 la valutazione pigra era già stata implementata da mamma Borland ?
Originariamente inviato da misterx
lasciate perdere le critiche, è codice prettamente didattico del docente :)
Critica è un termine che può essere frainteso, ognuno di noi fa le osservazioni che ritiene giuste, è poco didattico se da un lato mostra una cosa giusta e dall'altro ne fa una "pericolosa" (perchè formalmente corretta ma da "spezzadita" :D).
Io credo che si debba distinguere tra l'operatore lazy e la tecnica di comparazione lazy. I primi sono una semplificazione grammaticale, ma la sostanza stà nella seconda.
Mi spiego. Il fatto che esista, in Java, in C ecc..., un operatore dedicato al confronto pigro non significa che siano questi gli unici linguaggi a supportare questo particolare tipo di confronto.
Tecnicamente (almeno in Java) un && è un if annidato che opera sulla comparazione inversa:
io scrivo
if (a > 0 && b >0)
il codice che risulta è un
if(a <= 0) salta al punto successivo al blocco if
if(b <=0) salta al punto successivo al blocco if.
il che è identico, appunto, ad un if annidato:
if(a > 0) {
if( b > 0) {
//...
}
}
In buona sostanza, l'operatore lazy non è altro che una comodità per il programmatore. La valutazione pigra è implementata in ogni linguaggio che riconosca gli operatori di confronto "semplici" e l'espressione condizionale if-then (cioè tutti i linguaggi). "Mamma borland" (a cui peraltro molto è dovuto) da questo punto di vista non ha implementato un bel nulla che non esistesse ai tempi dell'Algol.
Ciao.
Ciao.
Originariamente inviato da PGI
Come ti giri un attimo scopri che un thread da prima pagina è finito in purgatorio :D
Con quel "controllata", riferito alle eccezioni, la banda di Gosling distingue tra le eccezioni la cui intercettazione deve essere necessariamente prevista prima della compilazione (es. IOException) e quelle che possono anche non essere intercettate (es. NullPointerException).
Naturalmente, nel secondo caso il programma si "impasta" durante l'esecuzione se viene generata l'eccezione.
Il motivo per cui esiste la distinzione è puramente "pratico": se prendi ad esempio l'eccezione NullPointerException, poichè ogni riferimento può essere potenzialmente "null", ogni volta che ne usi uno dovresti inserire un blocco try-catch, il che potrebbe anche far girare i marroni (un termine tecnico :D).
Ciao.
sai che ogni tanto torno sui medesimi passi in quanto qualche dubbio riemerge sempre :D
tu dici:
Naturalmente, nel secondo caso il programma si "impasta" durante l'esecuzione se viene generata l'eccezione.
ma si impasta in ogni caso se non usi un blocco try/catch e quindi, dove sta la finezza ?
Quel "naturalmente" era per sottolineare il fatto che sebbene un'eccezione non controllata non debba essere necessariamente gestita dal programmatore, in esecuzione si comporta esattamente ocme un'eccezione controllata, cioè interrompe la normale esecuzione del programma.
La "finezza" sta nel fatto che, per praticità, non sei obbligato a gestire eccezioni la cui evenienza sarebbe o troppo frequente o ingestibile.
La differenza tra eccezioni non controllate e controllate non risiede nella gerarchia, ma nel diverso modo in cui possono essere trattate.
Che la definizione a te rifilata da qualche buontempone sia errata è motivato non dal solo fatto che Gosling&Co. (che pure hanno definito le specifiche del linguaggio) chiamino eccezione non controllata ciò che intendo per tale, ma il fatto che, secondo logica, se usi RuntimeException per dire che sono eccezioni non controllate tutte le sue sottoclassi, non ti resta che dire che RuntimeException e un'eccezione non controllata, e a questo punto sappiamo chi siano queste eccezioni non controllate ma ancora non si sa cosa siano rispetto a tutti gli altri Throwable (saltando il fatto, non di poco conto, che anche gli Error sono eccezioni non controllate).
Originariamente inviato da PGI
La "finezza" sta nel fatto che, per praticità, non sei obbligato a gestire eccezioni la cui evenienza sarebbe o troppo frequente o ingestibile.
continuo a non capirti
se scrivi male un metodo e viene sollevata una eccezione il tuo programma cade e se non viene trovato nessuno che la cattura la JVM si limita a segnalare la sua bella catena di propagazione della eccezione
controllata o meno da come l'ho capita io serve solo in fase di compilazione al compilatore
se io definisco una eccezione mia personale che estende Exception, devo dire al compilatore chi solleverà l'eccezione atrimenti non compilo; tutto qui
non so se serva al compilatore per aggiungere nel bytecode riferimenti suoi personali
:muro:
Le eccezioni non sono lì perchè scrivi male un metodo.
Per quello c'è la bacchetta di frassino con cui frustare le mani.
il mio libro le definisce anomalie nella programmazione
tali anomalie vengono messe in evidenza quando scrivi male il codice
Però in quel libro si capisce anche che per "evidenza" intende la manifestazione dell'eccezione come interruzione del flusso del programma,.
In questo senso è un problema nella programmazione, quando, cioè, fai girare il tuo programma e ad un certo punto salta fuori un bel messaggio sulla consolle (oppure il programma cessa di rispondere).
Se nel tuo programma usi un socket e l'utente sbatte giù il telefono, viene generata una IOException.
E questa non è un'anomalia della programmazione (su questo non puoi che convenire).
Se questo non causa l'interruzione del tuo programma (perchè evidentemente hai gestito il caso IOException) ha programmato "bene", se va tutto a schifio, hai programmato "male".
non ho mai usato le funzioni(metodi) di I/O standard di java sio ad ora, purtroppo :(
ma ti obbligano a intercettare o a delegare eccezioni controllate ?
Su e giù per le librerie trovi diversi metodi che richiedono necessariamente la gestione di un'eccezione controllata.
La maggior parte dei metodi che gestiscono flussi input/output richiedono la gestione di IOException, poi ci sono anche metodi che, pur avendo meno a che fare con il collegamento tra l'applicazione e "l'hardware", "rilasciano" eccezioni, ad esempio alcuni componenti di testo obbligano all'intercettazione di un BadLocationException quando cerchi di infilarci dentro qualcosa.
Il fatto di non aver ancora usato le librerie IO non deve assolutamente scoraggiarti. Se, come credo tu stia facendo, approfondisci bene la struttura del linguaggio in sè, arriverai facilmente a poter usare la documentazione delle classi e dei metodi con "naturalezza": a quel punto passare da un Servlet ad uno String non richiederà che pochi minuti di lettura.
sisi va, ho bello che capito che ho seguito un corso del ca@@o
come si fa a non dire che ste zozze di eccezioni sono obbligatorie da "intercettarsi o da controllarsi o da delegare" se si utilizzano particolari metodi o classi di java ?
ma porca zozza......
ma come si fa ad imbastire un corso insistendo solo sulla costruzione e/o dichiarazione di eccezioni/classi/metodi propri ?
devo scoprire solo ora e grazie ad un banalissimo esempio del ca@@o che sono semplici direttive, regole, obbligazioni o come cavolo le vogliamo chiamare del compilatore alle quali devo sottostare se nò non compilo neanche a morire
l'ho sempre detto che il mondo è pieno di perditempo :muro:
PGI, non so se sono stato chiaro ed hai capito quali erano i "miei" dubbi :mad:
ok, intanto la colpa è sempre mia vero ?;)
Originariamente inviato da misterx
sisi va, ho bello che capito che ho seguito un corso del ca@@o
Non era mia intenzione dare alcun giudizio sulla validità del corso che hai seguito. E mi sembra strano che tu ne abbia un'opinione così bassa, di solito ogni serie di lezioni, per quanto "pessima" possa ritenersi, aumenta le "skill" di chi la segue.
ma come si fa ad imbastire un corso insistendo solo sulla costruzione e/o dichiarazione di eccezioni/classi/metodi propri ?
Se il corso è universitario necessariamente chi lo tiene deve insegnare il succo di Java. Se ti è stato mostrato come costruire e gestire un'eccezione a maggior ragione sarai in grado di gestire un'eccezione costruita da altri (ti risparmia metà del lavoro).
Per quanto riguarda la creazione di classi e metodi, beh, se non insegnassero come farseli da sè probabilmente staremmo parlando di C, non di Java.
PGI, non so se sono stato chiaro ed hai capito quali erano i "miei" dubbi :mad:
Ad essere sincero sono io adesso ad avere dei dubbi su quali fossero i tuoi dubbi :confused: :D
ok, intanto la colpa è sempre mia vero ?;)
Come "studente" sei nell'invidiabile posizione di chi, non sapendo per definizione (si studia ciò che non si sa), per definizione non ha colpe
Originariamente inviato da PGI
Ad essere sincero sono io adesso ad avere dei dubbi su quali fossero i tuoi dubbi :confused: :D
tutti quelli che ho espresso sino ad ora; dall'inizio del 3D :)
domande a bruciapelo :)
a) a cosa serve un'interfaccia ?
b) supponendo che all'inizio x sia positivo e y negativo:
while (x != y){
x = x-3;
y = y+5;
}
• il ciclo può essere infinito ?
• il ciclo può terminare ?
C'è aria di esami?
a) Quanto detto nei post precedenti.
b) Ecco perchè non ho scelto facoltà scientifiche.
Dai miei ricordi di analisi, direi che quel ciclo termina nel caso in cui
y - x = - 8, che dovrebbe essere una retta.
Allora è infinito se i valori iniziali di x e y sono tali che i punti x,y e x-3,y+5 appartengono ad una retta parallela a quell'altra altrimenti si incocciano e il ciclo va a patatrack (Lagrange mi fa un baffo mi fa :D). Da qui poi probabilmente qualcuno dotato di mente pensante riuscirebbe anche a cavarne i valori iniziali di x e y per cui il ciclo è infinito e quelli per cui non lo è, io mi accontento di avere il pollice opponibile :D
Ciao.
while (x != y){
x = x-3;
y = y+5;
}
beh, se non erro si ferma per x=3 ed y=-5 ma non so come sia possibilie ricavare a colpo d'occhio i valori che portano il loop "all'infinito"
p.s.
all'infinito non ci arriverà mai in quanto ci sono i limiti della macchina=calcolatore=PC=computer=blablabla :D
che trappole che ti piazzano :ncomment:
cmq, PGI, tu che lamentavi della non esistenza di una vera definizione di "classe", non esiste nemmeno traccia della definizione di "interfaccia" :D:D:D
meglio ridere va :D
theClimber
07-02-2004, 10:11
Riprendo l'argomento delle eccezioni, per dirti:
non scoraggiarti !
Le domande che ti poni non sono poi così banali, e bisogna gia' aver capito abbastanza solo per il fatto di essersele poste. :cool:
Tantopiu' che l'argomento non e' banale, essendo un punto abbastanza controverso di Java, che e' il solo linguaggio ad avere le eccezioni gestite.
Queste servono esattamente per essere:
Originariamente inviato da misterx
semplici direttive, regole, obbligazioni o come cavolo le vogliamo chiamare del compilatore alle quali devo sottostare se nò non compilo neanche a morire
Il fatto che esistano le eccezioni gestite non significa che bisogna abusarne (La tipica sindrome del 'esiste e percio' lo devo usare'), dimenticandosi delle RuntimeException.
Quando servono le eccezioni gestite? L'esempio di PGI sull'IOEXception e' molto opportuno, infatti in questi casi si ha sempre un reminder del fatto che il metodo che stai chiamando ha delle dipendenze da qualcosa (Il file system, la rete,...). Se non ci fosse la dichiarazione dell' eccezzione, il metodo 'mentirebbe' dicendo che una volta chiamata l'operazione puo' sempre essere eseguita senza problemi, quando in realta' questo dipende da cause esterne non controllabili.
Ti faccio un controesempio sulla RuntimeException: la divisione per 0 !! in questo caso l'eccezione e' runtime, in quanto viene generata se l'operazione viene chiamata utilizzando dati che non ne rispettano il contratto (Sono affaracci tuoi fare i controlli e non dare lo 0 in input).
Un'ultima cosa: le norme e le regole di programmazione di un linguaggio hanno lo scopo di parlare al computer (che si beve :gluglu: tranquillamente il codice eseguibile binario) , ma sono strumenti di lavoro forniti al programmatore per lavorare sia da solo, sia con altri. Linguaggi diversi hanno strumenti diversi, e non sempre uno strumento va bene per tutto, per questo e' fondamentale imparare e capire + linguaggi.
Ciao
ma ora che ci penso ho usato un metodo per l'apertura e lettura di un file ma non ricordo che il compilatore mi obbligasse a sollevare o catturare eccezioni in quanto, secondo il compilatore, tale metodo solleva per certo una eccezione:muro:
tornando un passo indietro e siccome in java non vedo mai usare l'operatore (distruttore ?) delete mi stavo chiedendo se la variabile static presente nella classe A viene inizializzata una sola volta in entrambi i casi espressi sotto; a mio avviso si e v permane identica o aumentata fino a quando una nuova istanza della medesima variabile viene invalidata
traduco quanto espresso sopra :D
se scrivo così
A c = new A(30);
System.out.println(c.getS());
A d = new A();
oppure così
A d = new A();
System.out.println(c.getS());
A c = new A(30);
la variabile di tipo static viene inizializzata solo la prima volta: giusto ?
class Prova{
public static void main(String[] args){
A c = new A(30);
System.out.println(c.getS());
A d = new A();
System.out.println(d.getS());
System.out.println(d.getP());
System.out.println(A.getV());
}
}
class A{
private static int v = 10;
private int p = 20;
private int s;
public A(int q){
v++;
p++;
s = q;
}
public A(){
this(7);
}
public int getP(){
return p;
}
public int getS(){
return s;
}
public static int getV(){
return v;
}
}
theClimber
08-02-2004, 12:51
Originariamente inviato da misterx
la variabile di tipo static viene inizializzata solo la prima volta: giusto ?
[/code]
Essenzialmente si. Viene inizializzata quando viene caricata la classe dal classloader.
Cmq se non e' final, puoi sempre cambiargli valore.
E' uscita la 1.5 (v. beta). Ragazzi, sono arrivati i nuovi giochini! :D
Originariamente inviato da PGI
E' uscita la 1.5 (v. beta). Ragazzi, sono arrivati i nuovi giochini! :D
per ora non la metto, di solito sto alla larga dalle beta :D
ho visto che hanno modificato nuovamente RMI, adesso non ci sono più nemmeno gli stub
I giochini, i giochini. :D
L'ho scaricata 5 minuti fa e sto "paciugando" con le nuove "features"
import java.util.*;
public class Prova {
public static void main(String[] a) {
new Prova();
}
public Prova() {
Vector<Tip> elementi = new Vector<Tip>();
elementi.add(new Tip(10, 10));
Tip tip = elementi.get(0);
System.out.println(tip.x);
System.out.println(tip.y);
}
class Tip {
int x, y;
Tip(int x, int y) {
this.x = x;
this.y = y;
}
}
}
compilato con javac Prova.java -source 1.5 -version 1.5
Adesso prova l'autoboxing e i parametrici.
Compatitemi, sono uno che si diverte con poco :D.
ti ho lasciato rilassare coi tuoi giochini anche sin troppo :D
mi stavo chiedendo se è sempre vero che quando si istanzia un oggetto il costruttore della classe X si fa aiutare sempre dal costruttore della sua superclasse :confused:
Quando chiami il costruttore di una classe, implicitamente invochi il costruttore vuoto della classe superiore, su su fino ad arrivare ad object.
Puoi "ridirigere" il traffico dichiarando quale costruttore della classe superiore desideri sia invocato, ma non puoi evitare che la trafila di costruttori "superiori" sia invocata.
Prendi ad esempio le due classi:
public class Padre {
String testo = "Nessuno";
private Padre() {
testo = "Padre";
System.out.println("Eseguito costruttore Padre()");
}
public Padre(int numero) {
System.out.println("Eseguito costruttore Padre(int)");
}
public String getTesto() {
return testo;
}
}
public class Figlio extends Padre {
public Figlio() {
super(20);
System.out.println("Eseguito costruttore Figlio()");
}
}
Se togli da "Figlio" il "super(20)", che richiama un costruttore specifico di "Padre", la classe non viene compilata perchè, mancando la richiesta esplicita di inizializzazione, il compilatore ci infila un bel "super()", ma "Padre()" è "private", non è accessibile, quindi non compila.
quindi è come se nel costruttore sotto ci fosse scritto super() sia che un costruttore possegga o non possegga argomenti ?
public class Padre {
String testo = "Nessuno";
private Padre() {
super();
testo = "Padre";
System.out.println("Eseguito costruttore Padre()");
}
}
[/QUOTE]
scusa ma mi sfugge il motivo di un tale meccanismo
ma sai che in questo 3D abbiamo ripassato, secondo me, la programmazione principale in java ? :)
Il motivo (un'enorme comodità) si coglie al volo scrivendo il codice di una bella finestra, cosa particolarmente complicata in Java:
public class Prova extends JFrame {
public Prova() {
//TA DAH!
}
}
Che succede in quel "Prova()"? Se non fosse garantito il percorso di inizializzazione della superclasse JFrame, a Prova mancherebbero tutte le operazioni compiute dal costruttore di JFrame, perchè i costruttori, come i metodi, vengono essere sovrascritti. In poche parole, Prova estenderebbe JFrame ma non si comporterebbe affatto come un JFrame, perchè il costruttore non fa un bel nulla.
Per quanto riguarda il ripasso, beh, non consiglierei al mio peggior nemico di iniziare a programmare in Java seguendo il percorso che abbiamo fatto, perchè quanto detto finora, benchè interessante (molto), mostra il lato più "comune" di Java, quello di essere un linguaggio di programmazione come altri, con le sue regole, le sue "particolarità" ecc...
C'è ben poco della forza reale di Java, che, per me, è la straordinaria produttività, la capacità di tradurre un'idea in un programma in un tempo che non è alla portata di altri linguaggi.
aspe, tanto per terminare il discorso senza lasciarmi dubbi alle spalle :D
quello che intendo io e che se con la classe Rettangolo sotto istanzio un bell'oggetto di tipo Rettangolo, questo viene costruito usando il costruttore della sua superclasse; nel caso sotto, ma anche in tutti gli altri casi, non essendovene esplicitamente specificata alcuna la sua superclasse di default è Object.
Bene, una istanza di Rettangolo viene costruita da Object e poi rinominata ed adattata per un Rettangolo.
Questo meccanismo serve per ereditare i metodi equals() e toString() di Object da parte di Rettangolo ?
A questo punto penso di si
public class Rettangolo {
double larghezza , altezza;
public Rettangolo(double l , double h){
larghezza = l;
altezza = h;
}
public int getArea(){
return larghezza *altezza;
}
}
chissà se sono stato chiaro :D
Se ti aiuta a comprendere la faccenda non vedo perchè non possa essere interpretata così. (Ma ogni oggetto viene costruito usando solo il contenuto del costruttore della classe di cui è istanza. Rettangolo ha un costruttore fatto così: Rettangolo() { super(); } non viene creato un Object che poi si trasforma in un Rettangolo)
Per una defiinzione più "tecnica" della procedura puoi guardare il capitolo 8, par. 8 e il 15 par. 9 di "The Java language spec. 2nd ed."
aspetta, a me è stato spiegata una cosa del genere:
public class Rettangolo {
double larghezza , altezza;
public Rettangolo(double l , double h){
qui è come se fosse scritto ---->super();
larghezza = l;
altezza = h;
}
public int getArea(){
return larghezza *altezza;
}
}
Whoops, non avevo neanche visto il costruttore della classe Rettangolo (cosa vuoi farci, con l'età i neuroni ogni tanto si prendono dele libertà).
...
qui è come se fosse scritto ---->super();
...
E' giusto.
Originariamente inviato da PGI
Whoops, non avevo neanche visto il costruttore della classe Rettangolo (cosa vuoi farci, con l'età i neuroni ogni tanto si prendono dele libertà).
...
qui è come se fosse scritto ---->super();
...
E' giusto.
oh, almeno ho capito giusto :D
di che classe sei ? :sofico:
'76 (dire che sono più giovane di quanto tu non pensassi, anche se falso, sarebbe di grande aiuto per il mio ego)
se serve a farti stare meglio sei più giovane di me, quindi :D
se le variabili (campi) ed i metodi statici fanno parte dell'intera classe, quanto istanzio un oggetto,mi viene da chiedermi se isyanzio anche una copia dei metodi non statici :confused:
Bisogna distinguere tra la creazione di un'istanza della classe (un oggetto) dal caricamento della classe, che è un passaggio logicamente e tecnicamente preventivo rispetto alla creazione dell'oggetto.
La JVM divide la memoria che ha disposizione in tanti "pezzi", ciascuno dei quali è deputato a contenere un tipo di elemento: c'è un'area di memoria per le classi, una per i metodi, per i campi ecc.
Ora, quando la macchina virtuale si trova di fronte ad un nuovo oggetto, prima di tutto verifica che il modello (la classe) sia presente in memoria.
Se manca la carica: questo caricamento comporta l'inizializzazione dei campi statici e, parallelamente, l'inizializzazione nell'area di memoria riservata ai metodi dei metodi statici.
Quando arriva "fisicamente" a creare l'oggetto, tutto quello che è static è già pronto e caricato. La distinzione opera solo in relazione al "quando" siano stati inizializzati i campi/metodi/blocchi static: nel caso di una classe che usata per la prima volta della macchina virtuale, è in quel momento che avviene l'inizializzazione. Successivamente al primo caricamento, la fase di inizializzazione della classe viene "saltata".
La creazione di un oggetto, per cui sia già stato caricato il modello di classe, comporta la sola inizializzazione dei campi/metodi/blocchi non statici: qui la cosa si ripete per ogni istanza della classe, perchè i campi/metodi/blocchi non statici "appartengono" al singolo oggetto.
Quindi sì, ogni volta che istanzi un oggetto crei una copia dei campi/metodi/blocchi non statici.
questa mi era sfuggita
ho le seguenti classi:
A
|---------
B |
| D
C
dove C estende B e B estende A
se scrivo:
B r = new C();
if( r instanceof Object)
System.out.println("vero");
else
System.out.println("falso");
la condizione è vera
if( r instanceof A)
System.out.println("vero");
else
System.out.println("falso");
è vera anche questa
morale, e magari mi sto anche ripetendo, l'ultima classe nella parte più bassa della gerarchia è istanceof di tutte le sue superclassi fino ad Object :muro:
mah, ci trovo un legame col super() di cui si discuteva, giorni fa
devo ammettere che è comodo se voglio contare quanti oggetti, che hanno qualcosa in comune, ho istanziato da qualche parte LinkedList, Array etc....
abbiamo le classi Cubo e Parallelepipedo, Cubo estende Parallelepipedo; in entrambe le classi è presente il metodo toString()
nella classe Cubo è presente il metodo getlato() definito nel modo seguente:
public double getlato(){
return "il lato del cubo vale:" + lato;
}
per comodità scrivo:
Object o = new Cubo(1);
System.out.println(o.toString());
e viene usato il metodo toString() del cubo
Se ora scrivo:
System.out.println(o.getlato());
non viene accettato dal compilatore in quanto tale metodo non è implementato in Object
ma io mi dico:
se è vero che (o) punta ad un oggetto di tipo Cubo, perchè riesce senza il "cast" ad usare il metodo toString() di cubo e non il metodo getlato() sempre di cubo?
dire semplicemente perchè getlato() non è implementato in Object non mi basta
:muro:
Originariamente inviato da misterx
dire semplicemente perchè getlato() non è implementato in Object non mi basta
però direi che è la risposta giusta
in fase di compilazione c'è un oggetto di tipo Object che non ha il metodo getlato.
viene utilizzato il toString della classe Cubo perché in fase dinamica è possibile scegliere quel metodo ma durante la compilazione non si può...
ah, quindi c'è differenza tra compilatore e JVM
è come dire che se riuscissi ad imbrogliare il compilatore riuscirei ad usare il metofo getlato() anche se implementato solo nella classe cubo ?
però se è così, compilatore e JVM vanno in direzioni diverse :cry:
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.