Toon posts:

[Java] Thread + NullpointerException

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik zit nu al uren te klooien met het volgende probleem..

Ik probeer een simpel veldje te maken met balletjes die bewegen en daarmee binnen het veld blijven. Door op de start knop te drukken komt er een bal bij, door op de stop knop te drukken moet hij stoppen. Ballen toevoegen werkt prima maar zodra ik op de knop 'stop' druk gaat het fout.

Ik krijg een foutmelding op regel 30:

code:
1
2
  if (event.getSource() == stop) {
      ball.stopBall();


Blijkbaar wijst ball dus nergens naar, hij wordt echter wel gemaakt als ik op de knop 'start' druk. Ik weet trouwens dat het niet erg netjes is geprogrammerd (Bij meerdere ballen raak je de mogelijkheid om ze te stoppen kwijt)

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package threads;

import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class Applet1 extends Applet implements ActionListener {
  
  private Ball ball;
  private Button start;
  private Button stop;

  public void init() {
    stop = new Button("Stop");
    add(stop);
    stop.addActionListener(this);
    start = new Button("Start");
    add(start);
    start.addActionListener(this);
  }

  public void actionPerformed(ActionEvent event) {
    if (event.getSource() == start) {
      Graphics g = getGraphics();
      Ball ball = new Ball(g);
      ball.start();
    }
    
    if (event.getSource() == stop) {
      ball.stopBall();
    }
  }


  class Ball
      extends Thread {
    private boolean keepGoing;
    private Graphics g;
    private int x = 7, xChange = 10;
    private int y = 0, yChange = 10;
    private int diameter = 10;
    private int rectLeftX = 0, rectRightX = 350;
    private int rectTopY = 0, rectBottomY = 300;

    public Ball(Graphics graphics) {
      g = graphics;
      keepGoing = true;
    }

    public void stopBall() {
      keepGoing = false;
    }

    public void run() {
      g.drawRect(rectLeftX, rectTopY, rectRightX - rectLeftX,
                 rectBottomY - rectTopY);

      while (keepGoing) {
        g.setColor(Color.white);
        g.fillOval(x, y, diameter, diameter);

        if (x + xChange <= rectLeftX)
          xChange = -xChange;
        if (x + xChange >= rectRightX)
          xChange = -xChange;

        if (y + yChange <= rectTopY)
          yChange = -yChange;
        if (y + yChange >= rectBottomY)
          yChange = -yChange;

        x = x + xChange;
        y = y + yChange;
        
        g.setColor(Color.red);
        g.fillOval(x, y, diameter, diameter);
        
        try {
          Thread.sleep(50);
        }

        catch (InterruptedException e) {
          System.err.println("Sleep exception");
        }
      }
    }
  }
}


Kan iemand me helpen?

  • momania
  • Registratie: Mei 2000
  • Laatst online: 21-05 06:42

momania

iPhone 30! Bam!

Java:
1
2
3
4
5
6
7
8
9
10
11
public void actionPerformed(ActionEvent event) {
    if (event.getSource() == start) {
      Graphics g = getGraphics();
      Ball ball = new Ball(g);
      ball.start();
    }
    
    if (event.getSource() == stop) {
      ball.stopBall();
    }
  }


De instantie van Ball wordt aangemaakt binnen je 'if'
Daarbuiten heb je dus geen referentie meer naar dat object en is je 2e 'if' bestaat ball dus gewoon niet ;)

[edit]
Het is trouwens netter om niet Thread te extenden, maar Runnable te implementeren bij je class Ball :)

[ Voor 12% gewijzigd door momania op 27-09-2004 23:37 ]

Neem je whisky mee, is het te weinig... *zucht*


  • JnX
  • Registratie: Februari 2001
  • Laatst online: 18-01 22:08

JnX

Moet je niet nog een object aanmaken van de klasse Ball voordat je de methode stopBall() daarvan aanroept. Dus toevoegen:

Graphics g = getGraphics();
Ball ball = new Ball(g);

  • grhmpf
  • Registratie: December 2000
  • Laatst online: 29-05-2022

grhmpf

Android <3

Je hebt niets aan die private variabele als je meer dan een balletjes hebt, zoals je zelf al zegt.
Alleen wijs je niets toe als je een nieuw Ball object maakt aan die private variabele. Dus die blijft null. Dus ball.stopBall() nullpointer exception.

En daar was je zo achter met een breakpoint en een goeie debugger.

--edit-- wel verdorie :)

[ Voor 5% gewijzigd door grhmpf op 27-09-2004 23:37 ]


Verwijderd

Topicstarter
Wow thnx voor de snelle replys :)

