Was bedeutet der Modifikator public?

Das Schlüsselwort public erlaubt den Zugriff auf das damit ausgezeichnete Sprachelement auch aus anderen Klassen und Packages heraus.

Es ermöglicht die Ansprache des gekennzeichneten Elementes innerhalb des gesamten Programmumfeldes, sodass etwa von einer public deklarierten Klasse auch aus einem ganz anderen package heraus ein Objekt gebildet werden kann.
Mit public können Klassen, Konstruktoren, Methoden und Felder ausgezeichnet werden. Interfaces, sowie deren Methoden und Felder, dürfen auch mit dem Schlüsselwort ausgezeichnet werden. Sie sind jedoch auch ohne explizite Auszeichnung immer public.

Aufgrund des Geheimnisprinzips, nach dem alle Sprachelemente aus Sicherheitsgründen nur aus den Programmbereichen heraus ansprechbar sein dürfen, für die dies zwingend notwendig ist, sollte mit der Vergabe von public so sparsam wie möglich umgegangen werden und die Zugriffsberechtigungen so eng wie möglich gesetzt werden.

public bei Klassen und Interfaces

Klassen und Interfaces, die öffentlich (public) deklariert werden, müssen in Dateien notiert werden, die den Bezeichner der Klasse oder des Interfaces als Namen tragen. Hieraus resultiert, dass pro Datei nur eine Klasse oder ein Interface public deklariert sein darf. Sind mehrere Klassen in einer Datei enthalten, darf nur die namensgebende public sein.
Dies ist für die Objektbildung entscheidend: Aus anderen packages heraus können die anderen Klassen nicht instanziert werden, auch nicht, wenn deren Konstruktor public deklariert wurde:

// abc.PublicBsp.java
				
package abc;
public class PublicBsp {
    public static void main(String[] args) {
        Test test = new Test();
    }
}

class Test {
    public Test() {
        //...
    }
}

//---------------------------------------

// xyz.PublicBsp.java

package xyz;
public class PublicBsp {
    public static void main(String[] args) {
        Test test = new Test();    // Fehler
    }
}

Umgekehrt müssen bei der Objektbildung aus einem anderen package heraus nicht nur die Klasse, sondern auch der Konstruktor public sein.

public bei Methoden

Der universelle Zugriff auf Methoden und Variablen, die public deklariert wurden, ist, wie bei Klassen auch, auch aus anderen packages heraus möglich. Er ist jedoch nur dann sinnvoll, wenn diese Elemente tatsächlich von überall her erreichbar sein müssen (s.o.). Anderenfalls sollten die Zugriffsmöglichkeiten auf protected, private oder package-default (ohne Modifikator) gesetzt werden. Näheres hierzu siehe unter Geheimnisprinzip.

public bei Feldern

Als Instanzvariablen genutzte Felder sollten nur in wohlbegründeten Ausnahmefällen public deklariert werden. Stattdessen sollten sie durch public deklarierte sog. Accessor-Methoden, auch Getter- und Setter-Methoden genannt, von außerhalb der deklarierenden Klasse angesprochen werden. So ist eine durchgängige Kontrolle des Wertebereiches möglich.

//...
private int myInt = 1234;
//...
public int getMyInt() {
    if(myInt < 10)
        myInt = 10;
    return myInt;
}

public void setMyInt(int mi) {
    myInt = 10;
    if(mi > 10)
        myInt = mi;
}
//...

Gängig ist hingegen die public-Deklaration von oft als Konstanten genutzten finalen und statischen Feldern. Da deren Wert nicht verändert werden kann, ist eine derartige Deklaration gelegentlich sinnvoll und i.a. problemlos möglich.

public static final int MY_CONST = 1234;

Zum Geheimnisprinzip und für einen Vergleich der Zugriffsmodifikatoren siehe auch den Artikel zum Geheimnisprinzip.