# Java 数据类型

原文：http://zetcode.com/lang/java/datatypes/

在 Java 教程的这一部分中，我们将讨论数据类型。

计算机程序（包括电子表格，文本编辑器，计算器或聊天客户端）可以处理数据。 用于各种数据类型的工具是现代计算机语言的基本组成部分。 数据类型是一组值以及对这些值的允许操作。

Java 编程语言是一种静态类型的语言。 这意味着每个变量和每个表达式都具有在编译时已知的类型。 Java 语言也是一种强类型语言，因为类型会限制变量可以容纳的值或表达式可以产生的值，限制对这些值支持的操作，并确定操作的含义。 强大的静态类型有助于在编译时检测错误。 动态类型语言（例如 Ruby 或 Python）中的变量会随着时间的推移接收不同的数据类型。 在 Java 中，一旦变量声明为某种数据类型，就无法保存其他数据类型的值。

Java 中有两种基本数据类型：基本类型和引用类型。 基本类型为：

*   布尔值
*   烧焦
*   字节
*   短
*   整型
*   长
*   浮动
*   双

Java 中每种类型都有一个特定的关键字。 基本类型不是 Java 中的对象。 原始数据类型不能存储在仅适用于对象的 Java 集合中。 可以将它们放置在数组中。

参考类型为：

*   班级类型
*   接口类型
*   数组类型

还有一个特殊的`null`类型，它代表不存在的值。

在 Ruby 编程语言中，一切都是对象。 甚至是基本数据类型。

```java
#!/usr/bin/ruby

4.times { puts "Ruby" }

```

这个 Ruby 脚本会在控制台上打印四次`"Ruby"`字符串。 我们在 4 号上调用次方法。 该数字是 Ruby 中的对象。

Java 有不同的方法。 它具有原始数据类型和包装器类。 包装器类将原始类型转换为对象。 包装器类将在下一章中介绍。

## 布尔值

我们的世界建立了双重性。 有天与地，水与火，阴与阳，男人与女人，爱与恨。 在 Java 中，`boolean`数据类型是具有以下两个值之一的原始数据类型：`true`或`false`。

快乐的父母正在等待孩子的出生。 他们为两种可能性都选择了名称。 如果要成为男孩，他们选择了罗伯特。 如果要成为女孩，他们会选择维多利亚。

`com/zetcode/BooleanType.java`

```java
package com.zetcode;

import java.util.Random;

public class BooleanType {

    public static void main(String[] args) {

        String name = "";
        Random r = new Random();
        boolean male = r.nextBoolean();

        if (male == true) {

            name = "Robert";
        }

        if (male == false) {

            name = "Victoria";
        }

        System.out.format("We will use name %s%n", name);

        System.out.println(9 > 8);
    }
}

```

该程序使用随机数生成器来模拟我们的情况。

```java
Random r = new Random();
boolean male = r.nextBoolean();

```

`Random`类用于产生随机数。 `nextBoolean()`方法随机返回一个布尔值。

```java
if (male == true) {

    name = "Robert";
}

```

如果布尔变量`male`等于`true`，则将名称变量设置为`"Robert"`。 `if`关键字适用于布尔值。

```java
if (male == false) {

    name = "Victoria";
}

```

如果随机生成器选择`false`，则将名称变量设置为`"Victoria"`。

```java
System.out.println(9 > 8);

```

关系运算符导致布尔值。 此行在控制台上显示为`true`。

```java
$ java BooleanType.java
We will use name Robert
true
$ java BooleanType.java
We will use name Robert
true
$ java BooleanType.java
We will use name Victoria
true

```

多次运行该程序。

## 整数

整数是实数的子集。 它们写时没有小数或小数部分。 整数落入集合`Z = {..., -2, -1, 0, 1, 2, ......}`中整数是无限的。

在计算机语言中，整数（通常）是原始数据类型。 实际上，计算机只能使用整数值的子集，因为计算机的容量有限。 整数用于计算离散实体。 我们可以有 3、4 或 6 个人，但不能有 3.33 个人。 我们可以拥有 3.33 公斤，4.564 天或 0.4532 公里。

| 类型 | 大小 | 范围 |
| --- | --- | --- |
| `byte` | 8 位 | -128 至 127 |
| `short` | 16 位 | -32,768 至 32,767 |
| `char` | 16 位 | 0 至 65,535 |
| `int` | 32 位 | -2,147,483,648 至 2,147,483,647 |
| `long` | 64 位 | -9,223,372,036,854,775,808 至 9,223,372,036,854,775,807 |

