How do I create a generic method in Java?

A generic method uses a type parameter to work with different reference types. This lets you reuse the same method without writing a separate version for each type.

In this example, you will create a method that prints the elements of an array. The same method will handle arrays of strings and integers.

Source code

Save the following code as GenericMethodExample.java:

public class GenericMethodExample {
    void main() {
        String[] languages = {"Java", "Python", "JavaScript"};
        Integer[] numbers = {10, 20, 30};

        IO.println("Languages:");
        printElements(languages);

        IO.println();
        IO.println("Numbers:");
        printElements(numbers);
    }

    <T> void printElements(T[] elements) {
        for (T element : elements) {
            IO.println(element);
        }
    }
}

Explanation

The printElements() method declares a type parameter named T:

<T> void printElements(T[] elements) {
    for (T element : elements) {
        IO.println(element);
    }
}

The <T> declaration appears before the return type, void. It makes T available as a type within the method declaration and body.

Each part has a purpose:

Part Purpose
<T> Declares the type parameter
void Indicates that the method does not return a value
T[] elements Accepts an array whose element type is T
T element Holds each element during iteration

T is a conventional name. You can use a more descriptive name when it makes the code easier to understand.

Calling the method with different types

The first call passes an array of strings:

String[] languages = {"Java", "Python", "JavaScript"};
printElements(languages);

The compiler infers T as String for this call.

The second call passes an array of integers:

Integer[] numbers = {10, 20, 30};
printElements(numbers);

For this call, the compiler infers T as Integer.

You do not need to specify the type argument explicitly because the compiler can infer it from the array argument.

Using wrapper types

Java generic type arguments must be reference types. You cannot use a primitive type such as int as the type argument for T.

That is why this example uses Integer[] rather than int[]. Autoboxing converts the integer literals into Integer objects when the array is initialized.

Passing an int[] directly to this method would cause a compilation error.

Run the example

Compile and run the file:

javac GenericMethodExample.java
java GenericMethodExample

Output

Languages:
Java
Python
JavaScript

Numbers:
10
20
30

The type parameter belongs to the method, so the containing class does not need to be generic. Use a generic method when the same operation can work with multiple types while keeping compile-time type checking.

How do I create interactive fireworks using Java Swing?

You can create interactive fireworks in Java Swing by combining Graphics2D, mouse events, and a javax.swing.Timer. Rockets travel toward the position you click, then explode into colorful particles affected by gravity, damping, and fading.

The effect does not require an external graphics library, physics engine, or image assets.

In this SwingLoops example, fireworks launch into a dark night sky above a simple city skyline. Each explosion produces dozens of glowing particles with different colors and movement patterns. You can click several positions to create multiple explosions, and the program launches fireworks automatically when you leave it running.

Interactive Fireworks

Interactive Fireworks

Source code

Save the following code as InteractiveFireworks.java:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;

public class InteractiveFireworks extends JPanel {
    private static final int W = 540, H = 960;
    private final Random random = new Random();
    private final java.util.List<Rocket> rockets = new ArrayList<>();
    private final java.util.List<Particle> particles = new ArrayList<>();
    private final Timer timer = new Timer(16, e -> update());
    private long lastLaunch;

