For any board game I'd tend to use buttons in a grid layout (or a group of grid layouts) like in this mine sweeper game or this chess board.
For the borders in this GUI though, I'd use a 3 x 3 group of nine grid layouts, each of which has a LineBorder
. By default the border would go around all four sides of the panel it is displayed in, but where they meet the border would be double width, thereby coming close to recreating the second image.
E.G.

import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import java.util.*;
public class Soduko {
private JComponent ui = null;
Soduko() {
initUI();
}
public void initUI() {
if (ui!=null) return;
ui = new JPanel(new GridLayout(3,3));
ui.setBorder(new EmptyBorder(4,4,4,4));
ArrayList<Integer> values = new ArrayList<>();
for (int ii=0; ii<34; ii++) {
values.add(0);
}
Random r = new Random();
for (int ii=34; ii<81; ii++) {
values.add(r.nextInt(9)+1);
}
Collections.shuffle(values);
int count=0;
for (int ii=0; ii<9; ii++) {
JPanel p = new JPanel(new GridLayout(3, 3));
p.setBorder(new LineBorder(Color.BLACK, 2));
ui.add(p);
for (int jj=0; jj<9; jj++) {
int v = values.get(count++).intValue();
String s = v>0 ? "" + v : "";
p.add(new JButton(s));
}
}
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
Soduko o = new Soduko();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}