何?これらの4つの概念を聞いたが、多くのJavaベテランは知らない!
Javaは多くの人が使用しているプログラミング言語ですが、Javaの概念の中には、理解するのが非常に難しいものもあります。長年のベテランでさえ、Javaの概念について混乱や混乱を抱えています。
したがって、この記事では、開発者がこれらの概念をより明確に理解できるように、Javaで最も難しい4つの概念を紹介し
ます。1。匿名内部クラスの使用
2.マルチスレッド
3.同期を実現する方法
4 。シリアル化
匿名の内部クラス
匿名内部クラスは匿名クラスとも呼ばれます。匿名内部クラスに名前がないことを除けば、ローカルクラスや内部クラスに少し似ています。匿名内部クラスの宣言とインスタンス化を同時に行うことができます。
匿名の内部クラスは、部分クラスを使用し、この部分クラスを1回だけ使用するシナリオにのみ適しています。
匿名の内部クラスには、明示的に宣言する必要のあるコンストラクターはありませんが、自動的に宣言された非表示のコンストラクターがあります。
匿名内部クラスを作成するには、次の2つの方法があり
ます。1。クラス(具象または抽象)を継承して匿名内部クラスを作成します
。2。インターフェイスを実装して匿名内部クラスを作成します。
次の例を見てみましょう。
interface Programmer {
void develop();
}
public class TestAnonymousClass {
public static Programmer programmer = new Programmer() {
@Override
public void develop() {
System.out.println("我是在类中实现了接口的匿名内部类");
}
};
public static void main(String[] args) {
Programmer anotherProgrammer = new Programmer() {
@Override
public void develop() {
System.out.println("我是在方法中实现了接口的匿名内部类");
}
};
TestAnonymousClass.programmer.develop();
anotherProgrammer.develop();
}
}
上記の例からわかるように、匿名クラスはクラスまたはメソッドのいずれかで作成できます。
先ほど、匿名クラスは具象クラスまたは抽象クラスを継承するか、インターフェースを実装できることにも触れました。したがって、上記のコードでは、Programmerというインターフェイスを作成し、そのインターフェイスをそれぞれTestAnonymousClassクラスとmain()メソッドに実装しました。
インターフェイスに加えて、Programmerは抽象クラスまたは具象クラスのいずれかになります。
次のコードのような抽象クラス:
public abstract class Programmer {
public abstract void develop();
}
具体的なクラスコードは次のとおりです。
public class Programmer {
public void develop() {
System.out.println("我是一个具体类");
}
}
OK、さらに進んで、Programmerクラスのパラメーターなしのコンストラクターがない場合はどうなりますか?匿名クラスのクラス変数にアクセスできますか?クラスを継承する場合、匿名クラスにすべてのメソッドを実装する必要がありますか?
public class Programmer {
protected int age;
public Programmer(int age) {
this.age = age;
}
public void showAge() {
System.out.println("年龄:" + age);
}
public void develop() {
System.out.println("开发中……除了异性,他人勿扰");
}
public static void main(String[] args) {
Programmer programmer = new Programmer(38) {
@Override
public void showAge() {
System.out.println("在匿名类中的showAge方法:" + age);
}
};
programmer.showAge();
}
}
1.匿名クラスを構築する場合、任意のコンストラクターを使用できます。上記のコードでは、パラメーターを使用してコンストラクターを使用したことがわかります。
2.匿名クラスは、具象クラスまたは抽象クラスを継承でき、インターフェイスを実装することもできます。したがって、アクセス修飾子のルールは通常のクラスのルールと同じです。子クラスは親クラスの保護された制限付きプロパティにアクセスできますが、プライベート制限付きプロパティにはアクセスできません。
3.匿名クラスが上記のコードのProgrammerクラスなどの具体的なクラスを継承する場合、すべてのメソッドを書き直す必要はありません。ただし、匿名クラスが抽象クラスを継承するか、インターフェイスを実装する場合、匿名クラスは、実装されていないすべての抽象メソッドを実装する必要があります。
4.匿名内部クラスで静的初期化を使用したり、静的変数を追加したりすることはできません。
5.匿名の内部クラスは、finalによって変更された静的定数を持つことができます。
匿名クラスの典型的な使用シナリオ
一時的な使用:問題を修正したり、機能を追加したりするために、クラスの一時的な実装を追加する必要がある場合があります。プロジェクトへのjavaファイルの追加を回避するために、特にこのクラスを1回だけ使用する場合は、匿名クラスを使用します。
UIイベントリスナー:Javaグラフィカルインターフェイスプログラミングで、匿名クラスで最も一般的に使用されるシナリオは、イベントリスナーを作成することです。次に例を示します
。button.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
}
});
上記のコードでは、匿名クラスを介してsetOnClickListenerインターフェイスを実装しています。ユーザーがボタンをクリックすると、トリガーされます。実装されたonClickメソッド。
マルチスレッド
Javaでのマルチスレッドとは、複数のスレッドを使用して大規模なタスクの実行プロセスを完了することです。マルチスレッドを使用すると、CPUを最大限に活用できます。
マルチスレッドを使用すると、スレッドはプロセスよりも軽いため、プロセスではなくスレッドを使用してタスク処理を実行します。スレッドは軽量のプロセスであり、プログラム実行の最小単位であり、メインメモリはスレッド間で共有されます。 、そしてプロセスはそうではありません。
スレッドのライフサイクル
上の図に示すように、スレッドのライフサイクルには6つの状態があります。これらの状態を順番に紹介します。
1.新規:スレッドインスタンスを作成すると、スレッドの状態は新規になります。この状態は、スレッドの最初の状態です。この時点で、スレッドを実行する準備はできていません。
2.実行可能:スレッドクラスのstart()メソッドが呼び出されると、スレッドはNew状態からRunnable状態に移行します。これは、このスレッドを実行する準備ができていることを意味します。ただし、スレッドが本当に実行したい場合は、このスレッドの実行をスケジュールするためのスレッドスケジューラが必要です。ただし、スレッドスケジューラは他のスレッドの実行でビジーであり、このスレッドの実行を時間内にスケジュールできない場合があります。スレッドスケジューラは、実行のためにスレッドプールからスレッドを選択するFIFO戦略に基づいています。
3.ブロック:さまざまな状況により、スレッドが自動的にブロック状態に切り替わる場合があります。たとえば、I / O操作の待機、ネットワーク接続の待機などです。さらに、現在実行中のスレッドよりも優先度の高いスレッドがあると、実行中のスレッドがブロック状態になる可能性があります。
4.待機中:同期ブロック内の同期されたオブジェクトの待機メソッドを呼び出すと、現在のスレッドが待機状態になります。別のスレッドの同じオブジェクトが同期されている同期ブロックでnotify()/ notifyAll()を呼び出すと、待機中のスレッドが実行可能状態になる可能性があります。
5.Timed_Waiting:待機状態と同じですが、時間制限があります。タイムアウトが経過すると、スレッドは自動的に実行可能状態になります。
6.終了:スレッドのrun()メソッドが実行された後、またはrun()メソッドが異常終了した後、スレッドは終了状態になります。
マルチスレッドを使用する理由
一般的には、複数のスレッドを使用して複数のことを同時に実行し、Javaアプリケーションをより高速に実行し、スレッドを使用して並列処理と並行処理を実装します。今日のCPUはマルチコアであり、周波数が高いため、シングルスレッドを使用すると、マルチコアCPUの利点が十分に活用されません。
重要な利点
- CPUをより有効に活用できる
- 応答性に関連するユーザーエクスペリエンスを向上させることができます
- 応答時間を短縮できます
- 同時に複数のクライアントにサービスを提供できます
スレッドを作成する方法は2つあります
1. Threadクラスを継承してスレッドを作成します。
この継承されたクラスは、Threadクラスのrun()メソッドをオーバーライドします。スレッドの実際の実行はrun()メソッド内から開始され、このスレッドのrun()メソッドはstart()メソッドを介して呼び出されます。
public class MultithreadDemo extends Thread {
@Override
public void run() {
try {
System.out.println("线程 " + Thread.currentThread().getName() + " 现在正在运行");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MultithreadDemo multithreadDemo = new MultithreadDemo();
multithreadDemo.start();
}
}
}
2. Runnableインターフェイスを実装してスレッドを作成します。java.lang.Runnableインターフェイス
を実装する新しいクラスを作成し、そのrun()メソッドを実装します。次に、Threadオブジェクトをインスタンス化し、このオブジェクトのstart()メソッドを呼び出します。
public class MultithreadDemo implements Runnable {
@Override
public void run() {
try {
System.out.println("线程 " + Thread.currentThread().getName() + " 现在正在运行");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new MultithreadDemo());
thread.start();
}
}
}
2つの作成方法の比較
- クラスがThreadクラスを継承する場合、このクラスは他のクラスを継承できません。Javaは単一の継承であるため、複数のクラスを同時に継承することはできません。複数の継承はインターフェイスのみを使用でき、クラスは複数のインターフェイスを実装できます。したがって、実際には、Threadクラスを継承するよりもRunnableインターフェイスを使用する方が適切です。
- 最初の作成方法では、yield()、interrupt()、および一般的に使用されない可能性のあるその他のメソッドをオーバーライドできます。ただし、2番目のメソッドを使用してスレッドを作成する場合、yield()などのメソッドを書き換えることはできません。
同期する
同期はマルチスレッドの条件下でのみ意味があり、一度に1つのスレッドのみが同期ブロックを実行できます。
Java自体はマルチスレッド言語であるため、Javaでは同期の概念が非常に重要です。マルチスレッド環境では、適切な同期が非常に重要です。
同期を使用する理由
マルチスレッド環境でコードを実行するには、オブジェクトに複数のスレッドからアクセスできる場合、オブジェクトの状態またはプログラムの実行でエラーが発生しないように、このオブジェクトの同期を使用する必要があります。
同期の概念に飛び込む前に、同期関連の問題を見てみましょう。
class Production {
//没有做方法同步
void printProduction(int n) {
for (int i = 1; i <= 5; i++) {
System.out.print(n * i+" ");
try {
Thread.sleep(400);
} catch (Exception e) {
System.out.println(e);
}
}
}
}
class MyThread1 extends Thread {
Production p;
MyThread1(Production p) {
this.p = p;
}
public void run() {
p.printProduction(5);
}
}
class MyThread2 extends Thread {
Production p;
MyThread2(Production p) {
this.p = p;
}
public void run() {
p.printProduction(100);
}
}
public class SynchronizationTest {
public static void main(String args[]) {
Production obj = new Production(); //多线程共享同一个对象
MyThread1 t1 = new MyThread1(obj);
MyThread2 t2 = new MyThread2(obj);
t1.start();
t2.start();
}
}
上記のコードを実行した後、同期を追加しなかったため、実行結果が非常に混乱していることがわかります。
出力:100 5
10200 15300 20400 25500次に、printProductionメソッドに同期を追加します。
class Production {
//做了方法同步
synchronized void printProduction(int n) {
for (int i = 1; i <= 5; i++) {
System.out.print(n * i+" ");
try {
Thread.sleep(400);
} catch (Exception e) {
System.out.println(e);
}
}
}
}
printProduction()に同期を追加するときに、すでに実行中のスレッドがある場合、このメソッドを再度実行できるスレッドはありません。今回は同期後の出力結果を順番に追加します。
出力:5 10 15 20
25100200300400500同期メソッドと同様に、Javaクラスとオブジェクトを同期することもできます。
注:実際、メソッド全体を同期する必要がない場合もあります。パフォーマンス上の理由から、実際には、同期する必要があるメソッドのコードの一部のみを同期できます。同期されるコードの部分は、メソッドの同期ブロックです。
シリアル化
Javaシリアル化は、Javaオブジェクトをバイトストリームに変換するためのメカニズムです。バイトストリームからJavaオブジェクトへの変換は、逆シリアル化と呼ばれ、シリアル化の逆の操作です。
シリアル化と逆シリアル化はプラットフォームに依存しません。つまり、Linuxシステムでシリアル化してから、Windowsオペレーティングシステムで逆シリアル化できます。
オブジェクトをシリアル化する場合は、ObjectOutputStreamクラスのwriteObject()メソッドを使用する必要があります。デシリアライズする場合は、ObjectOutputStreamクラスのreadObject()メソッドを使用する必要があります。
次の図に示すように、オブジェクトはバイトストリームに変換され、さまざまなメディアに保存されます。このプロセスはシリアル化です。図の右側では、バイトストリームがメモリなどのさまざまなメディアから取得され、オブジェクトに変換されていることもわかります。これは逆シリアル化と呼ばれます。
シリアル化を使用する理由
Javaオブジェクトを作成すると、プログラムの実行または終了後にこのオブジェクトの状態が消え、保存されません。
したがって、このタイプの問題を解決するために、Javaはシリアル化メカニズムを提供します。このようにして、オブジェクトの状態を一時的に保存または永続化できるため、後でこのオブジェクトが必要になったときに、逆シリアル化によってオブジェクトを復元できます。
シリアル化の方法を確認するためのコードを次に示します。
import java.io.Serializable;
public class Player implements Serializable {
private static final long serialVersionUID = 1L;
private String serializeValueName;
private transient String nonSerializeValuePos;
public String getSerializeValueName() {
return serializeValueName;
}
public void setSerializeValueName(String serializeValueName) {
this.serializeValueName = serializeValueName;
}
public String getNonSerializeValueSalary() {
return nonSerializeValuePos;
}
public void setNonSerializeValuePos(String nonSerializeValuePos) {
this.nonSerializeValuePos = nonSerializeValuePos;
}
@Override
public String toString() {
return "Player [serializeValueName=" + serializeValueName + "]";
}
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializingObject {
public static void main(String[] args) {
Player playerOutput = null;
FileOutputStream fos = null;
ObjectOutputStream oos = null;
playerOutput = new Player();
playerOutput.setSerializeValueName("niubi");
playerOutput.setNonSerializeValuePos("x:1000,y:1000");
try {
fos = new FileOutputStream("Player.ser");
oos = new ObjectOutputStream(fos);
oos.writeObject(playerOutput);
System.out.println("序列化数据被存放至Player.ser文件");
oos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
出力:シリアル化されたデータはPlayer.serファイルに保存されます
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class DeSerializingObject {
public static void main(String[] args) {
Player playerInput = null;
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream("Player.ser");
ois = new ObjectInputStream(fis);
playerInput = (Player) ois.readObject();
System.out.println("从Player.ser文件中恢复");
ois.close();
fis.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("player名字为 : " + playerInput.getSerializeValueName());
System.out.println("player位置为 : " + playerInput.getNonSerializeValuePos());
}
}
出力:
从Player.ser文件中恢复
player名字为 : niubi
player位置为 : null
主な機能
1.親クラスがSerializableインターフェースを実装している場合、サブクラスはSerializableインターフェースを実装する必要はありません。しかし、その逆は機能しません。
2.
シリアル化は非静的メンバー変数のみをサポートします。3。静的に変更された変数と定数および一時的に変更された変数はシリアル化されません。したがって、一部の非静的メンバー変数をシリアル化したくない場合は、transientを使用してそれらを変更します。
4.オブジェクトを逆シリアル化する場合、オブジェクトのコンストラクターは呼び出されません。
5.オブジェクトがシリアル化されるオブジェクトによって参照される場合、オブジェクトもシリアル化され、オブジェクトはSerializableインターフェースも実装する必要があります。
総括する
まず、匿名クラスの定義、使用シナリオ、使用方法を紹介しました。
次に、マルチスレッドとそのライフサイクル、およびマルチスレッドの使用シナリオについて説明しました。
もう一度、同期を理解します。同期を知った後、同期されたメソッドまたはコードブロックを同時に実行できるのは1つのスレッドだけです。スレッドが同期コードを実行している場合、他のスレッドは、同期コードを実行しているスレッドがリソースを解放するまでキューで待機することしかできません。
最後に、シリアル化とは、後で使用するためにオブジェクトの状態を保存することです。