在Java中理解数据类型

作者选择了自由开源基金作为为捐赠而写计划的一部分接受捐赠。

介绍

Java是一种静态类型的编程语言。这意味着当你创建一个变量时,你必须指定它的数据类型,即它存储的信息类型。这与动态类型的语言形成了对比,比如PHP。在动态类型的语言中,你不需要指定变量的数据类型,这可能看起来很轻松。

然而,了解数据类型并适当地使用它们可以让开发人员优化他们的代码,因为每种数据类型都有特定的资源需求。此外,如果你指定了一个数据类型,然后尝试存储不同类型的数据,比如出错时,你将无法编译代码。因此,使用静态类型的语言,甚至在进行任何测试之前就可以检测到错误。

Java有两种数据类型原始引用(也称为非原始)。在本教程中,您将使用变量来存储和使用Java程序中的信息,以了解Java中一些常用的数据类型。这并不是所有数据类型的详尽概述,但本指南将帮助您熟悉在Java中有哪些选项可用。

先决条件

要按照本教程进行,您需要:

原始类型

Java的原始类型是Java中最简单和最基本的数据类型。它们代表着诸如数字和字符之类的原始值。最常用的原始数据类型包括int(整数)、boolean(布尔值)和char(字符)。您可以在官方的Java数据类型文档中找到其余的类型。

整数

整数既可以是负数也可以是正数的整数。在Java中,您会使用int来存储它们。int可以容纳足够大的数字以满足大多数目的:从-2,147,483,6482,147,483,647

让我们看一个int在示例中的使用:

int theAnswer = 42;

原始类型始终以小写字母开头(int)。Java语法规则要求您首先指定数据类型(int),然后是其名称(theAnswer)。之后,您使用等号(=)将值42赋给变量。

不管数据类型如何,您都可以通过直接指定其名称而不是在其前面添加任何特殊字符来使用变量。这是因为Java可以将其识别为变量。

注意:变量theAnswer的名称和本教程中的所有其他变量都使用驼峰命名法编写。虽然没有严格要求必须使用它,但这是Java中被接受的命名约定。

声明变量后,您可以在方法中通过引用它来使用它,就像这样:

int theAnswer = 42;
System.out.println("The answer to all questions is " + theAnswer);

在第二行中,您使用来自包System.out的内置方法printlntheAnswer打印到控制台。这是测试变量是否按预期声明的最简单方法。

要查看此代码的实际效果,请使用Java Shell工具。安装Java后,在本地计算机上打开终端或命令提示符,然后键入jshell

  1. jshell

您的输出将类似于以下内容:

Output
| Welcome to JShell -- Version 11.0.16 | For an introduction type: /help intro jshell>

您可以将本教程中的代码示例粘贴到控制台中。完成后,您可以通过键入/exit退出jshell

要声明和使用int,请将以下行粘贴到jshell控制台中:

  1. int theAnswer = 42;
  2. System.out.println("The answer to all questions is " + theAnswer);

您将看到以下输出:

Output
theAnswer ==> 42 The answer to all questions is 42

此输出确认您将int变量theAnswer正确设置为42(theAnswer ==> 42)。您还成功地将theAnswer传递给了一个方法,并且该方法产生了预期的变量值。

布尔

布尔值为truefalse。在Java中,您将使用boolean来存储它们。例如,让我们创建一个boolean变量来定义Java是否有趣:

boolean isJavaFun = true;

您将变量isJavaFun定义为true。另一个boolean值是false

使用上述变量,您可以这样打印句子Java有趣:true

  1. boolean isJavaFun = true;
  2. System.out.println("Java is fun: " + isJavaFun);

jshell中运行这些行将产生以下输出:

Output
isJavaFun ==> true Java is fun: true

类似于int示例,方法println将打印括号中提供的参数。加号(+)将字符串“Java有趣:”与变量isJavaFun连接或拼接在一起,因此实际上它只是一个参数 – 字符串Java有趣:true

字符

要存储单个字母数字字符,您将使用char。例如:

char firstLetter = 'a';

请注意,字母a被单引号括起来。单引号只能用于char值。双引号用于字符串,稍后您将了解。