Table: Integer types in Java

可以根据我们的需要使用这些整数类型。 然后，我们可以将`byte`类型用于存储妇女生育的孩子数量的变量。 经过验证的年龄最大的人死于 122 岁，因此我们可能至少选择`short`类型作为年龄变量。 这将为我们节省一些内存。

整数文字可以用十进制，十六进制，八进制或二进制表示法表示。 如果数字后缀为 ASCII 字母`L`或`l`，则其类型为`long`。 否则为`int`类型。 大写字母`L`首选用于指定长数字，因为小写字母`l`容易与数字 1 混淆。

```java
int a = 34;
byte b = 120;
short c = 32000;
long d = 45000;
long e = 320000L;

```

我们有五个作业。 值 34、120、32000 和 45000 是`int`类型的整数文字。 `byte`和`short`类型没有整数文字。 如果这些值适合目标类型，则编译器不会抱怨并自动执行转换。 对于小于`Integer.MAX_VALUE`的`long`数字，`L`后缀是可选的。

```java
long x = 2147483648L;
long y = 2147483649L;

```

对于大于`Integer.MAX_VALUE`的`long`数字，我们必须添加`L`后缀。

当我们使用整数时，我们处理离散项。 例如，我们可以使用整数来计算苹果。

`com/zetcode/Apples.java`

```java
package com.zetcode;

public class Apples {

    public static void main(String[] args) {

        int baskets = 16;
        int applesInBasket = 24;

        int total = baskets * applesInBasket;

        System.out.format("There are total of %d apples%n", total);
    }
}

```

在我们的程序中，我们计算了苹果的总量。 我们使用乘法运算。

```java
int baskets = 16;
int applesInBasket = 24;

```

篮子数和每个篮子中的苹果数是整数值。

```java
int total = baskets * applesInBasket;

```

将这些值相乘，我们也得到一个整数。

```java
$ java Apples.java
There are total of 384 apples

```

这是程序的输出。

可以在 Java 中使用四种不同的表示法指定整数：十进制，八进制，十六进制和二进制。 二进制符号是在 Java 7 中引入的。众所周知，通常使用小数。 八进制数字以`0`字符开头，后跟八进制数字。 十六进制数字以`0x`字符开头，后跟十六进制数字。 二进制数字以`0b`开头，后跟二进制数字（零和一）。

`com/zetcode/IntegerNotations.java`

```java
package com.zetcode;

public class IntegerNotations {

    public static void main(String[] args) {

        int n1 = 31;
        int n2 = 0x31;
        int n3 = 031;
        int n4 = 0b1001;

        System.out.println(n1);
        System.out.println(n2);
        System.out.println(n3);
        System.out.println(n4);
    }
}

```

我们有四个整数变量。 每个变量都被分配了一个具有不同整数符号的值。

```java
int n1 = 31;
int n2 = 0x31;
int n3 = 031;
int n4 = 0b1001;

```

第一个是十进制，第二个十六进制，第三个八进制和第四个二进制。

```java
$ java IntegerNotations.java
31
49
25
9

```

我们看到了程序的输出。

大数字很难阅读。 如果我们有一个像 245342395423452 这样的数字，我们会发现很难快速阅读它。 在计算机外部，大数字之间用空格或逗号分隔。 从 Java SE 1.7 开始，可以用下划线分隔整数。

下划线不能在数字的开头或结尾，浮点字面量中的小数点附近以及`F`或`L`后缀之前使用。

`com/zetcode/UsingUnderscores.java`

```java
package com.zetcode;

public class UsingUnderscores {

    public static void main(String[] args) {

        long a = 23482345629L;
        long b = 23_482_345_629L;

        System.out.println(a == b);
    }
}

```

此代码示例演示了 Java 中下划线的用法。

```java
long a = 23482345629L;
long b = 23_482_345_629L;

```

我们有两个相同的长数字。 在第二个数字中，我们将数字中的每三个数字分开。 比较这两个数字，我们得到一个布尔值`true`。 `L`后缀告诉编译器我们有一个长整数。

使用的 Java `byte`，`short`，`int`和`long`类型确实表示固定精度数字。 这意味着它们可以表示有限数量的整数。 长类型可以代表的最大整数是 9223372036854775807。如果要处理更大的数字，则必须使用`java.math.BigInteger`类。 它用于表示不可变的任意精度整数。 任意精度整数仅受可用计算机内存量的限制。

`com/zetcode/VeryLargeIntegers.java`

