Los modelos personalizados dentro de una tabla permiten asociar datos en formatos muy diversos. Como ya se ha explicado lo normal es crear una tabla con un modelo por defecto donde se pasan como parámetros un elemento de tipos String[] y un elemento de tipo Object[][] desde la clase donde está la clase. Estos parámetros representan tanto el nombre / número de columnas como el contenido de cada una de las filas. Lo malo que tiene esto es que se trata de datos estancos, que no son exportables de forma sencilla.

Para poder hacer un modelo de datos personalizado hay que crear una clase que extienda de AbstractTableModel y sobreescribir los siguientes métodos:

public class ModeloPersonalizadoTabla extends AbstractTableModel {

    Class[] tipos = new Class[]{String.class, String.class, Integer.class, Boolean.class};
    Boolean[] editables = new Boolean[]{false, false, false, true};
    String[] nombreColumnas = new String[]{"Nombre", "Apellido", "Telefono", "Trabajando"};
    Object[][] datosFilas = new Object[][]{{"Nombre1", "Apellido", 123, true},
            {"Nombre2", "Apellido", 123, true},
            {"Nombre3", "Apellido", 123, true}};

    @Override
    public int getRowCount() {
        return datosFilas.length;
    }

    @Override
    public int getColumnCount() {
        return nombreColumnas.length;
    }

    @Override
    public String getColumnName(int column) {
        return nombreColumnas
; } }

Estos son los métodos mínimos, pero se pueden sobre escribir todos los que se quieran. Como se puede ver en el código los arrays de datos ya no es obligatorio pasarlos desde la clase donde está la tabla, sino que pueden ser declarados dentro del propio modelo En el caso de querer poner más características habría que sobreescribir los métodos deseados. Los principales son:

public class ModeloPersonalizadoTabla extends AbstractTableModel {

    Class[] tipos = new Class[]{String.class, String.class, Integer.class, Boolean.class};
    Boolean[] editables = new Boolean[]{false, false, false, true};
    String[] nombreColumnas = new String[]{"Nombre", "Apellido", "Telefono", "Trabajando"};
    Object[][] datosFilas = new Object[][]{{"Nombre1", "Apellido", 123, true},
            {"Nombre2", "Apellido", 123, true},
            {"Nombre3", "Apellido", 123, true}};

    @Override
    public int getRowCount() {
        return datosFilas.length;
    }

    @Override
    public int getColumnCount() {
        return nombreColumnas.length;
    }

    @Override
    public String getColumnName(int column) {
        return nombreColumnas
; } @Override public Class<?> getColumnClass(int columnIndex) { return tipos[columnIndex]; } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return editables[columnIndex]; } @Override public Object getValueAt(int rowIndex, int columnIndex) { return datosFilas[rowIndex][columnIndex]; } }

Tan solo faltaría asociar la tabla con el modelo creado

modeloPersonalizadoTabla = new ModeloPersonalizadoTabla();
tabla = new JTable(modeloPersonalizadoTabla);

Las restricciones que tienen esta forma de trabajar es que el AbstractTableModel no tiene la implementación de métodos directos para añadir / borrar elementos de la tabla, por lo que hay que crearlos en la clase. Para ello el origen de los datos debería ser una lista, de forma que los datos sean más manejables

ArrayList personas = new ArrayList();
public ModeloPersonalizadoTabla(ArrayList personas) {
        this.personas = personas;
        personas.add(new Persona("Nombre1", "Apellido", 123, true));
        personas.add(new Persona("Nombre2", "Apellido", 123, true));
        personas.add(new Persona("Nombre3", "Apellido", 123, true));
        personas.add(new Persona("Nombre4", "Apellido", 123, true));
}

Con la lista de datos rellena, escribir los datos dentro de la tabla se realiza en el método getValueAt, siendo la nueva sobreescritura

public class ModeloPersonalizadoTabla extends AbstractTableModel {

    Class[] tipos = new Class[]{String.class, String.class, Integer.class, Boolean.class};
    Boolean[] editables = new Boolean[]{false, false, false, true};
    String[] nombreColumnas = new String[]{"Nombre", "Apellido", "Telefono", "Trabajando"};
    Object[][] datosFilas = new Object[][]{{"Nombre1", "Apellido", 123, true},
            {"Nombre2", "Apellido", 123, true},
            {"Nombre3", "Apellido", 123, true}};

    ArrayList personas = new ArrayList();

    public ModeloPersonalizadoTabla() {
        personas.add(new Persona("Nombre1", "Apellido", 123, true));
        personas.add(new Persona("Nombre2", "Apellido", 123, true));
        personas.add(new Persona("Nombre3", "Apellido", 123, true));
        personas.add(new Persona("Nombre4", "Apellido", 123, true));
    }

    @Override
    public int getRowCount() {
        return personas.size();
    }

    @Override
    public int getColumnCount() {
        return nombreColumnas.length;
    }

    @Override
    public String getColumnName(int column) {
        return nombreColumnas
; } @Override public Class<?> getColumnClass(int columnIndex) { return tipos[columnIndex]; } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return editables[columnIndex]; } @Override public Object getValueAt(int rowIndex, int columnIndex) { Persona p = (Persona) personas.get(rowIndex); switch (columnIndex) { case 0: return p.getNombre(); case 1: return p.getApellido(); case 2: return p.getTelefono(); case 3: return p.isMarcado(); default: return null; } } }

Para crear o eliminar elementos lo que habría que haces es crear un método que añada o borre elementos de la lista y actualizar los datos de la tabla

   public void anadirDatos(Persona p){
        personas.add(p);
        fireTableDataChanged();
   }

   public void borrarDatos(int row){
        personas.remove(row);
        fireTableDataChanged();
  }

Por último tan solo quedaría utilizar los métodos desde la clase gráfica o controlador (mvc ejecutado)

Persona datoAgregar = new Persona("Nombre","Apellido",123,false);
modeloPersonalizadoTabla.anadirDatos(datoAgregar);
modeloPersonalizadoTabla.borrarDatos(tabla.getSelectedRow());