Java 数据类型

变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。

代码到内存

Java 中有两种类型,原始类型(Primitive Type)会被直接映射到 CPU 的基础类型,引用类型(Reference Type)则指向了内存中的对象。

  • 原始类型:boolean,char,byte,short,int,long,float,double。
  • 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double。

Object

Objects

所有其他类型都是对象,有两种特殊类型,String 和 arrays 是对象,但编译器认为它们是内置的类型。字符串的定义如下:

var text = "hello";
System.out.println("hello".length());
System.out.println("hello".toUpperCase());
System.out.println("hello".toLowerCase());
System.out.println("hello".charAt(0));
System.out.println("hello".indexOf('l'));
System.out.println("hello".indexOf('o'));Copy to clipboardErrorCopied

数组的定义如下:

var intArray = new int[] {2, 3};
System.out.println(intArray[0]);
intArray[0] = 42;
// intArray[-1] = 42;   // throws IndexOutOfBoundsException
var clonedArray = intArray.clone();
var arrayLength = intArray.length;
System.out.println(arrayLength);
System.out.println(intArray);
System.out.println(intArray.equals(clonedArray));
var matrix = new double[][] { { 2.0, 3.0}, { 4.0, 5.0 } };Copy to clipboardErrorCopied

由于基本类型和数组几乎没有方法,因此,如果要使用它们,则必须使用静态方法。静态方法是在可以使用语法调用的某个类型上声明的函数 SomeWhere.methodName(arg0, arg1, arg2)

var resultAsInt = java.lang.Integer.parseInt("42");
System.out.println(resultAsInt);
var text = java.util.Arrays.toString(intArray);
System.out.println(text);
var intList = List.of(2, 3);

原始类型

Java 语言提供了八种原始类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。Java 的基础类型包含了如下几种:

// boolean (true|false)
var result = true;
var anotherResult = false;
// char (character)
var firstLetter = 'j';
// int (signed 32 bits integer)
var numberOfLegs = 2;
// double (64 bits floating point)
var cost = 3.78;
// long and float
// 一些更特殊的类型,它们需要后缀(L或f)长(64位整数)并浮点(32位浮点数)
var longValue = 123L;
var floatValue = 123.5f;
// byte and short
// 还有字节(带符号的8位整数)和短(带符号的16位短整数),它们仅在定义对象时占用较少的内存
record CompactHeader(byte tag, short version) {}
short value = 12;
var result = value + value;Copy to clipboardErrorCopied

实际上,Java 中还存在另外一种基本类型 void,它也有对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作。

整型

Java 定义了四种整数类型:byte、short、int 和 long。所有这些类型都是有符号的、正值和负值。Java 不支持无符号、只支持正值的整数。许多其他计算机语言同时支持有符号和无符号整数。然而,Java 的设计者认为无符号整数是不必要的。具体来说,他们认为无符号的概念主要用于指定高阶位的行为,高阶位定义了整数值的符号。Java 通过添加一个特殊的无符号右移操作符,以不同的方式管理高阶位的含义。这样,就消除了对无符号整数类型的需求。

byte

byte 数据类型是 8 位、有符号的,以二进制补码表示的整数;最小值是 -128(-2^7) ,最大值是 127(2^7-1) ;默认值是 0 。byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int 类型的四分之一。

byte a = 100,byte b = -50。Copy to clipboardErrorCopied

计算机是用二进制来表示数据的,一个字节也就是 8 个比特位,其中最高位表示符号位(0 正 1 负),故 byte 的取值范围为 1000 0000 到 0111 1111。在 Java 中,是采用补码来表示数据的,正数的补码和原码相同,负数的补码是在原码的基础上各位取反然后加 1。1000 000 是补码,减一然后按位取反得到其原码 1000 0000。(减一得 0111 1111,再按位取反得 1000 0000)。因为是负数,所以最小的 byte 值为 -2^7=-128。0111 1111 的十进制为 2^7-1=127(等比序列求和)。byte 是一个字节,共有 2^8=256 种可能性,也就是 -128~127。

其他基本数据类型同理,char 没有负值,占两个字节,所以取值范围是 0~2^16-1(65535)。

short

short 数据类型是 16 位、有符号的以二进制补码表示的整数,最小值是 -32768(-2^15) ;最大值是 32767(2^15 - 1) 。short 数据类型也可以像 byte 那样节省空间。一个 short 变量是 int 型变量所占空间的二分之一;默认值是 0

short s = 1000,short r = -20000。Copy to clipboardErrorCopied

int

int 数据类型是 32 位、有符号的以二进制补码表示的整数;最小值是 -2,147,483,648(-2^31) ;最大值是 2,147,483,647(2^31 - 1) ;默认值是 0

一般地整型变量默认为 int 类型,除了其他用途之外,int 类型的变量通常被用来控制循环和索引数组。虽然您可能认为在不需要 int 的较大范围的情况下,使用字节或 short 会比使用 int 更有效,但事实可能并非如此。原因是,当在表达式中使用字节和短值时,当表达式执行时,它们会被提升为 int。因此,当需要使用整数时,int 通常是最佳选择。

