JavaRush /Java Blog /Random-TW /Java 中的原始類型:它們不那麼原始
Viacheslav
等級 3

Java 中的原始類型:它們不那麼原始

在 Random-TW 群組發布

介紹

應用程式開發可以被視為處理一些數據,或者更確切地說,儲存和處理它。今天我想談談第一個關鍵面向。Java中資料是如何儲存的?這裡我們有兩種可能的格式:引用資料型別和原始資料型別。讓我們討論一下基本類型的類型以及使用它們的可能性(無論人們怎麼說,這是我們程式語言知識的基礎)。 Java 原始資料型別是一切的基礎。不,我一點也不誇張。Oracle 有一個單獨的教學專門介紹基元:基元資料類型的 Java 中的原始類型:它們不那麼原始 - 1一些歷史。一開始是零。但零很無聊。然後比特出現了。為什麼叫他這個名字?它因縮寫“ binary digital (二進制數)而得名。也就是說,它只有兩個意義。既然它是零,現在它要么是 0,要么是 1,這是合乎邏輯的。生活變得更加有趣。這些碎片開始聚集成群。而這些羊群開始被稱為位元組(byte)。在現代世界中,位元組 = 2 的三次方,即 8. 但事實證明情況並非總是如此。關於「字節」這個名字的由來,有許多猜測、傳說和謠言。有些人認為這完全與當時的編碼有關,而有些人則認為以這種方式閱讀資訊更有利可圖。位元組是最小的可尋址記憶體區塊。它是在記憶體中具有唯一位址的位元組。有一個傳說,ByTe 是 Binary Term(機器字)的縮寫。機器字——簡單地說,這是處理器在一次操作中可以處理的資料量。以前,機器字大小與最小可尋址記憶體相同。在Java中,變數只能儲存位元組值。正如我上面所說,Java中有兩種類型的變數:
  • java原始類型直接儲存資料位元組的值(我們將在下面更詳細地了解這些原始類型);
  • 引用類型,在堆中儲存物件位址的字節,也就是說,透過這些變數我們可以直接存取物件本身(有點像物件的遠端控制)

Java位元組

所以,歷史給了我們一個位元組——我們可以使用的最小內存量。它由8位組成。java中最小的整數資料型別是byte。這是有符號的 8 位元類型。這是什麼意思?我們數一下。2^8 是 256。但是如果我們想要一個負數怎麼辦?而Java開發人員決定,二進位代碼「10000000」將代表-128,即最高有效位(最左邊的位)將指示該數字是否為負數。二進位「0111 1111」等於127。也就是說,128不能以任何方式指定,因為 它將是-128。完整的計算在這個答案中給出:Why the range of bytes of bytes in Java? -128 to 127 in Java? 要了解數字是如何獲得的,您應該看一下圖片:
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型別就會減少記憶體消耗。Byte 主要用於在將資料儲存在數組中時減少記憶體消耗(例如,將透過網路接收到的資料儲存在某個緩衝區中,這將實現為位元組數組)。但是當資料操作時,使用byte就不能滿足你的期望了。這是由於 Java 虛擬機器 (JVM) 的實作。由於大多數系統都是32位或64位,因此計算時byte和short會轉換為32位int,這個我們稍後會講到。這使得計算更加容易。有關更多詳細信息,請參閱字節加法轉換為 int 是因為 java 語言規則還是因為 jvm?。答案也包含 JLS(Java 語言規格)的連結。此外,在錯誤的地方使用位元組可能會導致尷尬的時刻:
public static void main(String []args){
        for (byte i = 1; i <= 200; i++) {
            System.out.println(i);
        }
}
這裡將會出現一個循環。由於計數器值達到最大值(127),因此會發生溢出,計數器值將變為-128。我們永遠無法擺脫這個循環。

短的

位元組值的限制相當小。因此,對於下一個資料類型,我們決定將位數加倍。也就是現在不是8位,而是16位。也就是2個位元組。可以用同樣的方法計算這些值。2^(16-1) = 2^15 = 32768。這意味著範圍是從-32768到32767。它很少用於任何特殊情況。正如 Java 語言文檔告訴我們的那樣:“在大型數組中,您可以使用 Short 來節省內存。”

整數

