■ 关于String和StringBuffer
▲ “==”和“equals”的用法
“==”是比较两个变量的值是否相等,“equals”是比较两个对象变量所代表的对象的内容是否相等。
看一个例子:
class
StringTest
...
{

public static void main(String[] args)...{
String str1=new String("abc");
String str2=new String("abc");

if(str1==str2)...{
System.out.println("str1==str2");
}

else...{
System.out.println("str1!=str2");
}
}
}
编译运行:

str1与str2并不相等,这是因为用new为str1和str2在堆上分配了内存空间,即在堆上创建了两个对象。
而在栈内存为它们分配了引用空间,并未在栈内存中创建一个具体的对象,它们只是引用了堆内存中对象的地址值。
所以比较str1和str2时只是比较了它们作为引用变量所引用的地址,当然会不相等。
如果我们想要比较str1和str2所引用的对象是否相等,应该用equals方法,比如:
class
StringTest
...
{

public static void main(String[] args)...{
String str1=new String("abc");
String str2=new String("abc");

if(str1.equals(str2))...{
System.out.println("str1 equals str2");
}

else...{
System.out.println("str1 doesn't equal str2");
}
}
}
编译运行:

▲ java中的重载操作符
针对String的“+”和“+=”,是Java中唯一被重载的操作符;在Java中,不允许程序员重载操作符,如:
class
StringTest
...
{

public static void main(String[] args)...{
String str="abc";
int i=3;
float f=4.5f;
char ch='a';
boolean b=true;
System.out.println(str + i + f + ch + b);
}
}
编译运行:

▲ java中的String和StringBuffer类
String类对象一个常量对象,如:
String str="abc";
str="def";
而我们在处理大量字符串的程序中,我们通常用StringBuffer来替代String,如:
class
StringTest
...
{

public static void main(String[] args)...{
StringBuffer sb=new StringBuffer();
String str="abc";
int i=3;
float f=4.5f;
char ch='a';
boolean b=true;
System.out.println(sb.append(str).append(i).append(f).append(ch).append(b).toString());
}
}
编译运行:

StringBuffer类的方法:
append append(boolean) 把 boolean 型参数的字符串表示添加到字符串缓冲区。 append(char) 把 char 型参数的字符串表示添加到字符串缓冲区。 append(char[]) 把 char 型数组参数的字符串表示添加到字符串缓冲区。 append(char[], int, int) 把 char 型数组参数的子数组的字符串表示添加到字符串缓冲区。 append(double) 把 double 型参数的字符串表示添加到该字符串缓冲区。 append(float) 把 float 型参数的字符串表示添加到该字符串缓冲区。 append(int) 把 int 型参数的字符串表示添加到该字符串缓冲区。 append(long) 把 long 型参数的字符串表示添加到该字符串缓冲区。 append(Object) 把 Object 型参数的字符串表示添加到该字符串缓冲区。 append(String) 添加字符串到字符串缓冲区。 toString 把字符串缓冲区的数据转换为字符串表示。 delete public StringBuffer delete(int start, int end) 移除此序列的子字符串中的字符。该子字符串从指定的 start 处开始,一直到索引 end - 1 处的字符; 如果不存在这种字符,则一直到序列尾部。如果 start 等于 end,则不发生任何更改。 抛出: StringIndexOutOfBoundsException - 如果 start 为负、大于 length() 或大于 end。 deleteCharAt public StringBuffer deleteCharAt(int index)移除此序列指定位置的 char。此序列将缩短一个 char。 注意: 如果给定索引处的字符是增补字符,则此方法将不会移除整个字符。 如果需要准确处理增补字符,那么可以通过调用 Character.charCount(thisSequence.codePointAt(index))(用此序列取代 thisSequence)来确定要移除的 char 的数量。 抛出:StringIndexOutOfBoundsException - 如果 index 为负或大于等于 length()。 |
在Java中,boolean、byte、short、int、long、char、float、double这八种是基本数据类型,其余的都是引用类型。
实例程序如下:
class
Student
...
{
String name;
int age;

Student(String name,int age)...{
this.name=name;
this.age=age;
}

public static void main(String[] args)...{
Student[] stu;
stu=new Student[3];

for(int i=0;i<stu.length;i++)...{
System.out.println(stu[i]);
}
}
}
编译运行:

■ 关于public static void main(String[] args)函数
main函数args主要用来接受命令行参数。例如程序:
class
Student
...
{

public static void main(String[] args)...{

for(int i=0;i<args.length;i++)...{

if(i>=0)...{
System.out.println(args[i]);
}
}
}
}
编译运行:

我们在运行的时候要传递参数,这样键入的字符串被保存在String[]类的args里,然后再打印出来。
■ 函数的调用
在Java中,传参时,都是以传值的方式进行。
对于基本数据类型,传递的是数据的拷贝;对于引用类型,传递的引用的拷贝。
我们讨论一下,关于实现两个数交换的程序:
用下面方法不能实现:
class
Student
...
{

public static void exchange(int x,int y)...{
x=x+y;
y=x-y;
x=x-y;
}


public static void main(String[] args)...{
int x=100;
int y=48;
System.out.println("Before exchange x and y:x="+x+" "+"y="+y);
exchange(x,y);
System.out.println( "After exchange x and y:x="+x+" "+"y="+y);
}
}
编译运行:

可见,并没有实现交换。
因为在exchange函数中确实有交换的操作,但是main函数是static的,交换操作并没有影响main函数中x和y的值。
再看下面的程序,能够实现交换功能:
class
Student
...
{

public static void exchange(int[] num)...{
num[0]=num[0]+num[1];
num[1]=num[0]-num[1];
num[0]=num[0]-num[1];
}

public static void main(String[] args)...{
int[] num=new int[2];
num[0]=100;
num[1]=48;
System.out.println("Before exchange num[0] and num[1]:num[0]="+num[0]+" "+"num[1]="+num[1]);
exchange(num);
System.out.println( "After exchange num[0] and num[1]:num[0]="+num[0]+" "+"num[1]="+num[1]);
}
}
编译运行:

下面的程序,也能够实现交换功能:
public
static
void
exchange(Point pt)
...
{
pt.x=pt.x+pt.y;
pt.y=pt.x-pt.y;
pt.x=pt.x-pt.y;
}

public
static
void
main(String[] args)
...
{
Point pt=new Point();
pt.x=100;
pt.y=48;
System.out.println("Before exchange pt[x,y]:");
System.out.println(pt);
exchange(pt);
System.out.println( "After exchange pt[x,y]:");
System.out.println(pt);
}
}

class
Point
...
{ //定义了一个Point类
int x,y;
}
编译运行:

其中,运行结果中Point@后面的数字是pt对象的散列码,是根据对象的内存地址得到的。查看帮助文档,我们可以知道:
toString public String toString()返回该对象的字符串表示。通常,toString 方法会返回一个“以文本方式表示”此对象的字符串。结果应是一个简明但易于读懂。建议所有子类都重写此方法。 Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成。换句话说,该方法返回一个字符串,它的值等于: getClass().getName() + '@' + Integer.toHexString(hashCode()) 返回: 该对象的字符串表示形式。 |
我们遵照帮助文档说明,对toString进行重写后,代码如下:
class
Student
...
{

public static void exchange(Point pt)...{
pt.x=pt.x+pt.y;
pt.y=pt.x-pt.y;
pt.x=pt.x-pt.y;
}

public static void main(String[] args)...{
Point pt=new Point();
pt.x=100;
pt.y=48;
System.out.println("Before exchange pt[x,y]:");
System.out.println(pt);
exchange(pt);
System.out.println( "After exchange pt[x,y]:");
System.out.println(pt);
}
}

class
Point
...
{
int x,y;

public String toString()...{ //重写了toString方法
return "pt[x="+x+",y="+y+"]";
}
}
编译运行:

如果只是想实现打印出一个点,我们也可以利用sun公司提供的Point类来实现:
import
java.awt.
*
;

class
Student
...
{

public static void exchange(Point pt)...{
pt.x=pt.x+pt.y;
pt.y=pt.x-pt.y;
pt.x=pt.x-pt.y;
}

public static void main(String[] args)...{
Point pt=new Point();
pt.x=100;
pt.y=48;
System.out.println("Before exchange pt.x and pt.y:pt.x="+pt.x+" "+"pt.y="+pt.y);
exchange(pt);
System.out.println( "After exchange numpt.x and pt.y:pt.x="+pt.x+" "+"pt.y="+pt.y);
}
}
编译运行:

注意:必须加上import java.awt.*;因为Point类是该包中的类。
Point类中重写了toString方法。
我们也可以这样输出一个Point类的对象:
System.out.println(pt);
编译运行:

■ 对象的克隆(clone)
为了获取对象的一份拷贝,我们可以利用Object类的clone()方法,实现的一般步骤为:
1。在派生类中覆盖基类的clone()方法,并声明为public;
2。在派生类的clone()方法中,调用super.clone();
3。在派生类中实现Cloneable接口。
我们按照这个步骤,编写一个实现对象克隆的程序:
class
Student
implements
Cloneable
...
{ //必须实现Cloneable接口,否则会抛出异常
String name;
int age;

Student(String name,int age)...{
this.name=name;
this.age=age;
}

public Object clone()...{ //实现clone方法,并声明为public
Object obj=null;

try...{
obj=super.clone(); //调用super.clone()

}catch(CloneNotSupportedException e)...{
System.out.println(e.toString());
}
return obj;
}

public static void main(String[] args)...{
Student stu1=new Student("ZhangSan",18);
Student stu2=(Student)stu1.clone(); //克隆stu1

System.out.println("After cloning stu1,the value of stu1 is :");
System.out.println("[stu1.name="+stu1.name+","+"stu1.age="+stu1.age+"] ");

System.out.println("After cloning stu1,the value of stu2 is :");
System.out.println("[stu2.name="+stu2.name+","+"stu2.age="+stu2.age+"] ");

stu2.name="LiSi";
stu2.age=22;

System.out.println("After assigning new value,the value of stu2 is :");
System.out.println("[stu2.name="+stu2.name+","+"stu2.age="+stu2.age+"]");
}
}
编译运行:

看另外一个关于克隆的例子,程序如下:
class
Student
implements
Cloneable
...
{ //定义Student类,有三个成员变量;并且要实现Cloneable接口
String name;
int age;
Professor prof;

Student(String name,int age,Professor prof)...{
this.name=name;
this.age=age;
this.prof=prof;
}

public Object clone()...{
Object obj=null;

try...{
obj=super.clone();

}catch(CloneNotSupportedException e)...{
System.out.println(e.toString());
}
return obj;
}

public static void main(String[] args)...{
Professor prof=new Professor("WangWu",58);
Student stu1=new Student("ZhangSan",18,prof);
Student stu2=(Student)stu1.clone(); //克隆stu1

System.out.println("After cloning stu1,the value of stu1 is :");
System.out.println("[sname="+stu1.name+","+"sage="+stu1.age+","+"pname="+stu1.prof.name+","+"page="+stu1.prof.age+"] ");

System.out.println("After cloning stu1,the value of stu2 is :");
System.out.println("[sname="+stu2.name+","+"sage="+stu2.age+","+"pname="+stu2.prof.name+","+"page="+stu2.prof.age+"] ");

stu2.name="LiSi";
stu2.age=22;
stu2.prof.name="ZhaoLiu";
stu2.prof.age=35;

System.out.println("After assigning new value,the value of stu2 is :");
System.out.println("[sname="+stu2.name+","+"sage="+stu2.age+","+"pname="+stu2.prof.name+","+"page="+stu2.prof.age+"] ");

System.out.println("After assigning new value,the value of stu1 is :");
System.out.println("[sname="+stu1.name+","+"sage="+stu1.age+","+"pname="+stu1.prof.name+","+"page="+stu1.prof.age+"] ");
}
}

class
Professor
...
{ //定义一个Professor类
String name;
int age;

Professor(String name,int age)...{
this.name=name;
this.age=age;
}
}
编译运行:

可见,当stu1克隆后,stu2和stu1相同;但是当stu2修改以后,stu1的name和age都没有变化,stu2的name和age都变化了,stu1与stu2的prof都变化了。
这是因为:在修改stu2中的prof时是通过引用修改的,这使得Prof在内存中的副本被修改,同时stu1也是引用prof的,所以当内存中的副本被修改后stu1与stu2的引用的对象的内容都发生了变化。
而Student类的对象stu1中的name在stu2修改时没有发生改变,这是因为String对象是一个常量对象,当然如果stu2修改时不会影响到stu1。
上面实现的克隆一般成为浅层次的克隆,下面看一下深层次的克隆,程序如下:
class
Student
implements
Cloneable
...
{ //必须实现Cloneable接口,否则会抛出异常
String name;
int age;
Professor prof;

Student(String name,int age,Professor prof)...{
this.name=name;
this.age=age;
this.prof=prof;
}

public Object clone()...{
Student ostu=null; //定义一个Student变量ostu

try...{
ostu=(Student)super.clone(); //让Student类继承Object类的clone方法

}catch(CloneNotSupportedException e)...{
System.out.println(e.toString());
}
ostu.prof=(Professor)prof.clone(); //通过ostu引用prof,克隆prof
return ostu;
}

public static void main(String[] args)...{
Professor prof=new Professor("WangWu",58);
Student stu1=new Student("ZhangSan",18,prof);
Student stu2=(Student)stu1.clone();

System.out.println("After cloning stu1,the value of stu1 is :");
System.out.println("[sname="+stu1.name+","+"sage="+stu1.age+","+"pname="+stu1.prof.name+","+"page="+stu1.prof.age+"] ");

System.out.println("After cloning stu1,the value of stu2 is :");
System.out.println("[sname="+stu2.name+","+"sage="+stu2.age+","+"pname="+stu2.prof.name+","+"page="+stu2.prof.age+"] ");

stu2.name="LiSi";
stu2.age=22;
stu2.prof.name="ZhaoLiu";
stu2.prof.age=35;

System.out.println("After assigning new value,the value of stu2 is :");
System.out.println("[sname="+stu2.name+","+"sage="+stu2.age+","+"pname="+stu2.prof.name+","+"page="+stu2.prof.age+"] ");

System.out.println("After assigning new value,the value of stu1 is :");
System.out.println("[sname="+stu1.name+","+"sage="+stu1.age+","+"pname="+stu1.prof.name+","+"page="+stu1.prof.age+"] ");
}
}

class
Professor
implements
Cloneable
...
{ //必须实现Cloneable接口,要Professor类也支持克隆
String name;
int age;

Professor(String name,int age)...{
this.name=name;
this.age=age;
}

public Object clone()...{ //实现clone方法,并声明为public
Object obj=null;

try...{
obj=super.clone(); //调用super.clone()

}catch(CloneNotSupportedException e)...{
System.out.println(e.toString());
}
return obj;
}
}
编译运行:

可见,当stu2修改时,不会影响stu1的值(pname=WangWu,page=58没有改变)。
为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?
在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
■ 数组的相关操作
在Java中,所有的数组都有一个缺省的属性length,用于获取数组中元素的个数。
数组的复制:System.arraycopy()。
数组的排序:Arrays.sort()。
在已排序的数组中查找某个元素:Arrays.binarySearch()。
▲ 数组的复制
先看数组的复制:System.arraycopy(),如下程序:
class
ArrayTest

...
{
public static void main(String[] args)

...{

int[] num1=new int[]...{1,2,3};
int[] num2=new int[10];
System.arraycopy(num1,1,num2,8,2);
for(int i=0;i<num2.length;i++)

...{
System.out.println(num2[i]);
}
}
}
编译运行:

关于对象数组,程序如下:
class
ArrayTest

...
{
public static void main(String[] args)

...{

Point[] pts1=new Point[]...{new Point(1,1),new Point(2,2),new Point(3,3)};
Point[] pts2=new Point[3];
System.arraycopy(pts1,0,pts2,0,pts1.length);

for(int i=0;i<pts1.length;i++)...{
System.out.println("("+pts2[i].x+","+pts2[i].y+")");
}
}
}

class
Point
...
{
int x,y;

Point(int x,int y)...{
this.x=x;
this.y=y;
}
}
编译运行:

如果我们把System.out.println("("+pts2[i].x+","+pts2[i].y+")");改写成为:
System.out.println(pts2[i]);
则编译运行,输出结果为:

这我们已经不陌生了,@后面表示Point类的散列码。
▲ 数组的排序和搜索
实现代码如下:
import
java.util.
*
;
class
ArrayTest

...
{
public static void main(String[] args)

...{

int[] num=new int[]...{3,1,7,4};

for(int i=0;i<num.length;i++)...{
System.out.println(num[i]);
}
Arrays.sort(num); //执行排序
System.out.println("Sorting now...");

for(int i=0;i<num.length;i++)...{
System.out.println(num[i]);
}
int index=Arrays.binarySearch(num,7); //执行搜索
System.out.println("Searched element:"+num[index]+"'s position is index="+index);
}
}
编译运行:

注意:用binarySearch搜索时,我们的数组必须已经排好序了!
对象数组的排序:
程序如下:
import
java.util.
*
;
class
ArrayTest

...
{
public static void main(String[] args)

...{

Student[] stu=new Student[]...{
new Student(2000,"ksky"),
new Student(2098,"msky"),
new Student(1992,"vsky"),
new Student(1992,"esky")
};
System.out.println("Before sorting:");

for(int i=0;i<stu.length;i++)...{
System.out.println(stu[i]);
}
Arrays.sort(stu);
System.out.println(" After sorting:");

for(int i=0;i<stu.length;i++)...{
System.out.println(stu[i]);
}
}
}

class
Student
implements
Comparable
...
{
int num;
String name;

Student(int num,String name)...{
this.num=num;
this.name=name;
}

public String toString()...{
return "num="+num+","+"name="+name;
}

public int compareTo(Object obj)...{
Student stu=(Student)obj;
return num>stu.num?1:(num==stu.num?0:-1);
}
}
编译运行:
显然,num排序完成。但是name,我们想要让相同两个num为1992的学生的name按照字母排序应该如何实现呢?
这也就是应该让vsky在后而esky在前面,则只要将compareTo方法修改为:
public
int
compareTo(Object obj)
...
{
Student stu=(Student)obj;
int result=num>stu.num?1:(num==stu.num?0:-1);

if(0==result)...{
result=name.compareTo(stu.name);
}
return result;
}
编译运行:
可见,除了num排好序后,name也按照字母排序,即:vsky在后而esky在前面。
compareTo的用法:
java.lang.Comparable接口类的方法 int compareTo(T o) 比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。 在前面的描述中,符号 sgn(expression) 表示数学上的 signum 函数,该函数根据 expression 的值是负数、零还是正数,分别返回 -1、0 或 1。 实现类必须确保对于所有的 x 和 y,都存在 sgn(x.compareTo(y)) == -sgn(y.compareTo(x))。(这意味着如果 y.compareTo(x) 抛出一个异常,则 x.compareTo(y) 也要抛出一个异常。) 实现类还必须确保关系是可传递的:(x.compareTo(y)>0 && y.compareTo(z)>0) 意味着 x.compareTo(z)>0。 最后,实现程序必须确保 x.compareTo(y)==0 意味着对于所有的 z,都存在 sgn(x.compareTo(z)) == sgn(y.compareTo(z))。 强烈推荐 (x.compareTo(y)==0) == (x.equals(y)) 这种做法,但不是 严格要求这样做。一般来说,任何实现 Comparable 接口和违背此条件的类都应该清楚地指出这一事实。推荐如此阐述:“注意:此类具有与 equals 不一致的自然排序。” 参数: o - 要比较的对象。 返回: 负整数、零或正整数,根据此对象是小于、等于还是大于指定对象。 抛出: ClassCastException - 如果指定对象的类型不允许它与此对象进行比较。 |
■ 引用类型-封装类
针对八种基本数据类型定义的相应的引用类型-封装类。
基本数据类型
|
封装类
|
boolean | Boolean |
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
基本数据类型与封装类的转换:
实例代码如下:
class
Test
...
{

public static void main(String[] args)...{
int i=10;
Integer integer=new Integer(i); //将一个整型变量转换成Integer对象
int k=integer.intValue(); //调用Integer类的方法intValue方法得到整型的值
System.out.println("k="+k);
String str=integer.toString(); //将Integer类的对象转换成String
System.out.println("str="+str);
String string=new String("2098");
System.out.println(Integer.valueOf(string)); //valueOf是一个静态方法
}
}
编译运行:

■ Class类
在Java中,每个class都有一个相应的Class对象。
也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。
▲ 获取Class实例
获取Class实例的三种方式:
(1)利用对象调用getClass()方法获取该对象的Class实例;
(2)使用Class类的静态方法forName(),用类的名字获取一个Class实例;
(3)运用.class的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例。
Class类在java.lang包中。
由于Class类会抛出一个java.lang.ClassNotFoundException异常,所以需要进行捕捉:
class
ClassTest
...
{

public static void main(String[] args)...{
Point pt=new Point();
Class cls1=pt.getClass();
System.out.println(cls1.getName());

try...{
Class cls2=Class.forName("Point");
System.out.println(cls2.getName());

}catch(Exception e)...{
e.printStackTrace();
}
Class cls3=Point.class;
System.out.println(cls3.getName());

Class cls4=int.class;
System.out.println(cls4.getName());

Class cls5=Integer.class;
System.out.println(cls5.getName());

Class cls6=Integer.TYPE;
System.out.println(cls6.getName());
}
}

class
Point
...
{
int x,y;
}
编译运行:

否则,如果不用try...catch捕捉处理异常,编译就会抛出异常:

在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。
如果没有被加载,JVM会根据类的名称找到.class文件并加载它。
一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象。
▲ 类的加载
我们看看系统何时加载类:
class
ClassTest
...
{

public static void main(String[] args)...{
System.out.println("Before new Point()");
new Point();
System.out.println("After new Point");

try...{
Class.forName("Line"); //加载Line类

}catch(Exception e)...{
e.printStackTrace();
}
}
}

class
Point
...
{
int x,y;

static...{
System.out.println("Loading Polint"); //静态的代码块
}
}

class
Line
...
{

static...{
System.out.println("Loading Line");
}
}
编译运行:

可见,Point类在new之后才加载类。
在上面程序中,Line类直接用下面的方式实现类的加载:
Class.forName("Line");
▲ 使用newInstance()动态初始化类
通过键入命令行参数,使用newInstance()动态初始化一个类的实例:
class
ClassTest
...
{

public static void main(String[] args)...{

if(args.length!=1)...{ //通过命令行传入参数
return;
}

try...{
Class cls=Class.forName(args[0]); //加载传入的参数
Point pt=(Point)cls.newInstance();
pt.output();

}catch(Exception e)...{
e.printStackTrace();
}
}
}

class
Point
...
{
int x,y;

static...{
System.out.println("Loading Polint");
}

void output()...{
System.out.println("x="+x+","+"y="+y);
}
}

class
Line
...
{

static...{
System.out.println("Loading Line");
}
}
编译运行:

newInstance()的返回值是Object类型的,这里要强制转换为Point类型。
但是如果我们在Point类中定义带参数的构造函数:
Point(
int
x,
int
y)
...
{
this.x=x;
this.y=y;
}
则编译运行,在键入命令行参数后运行会出错:

出错是因为newInstance()调用类中缺省的构造方法。
那么,如何能够通过调用一个带参数的构造方法来创建一个类的实例呢?
我们可以利用映象API(Reflection API)实现,通过构造函数创建一个类的实例。
■ 反射API(Reflection API)
先看看如何获得构造方法:
import
java.lang.reflect.
*
;

class
ClassTest
...
{

public static void main(String[] args)...{

if(args.length!=1)...{
return;
}

try...{
Class cls=Class.forName(args[0]);
Constructor[] cons=cls.getDeclaredConstructors(); //定义构造器数组cons保存从类中获取的构造方法

for(int i=0;i<cons.length;i++)...{
System.out.println(cons[i]);
}
Method[] method=cls.getDeclaredMethods(); // 定义方法数组method存从类中获取的方法

for(int i=0;i<method.length;i++)...{
System.out.println(method[i]);
}

}catch(Exception e)...{
e.printStackTrace();
}
}
}

class
Point
...
{
int x,y;

static...{
System.out.println("Loading Polint");
}

Point(int x,int y)...{
this.x=x;
this.y=y;
}

void output()...{
System.out.println("x="+x+","+"y="+y);
}
}

class
Line
...
{

static...{
System.out.println("Loading Line");
}
}
编译运行:

实现调用构造方法的程序如下:
import
java.lang.reflect.
*
;

class
ClassTest
...
{

public static void main(String[] args)...{

if(args.length!=1)...{
return;
}

try...{
Class cls=Class.forName(args[0]);
Constructor[] cons=cls.getDeclaredConstructors(); //定义构造器数组cons保存从类中获取的构造方法
Class[] params=cons[0].getParameterTypes(); //获得构造方法标准参数类型,保存在Class类的对象数组params
Object[] paramValues=new Object[params.length]; //paramValues对象数组保存参数的值

for(int i=0;i<params.length;i++)...{

if(params[i].isPrimitive())...{ //判断如果params[i]是标准数据类型
paramValues[i]=new Integer(i+3); //则传递参数的值
}
Object obj=cons[0].newInstance(paramValues); //初始化构造器
Method[] method=cls.getDeclaredMethods();
method[0].invoke(obj,(Object)null);
}

}catch(Exception e)...{
e.printStackTrace();
}
}
}

class
Point
...
{
int x,y;

static...{
System.out.println("Loading Polint");
}

Point(int x,int y)...{
this.x=x;
this.y=y;
}

void output()...{
System.out.println("x="+x+","+"y="+y);
}
}

class
Line
...
{

static...{
System.out.println("Loading Line");
}
}
编译没有错误。
但是键入命令行参数时出现异常:
?????????????????????????????????????????????????????????????????????????????????????????
sunxin老师的代码也出现错误啊?????
?????????????????????????????????????????????????????????????????????????????????????????
D:/javae/Lesson4>javac ClassTest.java
D:/javae/Lesson4>java ClassTest Line
Loading Line
D:/javae/Lesson4>java ClassTest Point
Loading Polint
java.lang.IllegalArgumentException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstruct
orAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingC
onstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:494)
at ClassTest.main(ClassTest.java:16)
但是键入Line类却可以成功调用。
■ 设计模式
在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方案,这就是软件模式。
每一个模式描述了一个在我们程序设计中经常发生的问题,以及该问题的解决方案。
当我们碰到模式所描述的问题,就可以直接用相应的解决方法去解决这个问题,这就是设计模式。
▲ 单例(Singleton)模式
(1)一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类。
(2)单例类的一个最重要的特点是类的构造方法是私有的,从而避免了外部利用构造方法直接创建多个实例。
单例类的实现格式如下:
class
Singleton
...
{
private static final Singleton st=new Singleton();

private Singleton()...{}

public static Singleton getInstance()...{
return st;
}
}
Runtime类是使用单例模式的一个例子。
▲ Runtime类
每一个Java程序都有一个Runtime类的单一实例。
通过Runtime.getRuntime()获取Runtime类的实例:
class
RuntimeTest
...
{

public static void main(String[] args)...{
Runtime rt=Runtime.getRuntime();
System.out.println(rt.freeMemory()); //自由内存大小
System.out.println(rt.totalMemory()); //总内存大小
System.out.println(rt.availableProcessors()); //可用的处理机数量
System.out.println(rt.maxMemory()); //最大内存
}
}
编译运行:

Runtime类的exec方法可以执行外部命令:
import
java.io.
*
;

class
RuntimeTest
...
{

public static void main(String[] args)...{
Runtime rt=Runtime.getRuntime();

try...{
Process proc=rt.exec("java ArrayTest");
InputStream ins=proc.getInputStream();
int data;

while((data=ins.read())!=-1)...{
System.out.print((char)data);
}

}catch(Exception e)...{
e.printStackTrace();
}
}
}
编译运行:

比如:我们要打开记事本程序,则执行下面语句即可实现:
System.out.println(rt.exec("notepad"));
再比如,我们要实现编译java源程序,则实行下面语句:
System.out.println(rt.exec("javac ArrayTest.java"));
说明:程序中用到的Process类是用于管理子进程的。