Here's a simple Java implementation of a neural network with backpropagation for training. The network has configurable layers and activation functions.

```java
import java.util.Random;
import java.util.function.Function;

public class NeuralNetwork {
    private final double[][][] weights;
    private final Function<Double, Double> activationFunction;
    private final Function<Double, Double> activationDerivative;

    public NeuralNetwork(int[] layerSizes, Function<Double, Double> activationFunction,
                         Function<Double, Double> activationDerivative) {
        this.activationFunction = activationFunction;
        this.activationDerivative = activationDerivative;

        weights = new double[layerSizes.length - 1][][];
        Random random = new Random();
        for (int i = 0; i < weights.length; i++) {
            int inputSize = layerSizes[i] + 1;
            int outputSize = layerSizes[i + 1];
            weights[i] = new double[inputSize][outputSize];
            for (int j = 0; j < inputSize; j++) {
                for (int k = 0; k < outputSize; k++) {
                    weights[i][j][k] = random.nextDouble() * 2 - 1;
                }
            }
        }
    }

    public double[] predict(double[] input) {
        double[] activations = input;
        for (double[][] layerWeights : weights) {
            activations = activate(activations, layerWeights);
        }
        return activations;
    }

    private double[] activate(double[] input, double[][] layerWeights) {
        double[] extendedInput = new double[input.length + 1];
        System.arraycopy(input, 0, extendedInput, 0, input.length);
        extendedInput[input.length] = 1; // bias

        double[] output = new double[layerWeights[0].length];
        for (int i = 0; i < output.length; i++) {
            double sum = 0;
            for (int j = 0; j < extendedInput.length; j++) {
                sum += extendedInput[j] * layerWeights[j][i];
            }
            output[i] = activationFunction.apply(sum);
        }
        return output;
    }

    public void train(double[] input, double[] targetOutput, double learningRate) {
        double[][][] weightDeltas = new double[weights.length][][];
        double[] error = null;

        for (int layerIndex = weights.length - 1; layerIndex >= 0; layerIndex--) {
            double[][] layerWeights = weights[layerIndex];
            int inputSize = layerWeights.length;
            int outputSize = layerWeights[0].length;

            if (error == null) {
                error = new double[outputSize];
                for (int i = 0; i < outputSize; i++) {
                    error[i] = targetOutput[i] - predict(input)[i];
                }
            } else {
                double[] nextError = new double[inputSize - 1]; // exclude bias
                for (int i = 0; i < nextError.length; i++) {
                    double sum = 0;
                    for (int j = 0; j < outputSize; j++) {
                        sum += layerWeights[i][j] * error[j];
                    }
                    nextError[i] = sum;
                }
                error = nextError;
            }

            double[] activations = input;
            if (layerIndex > 0) {
                for (int i = 0; i < layerIndex; i++) {
                    activations = activate(activations, weights[i]);
                }
            }

            double[] extendedActivations = new double[activations.length + 1];
            System.arraycopy(activations, 0, extendedActivations, 0, activations.length);
            extendedActivations[activations.length] = 1; // bias

            weightDeltas[layerIndex] = new double[inputSize][outputSize];
            for (int i = 0; i < inputSize; i++) {
                for (int j = 0; j < outputSize; j++) {
                    double delta = learningRate * error[j] * activationDerivative.apply(extendedActivations[i]);
                    weightDeltas[layerIndex][i][j] = delta;
                }
            }
        }

        for (int layerIndex = 0; layerIndex < weights.length; layerIndex++) {
            for (int i = 0; i < weights[layerIndex].length; i++) {
                for (int j = 0; j < weights[layerIndex][i].length; j++) {
                    weights[layerIndex][i][j] += weightDeltas[layerIndex][i][j];
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] layerSizes = {2, 4, 1};
        Function<Double, Double> sigmoid = x -> 1 / (1 + Math.exp(-x));
        Function<Double, Double> sigmoidDerivative = x -> x * (1 - x);

        NeuralNetwork nn = new NeuralNetwork(layerSizes, sigmoid, sigmoidDerivative);

        double[][] inputs = {{0, 0}, {0, 1}, {1, 0}, {1, 1}};
        double[][] outputs = {{0}, {1}, {1}, {0}};

        for (int epoch = 0; epoch < 10000; epoch++) {
            for (int i = 0; i < inputs.length; i++) {
                nn.train(inputs[i], outputs[i], 0.1);
            }
        }

        for (double[] input : inputs) {
            double[] prediction = nn.predict(input);
            System.out.printf("Input: [%f, %f], Output: %f%n", input[0], input[1], prediction[0]);
        }
    }
}
```

This program creates a simple neural network with one hidden layer and trains it to learn the XOR function. The activation function used is the sigmoid function, but you can easily change it by modifying the `activationFunction` and `activationDerivative` parameters in the constructor.

Please note that this implementation is quite basic and not optimized for performance or flexibility. For more advanced use cases, consider using a dedicated library like DL4J or TensorFlow Java API.
다음은 트레이닝을 위한 역전파를 사용하는 신경망의 간단한 Java 구현입니다. 이 네트워크에는 구성 가능한 레이어와 활성화 기능이 있습니다.

