ディレクトリ
記事のこのシリーズは、GitHubのの「Javaのインタビューガイド」の倉庫に私に手配します、よりエキサイティングなコンテンツが私の倉庫のビューにアクセスしてください
https://github.com/h2pl/Java-Tutorial
そのトラブルのスポットスターカザフスタン下のように
記事は、最初に私の個人ブログに登場しました。
www.how2playlife.com
この記事では、ネットワークから、この記事の一部は、この記事の主題に明確かつ徹底的に話す、と私は技術のブログのコンテンツの優れた統合をたくさん考えている「のJava Foundationシリーズボーエンを強化する」Javaテクノロジのいんちきは、[の]マイクロチャンネル公共番号ですその侵害は、作者に連絡してください場合は、良いブログの記事の数を挙げています。
、全体のJavaテクノロジー・システムのより完全な理解は、自分自身を形成するためにこのブログのシリーズは、高度にエントリから、ステップバイステップでは、Javaの基本を学習する方法をお見せし、実際の戦闘を開始します、その後、各ポイントの原理を実現するためにJavaの知識を理解します知的枠組み。あなたの学習成果を要約し、テストするために、このシリーズは面接の質問に対応するすべての知識ポイントを提供し、答えを提案します。
あなたがこの記事のシリーズのための提案を持っている、またはご質問がある場合は、あなたが公共の川や湖[] Javaテクノロジ作者に連絡するために多くの懸念もすることができます、あなたはこのブログシリーズの作成・改訂に参加することを歓迎しています。
Javaで最後のキーワードが広く使用されている、あなたはメンバ変数、メソッド、クラス、ローカル変数を宣言することができます。参照がfinalとして宣言されたら、参照を変更することはできません。最後のキーワードは、このブログでは、最終的なキーワードの特性を説明するためにJavaのメモリレベルからの同期を確保することで、同期メモリを保証することができます。インタビューの内容も発生する可能性があります。
最終使用
最終的変数
クラス定数として使用される最終的な変数メンバー変数またはローカル変数(メソッド内のローカル変数)、およびしばしば最終の静的クラスのメンバーで一緒に使用されます。どのクラス定数は、それらが宣言されているとき、定数の最後のメンバーがコンストラクタで初期化することができます初期化する必要があります。
public class Main {
public static final int i; //报错,必须初始化 因为常量在常量池中就存在了,调用时不需要类的初始化,所以必须在声明时初始化
public static final int j;
Main() {
i = 2;
j = 3;
}
}
その上で述べたように、クラス定数のために、定数プールは、JVMにキャッシュされ、変数を読み込むときには、クラスをロードしません。
public class Main {
public static final int i = 2;
Main() {
System.out.println("调用构造函数"); // 该方法不会调用
}
public static void main(String[] args) {
System.out.println(Main.i);
}
}
変数と参照の基本データ型の最終修正
@Test
public void final修饰基本类型变量和引用() {
final int a = 1;
final int[] b = {1};
final int[] c = {1};
// b = c;报错
b[0] = 1;
final String aa = "a";
final Fi f = new Fi();
//aa = "b";报错
// f = null;//报错
f.a = 1;
}
最後の方法は、メソッドがクラスメソッドをオーバーライドするキルトできないことを示し、方法は、最終として宣言され、コンパイル時に静的結合されており、実行時に動的バインディングを必要としません。場合は、メソッド呼び出しの最後の使用はinvokespecial命令です。
class PersonalLoan{
public final String getName(){
return"personal loan”;
}
}
class CheapPersonalLoan extends PersonalLoan{
@Override
public final String getName(){
return"cheap personal loan";//编译错误,无法被重载
}
public String test() {
return getName(); //可以调用,因为是public方法
}
}
finalクラス
最後のクラスが継承することはできません、既定のメソッド最後のクラスは、最終的なタイプ、Stringクラスとクラスになり、最終的にある整数型のJavaです。
class Si{
//一般情况下final修饰的变量一定要被初始化。
//只有下面这种情况例外,要求该变量必须在构造方法中被初始化。
//并且不能有空参数的构造方法。
//这样就可以让每个实例都有一个不同的变量,并且这个变量在每个实例中只会被初始化一次
//于是这个变量在单个实例里就是常量了。
final int s ;
Si(int s) {
this.s = s;
}
}
class Bi {
final int a = 1;
final void go() {
//final修饰方法无法被继承
}
}
class Ci extends Bi {
final int a = 1;
// void go() {
// //final修饰方法无法被继承
// }
}
final char[]a = {'a'};
final int[]b = {1};
final class PersonalLoan{}
class CheapPersonalLoan extends PersonalLoan { //编译错误,无法被继承
}
@Test
public void final修饰类() {
//引用没有被final修饰,所以是可变的。
//final只修饰了Fi类型,即Fi实例化的对象在堆中内存地址是不可变的。
//虽然内存地址不可变,但是可以对内部的数据做改变。
Fi f = new Fi();
f.a = 1;
System.out.println(f);
f.a = 2;
System.out.println(f);
//改变实例中的值并不改变内存地址。
Fi ff = f;
//让引用指向新的Fi对象,原来的f对象由新的引用ff持有。
//引用的指向改变也不会改变原来对象的地址
f = new Fi();
System.out.println(f);
System.out.println(ff);
}
知識finalキーワード
- 彼らは、宣言またはコンストラクタで初期化されている場合、最終的なメンバ変数は、それ以外の場合は、コンパイルエラーを報告し、初期化する必要があります。最後の変数が初期化されるように再び割り当てることができません。
- ローカル変数は、宣言の時に割り当てる必要があります。プロセスが初期化されていないので
- すべての変数で匿名クラスでは、最終的な変数でなければなりません。
- 最終的な方法は、書き換えができないfinalクラスを継承することはできません
- インタフェース自体で宣言されたすべての変数は最終です。匿名クラスと同様に
- 最終的抽象の両方のキーワードが反比例している、最後のクラスは抽象的なことはできません。
- コンパイル時に、最終的なメソッド結合、静的バインディング(静的バインディング)。
- クラス、メソッド、finalとして宣言された変数は、パフォーマンスを向上させるため、JVMを推定する機会があり、その後、最適化することができます。
最終的なアプローチの利点:
- パフォーマンスを向上させる、JVMは、一定の変数バッファプールの決勝になります
- 追加の同期オーバーヘッドなしに、同時に安全な複数のスレッドで、最終的な変数
- 最後の方法静的にコンパイルされ、コールレートの増加
- 最後のクラスオブジェクトのみが読める作成され、安全に複数のスレッド間で共有することができます
ベストプラクティスfinalキーワード
最終的用法
図1に示すように、一定のための最終的な平均値は、例えば、最終INT I = 100、変更することができません。iの値は常に100です。
なく、同じ変数に対して、この基準は、例えば、最終的なファイルf =新しいファイル、変更されない特定する(「 C:\ test.txtの」)。
自体fの方法であって、このようなリードを変更することを許可するかどうかのようなメンバ変数を変更した場合Fは、これは、変更することができない必要があります。鮮やかなメタファーがあります:女性は彼女の夫は、最終的な定義、職業や収入の夫は変更することが許可されていますが、この女性は、彼女の夫は変更されません。
最終についてのブランク
それぞれ、静的変数、ローカル変数やインスタンス変数、定数の三種類:三つの変数に、最終的な変更をあります。
最後の変数が定義されたほか、では、あなたが宣言することができ、また、ブランクfinal変数として知られている初期値を、与えることなく、どんな状況、コンパイラは空白最終的には、使用する前に初期化されなければならないことを保証しません。
しかし、最終的なブランクは、そのために、オブジェクトのクラスの最終的なデータ・メンバが異なるに従って達成することができ、最終的なキーワード、最終の利用において大きな柔軟性を提供し、その一定の機能が維持されます。
public class FinalTest {
final int p;
final int q=3;
FinalTest(){
p=1;
}
FinalTest(int i){
p=i;//可以赋值,相当于直接定义p
q=i;//不能为一个final变量赋值
}
}
最後のメモリ割り当て
ただ、ここで詳細に発売建てメカニズムを、言及。
知るために、時間そのものの機能を実行することに加えて、関数を呼び出すだけでなく、この機能(クラス内部マッピングテーブル関数のシグネチャと関数のアドレス)を探すために余分な時間を必要としています。したがって、性能の消費を低減することに等価である関数呼び出しの数を減少させます。
最終変更機能がコンパイラ最適化され、最適化の結果は、関数呼び出しの回数を減らすことです。どのように達成するために、たとえばあなたを表示するには:
public class Test{
final void func(){System.out.println("g");};
public void main(String[] args){
for(int j=0;j<1000;j++)
func();
}}
经过编译器优化之后,这个类变成了相当于这样写:
public class Test{
final void func(){System.out.println("g");};
public void main(String[] args){
for(int j=0;j<1000;j++)
{System.out.println("g");}
}}
右、違いを参照してください?コンパイラは、直接、関数funcローカル呼び出し元の関数の本体に埋め込まこれはバイトコードに、当然のことながら、千回の関数呼び出しの節約にコンパイラプロセスを結果が、我々はそれに見て、このように想像することができます。
コンパイラインラインコードの長さが大幅に増加した後、このように時間そのJVMバイトコードの解釈を増加しかし、身体の機能が長すぎる場合、それは、最終と逆であってもよいです。
最終的な修正法を使用する場合、コンパイラは、速度と効率を向上させることを発呼者にコードを挿入する方法を最終的に変更されるが、メソッド本体の最終的な変形が大きすぎることができない、コンパイラは、インラインを放棄することができますしかし、どのような方法で多くをあきらめるだろう、私は計算のテストを行っていません。
次の二つの質問は述べ継続するコンテンツです
スピードと効率を向上させる最終修正方法を使用します
私は5回実行されます、次のテストコードを参照してください。
public class Test
{
public static void getJava()
{
String str1 = "Java ";
String str2 = "final ";
for (int i = 0; i < 10000; i++)
{
str1 += str2;
}
}
public static final void getJava_Final()
{
String str1 = "Java ";
String str2 = "final ";
for (int i = 0; i < 10000; i++)
{
str1 += str2;
}
}
public static void main(String[] args)
{
long start = System.currentTimeMillis();
getJava();
System.out.println("调用不带final修饰的方法执行时间为:" + (System.currentTimeMillis() - start) + "毫秒时间");
start = System.currentTimeMillis();
String str1 = "Java ";
String str2 = "final ";
for (int i = 0; i < 10000; i++)
{
str1 += str2;
}
System.out.println("正常的执行时间为:" + (System.currentTimeMillis() - start) + "毫秒时间");
start = System.currentTimeMillis();
getJava_Final();
System.out.println("调用final修饰的方法执行时间为:" + (System.currentTimeMillis() - start) + "毫秒时间");
}
}
结果为:
第一次:
调用不带final修饰的方法执行时间为:1732毫秒时间
正常的执行时间为:1498毫秒时间
调用final修饰的方法执行时间为:1593毫秒时间
第二次:
调用不带final修饰的方法执行时间为:1217毫秒时间
正常的执行时间为:1031毫秒时间
调用final修饰的方法执行时间为:1124毫秒时间
第三次:
调用不带final修饰的方法执行时间为:1154毫秒时间
正常的执行时间为:1140毫秒时间
调用final修饰的方法执行时间为:1202毫秒时间
第四次:
调用不带final修饰的方法执行时间为:1139毫秒时间
正常的执行时间为:999毫秒时间
调用final修饰的方法执行时间为:1092毫秒时间
第五次:
调用不带final修饰的方法执行时间为:1186毫秒时间
正常的执行时间为:1030毫秒时间
调用final修饰的方法执行时间为:1109毫秒时间
由以上运行结果不难看出,执行最快的是“正常的执行”即代码直接编写,而使用final修饰的方法,不像有些书上或者文章上所说的那样,速度与效率与“正常的执行”无异,而是位于第二位,最差的是调用不加final修饰的方法。
表示:それなしでより良いが追加されました。
変更された変数を使用すると、最終的な変数値がそれを変更することはできませんします。
コードを参照してください。
public class Final
{
public static void main(String[] args)
{
Color.color[3] = "white";
for (String color : Color.color)
System.out.print(color+" ");
}
}
class Color
{
public static final String[] color = { "red", "blue", "yellow", "black" };
}
执行结果:
red blue yellow white
看!,黑色变成了白色。
使用FindBugsのにパブリック静的文字列[]色= {求められますプラグ ;、「赤」「青」、「黄」、「黒」} 、このラインは安全でないが、最終的な変形例を行変数の値が何も最終的には変更されないことがないようにするので、コードは、まだ安全ではありません!その理由は次のとおりです。変数自体は新しい値を割り当てることはできませんが、変数の内部構造が変更されていないことを保証することはできませんし、最終的なキーワードのみを保証することができます。例えば、次のコードColor.color =新しいString mainメソッドは、[] {「」};エラーとなります。
どのように内部の配列を確保するために変更されることはありません
那可能有的同学就会问了,加上final关键字不能保证数组不会被外部修改,那有什么方法能够保证呢?答案就是降低访问级别,把数组设为private。这样的话,就解决了数组在外部被修改的不安全性,但也产生了另一个问题,那就是这个数组要被外部使用的。
この問題を解決するためのコードを参照してください。
import java.util.AbstractList;
import java.util.List;
public class Final
{
public static void main(String[] args)
{
for (String color : Color.color)
System.out.print(color + " ");
Color.color.set(3, "white");
}
}
class Color
{
private static String[] _color = { "red", "blue", "yellow", "black" };
public static List<String> color = new AbstractList<String>()
{
@Override
public String get(int index)
{
return _color[index];
}
@Override
public String set(int index, String value)
{
throw new RuntimeException("为了代码安全,不能修改数组");
}
@Override
public int size()
{
return _color.length;
}
};
}
依然として配列要素がアクセスさせながらそうOK、双方は、コードのセキュリティを確保します。
3つのルール最終的なアプローチ
ルール1:最終修正方法をオーバーライドすることはできません。
ルール2:最終的修飾法だけで書き換えることはできませんが、それが過負荷状態にすることができます。
ルール3:書き換えされていない親クラスの民間最終方法、サブクラスで再定義することができ、。
サンプルコード
规则1代码
public class FinalMethodTest
{
public final void test(){}
}
class Sub extends FinalMethodTest
{
// 下面方法定义将出现编译错误,不能重写final方法
public void test(){}
}
规则2代码
public class Finaloverload {
//final 修饰的方法只是不能重写,完全可以重载
public final void test(){}
public final void test(String arg){}
}
规则3代码
public class PrivateFinalMethodTest
{
private final void test(){}
}
class Sub extends PrivateFinalMethodTest
{
// 下面方法定义将不会出现问题
public void test(){}
}
最終とJVMの間の関係
前述のロックおよび揮発性と比較すると、通常の変数アクセスのような、最終的なフィールドを読み書きします。最後のドメインの場合、コンパイラやプロセッサは、2つの並べ替えルールに準拠します
- 最後のフィールドのコンストラクタに書き込まれ、その後、オブジェクトは、これら二つの動作の間に再配列しない、参照変数に割り当てられたこの構成を参照。
- 最終の参照を含む第一の標的ドメインを読み取り、次いで、最終的な最初のフィールドを読み取り、並べ替えは、これら2つの操作の間はありません。
以下では、これら2つのルールを説明するために、いくつかのサンプルコードを渡します。
FinalExampleクラス{公共 のint I; //共通変数 最終int型のJ; //最終的な変数 、静的FinalExample OBJ; ` 公共ボイドFinalExample(){//コンストラクタ I = 1; //共通ドメインライト J = 2; / /最終フィールドを書き込む } パブリック静的ボイドライター(){//実行スレッドA書き込み )OBJ =新しい新しいFinalExampleを(; } パブリック静的ボイドリーダー(){//読み取りスレッドB実行 FinalExampleオブジェクトを= OBJ; //読み取りオブジェクト参照 INT A = object.i; //読み取り通常ドメイン INT B = object.j; //読み取り最終フィールド } ` }
別のスレッドBが続くライタースレッドAの実行()メソッドは、リーダー()メソッドを実行することを想定しています。我々の下にはこれら2つのルールを説明するために、これらの2つのスレッドを介して相互作用。
並べ替えルールに、最終的なドメインを書きます
書かれた最後のフィールドの並べ替え規則は、コンストラクタの外に最後のフィールドを並べ替えの書き込みを禁止しています。この実装ルールには、次の2つの側面が含まれています。
- JMMは、コンストラクタの外に最後のフィールドを並べ替え書き込むようにコンパイラを禁止しています。
- コンパイラは最後のフィールドの後に書きます前に、コンストラクタはStoreStore障壁を挿入し、返します。この障壁は、コンストラクタの外に最後のフィールドを並べ替え書き込むためのプロセッサを禁止されています。
私たちは今、作家()メソッドを分析してみましょう。finalExampleは=新しいFinalExample():ライター()メソッドは、コードの唯一の行を含みます。このコードは、2つのステップを含みます。
- オブジェクト型構築FinalExample。
- 参照変数objに参照オブジェクトの割り当て。
スレッドBは、以下の、(すぐになぜこの仮定を説明する)は、オブジェクト参照の並べ替えを負いませんし、ドメインオブジェクトのメンバーの間で読ま読み込み可能な実行順序であります:
図では、書き込み動作共通ドメインが推奨され、スレッドBの外側コンストラクタをコンパイルするために命じ変数iの初期化の前に、普通誤った読みの値を読み出します。書き込まれた最後のフィールドの動作は、最終的な照合ドメインを再度書き込まれるコンストラクタ内で、スレッドBは、最終的な変数の初期化後に正しい読取り値を読み出し、「定義されています」。
書かれた最後のフィールドの並べ替えルールのことを確認するには:任意のスレッドを基準として見ることができる前に、通常のドメインが対象で、この保証はありませんが、最後のドメインオブジェクトが既に、正しく初期化されています。一例として、リーダースレッドBにオブジェクト参照OBJ、おそらくまだ構築されていないオブジェクトobjは(iは共通のドメインの操作を記述する場合の初期値は2まだ書き込まれていないコンストラクタに並べ替えする「見る」の上図一般的なドメインI)。
ドメインを並べ替え最終規則を読みます
次のように読み取りフィールドの順序を変更することは、最終的なルールは以下のとおりです。
- 1つのスレッドで、最終フィールドの読み出した初期読み出し一次オブジェクト参照は、プロセッサが2つの操作を並べ替えJMM禁止されて、オブジェクトが含まれている(なおだけプロセッサは、このルール)。LoadLoadコンパイラは、最終的な読み取りドメイン操作の前にバリアを挿入します。
最初の読み取りと最終的なオブジェクト参照フィールドを読み取る最初のオブジェクト、二つの操作の間に間接的な依存関係を含んでいます。コンパイラー準拠間接的な依存関係するので、コンパイラは、これら二つの動作の並べ替えを行います。ほとんどのプロセッサは、ほとんどのプロセッサは、これら2つの操作の順序を変更しませんが、間接的に依存遵守します。しかし、プロセッサの数は、オペレータが(例えばアルファプロセッサのような)並び替え間接的依存を行うことができ、このルールは、そのようなプロセッサのために設計されています。
リーダー()メソッドは、3つの操作が含まれています。
- まず、参照変数OBJを読みます。
- オブジェクトに通常のドメイン変数j OBJポイントを読み込むための初期参照。
- まず私は、参照変数ポインティングオブジェクトOBJ finalフィールドをお読みください。
今、私たちは発生プロセッサに間接的に依存して、プログラムの実行の不遵守で、次が可能な実行順序である一方、ライター・スレッドAは、任意の並べ替えを持っていないことを前提としています。
オブジェクトのプロセッサリオーダー参照によって通常のオブジェクトドメインの図では、読み出し動作の読み出しの前に。スレッドAが書かれて書かれていない通常のドメインを、読んで、これは間違った読み出し動作です。最終的なフィールドが最後のフィールドが正しい読み出し動作であるスレッドAを、初期化された時点でオブジェクト参照を、読んだ後に「定義」読み取り、操作対象の最終フィールドを並べ替え規則を読み出します。
対象ドメインの最後の読み取りの前に、我々は最初の最後のフィールドは、オブジェクトの参照が含まれて読んでますことを保証するために、最後のフィールドの並べ替えルールを読みます。参照がnullでない場合は、このサンプル・プログラムでは、そのオブジェクトの参照最終ドメインは、前のスレッドを初期化されている必要があります。
最後のフィールドは、参照型である場合
我々は最後のフィールドは、基礎となるデータ型で、最後のフィールドが参照型である場合を見てみましょう参照の上、どのような効果があるのだろうか?
次のサンプルコードを考えてみましょう。
{クラスFinalReferenceExampleパブリック 最終のint [] intArray; //最終参照型 静的FinalReferenceExampleのOBJ; 公共FinalReferenceExample(){//コンストラクタ intArray =新しい新しいINT [1]; // 1 。intArray [0] = 1; // 2 } パブリック静的ボイドwriterOne(){//スレッドA実行書き込み OBJ =新しい新しいFinalReferenceExample(); // 3 } パブリック静的ボイドwriterTwoを(){//書き込みスレッドBを実行 obj.intArray [0] = 2; // 4 } パブリック静的ボイドリーダー(){//スレッドCが実行読み取り IF(OBJ = NULL!){// 5。 INT obj.intArray TEMP1 = [0]; // 6 } } }
ここで最後のフィールドは、int型の配列オブジェクトを参照する参照型です。参照型の場合は、書き込み磁界最終並べ替えルールコンパイラとプロセッサは、次の制約を追加します。
- ドメインの最終的な基準部材のオブジェクトのコンストラクタ関数で記述され、そしてこれは、これらの二つの操作の間に再配列しない、参照変数に割り当てられたオブジェクトのコンストラクタ関数外部構成で参照されています。
例えば上記の手順は、我々は、スレッドBがwriterTwoを実行した後、最初のスレッドA実行writerOne()メソッドが実行されると仮定する()メソッドは、スレッドCの後に実行されるリーダー()メソッドを実行します。ここでは一つの可能なスレッドの実行タイミングは次のとおりです。
図において、1は最後のフィールドに書き込まれ、2が参照ドメインオブジェクトのドメインの最後のメンバーに書き込まれる、図3は、オブジェクト参照の構造は、参照変数に割り当てられています。ここで図1及び3に加えて図2及び図3を並べ替えることができない、前述の順序を変更することができません。
JMMは、リーダスレッドCは、少なくともドメインのメンバーは、最終のコンストラクタ内のオブジェクトへの参照をスレッド書く見ることができることを確実にします。少なくとも配列に1標識されている参照するに、すなわち0 C。アレイエレメントへの書き込みスレッドBが書き込まれ、リーダスレッドが表示されないことがあり、Cに見えるかもしれません。JMMは、このときの結果は予測不可能であり、読み出されたデータとの間の競合が存在する目に見えるので、書き込みスレッドBはスレッドCを読み取り、スレッドBはスレッドCを記述することが保証されません。
あなたが読み取り、書き込み、スレッドBスレッドCは、書くために配列の要素を参照してくださいことを確認するには、メモリはスレッドの可視性を読むことを確実にするために、スレッドBとCの間で同期プリミティブ(ロックまたは揮発性)を使用する必要があります。
参考記事
https://www.infoq.cn/article/java-memory-model-6
https://www.jianshu.com/p/067b6c89875a
https://www.jianshu.com/p/f68d6ef2dcf0
ます。https:// WWW .cnblogs.com / xiaoxi / P / 6392154.html
https://www.iteye.com/blog/cakin24-2334965
https://blog.csdn.net/chengqiuming/article/details/70139503
ます。https://ブログ。 csdn.net/hupuxiang/article/details/7362267
マイクロチャンネル公衆数
Javaテクノロジの川や湖
SSM、SpringBoot:あなたは私のリアルタイム更新記事とドライの共有に注力したい場合は、川や湖の私の公共の数に焦点を当てることができます] [Javaの技術のJava、テクニカルエンジニアアリステーション、著者黄斜めには、Javaの関連技術に焦点を当てMySQL、分散、ミドルウェア、クラスタ、Linuxでは、ネットワーク、マルチスレッド、時には完全なJavaスタックの開発に取り組ん学習経験を共有するポイントドッカー、ELKだけでなく、ドライ商品や技術を話します!
Javaのエンジニアは、学習リソースを必要と:一部のJavaエンジニアの一般的な学習リソースを、世間の注目の数は、背景がキーワードの返信の「Java」何のルーチンを無料で入手することがありません。
個人公開番号:黄斜め
学び続ける、SpringBoot、MySQLの、分散、ミドルウェア、サービス、だけでなく、投資銀行業務のポイントを理解し、時折話すポイントアルゴリズムとコンピュータの理論的根拠:著者はマスター985、JAVAのバックエンドテクノロジ・スタックに特化したアリゴールドのドレスJAVAエンジニアであり、書き込みは、生涯学習の力を信じて!
プログラマ3T技術学習リソース:リソースまくる技術を学ぶいくつかのプログラマは、世間の注目の数は、背景がキーワード返信「データ」は何のルーチンを無料で入手することはできません。