PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : JAVA - 2 Verständnisfragen


Nasenbaer
2008-05-17, 12:02:55
Frage 1:

Da ich bisher eher C++ programmiert hatte habe ich es mir dort angewöhnt parameter, die in einer Methode nur gelesen werden, per const zu übergeben. Z.B. so


void Rechne(const int x)
{
return x*x;
}


Unter Java hätte ich das jetzt so gemacht:

void Rechne(final int x)
{
return x*x;
}


Meine Frage ist ob das die gleiche Bedeutung hat und vielmehr ob das von der Performance überhaupt etwas bringt so wie obiger Code angeblich bei C++ kleine Optimierungen zulassen soll.


Frage 2:
Java soll ja wohl ausschließlich Call-By-Reference nutzen. Nun habe ich eine Klasse Kugel die bspw. ihren Mittelpunkt als 3D-Vektor zugewiesen bekommen soll. Z.B. sowas:


Vector3d origin = new Vector3d(1,1,0);
double radius = 2;
Kugel myKugel = new Kugel(origin, radius);


Wenn ich nach diesem Code noch diese Zeile einfügen würde:

origin.x = 5;

dann würde dass doch auch Auswirkungen auf das Kugel-Objekt haben da darin ja nur eine Referenz auf origin gespeichert wurde, oder?
Also muss ich dann bei solchen Sachen generell im Constructor/Setter die Parameter mit clone() in den Objekten speichern, damit sie von außen nicht veränderbar sind?

Shink
2008-05-17, 12:41:48
Frage 1:

Da ich bisher eher C++ programmiert hatte habe ich es mir dort angewöhnt parameter, die in einer Methode nur gelesen werden, per const zu übergeben. Z.B. so


void Rechne(const int x)
{
return x*x;
}


Unter Java hätte ich das jetzt so gemacht:

void Rechne(final int x)
{
return x*x;
}


Meine Frage ist ob das die gleiche Bedeutung hat und vielmehr ob das von der Performance überhaupt etwas bringt so wie obiger Code angeblich bei C++ kleine Optimierungen zulassen soll.
Hat prinzipiell die selbe Bedeutung. Ob es etwas bringt in Bezug auf Performance kommt auf die JRE darauf an: Auf einem Embedded Java-Only System wie dem DALSEMI TINI bringt das ordentlich Performance.
In der PC/Server/etc. Welt kann man solche Optimierungsversuche getrost vergessen. Wenn du das da verwenden willst dann nur um auszudrücken dass niemand diesen Wert ändern darf (oder wenn du im weiteren Verlauf einen Final-Wert brauchst; z.B. bei einer impliziten Klassendefinition).



Frage 2:
Java soll ja wohl ausschließlich Call-By-Reference nutzen. Nun habe ich eine Klasse Kugel die bspw. ihren Mittelpunkt als 3D-Vektor zugewiesen bekommen soll. Z.B. sowas:


Vector3d origin = new Vector3d(1,1,0);
double radius = 2;
Kugel myKugel = new Kugel(origin, radius);


Wenn ich nach diesem Code noch diese Zeile einfügen würde:

origin.x = 5;

dann würde dass doch auch Auswirkungen auf das Kugel-Objekt haben da darin ja nur eine Referenz auf origin gespeichert wurde, oder?
Ja, natürlich. Vielleicht willst du das ja sogar (z.B. zwei Objekte mit dem selben Ursprung).

Also muss ich dann bei solchen Sachen generell im Constructor/Setter die Parameter mit clone() in den Objekten speichern, damit sie von außen nicht veränderbar sind?
Ja, wenn du das so willst. Normalerweise lässt man den Zugriff auf z.B. origin.x nur per Getter und Setter zu, so hat man die Kontrolle darüber ob das jemand verändern darf oder nicht: Wenns nur ein origin.getX() gibt und kein origin.setX(int x), dann wird niemand auf die Idee kommen das so zu setzen.

darph
2008-05-17, 12:42:46
Zu 2:

Schau dir dieses superbillige Beispiel mal an:

package asdf;

public class Point {

private int x;
private int y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

public int getX() {
return x;
}

public int getY() {
return y;
}

public void setX(int x) {
this.x = x;
}
}

package asdf;

public class Line {

private Point start;
private Point end;


public Line(Point start, Point end) {
this.start = start;
this.end = end;
}

public String toString() {
return "from (" + start.getX() + ";" + start.getY()
+ ") to (" + end.getX() + ";" + end.getY() + ")";
}
}