char 看起来不是特别有用的类型,因为你不太可能需要一个变量被赋值为单个字符。然而,char 被用作字符串类(例如 String)的构建块,它们基本上是 char 值的集合。

正如你在本节中看到的,原始类型变量的声明和使用很简单,因为它们代表诸如整数之类的简单值。这些值可以立即使用,不需要额外的操作,比如创建对象、调用方法等。

引用类型

在这个系列的第一个教程中,如何在Java中编写您的第一个程序,你学到了Java代码是以类的形式组织的,这些类被用作创建对象的模板。当这些对象被赋给变量时,你是在指向或引用这些对象。在这些情况下,变量被分类为引用类型。这些变量也被称为非原始类型,因为原始类型变量不能指向对象。

对象之所以强大,是因为它们具有高级属性,并且可以在触发其方法时执行操作。然而,如果没有指向它们的变量,这些对象是无法访问且几乎无法使用的。这就是为什么引用类型变量对于Java和整个面向对象编程至关重要的原因。

注意:引用类型指向从类创建的对象。为避免混淆,在以下示例中,引用类型和创建的对象将是同一类。

然而,在复杂的程序中,这种情况很少发生。在Java中,接口是对特定行为的一组要求,这些要求可以由一个或多个类满足。满足接口要求的类被称为实现了该接口。因此,在复杂的程序中,通常会声明一个具有接口引用类型的变量。这样,您可以指定变量应该表现的行为,而无需将其绑定到该行为的具体实现。这使您能够轻松更改变量指向的实现,而无需更改变量的使用方式。这个复杂的概念是关于继承多态的更高级主题的一部分,这将是我们Java系列中的一个独立教程。

虽然只有几种原始类型,但引用类型实际上是无限的,因为类(和接口)的数量没有限制,每个类代表一种引用类型。在Java中有许多内置的类提供了基本的功能。其中最常用的类可以在核心java.lang包中找到。在本节中,您将会回顾其中一些。

String

String类表示由字符组成的字符串。要声明String或任何其他引用类型的变量,首先要指定其类型,然后是变量的名称。之后,使用等号为其赋值。到目前为止,这与使用原始类型的方式类似。但是,引用类型指向对象,因此如果尚未创建对象,您必须创建一个对象。下面是一个例子:

String hello = new String("Hello");

hello是具有引用类型String的变量名。您将其赋值给一个新的String对象。使用new关键字和类的名称(在这种情况下是String)创建新的String对象。类String以大写字母开头。按照惯例,所有类和因此所有引用类型都以大写字母开头。

每个类都有一个名为构造函数的特殊方法,用于创建新对象。您可以通过在类名后面添加括号(())来调用此构造函数。构造函数可能接受参数,就像上面的示例中,参数"Hello"被应用于String的构造函数。

为了确认hello变量的行为是否符合预期,可以再次将其传递给println方法,如下所示:

  1. String hello = new String("Hello");
  2. System.out.println(hello);

jshell中运行这些代码将产生以下输出:

Output
hello ==> "Hello" Hello

这次,输出确认变量hello被设置为Hello。之后,同样的Hello被打印到新行,证实println()方法已经处理了它。

包装类

在前面的部分中,您使用了String引用类型,这是经常使用的类型。其他常用的引用类型是所谓的基本类型的包装器。包装类包装或包含原始数据,因此得名。所有基本类型都有包装器对应,以下是一些示例:

  • Integer: 用于包装int值。
  • Character: 用于包装char值。
  • Boolean: 用于包装boolean值。

这些包装器存在是为了将简单的原始值升级为功能强大的对象。每个包装器都有与其设计用来存储的值相关的即用方法。

Integer为例,您将探索它。在前一节中,您使用new关键字创建了一个String对象。但是,有些类提供甚至鼓励使用特殊方法从它们那里获取对象,Integer就是其中之一。在Integer的情况下,使用特殊方法主要是为了资源优化,但在其他情况下,它可能是为了简化复杂对象的构建。