    public InteractiveFireworks() {
        setPreferredSize(new Dimension(W, H));
        setBackground(new Color(3, 5, 20));

        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                double x = e.getX() * W / (double) getWidth();
                double y = e.getY() * H / (double) getHeight();
                launch(x, y);
            }
        });
    }

    @Override
    public void addNotify() {
        super.addNotify();
        lastLaunch = System.currentTimeMillis();
        timer.start();
    }

    @Override
    public void removeNotify() {
        timer.stop();
        super.removeNotify();
    }

    private void launch(double tx, double ty) {
        double x = 40 + random.nextDouble() * (W - 80);
        double y = H;
        double dx = tx - x;
        double dy = ty - y;
        double distance = Math.max(1, Math.hypot(dx, dy));
        double speed = 9 + random.nextDouble() * 3;

        rockets.add(new Rocket(x, y,
                dx / distance * speed,
                dy / distance * speed,
                tx, ty, random.nextFloat()));
    }

    private void update() {
        long now = System.currentTimeMillis();

        if (now - lastLaunch >= 1700) {
            launch(80 + random.nextDouble() * (W - 160),
                    100 + random.nextDouble() * 350);
            lastLaunch = now;
        }

        for (Iterator<Rocket> it = rockets.iterator(); it.hasNext();) {
            Rocket r = it.next();
            double remaining = Point2D.distance(
                    r.x, r.y, r.tx, r.ty);

            if (remaining <= Math.hypot(r.vx, r.vy)) {
                r.x = r.tx;
                r.y = r.ty;
                explode(r);
                it.remove();
            } else {
                r.x += r.vx;
                r.y += r.vy;
            }
        }

        for (Iterator<Particle> it = particles.iterator();
             it.hasNext();) {
            Particle p = it.next();

            p.px = p.x;
            p.py = p.y;
            p.x += p.vx;
            p.y += p.vy;
            p.vx *= .988;
            p.vy = p.vy * .988 + .075;
            p.life -= .013f;

            if (p.life <= 0) it.remove();
        }

        repaint();
    }

    private void explode(Rocket r) {
        int count = 80 + random.nextInt(30);
        int pattern = random.nextInt(3);

        for (int i = 0; i < count; i++) {
            double angle = pattern == 2
                    ? random.nextDouble() * Math.PI * 2
                    : i * Math.PI * 2 / count;

            double speed = 2 + random.nextDouble() * 4;

            if (pattern == 1) {
                speed *= .5 + Math.abs(Math.sin(angle * 2));
            }

            particles.add(new Particle(r.x, r.y,
                    Math.cos(angle) * speed,
                    Math.sin(angle) * speed,
                    r.hue + random.nextFloat() * .08f));
        }
    }

    @Override
    protected void paintComponent(Graphics graphics) {
        super.paintComponent(graphics);
        Graphics2D g = (Graphics2D) graphics.create();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        g.scale(getWidth() / (double) W,
                getHeight() / (double) H);

        g.setPaint(new GradientPaint(0, 0,
                new Color(3, 5, 20),
                0, H, new Color(20, 9, 44)));
        g.fillRect(0, 0, W, H);

        drawCity(g);

        for (Rocket r : rockets) {
            g.setColor(Color.WHITE);
            g.fill(new Ellipse2D.Double(r.x - 3, r.y - 3, 6, 6));
            g.setColor(new Color(255, 190, 100, 100));
            g.setStroke(new BasicStroke(3));
            g.draw(new Line2D.Double(
                    r.x, r.y + 18, r.x, r.y));
        }

        for (Particle p : particles) {
            Color c = Color.getHSBColor(p.hue % 1f, .8f, 1f);
            int alpha = (int) (p.life * 230);

            g.setColor(new Color(c.getRed(),
                    c.getGreen(), c.getBlue(), alpha / 4));
            g.fill(new Ellipse2D.Double(
                    p.x - 8, p.y - 8, 16, 16));

            g.setColor(new Color(c.getRed(),
                    c.getGreen(), c.getBlue(), alpha));
            g.setStroke(new BasicStroke(2,
                    BasicStroke.CAP_ROUND,
                    BasicStroke.JOIN_ROUND));
            g.draw(new Line2D.Double(p.px, p.py, p.x, p.y));
            g.fill(new Ellipse2D.Double(
                    p.x - 2, p.y - 2, 4, 4));
        }

        g.dispose();
    }

    private void drawCity(Graphics2D g) {
        for (int i = 0; i < 18; i++) {
            int x = i * 32;
            int height = 45 + (i * 37) % 110;

            g.setColor(new Color(8, 12, 28));
            g.fillRect(x, H - height, 35, height);

            g.setColor(new Color(255, 205, 90, 100));
            for (int y = H - height + 12; y < H - 8; y += 18) {
                g.fillRect(x + 8, y, 4, 6);
                g.fillRect(x + 22, y, 4, 6);
            }
        }
    }

    private static class Rocket {
        double x, y, vx, vy, tx, ty;
        float hue;

        Rocket(double x, double y, double vx, double vy,
               double tx, double ty, float hue) {
            this.x = x;
            this.y = y;
            this.vx = vx;
            this.vy = vy;
            this.tx = tx;
            this.ty = ty;
            this.hue = hue;
        }
    }

    private static class Particle {
        double x, y, px, py, vx, vy;
        float hue, life = 1f;

        Particle(double x, double y,
                 double vx, double vy, float hue) {
            this.x = this.px = x;
            this.y = this.py = y;
            this.vx = vx;
            this.vy = vy;
            this.hue = hue;
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("SwingLoops - Fireworks");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setContentPane(new InteractiveFireworks());
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }
}

Run the example

You need Java 17 or later and a desktop environment capable of running Swing applications.

Open a terminal in the directory containing InteractiveFireworks.java, then compile and run it:

javac InteractiveFireworks.java
java InteractiveFireworks