```java
package com.zetcode;

import java.math.BigInteger;

public class VeryLargeIntegers {

    public static void main(String[] args) {

        System.out.println(Long.MAX_VALUE);

        BigInteger b = new BigInteger("92233720368547758071");
        BigInteger c = new BigInteger("52498235605326345645");

        BigInteger a = b.multiply(c);

        System.out.println(a);
    }
}

```

在`java.math.BigInteger`类的帮助下，我们将两个非常大的数字相乘。

```java
System.out.println(Long.MAX_VALUE);

```

我们打印可以用`long`类型表示的最大整数值。

```java
BigInteger b = new BigInteger("92233720368547758071");
BigInteger c = new BigInteger("52498235605326345645");

```

我们定义了两个`BigInteger`对象。 它们都具有`long`类型可以容纳的更大的值。

```java
BigInteger a = b.multiply(c);

```

使用`multiply()`方法，我们将两个数字相乘。 请注意，`BigInteger`数字是不可变的。 该操作返回一个新值，我们将其分配给新变量。

```java
System.out.println(a);

```

计算出的整数将打印到控制台。

```java
$ java VeryLargeIntegers.java
9223372036854775807
4842107582663807707870321673775984450795

```

这是示例输出。

## 算术溢出

算术溢出是在计算产生的结果的大小大于给定寄存器或存储位置可以存储或表示的结果时发生的条件。

`com/zetcode/Overflow.java`

```java
package com.zetcode;

public class Overflow {

    public static void main(String[] args) {

        byte a = 126;

        System.out.println(a);
        a++;

        System.out.println(a);
        a++;

        System.out.println(a);
        a++;

        System.out.println(a);
    }
}

```

在此示例中，我们尝试分配一个超出数据类型范围的值。 这导致算术溢出。

```java
$ java Overflow.java
126
127
-128
-127

```

发生溢出时，变量将重置为负的上限值。

## 浮点数字

实数测量连续的数量，例如重量，高度或速度。 浮点数表示计算中实数的近似值。 在 Java 中，我们有两种原始浮点类型：`float`和`double`。 `float`是单精度类型，以 32 位存储数字。 `double`是双精度类型，以 64 位存储数字。 这两种类型具有固定的精度，不能完全表示所有实数。 在必须使用精确数字进行处理的情况下，可以使用`BigDecimal`类。

带有`F/f`后缀的浮点数是`float`类型，`double`数字有`D/d`后缀。 `double`数字的后缀是可选的。

假设一个短跑运动员跑了 1 个小时，跑了 9.87 秒。 他的公里/小时速度是多少？

`com/zetcode/Sprinter.java`

```java
package com.zetcode;

public class Sprinter {

    public static void main(String[] args) {

        float distance;
        float time;
        float speed;

        distance = 0.1f;

        time = 9.87f / 3600;

        speed = distance / time;

        System.out.format("The average speed of a sprinter is %f km/h%n", speed);
    }
}

```

在此示例中，必须使用浮点值。 在这种情况下，浮点数据类型的低精度不会造成问题。

```java
distance = 0.1f;

```

100m 是 0.1km。

```java
time = 9.87f / 3600;

```

9.87s 是`9.87 / 60 * 60 h`。

```java
speed = distance / time;

```

为了获得速度，我们将距离除以时间。

```java
$ java Sprinter.java
The average speed of a sprinter is 36.474163 km/h

```

这是程序的输出。 小数舍入误差不会影响我们对短跑运动员速度的理解。

`float`和`double`类型不精确。

`com/zetcode/FloatingInPrecision.java`

```java
package com.zetcode;

public class FloatingInPrecision {

    public static void main(String[] args) {

        double a = 0.1 + 0.1 + 0.1;
        double b = 0.3;

        System.out.println(a);
        System.out.println(b);

        System.out.println(a == b);
    }
}

```

该代码示例说明了浮点值的不精确性质。

```java
double a = 0.1 + 0.1 + 0.1;
double b = 0.3;

```

我们定义两个`double`值。 `D/d`后缀是可选的。 乍一看，它们应该相等。

```java
System.out.println(a);
System.out.println(b);

```

打印它们将显示很小的差异。

```java
System.out.println(a == b);

```

该行将返回`false`。

```java
$ java FloatingInPrecision.java
0.30000000000000004
0.3
false

```

边距误差很小。 因此，比较运算符返回布尔值`false`。

当我们使用货币，货币以及通常用于业务应用时，我们需要使用精确的数字。 基本浮点类型的舍入误差是不可接受的。