int a = 100000, int b = -200000Copy to clipboardErrorCopied

long

long 数据类型是 64 位、有符号的以二进制补码表示的整数;最小值是 -9,223,372,036,854,775,808(-2^63) ;最大值是 9,223,372,036,854,775,807(2^63 -1) ;默认值是 0L

long a = 100000L,Long b = -200000LCopy to clipboardErrorCopied

"L"理论上不分大小写,但是若写成"l"容易与数字"1"混淆,不容易分辩。所以最好大写。这种类型主要使用在需要比较大整数的系统上;

public class Light {
    public static void main(String[] args) {
        int lightspeed;
        long days;
        long seconds;
        long distance;
        lightspeed = 186000;
        days = 1000;
        seconds = days * 24 * 60 * 60;
        distance = lightspeed * seconds;
        System.out.print("In " + days);
        System.out.print(" days light will travel about ");
        System.out.println(distance + " miles.");
    }
}Copy to clipboardErrorCopied

浮点型

浮点数,也称为实数,用于评估需要分数精度的表达式。例如,诸如平方根,或正弦和余弦等超常数的计算,其值的精度需要使用浮点类型。Java 实现了标准的(IEEE-754)浮点类型和运算符集。浮点类型有两种,float 和 double,分别表示单精度和双精度的数字。

float

float 数据类型是单精度、32 位、符合 IEEE 754 标准的浮点数,默认值是 0.0f 。float 在储存大型浮点数组的时候可节省内存空间;单精度在某些处理器上速度更快,占用的空间是双精度的一半,但当值非常大或非常小时,会变得不精确。当你需要一个分数部分,但不需要很大程度的精度时,float 类型的变量是有用的。例如,当表示美元和美分时,float 可以很有用。

float f1 = 234.5fCopy to clipboardErrorCopied

double

double 数据类型是双精度、64 位、符合 IEEE 754 标准的浮点数;浮点数的默认类型为 double 类型;默认值是 0.0d 。双精度,正如双关键字所表示的那样,使用 64 位来存储一个值。在一些为高速数学计算而优化的现代处理器上,双精度实际上比单精度快。所有的超越数学函数,如 sin( )、cos( )和 sqrt( ),都会返回 double 类型。当你需要在多次迭代计算中保持精度,或者要处理大值数字时,double 是最佳选择。

double d1 = 123.4
public class Area {
    public static void main(String[] args) {
        double pi, r, a;
        r = 10.8;
        pi = 3.1416;
        a = pi * r * r;
        System.out.println("Area of circle is " + a);
    }
}Copy to clipboardErrorCopied

char

在 Java 中,用于存储字符的数据类型是 char。需要理解的一个关键点是,Java 使用 Unicode 来表示字符。Unicode 定义了一个完全国际化的字符集,可以代表所有人类语言中的所有字符。它是拉丁文、希腊文、阿拉伯文、西里尔文、希伯来文、片假名、汉字等几十个字符集的统一。在 Java 创建的时候,Unicode 要求 16 位。因此,在 Java 中 char 是一个 16 位的类型。char 的范围是 0 到 65,536。没有负数的 char。被称为 ASCII 的标准字符集仍然一如既往地从 0 到 127,而扩展的 8 位字符集 ISO-Latin-1 的范围是 0 到 255。由于 Java 的设计是为了让程序能够被编写为全球使用,所以使用 Unicode 来表示字符是有道理的。当然,对于英语、德语、西班牙语或法语等语言来说,使用 Unicode 的效率有些低,因为这些语言的字符可以很容易地包含在 8 位之内。但这就是全球可移植性必须付出的代价。

char 类型是一个单一的 16 位 Unicode 字符;最小值是 \u0000 (即为 0);最大值是 \uffff (即为 65,535)。char 数据类型可以储存任何字符;

char letter = 'A';
public class CharDemo {
    public static void main(String[] args) {
        char ch1, ch2;
        ch1 = 88;
        ch2 = 'Y';
        System.out.print("ch1 and ch2: ");
        System.out.println(ch1 + " " + ch2);
    }
}Copy to clipboardErrorCopied

请注意,ch1 被分配的值是 88,这是对应于字母 X 的 ASCII(和 Unicode)值,如前所述,ASCII 字符集占据了 Unicode 字符集的前 127 个值。出于这个原因,所有在其他语言中使用过的字符的老把戏在 Java 中也能用。虽然 char 被设计成用来存放 Unicode 字符,但它也可以被用作整数类型,你可以在上面进行算术运算。例如,您可以将两个字符相加,或者递增一个字符变量的值。

public class CharDemo2 {
    public static void main(String[] args) {
        char ch1;
        ch1 = 'X';
        System.out.println("ch1 contains " + ch1);
        ch1++;
        System.out.println("ch1 is now " + ch1);
    }
}Copy to clipboardErrorCopied

在程序中,首先给 ch1 赋值 X,然后,ch1 被递增,结果 ch1 包含 Y,即 ASCII(和 Unicode)序列中的下一个字符。这将导致 ch1 包含 Y,即 ASCII(和 Unicode)序列中的下一个字符。