所以我們找到了最常用的類型。它佔用 32 位,即 4 個位元組。總的來說,我們繼續翻倍。值的範圍是從-2^31到2^31 – 1。

最大整數值

int 2147483648的最大值是1,這個值一點也不小。如上所述,為了優化計算,因為 考慮到現代電腦的位元容量,計數更方便;資料可以隱式轉換為 int。這是一個簡單的例子:
byte a = 1;
byte b = 2;
byte result = a + b;
這樣的程式碼無害,但我們收到錯誤:“錯誤:不相容的類型:從 int 到 byte 可能有損轉換。” 您必須將其更正為 byte result = (byte)(a + b); 還有一個無害的例子。如果我們運行以下程式碼會發生什麼?
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 浮點型與 Java 雙精度型

這些類型稱為浮點類型。也就是說,這些不是整數類型。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 工作範例with BigDecimal 可以在這裡看到:Make cents with 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?This is Not a number,表示結果無法計算且不是數字。下面是一個例子:我們要計算 -4 的平方根。4 的平方根是 2。也就是說,必須對 2 平方,然後得到 4。必須平方什麼才能得到 -4?這是行不通的,因為... 如果有正數,則保留。如果它是負數,那麼減去一個負數就會得到一個正數。也就是說,它是不可計算的。
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);
}
這是另一個關於浮點數主題的精彩概述:你的觀點在哪裡?
還有什麼要讀的:

Java布林值

下一個類型是布林型(邏輯類型)。它只能接受值 true 或 false,即關鍵字。用於邏輯運算,例如 while 循環,以及使用 if、switch 進行分支。在這裡你能發現哪些有趣的事呢?嗯,舉個例子,理論上我們只需要1位訊息,0或1,即真或假。但實際上,Boolean 會佔用更多的內存,這取決於特定的 JVM 實作。通常這與 int 的成本相同。另一種選擇是使用 BitSet。以下是《Java 基礎知識》一書中的簡短描述:BitSet

Java字元型

現在我們已經到達了最後一個原始類型。所以,char中的資料佔用16位,描述了字元。Java 對 char 使用 Unicode 編碼。符號可以按照兩個表來設定(你可以在這裡看到):
  • 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");
        }
    }

}
我強烈建議您查看tutorialspoint 的線上IDE。當我在一次會議上看到這個難題時,我的精神振奮了。我希望你也喜歡這個例子) 更新:這是在 Joker 2017 上,報告:“ Java Puzzlers NG S03 - 你們都來自哪裡?!

文字

文字是明確指定的值。使用文字,您可以指定不同數字系統中的值:
  • 十進制:10
  • 十六進位:0x1F4,以0x開頭
  • 八進位:010,從零開始。
  • 二進位(Java7以後):0b101,從0b開始
我會更多地討論八進制系統,因為它很有趣:
int costInDollars = 08;
這行程式碼將無法編譯:
error: integer number too large: 08
似乎是無稽之談。現在讓我們記住二進制和八進制系統。二進位系統中沒有二元,因為 有兩個值(從0開始)。八進制有 8 個值,從零開始。也就是說,值 8 本身並不存在。因此,這是一個乍看之下似乎很荒謬的錯誤。請記住,以下是翻譯值的「後續」規則:
Java 中的原始類型:它們不那麼原始 - 4

包裝類

Java 中的基元有自己的包裝類,以便您可以將它們作為物件使用。也就是說,對於每個基本類型都有一個對應的參考類型。 Java 中的原始類型:它們不那麼原始 - 5包裝類別是不可變的:這意味著一旦建立了對象,它的狀態(值欄位的值)就無法更改。包裝類別被聲明為最終的:對象,可以說是唯讀的。我還想提一下,不可能從這些類別繼承。Java 會自動在基本型別及其包裝器之間進行轉換:
Integer x = 9;          // autoboxing
int n = new Integer(3); // unboxing
將基本型別轉換為參考型別(int->Integer)的過程稱為自動裝箱,反之稱為拆箱。這些類別使得在物件內保存基元成為可能,而物件本身的行為將像物件一樣(好吧,就像任何其他物件一樣)。有了這些,我們就得到了大量各種有用的靜態方法,例如比較數字、將符號轉換為大小寫、確定符號是字母還是數字、搜尋最小數字等。提供的功能集僅取決於包裝器本身。您自己實作 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