java第四课

■  关于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
byteByte
shortShort
intInteger
longLong
charCharacter
floatFloat
doubleDouble

基本数据类型与封装类的转换:

实例代码如下:

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类是用于管理子进程的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值