JavaRush /Java Blog /Random-JA /Java のプリミティブ型: それほどプリミティブではありません
Viacheslav
レベル 3

Java のプリミティブ型: それほどプリミティブではありません

Random-JA グループに公開済み

導入

アプリケーション開発は、データを操作する、またはデータを保存して処理するものと考えることができます。今日は最初の重要な側面について触れたいと思います。Java ではデータはどのように保存されますか? ここでは、参照データ型プリミティブデータ型の2 つの形式が考えられます。プリミティブ型の種類と、それらを扱う可能性について話しましょう (何と言っても、これはプログラミング言語の知識の基礎です)。 Java のプリミティブ データ型は、すべての基礎となります。いいえ、決して誇張しているわけではありません。Oracle には、プリミティブ専用の別のチュートリアルがあります:プリミティブ データ型の Java のプリミティブ型: それほどプリミティブではありません - 1簡単な歴史。最初はゼロでした。でもゼロは退屈だ。そしてビットが現れました。なぜ彼はそう呼ばれたのですか?「 binary digi t 」(二進数)の略語からそう名付けられました。つまり、2つの意味しかありません。ゼロだったので、今は 0 か 1 のどちらかになるのは当然です。そして、人生はより楽しくなりました。破片は群れを成し始めました。そして、これらの群れはバイト(バイト)と呼ばれるようになりました。現代世界では、バイト = 2 の 3 乗、つまり 8. しかし、これが常に当てはまるわけではないことが判明しました。バイトという名前の由来については、多くの推測、伝説、噂があります。すべては当時のエンコーディングに関するものだと考える人もいれば、この方法で情報を読んだ方が有益だと考える人もいます。バイトは、アドレス指定可能なメモリの最小部分です。メモリ内で一意のアドレスを持つバイトです。ByTe は Binary Term (機械語) の略語であるという伝説があります。マシンワード - 簡単に言えば、プロセッサが 1 回の操作で処理できるデータの量です。以前は、マシンワードのサイズは、アドレス指定可能な最小メモリと同じでした。Java では、変数はバイト値のみを格納できます。上で述べたように、Java には 2 種類の変数があります。
  • Java プリミティブ型はデータ バイトの値を直接格納します (これらのプリミティブ型については以下で詳しく説明します)。
  • 参照型は、オブジェクトのアドレスのバイトをヒープに保存します。つまり、これらの変数を通じて、オブジェクト自体に直接アクセスできます (オブジェクトのリモート コントロールのようなもの)。

Javaバイト

したがって、歴史は私たちにバイト、つまり使用できるメモリの最小量を与えてくれました。そしてそれは8ビットで構成されています。Java の最小の整数データ型はバイトです。符号付き 8 ビット型です。それはどういう意味ですか?数えてみましょう。2^8 は 256 です。しかし、負の数が必要な場合はどうすればよいでしょうか? そして Java 開発者は、バイナリ コード「10000000」は -128 を表す、つまり最上位ビット (左端のビット) が数値が負かどうかを示すことになると判断しました。2 進数の「0111 1111」は 127 に相当します。つまり、128 はいかなる方法でも指定できません。-128になります。完全な計算は次の回答に記載されています: Java ではバイトの範囲が -128 から 127 になるのはなぜですか? 数値がどのように取得されるかを理解するには、次の図を見る必要があります。
Java のプリミティブ型: それほどプリミティブではない - 2
したがって、サイズを計算するには、2^(8-1) = 128 となります。これは、最小制限 (マイナスが付きます) が -128 になることを意味します。最大値は 128 – 1 (ゼロを引く) です。つまり、最大値は 127 になります。実際、バイト型を「高レベル」で扱うことはあまりありません。基本的に、これは「生」データの処理です。たとえば、ネットワークを介したデータ送信を扱う場合、データが何らかの通信チャネルを介して送信される 0 と 1 のセットである場合です。または、ファイルからデータを読み取るとき。これらは、文字列やエンコーディングを操作するときにも使用できます。コード例:
public static void main(String []args){
        byte value = 2;
        byte shortByteValue = 0b10; // 2
        System.out.println(shortByteValue);
        // Начиная с JDK7 мы можем разделять литералы подчёркиваниями
        byte minByteValue = (byte) 0B1000_0000; // -128
        byte maxByteValue = (byte) 0b0111_1111; // 127
        byte minusByteValue = (byte) 0b1111_1111; // -128 + 127
        System.out.println(minusByteValue);
        System.out.println(minByteValue + " to " + maxByteValue);
}
ちなみに、バイト型を使えばメモリ消費量が減るとは考えないでください。バイトは主に、データを配列に格納するとき (たとえば、ネットワーク経由で受信したデータをバイト配列として実装されるバッファに格納するとき) にメモリ消費を削減するために使用されます。ただし、データに対して操作を実行する場合、byte を使用すると期待どおりにはなりません。これは、Java 仮想マシン (JVM) の実装によるものです。ほとんどのシステムは 32 ビットまたは 64 ビットであるため、計算中の byte および short は 32 ビット int に変換されます。これについては後で説明します。これにより計算が容易になります。詳細については、「バイトの追加が int に変換されるのは、Java 言語ルールのせいですか、それとも jvm のせいですか?」を参照してください。。回答には、JLS (Java 言語仕様) へのリンクも含まれています。さらに、間違った場所で byte を使用すると、厄介な瞬間が生じる可能性があります。
public static void main(String []args){
        for (byte i = 1; i <= 200; i++) {
            System.out.println(i);
        }
}
ここでループが発生します。カウンタ値が最大値(127)に達したため、オーバーフローが発生し、値は-128となります。そして私たちはそのサイクルから決して抜け出すことはできません。

