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 的数组:

目录
相关文章
|
10天前
|
XML 存储 Java
11:Servlet中初始化参数的获取与应用-Java Web
11:Servlet中初始化参数的获取与应用-Java Web
25 3
|
1天前
|
存储 算法 搜索推荐
滚雪球学Java(27):从零开始学习数组:定义和初始化
【5月更文挑战第2天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
7 3
|
1天前
|
安全 Java 调度
Java一分钟:多线程编程初步:Thread类与Runnable接口
【5月更文挑战第11天】本文介绍了Java中创建线程的两种方式:继承Thread类和实现Runnable接口,并讨论了多线程编程中的常见问题,如资源浪费、线程安全、死锁和优先级问题,提出了解决策略。示例展示了线程通信的生产者-消费者模型,强调理解和掌握线程操作对编写高效并发程序的重要性。
25 3
|
2天前
|
Java
【JAVA基础篇教学】第五篇:Java面向对象编程:类、对象、继承、多态
【JAVA基础篇教学】第五篇:Java面向对象编程:类、对象、继承、多态
|
3天前
|
存储 安全 Java
Java容器类List、ArrayList、Vector及map、HashTable、HashMap
Java容器类List、ArrayList、Vector及map、HashTable、HashMap
|
3天前
|
Java 编译器 开发者
Java一分钟之-继承:复用与扩展类的特性
【5月更文挑战第9天】本文探讨了Java中的继承机制,通过实例展示了如何使用`extends`创建子类继承父类的属性和方法。文章列举了常见问题和易错点,如构造器调用、方法覆盖、访问权限和类型转换,并提供了解决方案。建议深入理解继承原理,谨慎设计类结构,利用抽象类和接口以提高代码复用和扩展性。正确应用继承能构建更清晰、灵活的代码结构,提升面向对象设计能力。
9 0
|
3天前
|
Java
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
13 0
|
3天前
|
Java API 调度
【Java多线程】Thread类的基本用法
【Java多线程】Thread类的基本用法
6 0
|
3天前
|
SQL Java 数据库连接
JDBC Java标准库提供的一些api(类+方法) 统一各种数据库提供的api
JDBC Java标准库提供的一些api(类+方法) 统一各种数据库提供的api
9 0
|
4天前
|
Java
Java一分钟之-类与对象:面向对象编程入门
【5月更文挑战第8天】本文为Java面向对象编程的入门指南,介绍了类与对象的基础概念、常见问题及规避策略。文章通过代码示例展示了如何定义类,包括访问修饰符的适当使用、构造器的设计以及方法的封装。同时,讨论了对象创建与使用时可能遇到的内存泄漏、空指针异常和数据不一致等问题,并提供了相应的解决建议。学习OOP需注重理论与实践相结合,不断编写和优化代码。
26 1