`com/zetcode/CountingMoney.java`

```java
package com.zetcode;

public class CountingMoney {

    public static void main(String[] args) {

        float c = 1.46f;
        float sum = 0f;

        for (int i=0; i<100_000; i++) {

            sum += c;
        }

        System.out.println(sum);
    }
}

```

`1.46f`代表 1 欧元和 46 美分。 我们从 100000 个这样的金额中创建一个总和。

```java
for (int i=0; i<100_000; i++) {

    sum += c;
}

```

在此循环中，我们从 100000 这样的金额中创建一个总和。

```java
$ java CountingMoney.java
146002.55

```

计算得出的错误为 2 欧元和 55 美分。

为了避免此裕度误差，我们利用了`BigDecimal`类。 它用于保存不可变的，任意精度的带符号十进制数字。

`com/zetcode/CountingMoney2.java`

```java
package com.zetcode;

import java.math.BigDecimal;

public class CountingMoney2 {

    public static void main(String[] args) {

        BigDecimal c = new BigDecimal("1.46");
        BigDecimal sum = new BigDecimal("0");

        for (int i=0; i<100_000; i++) {

            sum = sum.add(c);
        }

        System.out.println(sum);
    }
}

```

我们用相同的金额执行相同的操作。

```java
BigDecimal c = new BigDecimal("1.46");
BigDecimal sum = new BigDecimal("0");

```

我们定义两个`BigDecimal`数字。

```java
for (int i=0; i<100_000; i++) {

    sum = sum.add(c);
}

```

`BigDecimal`数字是不可变的，因此在每个循环中始终将一个新对象分配给`sum`变量。

```java
$ java CountingMoney2.java
146000.00

```

在此示例中，我们获得了精确值。

Java 支持浮点值的科学语法。 也称为指数表示法，它是一种写数字太大或太小而不能方便地用标准十进制表示法写的方式。

`com/zetcode/ScientificNotation.java`

```java
package com.zetcode;

import java.math.BigDecimal;
import java.text.DecimalFormat;

public class ScientificNotation {

    public static void main(String[] args) {

        double n = 1.235E10;
        DecimalFormat dec = new DecimalFormat("#.00");

        System.out.println(dec.format(n));

        BigDecimal bd = new BigDecimal("1.212e-19");

        System.out.println(bd.toEngineeringString());
        System.out.println(bd.toPlainString());
    }
}

```

我们使用科学计数法定义两个浮点值。

```java
double n = 1.235E10;

```

这是`double`类型的浮点值，以科学计数法表示。

```java
DecimalFormat dec = new DecimalFormat("#.00");

System.out.println(dec.format(n));

```

我们使用`DecimalFormat`类将`double`值排列为标准十进制格式。

```java
BigDecimal bd = new BigDecimal("1.212e-19");

System.out.println(bd.toEngineeringString());
System.out.println(bd.toPlainString());

```

`BigDecimal`类采用科学计数法中的浮点值作为参数。 我们使用该类的两种方法来打印工程字符串和纯字符串中的值。

```java
$ java ScientificNotation.java
12350000000.00
121.2E-21
0.0000000000000000001212

```

This is the example output.

## 枚举

枚举类型是一种特殊的数据类型，它使变量成为一组预定义的常量。 可以将任何枚举器分配为已声明为具有枚举类型的变量作为值。 枚举使代码更具可读性。 当我们处理只能从一小部分可能的值中取出一个的变量时，枚举很有用。

`com/zetcode/Enumerations.java`

```java
package com.zetcode;

public class Enumerations {

    enum Days {

        MONDAY,
        TUESDAY,
        WEDNESDAY,
        THURSDAY,
        FRIDAY,
        SATURDAY,
        SUNDAY
    }

    public static void main(String[] args) {

        Days day = Days.MONDAY;

        if (day == Days.MONDAY) {

            System.out.println("It is Monday");
        }

        System.out.println(day);

        for (Days d : Days.values()) {

            System.out.println(d);
        }
    }
}

```

在我们的代码示例中，我们为工作日创建一个枚举。

```java
enum Days {

    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}

```

使用`enum`关键字创建代表星期几的枚举。 枚举项是常量。 按照约定，常量用大写字母表示。

```java
Days day = Days.MONDAY;

```

我们有一个名为`day`的变量，其类型为`Days`。 它被初始化为星期一。

```java
if (day == Days.MONDAY) {

    System.out.println("It is Monday");
}

```