boolean

boolean 数据类型表示一位的信息;只有两个取值:true 和 false;默认值是 false

类型默认值

对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。请看下面的例子:

public class PrimitiveTypeTest {
    public static void main(String[] args) {
        // byte
        System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);
        System.out.println("包装类:java.lang.Byte");
        System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);
        System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);
        System.out.println();
        // short
        System.out.println("基本类型:short 二进制位数:" + Short.SIZE);
        System.out.println("包装类:java.lang.Short");
        System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);
        System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);
        System.out.println();
        // int
        System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);
        System.out.println("包装类:java.lang.Integer");
        System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);
        System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);
        System.out.println();
        // long
        System.out.println("基本类型:long 二进制位数:" + Long.SIZE);
        System.out.println("包装类:java.lang.Long");
        System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);
        System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);
        System.out.println();
        // float
        System.out.println("基本类型:float 二进制位数:" + Float.SIZE);
        System.out.println("包装类:java.lang.Float");
        System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);
        System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);
        System.out.println();
        // double
        System.out.println("基本类型:double 二进制位数:" + Double.SIZE);
        System.out.println("包装类:java.lang.Double");
        System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);
        System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);
        System.out.println();
        // char
        System.out.println("基本类型:char 二进制位数:" + Character.SIZE);
        System.out.println("包装类:java.lang.Character");
        // 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台
        System.out.println("最小值:Character.MIN_VALUE="
                + (int) Character.MIN_VALUE);
        // 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台
        System.out.println("最大值:Character.MAX_VALUE="
                + (int) Character.MAX_VALUE);
    }
}Copy to clipboardErrorCopied

下表列出了 Java 各个类型的默认值:

数据类型 默认值
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char 'u0000'
String (or any object) null
boolean false

引用类型

在 Java 中,引用类型的变量非常类似于 C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型,比如 Employee、Puppy 等。变量一旦声明后,类型就不能被改变了。对象、数组都是引用数据类型,所有引用类型的默认值都是 null;一个引用变量可以用来引用任何与之兼容的类型。例子:Site site = new Site("JiangGuo")

枚举类型

枚举类型(Enumerated Type)很早就出现在编程语言中,它被用来将一组类似的值包含到一种类型当中。而这种枚举类型的名称则会被定义成独一无二的类型描述符,在这一点上和常量的定义相似。不过相比较常量类型,枚举类型可以为申明的变量提供更大的取值范围。为了改进 Java 语言在这方面的不足弥补缺陷,5.0 版本 SDK 发布时候,在语言层面上增加了枚举类型。枚举类型的定义也非常的简单,用 enum 关键字加上名称和大括号包含起来的枚举值体即可:

// 定义一周七天的枚举类型
public enum WeekDay { Mon, Tue, Wed, Thu, Fri, Sat, Sun }
// 读取当天的信息
WeekDay today = readToday();
// 根据日期来选择进行活动
switch(today) {
 Mon: do something; break;
 Tue: do something; break;
 Wed: do something; break;
 Thu: do something; break;
 Fri: do something; break;
 Sat: play sports game; break;
 Sun: have a rest; break;
}Copy to clipboardErrorCopied

最直接的益处就是扩大 switch 语句使用范围。5.0 之前,Java 中 switch 的值只能够是简单类型,比如 int、byte、short、char, 有了枚举类型之后,就可以使用对象了。需要注意的是,Java 中的枚举类型实际上会被编译为类文件,值即是这个类型的成员变量,譬如我们丰富下前文的枚举类型:

public enum WeekDay {
  Mon("Monday"),
  Tue("Tuesday"),
  Wed("Wednesday"),
  Thu("Thursday"),
  Fri("Friday"),
  Sat("Saturday"),
  Sun("Sunday");
  private final String day;
  private WeekDay(String day) {
    this.day = day;
  }
  public static void printDay(int i) {
    switch (i) {
      case 1:
        System.out.println(WeekDay.Mon);
        break;
      // ...
        default:
        System.out.println("wrong number!");
    }
  }
  public String getDay() {
    return day;
  }
}Copy to clipboardErrorCopied

自定义枚举类型

在 JDK5 之前,Java 语言不支持枚举类型,只能用类(class)来模拟实现枚举类型。

/** 订单状态枚举 */
public final class OrderStatus {
  /** 属性相关 */
  /** 状态取值 */
  private final int value;
  /** 状态描述 */
  private final String description;
  /** 常量相关 */
  /** 已创建(1) */
  public static final OrderStatus CREATED = new OrderStatus(1, "已创建");
  /** 进行中(2) */
  public static final OrderStatus PROCESSING = new OrderStatus(2, "进行中");
  /** 已完成(3) */
  public static final OrderStatus FINISHED = new OrderStatus(3, "已完成");
  /** 构造函数 */
  private OrderStatus(int value, String description) {
    this.value = value;
    this.description = description;
  }
  /** 获取状态取值 */
  public int getValue() {
    return value;
  }
  /** 获取状态描述 */
  public String getDescription() {
    return description;
  }
}