A window with a preferred drawing area of 540 × 960 pixels opens. Fireworks launch automatically every 1.7 seconds.

Click anywhere in the window to launch a rocket toward that position. Clicking several locations creates multiple simultaneous explosions.

You can change the preferred panel size if the window is too large for your screen.

How does the animation work?

Updating the animation with a Swing Timer

The application uses a javax.swing.Timer to update the animation approximately every 16 milliseconds:

private final Timer timer = new Timer(16, e -> update());

Each update moves the rockets, updates the particles, removes expired objects, and requests a repaint.

The timer starts in addNotify() and stops in removeNotify(), preventing the animation from continuing after the panel is removed.

Because the timer executes its callback on Swing’s Event Dispatch Thread, mouse events, animation updates, and painting share the same thread.

Moving rockets toward the target

Each rocket starts near the bottom of the screen and receives a velocity pointing toward its target.

The direction is calculated from the difference between the target and starting positions:

double dx = tx - x;
double dy = ty - y;
double distance = Math.max(1, Math.hypot(dx, dy));

Math.hypot() calculates the Euclidean distance between two points.

We divide the direction components by this distance and multiply them by the desired speed:

double vx = dx / distance * speed;
double vy = dy / distance * speed;

This ensures that the rocket moves at a consistent speed regardless of its destination.

During each animation update, its position changes according to its velocity:

r.x += r.vx;
r.y += r.vy;

Once the rocket is close enough to reach its target during the next movement, the program triggers an explosion.

Creating particles with circular motion

An explosion produces between 80 and 109 particles.

For a circular burst, each particle receives a different angle:

double angle = i * Math.PI * 2 / count;

The horizontal and vertical velocity components are calculated using:

double vx = Math.cos(angle) * speed;
double vy = Math.sin(angle) * speed;

Together, these calculations distribute particles around the explosion center.

The example also introduces variations in particle speed and direction, producing different explosion shapes.

Adding gravity and damping

After an explosion, particles move independently.

Each frame applies damping and gravity:

p.vx *= .988;
p.vy = p.vy * .988 + .075;

Multiplying velocity by .988 gradually slows the particles.

The additional .075 accelerates them downward because Java2D’s Y coordinate increases toward the bottom of the screen.

The result is a curved trajectory: particles spread outward, slow down, and eventually fall.

This is a simple visual physics simulation rather than a fully realistic model, but it works well for a fireworks animation.

Creating fading particles

Each particle begins with a lifetime of 1.0:

float life = 1f;

Its lifetime decreases with every update:

p.life -= .013f;

The remaining lifetime determines the particle’s transparency:

int alpha = (int) (p.life * 230);

As life approaches zero, the particle becomes less visible.

Expired particles are removed using the iterator, which also prevents the particle collection from growing indefinitely.

Creating a glow effect

The glow is produced by drawing each particle twice.

First, a larger circle is drawn with low opacity:

g.setColor(new Color(
        c.getRed(), c.getGreen(), c.getBlue(), alpha / 4));

g.fill(new Ellipse2D.Double(
        p.x - 8, p.y - 8, 16, 16));

A small, brighter particle is then drawn on top.

The application also draws a short line between each particle’s previous and current positions, creating a glowing motion trail.

This approximates a neon effect using ordinary Java2D shapes, without shaders or blur filters.

Generating firework colors

The example uses Color.getHSBColor() to generate colors:

Color c = Color.getHSBColor(p.hue % 1f, .8f, 1f);

Each rocket receives a random hue, and its particles use nearby hue values.

This gives every explosion a recognizable color theme while still allowing subtle variation between individual particles.

Drawing the night sky

The background uses a GradientPaint that transitions from dark navy to deep purple.

A simple city skyline is drawn using rectangles, with smaller yellow rectangles representing illuminated windows.

These elements provide context for the fireworks without requiring image assets.

Customize the animation

Try changing one parameter at a time to see how it affects the result.

Parameter Effect
16 in the timer Animation update interval in milliseconds
1700 Time between automatic launches
80 + random.nextInt(30) Number of particles per explosion
9 + random.nextDouble() * 3 Rocket speed
.988 Particle damping; smaller values slow particles faster
.075 Gravity; larger values make particles fall faster
.013f Lifetime reduction; larger values make particles disappear sooner
new Color(3, 5, 20) Background color

For example, increasing the particle count to 150 makes each firework look fuller, but also increases the amount of rendering work.

Reducing gravity creates a floating, almost magical explosion, while increasing gravity makes particles fall more quickly.