Okeej, Ball heeft geen referentie meer buiten de if... Hoe kan ik er dan voor zorgen dat dit wel zo is. Dat ik dus nog steeds met de knop start een nieuwe bal toe kan voegen en hem met stop kan stoppen? Ik kan

code:
1
2
Graphics g = getGraphics();
Ball ball = new Ball(g);


Wel toevoegen in de if waar stop true is maar dat schiet niet op want dan maakt ie gewoon een nieuwe aan over de oude heen.
Private heeft er toch niks mee temaken? Het gebeurd allemaal toch in dezelfde klasse?

Mja sorry, ik ben geloof ik nog een beetje zoekende :)

  • momania
  • Registratie: Mei 2000
  • Laatst online: 21-05 06:42

momania

iPhone 30! Bam!

Verwijderd schreef op 28 september 2004 @ 00:04:
Private heeft er toch niks mee temaken? Het gebeurd allemaal toch in dezelfde klasse?
Private heeft er alles mee te maken ;)

Even simpel gezegd: alles wat je tussen twee accolades { & } declareert, kan je daar buiten niet meer gebruiken door er naar te refereren.

Het simpelste dat je kan doen is een lijst bijhouden met alle gestarte threads.

Java:
1
2
3
4
5
public class Applet1 extends Applet implements ActionListener {
  
  private Ball ball;
  private Button start;
  private Button stop;

Die private Ball ball kan je daar wel weg halen, daar heb je niets aan aangezien je steeds een nieuwe wilt hebben en alle referenties nog wilt behouden.
Zou je 1 class variabele ball hebben, dan overschrijf je je referentie steeds ;)

Hier zou je dit van kunnen maken:
Java:
1
2
3
4
5
public class Applet1 extends Applet implements ActionListener {
  
  private List balls = new ArrayList();
  private Button start;
  private Button stop;


Dan kan je in je ActionPerformed zoiets doen:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void actionPerformed(ActionEvent event) {
    if (event.getSource() == start) {
      Graphics g = getGraphics();
      Ball ball = new Ball(g);
      ball.start();
      // voeg de ball aan je lijst toe
      balls.add(ball);
    }
    
    if (event.getSource() == stop) {
      if (balls.size() > 0) {  // <- dit staat wel schunnig btw :+
         // vraag uit de lijst de eerste Ball op en roep de stopBall() aan.
         Ball ball = (Balls)balls.getFirst();
         ball.stopBall();
      }
    }
  }


Dit is iig een stap die je verder kan maken.
Als je nou verder wilt met dit soort threading, dan kan ik je aanraden om van het hele Applet verhaal af te stappen en gewoon een paar console dingetjes gaan bouwen waar je met minder overhead aan code veel meer van kan leren ;)

Neem je whisky mee, is het te weinig... *zucht*


Verwijderd