package asdf;

public class Testing {

public static void main(String[] args) {
Point start = new Point(2,5);
Point end = new Point(4,9);

Line l = new Line(start, end);
System.out.println(l.toString());

start.setX(245);
System.out.println(l.toString());
}
}



Ausgabe: from (2;5) to (4;9)
from (245;5) to (4;9)

Wie du siehst: Ja, du kannst durch Call-by-Reference-Value (java macht da was eigenes) ein Objekt im Nachhinein ändern. In diesem konkreten Fall würde es genügen, wenn du einfach die Methode "setX(x)" wegläßt, weil die Membervariablen allesamt private sind und somit von außen her nicht mehr veränderbar sind.

Es ist natürlich persönliche Geschmackssache, aber ich halte public Membervariablen für schlechten Stil. Wenn ich etwas explizit später setzen will, dann schreibe ich eine Setter-Methode. Ansonsten eben nicht. ;)

Edith sagt: zu langsam. ;(

Nasenbaer
2008-05-17, 13:05:06
Zu Frage 1 ok das wäre geklärt. Danke :)

Frage 2:

Da habt ihr wohl was falsch verstanden. Mal ausführlicher:


class Kugel
{
private Vector3d m_Origin;
private double m_Radius;

Kugel(Vec3d origin, double radius)
{
m_Origin = origin;
m_Radius = radius;
}
}


Wenn ich nun das hier mache:

Vector3d origin = new Vector3d(1,1,0);
double radius = 2;
Kugel myKugel = new Kugel(origin, radius);

und danach irgendwas mit origin anstelle, dann soll das keine Auswirkung auf das in myKugel gespeicherte m_Origin haben.

Um das zu erreichen müsste ich doch dann den Constructor von Kugel so abändern?


Kugel(Vec3d origin, double radius)
{
m_Origin = origin.clone();
m_Radius = radius;
}


Und integrale Datentypen werden doch weiter per Call-By-Value übergeben oder?

Monger
2008-05-17, 13:26:16
Meine Frage ist ob das die gleiche Bedeutung hat und vielmehr ob das von der Performance überhaupt etwas bringt so wie obiger Code angeblich bei C++ kleine Optimierungen zulassen soll.

Es bringt tatsächlich ein kleines bißchen an Performance. Wichtiger ist aber, dass man so viele Fehler gleich im vorneherein ausschließen kann.
Das "final" Schlüsselwort ist eine feine Sache. Gerade in if...then...else Bäumen stellt man so sicher, dass die Variable auf allen Pfaden genau eine Zuweisung bekommt, und nicht mehr. Bei lokalen Variablen kann man "final" ruhig so häufig verwenden wie möglich.



Java soll ja wohl ausschließlich Call-By-Reference nutzen.

Ist zwar Klugscheißerei, aber: Java nutzt ausschließlich Call-By-Value. Das heißt: jede Objektreferenz wird beim Aufruf kopiert.

Das ist nicht das selbe wie eine Referenz in C++!

Beispiel:

public void doSth(Point p){
p = new Point(1,2);
}

In C++ würde jetzt nach dem Aufruf dieser Methode an p ein neues Objekt hängen. Hier nicht! Diese Methode ist komplett sinnfrei: sie erzeugt ein Objekt, und schmeißt es im nächsten Moment sofort wieder weg.


Also muss ich dann bei solchen Sachen generell im Constructor/Setter die Parameter mit clone() in den Objekten speichern, damit sie von außen nicht veränderbar sind?
clone() ist so eine Sache. Auch Sun selbst empfiehlt, Copy-Konstruktoren statt clone() zu nutzen. Was nämlich eine Objektkopie genau ist, ist arg interpretationsfähig, und da jedes Objekt eine clone() Methode hat (ohne aber zu dessen korrekter Implementierung gezwungen zu sein), hat man nie eine Garantie ob ein Objekt genau das macht was man eigentlich erwartet.

Aber um auf dein Beispiel zurückzukommen: die Methode die ein Objekt erzeugt, hat natürlich immer die Freiheit, damit völligen Blödsinn anzustellen. Solange die aufrufende Methode den Scope aller Hilfsvariablen möglich eng hält, ist die Chance sehr gering dass du "aus Versehen" an eine Objektkomponente rankommst die dich nichts angeht.