```java
import java.util.Random;
import java.util.function.Function;

public class NeuralNetwork {
    private final double[][][] weights;
    private final Function<Double, Double> activationFunction;
    private final Function<Double, Double> activationDerivative;

    public NeuralNetwork(int[] layerSizes, Function<Double, Double> activationFunction,
                         Function<Double, Double> activationDerivative) {
        this.activationFunction = activationFunction;
        this.activationDerivative = activationDerivative;

        weights = new double[layerSizes.length - 1][][];
        Random random = new Random();
        for (int i = 0; i < weights.length; i++) {
            int inputSize = layerSizes[i] + 1;
            int outputSize = layerSizes[i + 1];
            weights[i] = new double[inputSize][outputSize];
            for (int j = 0; j < inputSize; j++) {
                for (int k = 0; k < outputSize; k++) {
                    weights[i][j][k] = random.nextDouble() * 2 - 1;
                }
            }
        }
    }

    public double[] predict(double[] input) {
        double[] activations = input;
        for (double[][] layerWeights : weights) {
            activations = activate(activations, layerWeights);
        }
        return activations;
    }

    private double[] activate(double[] input, double[][] layerWeights) {
        double[] extendedInput = new double[input.length + 1];
        System.arraycopy(input, 0, extendedInput, 0, input.length);
        extendedInput[input.length] = 1; // bias

        double[] output = new double[layerWeights[0].length];
        for (int i = 0; i < output.length; i++) {
            double sum = 0;
            for (int j = 0; j < extendedInput.length; j++) {
                sum += extendedInput[j] * layerWeights[j][i];
            }
            output[i] = activationFunction.apply(sum);
        }
        return output;
    }

    public void train(double[] input, double[] targetOutput, double learningRate) {
        double[][][] weightDeltas = new double[weights.length][][];
        double[] error = null;

        for (int layerIndex = weights.length - 1; layerIndex >= 0; layerIndex--) {
            double[][] layerWeights = weights[layerIndex];
            int inputSize = layerWeights.length;
            int outputSize = layerWeights[0].length;

            if (error == null) {
                error = new double[outputSize];
                for (int i = 0; i < outputSize; i++) {
                    error[i] = targetOutput[i] - predict(input)[i];
                }
            } else {
                double[] nextError = new double[inputSize - 1]; // exclude bias
                for (int i = 0; i < nextError.length; i++) {
                    double sum = 0;
                    for (int j = 0; j < outputSize; j++) {
                        sum += layerWeights[i][j] * error[j];
                    }
                    nextError[i] = sum;
                }
                error = nextError;
            }

            double[] activations = input;
            if (layerIndex > 0) {
                for (int i = 0; i < layerIndex; i++) {
                    activations = activate(activations, weights[i]);
                }
            }

            double[] extendedActivations = new double[activations.length + 1];
            System.arraycopy(activations, 0, extendedActivations, 0, activations.length);
            extendedActivations[activations.length] = 1; // bias

            weightDeltas[layerIndex] = new double[inputSize][outputSize];
            for (int i = 0; i < inputSize; i++) {
                for (int j = 0; j < outputSize; j++) {
                    double delta = learningRate * error[j] * activationDerivative.apply(extendedActivations[i]);
                    weightDeltas[layerIndex][i][j] = delta;
                }
            }
        }

        for (int layerIndex = 0; layerIndex < weights.length; layerIndex++) {
            for (int i = 0; i < weights[layerIndex].length; i++) {
                for (int j = 0; j < weights[layerIndex][i].length; j++) {
                    weights[layerIndex][i][j] += weightDeltas[layerIndex][i][j];
                }
            }
        }
    }

    public static void main(String[] args) {
        int[] layerSizes = {2, 4, 1};
        Function<Double, Double> sigmoid = x -> 1 / (1 + Math.exp(-x));
        Function<Double, Double> sigmoidDerivative = x -> x * (1 - x);

        NeuralNetwork nn = new NeuralNetwork(layerSizes, sigmoid, sigmoidDerivative);

        double[][] inputs = {{0, 0}, {0, 1}, {1, 0}, {1, 1}};
        double[][] outputs = {{0}, {1}, {1}, {0}};

        for (int epoch = 0; epoch < 10000; epoch++) {
            for (int i = 0; i < inputs.length; i++) {
                nn.train(inputs[i], outputs[i], 0.1);
            }
        }

        for (double[] input : inputs) {
            double[] prediction = nn.predict(input);
            System.out.printf("Input: [%f, %f], Output: %f%n", input[0], input[1], prediction[0]);
        }
    }
}
```

이 프로그램은 하나의 숨겨진 계층을 가진 간단한 신경망을 생성하고 XOR 함수를 학습하도록 훈련합니다. 사용된 활성화 함수는 시그모이드 함수이지만 생성자에서 `activationFunction` 및 `activationDerivative` 매개 변수를 수정하여 쉽게 변경할 수 있습니다.

이 구현은 매우 기본적이며 성능이나 유연성에 최적화되어 있지 않다는 점에 유의하시기 바랍니다. 보다 고급 사용 사례의 경우 DL4J 또는 TensorFlow Java API와 같은 전용 라이브러리를 사용하는 것이 좋습니다.