Java类的初始化和清理(六)

简介: Java类的初始化和清理

动态数组创建

如果在编写程序时,不确定数组中需要多少个元素,那么该怎么办呢?你可以直接使用 new 在数组中创建元素。下面例子中,尽管创建的是基本类型数组,new 仍然可以工作(不能用 new 创建单个的基本类型数组):

// housekeeping/ArrayNew.java
// Creating arrays with new
import java.util.*;
public class ArrayNew {
    public static void main(String[] args) {
        int[] a;
        Random rand = new Random(47);
        a = new int[rand.nextInt(20)];
        System.out.println("length of a = " + a.length);
        System.out.println(Arrays.toString(a));
    } 
}

输出:

length of a = 18
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

数组的大小是通过 Random.nextInt()随机确定的,这个方法会返回 0 到输入参数之间的一个值。 由于随机性,很明显数组的创建确实是在运行时进行的。此外,程序输出表明,数组元素中的基本数据类型值会自动初始化为空值(对于数字和字符是 0;对于布尔型是 false)。Arrays.toString()java.util 标准类库中的方法,会产生一维数组的可打印版本。


本例中,数组也可以在定义的同时进行初始化:

int[] a = new int[rand.nextInt(20)];

如果可能的话,应该尽量这么做。

如果你创建了一个非基本类型的数组,那么你创建的是一个引用数组。以整型的包装类型 Integer 为例,它是一个类而非基本类型:

// housekeeping/ArrayClassObj.java
// Creating an array of nonprimitive objects
import java.util.*;
public class ArrayClassObj {
    public static void main(String[] args) {
        Random rand = new Random(47);
        Integer[] a = new Integer[rand.nextInt(20)];
        System.out.println("length of a = " + a.length);
        for (int i = 0; i < a.length; i++) {
            a[i] = rand.nextInt(500); // Autoboxing
        }
        System.out.println(Arrays.toString(a));
    }
}

输出:

length of a = 18
[55, 193, 361, 461, 429, 368, 200, 22, 207, 288, 128, 51, 89, 309, 278, 498, 361, 20]

这里,即使使用 new 创建数组之后:

Integer[] a = new Integer[rand.nextInt(20)];  

它只是一个引用数组,直到通过创建新的 Integer 对象(通过自动装箱),并把对象赋值给引用,初始化才算结束:

a[i] = rand.nextInt(500);

如果忘记了创建对象,但试图使用数组中的空引用,就会在运行时产生异常。

也可以用花括号括起来的列表来初始化数组,有两种形式:

// housekeeping/ArrayInit.java
// Array initialization
import java.util.*;
public class ArrayInit {
    public static void main(String[] args) {
        Integer[] a = {
                1, 2,
                3, // Autoboxing
        };
        Integer[] b = new Integer[] {
                1, 2,
                3, // Autoboxing
        };
        System.out.println(Arrays.toString(a));
        System.out.println(Arrays.toString(b));
    }
}

输出:

[1, 2, 3]
[1, 2, 3]

在这两种形式中,初始化列表的最后一个逗号是可选的(这一特性使维护长列表变得更容易)。


尽管第一种形式很有用,但是它更加受限,因为它只能用于数组定义处。第二种和第三种形式可以用在任何地方,甚至用在方法的内部。例如,你创建了一个 String 数组,将其传递给另一个类的 main() 方法,如下:

// housekeeping/DynamicArray.java
// Array initialization
public class DynamicArray {
    public static void main(String[] args) {
        Other.main(new String[] {"fiddle", "de", "dum"});
    }
}
class Other {
    public static void main(String[] args) {
        for (String s: args) {
            System.out.print(s + " ");
        }
    }
}

输出:

fiddle de dum 

Other.main()的参数是在调用处创建的,因此你甚至可以在方法调用处提供可替换的参数。

可变参数列表

类似 C 语言的可变参数列表(C 通常把它称为"varargs")来创建和调用方法。

这可应用在参数个数或类型未知的场景。由于所有类都最后继承自 Object 类,所以你可以创建一个以 Object 数组为参数的方法,类似如下调用:

// housekeeping/VarArgs.java
// Using array syntax to create variable argument lists
class A {}
public class VarArgs {
    static void printArray(Object[] args) {
        for (Object obj: args) {
            System.out.print(obj + " ");
        }
        System.out.println();
    }
    public static void main(String[] args) {
        printArray(new Object[] {47, (float) 3.14, 11.11});
        printArray(new Object[] {"one", "two", "three"});
        printArray(new Object[] {new A(), new A(), new A()});
    }
}

输出:

47 3.14 11.11 
one two three 
A@15db9742 A@6d06d69c A@7852e922

printArray() 的参数是 Object 数组,使用 for-in 语法遍历打印。标准 Java 库能输出有意义的内容,但这里创建的是类的对象,打印出的内容是类名,后面跟着一个 @ 符号以及多个十六进制数字。因而,默认行为(若未重写toString())就是打印类名和对象的地址。