短い

バイト値の制限は非常に小さいです。したがって、次のデータ型ではビット数を 2 倍にすることにしました。つまり、8 ビットではなく 16、つまり 2 バイトになります。値も同様に計算できます。2^(16-1) = 2^15 = 32768。これは、範囲が -32768 から 32767 であることを意味します。特殊な場合に使用されることはほとんどありません。Java 言語のドキュメントには、「short を使用すると、大きな配列のメモリを節約できます。」と 記載されています。

整数

そこで、最も頻繁に使用されるタイプにたどり着きました。32 ビット、つまり 4 バイトを占めます。一般的には2倍になり続けます。値の範囲は -2^31 ~ 2^31 – 1 です。

最大 int 値

int 2147483648 の最大値は1 であり、決して小さくありません。上で述べたように、計算を最適化するには、最新のコンピューターでは、ビット容量を考慮してカウントする方が便利で、データを暗黙的に int に変換できます。簡単な例を次に示します。
byte a = 1;
byte b = 2;
byte result = a + b;
このような無害なコードですが、「エラー: 互換性のない型: int から byte への不可逆変換の可能性があります。」というエラーが発生します。byte result = (byte)(a + b); に修正する必要があります。そして、もう 1 つの無害な例です。次のコードを実行するとどうなるでしょうか?
int value = 4;
System.out.println(8/value);
System.out.println(9/value);
System.out.println(10/value);
System.out.println(11/value);
そして結論が得られます
2
2
2
2
*パニックの音*
実際のところ、int 値を扱う場合、残りは破棄され、全体の部分だけが残ります (そのような場合は double を使用する方が良いです)。

長さ

引き続き倍増していきます。32 を 2 で乗算すると、64 ビットが得られます。伝統的に、これは 4 * 2、つまり 8 バイトです。値の範囲は -2^63 ~ 2^63 – 1 です。十分すぎるほどです。このタイプでは、大きな数を数えることができます。時間を扱うときによく使用されます。あるいは長距離など。数値が長いことを示すには、数値の後にリテラル L – Long を置きます。例:
long longValue = 4;
longValue = 1l; // Не ошибка, но плохо читается
longValue = 2L; // Идеально
先を行きたいと思います。次に、プリミティブをオブジェクトとして扱うことを可能にする、プリミティブに対応するラッパーがあるという事実を考慮します。しかし、興味深い機能があります。以下に例を示します。同じTutorialspoint オンライン コンパイラを使用して、次のコードを確認できます。
public class HelloWorld {

     public static void main(String []args) {
        printLong(4);
     }