Der zweite Aspekt ist viel wichtiger: nämlich, dass man von den Attributen einer Klasse so wenig sichtbar macht wie möglich, und seine Getter und Setter nur sehr gezielt einsetzt.

Hardwaretoaster
2008-05-17, 13:29:04
Zu Frage 1 ok das wäre geklärt. Danke :)

Frage 2:

Da habt ihr wohl was falsch verstanden. Mal ausführlicher:


class Kugel
{
private Vector3d m_Origin;
private double m_Radius;

Kugel(Vec3d origin, double radius)
{
m_Origin = origin;
m_Radius = radius;
}
}


Wenn ich nun das hier mache:

Vector3d origin = new Vector3d(1,1,0);
double radius = 2;
Kugel myKugel = new Kugel(origin, radius);

und danach irgendwas mit origin anstelle, dann soll das keine Auswirkung auf das in myKugel gespeicherte m_Origin haben.

Um das zu erreichen müsste ich doch dann den Constructor von Kugel so abändern?


Kugel(Vec3d origin, double radius)
{
m_Origin = origin.clone();
m_Radius = radius;
}


Und integrale Datentypen werden doch weiter per Call-By-Value übergeben oder?

So würde ich es auch impertretieren, bina ber auch noch am Java lernen.

BTW: was machst du mit den Kugeln und Co.? (ich muss zur Übung nämlich gerade eine Mini-Membransim. basteln; ob hab' von der grafischen Ausgabe keinen Plan *g*).

darph
2008-05-17, 13:59:17
hier stand unsinn.

Nasenbaer
2008-05-17, 14:18:28
@Monger
Danke für die Infos. Allerdings bin ich mir bei meinem konkreten Problem immer noch nicht ganz sicher wie ich nun vorgehen soll.

Ich will ja, dass man den Kugel-Ursprung nur über Setter manipulieren kann. Wenn ich dann aber den übergebenen Vektor nicht kopieren, dann is er ja weiterhin von außen manipulierbar - nun dass nun 2 Variablen den gleichen Speicher referenzieren.

Wäre es dann besser den Setter zu schreiben:


public setRadius(double x, double y, double z)
{
m_Origin.x = x;
// ...
}


Oder wie wäre es nun clever hier vorzugehen?

Nasenbaer
2008-05-17, 14:55:45
Achso

@Hardwaretoaster
Ich bastel gerade nen RayTracer um die dafür benötigten Algorithmen besser zu verstehen.

Hardwaretoaster
2008-05-17, 15:10:58
Oh, viel Spaß dabei, ich muss mich erstmal mit etwas weniger anspruchvollem auseinandersetzen.
Naja, ich hoffe ja, dass ich meine Probleme /leichte Abneigungen gegen Programmierung noch ablegen kann.
Auch wenn ich die anderen Fächer ganz gut meistere, irgndwie wäre ich sonst mit einem Informationstechnikstudium falsch dran...

Tiamat
2008-05-17, 15:26:11
package asdf;

public class Line {

private Point start;
private Point end;


public Line(Point start, Point end) {
this.start = start;
this.end = end;
}

public String toString() {
return "from (" + start.getX() + ";" + start.getY()
+ ") to (" + end.getX() + ";" + end.getY() + ")";
}
}


Womit hat es eigentlich mit dem this. Operator auf sich ?
Seh ich das richtig, dass ich damit einer außerhalb der Methode befindenden Variable den Wert des Parameters zuordne, da dieser ja innerhalb der Methode einen eigenen Namensraum hat ?

instinct
2008-05-17, 15:41:39
mit this sprichst du immer die variablen der klasse an. Somit ist es in deinem Fall möglich, dass dein Paramter den gleichen Namen trägt wie die oben deklariert Variable.
Würdest du 'start = start' schreiben, würde lediglich dein Parameter mit dem selben Wert beschrieben werden und deine Variable start wäre immer noch null.

darph
2008-05-17, 15:48:45
package asdf;

public class Line {

private Point start;
private Point end;


public Line(Point start, Point end) {
this.start = start;
this.end = end;
}

public String toString() {
return "from (" + start.getX() + ";" + start.getY()
+ ") to (" + end.getX() + ";" + end.getY() + ")";
}
}


Womit hat es eigentlich mit dem this. Operator auf sich ?
Seh ich das richtig, dass ich damit einer außerhalb der Methode befindenden Variable den Wert des Parameters zuordne, da dieser ja innerhalb der Methode einen eigenen Namensraum hat ?


ähh. ja. Wobei sich "außerhalb" nicht auf lokale Variablen in anderen Methoden der gleiche Klasse bezieht.

1 public Class Fasel{
2
3 private String foo; // bekommt den Wert "barb"
4
5 public Fasel(String f) {
6 String foo = f + "a"; // "bara"
7 this.foo = f + "b"; // bekommt den Wert "barb"
8 }
9
10 public String getFoo() {
11 return foo; // "barb"
12 }
13 }


Aufruf dann mit Fasel fasel = new Fasel("bar");

das in Zeile 6 definierte foo ist nur sichtbar und existent INNERHALB der geschweiften Klammern, also in diesem Fall nur innerhalb der Methode (hier: dem Kontruktor). Membervariablen gehören dem Objekt (this, Zeile 7) und du kannst so damit darauf zugreifen. Das ist nötig, wenn (wie hier) eine lokale Variable den gleichen Namen hat (Zeile 6).

Der Zugriff auf die Variable foo in Zeile 11 bezieht sich auf die Membervariable, nicht auf die im Konstruktor definierte lokale Variable, da die zu diesem Zeitpunkt nicht mehr existiert. In der Methode getFoo wäre es in diesem Beispiel egal, ob du "this.foo" oder "foo" schreibst, weil es eindeutig ist.

So, wie ich es im Konstruktor gemacht habe, ist es aber äußerst unschön, weil es schnell zu Verwirrung führen kann. Ich nutze sowas nur, wenn ich im Konstruktor Membervariablen setze. Ansonsten nimmt man sprechende Bezeichner.

Monger
2008-05-17, 16:54:22
Womit hat es eigentlich mit dem this. Operator auf sich ?


Wie schon bereits erwähnt, bezieht sich "this" immer auf genau dieses Objekt - somit sind alle Attributzugriffe immer implizit auf "this". Das wird dir noch deutlich öfter über den Weg laufen, wenn du mit inneren Klassen arbeitest.
Neben "this" gibt es auch noch "super" - was vorallem in Konstruktoren öfters vorkommt.

The_Invisible
2008-05-17, 17:29:08
Aufruf dann mit Fasel fasel = new Fasel("bar");[/CODE]

das in Zeile 6 definierte foo ist nur sichtbar und existent INNERHALB der geschweiften Klammern, also in diesem Fall nur innerhalb der Methode (hier: dem Kontruktor). Membervariablen gehören dem Objekt (this, Zeile 7) und du kannst so damit darauf zugreifen. Das ist nötig, wenn (wie hier) eine lokale Variable den gleichen Namen hat (Zeile 6).

Der Zugriff auf die Variable foo in Zeile 11 bezieht sich auf die Membervariable, nicht auf die im Konstruktor definierte lokale Variable, da die zu diesem Zeitpunkt nicht mehr existiert. In der Methode getFoo wäre es in diesem Beispiel egal, ob du "this.foo" oder "foo" schreibst, weil es eindeutig ist.

So, wie ich es im Konstruktor gemacht habe, ist es aber äußerst unschön, weil es schnell zu Verwirrung führen kann. Ich nutze sowas nur, wenn ich im Konstruktor Membervariablen setze. Ansonsten nimmt man sprechende Bezeichner.

ja, im gesamten projekt am besten nur eine variante bevorzugen, bei größeren projekten kennt sich sonst keiner mehr aus, inklusive sich selbst nach einiger zeit :D

mfg

Monger
2008-05-17, 17:36:23
@Monger
Danke für die Infos. Allerdings bin ich mir bei meinem konkreten Problem immer noch nicht ganz sicher wie ich nun vorgehen soll.

Ich will ja, dass man den Kugel-Ursprung nur über Setter manipulieren kann.

Sobald deine Ursprungsvariable für den Point aus dem Scope rausläuft, hat keiner mehr die Chance, an dem Objekt irgendwas zu ändern. Halt einfach die Sichtbarkeit von diesem Point so gering wie möglich, dann verhinderst du Fehlkonstruktionen.

Jedesmal bis runter auf die Elementartypen aufzubrechen und zu kopieren wäre inperformant, ziemlich klobig - und auch ein bißchen arg paranoid! ;)