与将日变量与某个数字进行比较相比，此代码更具可读性。

```java
System.out.println(day);

```

该行将在星期一打印到控制台。

```java
for (Days d : Days.values()) {

    System.out.println(d);
}

```

此循环将整天打印到控制台。 静态`values()`方法按声明顺序返回包含此`enum`类型常量的数组。 此方法可用于通过增强的`for`语句迭代常量。 增强的`for`逐元素遍历数组，并将其打印到终端。

```java
$ java Enumerations.java
It is Monday
MONDAY
MONDAY
TUESDAY
WEDNESDAY
THURSDAY
FRIDAY
SATURDAY
SUNDAY

```

This is the example output.

可以给枚举常量一些值。

`com/zetcode/Enumerations2.java`

```java
package com.zetcode;

enum Season {

    SPRING(10),
    SUMMER(20),
    AUTUMN(30),
    WINTER(40);

    private int value;

    private Season(int value) {
        this.value = value;
    }

    public int getValue() {

        return value;
    }
}

public class Enumerations2 {

    public static void main(String[] args) {

        for (Season season : Season.values()) {
            System.out.println(season + " " + season.getValue());
        }
    }
}

```

该示例包含一个`Season`枚举，该枚举具有四个常量。

```java
SPRING(10),
SUMMER(20),
AUTUMN(30),
WINTER(40);

```

在这里，我们定义`enum`的四个常数。 给常数指定特定值。

```java
private int value;

private Season(int value) {
    this.value = value;
}

```

当定义常量时，我​​们还必须创建一个构造器。 构造器将在本教程的后面部分介绍。

```java
SPRING 10
SUMMER 20
AUTUMN 30
WINTER 40

```

This is the example output.

## 字符串和字符

`String`是代表计算机程序中文本数据的数据类型。 Java 中的字符串是字符序列。 `char`是单个字符。 字符串用双引号引起来。

由于字符串在每种编程语言中都非常重要，因此我们将为它们专门整整一章。 在这里，我们仅举一个小例子。

`com/zetcode/StringsChars.java`

```java
package com.zetcode;

public class StringsChars {

    public static void main(String[] args) {

        String word = "ZetCode";

        char c = word.charAt(0);
        char d = word.charAt(3);

        System.out.println(c);
        System.out.println(d);
    }
}

```

程序将`Z`字符打印到终端。

```java
String word = "ZetCode";

```

在这里，我们创建一个字符串变量，并为其分配“ ZetCode”值。

```java
char c = word.charAt(0);

```

`charAt()`方法返回指定索引处的`char`值。 序列的第一个`char`值在索引 0 处，第二个在索引 1 处，依此类推。

```java
$ java StringsChars.java
Z
C

```

该程序将“ ZetCode”字符串的第一个和第四个字符打印到控制台。

## 数组

数组是处理元素集合的复杂数据类型。 每个元素都可以通过索引访问。 数组的所有元素必须具有相同的数据类型。

我们将整章专门介绍数组。 这里我们仅显示一个小例子。

`com/zetcode/ArraysEx.java`

```java
package com.zetcode;

public class ArraysEx {

    public static void main(String[] args) {

        int[] numbers = new int[5];

        numbers[0] = 3;
        numbers[1] = 2;
        numbers[2] = 1;
        numbers[3] = 5;
        numbers[4] = 6;

        int len = numbers.length;

        for (int i = 0; i < len; i++) {

            System.out.println(numbers[i]);
        }
    }
}

```

在此示例中，我们声明一个数组，用数据填充它，然后将数组的内容打印到控制台。

```java
int[] numbers = new int[5];

```

我们创建一个整数数组，该数组最多可以存储 5 个整数。 因此，我们有五个元素组成的数组，索引为`0..4`。

```java
numbers[0] = 3;
numbers[1] = 2;
numbers[2] = 1;
numbers[3] = 5;
numbers[4] = 6;

```

在这里，我们为创建的数组分配值。 我们可以通过数组访问符号访问数组的元素。 它由数组名称和方括号组成。 在方括号内，我们指定所需元素的索引。

```java
int len = numbers.length;

```

每个数组都有一个`length`属性，该属性返回数组中的元素数。

```java
for (int i = 0; i < len; i++) {

    System.out.println(numbers[i]);
}

```

我们遍历数组并将数据打印到控制台。

```java
$ java ArraysEx.java
3
2
1
5
6

```

This is the output of the program.

在 Java 教程的这一部分中，我们介绍了 Java 中的数据类型。