    public static void printLong(long longValue) {
        System.out.println(longValue);
    }
}
このコードはエラーなしで動作し、すべて問題ありません。しかし、printLong メソッドの型が Long から Long に置き換えられると (つまり、型がプリミティブではなくオブジェクトになると)、Java にとってどのパラメータを渡しているのかが不明瞭になります。int が送信されており、エラーが発生すると想定し始めます。したがってメソッドの場合は4Lを明示する必要があります。データベースを操作する場合、long は ID としてよく使用されます。

Java float と Java double

これらの型は浮動小数点型と呼ばれます。つまり、これらは整数型ではありません。float 型は 32 ビット (int と同様)、double は倍精度型と呼ばれるため、64 ビット (好みのように 2 を掛けます) です。例:
public static void main(String []args){
        // float floatValue = 2.3; lossy conversion from double to float
        float floatValue = 2.3F;
        floatValue = 2.3f;
        double doubleValue = 2.3;
        System.out.println(floatValue);
        double cinema = 7D;
}
(型の精度による) 値の違いの例を次に示します。
public static void main(String []args){
        float piValue = (float)Math.PI;
        double piValueExt = Math.PI;
        System.out.println("Float value: " + piValue );
        System.out.println("Double value: " + piValueExt );
 }
これらのプリミティブ型は、たとえば数学で使用されます。これが証明であり、数値 PIを計算するための定数です。まあ、一般的には、Math クラスの API を見ることができます。他に重要で興味深いことは次のとおりです。ドキュメントにも次のように書かれています。「このデータ型は、通貨などの正確な値には決して使用しないでください。」そのためには、代わりに java.math.BigDecimal クラスを使用する必要があります。Numbers と Strings では、BigDecimal および Java プラットフォームによって提供されるその他の便利なクラスについて説明します。」つまり、float および double のお金を計算する必要はありません。NASA での作業の例を使用した精度の例: Java BigDecimal、高精度計算の処理 まあ 、自分で感じてみましょう。
public static void main(String []args){
        float amount = 1.0000005F;
        float avalue = 0.0000004F;
        float result = amount - avalue;
        System.out.println(result);
}
この例に従って、数字の 5 と 4 の前に 0 を追加します。すると、すべての恐怖がわかります) このトピックに関するロシア語の float と double に関する興味深いレポートがあります : https://youtu.be/1RCn5ruN1fk BigDecimal の使用方法はここで参照できます: BigDecimal でセントを作成する ところで、float と double は単なる数値以上のものを返すことができます。たとえば、以下の例は Infinity を返します。
public static void main(String []args){
        double positive_infinity = 12.0 / 0;
        System.out.println(positive_infinity);
}
そしてこれは NAN を返します:
public static void main(String []args){
        double positive_infinity = 12.0 / 0;
        double negative_infinity = -15.0 / 0;
        System.out.println(positive_infinity + negative_infinity);
}
無限については明らかです。NaNとは何ですか? これはNot a numberです。つまり、結果は計算できず、数値ではありません。以下に例を示します。-4 の平方根を計算したいとします。4 の平方根は 2 です。つまり、2 を 2 乗すると 4 が得られます。-4 を得るには何を 2 乗しなければなりませんか? それはうまくいきません、なぜなら... 正の数がある場合、それは残ります。そして、それがマイナスだった場合、マイナス×マイナスでプラスになります。つまり、計算可能ではありません。
public static void main(String []args){
        double sqrt = Math.sqrt(-4);
        System.out.println(sqrt + 1);
        if (Double.isNaN(sqrt)) {
           System.out.println("So sad");
        }
        System.out.println(Double.NaN == sqrt);
}
ここでは、浮動小数点数のトピックに関するもう 1 つの優れた概要を示します。あなたの論点はどこですか?
他に読むべきもの:

Javaブール値

次の型は Boolean (論理型) です。キーワードである true または false の値のみを受け入れることができます。while ループなどの論理演算や、if、switch を使用した分岐で使用されます。ここでどんな興味深いことがわかるでしょうか? たとえば、理論的には、0 または 1、つまり true または false の 1 ビットの情報だけが必要です。しかし実際には、Boolean はより多くのメモリを消費し、これは特定の JVM 実装に依存します。通常、これのコストは int と同じです。もう 1 つのオプションは、BitSet を使用することです。以下は Java Fundamentals の本からの簡単な説明です: BitSet

Java 文字

