JavaRush /Java 博客 /Random-ZH /Java 中的原始类型:它们并不那么原始
Viacheslav
第 3 级

Java 中的原始类型:它们并不那么原始

已在 Random-ZH 群组中发布

介绍

应用程序开发可以被视为处理一些数据,或者更确切地说,存储和处理它。今天我想谈谈第一个关键方面。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 个值,从 0 开始。也就是说,值 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