数组
在 Java 代码中数组是动态创建的对象。一个数组可以包含若干个同类型的变量。这些变量可以是基本类型或者对象引用,一个数组甚至可以包含其他数组。
声明数组变量
声明数组变量时,代码创建一个变量用于包含对数组对象的引用。它不创建数组对象或者为数组元素分配空间。在声明时指定数组大小是非法的。方括号可以作为类型的一部分出现在声明的开始处,也可以作为数组标识符的一部分:
int[] i; // array of int
byte b[]; // array of byte
Object[] o, // array of Object
short s[][]; // array of arrays of short
构建数组
可以用 new 操作符构建一个数组。需要包括数组的大小和它所包含的元素的类型。对于多维数组,可以只指定第一维大小:
int [] marks = new int[100];
String[][] s = new String[3][];
初始化数组
数组的初始化值可以写成花括号中的一列由逗号分隔的表达式:
String s[] = { new String("apple"),new String("mango") };
int i[][] = { {1, 2}, {3,4} };
也可以用循环来初始化数组:
int i[] = new int[5];
for(int j = 0; j < i.length;j++)
{
i[j] = j;
}
访问数组元素
数组索引是从 0 开始的,到 n-1 结束,其中 n 是数组大小。要得到数组大小,需要使用名为 length 的数组实例变量。如果试图访问 0 到 n-1 范围之外的索引值,那么就会抛出一个 ArrayIndexOutOfBoundsException。
声明类、变量和方法
现在我们来看一下用于修饰类、方法和变量的方法。有两种修饰符 -- 访问修饰符和非访问修饰符。访问修饰符让我们可以对代码限制访问或者提供更多的访问。
类修饰符
可用的访问修饰符有 public、 private 和 protected。不过,一个顶级类只能有 public 和默认的访问级别。如果没有指定访问修饰符,那么这个类将具有默认访问。只有在同一包中的类可以看到具有默认访问的类。如果一个类被声明为 public,那么其他包中的类也可以访问它。
让我们看看一些类的非访问修饰符的效果。 final 关键词(关于关键词的更多内容请参阅 Java 关键词和标识符 ) 不允许对类进行扩展。 abstract 类不能实例化,但是可以被子类扩展:
public final class Apple {..}
class GreenApple extends Apple {} // Not allowed, compile time error
方法修饰符和变量修饰符
所有访问修饰符都可以用于类的成员。私有成员只能从类内部访问。被保护的成员只能被同一包中的类或者该类的子类访问。公有成员可以被所有其他类访问。
如果没有指定访问修饰符,那么这些成员将具有默认访问,只有在同一包中的其他类可以访问它们。
现在我们来探讨可以用于成员声明的其他修饰符。其中一些只能用于方法,另一些只能用于变量,如下图所示:
图 1. 方法和变量的修饰符
synchronized 方法在某一时刻只能被一个线程访问。 Transient 变量不能被序列化。 abstract 方法不具有实现,它必须通过其包含类的第一个具体子类来实现。起码包含一个 abstract 方法的类必须声明为 abstract。不过, abstract 类不一定需要包含 abstract 方法:
public abstract class MyAbstractClass
{
public abstract void test();
}
native 修饰符表明这个方法不是用 Java 语言而是由一种本机语言编写的。 strictfp 关键词(关于关键词的更多内容请参阅 Java 关键词和标识符 )只用于方法和类,它强制使浮点符合 IEE754 标准。变量可以声明为 volatile,这种情况下,线程必须在每次访问这个变量时协调字段的工作副本与主副本。
Static 变量被类的所有实例所共享。可以在该类根本就没有任何实例的情况下使用 Static 方法和变量:
class StaticTest
{
static int i = 0;
static void getVar()
{
i++;
System.out.println(i);
}
}
class Test
{
public static void main(String args[])
{
StaticTest.getVar();
}
}
构造函数 在用类创建对象时使用构造函数。构造函数名必须与类名相匹配并且必须没有返回类型。它们可以被重载,但是不被子类继承。 调用构造函数 只能从其他构造函数中调用构造函数。要调用同一个类中的构造函数,用匹配的参数调用 this() 函数。要调用超类中的构造函数,用匹配的参数调用 super() 函数。创建子类对象时,按层次结构中从上到下的顺序调用所有超类的构造函数。 默认构造函数 如果没有在类中提供任何其他构造函数,编译器将创建默认构造函数。它没有任何参数。默认构造函数调用超类的无参数构造函数。它与类有同样的访问修饰符。 不过,哪怕在类中编写了一个构造函数,编译器就不会提供默认构造函数。例如,下面的类具有一个定义了两个参数的构造函数。这里如果我们试图不传递参数就实例化这个类,那么编译器将给出错误,因为没有默认构造函数:
class Dot
{
int x, y;
Dot(int x, int y)
{
this.x = x;
this.y = y;
}
}
如果调用类的默认构造函数,而超类没有不带参数的构造函数,那么您的代码将不能编译。原因是子类的默认构造函数隐式地调用其超类的无参数构造函数。例如:
class Dot
{
int x, y;
Dot(int x, int y)
{
this.x = x;
this.y = y;
}
}
class MyDot extends Dot { }
class Test
{
public static void main(String args[])
{
MyDot dot=new MyDot();
}
}
小结 本节我们讨论了第一项目标中的重要概念。我们讨论了声明和构建一维及多维数组的有效方法。在了解方法和类的修饰符的效果时,一定要掌握修饰符的合法组合。例如,不能将一个 final 方法声明为 abstract。我们还学习了有关构造函数的内容。记住,编译器只有在您没有编写任何构造函数的情况下才会提供默认的无参数构造函数
练习
问题:
编译和运行以下程序的结果是什么?
class Box
{
int b,w;
void Box(int b,int w)
{
this.b = b;
this.w = w;
}
}
public class MyBox extends Box
{
MyBox()
{
super(10,15);
System.out.println(b + "," + w);
}
static public void main(String args[])
{
MyBox box = new MyBox();
}
}
选项:
A. 不能编译,main 方法没有正确声明
B. 输出 10,15
C. 输出 0,0
D. 以上都不是
正确答案:
D
说明:
这个程序不能编译,因为对于子类构造函数中的 super(10,15) 调用,在基类中没有匹配的构造函数。 void Box(int b, int w) 不是构造函数,因为它给出了返回类型。如果它是一个构造函数,那么变量 w 和 h 将被初始化为 10 和 15。这个程序就可以正确编译并输出 10,15 。 在 main() 方法的声明中没有错误, static 和 public 可以以任何顺序出现。