Experimenting with these values is an easy way to create your own animation style.

The complete example uses only Java Swing and Java2D. No external libraries, physics engines, or image assets are required.

Happy coding!

How do I create a custom exception in Java?

You can create a custom exception to describe a problem specific to your application. Give it a meaningful name so calling code can recognize and handle the problem.

In this example, you will create an InvalidAgeException and throw it when someone under 18 tries to register.

Source code

Save the following code as CustomExceptionExample.java:

public class CustomExceptionExample {
    void main() {
        try {
            register(16);
        } catch (InvalidAgeException exception) {
            IO.println("Registration failed: " + exception.getMessage());
        }

        IO.println("The program continues.");
    }

    void register(int age) throws InvalidAgeException {
        if (age < 18) {
            throw new InvalidAgeException(
                    "You must be at least 18 years old.");
        }

        IO.println("Registration successful.");
    }
}

class InvalidAgeException extends Exception {
    InvalidAgeException(String message) {
        super(message);
    }
}

Explanation

The custom exception extends Exception:

class InvalidAgeException extends Exception {
    InvalidAgeException(String message) {
        super(message);
    }
}

This makes InvalidAgeException a checked exception. Code that calls a method declaring this exception must either catch it or declare that it can propagate it.

The constructor accepts a message and passes it to the superclass constructor:

super(message);

Calling code can retrieve that message using getMessage().

Declaring the exception

The register() method declares the exception using throws:

void register(int age) throws InvalidAgeException {

This declaration tells callers that the method may throw an InvalidAgeException.

Throwing the exception

The method checks whether the age meets the registration requirement:

if (age < 18) {
    throw new InvalidAgeException(
            "You must be at least 18 years old.");
}

The throw statement creates and throws the exception. Execution leaves the method immediately, so the success message is skipped.

Notice the difference between the two keywords:

Keyword Purpose
throw Throws a specific exception object
throws Declares exceptions that a method may propagate

Handling the exception

The calling code catches the custom exception:

try {
    register(16);
} catch (InvalidAgeException exception) {
    IO.println("Registration failed: " + exception.getMessage());
}

Since 16 is below the minimum age, the catch block prints the failure message.

Once the exception is handled, the program continues after the try-catch statement.

Run the example

Compile and run the file:

javac CustomExceptionExample.java
java CustomExceptionExample

Output

Registration failed: You must be at least 18 years old.
The program continues.

If you change the method call to register(20), the output becomes:

Registration successful.
The program continues.

Choosing an exception type

Extend Exception when you want callers to catch or declare the exception. Extend RuntimeException when you want an unchecked exception, which callers are not required to catch or declare.

Use a custom exception when its name gives callers useful information or lets them handle a specific failure separately. For a simple invalid argument, a standard exception such as IllegalArgumentException may be sufficient.

How do I implement the Factory Method pattern in Java?

The Factory Method pattern is a Gang of Four (GoF) creational design pattern. It defines an operation for creating a product while allowing subclasses to choose the concrete product returned. This lets a base class reuse a workflow without hard-coding the product implementation into that workflow.

This article continues our creational patterns series after Singleton. Here, we focus on choosing an implementation through subclassing.

The problem

Suppose an application sends notifications by email and SMS. Both channels support the same operation, send(), but each has its own implementation.

We want a notification service to follow a shared workflow: create a notification, then send the message. The shared workflow should work with either channel. Factory Method gives subclasses a creation step to customize.

A complete example

Save the following code as FactoryMethodExample.java. The classes are nested so that the entire example fits into one source file. In a larger application, you can place them in separate files.

package org.kodejava.pattern.factorymethod;

public class FactoryMethodExample {
    interface Notification {
        void send(String message);
    }

    static class EmailNotification implements Notification {
        @Override
        public void send(String message) {
            System.out.println("Email: " + message);
        }
    }

    static class SmsNotification implements Notification {
        @Override
        public void send(String message) {
            System.out.println("SMS: " + message);
        }
    }

    abstract static class NotificationService {
        protected abstract Notification createNotification();

        public void notifyUser(String message) {
            Notification notification = createNotification();
            notification.send(message);
        }
    }

    static class EmailNotificationService extends NotificationService {
        @Override
        protected Notification createNotification() {
            return new EmailNotification();
        }
    }

    static class SmsNotificationService extends NotificationService {
        @Override
        protected Notification createNotification() {
            return new SmsNotification();
        }
    }

    public static void main(String[] args) {
        NotificationService email = new EmailNotificationService();
        email.notifyUser("Your order has been shipped.");

        NotificationService sms = new SmsNotificationService();
        sms.notifyUser("Your verification code is 482731.");
    }
}

The channel implementations print messages to the console to keep the example self-contained. They do not contact an email provider or SMS gateway.

Compile and run from the directory containing the source file:

javac -d out FactoryMethodExample.java
java -cp out org.kodejava.pattern.factorymethod.FactoryMethodExample

Output:

Email: Your order has been shipped.
SMS: Your verification code is 482731.

How it works

The example has four roles:

GoF role Example Responsibility
Product Notification Declares the operation shared by all notifications.
Concrete products EmailNotification, SmsNotification Implement the channel behavior.
Creator NotificationService Declares the factory method and uses its result in a shared workflow.
Concrete creators EmailNotificationService, SmsNotificationService Override the factory method to choose a concrete product.

The factory method is createNotification(). The inherited notifyUser() method calls it and uses the returned Notification without needing to know its concrete class.

Java supports this structure through abstract methods and method overriding: each concrete service supplies the creation behavior declared by its abstract superclass. See Java’s documentation on abstract methods and classes.

The client still selects a concrete service in main(). The pattern moves product creation behind an overridable method; it does not eliminate the need to choose which service the application uses.

Adding another channel

To support push notifications, implement Notification in a new PushNotification class. Then extend NotificationService and override createNotification() to return that product. The existing notifyUser() workflow can remain unchanged.

This example creates a new product for each call. Factory Method itself does not require a new instance every time; an implementation can return a reusable product if its lifecycle and thread-safety requirements allow it.

Factory Method versus a simple factory

A utility such as NotificationFactory.create("email"), with a conditional selecting a concrete class, is commonly called a simple factory. That can be useful, but it does not demonstrate the subclass-based creation mechanism shown here.

In this example, the defining relationship is that the creator’s shared workflow invokes a factory method overridden by its subclasses. Merely naming a method create() does not establish that structure.

When should I use it?

Use Factory Method when a base class has useful shared behavior, but subclasses need to customize the products that behavior uses. It is especially suitable for extension points in frameworks or reusable libraries.

The tradeoff is additional classes and inheritance. If all you need is a small conditional that constructs an object, a simple factory may be sufficient. If callers can supply an already-created Notification, constructor injection may provide a simpler way to customize the behavior.

Next in this series: Abstract Factory, which focuses on creating families of related products.

How do I handle an exception using try-catch in Java?

You can use a try-catch statement to handle an exception and provide a useful response when an operation fails.

In this example, you will try to convert text into an integer. If the text is not a valid integer, the program will display a message and continue running.

Source code

Save the following code as TryCatchExample.java:

public class TryCatchExample {
    void main() {
        String input = "twenty";

        try {
            int number = Integer.parseInt(input);
            IO.println("Parsed number: " + number);
        } catch (NumberFormatException exception) {
            IO.println("Cannot convert \"" + input + "\" to an integer.");
            IO.println("Please enter a whole number, such as 20.");
        }

        IO.println("The program continues.");
    }
}

Explanation

The variable input contains the text to convert:

String input = "twenty";

The try block contains the operation that may throw an exception:

try {
    int number = Integer.parseInt(input);
    IO.println("Parsed number: " + number);
}

Integer.parseInt() converts a valid integer string, such as "20", into an int.

If the text cannot be parsed as an int, the method throws a NumberFormatException. This also happens when the number is outside the range supported by int.

Catching the exception

The catch block handles NumberFormatException:

catch (NumberFormatException exception) {
    IO.println("Cannot convert \"" + input + "\" to an integer.");
    IO.println("Please enter a whole number, such as 20.");
}

Since "twenty" is not a valid integer string, execution moves from Integer.parseInt() to this block.

The remaining statement inside the try block is skipped, so "Parsed number: ..." is not printed.

The parameter exception refers to the exception object. This example provides its own message rather than displaying the exception’s technical details.

Continuing after the catch block

After the exception is handled, execution continues with the statement following the try-catch:

IO.println("The program continues.");

Catching an exception does not retry the failed operation automatically.

Run the example

Compile and run the file:

javac TryCatchExample.java
java TryCatchExample

Output

Cannot convert "twenty" to an integer.
Please enter a whole number, such as 20.
The program continues.

If you change input to "20":

String input = "20";

The conversion succeeds, the catch block is skipped, and the output becomes:

Parsed number: 20
The program continues.

Catch the specific exception you can handle. Here, NumberFormatException clearly identifies the expected conversion failure.