Topicstarter
Thnx, ik ga wel me console programmatjes prutsen dan want hier word ik helemaal moedeloos van ;( :P

  • Standeman
  • Registratie: November 2000
  • Laatst online: 17:36

Standeman

Prutser 1e klasse

even een off-topic tip, download een goede java IDE met debugger (bijv de gratis ide's zoals netbeans en eclipse), dat kan een hoop van je problemen vaak vrij snel oplossen d.m.v. een dubugger :))

The ships hung in the sky in much the same way that bricks don’t.


  • Verbal Kint
  • Registratie: Januari 2001
  • Laatst online: 27-05-2025

Verbal Kint

The man with the plan

Sterker nog, een dergelijke fout (refereren aan een variabele buiten de scope) levert je onmiddelijk een error op in een IDE, zelfs zonder dat je runt of debugt. Daarnasst kan je je IDE nog veel meer dingen laten controleren. Sommige zijn gratis te downloaden (Eclipse) dus waarom zou je he niet gebruiken...

Great minds think alike!


Verwijderd

en eclipse is een erg goed programma. Downloaden dus! :P

Het is niet zo moelijk als het lijkt.
Door private List balls = new ArrayList(); boven in je klasse te zetten, is balls in heel je klasse bekend. Ook in je actionListener. Overal is het hetzelfde lijstje, want een object (balls) veranderd niet. (balls is in feite een pointer naar het stuk geheugen waar die arraylist in staat).

Objecten kunnen hooguit 'verggeten' worden. Dat gebeurt als datgene waarin ze zijn gemaakt klaar is. Zijn ze gemaakt in een methode, worden ze vergeten als de methode klaar is (meestal vrij snel), zijn ze gemaakt in een klasse, worden ze vergeten als de klasse klaar is (kan soms altijd living zijn).

je kunt nu dus overal in je klasse die 'balls' aanspreken (hij is immers op klasse-niveau aangemaakt en niet op methode-niveau). Een lijst met ballen.
Elke bal die start die zet je achter in de lijst (je wil die bal namelijk niet vergeten als je de functie verlaat en de ArrayList balls wordt niet vergeten (athans, niet zolang die klasse blijft bestaan)). Daar kun je dus mooi alles in proppen.

Wil je stoppen, wil je dat de oudste bal (die dus voor in de lijst staat) , opzout . Die moeten we dus eerst hebben:
Ball ball = (Balls)balls.getFirst(); ;
je pakt de eerste uit de lijst. Nu weet een ArrayList niet anders dan dat dat objecten zijn (kan er ook eens wat anders dan een bal in, kan ik ook nog eens een ArrayList gebruiken voor mijn programma's zonder ballen ). Dus je moet wel even aangeven waar je het over hebt... De regel moet eigenlijk zijn:
Ball ball = (Ball)balls.getFirst();
Balls is immers geen klasse.

Dan stop je die bal.
ball.stopBall();

en tot slot flikker je de bal uit je lijst (wat moet je immers met zo'n loze bal).
dat zal ongetwijfeld iets zijn zoals balls.removeFirst(); of balls.remove(0);
Dat is wel terug te vinden in de Arraylist-beschrijving op http://java.sun.com/j2se/1.4.2/docs/api/index.html

(kun je gelijk vinden dat je java.util.* moet importeren om die ArrayList-klasse te vinden)

[ Voor 4% gewijzigd door Verwijderd op 28-09-2004 22:35 ]


Verwijderd

NOCT,

Veel mensen geven hier goedbedoelde adviezen zonder echt te weten wat er aan de hand is. Of omdat ze niet snappen waarom je de fout krijgt beginnen ze met workarounds. Laat je hierdoor niet op het verkeerde pad sturen. Je moet er niet omheen werken maar het probleem begrijpen en oplossen.

Om de door jou gesubmitte code werkend te krijgen is er slechts een klein beetje kennis nodig. Ik probeer je uit te leggen wat er fout gaat.

De null-pointer die jij krijgt op regel 30 is logisch te verklaren als je weet waar welke variabelen geldig zijn. Deze geldigheid heet scope.
Je hebt GLOBAL en LOCAL vars. Een referentie naar een object werkt volgens dezelfde scope principes.
Een variabele is alleen geldig ( in scope) binnen de dichstbijzijnde accolades.


Hier is je code met alleen de noodzakelijke regels:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Applet1 extends Applet implements ActionListener {
  
  private Ball ball;
  
  public void init() {
  }

  public void actionPerformed(ActionEvent event) {
    if (event.getSource() == start) {
      Graphics g = getGraphics();
      Ball ball = new Ball(g);
      ball.start();
    }
    
    if (event.getSource() == stop) {
      ball.stopBall();
    }
  }


Op regel 11 heb jij staan "Ball ball = new Ball(g);" Deze 'ball is alleen geldig in de scope van het if statement tussen regel 9 en 13. Deze 'ball' is een LOCAL.

Op regel 3 defineer je een referentie van het type Ball. Deze is GLOBAL om dat je hem binnen de scope van de hele class definieert en dus overall aan te roepen.

Je hebt nu 2!!! referenties met de naam ball! Dat is allemaal volgens de regels, maar je moet dan wel opletten of de juiste reference gebruikt.

Aangezien de 'ball' van regel 11 na de accolade op regel 13 niet meer bestaat wordt op regel 16 de 'ball' die je op regel 3 creert gebruikt en daaraan heb je nergens met het " =new Ball()" statement een een object aangekoppeld. Dus vandaar de null pointer.

De oplossing is door regel 11 te veranderen in "ball = new Ball(g);"
Nu weet de compiler dat de op regel 3 gedefinieerde ball moet worden gebruikt.
Omdat je nu aan ball geen type defenitie geeft (Ball) wordt de definitie van regel 3 gebruikt.

[ Voor 10% gewijzigd door Verwijderd op 28-09-2004 23:55 ]


Verwijderd

Had niet gezien dat ball al een keer eerder was gedefinieerd. Verder kun je met jouw verhaal maar één ball stoppen en niet meerdere, aangezien je ze nergens bij houdt.

Maar je uitleg is misschien wel wat duidelijker geformuleerd en als beginner wat makkelijker om eerst uit te proberen :)

Verwijderd

Topicstarter
Wooow thnx! _/-\o_

Die scope was inderdaad het probleem,erg heldere uitleg ook.

Dat was ook gelijk de reden dat ik niks kon beginnen met die ArrayList, inmiddels werkt het allemaal :)

Nogmaals bedankt iedereen!
Pagina: 1