你可能看到像上面这样编写的 Java 5 前的代码,可产生可变的参数列表。

Java 5 中,这种特性终于添加了进来,就像在 printArray() 中看到的那样:

// housekeeping/NewVarArgs.java
// Using array syntax to create variable argument lists
public class NewVarArgs {
    static void printArray(Object... args) {
        for (Object obj: args) {
            System.out.print(obj + " ");
        }
        System.out.println();
    }
    public static void main(String[] args) {
        // Can take individual elements:
        printArray(47, (float) 3.14, 11.11);
        printArray(47, 3.14F, 11.11);
        printArray("one", "two", "three");
        printArray(new A(), new A(), new A());
        // Or an array:
        printArray((Object[]) new Integer[] {1, 2, 3, 4});
        printArray(); // Empty list is OK
    }
}

输出:

47 3.14 11.11 
47 3.14 11.11 
one two three 
A@15db9742 A@6d06d69c A@7852e922 
1 2 3 4 

有了可变参数,你就不用再显式地编写数组语法,当你指定参数时,编译器实际上会为你填充数组。你获取的仍然是一个数组,这就是为什么 printArray() 可使用 for-in 迭代数组。

但这不仅仅只是从元素列表到数组的自动转换。注意程序的倒数第二行,一个 Integer 数组(通过自动装箱创建)被转型为一个 Object 数组(为了移除编译器的警告),并传递给了 printArray()。显然,编译器会发现这是一个数组,不会执行转换。因此,如果你有一组事物,可以把它们当作列表传递,而如果你已经有了一个数组,该方法会把它们当作可变参数列表来接受。


程序的最后一行表明,可变参数的个数可以为 0。当具有可选的尾随参数时,这一特性会有帮助:

// housekeeping/OptionalTrailingArguments.java
public class OptionalTrailingArguments {
    static void f(int required, String... trailing) {
        System.out.print("required: " + required + " ");
        for (String s: trailing) {
            System.out.print(s + " ");
        }
        System.out.println();
    }
    public static void main(String[] args) {
        f(1, "one");
        f(2, "two", "three");
        f(0);
    }
}

输出:

required: 1 one 
required: 2 two three 
required: 0 

这段程序展示了如何使用除了 Object 类之外类型的可变参数列表。这里,所有的可变参数都是 String 对象。可变参数列表中可以使用任何类型的参数,包括基本类型。下面例子展示了可变参数列表变为数组的情形,并且如果列表中没有任何元素,那么转变为大小为 0 的数组:

目录
相关文章
|
4月前
|
Java 编译器 API
Java 密封类:精细化控制继承关系
Java 密封类:精细化控制继承关系
347 83
|
2月前
|
安全 Java 数据建模
Java记录类:简化数据载体的新选择
Java记录类:简化数据载体的新选择
232 101
|
2月前
|
安全 Java 开发者
Java记录类:简化数据载体的新方式
Java记录类:简化数据载体的新方式
287 100
|
5月前
|
IDE Java 数据挖掘
Java 基础类从入门到精通实操指南
这份指南专注于**Java 17+**的新特性和基础类库的现代化用法,涵盖开发环境配置、数据类型增强(如文本块)、字符串与集合处理进阶、异常改进(如密封类)、IO操作及实战案例。通过具体代码示例,如CSV数据分析工具,帮助开发者掌握高效编程技巧。同时提供性能优化建议和常用第三方库推荐,适合从入门到精通的Java学习者。资源链接:[点此下载](https://pan.quark.cn/s/14fcf913bae6)。
227 36
|
3月前
|
安全 IDE Java
Java记录类型(Record):简化数据载体类
Java记录类型(Record):简化数据载体类
419 143
|
1月前
|
存储 Java 索引
用Java语言实现一个自定义的ArrayList类
自定义MyArrayList类模拟Java ArrayList核心功能,支持泛型、动态扩容(1.5倍)、增删改查及越界检查,底层用Object数组实现,适合学习动态数组原理。
87 4
|
1月前
|
IDE JavaScript Java
在Java 11中,如何处理被弃用的类或接口?
在Java 11中,如何处理被弃用的类或接口?
165 5
|
1月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
143 1
|
1月前
|
Java Go 开发工具
【Java】(8)正则表达式的使用与常用类分享
正则表达式定义了字符串的模式。正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。
203 1
|
1月前
|
存储 Java 程序员
【Java】(6)全方面带你了解Java里的日期与时间内容,介绍 Calendar、GregorianCalendar、Date类
java.util 包提供了 Date 类来封装当前的日期和时间。Date 类提供两个构造函数来实例化 Date 对象。第一个构造函数使用当前日期和时间来初始化对象。Date( )第二个构造函数接收一个参数,该参数是从1970年1月1日起的毫秒数。
148 1