Registrieren, Stapel, Heap, statischer Speicher, konstanter Speicher (konstanter Pool Zeichenfolge a = „abc“ a befindet sich auf dem Stapel, „abc“ befindet sich im Konstantenpool), Nicht-RAM Speicher
als Mitglied der Klasse wird automatisch auf den Standardwert
Boolescher Wert 1Bit Standardfalsch
Byte 8Bits ein Byte u0000
char 16Bits Ein Zeichen 0
kurze 16Bits 0
int 32Bits 0
float 32Bits 0.0f
long 64Bits 0L
doppelte 64Bits 0,0d
3.BigInteger, Bigdecimal
Hohe Präzision, langsame Geschwindigkeit
verfügt über einen speziellen „Garbage Collector“, der nach allen Objekten sucht, die mit neu erstellt wurden, und identifiziert, welche davon werden nicht mehr zitiert. Anschließend gibt es automatisch den von diesen inaktiven Objekten belegten Speicher frei, sodass er von neuen Objekten verwendet werden kann.
5.Elternklasse statische Mitgliedsvariablen und statische Anweisung
statische Mitgliedsvariablen der Unterklasse und statische Anweisung
nicht- statische Mitgliedsvariable der übergeordneten Klasse und sind nicht statischer Anweisungsblock
Konstruktormethode der übergeordneten Klasse
statische Mitgliedsvariablen von 🎜>Unterklassen und nicht-statische Anweisungsblöcke
Konstruktormethode der Unterklasse(
statisch wird nur einmal ausgeführt. Das erste Mal neu )
6.Vector
ist ein Array, das automatisch wächst. Es verfügt über eine hohe Abfrageeffizienz, eine geringe Additions- und Löscheffizienz, Thread-Sicherheit, eine langsame Geschwindigkeit und verdoppelt die ursprüngliche Größe.ArrayList
vergrößert das Array automatisch mit hoher Abfrageeffizienz, geringer Effizienz beim Hinzufügen und Löschen, Thread-Unsicherheit und hoher Geschwindigkeit. Das Wachstum beträgt das Original0,5 mal. LinkedList
Bidirektionale zirkuläre verknüpfte Liste, geringe Abfrageeffizienz, hohe Additions- und Löscheffizienz, Thread-unsicher.HashMap
ist ein verknüpfter Listen-Hash, der eine Kombination aus einem Array und einer verknüpften Liste ist. Er erlaubtNullwerte Thread-sicher und hocheffizient. TreeMap
wird als binär sortierter Baum implementiert, wobeiSchlüssel zum Sortieren verwendet wird. LinkedHashMap
ist für Hash-Tabellen und verknüpfte Listen implementiert. Es ist eine Unterklasse vonHashMap. Es behält die Reihenfolge der Einfügung bei und gibt sie aus Bei Bedarf ist die Reihenfolge dieselbe wie bei der Eingabe. Verwenden Sie dann LinkedHashMap. Hashtable ist threadsicher, ineffizient und erlaubt keine Nullwerte. Set ist nicht wiederholbar: TreeSet wird basierend auf TreeMap implementiert und ist bestellt Ja, Thread-unsicher. HashSet wird basierend auf HashMap, HashMaps Taste . wird basierend auf LinkedHashMap implementiert und geordnet. Verwenden Sie entrySet , um Map Klassensammlung KV anstelle der Methode keySet für die Durchquerung. Erklärung: 1, entrySet implementiert die Schnittstelle Set, die Schlüssel-Wert-Paare speichert. Ein K entspricht einem V. 7. Durchqueren der Karte
keySet durchläuft tatsächlich 2 Mal, sobald es in Iterator konvertiert wird -Objekt, das andere Mal besteht darin, den Wert entsprechend dem Schlüssel aus der
hashMap >. Und entrySet wurde nur einmal durchlaufen und sowohl Schlüssel als auch Wert eingegeben Eintrag hat eine höhere Effizienz . Wenn es sich um JDK8
handelt, verwenden Sie die Methode Map.foreach . Positivbeispiel: values()
gibt den Wertesatz V zurück, der eine Liste set object; keySet() gibt den K -Wertsatz zurück, der Ein Set -Sammlungsobjekt;
entrySet() gibt eine Sammlung von K-V -Wertkombinationen zurück.
2, eine Methode zum Durchqueren der Karte.
Set
for (Map.Entry
System.out .println(entry.getKey()+","+entry.getValue());
Get KgetKey
, und das andere ist
keySet. Set
System.out.println(s+","+map.get(s)) ;
}
IO
SystemBridge-Stream: InputStreamReaderByte-Stream in Zeichen-Stream konvertieren. (Lesen wie in einen Zeichenstrom umgewandelt)
OutputStreamWriterKonvertieren Sie den Zeichenstrom in einen Bytestrom (Schreiben wie in einen Bytestrom umgewandelt)
Flussklassifizierung
Klassifizierung verwenden | Byte-Eingabestream | Byte-Ausgabestream | Zeicheneingabestream | Zeichenausgabestream | ||
Abstrakte Basisklasse | InputStream |
OutputStream |
Leser |
Schriftsteller |
Node Stream |
|
Zugriffsdatei | FileInputStream |
FileOutStream |
FileReader |
FileWriter |
Zugriffswert |
|
ByteArrayInputStream |
ByteArrayOutStream |
CharArrayReader |
CharArrayWriter |
Zugriffspipeline (Thread-Interaktion) |
||
PipedInputStream |
PipedOutStream |
PipedReader |
PipedWriter |
Zugriffszeichenfolge |
||
StringReader |
StringWriter |
Verarbeitungsablauf | ||||
Gepufferter Stream | BufferedInputStream | BufferedOutputStream | BufferedReader |
BufferedWriter |
||
Stream konvertieren |
|
|
InputStreamReader |
OutputStreamWriter |
||
Object Stream |
ObjectInputStream |
ObjectOutputStream |
|
|
||
Abstrakte Basisklasse (Filterung) |
FilterInputStream |
FilterOutputStream |
FilterReader |
FilterWriter |
||
PrintStream |
|
PrintStream |
| PrintWriter |
||
Pushback InputStream |
PushbackInputStream |
PushbackReader |
|
|||
Spezieller Stream |
DataInputStream |
DataOutputStream |
|
|
1. Java IO verwendet den Dekorationsmodus, der Verarbeitungsströme verwendet, um Knotenströme zu umschließen, um Code-Vielseitigkeit zu erreichen.
2.Wie man Verarbeitungsfluss und Knotenfluss unterscheidet, erfordert beim Erstellen eines neuen Knotenflusses eine Datenquelle (Datei, Netzwerk), während für den Verarbeitungsfluss ein Knotenfluss erforderlich ist als Parameter.
3.Die Funktion des Verarbeitungsflusses besteht darin, die Vielseitigkeit des Codes, den Komfort beim Schreiben von Code und die Leistung zu verbessern.
4.Knotenströme sind alle Implementierungsklassen, die der abstrakten Basisklasse entsprechen, und sie alle implementieren die grundlegenden Lese- und Schreibmethoden der abstrakten Basisklasse. Wenn unter anderem die Methode read() -1 zurückgibt, bedeutet dies, dass das Ende der Datenquelle gelesen wurde.
1. Framediagramm des Eingabestreams in Bytes
Das Folgende ist ein Framediagramm des Eingabestreams in Bytes.
Daran können wir sehen.
(01) InputStream ist eine Superklasse für Eingabestreams in Bytes. InputStream stellt die Schnittstelle read() bereit, um Bytedaten aus dem Eingabestream zu lesen.
(02) ByteArrayInputStream ist ein Byte-Array-Eingabestream. Es enthält einen internen Puffer, der die aus dem Stream gelesenen Bytes enthält. Laienhaft ausgedrückt ist sein interner Puffer ein Byte-Array, und die Essenz von ByteArrayInputStream wird durch Byte-Arrays erreicht.
(03) PipedInputStream ist ein Pipeline-Eingabestream. Er wird zusammen mit PipedOutputStream verwendet, um eine Pipeline-Kommunikation zwischen mehreren Threads zu erreichen.
(04) FilterInputStream ist ein Filter-Eingabestream. Es ist die Oberklasse von DataInputStream und BufferedInputStream.
(05) DataInputStream ist der Dateneingabestream. Es wird verwendet, um andere Eingabeströme zu dekorieren, was „Anwendungen ermöglicht, grundlegende Java -Datentypen aus dem zugrunde liegenden Eingabestrom auf maschinenunabhängige Weise zu lesen“.
(06) BufferedInputStream ist ein gepufferter Eingabestream. Es fügt einem anderen Eingabestream eine Pufferfunktion hinzu.
(07) Datei ist eine abstrakte Darstellung von „Datei“ und „Verzeichnispfadname“. Bezüglich Datei beachten Sie bitte zwei Punkte:
a), Datei repräsentiert nicht nur eine Datei, sie kann auch ein Verzeichnis darstellen!
b), File ist in io definiert, aber seine Superklasse ist stattdessen Object von InputStream.
(08) FileDescriptor ist ein „Dateideskriptor“. Es kann zur Darstellung offener Dateien, offener Sockets usw. verwendet werden.
(09) FileInputStream ist ein Dateieingabestream. Es wird normalerweise zum Lesen von Dateien verwendet.
(10) ObjectInputStream ist ein Objekteingabestream. Es wird zusammen mit ObjectOutputStream verwendet, um eine dauerhafte Speicherung von „Basisdaten oder Objekten“ bereitzustellen.
2. Frame-Diagramm des Ausgabestreams in Bytes
Das Folgende ist der Frame des Ausgabestreams in Bytes.
Daran können wir sehen. Die gemeinsame übergeordnete Klasse für Ausgabestreams in Bytes ist OutputStream.
(01) OutputStream ist eine Superklasse für Ausgabestreams in Bytes. OutputStream stellt die Schnittstelle write() bereit, um Bytedaten aus dem Ausgabestream zu lesen.
(02) ByteArrayOutputStream ist ein Byte-Array-Ausgabestream. In ByteArrayOutputStream geschriebene Daten werden in ein Byte -Array geschrieben. Der Puffer wächst automatisch, wenn kontinuierlich Daten geschrieben werden. Daten können mit toByteArray() und toString() abgerufen werden.
(03) PipedOutputStream ist ein Pipeline-Ausgabestream. Er wird zusammen mit PipedInputStream verwendet, um eine Pipeline-Kommunikation zwischen mehreren Threads zu erreichen.
(04) FilterOutputStream ist der Filterausgabestream. Es ist eine Superklasse von DataOutputStream, BufferedOutputStream und PrintStream.
(05) DataOutputStream ist der Datenausgabestream. Es wird verwendet, um andere Ausgabeströme zu dekorieren, was „Anwendungen ermöglicht, maschinenunabhängig in die zugrunde liegenden Java -Datentypen zu schreiben“.
(06) BufferedOutputStream ist ein gepufferter Ausgabestream. Dadurch wird einem anderen Ausgabestream eine Pufferfunktion hinzugefügt.
(07) PrintStream ist der Druckausgabestream. Es wird verwendet, um andere Ausgabeströme zu dekorieren und anderen Ausgabeströmen Funktionalität hinzuzufügen, damit diese problemlos verschiedene Datenwertdarstellungen drucken können.
(08) FileOutputStream ist ein Dateiausgabestream. Es wird typischerweise zum Schreiben in Dateien verwendet.
(09) ObjectOutputStream ist ein Objektausgabestream. Es wird zusammen mit ObjectInputStream verwendet, um eine dauerhafte Speicherung von „Basisdaten oder Objekten“ bereitzustellen.
Threads umfassen die folgenden 5 Zustände.
1. Neuer Zustand(Neu): Nachdem das Thread-Objekt erstellt wurde, tritt es in den neuen Zustand ein. Beispiel: Thread thread = new Thread().
2. Bereitschaftszustand (Ausführbar): wird auch „ausführbarer Zustand“ genannt. Nachdem das Thread-Objekt erstellt wurde, rufen andere Threads die Methode start() des Objekts auf, um den Thread zu starten. Zum Beispiel thread.start(). Threads im Bereitschaftszustand können jederzeit zur Ausführung durch CPU eingeplant werden.
3. Laufstatus(Wird ausgeführt): Thread erhält die CPU-Berechtigung zur Ausführung. Es ist zu beachten, dass ein Thread nur aus dem Bereitschaftszustand in den laufenden Zustand wechseln kann.
4. Blockierter Zustand(Blockiert): Der blockierte Zustand liegt vor, wenn der Thread aus irgendeinem Grund aufgibt CPUNutzungsrecht, vorübergehend ausgesetzt. Bis der Thread in den Bereitschaftszustand wechselt, besteht die Möglichkeit, in den Ausführungszustand zu wechseln. Es gibt drei Blockierungssituationen:
(01) Warten auf Blockierung-- Durch Aufrufen des Threads wait()Methode, um den Thread auf den Abschluss einer bestimmten Arbeit warten zu lassen.
(02) Synchronisationsblockierung-- Thread konnte keine synchronisierteSynchronisationssperre erwerben (Da die Sperre von anderen Threads belegt ist ), wechselt sie in den synchronen Blockierungszustand.
(03) Andere Blockierung-- Durch Aufrufen des Threads sleep() oder join() oder wenn eine I/O-Anfrage ausgegeben wird, wechselt der Thread in den Blockierungszustand. Wenn der Status sleep() eine Zeitüberschreitung aufweist, wartet join() darauf, dass der Thread beendet wird oder eine Zeitüberschreitung auftritt, oder E/A Wenn die Verarbeitung abgeschlossen ist, kehrt der Thread in den Bereitschaftszustand zurück.
5. Todeszustand(Dead) : Der Thread wurde aufgrund einer Ausnahme ausgeführt oder beendetrun() Methode beendet der Thread seinen Lebenszyklus .
// InheritsThread
class MyThread erweitert Thread{
public void run(){
...
}
};
MyThread mythread = new MyThread();
//ImplementierungRunnable
Klasse MyThread implementiert Runnable{
public void run(){
...
}
};
MyThread mt=new MyThread( );
Thread t1=new Thread(mt);
mythread.start() startet einen neuen Thread und führt die Methode run() im neuen Thread aus.
und mythread.run() führen die Methode run() direkt im aktuellen Thread aus und starten ihn nicht . Ein neuer Thread zum Ausführen von run().
Wir fassen die Grundregeln von synchronisiert wie folgt zusammen3 Elemente und veranschaulichen Sie diese anhand von Beispielen.
Artikel 1: Wenn ein Thread auf "ein bestimmtes Objekt" zugreift"synchronisierteMethode" oder "synchronisierteCodeblock " " >"synchronisierteMethode" oder "synchronisierteCodeblock „ Der Zugriff auf wird gesperrt. Artikel 2: Wenn ein Thread auf "ein bestimmtes Objekt" zugreift
"synchronisierteMethode" oder "synchronisierteCodeblock ", andere Threads können weiterhin auf den asynchronen Codeblock des Objekts zugreifen "" . Artikel 3: Wenn ein Thread auf "ein bestimmtes Objekt""synchronisierte
Methode" oder "synchronisierteCodeblock ", andere Threads haben andere "synchronisierteMethode" oder " synchronisiertCodeblock"Der Zugriff auf wird gesperrt.
notify()-- Wach auf und warte auf diesem Objektmonitor eines einzelnen Threads.
notifyAll()-- Weckt alle Threads auf, die auf diesem Objektmonitor warten.
wait()-- Lassen Sie den aktuellen Thread in "Warten(Blockieren)Status", ", bis ein anderer Thread notify() Methode oder notifyAll() Methode “, der aktuelle Thread wird aktiviert (Geben Sie "Bereitschaftsstatus") ein. warten(lange Zeitüberschreitung)--
Lassen Sie den aktuellen Thread in "warten(blockieren )Status ", ", bis andere Threads dieses Objekts aufrufen notify() -Methode oder notifyAll() -Methode oder überschreiten Sie die angegebene Zeitspanne “, Der aktuelle Thread wird aktiviert ( und wechselt in den "Bereitschaftszustand "). wait(long timeout, int nanos)--
Lassen Sie den aktuellen Thread in "Waiting( Blockiert ) Status ", ", bis ein anderer Thread dieses Objekt aufruft notify() -Methode oder notifyAll() -Methode oder ein anderer Thread unterbricht den aktuellen Thread, oder es hat eine tatsächliche Zeitspanne gedauert abgelaufen ", der aktuelle Thread ist aktiviert(geht in den "Bereitschaftszustand “).
s Funktion ist Konzession. Dadurch kann der aktuelle Thread von "Running State" bis " wechseln Bereitstatus “, wodurch andere wartende Threads mit derselben Priorität Ausführungsrechte erhalten können. Es gibt jedoch keine Garantie dafür, dass yield() ist Nach können andere Threads mit der gleichen Priorität definitiv Ausführungsrechte erhalten. Es ist auch möglich, dass der aktuelle Thread das eingegeben hat Laufzustand"Lauf weiter!
sleep() ist in Thread.java Mitte. sleep()
wird verwendet, um den aktuellen Thread in den Ruhezustand zu versetzen, d 🎜> bis "Ruhezustand(Blockierung)Status". sleep() gibt die Schlafzeit an, und die Thread-Schlafzeit ist größer als / gleich der Schlafzeit Wird der Thread erneut aktiviert, wechselt er von "Blocked State" zu "Ready State" und wartet somit auf die geplante Ausführung von cpu. Wir wissen, dass die Funktion von wait() darin besteht, den aktuellen Thread mit dem "
Laufstatus laufen zu lassen "Eingabe"Warten(Blockierung)Status “ wird auch die Synchronisationssperre aufheben. Die Funktion von sleep() besteht darin, auch den aktuellen Thread von „Laufstatus“Enter "Ruhezustand(Blockiert)Status“. Allerdings wird wait() die Synchronisationssperre des Objekts aufheben, während sleep() die Sperre nicht aufheben wird .
Das folgende Beispiel zeigt, dass sleep() die Sperre nicht aufhebt.
Wir wissen, dass die Funktion von wait() darin besteht, den aktuellen Thread in "Running state" zu veranlassen >"Warten(Blockierung)Status" , die Synchronisationssperre wird ebenfalls aufgehoben. Die Funktion von yield() ist eine Konzession, sie sorgt auch dafür, dass der aktuelle Thread den "Laufstatus verlässt " . Der Unterschied zwischen ihnen ist: (01) wait()
ermöglicht die Ausführung des Threads durch "Ausführungsstatus" Geben Sie "Warten(Blockiert)Status" und yield() soll den Thread um ""Enter "Bereitschaftszustand". (02) wait() bewirkt, dass der Thread die Synchronisationssperre des von ihm gehaltenen Objekts aufhebt, während die Methode
yield() dies nicht tut Lösen Sie die Sperre. 7.join()
Thread definiert. Java in. join() Die Rolle von : Lassen Sie
" Hauptthread " auf warten "Sub-Thread " kann nach seinem Ende weiterlaufen. Dieser Satz mag etwas unklar sein, aber wir verstehen ihn dennoch anhand von Beispielen: // Hauptthread
öffentliche Klasse Vater erweitert Thread { public void run() {
Son s = new Son(); s.start(); s.join();... }}//
Son Thread
public class Son erweitert Thread { public void run( ) {
... }} Sein Quellcode ist, dass, wenn der untergeordnete Thread aktiv ist (isAlive()), Der Hauptthread wird nicht aufhören zu warten (wait(0))8.interrupt()
interrupt()-Methode dieses Threads aufrufen, bestehen sie die checkAccess()-Prüfung Berechtigungen. Dies kann die Ausnahme SecurityException auslösen.
Wenn sich dieser Thread in einem blockierten Zustand befindet: Rufen Sie wait(), wait(long) oder wait(long, int) Es geht in den Wartezustand (blockierend) über oder ruft den Thread join() auf, beitreten ( long), join(long, int), sleep(long), sleep(long, int) versetzt es ebenfalls in einen blockierenden Zustand. Wenn der Thread seine interrupt()-Methode aufruft, während er sich im blockierten Zustand befindet, dann ist sein “Interrupt-Status“ wird gelöscht und eine InterruptedException-Ausnahme wird empfangen. Beispielsweise wechselt ein Thread durch wait() in den blockierten Zustand und unterbricht den Thread dann durch interrupt(); 🎜>interrupt() setzt das Interrupt-Flag des Threads sofort auf "true", aber da der Thread blockiert ist, wird das >"Interrupt-Flag " wird sofort auf "false" gelöscht, und zwar gleichzeitig Mal eine InterruptedException Ausnahme.
Wenn ein Thread in einemSelektor-Selektor blockiert ist, wird er durch interrupt() unterbrochen; Das Interrupt-Flag des Threads wird auf true gesetzt und er kehrt sofort vom Auswahlvorgang zurück.
Wenn die oben genannte Situation nicht eintritt und der Thread durchinterrupt() unterbrochen wird, wird seine Unterbrechungsmarkierung auf gesetzt „wahr“.
Das Unterbrechen eines„ beendeten Threads “ führt zu nichts.
8“ Unterbrechen “ Möglichkeit, den Thread im „Blockierter Zustand“. Wenn der Thread aufgrund der Aufrufe sleep(), wait(), join() Normalerweise übergeben wir die Markierung „“ "-Methode beendet einen Thread im "Laufzustand ". Darunter auch "Break Mark" und "Extra Add Mark“. @Overridepublic void run() { while (!isInterrupted()) { // Aufgabe ausführen... } } Erklärung: isInterrupted() ist zu ermitteln Die Unterbrechung des Threads Ist das Tag wahr. Wenn ein Thread ausgeführt wird und wir ihn beenden müssen, können wir die Methode interrupt() des Threads aufrufen und dabei das Interrupt-Flag des Threads als true verwenden , das heißt, isInterrupted() gibt true zurück. Zu diesem Zeitpunkt wird die while-Schleife beendet. (02) Zusätzliche Tags hinzufügen " über "". hat die folgende Form: flag = false; } @Overridepublic void run() { while (flag) { // ... }} Flag-Tag im Thread und sein Standardwert ist true ; und wir stellen stopTask() bereit, um das Flag zu setzen. Wenn wir den Thread beenden müssen, ermöglicht der Aufruf der Methode stopTask() des Threads, dass der Thread die while-Schleife verlässt. Hinweis: Durch die Definition von Sprechen Sie abschließend über interrupted() und isInterrupted(). Jeder Thread hat eine Priorität. "Threads mit hoher Priorität" haben Priorität vor "Threads mit niedriger Priorität ” Ausführen. Jeder Thread kann als Daemon oder Nicht-Daemon markiert werden. Wenn ein neuer untergeordneter Thread innerhalb eines laufenden Hauptthreads erstellt wird, wird die Priorität des untergeordneten Threads auf die Priorität des Hauptthreads gesetzt, der ihn erstellt hat, „, genau dann, wenn “. Der Hauptthread, der es erstellt hat, ist der Daemon-Thread " "Der untergeordnete Thread ist der Daemon-Thread". Wenn die virtuelle Java main() Methodenstart). JVM läuft weiter, bis eine der folgenden Bedingungen eintritt: JVM wird beendet: (01) hat die Methode exit() aufgerufen und exit() hat die Berechtigung zur normalen Ausführung. (02) Alle "Nicht-Daemon-Threads "sind tot(Das heißt, JVM hat nur "Daemon-Thread ). Jeder Thread ist als „Daemon-Thread “ oder "Benutzerthread". Wenn nur der Daemon-Thread ausgeführt wird, wird JVM automatisch beendet.
und anderer Methoden in den Blockierungszustand eintritt, wenn der Thread interrupt()Setzen Sie das Interrupt-Flag des Threads auf true. Aufgrund des Blockierungszustands wird die Unterbrechungsmarkierung gelöscht und eine InterruptedException-Ausnahme generiert. Platzieren Sie InterruptedException entsprechend, um den Thread zu beenden
8.2 Beenden Sie den Thread im „Laufzustand“
(01) Beenden Sie den Thread über "Interrupt-Flag " .
hat die folgende Form:
Hinweis: interrupt() beendet nicht den laufenden Zustand von "" s Thread! Dadurch wird das Interrupt-Flag des Threads auf true gesetzt.
private volatile boolean flag= true;protected void stopTask() {
flag als Typ flüchtig soll sichergestellt werden, dass flag Sichtbarkeit. Das heißt, nachdem andere Threads das Flag über stopTask() geändert haben, kann dieser Thread das geänderte sehen Wert der Flagge.
interrupted() und isInterrupted() können beide verwendet werden, um den "-Interrupt des Objekts zu erkennen Flagge “.
Der Unterschied besteht darin, dass interrupted() zusätzlich zur Rückgabe der Unterbrechungsmarkierung auch die Unterbrechungsmarkierung (Die bevorstehende Interrupt-Markierung wird auf false) gesetzt und isInterrupted() gibt nur das Interrupt-Flag zurück
9.Thread-Priorität und Daemon-Threads
Das obige ist der detaillierte Inhalt vonTeilen Sie einige grundlegende Java-Interviewfragen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!