これで、最後のプリミティブ型に到達しました。したがって、char のデータは 16 ビットを占め、文字を記述します。Java は char に Unicode エンコーディングを使用します。シンボルは 2 つの表に従って設定できます (ここで確認できます)。
  • Unicode 文字テーブル
  • ASCII文字テーブル
Java のプリミティブ型: それほどプリミティブではない - 3
スタジオでの例:
public static void main(String[] args) {
    char symbol = '\u0066'; // Unicode
    symbol = 102; // ASCII
    System.out.println(symbol);
}
ちなみに、char は本質的に数値であるため、合計などの数学演算をサポートします。そして、これが面白い結果を招くこともあります。
public class HelloWorld{

    public static void main(String []args){
        String costForPrint = "5$";
        System.out.println("Цена только для вас " +
        + costForPrint.charAt(0) + getCurrencyName(costForPrint.charAt(1)));
    }

    public static String getCurrencyName(char symbol) {
        if (symbol == '$') {
            return " долларов";
        } else {
            throw new UnsupportedOperationException("Not implemented yet");
        }
    }

}
チュートリアルポイント から オンライン IDE をチェックアウトすることを強くお勧めします。あるカンファレンスでこのパズルを見たとき、私の気持ちは高揚しました。この例も気に入っていただければ幸いです) 更新:これは Joker 2017 でのレポートです: 「Java Puzzlers NG S03 - 皆さんはどこから来たのですか?!

リテラル

リテラルは明示的に指定された値です。リテラルを使用すると、さまざまな数値体系で値を指定できます。
  • 10進法:10
  • 16 進数: 0x1F4、0x で始まる
  • 8 進数: 010、ゼロから始まります。
  • バイナリ システム (Java7 以降): 0b101、0b から始まります
面白いので、8 進法についてもう少し詳しく説明します。
int costInDollars = 08;
このコード行はコンパイルされません。
error: integer number too large: 08
それはナンセンスのように思えます。ここで、二進法と八進法について思い出してみましょう。二進法には 2 はありません。値は 2 つあります (0 から始まります)。また、8 進法には 0 から始まる 8 つの値があります。つまり、値 8 自体は存在しません。したがって、これは一見すると不合理に見える誤りです。覚えておいていただきたいのは、値を翻訳するための「フォローアップ」ルールは次のとおりです。
Java のプリミティブ型: それほどプリミティブではない - 4

ラッパークラス

Java のプリミティブには、オブジェクトとして操作できるように独自のラッパー クラスがあります。つまり、プリミティブ型ごとに、対応する参照型が存在します。 Java のプリミティブ型: それほどプリミティブではない - 5ラッパー クラスは不変です。これは、オブジェクトが作成されると、その状態 (値フィールドの値) を変更できないことを意味します。ラッパー クラスは、final: オブジェクトとして宣言され、いわば読み取り専用です。これらのクラスから継承することはできないことにも言及したいと思います。Java は、プリミティブ型とそのラッパーの間で自動的に変換を行います。
Integer x = 9;          // autoboxing
int n = new Integer(3); // unboxing
プリミティブ型を参照型 (int->Integer) に変換するプロセスはautoboxingと呼ばれ、その逆はunboxingと呼ばれます。これらのクラスを使用すると、オブジェクト内にプリミティブを保存できるようになり、オブジェクト自体がオブジェクトのように (つまり、他のオブジェクトと同様に) 動作します。これらすべてにより、数値の比較、記号の大文字と小文字の変換、記号が文字か数値かの判断、最小数値の検索など、さまざまで便利な静的メソッドが多数得られます。提供される機能セットは、ラッパー自体にのみ依存します。int のラッパーの独自の実装の例:
public class CustomerInt {

   private final int value;

   public CustomerInt(int value) {
       this.value = value;
   }