在下面的示例中,您使用valueOf方法创建一个名为theAnswerInteger变量,其值为42:

  1. Integer theAnswer = Integer.valueOf(42);
  2. System.out.println(theAnswer);

jshell中,您将得到以下输出:

Output
theAnswer ==> 42 42

通过调用Integer方法valueOf(42),您指示Java为您提供一个具有此值的对象。在幕后,Java将检查是否已经有一个具有这样的值的对象在其缓存中。如果有,该对象将链接到theAnswer变量。如果没有,将为theAnswer变量创建一个新对象。

出于性能原因,许多内置类提供这样的方法,并建议使用,如果不是强制性的。在Integer的情况下,您仍然可以使用new关键字创建对象,但会收到一个弃用警告。

除了String和包装器之外,还有其他一些有用的内置引用类型,您可以在java.lang package summary中找到它们。要完全理解其中一些更高级的引用类型,需要额外的解释或先验知识。这就是为什么我们将在Java系列的下一个教程中涵盖其中一些。

字面值

字面值表示可以直接在代码中使用的固定值,因此可以分配给原始类型和引用类型。有几种类型的字面值,可以归类如下。

原始类型字面值

在原始类型部分已经使用了一些字面值。对于每种原始类型,都有一个字面值,例如我们的示例中的:42'a'true。诸如42这样的整数是整数字面值。类似地,诸如'a'这样的字符是字符字面值,truefalse是布尔字面值。

原始类型字面值也可以用于创建引用类型的值。在代码 Integer.valueOf(42) 中,使用了 int 字面值来创建一个 Integer 对象。还有一种简写方式,你可以直接赋值,就像这样:

Integer theAnswer = 42;

42 是一个整数字面值,就像任何整数一样,你可以直接将其赋值给 theAnswer 变量,无需任何额外的语句。因为方便,所以通常会看到像这样声明一个 Integer

这种简写方式对其他原始类型字面值及其对应的引用类型同样适用,比如 Boolean

Boolean isFun = true;

true 是字面值,直接赋值给 isFun 类型为 Boolean 的变量。同样,还有一个 false 字面值,你也可以以相同的方式赋值。

字符串字面值

还有一种特殊的字面值用于 String 引用类型,它的特征是用双引号括起来。在这个例子中,它是 "Hello, World!"

String helloWorld = "Hello, World!";

使用字面值更简单更短,这也是许多程序员喜欢的原因。然而,你仍然可以像在引用类型部分所做的那样,通过声明一个新的 String 对象来声明一个 String 变量。

空字面量

还有一个重要的字面量:null,它表示值的缺失或对象的不存在。Null允许您创建一个引用类型,并将其指向null,而不是指向对象。null可以用于所有引用类型,但不能用于任何原始类型。

  1. String initiallyNullString = null;
  2. System.out.println("The class name is: " + initiallyNullString.getClass());

关于null字面量有一个重要的注意事项:您可以使用它声明变量,但在重新分配合适的非空值之前,不能使用这些变量。如果尝试使用具有null值的引用类型变量,则会出错。以下是一个示例:

Output
initiallyNullString ==> null | Exception java.lang.NullPointerException | at (#4:1)

当您尝试在jshell中运行此代码时,您将看到类似以下的错误:

根据您的操作系统和Java版本,输出可能有所不同。

注意:为简单起见,我们将java.lang.NullPointerException称为错误,即使在技术上它实际上是一个异常。有关异常和错误的更多信息,请查看教程,Java中的异常处理

要解决错误,您必须像这样重新分配initiallyNullString的值:

  1. String initiallyNullString = null;
  2. initiallyNullString = "not null any longer";
  3. System.out.println("The class name is: " + initiallyNullString.getClass());

修复后的新代码将打印以下输出:

Output
initiallyNullString ==> null initiallyNullString ==> "not null any longer" The class name is: class java.lang.String

上述输出显示了initiallyNullString首先是null,然后它变成了一个包含"not null any longer"的新String对象。接下来,当调用实例化对象的getClass()方法时,您将获得java.lang.String,其中String是类名,java.lang是其包。最后,将打印出完整而有意义的消息:"The class name is: class java.lang.String"

这种null值声明在遗留代码中更常见。它们被用来首先创建一个变量,然后再分配其真实值,通常通过确定后者的一些逻辑。然而,自Java版本8以来,有一种新的引用类型称为Optional,它更适用于以前使用null的情况。

本地变量类型推断

直到现在,您一直在使用Java中的一些常见数据类型来定义变量。然而,Java 10引入了一个名为局部变量类型推断的新特性,允许您在新变量前使用关键字var。有了这个特性,Java会从本地上下文中推断(也就是说,自动猜测)数据类型。类型推断颇具争议,因为它与先前解释的定义变量的冗长性相对立。这种特性的优势和劣势是有争议的,但事实是其他静态类型的语言,比如C++,支持类型推断。

无论如何,类型推断不能完全替代使用数据类型,因为它仅适用于局部变量,即方法内部的变量。让我们看一个使用var的例子:

  1. var hello = "Hello";
  2. System.out.println(hello);

您使用var关键字声明变量hello,以便指示Java检测其数据类型。之后,您以通常的方式将其打印到控制台,以确认它是否按预期工作:

Ouput
hello ==> "Hello" Hello

只要您的Java安装(更具体地说,是JDK)版本在10以上,此示例将正常工作。var关键字不支持在旧版本中。

类型推断发生在编译过程中,也就是编译代码时。编译过程将纯文本源代码转换为机器代码,并应用各种优化,包括类型推断。这确保了为推断类型的变量提供了正确数量的系统内存。因此,在编译后运行的机器代码是完全优化的,就好像您手动指定了所有数据类型一样。

在这个示例中,var关键字之所以起作用是因为该变量是局部的,而var数据类型只适用于局部变量。局部变量被定义在方法内部,只能在方法内部访问,这就是为什么它们被称为“局部”的原因。

为了显示var只能用于局部变量,尝试将其放在主方法外部,像这样:

  1. public class Hello {
  2. var hello = "Hello";
  3. public static void main(String[] args) {
  4. // example code
  5. }
  6. }

当您将以上代码粘贴到jshell中时,您将会收到以下错误:

Output
| Error: | 'var' is not allowed here | var hello = "Hello"; | ^-^

var不允许在那里使用,因为hello在方法外部,不再被视为局部。因此,对于非局部变量,类型推断不起作用,因为上下文不能可靠地用于检测数据类型。

虽然使用var可能会有挑战,并且不是必需的,但您可能会经常遇到它,因此了解它是有用的。

保留关键字

在Java中声明变量时,还有一个重要规则需要知道。有一些保留关键字不能用作变量名。例如,你不能声明一个类型为int并命名为new的基本类型变量,像这样:

  1. int new = 1;

如果你尝试这个例子,你会得到编译错误,因为new是一个保留关键字。

Output
| Error: | '.class' expected | int new = 1; | ^ | Error: | <identifier> expected | int new = 1; | ^ | Error: | '(' or '[' expected | int new = 1; | ^ | Error: | unexpected type | required: value | found: class | int new = 1; | ^--^ | Error: | missing return statement | int new = 1; | ^----------^

关键字new用于创建新对象,而Java并不希望在这个位置看到它。在前面输出的错误列表中,第一部分是最重要的:

Output
| Error: | '.class' expected | int new = 1; | ^

错误'.class' expected意味着当你使用new关键字时,Java期望后面跟着一个类。在这一点上,Java无法解释该语句,而其余的错误随之而来。

其余的保留关键字,如abstractcontinuedefaultforbreak,在Java中也有特定的含义,不能用作变量名。完整的保留关键字列表可以在Java语言关键字页面找到。即使你记不住所有的保留关键字,你也可以利用编译错误来识别问题。

总结

在这个教程中,你学习了Java中的原始数据类型和引用数据类型,这是一个复杂但必要的主题。花点时间练习它,并多次查看示例。尝试更改一些数据类型和数值。注意何时会引发错误,何时不会,以便培养成功执行代码的感觉。

要了解更多关于Java的内容,请查看我们的如何在Java中编码系列。

Source:
https://www.digitalocean.com/community/tutorials/understanding-data-types-in-java