类型

编辑

类型是对数据进行分类的方式,用于定义值的属性。这些属性指定了值表示的数据以及值在操作期间的求值规则。每种类型都属于以下类别之一:原始引用动态

原始类型

编辑

原始类型表示 JVM 中原生构建的基本数据,并分配到非堆内存。声明一个原始类型变量或访问原始类型成员字段(来自引用类型实例),并为其分配一个原始类型值,以便在后续操作中进行求值。新声明的原始类型变量的默认值在下面的定义中列出。原始类型值在赋值期间或作为方法/函数调用的参数时会被复制。

原始类型具有相应的引用类型(也称为装箱类型)。使用字段访问操作符方法调用操作符对原始类型值强制求值为其对应的引用类型值。

以下是可用的原始类型。相应的引用类型在括号中列出。例如,Bytebyte 原始类型的引用类型。

可用的原始类型
byte (Byte)
8 位,有符号,二进制补码整数。范围:[-128, 127]。默认值:0
short (Short)
16 位,有符号,二进制补码整数。范围:[-32768, 32767]。默认值:0
char (Character)
16 位,无符号,Unicode 字符。范围:[0, 65535]。默认值:0\u0000
int (Integer)
32 位,有符号,二进制补码整数。范围:[-2^31, 2^31-1]。默认值:0
long (Long)
64 位,有符号,二进制补码整数。范围:[-2^63, 2^63-1]。默认值:0
float (`Float)`
32 位,有符号,单精度,IEEE 754 浮点数。默认值:0.0
double (Double)
64 位,有符号,双精度,IEEE 754 浮点数。默认值:0.0
boolean (Boolean)
逻辑量,具有两个可能的值:truefalse。默认值:false

示例

  • 在声明、声明和赋值中使用的原始类型。

    int i = 1;        
    double d;         
    boolean b = true; 

    声明 int i; 将 int 1 存储到 i

    声明 double d; 将默认 double 0.0 存储到 d

    声明 boolean b; 将 boolean true 存储到 b

  • 使用相应的引用类型对原始类型进行方法调用。

    int i = 1;    
    i.toString(); 

    声明 int i; 将 int 1 存储到 i

    i 加载 → int 1; 装箱 int 1Integer 1 引用; 在 Integer 1 引用 上调用 toStringString '1'

引用类型

编辑

引用类型是命名的构造(对象),可能表示多个数据片段(成员字段)和用于操作该数据的逻辑(成员方法),定义为脚本的应用程序编程接口 (API) 的一部分。

引用类型实例是分配给堆的单个引用类型对象的一组数据。使用新实例操作符来分配引用类型实例。使用引用类型实例来加载、存储和操作复杂数据。

引用类型值引用引用类型实例,并且多个引用类型值可能引用同一个引用类型实例。对引用类型实例的更改将影响引用该特定实例的所有引用类型值。

声明一个引用类型变量或访问引用类型成员字段(来自引用类型实例),并为其分配一个引用类型值,以便在后续操作中进行求值。新声明的引用类型变量的默认值为 null。引用类型值在赋值期间或作为方法/函数调用的参数时会被浅拷贝。将 null 赋值给引用类型变量表示引用类型值不引用任何引用类型实例。当没有任何引用类型值引用某个引用类型实例时,JVM 将对其进行垃圾回收。将 null 作为参数传递给方法/函数调用表示该参数不引用任何引用类型实例。

引用类型对象定义了零个到多个以下各项:

静态成员字段
静态成员字段是命名的且类型化的数据片段。每个引用类型对象都包含一组代表其静态成员字段的数据。使用字段访问操作符以及引用类型对象名称来访问静态成员字段,以便加载和存储到特定的引用类型对象。使用静态成员字段不需要分配引用类型实例。
非静态成员字段
非静态成员字段是命名的且类型化的数据片段。每个引用类型实例都包含一组代表其引用类型对象的非静态成员字段的数据。使用字段访问操作符来加载和存储到特定引用类型实例的非静态成员字段。使用非静态成员字段需要分配引用类型实例。
静态成员方法
静态成员方法是在引用类型对象上调用的函数。使用方法调用操作符以及引用类型对象名称来调用静态成员方法。使用静态成员方法不需要分配引用类型实例。
非静态成员方法
非静态成员方法是在引用类型实例上调用的函数。在引用类型实例上调用的非静态成员方法可以从该特定引用类型实例的非静态成员字段加载数据并存储到其中。使用方法调用操作符以及特定的引用类型实例来调用非静态成员方法。使用非静态成员方法需要分配引用类型实例。
构造函数
构造函数是一种特殊类型的函数,用于分配由特定引用类型对象定义的引用类型实例。使用新实例操作符来分配引用类型实例。

引用类型对象遵循基本继承模型。考虑类型 A 和 B。如果 B 继承(能够访问 A 的所有非静态成员作为自己的成员),则认为类型 A 是 B 的父类型,而 B 是 A 的子类型。如果从 B 到 A 存在递归的父子关系,且两者之间有零到多个类型,则认为类型 B 是 A 的后代。在这种情况下,B 继承了 A 的所有非静态成员以及两者之间所有类型的非静态成员。在这两种关系中,类型 B 也被认为是类型 A。

示例

  • 在几种不同的操作中求值的引用类型。

    List l = new ArrayList(); 
    l.add(1);                 
    int i = l.get(0) + 2;     

    声明 List l; 分配 ArrayList 实例 → ArrayList 引用; 将 ArrayList 引用 隐式转换为 List 引用List 引用; 将 List 引用 存储到 l

    l 加载 → List 引用; 将 int 1 隐式转换为 defdef; 使用参数 (def) 在 List 引用 上调用 add

    声明 int i; 从 l 加载 → List 引用; 使用参数 (int 0) 在 List 引用 上调用 getdef; 将 def 隐式转换为 int 1int 1; 将 int 1int 2 相加 → int 3; 将 int 3 存储到 i

  • 共享引用类型实例。

    List l0 = new ArrayList();     
    List l1 = l0;                  
    l0.add(1);                     
    l1.add(2);                     
    int i = l1.get(0) + l0.get(1); 

    声明 List l0; 分配 ArrayList 实例 → ArrayList 引用; 将 ArrayList 引用 隐式转换为 List 引用List 引用; 将 List 引用 存储到 l0

    声明 List l1; 从 l0 加载 → List 引用; 将 List 引用 存储到 l1(注意 l0l1 引用同一个实例,称为浅拷贝)

    l0 加载 → List 引用; 将 int 1 隐式转换为 defdef; 使用参数 (def) 在 List 引用 上调用 add

    l1 加载 → List 引用; 将 int 2 隐式转换为 defdef; 使用参数 (def) 在 List 引用 上调用 add

    声明 int i; 从 l0 加载 → List 引用; 使用参数 (int 0) 在 List 引用 上调用 getdef @0; 将 def @0 隐式转换为 int 1int 1; 从 l1 加载 → List 引用; 使用参数 (int 1) 在 List 引用 上调用 getdef @1; 将 def @1 隐式转换为 int 2int 2; 将 int 1int 2 相加 → int 3; 将 int 3 存储到 i

  • 使用引用类型的静态成员。

    int i = Integer.MAX_VALUE;       
    long l = Long.parseLong("123L"); 

    声明 int i; 从 IntegerMAX_VALUE 加载 → int 2147483647; 将 int 2147483647 存储到 i

    声明 long l; 使用参数 (long 123) 调用 Long 上的 parseLonglong 123; 将 long 123 存储到 l

动态类型

编辑

动态类型值可以使用单一类型名称 def 来表示任何原始类型或引用类型的值。def 类型值在运行时会模拟它所表示的任何值的行为,并且在操作期间求值时,始终表示任何类型值的最底层子类型值。

声明一个 def 类型变量或访问 def 类型成员字段(来自引用类型实例),并为其赋值任何类型的值,以便在后续操作期间进行求值。新声明的 def 类型变量的默认值为 nulldef 类型变量或方法/函数参数可以在脚本的编译和求值期间更改其表示的类型。

使用 def 类型可能会对性能产生轻微影响。当性能至关重要时,请直接使用原始类型和引用类型。

错误

  • 如果 def 类型值在运行时表示不适合操作求值的类型。

示例

  • def 类型的常用用法。

    def dp = 1;               
    def dr = new ArrayList(); 
    dr = dp;                  

    声明 def dp; 隐式转换 int 1defdef; 将 def 存储到 dp

    声明 def dr; 分配 ArrayList 实例 → ArrayList 引用; 隐式转换 ArrayList 引用defdef; 将 def 存储到 dr

    dp 加载 → def; 将 def 存储到 dr; (注意 dr 表示的类型从 ArrayList 切换为 int)

  • 表示值的最底层子类型的 def 类型值。

    Object l = new ArrayList(); 
    def d = l;                  
    d.ensureCapacity(10);       

    声明 Object l; 分配 ArrayList 实例 → ArrayList 引用; 隐式转换 ArrayList 引用Object 引用Object 引用; 将 Object 引用 存储到 l

    声明 def d; 从 l 加载 → Object 引用; 隐式转换 Object 引用defdef; 将 def 存储到 d;

    d 加载 → def; 隐式转换 defArrayList 引用ArrayList 引用; 使用参数 (int 10) 在 ArrayList 引用 上调用 ensureCapacity; (注意 def 被隐式转换为 ArrayList 引用,因为 ArrayList` 是 def 类型值所表示的最底层子类型值)

字符串类型

编辑

String 类型是一种特殊的引用类型,不需要显式分配。使用字符串字面量直接求值 String 类型值。虽然不是必需的,但新实例运算符可以分配 String 类型实例。

示例

  • String 类型的常用用法。

    String r = "some text";             
    String s = 'some text';             
    String t = new String("some text"); 
    String u;                           

    声明 String r; 将 String "some text" 存储到 r

    声明 String s; 将 String 'some text' 存储到 s

    声明 String t; 使用参数 (String "some text") 分配 String 实例 → String "some text"; 将 String "some text" 存储到 t

    声明 String u; 将默认 null 存储到 u

void 类型

编辑

void 类型表示缺少类型的概念。使用 void 类型来指示函数不返回值。

示例

  • 在函数中使用 void 类型。

    void addToList(List l, def d) {
        l.add(d);
    }

数组类型

编辑

数组类型是一种特殊的引用类型,其中数组类型实例包含一系列分配给堆的值。数组类型实例中的每个值都定义为一个元素。数组类型实例中的所有元素都属于声明中指定的相同类型(元素类型)。每个元素都被分配一个范围在 [0, length) 内的索引,其中 length 是为数组类型实例分配的元素总数。

使用新数组运算符数组初始化运算符来分配数组类型实例。声明一个数组类型变量或访问数组类型成员字段(来自引用类型实例),并为其赋值一个数组类型值,以便在后续操作期间进行求值。新声明的数组类型变量的默认值为 null。在赋值或作为方法/函数调用的参数期间,数组类型值会被浅复制。将 null 赋值给数组类型变量,表示该数组类型值不引用任何数组类型实例。当没有任何数组类型值引用数组类型实例时,JVM 将对其进行垃圾回收。将 null 作为参数传递给方法/函数调用,表示该参数不引用任何数组类型实例。

使用数组长度运算符检索数组类型值的长度作为 int 类型值。使用数组访问运算符从数组类型实例中的单个元素加载和存储数据。

当使用范围 [2, d](其中 d >= 2)分配具有多个维度的数组类型实例时,范围 [1, d-1] 中的每个维度中的每个元素也都是数组类型。每个维度 n 的元素类型都是一个数组类型,其维度数等于 d-n。例如,考虑具有 3 个维度的 int[][][]。第 3 维度 d-3 中的每个元素都是原始类型 int。第 2 维度 d-2 中的每个元素都是数组类型 int[]。第 1 维度 d-1 中的每个元素都是数组类型 int[][]

示例

  • 一维数组的常用用法。

    int[] x;                   
    float[] y = new float[10]; 
    def z = new float[5];      
    y[9] = 1.0F;               
    z[0] = y[9];               

    声明 int[] x; 将默认 null 存储到 x

    声明 float[] y; 分配长度为 [10]1-d float array 实例 → 1-d float array 引用; 将 1-d float array 引用 存储到 y

    声明 def z; 分配长度为 [5]1-d float array 实例 → 1-d float array 引用; 隐式转换 1-d float array 引用defdef; 将 def 存储到 z

    y 加载 → 1-d float array 引用; 将 float 1.0 存储到 1-d float array 引用index [9]

    y 加载 → 1-d float array 引用 @0; 从 1-d float array 引用 @0index [9] 加载 → float 1.0; 从 z 加载 → def; 隐式转换 def1-d float array 引用 @11-d float array 引用 @1; 将 float 1.0 存储到 1-d float array 引用 @1index [0]

  • 多维数组的常用用法。

    int[][][] ia3 = new int[2][3][4]; 
    ia3[1][2][3] = 99;                
    int i = ia3[1][2][3];             

    声明 int[][][] ia; 分配长度为 [2, 3, 4]3-d int array 实例 → 3-d int array 引用; 将 3-d int array 引用 存储到 ia3

    ia3 加载 → 3-d int array 引用; 将 int 99 存储到 3-d int array 引用index [1, 2, 3]

    声明 int i; 从 ia3 加载 → 3-d int array 引用; 从 3-d int array 引用index [1, 2, 3] 加载 → int 99; 将 int 99 存储到 i