   public int getValue() {
       return value;
   }
}
メインパッケージの java.lang には、Boolean、Byte、Short、Character、Integer、Float、Long、Double クラスの実装がすでに含まれているため、独自のものを作成する必要はなく、既成のパッケージを再利用するだけです。もの。たとえば、このようなクラスを使用すると、リストなどを作成することができます。 なぜなら、List にはオブジェクトのみが含まれるべきであり、プリミティブには含まれないからです。プリミティブ型の値を変換するには、静的な valueOf メソッドがあります。たとえば、Integer.valueOf(4) は Integer 型のオブジェクトを返します。逆変換には、intValue()、longValue() などのメソッドがあります。コンパイラは独自に valueOf および *Value への呼び出しを挿入します。これが自動ボックス化と自動アンボックス化の本質です。上記の自動パッキングと自動アンパッキングの例は、実際には次のようになります。
Integer x = Integer.valueOf(9);
int n = new Integer(3).intValue();
自動パックと自動アンパックの詳細については、 この記事を参照してください。

キャスト

При работе с примитивами существует такое понятие How приведение типов, одно из не очень приятных свойств C++, тем не менее приведение типов сохранено и в языке Java. Иногда мы сталкиваемся с такими ситуациями, когда нам нужно совершать взаимодействия с данными разных типов. И очень хорошо, что в некоторых ситуациях это возможно. В случае с ссылочными переменными, там свои особенности, связанные с полиморфизмом и наследованием, но сегодня мы рассматриваем простые типы и соответственно приведение простых типов. Существует преобразование с расширением и преобразование сужающее. Всё на самом деле просто. Если тип данных становится больше (допустим, был int, а стал long), то тип становится шире (из 32 бит становится 64). И в этом случае мы не рискуем потерять данные, т.к. если влезло в int, то в long влезет тем более, поэтому данное приведение мы не замечаем, так How оно осуществляется автоматически. А вот в обратную сторону преобразование требует явного указания от нас, данное приведение типа называется — сужение. Так сказать, чтобы мы сами сказали: «Да, я даю себе отчёт в этом. В случае чего — виноват сам».
public static void main(String []args){
   int intValue = 128;
   byte value = (byte)intValue;
   System.out.println(value);
}
Whatбы потом в таком случае не говорor что «Ваша Джава плохая», когда получат внезапно -128 instead of 128 ) Мы ведь помним, что в byteе 127 верхнее meaning и всё что находилось выше него соответственно можно потерять. Когда мы явно превратor наш int в byte, то произошло переполнение и meaning стало -128.

Область видимости

Это то место в codeе, где данная переменная будет выполнять свои функции и хранить в себе Howое-то meaning. Когда же эта область закончится, переменная перестанет существовать и будет стерта из памяти и. How уже можно догадаться, посмотреть or получить ее meaning будет невозможно! Так что же это такое — область видимости? Java のプリミティブ型: それほどプリミティブではない - 6Область определяется "блоком" — вообще всякой областью, замкнутой в фигурные скобки, выход за которые сулит удаление данных объявленных в ней. Или How минимум — сокрытие их от других блоков, открытых вне текущего. В Java область видимости определяется двумя основными способами:
  • Классом.
  • Методом.
Как я и сказал, переменная не видна codeу, если она определена за пределами блока, в котором она была инициализирована. Смотрим пример:
int x;
x = 6;
if (x >= 4) {
   int y = 3;
}
x = y;// переменная y здесь не видна!
И How итог мы получим ошибку:

Error:(10, 21) java: cannot find symbol
  symbol:   variable y
  location: class com.javaRush.test.type.Main
Области видимости могут быть вложенными (если мы объявor переменную в первом, внешнем блоке, то во внутреннем она будет видна).

Заключение

Сегодня мы познакомorсь с восемью примитивными типами в Java. Эти типы можно разделить на четыре группы:
  • Целые числа: byte, short, int, long — представляют собой целые числа со знаком.
  • Числа с плавающей точкой — эта группа включает себе float и double — типы, которые хранят числа с точностью до определённого знака после запятой.
  • Булевы значения — boolean — хранят значения типа "истина/ложь".
  • 文字- このグループには char 型が含まれます。
上記のテキストで示したように、Java のプリミティブはそれほど原始的ではなく、多くの問題を効果的に解決できます。ただし、これには、プログラムで予期しない動作が発生したくない場合に留意すべきいくつかの機能も導入されています。よく言われるように、すべての費用を支払わなければなりません。「急な」(広い) 範囲 (長いような) のプリミティブが必要な場合は、より大きなメモリの割り当てを犠牲にして、逆方向に割り当てます。メモリを節約しバイトを使用することで、-128 ~ 127 の限定された範囲が得られます。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION