第6章 面向对象入门

本章读者可以学到如下实例:

  • 实例043 自定义图书类
  • 实例044 温度单位转换工具
  • 实例045 成员变量的默认初始化值
  • 实例046 单例模式的应用
  • 实例047 汉诺塔问题求解
  • 实例048 编写同名的方法
  • 实例049 构造方法的应用
  • 实例050 统计图书的销售量
  • 实例051 两只完全相同的宠物
  • 实例052 重新计算对象的哈希码
  • 实例053 使用字符串输出对象
  • 实例054 对象的假克隆
  • 实例055 Java对象的浅克隆
  • 实例056 Java对象的深克隆
  • 实例057 序列化与对象克隆
  • 实例058 深克隆效率的比较

实例044 温度单位转换工具

实例说明

目前有两种广泛使用的温度单位,即摄氏度和华氏度。在标准大气压下,沸腾的水可以表示成100摄氏度和212华氏度。
本实例将编写一个简单的温度单位转换工具,它可以将用户输入的摄氏度转换成华氏度

实现过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.mingrisoft;

import java.util.Scanner;

public class CelsiusConverter {
/**
* 摄氏度转华氏度
*
* @param celsius 摄氏度
* @return 华氏度
*/
public double celsiusToFahrenheit(double celsius) {
// 计算华氏温度
double fahrenheit = 1.8 * celsius + 32;
// 返回华氏温度
return fahrenheit;
}

/**
* 华氏度转摄氏度
*
* @param fahrenheit 华氏度
* @return 摄氏度
*/
private double fahrenheitToCelsius(double fahrenheit) {
// 计算摄氏度
double celsius = (fahrenheit - 32) / 1.8;
// 计算华氏度
return celsius;
}

public static void main(String[] args) {
// 创建类的对象
CelsiusConverter converter = new CelsiusConverter();
System.out.print("请输入 摄氏度:");
// 获得控制台输入
Scanner in = new Scanner(System.in);
// 获得用户输入的摄氏温度
double celsius = in.nextDouble();
// 转换温度为华氏度
System.out.println(celsius + "摄氏度=" + converter.celsiusToFahrenheit(celsius) + "华氏度");
System.out.print("请输入 华氏度:");
double fahrenheit = in.nextDouble();
System.out.println(fahrenheit + "华氏度=" + converter.fahrenheitToCelsius(fahrenheit) + "摄氏度");
in.close();
}
}

技术要点

实例046 单例模式的应用

单例类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.mingrisoft;

public class Emperor {
// 1 声明一个Emperor类的引用
private static Emperor emperor = null;

// 2 将构造方法私有
private Emperor() {
}

// 3 提供获取实例化引用的方法
public static Emperor getInstance() {
if (emperor == null) {
emperor = new Emperor();
}
return emperor;
}
}

测试类:

1
2
3
4
5
6
7
8
9
package com.mingrisoft;

public class Test {
public static void main(String[] args) {
Emperor emperor1 = Emperor.getInstance();
Emperor emperor2 = Emperor.getInstance();
System.out.println(emperor1 == emperor2);
}
}

技术要点

单例模式的特点在于仅能获得一个对象。为了防止其他用户创建对象,需要将构造方法设置成private的,然后提供一个静态方法,该方法返回这个类的对象。

实例047 汉诺塔问题求解

实例说明

汉诺塔问题的描述如下:有A、B和C 3根柱子,在A上从下往上按照从小到大的顺序放着64个圆盘,以B为中介,把盘子全部移动到C上。移动过程中,要求任意盘子的下面要么没有盘子,要么只能有比它大的盘子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.mingrisoft;

public class HanoiTower {
public static void moveDish(int level, char from, char inter, char to) {
// 如果只有一个盘子就退出迭代
if (level == 1) {
// System.out.println("从 " + from + " 移动盘子 1 号到 " + to);
System.out.println("从 " + from + " 移动盘子 " + level + " 号到 " + to);
}
// 如果有大于一个盘子就继续迭代
else {
moveDish(level - 1, from, to, inter);
System.out.println("从 " + from + " 移动盘子 " + level + " 号到 " + to);
moveDish(level - 1, inter, from, to);
}
}

public static void main(String[] args) {
// 设置汉诺塔为3阶
int nDisks = 3;
// int nDisks = 4;
// 实现移动算法
moveDish(nDisks, 'A', 'B', 'C');
}
}

运行效果

1
2
3
4
5
6
7
从 A 移动盘子 1 号到 C
从 A 移动盘子 2 号到 B
从 C 移动盘子 1 号到 B
从 A 移动盘子 3 号到 C
从 B 移动盘子 1 号到 A
从 B 移动盘子 2 号到 C
从 A 移动盘子 1 号到 C

技术要点

为了将N个盘子从A移动到C,需要先将第N个盘子上面的N-1个盘子移动到B上,这样才能将第N个盘子移动到C上。
同理,为了将第N-1个盘子从B移动到C上,需要将N-2个盘子移动到A上,这样才能将第N-1个盘子移动到C上。
通过递归就可以实现汉诺塔问题的求解。

实例051 两只完全相同的宠物

实例说明

由于生命的复杂性,寻找两只完全相同的宠物(类的对象)是不可能的。Java中,可以通过比较对象的成员变量来判断对象是否相同。本实例将创建3只宠物猫,通过比较它们的名字、年龄、重量和颜色属性来看它们是否相同。

实现过程

Cat.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.mingrisoft;

import java.awt.Color;

public class Cat {
// 表示猫咪的名字
private String name;
// 表示猫咪的年龄
private int age;
// 表示猫咪的重量
private double weight;
// 表示猫咪的颜色
private Color color;

// 初始化猫咪的属性
public Cat(String name, int age, double weight, Color color) {
this.name = name;
this.age = age;
this.weight = weight;
this.color = color;
}

// 利用属性来判断猫咪是否相同
@Override
public boolean equals(Object obj) {
// 1.如果是同一个对象,则相同
if (this == obj) {
return true;
}
// 2.如果有一个为null,则不同
if (obj == null) {
return false;
}
// 3.如果类型不同,则不同
if (getClass() != obj.getClass()) {
return false;
}
// 4.强制类型转换
Cat cat = (Cat) obj;
// 5.比较属性
return name.equals(cat.name) && (age == cat.age) && (weight == cat.weight) && (color.equals(cat.color));
}

@Override
public String toString() {// 重写toString()方法
StringBuilder sb = new StringBuilder();
sb.append("名字:" + name + ",\t");
sb.append("年龄:" + age + ",\t");
sb.append("重量:" + weight + ",\t");
sb.append("颜色:" + color + "");
return sb.toString();
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.mingrisoft;

import java.awt.Color;

public class Test {
public static void main(String[] args) {
// 创建猫咪1号
Cat cat1 = new Cat("Java", 12, 21, Color.BLACK);
// 创建猫咪2号
Cat cat2 = new Cat("C++", 12, 21, Color.WHITE);
// 创建猫咪3号
Cat cat3 = new Cat("Java", 12, 21, Color.BLACK);
// 输出猫咪1号
System.out.println("猫咪1号:" + cat1);
// 输出猫咪2号
System.out.println("猫咪2号:" + cat2);
// 输出猫咪3号
System.out.println("猫咪3号:" + cat3);
// 比较是否相同
System.out.println("猫咪1号是否与猫咪2号相同:" + cat1.equals(cat2));
// 比较是否相同
System.out.println("猫咪1号是否与猫咪3号相同:" + cat1.equals(cat3));
}

}

运行效果

1
2
3
4
5
猫咪1号:名字:Java,    年龄:12,       重量:21.0,     颜色:java.awt.Color[r=0,g=0,b=0]
猫咪2号:名字:C++, 年龄:12, 重量:21.0, 颜色:java.awt.Color[r=255,g=255,b=255]
猫咪3号:名字:Java, 年龄:12, 重量:21.0, 颜色:java.awt.Color[r=0,g=0,b=0]
猫咪1号是否与猫咪2号相同:false
猫咪1号是否与猫咪3号相同:true

技术要点

Java中的类都是Object类的直接或间接子类。在Object类中定义了equals方法用于比较类的两个对象是否相同。该方法的默认实现仅能比较两个对象是否是同一个对象。通常在定义类时推荐重写这个方法。

重点 equals方法的规范定义描述

  • 1.如果是同一个对象,则相同
  • 2.如果有一个为null,则不同
  • 3.如果类型不同,则不同
  • 4.强制类型转换,比较属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public boolean equals(Object obj) {
// 1.如果是同一个对象,则相同
if (this == obj) {
return true;
}
// 2.如果有一个为null,则不同
if (obj == null) {
return false;
}
// 3.如果类型不同,则不同
if (getClass() != obj.getClass()) {
return false;
}
// 4.强制类型转换
Cat cat = (Cat) obj;
// 5.比较属性
return name.equals(cat.name) && (age == cat.age) && (weight == cat.weight) && (color.equals(cat.color));
}

实例052 重新计算对象的哈希码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.mingrisoft;

import java.awt.Color;

public class Cat {
// 表示猫咪的名字
private String name;
// 表示猫咪的年龄
private int age;
// 表示猫咪的重量
private double weight;
// 表示猫咪的颜色
private Color color;

// 初始化猫咪的属性
public Cat(String name, int age, double weight, Color color) {
this.name = name;
this.age = age;
this.weight = weight;
this.color = color;
}

// 利用属性来判断猫咪是否相同
@Override
public boolean equals(Object obj) {
// 1.如果是同一个对象,则相同
if (this == obj) {
return true;
}
// 2.如果有一个为null,则不同
if (obj == null) {
return false;
}
// 3.如果类型不同,则不同
if (getClass() != obj.getClass()) {
return false;
}
// 4.强制类型转换
Cat cat = (Cat) obj;
// 5.比较属性
return name.equals(cat.name) && (age == cat.age) && (weight == cat.weight) && (color.equals(cat.color));
}

@Override
// 重写hashCode()方法
public int hashCode() {
return 7 * name.hashCode()
+ 11 * Integer.valueOf(age).hashCode()
+ 13 * Double.valueOf(weight).hashCode()
+ 17 * color.hashCode();
}

@Override
public String toString() {// 重写toString()方法
StringBuilder sb = new StringBuilder();
sb.append("名字:" + name + ",\t");
sb.append("年龄:" + age + ",\t");
sb.append("重量:" + weight + ",\t");
sb.append("颜色:" + color + "");
return sb.toString();
}

}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.mingrisoft;

import java.awt.Color;

public class Test {
public static void main(String[] args) {
// 创建猫咪1号
Cat cat1 = new Cat("Java", 12, 21, Color.BLACK);
// 创建猫咪2号
Cat cat2 = new Cat("C++", 12, 21, Color.WHITE);
// 创建猫咪3号
Cat cat3 = new Cat("Java", 12, 21, Color.BLACK);
// 输出猫咪1号的哈希码
System.out.println("猫咪1号的哈希码:" + cat1.hashCode());
// 输出猫咪2号的哈希码
System.out.println("猫咪2号的哈希码:" + cat2.hashCode());
// 输出猫咪3号的哈希码
System.out.println("猫咪3号的哈希码:" + cat3.hashCode());
// 比较是否相同
System.out.println("猫咪1号是否与猫咪2号相同:" + cat1.equals(cat2));
// 比较是否相同
System.out.println("猫咪1号是否与猫咪3号相同:" + cat1.equals(cat3));
}

}

运行效果

1
2
3
4
5
猫咪1号的哈希码:849794130
猫咪2号的哈希码:1119356584
猫咪3号的哈希码:849794130
猫咪1号是否与猫咪2号相同:false
猫咪1号是否与猫咪3号相同:true

简单的计算哈希码的方式

一种简单的计算哈希码的方式是:
将重写equals方法时使用到的成员变量,乘以不同的质数然后求和,以此作为新的哈希码

实例054 对象的假克隆

实例说明

对象的克隆是Java中的一项高级技术,它可以根据给定的对象,获得与其完全相同的另个对象。在克隆过程中,有些注意事项。本实例将演示常见的错误,其运行效果如图6.12所示。

技术要点

Java中,对于基本类型可以使用“==”来进行克隆,此时两个变量除了相等是没有任何关系的。而对于引用类型却不能简单地使用“==”进行克隆,这与Java的内存空间使用有关。Java将内存空间分成两块,即栈和堆。

  • 在栈中保存基本类型和引用变量;
  • 在堆中保存对象。

对于引用变量而言,使用“==”将修改引用,而不是复制堆中的对象。此时两个引用变量将指向同一个对象。因此,如果一个变量对其进行修改则会改变另一个变量。

实例055 Java对象的浅克隆

克隆相关内容

实例说明

在克隆对象时,

  • 如果对象的成员变量是基本类型,则使用浅克隆即可完成。
  • 如果对象的成员变量包括可变引用类型,则需要使用深克隆。

本实例将演示如何使用浅克隆

实现过程

Address.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.mingrisoft;

public class Address {
// 表示员工所在的国家
private String state;
// 表示员工所在的省
private String province;
// 表示员工所在的市
private String city;

// 利用构造方法进行初始化
public Address(String state, String province, String city) {
this.state = state;
this.province = province;
this.city = city;
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

public String getProvince() {
return province;
}

public void setProvince(String province) {
this.province = province;
}

public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

// 重写toString()方法
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("国家:" + state + ", ");
sb.append("省:" + province + ", ");
sb.append("市:" + city);
return sb.toString();
}
}

Employee.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.mingrisoft;

public class Employee implements Cloneable {
// 表示员工的姓名
private String name;
// 表示员工的年龄
private int age;
// 表示员工的地址
private Address address;

// 利用构造方法进行初始化
public Employee(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Address getAddress() {
return address;
}

public void setAddress(Address address) {
this.address = address;
}

// 重写toString()方法
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("姓名:" + name + ", ");
sb.append("年龄:" + age + "\n");
sb.append("地址:" + address);
return sb.toString();
}

// 实现浅克隆
@Override
public Employee clone() {
Employee employee = null;
try {
// 直接调用Object的clone方法
employee = (Employee) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return employee;
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.mingrisoft;

public class Test {
public static void main(String[] args) {
System.out.println("---------------------克隆之前--------------------------");
// 创建address对象
Address address = new Address("中国", "吉林", "长春");
// 创建employee1对象
Employee employee1 = new Employee("张三", 30, address);
System.out.println("员工1的信息:");
// 输出employee1对象
System.out.println(employee1);
System.out.println("---------------------克隆之前--------------------------");
// 使用克隆创建employee2对象
Employee employee2 = employee1.clone();
// 修改员工地址
employee2.getAddress().setState("中国");
// 修改员工地址
employee2.getAddress().setProvince("四川");
// 修改员工地址
employee2.getAddress().setCity("成都");
// 修改员工名字
employee2.setName("李四");
// 修改员工年龄
employee2.setAge(24);
System.out.println("员工1的信息:");
// 输出employee1对象
System.out.println(employee1);
System.out.println("员工2的信息:");
// 输出employee2对象
System.out.println(employee2);
}
}

运行效果:

1
2
3
4
5
6
7
8
9
10
11
---------------------克隆之前--------------------------
员工1的信息:
姓名:张三, 年龄:30
地址:国家:中国, 省:吉林, 市:长春
---------------------克隆之前--------------------------
员工1的信息:
姓名:张三, 年龄:30
地址:国家:中国, 省:四川, 市:成都
员工2的信息:
姓名:李四, 年龄:24
地址:国家:中国, 省:四川, 市:成都

从运行结果上看,使用浅克隆得来的对象和原来的对象之间的

  • 基本类型的属性和不可变引用类型的属性不会相互影响
  • 可变引用类型的属性会相互影响

多学两招

如果引用类型是不可变的,如String类的对象,则不必进行深克隆

技术要点

当需要克隆对象时,需要使用clone()方法,该方法的声明如下:

需要注意的是,该方法是一个受保护的方法,通常需要重写该方法并将访问权限限定符改成public。该方法将类中各个域进行复制,如果对于引用类型的域,这种操作就会有问题,因此称作浅克隆
提供克隆功能的类需要实现Cloneable接口,否则使用cloned方法时会抛出CloneNotSupportedException异常。

实例056 Java对象的深克隆

实例说明

如果类的成员变量中包括可变引用类型,则在克隆时就需要使用深克隆。本实例将演示如何使用深克隆

实现过程

Address.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.mingrisoft;

public class Address implements Cloneable {
// 表示员工所在的国家
private String state;
// 表示员工所在的省
private String province;
// 表示员工所在的市
private String city;

// 利用构造方法进行初始化
public Address(String state, String province, String city) {
this.state = state;
this.province = province;
this.city = city;
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

public String getProvince() {
return province;
}

public void setProvince(String province) {
this.province = province;
}

public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

// 重写toString()方法
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("国家:" + state + ", ");
sb.append("省:" + province + ", ");
sb.append("市:" + city);
return sb.toString();
}

@Override
// 实现浅克隆
protected Address clone() {
Address address = null;
try {
address = (Address) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return address;
}
}

脚下留神:Address类的域不是基本类型就是不可变类型,所以可以直接使用浅克隆

Employee.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package com.mingrisoft;

public class Employee implements Cloneable {
// 表示员工的姓名
private String name;
// 表示员工的年龄
private int age;
// 表示员工的地址
private Address address;

// 利用构造方法进行初始化
public Employee(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Address getAddress() {
return address;
}

public void setAddress(Address address) {
this.address = address;
}

@Override
// 重写toString()方法
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("姓名:" + name + ", ");
sb.append("年龄:" + age + "\n");
sb.append("地址:" + address);
return sb.toString();
}

@Override
// 实现浅克隆
public Employee clone() {
Employee employee = null;
try {
employee = (Employee) super.clone();
// 可变引用类型也调用clone方法
employee.address = address.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return employee;
}
}

多学两招:在Java5.0版中,支持重写方法时返回协变类型,因此可以返回Employee对象。

Test.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.mingrisoft;

public class Test {
public static void main(String[] args) {
System.out.println("---------------------克隆之前--------------------------");
Address address = new Address("中国", "吉林", "长春");// 创建address对象
Employee employee1 = new Employee("张三", 30, address);// 创建employee1对象
System.out.println("员工1的信息:");
System.out.println(employee1);// 输出employee1对象
System.out.println("---------------------克隆之后--------------------------");
Employee employee2 = employee1.clone();// 使用克隆创建employee2对象
employee2.getAddress().setState("中国"); // 修改员工地址
employee2.getAddress().setProvince("四川"); // 修改员工地址
employee2.getAddress().setCity("成都"); // 修改员工地址
employee2.setName("李四"); // 修改员工名字
employee2.setAge(24);// 修改员工年龄
System.out.println("员工1的信息:");
System.out.println(employee1);// 输出employee1对象
System.out.println("员工2的信息:");
System.out.println(employee2);// 输出employee2对象
}

}

运行效果

1
2
3
4
5
6
7
8
9
10
11
---------------------克隆之前--------------------------
员工1的信息:
姓名:张三, 年龄:30
地址:国家:中国, 省:吉林, 市:长春
---------------------克隆之后--------------------------
员工1的信息:
姓名:张三, 年龄:30
地址:国家:中国, 省:吉林, 市:长春
员工2的信息:
姓名:李四, 年龄:24
地址:国家:中国, 省:四川, 市:成都

从运行结果可以看出,拷贝得来的员工2不再和员工1共享同一个地址对象了,修改员工2的地址,不会影响到员工1的地址.

技术要点

通常情况下,需要克隆对象时都需要使用深克隆。但需要注意的是,如果引用类型中还有可变的引用类型成员变量,则它也需要进行克隆。例如本实例中Address类如果增加了一个Date成员变量表示开始在此居住的时间,则其也需要被克隆。

实例057 序列化与对象克隆

实例说明

如果类的成员变量比较复杂,例如使用了多个可变引用类型,使用clone()方法来克隆是非常麻烦的。此时,也可以考虑使用序列化的方式完成克隆。本实例将演示其用法,实例的运行效果如图6.15所示。

实现过程

Address.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.mingrisoft;

import java.io.Serializable;

public class Address implements Serializable {
private static final long serialVersionUID = 4983187287403615604L;
// 表示员工所在的国家
private String state;
// 表示员工所在的省
private String province;
// 表示员工所在的市
private String city;

// 利用构造方法初始化各个域
public Address(String state, String province, String city) {
this.state = state;
this.province = province;
this.city = city;
}

public String getState() {
return state;
}

public void setState(String state) {
this.state = state;
}

public String getProvince() {
return province;
}

public void setProvince(String province) {
this.province = province;
}

public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

public static long getSerialversionuid() {
return serialVersionUID;
}

@Override
// 使用地址属性表示地址对象
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("国家:" + state + ", ");
sb.append("省:" + province + ", ");
sb.append("市:" + city);
return sb.toString();
}

}

Employee.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package com.mingrisoft;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Employee implements Serializable {
private static final long serialVersionUID = 3049633059823371192L;
// 表示员工的姓名
private String name;
// 表示员工的年龄
private int age;
// 表示员工的地址
private Address address;

// 利用构造方法初始化各个域
public Employee(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public Address getAddress() {
return address;
}

public void setAddress(Address address) {
this.address = address;
}

@Override
// 重写toString()方法
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(" 姓名:" + name + ", ");
sb.append("年龄:" + age + "\n");
sb.append(" 地址:" + address);
return sb.toString();
}

/**
* 反序列化Employee.
*
* @return 反序列化得到的Employee对象.
*/
public static Employee readEmployee() {
Employee employee = null;
ObjectInputStream in = null;
try {
in = new ObjectInputStream(new FileInputStream("employee.dat"));
employee = (Employee) in.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return employee;
}

/**
* 序列化Employee.
*
* @param employee 需要序列化的Employee对象.
*/
public static void writeEmployee(Employee employee) {
ObjectOutputStream out = null;
// 对象输出流
try {
out = new ObjectOutputStream(new FileOutputStream("employee.dat"));
// 序列化对象1
out.writeObject(employee);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

Test.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.mingrisoft;

public class Test {
public static void main(String[] args) {
System.out.println("----------------------------序列化之前----------------------------");
// 创建address对象
Address address = new Address("中国", "吉林", "长春");
// 创建employee1对象
Employee employee1 = new Employee("张三", 30, address);
System.out.println("员工1的信息:");
// 输出employee1对象
System.out.println(employee1);
System.out.println("----------------------------序列化之后----------------------------");
// 序列化employee1对象
Employee.writeEmployee(employee1);
// 反序列化得到employee2对象
Employee employee2 = Employee.readEmployee();
System.out.println("员工2的信息:");
System.out.println(employee2);
System.out.println("----------------------------员工2修改之后----------------------------");
if (employee2 != null) {
// 修改员工地址
employee2.getAddress().setState("中国");
// 修改员工地址
employee2.getAddress().setProvince("四川");
// 修改员工地址
employee2.getAddress().setCity("成都");
// 修改员工名字
employee2.setName("李四");
// 修改员工年龄
employee2.setAge(24);

System.out.println("员工1的信息:");
// 输出employee1对象
System.out.println(employee1);
System.out.println("员工2的信息:");
// 输出employee2对象
System.out.println(employee2);
}
}
}

运行效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
----------------------------序列化之前----------------------------
员工1的信息:
姓名:张三, 年龄:30
地址:国家:中国, 省:吉林, 市:长春
----------------------------序列化之后----------------------------
员工2的信息:
姓名:张三, 年龄:30
地址:国家:中国, 省:吉林, 市:长春
----------------------------员工2修改之后----------------------------
员工1的信息:
姓名:张三, 年龄:30
地址:国家:中国, 省:吉林, 市:长春
员工2的信息:
姓名:李四, 年龄:24
地址:国家:中国, 省:四川, 市:成都

从运行结果可以看出,修改员工2的成员变量,不会影响到员工1的成员变量.

技术要点

进行序列化的类需要实现Serializable接口,该接口中并没有定义任何方法,是一个标识接口。如果类中有可变的引用类型成员变量,则该变量也需要实现Serializable接口,依此类推。
本实例中采用将对象写入本地文件的方式完成序列化,读者也可以将其写入到内存中再读取。

实例058 深克隆效率的比较

实例说明前面介绍了两种实现深克隆的方式,本实例将分别使用这两种方式克隆10000个对象,并输出花费的时间,这样读者可以对它们的效率差异有个直观的认识。

实现过程

Employee.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.mingrisoft;

import java.io.Serializable;

public class Employee implements Cloneable, Serializable {
private static final long serialVersionUID = 5022956767440380940L;
// 表示员工的姓名
private String name;
// 表示员工的年龄
private int age;

// 利用构造方法初始化各个域
public Employee(String name, int age) {
this.name = name;
this.age = age;
}

@Override
// 重写toString()方法
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("姓名:" + name + ", ");
sb.append("年龄:" + age + "\n");
return sb.toString();
}

@Override
// 使用父类的clone()方法实现深克隆
protected Employee clone() {
Employee employee = null;
try {
employee = (Employee) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return employee;
}
}

Test.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package com.mingrisoft;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

public class Test {

public static void main(String[] args) {
// 创建Employee类的对象
Employee employee = new Employee("李四", 25);
// 获得当前系统时间
long currentTime = System.currentTimeMillis();
// 创建列表保存对象
List<Employee> employeeList1 = new ArrayList<Employee>();
// 循环次数
int times = 100000;
for (int i = 0; i < times; i++) {
// 使用克隆方式获得对象
Employee employee2 = employee.clone();
// 添加到List中
employeeList1.add(employee2);
}
System.out.println("克隆花费时间:" + (System.currentTimeMillis() - currentTime) + "毫秒");
// 获得当前时间
currentTime = System.currentTimeMillis();
// 创建列表保存对象
List<Employee> employeeList2 = new ArrayList<Employee>();
for (int i = 0; i < times; i++) {
// 序列化对象到内存
ByteArrayOutputStream byteArrayOut = witeEmployee(employee);
// 从内存中读取对象
Employee employee2 = readEmployee(byteArrayOut);
// 添加到List中
employeeList2.add(employee2);
}
System.out.println("序列化花费时间:" + (System.currentTimeMillis() - currentTime) + "毫秒");
}

/**
* 反序列化对象,从内存中读取出Employee对象.
*
* @param byteArrayOut 保存Empoyee对象的输出流.
* @return Employee对象.
*/
private static Employee readEmployee(ByteArrayOutputStream byteArrayOut) {
Employee employee = null;
// 获得字节数组输出流内容
ByteArrayInputStream bais = new ByteArrayInputStream(byteArrayOut.toByteArray());
ObjectInputStream in = null;
try {
// 创建对象输入流
in = new ObjectInputStream(bais);
// 读取对象
employee = (Employee) in.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
// 释放资源
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return employee;
}

/**
* 序列化对象,将Employee对象写入字节数组输出流
*
* @param employee 要序列化的对象.
* @return 字节数组输出流.
*/
private static ByteArrayOutputStream witeEmployee(Employee employee) {
// 创建字节数组输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream out = null;
try {
// 创建对象输出流
out = new ObjectOutputStream(baos);
// 将对象写入到输出流中
out.writeObject(employee);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
try {
// 释放资源
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return baos;
}
}

运行效果

1
2
克隆花费时间:13毫秒
序列化花费时间:1992毫秒

可以看到使用序列化实现的深拷贝所需要的时间比深拷贝的时间要长的多

技术要点

使用ByteArrayOutputStreamByteArrayInputStream可以将对象保存在内存中,这样就不必产生一个本地文件来完成序列化的功能。

实例040 使用直接插入法对数组排序

实例说明

本实例演示如何使用直接插入排序法对一维数组进行排序。运行本实例,首先单击“随机生成数组”按钮,生成一个随机数组,并显示在左边的文本框中;然后单击“排序”按钮,使用直接插入排序法对生成的一维数组进行排序,并将排序后的一维数组显示在右边的文本框中。实例的运行效果如图513所示。

实现过程

(3)编写“随机生成数组”按钮的事件处理方法,在该方法中利用Ra象的法生成随机为数组设置初始值。关键代码如:

(4)编写“排序”按钮的事件处理方法,在该方法中使用直接插入排序算法对数组进行排序,并将排序后的数组显示到界面中。关键代码如下

技术要点

本实例主要用到了直接插入排序算接插入排序算法的实现原理进行详细讲解排序是将录插序使得到的新数列仍然有序。插算法的思想是:
将n个有序数存放在数组a中,要插入的数为x,首先确定x插在数组中的位置p,数组中p之后的元素都向后移一个位置,空出a(p),将x放入a(p)。这样即可实现插入后数列仍然有序。
使用插入排序法排序的过程如图514所示。

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package com.lan;

import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.EmptyBorder;

public class InsertSort extends JFrame
{
private static final long serialVersionUID = -8006819934162242254L;
private JPanel contentPane;
// 待排序的数组
private int[] array = new int[10];
/**
* Launch the application.
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable() {
public void run()
{
try
{
InsertSort frame = new InsertSort();
frame.setVisible(true);
} catch (Exception e)
{
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public InsertSort() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
GridBagLayout gbl_contentPane = new GridBagLayout();
gbl_contentPane.columnWidths = new int[]{0, 0};
gbl_contentPane.rowHeights = new int[]{0, 0, 0, 0, 0};
gbl_contentPane.columnWeights = new double[]{1.0, Double.MIN_VALUE};
gbl_contentPane.rowWeights = new double[]{0.0, 0.0, 1.0, 0.0, Double.MIN_VALUE};
contentPane.setLayout(gbl_contentPane);

JTextArea textAreaArray = new JTextArea();
// textArea.setRows(1);
GridBagConstraints gbc_textAreaArray = new GridBagConstraints();
gbc_textAreaArray.insets = new Insets(0, 0, 5, 0);
gbc_textAreaArray.fill = GridBagConstraints.BOTH;
gbc_textAreaArray.gridx = 0;
gbc_textAreaArray.gridy = 0;
contentPane.add(textAreaArray, gbc_textAreaArray);

JButton buttonCreateRandomArray = new JButton("生成随机数组");
buttonCreateRandomArray.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e)
{
textAreaArray.setText("");
Random random = new Random();
for (int i = 0; i < array.length; i++)
{
array[i] = random.nextInt(50);
textAreaArray.append(String.valueOf(array[i]));
if (i < array.length - 1)
{
textAreaArray.append(",");
}
}
}
});
GridBagConstraints gbc_buttonCreateRandomArray = new GridBagConstraints();
gbc_buttonCreateRandomArray.insets = new Insets(0, 0, 5, 0);
gbc_buttonCreateRandomArray.gridx = 0;
gbc_buttonCreateRandomArray.gridy = 1;
contentPane.add(buttonCreateRandomArray, gbc_buttonCreateRandomArray);

JScrollPane scrollPane = new JScrollPane();
GridBagConstraints gbc_scrollPane = new GridBagConstraints();
gbc_scrollPane.fill = GridBagConstraints.BOTH;
gbc_scrollPane.insets = new Insets(0, 0, 5, 0);
gbc_scrollPane.gridx = 0;
gbc_scrollPane.gridy = 2;
contentPane.add(scrollPane, gbc_scrollPane);

JTextArea textAreaShow = new JTextArea();
textAreaShow.setFont(new Font("Monospaced", Font.BOLD, 14));
scrollPane.setViewportView(textAreaShow);

JButton buttonInsertSort = new JButton("直接插入排序");
buttonInsertSort.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
textAreaShow.setText("");
insertSort(textAreaShow);
}

private void insertSort(JTextArea textAreaShow)
{
int toInsert;
for (int i = 1; i < array.length; i++)
{
// 缓存要插入的元素
toInsert = array[i];
// 遍历前面已经排序要的序列
int j = i - 1;
// 如果前面的一个元素比要插入的元素大,就将这个大的元素后移一格
while (j >= 0 && array[j] > toInsert)
{
// 将比要插入的元素大的元素全部向后移动一格
array[j + 1] = array[j];
// 比较下一个格
j--;
}
//
array[j + 1] = toInsert;
// printArray(textAreaShow);
printStep(textAreaShow, i);
}
}

private void printStep(JTextArea textAreaShow, int i)
{
textAreaShow.append("[");
for (int k = 0; k < array.length; k++)
{
textAreaShow.append(String.valueOf(array[k]));
if (k == i)
{
textAreaShow.append("]");
}
if (k < array.length - 1)
{
textAreaShow.append(",");
}
}
textAreaShow.append("\n");
}
});
GridBagConstraints gbc_buttonInsertSort = new GridBagConstraints();
gbc_buttonInsertSort.gridx = 0;
gbc_buttonInsertSort.gridy = 3;
contentPane.add(buttonInsertSort, gbc_buttonInsertSort);
}
}

运行效果

待排序数组:

1
41,22,37,46,45,0,42,12,10,34

排序效果:

1
2
3
4
5
6
7
8
9
[22,41],37,46,45,0,42,12,10,34
[22,37,41],46,45,0,42,12,10,34
[22,37,41,46],45,0,42,12,10,34
[22,37,41,45,46],0,42,12,10,34
[0,22,37,41,45,46],42,12,10,34
[0,22,37,41,42,45,46],12,10,34
[0,12,22,37,41,42,45,46],10,34
[0,10,12,22,37,41,42,45,46],34
[0,10,12,22,34,37,41,42,45,46]

待排序数组

1
11,34,35,15,36,0,13,24,16,16

排序效果:

1
2
3
4
5
6
7
8
9
[11,34],35,15,36,0,13,24,16,16
[11,34,35],15,36,0,13,24,16,16
[11,15,34,35],36,0,13,24,16,16
[11,15,34,35,36],0,13,24,16,16
[0,11,15,34,35,36],13,24,16,16
[0,11,13,15,34,35,36],24,16,16
[0,11,13,15,24,34,35,36],16,16
[0,11,13,15,16,24,34,35,36],16
[0,11,13,15,16,16,24,34,35,36]

实例042 反转数组中元素的顺序

实例说明

反转数组就是以相反的顺序把原有数组的内容重新排序。本实例在GUI窗体中演示反转数组中元素的顺序。运行本实例,首先在界面的文本框内输入数组元素,每个元素使用空格分隔,然后单击界面上的“反转数组元素”按钮,程序将对数组进行反转运算,并把运算过程中对数组的改变显示在窗体中。实例的运行效果如图516所示。

反转和倒序排序的区别

脚下留神:“反转”并不等于“倒序排序”。

  • “反转”只是将数组元素的顺序进行颠倒,并不对其执行排序操作;
  • 而“倒序排序”则是对数组中的元素进行从大到小的排序。

实现过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void reverseArray(int[] array)
{
int temp;
int i_thFromLast;
for (int i = 0; i < array.length / 2; i++)
{
// 计算倒数第i个元素的索引
i_thFromLast = array.length - 1 - i;
// 将第i个元素和倒数第i个元素交换
temp = array[i];
array[i] = array[i_thFromLast];
array[i_thFromLast] = temp;
printSwapStep(array, i_thFromLast, i);
}
}

技术要点

本实例的核心技术是使用了数组反转算法。反转算法的基本思想比较简单,也很好理解,思路就是:

  • 把数组最后一个元素与第一个元素替换,
  • 倒数第二个元素与第二个元素替换,
  • 依此类推,直到把所有数组元素反转替换。

反转数组元素是对数组两边的元素进行替换,所以只需要循环数组长度的半数。例如,对长度为6的一维数组进行反转,只需要循环6/2,即3次,具体的反转过程如图517所示

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package com.lan;

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;

public class ReverseArray extends JFrame
{
private final class ReverseButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
String[] arrayStrs = textAreaInput.getText().split(",");
int[] array = new int[arrayStrs.length];
for (int i = 0; i < array.length; i++)
{
array[i] = Integer.parseInt(arrayStrs[i]);
}
reverseArray(array);
}
private void reverseArray(int[] array)
{
int temp;
int i_thFromLast;
for (int i = 0; i < array.length / 2; i++)
{
// 计算倒数第i个元素的索引
i_thFromLast = array.length - 1 - i;
// 将第i个元素和倒数第i个元素交换
temp = array[i];
array[i] = array[i_thFromLast];
array[i_thFromLast] = temp;
printSwapStep(array, i_thFromLast, i);
}
}
private void printSwapStep(int[] array, int i_thFromLast, int i)
{
for (int j = 0; j < array.length; j++)
{
if (j == i)
{
textAreaShow.append("[");
}
textAreaShow.append(String.valueOf(array[j]));
if (j == i_thFromLast)
{
textAreaShow.append("]");
}
if (j < array.length - 1)
{
textAreaShow.append(",");
}
}
textAreaShow.append("\n");
}
}

private static final long serialVersionUID = 5911081012919816847L;
private JPanel contentPane;
private JTextArea textAreaShow = new JTextArea();
private JTextArea textAreaInput = new JTextArea();
private final JMenuBar menuBar = new JMenuBar();
private final JMenu menu = new JMenu("菜单");
private final JMenuItem menuItem = new JMenuItem("退出");

/**
* Launch the application.
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable() {
public void run()
{
try
{
ReverseArray frame = new ReverseArray();
frame.setVisible(true);
} catch (Exception e)
{
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public ReverseArray() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
dispose();
}
});
// 为菜单设置alt+f快捷键(助记符)
menu.setMnemonic('F');
GridBagConstraints gbc_menuBar = new GridBagConstraints();
gbc_menuBar.anchor = GridBagConstraints.EAST;
gbc_menuBar.gridx = 0;
gbc_menuBar.gridy = 0;

contentPane.add(menuBar, gbc_menuBar);
menuBar.add(menu);
menu.add(menuItem);
// 为菜单项设置快捷键
KeyStroke Ctrl_Shift_W = KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK + KeyEvent.SHIFT_DOWN_MASK);
menuItem.setAccelerator(Ctrl_Shift_W);

contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
GridBagLayout gbl_contentPane = new GridBagLayout();
gbl_contentPane.columnWidths = new int[]{0, 0};
gbl_contentPane.rowHeights = new int[]{0, 0, 0, 0, 0};
gbl_contentPane.columnWeights = new double[]{1.0, Double.MIN_VALUE};
gbl_contentPane.rowWeights = new double[]{0.0, 0.0, 0.0, 1.0, Double.MIN_VALUE};
contentPane.setLayout(gbl_contentPane);

JPanel panel = new JPanel();
panel.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"),
"\u8F93\u5165\u6570\u7EC4\u5143\u7D20,\u4EE5\u82F1\u6587\u9017\u53F7\u5206\u9694,\u5982:1,2,3,4,5", TitledBorder.LEFT,
TitledBorder.TOP, null, new Color(0, 0, 0)));
GridBagConstraints gbc_panel = new GridBagConstraints();
gbc_panel.fill = GridBagConstraints.BOTH;
gbc_panel.insets = new Insets(0, 0, 5, 0);
gbc_panel.gridx = 0;
gbc_panel.gridy = 1;
contentPane.add(panel, gbc_panel);
GridBagLayout gbl_panel = new GridBagLayout();
gbl_panel.columnWidths = new int[]{0, 0};
gbl_panel.rowHeights = new int[]{0, 0};
gbl_panel.columnWeights = new double[]{1.0, Double.MIN_VALUE};
gbl_panel.rowWeights = new double[]{0.0, Double.MIN_VALUE};
panel.setLayout(gbl_panel);

GridBagConstraints gbc_textAreaInput = new GridBagConstraints();
gbc_textAreaInput.fill = GridBagConstraints.BOTH;
gbc_textAreaInput.gridx = 0;
gbc_textAreaInput.gridy = 0;
panel.add(textAreaInput, gbc_textAreaInput);

JButton buttonReverse = new JButton("反转数组");
// 为按钮绑定组合快捷键
buttonReverse.registerKeyboardAction(new ReverseButtonListener(), KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK),
JComponent.WHEN_IN_FOCUSED_WINDOW);
buttonReverse.addActionListener(new ReverseButtonListener());
GridBagConstraints gbc_buttonReverse = new GridBagConstraints();
gbc_buttonReverse.insets = new Insets(0, 0, 5, 0);
gbc_buttonReverse.gridx = 0;
gbc_buttonReverse.gridy = 2;
contentPane.add(buttonReverse, gbc_buttonReverse);

GridBagConstraints gbc_textAreaShow = new GridBagConstraints();
gbc_textAreaShow.insets = new Insets(0, 0, 5, 0);
gbc_textAreaShow.fill = GridBagConstraints.BOTH;
gbc_textAreaShow.gridx = 0;
gbc_textAreaShow.gridy = 3;
contentPane.add(textAreaShow, gbc_textAreaShow);
}
}

Swing设置组合快捷键

为菜单 设置alt+X快捷键

1
2
3
4
5
private final JMenu menu = new JMenu("菜单");
......
// 为菜单设置alt+f快捷键(助记符)
menu.setMnemonic('F');
......

为菜单项设置组合快捷键

1
2
3
4
5
6
7
8
9
10
11
12
private final JMenuItem menuItem = new JMenuItem("退出");
........
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
dispose();
}
});
// 为菜单项设置Ctrl+Shif+W快捷键
KeyStroke Ctrl_Shift_W = KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_DOWN_MASK + KeyEvent.SHIFT_DOWN_MASK);
menuItem.setAccelerator(Ctrl_Shift_W);
........

为按钮设置Ctrl+xxx组合快捷键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
......
// 反转数组按钮的时间监听器
private final class ReverseButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
String[] arrayStrs = textAreaInput.getText().split(",");
int[] array = new int[arrayStrs.length];
for (int i = 0; i < array.length; i++)
{
array[i] = Integer.parseInt(arrayStrs[i]);
}
reverseArray(array);
}
private void reverseArray(int[] array)
{
int temp;
int i_thFromLast;
for (int i = 0; i < array.length / 2; i++)
{
// 计算倒数第i个元素的索引
i_thFromLast = array.length - 1 - i;
// 将第i个元素和倒数第i个元素交换
temp = array[i];
array[i] = array[i_thFromLast];
array[i_thFromLast] = temp;
printSwapStep(array, i_thFromLast, i);
}
}
private void printSwapStep(int[] array, int i_thFromLast, int i)
{
for (int j = 0; j < array.length; j++)
{
if (j == i)
{
textAreaShow.append("[");
}
textAreaShow.append(String.valueOf(array[j]));
if (j == i_thFromLast)
{
textAreaShow.append("]");
}
if (j < array.length - 1)
{
textAreaShow.append(",");
}
}
textAreaShow.append("\n");
}
}
......
JButton buttonReverse = new JButton("反转数组");
// 为按钮设置事件监听器
buttonReverse.addActionListener(new ReverseButtonListener());
// 为按钮绑定组合快捷键
buttonReverse.registerKeyboardAction(new ReverseButtonListener(), KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_DOWN_MASK),
JComponent.WHEN_IN_FOCUSED_WINDOW);
......

实例041 使用sort()方法对数组排序

实例说明

实际开发项目时,经常需要在程序中对数组进行排序,而且,在计算机常用算法中,也提供了很多种对数组进行排序的算法,如冒泡排序法、直接插入法和选择排序法等,但在使用排序算法时,开发人员必须手动编写一堆代码,而且有的实现起来比较麻烦。JavaArrays类提供了一个sort()方法,使用该方法可以很方便地对各种数组进行排序,大大降低了数组排序的难度,本实例就将使用该方法对数组进行快速排序。实例的运行效果如图515所示。

指点迷津

Arrays类提供了创建、操作、搜索和排序数组的方法。在程序开发中有效利用Arrays类的各种方法来完成数组操作将大幅度提升程序开发效率,并且Arrays类的方法是经过测试的,可以减少程序开发中错误代码的出现。

技术要点

本实例在对数组进行快速排序时,主要用到了Arrays类的sort()方法,下面对其进行详细讲解。
Arrays类位于java.util包,它是数组的一个工具类,包含很多方法,其中sort()方法就是Arrays类提供的对数组进行排序的方法,它有很多重载格式,可以接收任何数据类型的数组并执行不同类型的排序。本实例使用sort()方法的int参数类型的重载实现,其方法声明如下:

1
public static void sort(int[] array)

参数说明
array:要排序的int类型的一维数组。

实例039 使用快速排序法对数组排序

实例说明

快速排序(QuickSort)是对气泡排序的一种改进,其排序速度相对较快。本实例演示如何使用快速排序法对一维数组进行排序,运行本实例,首先单击“生成随机数组”按钮,生成一个随机数组,并显示在上方的文本框中;然后单击“排序”按钮,使用快速排序法对生成的一维数组进行排序,并将排序后的一维数组显示在下方的文本框中。实例的运行效果如图511所示

实现过程

(3)编写“排序”按钮的事件处理方法,在该方法中利用快速排序算法对生成的随机数组进行排序,并将排序过程输出到文本域控件中。关键代码如下:

(4)编写快速排序方法quickSort,这个方法将被按钮的事件处理方法调用,该方法在实现快速排序的同时,把排序过程显示到文本域控件中。关键代码如下:

(5)由于快速排序方法中频繁地交换数组元素,而且在程序代码中出现的次数较多,所以应该把数组元素交换单独提炼为一个swap()方法,以实现代码重用,并且可以在该方法中掌握排序过程并显示到文本域控件。关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
private void quickSort(int[] array)
{
quickSort(array, 0, array.length - 1);
}
private void quickSort(int[] array, int left, int right)
{
if (left < right)
{
int splitIndex = divide(array, left, right);
quickSort(array, left, splitIndex - 1);
quickSort(array, splitIndex + 1, right);
}
}
private int divide(int[] array, int left, int right)
{
int splitPoint = array[left];
int i = left;
int j = right;
while (i < j)
{
// 从右到左 找出比分割点小的元素
while (j > i)
{
// 如果在右边找到比分割点小的数,结束查找
if (array[j] < splitPoint)
{
break;
}
j--;
}
// 从左往右 找出比分割点大的元素
while (i < j)
{
// 如果找到一个比分割点大的数,则结束查找
if (array[i] > splitPoint)
{
break;
}
i++;
}
// j>i时交换j==i时不交换
if (j > i)
{
swap(array, i, j);
printSwap(array, i, j, left, right);;
}
}
swap(array, left, i);
printSwap(array, left, i, left, right);
return i;
}
private void swap(int[] array, int i, int j)
{
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}

技术要点

本实例主要用到了快速排序算法,下面对快速排序算法的实现原理进行详细讲解。快速排序算法是对冒泡排序算法的一种改进,它的基本思想是:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据小,然后再按此方法对这两部分数据分别进行快速排序。整个排序过程可以递归进行,以此使整个数据变成有序序列。
假设要排序的数组是A[1]...A[N],

  • 首先任意选取一个数据(通常选用第一个数据)作为关键数据,
  • 然后将所有比它小的数都放到它前面,
  • 所有比它大的数都放到它后面,

这个过程称为一趟快速排序,递归调用此过程,即可实现数组的快速排序。
使用快速排序法的排序过程如图5.12所示

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
package com;

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.EmptyBorder;
import java.awt.Font;

public class QuickSort2 extends JFrame
{
private static final long serialVersionUID = -7934071799529975055L;
private JPanel contentPane;

/**
* Launch the application.
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable() {
public void run()
{
try
{
QuickSort2 frame = new QuickSort2();
frame.setVisible(true);
} catch (Exception e)
{
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public QuickSort2() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 500, 500);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
GridBagLayout gbl_contentPane = new GridBagLayout();
gbl_contentPane.columnWidths = new int[]{0, 0};
gbl_contentPane.rowHeights = new int[]{0, 0, 0, 0, 0};
gbl_contentPane.columnWeights = new double[]{1.0, Double.MIN_VALUE};
gbl_contentPane.rowWeights = new double[]{0.0, 0.0, 1.0, 0.0, Double.MIN_VALUE};
contentPane.setLayout(gbl_contentPane);

JTextArea textAreaArray = new JTextArea();
textAreaArray.setRows(1);
GridBagConstraints gbc_arrayTextArea = new GridBagConstraints();
gbc_arrayTextArea.insets = new Insets(0, 0, 5, 0);
gbc_arrayTextArea.fill = GridBagConstraints.BOTH;
gbc_arrayTextArea.gridx = 0;
gbc_arrayTextArea.gridy = 0;
contentPane.add(textAreaArray, gbc_arrayTextArea);

JButton jButtonRandomArray = new JButton("生成随机数组");
jButtonRandomArray.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
textAreaArray.setText("");
int arrayLength = 10;
int[] array = new int[arrayLength];
Random random = new Random();
for (int i = 0; i < array.length; i++)
{
array[i] = random.nextInt(50);
textAreaArray.append("" + array[i]);
if (i < array.length - 1)
{
textAreaArray.append(",");
}
}

}
});
GridBagConstraints gbc_jButtonRandomArray = new GridBagConstraints();
gbc_jButtonRandomArray.insets = new Insets(0, 0, 5, 0);
gbc_jButtonRandomArray.gridx = 0;
gbc_jButtonRandomArray.gridy = 1;
contentPane.add(jButtonRandomArray, gbc_jButtonRandomArray);

JScrollPane scrollPane = new JScrollPane();
GridBagConstraints gbc_scrollPane = new GridBagConstraints();
gbc_scrollPane.fill = GridBagConstraints.BOTH;
gbc_scrollPane.insets = new Insets(0, 0, 5, 0);
gbc_scrollPane.gridx = 0;
gbc_scrollPane.gridy = 2;
contentPane.add(scrollPane, gbc_scrollPane);

JTextArea textAreaShow = new JTextArea();
textAreaShow.setFont(new Font("Monospaced", Font.PLAIN, 14));
scrollPane.setViewportView(textAreaShow);

JButton jButtonQuickSort = new JButton("快速排序");
jButtonQuickSort.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
textAreaShow.setText("");
String[] arrayStrs = textAreaArray.getText().split(",");
int[] array = new int[arrayStrs.length];
for (int i = 0; i < array.length; i++)
{
array[i] = Integer.parseInt(arrayStrs[i]);
}
textAreaShow.append("排序之前:\n");
printArray(array, textAreaShow);
textAreaShow.append("\n");
quickSort(array);
textAreaShow.append("\n");
textAreaShow.append("排序之后:\n");
printArray(array, textAreaShow);
}
private void printArray(int[] array, JTextArea textAreaShow)
{
// textAreaShow.append("[");
for (int i = 0; i < array.length; i++)
{
textAreaShow.append("" + array[i]);
if (i < array.length - 1)
{
textAreaShow.append(",");
}
}
// textAreaShow.append("]");
textAreaShow.append("\n");
}
private void quickSort(int[] array)
{
quickSort(array, 0, array.length - 1);
}
private void quickSort(int[] array, int left, int right)
{
if (left < right)
{
int splitIndex = divide(array, left, right);
quickSort(array, left, splitIndex - 1);
quickSort(array, splitIndex + 1, right);
}
}
private int divide(int[] array, int left, int right)
{
int splitPoint = array[left];
int i = left;
int j = right;
while (i < j)
{
// 从右到左 找出比分割点小的元素
while (j > i)
{
// 如果在右边找到比分割点小的数,结束查找
if (array[j] < splitPoint)
{
break;
}
j--;
}
// 从左往右 找出比分割点大的元素
while (i < j)
{
// 找到一个比分割点大的数,则结束查找
if (array[i] > splitPoint)
{
break;
}
i++;
}
if (j > i)
{
swap(array, i, j);
printSwap(array, i, j, left, right);;
}
}
swap(array, left, i);
printSwap(array, left, i, left, right);
// textAreaShow.append("分割点:" + array[i] + ",index=" + i+"\n");
return i;
}
private void swap(int[] array, int i, int j)
{
int temp = array[i];
array[i] = array[j];
array[j] = temp;
// printSwap(array, i, j);
}
private void printSwap(int[] array, int i, int j, int left, int right)
{
for (int k = 0; k < array.length; k++)
{
if (k == left)
{
textAreaShow.append("[");
}
if (k == i || k == j)
{
textAreaShow.append("'" + array[k] + "'");
} else
{
textAreaShow.append("" + array[k]);
}
if (k == right)
{
textAreaShow.append("]");
}
if (k < array.length - 1)
textAreaShow.append(",");
}
textAreaShow.append("\n");
}
});
GridBagConstraints gbc_jButtonQuickSort = new GridBagConstraints();
gbc_jButtonQuickSort.gridx = 0;
gbc_jButtonQuickSort.gridy = 3;
contentPane.add(jButtonQuickSort, gbc_jButtonQuickSort);
}

}

实例038 使用冒泡排序法对数组排序

实例说明

本实例演示如何使用冒泡排序法对一维数组进行排序。运行本实例,首先单击“生成随机数组”按钮,生成一个随机数组,并显示在上方的文本域控件中;然后单击“排序”按钮,使用冒泡排序法对生成的一维数组进行排序,并将排序过程中一维数组的变化显示在下方的文本域控件中。实例的运行效果如图59所示。

实现过程

(3)编写“生成随机数组”按钮的事件处理方法,在该方法中创建Randon随机数对象,初始化数组元素值时,通过该对象为每个数组元素生成随机数。关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
JButton randomArrayButton = new JButton("生成随机数组");
randomArrayButton.setFont(new Font("宋体", Font.PLAIN, 14));
randomArrayButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
// 清空输入框
textField.setText("");
int[] array = new int[10];
int randomInt;
String arrayStr = "";
Random random = new Random();
for (int i = 0; i < array.length; i++)
{
randomInt = random.nextInt(50);
array[i] = randomInt;
if (i < array.length - 1)
{
arrayStr += array[i] + ",";
} else
{
arrayStr += array[i] + "";
}
}
textField.setText(arrayStr);
// System.out.println(arrayStr);
}
});

(4)编写“排序”按钮的事件处理方法,在该方法中使用排序算法对生成的随机数组进行排序,然后把排序后的数组元素显示到文本域控件中。关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
JButton bubbleSortButton = new JButton("冒泡排序");
bubbleSortButton.setFont(new Font("宋体", Font.PLAIN, 14));
bubbleSortButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
textArea.setText("");
String arrayStr = textField.getText();
String[] arrayStrs = arrayStr.split(",");
int[] array = new int[arrayStrs.length];
for (int i = 0; i < array.length; i++)
{
array[i] = Integer.parseInt(arrayStrs[i]);
}
textArea.append("待排序的数组:");
printArray(array);
textArea.append("排序轮次\t排序结果\n");
bubbleSort(array);
}

private void printArray(int[] array)
{
for (int j = 0; j < array.length; j++)
{
if (j < array.length - 1)
{
textArea.append(array[j] + ",");
} else
{
textArea.append(array[j] + "");
}
}
textArea.append("\n");
}

private void bubbleSort(int[] array)
{
int temp;
// 每次能选出一个最大的元素到尾部
for (int i = 0; i < array.length; i++)
{
for (int j = 0; j < array.length - i - 1; j++)
{
// 如果前面的大于后面的
if (array[j] > array[j + 1])
{
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
// 打印这次排序的结果
printSortingSteps(array, i);
}
}

private void printSortingSteps(int[] array, int i)
{
textArea.append(i + 1 + "\t");
for (int j = 0; j < array.length; j++)
{
if (j == array.length - i - 1)
{
textArea.append("[");
}
if (j < array.length - 1)
{
textArea.append(array[j] + ",");
} else
{
textArea.append(array[j] + "");
}

}
textArea.append("]\n");
}
});

技术要点

在实现本实例时,主要用到了冒泡排序算法。冒泡排序的基本思想是
对比相邻的元素值,如果满足条件就交换元素值,把较小的元素移动到数组前面,把大的元素移动到数组后面(也就是交换两个元素的位置),这样数组元素就像气泡一样从底部上升到顶部。
冒泡算法在双层循环中实现,其中外层循环控制排序轮数,是要排序数组长度-1次。
而内层循环主要是用于对比临近元素的大小,以确定是否交换位置,对比和交换次数依排序轮数而减少。

例如一个拥有6个元素的数组,在排序过程中每一次循环的排序过程和结果如图5.10所
图片

第一轮外层循环时把最大的元素值63移动到了最后面(相应地比63小的元素向前移动,类似气泡上升),
第二轮外层循环不再对比最后一个元素值63,因为它已经确认为最大(不需要上升),应该放在最后,需要对比和移动的是其他剩余元素,这次将元素24移动到了63的前一个位置。
其他循环将依此类推,继续完成排序任务。

关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void bubbleSort(int[] array)
{
int temp;
// 每次能选出一个最大的元素到尾部
for (int i = 0; i < array.length; i++)
{
for (int j = 0; j < array.length - i - 1; j++)
{
// 如果前面的大于后面的
if (array[j] > array[j + 1])
{
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
// 打印这次排序的结果
printSortingSteps(array, i);
}
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
package com;

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import java.awt.Font;

public class BubbleSort extends JFrame
{
private static final long serialVersionUID = -3781524001636217405L;
private JPanel contentPane;
private JTextField textField;

/**
* Launch the application.
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable() {
public void run()
{
try
{
BubbleSort frame = new BubbleSort();
frame.setVisible(true);
} catch (Exception e)
{
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public BubbleSort() {
setTitle("冒泡排序");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 350);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
GridBagLayout gbl_contentPane = new GridBagLayout();
gbl_contentPane.columnWeights = new double[]{1.0};
gbl_contentPane.rowWeights = new double[]{0.0, 0.0, 1.0, 0.0};
contentPane.setLayout(gbl_contentPane);

textField = new JTextField();
textField.setFont(new Font("宋体", Font.PLAIN, 14));
GridBagConstraints gbc_textField = new GridBagConstraints();
gbc_textField.insets = new Insets(0, 0, 5, 0);
gbc_textField.fill = GridBagConstraints.HORIZONTAL;
gbc_textField.gridx = 0;
gbc_textField.gridy = 0;
contentPane.add(textField, gbc_textField);
textField.setColumns(10);

JButton randomArrayButton = new JButton("生成随机数组");
randomArrayButton.setFont(new Font("宋体", Font.PLAIN, 14));
randomArrayButton.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e)
{
// 清空输入框
textField.setText("");
int[] array = new int[10];
int randomInt;
String arrayStr = "";
Random random = new Random();
for (int i = 0; i < array.length; i++)
{
randomInt = random.nextInt(50);
array[i] = randomInt;
if (i < array.length - 1)
{
arrayStr += array[i] + ",";
} else
{
arrayStr += array[i] + "";
}
}
textField.setText(arrayStr);
// System.out.println(arrayStr);
}
});
GridBagConstraints gbc_randomArrayButton = new GridBagConstraints();
gbc_randomArrayButton.insets = new Insets(0, 0, 5, 0);
gbc_randomArrayButton.gridx = 0;
gbc_randomArrayButton.gridy = 1;
contentPane.add(randomArrayButton, gbc_randomArrayButton);

JScrollPane scrollPane = new JScrollPane();
GridBagConstraints gbc_scrollPane = new GridBagConstraints();
gbc_scrollPane.fill = GridBagConstraints.BOTH;
gbc_scrollPane.insets = new Insets(0, 0, 5, 0);
gbc_scrollPane.gridx = 0;
gbc_scrollPane.gridy = 2;
contentPane.add(scrollPane, gbc_scrollPane);

JTextArea textArea = new JTextArea();
textArea.setFont(new Font("宋体", Font.PLAIN, 14));
scrollPane.setViewportView(textArea);

JButton bubbleSortButton = new JButton("冒泡排序");
bubbleSortButton.setFont(new Font("宋体", Font.PLAIN, 14));
bubbleSortButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
textArea.setText("");
String arrayStr = textField.getText();
String[] arrayStrs = arrayStr.split(",");
int[] array = new int[arrayStrs.length];
for (int i = 0; i < array.length; i++)
{
array[i] = Integer.parseInt(arrayStrs[i]);
}
textArea.append("待排序的数组:");
printArray(array);
textArea.append("排序轮次\t排序结果\n");
bubbleSort(array);
}

private void printArray(int[] array)
{
for (int j = 0; j < array.length; j++)
{
if (j < array.length - 1)
{
textArea.append(array[j] + ",");
} else
{
textArea.append(array[j] + "");
}
}
textArea.append("\n");
}

private void bubbleSort(int[] array)
{
int temp;
// 每次能选出一个最大的元素到尾部
for (int i = 0; i < array.length; i++)
{
for (int j = 0; j < array.length - i - 1; j++)
{
// 如果前面的大于后面的
if (array[j] > array[j + 1])
{
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
// 打印这次排序的结果
printSortingSteps(array, i);
}
}

private void printSortingSteps(int[] array, int i)
{
textArea.append(i + 1 + "\t");
for (int j = 0; j < array.length; j++)
{
if (j == array.length - i - 1)
{
textArea.append("[");
}
if (j < array.length - 1)
{
textArea.append(array[j] + ",");
} else
{
textArea.append(array[j] + "");
}

}
textArea.append("]\n");
}
});
GridBagConstraints gbc_bubbleSortButton = new GridBagConstraints();
gbc_bubbleSortButton.gridx = 0;
gbc_bubbleSortButton.gridy = 3;
contentPane.add(bubbleSortButton, gbc_bubbleSortButton);
}

}

实例037 使用选择排序法对数组排序

实例说明
选择排序(SelectionSort)是一种简单直观的排序算法。本实例演示如何使用选择排序法.
对一维数组进行排序,运行本实例,

  • 首先单击“生成随机数组”按钮,生成一个随机数组,并显示在上方的文本域控件中;
  • 然后单击“排序”按钮,使用选择排序法对生成的维数组进行排序,并将排序后的一维数组显示在下方的文本域控件中。

实例的运行效果如图57所示。

实现过程

(3)编写“生成随机数组”按钮的事件处理方法,在该方法中创建Random随机数对象,初始化数组元素值时,通过该对象为每个数组元素生成随机数。关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
JButton btnNewButton = new JButton("生成随机数");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
int[] numbers = new int[10];
for (int i = 0; i < numbers.length; i++)
{
// 生成[0,50)之间的随机数
numbers[i] = new Random().nextInt(50);
if (i < numbers.length - 1)
textArea.append(numbers[i] + " ");
else
{
textArea.append(numbers[i] + "");
}
}
}
});

(4)编写“排序”按钮的事件处理方法,在该方法中使用排序算法对生成的随机数组进行排序,然后把排序后的数组元素显示到文本域控件中。关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
JButton btnNewButton_1 = new JButton("选择排序");
btnNewButton_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
// 清空显示区域
textArea_1.setText("");
// 解析字符串为int数组
String numStr = textArea.getText();
String[] numStrs = numStr.split(" ");
int[] numbers = new int[numStrs.length];
for (int i = 0; i < numbers.length; i++)
{
numbers[i] = Integer.parseInt(numStrs[i]);
}
// 排序
for (int i = 0; i < numbers.length; i++)
{
int maxIndex = 0;
// 查找未排序区域中的最大值
for (int j = 0; j < numbers.length - i; j++)
{
if (numbers[j] > numbers[maxIndex])
{
maxIndex = j;
}
}
// 缓存最后的数
int temp = numbers[numbers.length - 1 - i];
// 将大的数换到最后
numbers[numbers.length - 1 - i] = numbers[maxIndex];
// 将最后的数换到
numbers[maxIndex] = temp;
}
for (int i = 0; i < numbers.length; i++)
{
if (i < numbers.length - 1)
textArea_1.append(numbers[i] + " ");
else
{
textArea_1.append(numbers[i] + "");
}
}
}
});

多学两招

利用选择排序法从数组中挑选最大值并放在数组最后,而遇到重复的相等值不会做任何处理,所以,如果程序允许数组有重复值的情况,建议使用选择排序方法,因为它的数据交换次数较少,相对速度也会略微提升,这取决于数组中重复值的数量

技术要点

本实例应用的主要技术点就是选择排序算法。选择排序算法的基本思想如下:
每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。

例如,对一个一维数组采用选择排序算法进行排序的过程如图5.8所示。
图片

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package com;

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.EmptyBorder;

public class SelectSort extends JFrame
{

private static final long serialVersionUID = -857370078330509549L;
private JPanel contentPane;

/**
* Launch the application.
*/
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable() {
public void run()
{
try
{
SelectSort frame = new SelectSort();
frame.setVisible(true);
} catch (Exception e)
{
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public SelectSort() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
GridBagLayout gbl_contentPane = new GridBagLayout();
gbl_contentPane.rowHeights = new int[]{0, 0, 0, 0};
gbl_contentPane.columnWeights = new double[]{1.0};
gbl_contentPane.rowWeights = new double[]{1.0, 0.0, 1.0, 0.0};
contentPane.setLayout(gbl_contentPane);

JScrollPane scrollPane = new JScrollPane();
GridBagConstraints gbc_scrollPane = new GridBagConstraints();
gbc_scrollPane.fill = GridBagConstraints.BOTH;
gbc_scrollPane.gridx = 0;
gbc_scrollPane.gridy = 0;
contentPane.add(scrollPane, gbc_scrollPane);

JTextArea textArea = new JTextArea();
scrollPane.setViewportView(textArea);

JButton btnNewButton = new JButton("生成随机数");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
textArea.setText("");
int[] numbers = new int[10];
for (int i = 0; i < numbers.length; i++)
{
// 生成[0,50)之间的随机数
numbers[i] = new Random().nextInt(50);
if (i < numbers.length - 1)
textArea.append(numbers[i] + " ");
else
{
textArea.append(numbers[i] + "");
}
}
}
});
GridBagConstraints gbc_btnNewButton = new GridBagConstraints();
gbc_btnNewButton.insets = new Insets(5, 0, 5, 0);
gbc_btnNewButton.gridx = 0;
gbc_btnNewButton.gridy = 1;
contentPane.add(btnNewButton, gbc_btnNewButton);

JScrollPane scrollPane_1 = new JScrollPane();
GridBagConstraints gbc_scrollPane_1 = new GridBagConstraints();
gbc_scrollPane_1.fill = GridBagConstraints.BOTH;
gbc_scrollPane_1.gridx = 0;
gbc_scrollPane_1.gridy = 2;
contentPane.add(scrollPane_1, gbc_scrollPane_1);

JTextArea textArea_1 = new JTextArea();
textArea_1.setEditable(false);
scrollPane_1.setViewportView(textArea_1);

JButton btnNewButton_1 = new JButton("选择排序");
btnNewButton_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
// 清空显示区域
textArea_1.setText("");
// 解析字符串为int数组
String numStr = textArea.getText();
String[] numStrs = numStr.split(" ");
int[] numbers = new int[numStrs.length];
for (int i = 0; i < numbers.length; i++)
{
numbers[i] = Integer.parseInt(numStrs[i]);
}
// 选择排序法
for (int i = 0; i < numbers.length; i++)
{
int maxIndex = 0;
// 查找未排序区域中的最大值
for (int j = 0; j < numbers.length - i; j++)
{
if (numbers[j] > numbers[maxIndex])
{
maxIndex = j;
}
}
// 缓存最后的数
int temp = numbers[numbers.length - 1 - i];
// 将大的数换到最后
numbers[numbers.length - 1 - i] = numbers[maxIndex];
// 将最后的数换到
numbers[maxIndex] = temp;
// 打印排序结果
printEachSortingStepResults(numbers, i);
}
for (int i = 0; i < numbers.length; i++)
{
if (i < numbers.length - 1)
textArea_1.append(numbers[i] + " ");
else
{
textArea_1.append(numbers[i] + "");
}
}
}
/**
* 打印每个排序步骤的结果
* @param numbers 要排序的数组
* @param i 选择排序算法运行的次数.
*/
private void printEachSortingStepResults(int[] numbers, int i)
{
System.out.print("[");
for (int j = 0; j < numbers.length; j++)
{
System.out.print(numbers[j]);
if (j < numbers.length - 1 && j != numbers.length - i - 1)
{
System.out.print(",");
}
if (j == numbers.length - i - 1)
{
if (i == 0)
System.out.print("]");
else
{
System.out.print("],");
}
}

}
System.out.println("");
}
});
GridBagConstraints gbc_btnNewButton_1 = new GridBagConstraints();
gbc_btnNewButton_1.insets = new Insets(5, 0, 5, 0);
gbc_btnNewButton_1.gridx = 0;
gbc_btnNewButton_1.gridy = 3;
contentPane.add(btnNewButton_1, gbc_btnNewButton_1);
GridBagConstraints gbc_textArea = new GridBagConstraints();
gbc_textArea.fill = GridBagConstraints.BOTH;
gbc_textArea.gridx = 0;
gbc_textArea.gridy = 1;
GridBagConstraints gbc_textArea_1 = new GridBagConstraints();
gbc_textArea_1.insets = new Insets(0, 0, 5, 0);
gbc_textArea_1.fill = GridBagConstraints.BOTH;
gbc_textArea_1.gridx = 0;
gbc_textArea_1.gridy = 2;
}

}

运行效果

生成的数组:

1
4 23 31 1 29 47 36 20 13 41

排序后的数组:

1
1 4 13 20 23 29 31 36 41 47

每一趟排序过程

1
2
3
4
5
6
7
8
9
10
[4,23,31,1,29,41,36,20,13,47]
[4,23,31,1,29,13,36,20,41],47
[4,23,31,1,29,13,20,36],41,47
[4,23,20,1,29,13,31],36,41,47
[4,23,20,1,13,29],31,36,41,47
[4,13,20,1,23],29,31,36,41,47
[4,13,1,20],23,29,31,36,41,47
[4,1,13],20,23,29,31,36,41,47
[1,4],13,20,23,29,31,36,41,47
[1],4,13,20,23,29,31,36,41,47

实例036 通过复选框控件数组实现添加多个复选框控件

实例说明
复选框控件在GUI程序界面设计时经常使用,例如选择用户爱好的程序界面中要添加很多选项,这些选项如果通过GUI界面设计器来录入非常费时,而且生成的代码臃肿,不方便维护。不过可以通过复选框控件数组实现在窗体中添加多个复选框。本实例将通过复选框控件数组实现选择用户爱好信息的复选框,并且界面中的复选框数量可以根据指定复选框名称的字符串数组的长度来自动调节。实例的运行效果如图5.6所示。

实现过程

编写getPanel()方法创建面板并在面板中通过控件数组来创建爱好复选框。其中所有复选框的文本都是由字符串数组定义的,复选框的数量也是根据字符串数组长度确定的。关键代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package com.mingrisoft;

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;

import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class CheckBoxArray extends JFrame {

private static final long serialVersionUID = -5338362310060106193L;
private JPanel contentPane;
private JPanel panel;

/**
* Launch the application.
*/
public static void main(String[] args) {

EventQueue.invokeLater(new Runnable() {
public void run() {
try {
CheckBoxArray frame = new CheckBoxArray();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public CheckBoxArray() {
setTitle("通过复选框控件数组实现添加多个复选框控件");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 409, 331);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);

JLabel label = new JLabel("你的爱好有哪些:");
contentPane.add(label, BorderLayout.NORTH);
contentPane.add(getPanel(), BorderLayout.CENTER);
}

private JPanel getPanel() {
if (panel == null) {
// 创建面板对象
panel = new JPanel();
// 设置网格布局管理器
panel.setLayout(new GridLayout(0, 4));
// 创建控件文本数组
String[] labels = { "足球", "篮球", "魔术", "乒乓球", "看电影", "魔兽世界", "CS战队", "羽毛球", "游泳", "旅游", "爬山", "唱歌", "写博客",
"动物世界", "拍照", "弹吉他", "读报纸", "飙车", "逛街", "逛商场", "麻将", "看书", "上网看资料", "新闻", "军事", "八卦", "养生", "饮茶" };
// 创建控件数组
JCheckBox[] boxs = new JCheckBox[labels.length];
// 遍历控件数组
for (int i = 0; i < boxs.length; i++) {
// 初始化数组中的复选框组件
boxs[i] = new JCheckBox(labels[i]);
// 把数组元素(即每个复选框)添加到面板中
panel.add(boxs[i]);
}
}
return panel;
}
}

多学两招

在编写一个方法时,要考虑到方法的通用性,尽量对方法进行抽象让方法适合更多的模块调用,例如本实例中的getPanel()方法完全可以把控件标签文本数组作为方法的参数,这样其他模块就可以为该方法传递参数创建爱好选择面板了

1
2
3
4
5
6
7
8
9
10
11
12
private JPanel getPanel(String[] labels) {
JPanel panel = new JPanel();
// 设置网格布局管理器
panel.setLayout(new GridLayout(0, 4));
JCheckBox[] checkBoxs = new JCheckBox[labels.length];
for (int i = 0; i < checkBoxs.length; i++) {
checkBoxs[i] = new JCheckBox(labels[i]);
panel.add(checkBoxs[i]);
}

return panel;
}

技术要点

本实例中应用的主要技术是在JPanel面板中应用复选框控件数组添加复选框控件。应用复选框控件数组添加复选框控件的具体实现步骤如下:
(1)定义一个字符串数组,内容为复选框的标题文本。
(2)创建JCheckBox类型的控件数组,即复选框控件数组,其长度与步骤(1)中创建的字符串数组的长度相同。
(3)通过for循环遍历刚刚创建的复选框控件数组,并将数组元素(即每个复选框)添加到面板中。

实例035 使用按钮控件数组实现计算器界面

实例说明

控件数组的应用范围非常广泛,合理使用控件数组可以提高程序开发效率。本实例将应用按钮控件数组来管理界面中的所有按钮控件,从而使用最少的代码实现模拟的计算器界面。实例的运行效果如图5.5所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.mingrisoft;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.UIManager;

public class ButtonArrayExample extends JFrame {
private static final long serialVersionUID = 6626440733001287873L;
private JTextField textField;

public static void main(String args[]) {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Throwable e) {
e.printStackTrace();
}
ButtonArrayExample frame = new ButtonArrayExample();
// 设置窗体可见,默认为不可见
frame.setVisible(true);
}

public ButtonArrayExample() {
super(); // 继承父类的构造方法
BorderLayout borderLayout = (BorderLayout) getContentPane().getLayout();
borderLayout.setHgap(20);
borderLayout.setVgap(10);
// 设置窗体的标题
setTitle("按钮数组实现计算器界面 ");
// 设置窗体的显示位置及大小
setBounds(100, 100, 290, 282);
// 设置窗体关闭按钮的动作为退出
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
textField = new JTextField();
textField.setHorizontalAlignment(SwingConstants.TRAILING);
textField.setPreferredSize(new Dimension(12, 50));
getContentPane().add(textField, BorderLayout.NORTH);
textField.setColumns(10);
// 创建网格布局管理器对象
final GridLayout gridLayout = new GridLayout(4, 0);
// 设置组件的水平间距
gridLayout.setHgap(5);
// 设置组件的垂直间距
gridLayout.setVgap(5);
// 获得容器对象
JPanel panel = new JPanel();
// 设置容器采用网格布局管理器
panel.setLayout(gridLayout);
getContentPane().add(panel, BorderLayout.CENTER);
String[][] names = { { "1", "2", "3", "+" }, { "4", "5", "6", "-" }, { "7", "8", "9", "×" },
{ ".", "0", "=", "÷" } };
JButton[][] buttons = new JButton[4][4];
for (int row = 0; row < names.length; row++) {
for (int col = 0; col < names.length; col++) {
// 创建按钮对象
buttons[row][col] = new JButton(names[row][col]);
// 将按钮添加到面板中
panel.add(buttons[row][col]);
}
}
}
}

技术要点

本实例的关键点在于GridLayout布局管理器的应用,通过它可以自动完成控件的布局与大小控制,否则,程序还要单独创建控制每个控件位置与大小的代码,其代码复杂度可想而知。通过GridLayout布局管理器,只需要指定布局的行列数量即可。下面介绍一下GUI如何使用GridLayout布局管理器。

1. 创建指定行列数量的布局管理器

可以在GridLayout类的构造方法中传递两个int类型的参数分别指定布局的行数与列数。其方法的声明如下:

1
public GridLayout(int rows, int cols)

参数说明

  • rows:布局的行数。
  • cols:布局的列数。

2. 设置容器的布局管理器

创建容器布局管理器后,可以把它添加到某个容器的layout属性中,这需要调用容器的设置布局管理器的方法来实现。其方法的声明如下:

1
public void setLayout(LayoutManager mgr)

参数说明

  • mgr:布局管理器对象

实例033 利用数组随机抽取幸运观众

实例说明

数组在程序开发中被广泛应用,使用数组可以使程序代码更加规范,更易于维护。例如,字符串数组可用于定义表格控件的列名称,而整型数组可以用来定义列对应的宽度,本实例就通过这两个数组实现了对表格控件中表头列的设置。实例的运行效果如图54所示。

脚下留神

如果直接将表格控件添加到滚动面板以外的容器中,首先应该通过JTable类的getTableHeader()方法获取表格的JTableheader表头类的对象,然后再将该对象添加到容器相应的位置,否则表格将没有表头,无法显示任何列名称.

技术要点

本实例的关键技术在于设置表格的数据模型和访问列模型。其中表格的数据模型可以采用DefaultTableModel类创建数据模型对象,而创建过程中可以把字符串数组作为参数来创建表格列的名称.

1. 创建表格数据模型

DefaultAbleModel类的构造方法有很多,其中一个可以把字符串数组作为参数来生成列名称,同时接收int类型的参数来设置表格添加多少行空白数据。这个构造方法的声明如下:

1
2
3
public DefaultTableModel(Object[] columnNames, int rowCount) {

}

参数说明

  • columnNames:存放列名的数组。
  • rowCount:指定创建多少行空白数据。

2. 设置表格数据模型

JTable类是表格控件,它提供了setModel()方法来设置表格的数据模型。设置数据模型以后表格控件可以从数据模型中提取表头所有列名称和所有行数据,这个数据模型将负责表格所有数据的维护。该设置表格模型的方法的声明如下:

1
public void setModel(final TableModel dataModel)

参数说明
dataModel:此表的新数据模型。

3. 获取表格列模型

表格中所有列对象都存放在列模型中,它们用于定义表格的每个列的名称及宽度等信息。表格的列模型可以通过getColumnModel()方法来获取。其方法的声明如下

1
public TableColumnModel getColumnModel()

4. 设置列宽度

列对象存放在列模型中,并且列的宽度需要通过列对象的setPreferredWidth()方法来设置。该方法的声明如下:

1
public void setPreferredWidth(int preferredWidth)

参数说明
preferredWidth:列对象的首选宽度参数。